Skip to content

Commit 62d6580

Browse files
authored
perf(tests): cache dd-trace tarball for sandbox creation (#7351)
Parallel integration tests were bottlenecked by multiple processes simultaneously packing dd-trace.tgz during sandbox creation. This caused severe I/O contention. Cache the tarball at the sandboxRoot level so it's only created once per test run, then reused by all parallel workers. Debugger integration tests performance improvement example: - Sequential: 5:06 - Parallel (--jobs 2) before: 6:47 (slower due to contention) - Parallel (--jobs 2) after: 3:02 (40% faster than sequential) - System CPU overhead reduced from 424s to 164s.
1 parent 38b60f7 commit 62d6580

File tree

2 files changed

+73
-4
lines changed

2 files changed

+73
-4
lines changed

integration-tests/helpers/index.js

Lines changed: 68 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -325,6 +325,68 @@ function execHelper (command, options) {
325325
}
326326
}
327327

328+
/**
329+
* Pack dd-trace into a tarball at the specified path.
330+
*
331+
* @param {string} tarballPath - The path where the tarball should be created
332+
* @param {NodeJS.ProcessEnv} env - The environment to use for the pack command
333+
*/
334+
function packTarball (tarballPath, env) {
335+
execHelper(`${BUN} pm pack --quiet --gzip-level 0 --filename ${tarballPath}`, { env })
336+
log('Tarball packed successfully:', tarballPath)
337+
}
338+
339+
/**
340+
* Pack the tarball with file locking to coordinate between parallel workers.
341+
* Only one worker will pack the tarball, others will wait for it to be ready.
342+
*
343+
* @param {string} tarballPath - The path where the tarball should be created
344+
* @param {NodeJS.ProcessEnv} env - The environment to use for the pack command
345+
* @returns {Promise<void>}
346+
*/
347+
async function packTarballWithLock (tarballPath, env) {
348+
if (existsSync(tarballPath)) {
349+
log('Tarball already exists:', tarballPath)
350+
return
351+
}
352+
353+
const lockFile = `${tarballPath}.lock`
354+
let lockFd
355+
356+
try {
357+
// Try to acquire the lock by creating the lock file exclusively
358+
lockFd = await fs.open(lockFile, 'wx')
359+
log('Lock acquired, packing tarball:', tarballPath)
360+
361+
// Double-check if tarball was created while we were acquiring the lock
362+
if (existsSync(tarballPath)) {
363+
log('Tarball already exists (created while waiting for lock):', tarballPath)
364+
return
365+
}
366+
367+
// We have the lock, pack the tarball
368+
packTarball(tarballPath, env)
369+
} catch (err) {
370+
if (err.code === 'EEXIST') {
371+
// Lock exists, another process is packing - wait for the tarball to appear
372+
log('Lock file exists, waiting for tarball:', tarballPath)
373+
374+
while (!existsSync(tarballPath)) {
375+
await new Promise(resolve => setTimeout(resolve, 100))
376+
}
377+
378+
log('Tarball ready:', tarballPath)
379+
} else {
380+
throw err
381+
}
382+
} finally {
383+
// Strictly no need to clean up the lock - it's in a temp directory
384+
if (lockFd) {
385+
lockFd.close().catch(() => {})
386+
}
387+
}
388+
}
389+
328390
/**
329391
* @param {string[]} dependencies
330392
* @param {boolean} isGitRepo
@@ -365,15 +427,17 @@ async function createSandbox (
365427
return { folder: path.join(process.cwd(), 'integration-tests'), remove: async () => {} }
366428
}
367429
const folder = path.join(sandboxRoot, id().toString())
368-
const out = path.join(sandboxRoot, 'dd-trace.tgz')
430+
const tarballEnv = process.env.DD_TEST_SANDBOX_TARBALL_PATH
431+
const out = tarballEnv && tarballEnv !== '0' && tarballEnv !== 'false'
432+
? tarballEnv
433+
: path.join(sandboxRoot, 'dd-trace.tgz')
369434
const deps = cappedDependencies.concat(`file:${out}`)
370435

371436
await fs.mkdir(folder, { recursive: true })
372437
const addOptions = { cwd: folder, env: restOfEnv }
373438
const addFlags = ['--trust']
374-
if (!existsSync(out)) {
375-
execHelper(`${BUN} pm pack --quiet --gzip-level 0 --filename ${out}`, { env: restOfEnv })
376-
}
439+
440+
await packTarballWithLock(out, restOfEnv)
377441

378442
if (process.env.OFFLINE === '1' || process.env.OFFLINE === 'true') {
379443
addFlags.push('--prefer-offline')

scripts/mocha-parallel-files.js

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -362,8 +362,13 @@ async function main () {
362362
options.require = opts.require
363363
}
364364

365+
// Enable shared tarball caching for parallel runs to avoid redundant packing
366+
const DD_TEST_SANDBOX_TARBALL_PATH = process.env.DD_TEST_SANDBOX_TARBALL_PATH ||
367+
(jobs > 1 ? path.join(os.tmpdir(), 'dd-trace-integration-test.tgz') : undefined)
368+
365369
const childEnv = {
366370
...process.env,
371+
DD_TEST_SANDBOX_TARBALL_PATH,
367372
MOCHA_RUN_FILE_CONFIG: JSON.stringify(options),
368373
}
369374

0 commit comments

Comments
 (0)