Skip to content

Commit 7001200

Browse files
committed
Default to zstd compression for profile uploads on Node.js 24+
1 parent 39fb819 commit 7001200

File tree

3 files changed

+23
-14
lines changed

3 files changed

+23
-14
lines changed

integration-tests/profiler/profiler.spec.js

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ if (process.platform !== 'win32') {
2424
}
2525

2626
const TIMEOUT = 30000
27+
const isAtLeast24 = satisfies(process.versions.node, '>=24.0.0')
2728

2829
function checkProfiles (agent, proc, timeout,
2930
expectedProfileTypes = DEFAULT_PROFILE_TYPES, expectBadExit = false, expectSeq = true
@@ -95,9 +96,9 @@ function processExitPromise (proc, timeout, expectBadExit = false) {
9596
}
9697

9798
async function getLatestProfile (cwd, pattern) {
98-
const pprofGzipped = await readLatestFile(cwd, pattern)
99-
const pprofUnzipped = zlib.gunzipSync(pprofGzipped)
100-
return { profile: Profile.decode(pprofUnzipped), encoded: pprofGzipped.toString('base64') }
99+
const pprofCompressed = await readLatestFile(cwd, pattern)
100+
const pprofUncompressed = zlib[isAtLeast24 ? 'zstdDecompressSync' : 'gunzipSync'](pprofCompressed)
101+
return { profile: Profile.decode(pprofUncompressed), encoded: pprofCompressed.toString('base64') }
101102
}
102103

103104
async function readLatestFile (cwd, pattern) {
@@ -360,7 +361,7 @@ describe('profiler', () => {
360361
const execArgv = []
361362
if (satisfies(process.versions.node, '>=22.9.0')) {
362363
env.DD_PROFILING_ASYNC_CONTEXT_FRAME_ENABLED = 1
363-
if (!satisfies(process.versions.node, '>=24.0.0')) {
364+
if (!isAtLeast24) {
364365
// For Node 22.9.0+, use the experimental command line flag for Node to enable
365366
// async context frame. Node 24 has it enabled by default.
366367
execArgv.push('--experimental-async-context-frame')

packages/dd-trace/src/profiling/config.js

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -179,6 +179,9 @@ class Config {
179179

180180
this.heapSamplingInterval = options.heapSamplingInterval ??
181181
(Number(DD_PROFILING_HEAP_SAMPLING_INTERVAL) || 512 * 1024)
182+
183+
const isAtLeast24 = satisfies(process.versions.node, '>=24.0.0')
184+
182185
const uploadCompression0 = options.uploadCompression ?? DD_PROFILING_DEBUG_UPLOAD_COMPRESSION ?? 'on'
183186
let [uploadCompression, level0] = uploadCompression0.split('-')
184187
if (!['on', 'off', 'gzip', 'zstd'].includes(uploadCompression)) {
@@ -206,9 +209,12 @@ class Config {
206209
}
207210
}
208211

209-
// Default to gzip
212+
// Default to either zstd (on Node.js 24+) or gzip (earlier Node.js). We could default to ztsd
213+
// everywhere as we ship a Rust zstd compressor for older Node.js versions, but on 24+ we use
214+
// the built-in one that runs asynchronously on libuv worker threads, just as gzip does. This is
215+
// the least disruptive choice.
210216
if (uploadCompression === 'on') {
211-
uploadCompression = 'gzip'
217+
uploadCompression = isAtLeast24 ? 'zstd' : 'gzip'
212218
}
213219

214220
this.uploadCompression = { method: uploadCompression, level }
@@ -224,7 +230,7 @@ class Config {
224230

225231
let canUseAsyncContextFrame = false
226232
if (samplingContextsAvailable) {
227-
if (satisfies(process.versions.node, '>=24.0.0')) {
233+
if (isAtLeast24) {
228234
canUseAsyncContextFrame = !hasExecArg('--no-async-context-frame')
229235
} else if (satisfies(process.versions.node, '>=22.9.0')) {
230236
canUseAsyncContextFrame = hasExecArg('--experimental-async-context-frame')
@@ -234,7 +240,7 @@ class Config {
234240
if (this.asyncContextFrameEnabled && !canUseAsyncContextFrame) {
235241
if (!samplingContextsAvailable) {
236242
turnOffAsyncContextFrame(`on ${process.platform}`)
237-
} else if (satisfies(process.versions.node, '>=24.0.0')) {
243+
} else if (isAtLeast24) {
238244
turnOffAsyncContextFrame('with --no-async-context-frame')
239245
} else if (satisfies(process.versions.node, '>=22.9.0')) {
240246
turnOffAsyncContextFrame('without --experimental-async-context-frame')

packages/dd-trace/test/profiling/config.spec.js

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,8 @@ const { ConsoleLogger } = require('../../src/profiling/loggers/console')
1919

2020
const samplingContextsAvailable = process.platform !== 'win32'
2121
const oomMonitoringSupported = process.platform !== 'win32'
22+
const isAtLeast24 = satisfies(process.versions.node, '>=24.0.0')
23+
const zstdOrGzip = isAtLeast24 ? 'zstd' : 'gzip'
2224

2325
describe('config', () => {
2426
let Config
@@ -60,7 +62,7 @@ describe('config', () => {
6062
assert.strictEqual(config.profilers[1].codeHotspotsEnabled(), samplingContextsAvailable)
6163
assert.strictEqual(config.v8ProfilerBugWorkaroundEnabled, true)
6264
assert.strictEqual(config.cpuProfilingEnabled, samplingContextsAvailable)
63-
assert.strictEqual(config.uploadCompression.method, 'gzip')
65+
assert.strictEqual(config.uploadCompression.method, zstdOrGzip)
6466
assert.strictEqual(config.uploadCompression.level, undefined)
6567
})
6668

@@ -510,7 +512,7 @@ describe('config', () => {
510512
}
511513

512514
describe('async context', () => {
513-
const isSupported = samplingContextsAvailable && satisfies(process.versions.node, '>=24.0.0')
515+
const isSupported = samplingContextsAvailable && isAtLeast24
514516
describe('where supported', () => {
515517
it('should be on by default', function () {
516518
if (!isSupported) {
@@ -590,15 +592,15 @@ describe('config', () => {
590592
}
591593

592594
it('should accept known methods', () => {
593-
expectConfig(undefined, 'gzip', undefined)
595+
expectConfig(undefined, zstdOrGzip, undefined)
594596
expectConfig('off', 'off', undefined)
595-
expectConfig('on', 'gzip', undefined)
597+
expectConfig('on', zstdOrGzip, undefined)
596598
expectConfig('gzip', 'gzip', undefined)
597599
expectConfig('zstd', 'zstd', undefined)
598600
})
599601

600602
it('should reject unknown methods', () => {
601-
expectConfig('foo', 'gzip', undefined, 'Invalid profile upload compression method "foo". Will use "on".')
603+
expectConfig('foo', zstdOrGzip, undefined, 'Invalid profile upload compression method "foo". Will use "on".')
602604
})
603605

604606
it('should accept supported compression levels in methods that support levels', () => {
@@ -618,7 +620,7 @@ describe('config', () => {
618620

619621
it('should reject compression levels in methods that do not support levels', () => {
620622
['on', 'off'].forEach((method) => {
621-
const effectiveMethod = method === 'on' ? 'gzip' : method
623+
const effectiveMethod = method === 'on' ? zstdOrGzip : method
622624
expectConfig(`${method}-3`, effectiveMethod, undefined,
623625
`Compression levels are not supported for "${method}".`)
624626
expectConfig(`${method}-foo`, effectiveMethod, undefined,

0 commit comments

Comments
 (0)