From 0bbd3bd2a5dd9606e806ef7f5e8fe3c42cf43671 Mon Sep 17 00:00:00 2001 From: bm1549 Date: Mon, 23 Mar 2026 20:50:01 -0400 Subject: [PATCH 1/5] fix(tracing): format _dd.p.ksr with decimal notation instead of scientific notation Very small sampling rates (e.g. 0.0000001) were formatted using toPrecision(6) + toString() which outputs scientific notation like "1e-7". This changes to explicit rounding at the integer level and toFixed(6) formatting to always produce decimal notation with up to 6 decimal digits, trailing zeros stripped (e.g. "0.000001"). Fixes APMAPI-1869 Co-Authored-By: Claude Opus 4.6 (1M context) --- packages/dd-trace/src/priority_sampler.js | 5 ++- .../dd-trace/test/priority_sampler.spec.js | 39 +++++++++++++++++++ 2 files changed, 42 insertions(+), 2 deletions(-) diff --git a/packages/dd-trace/src/priority_sampler.js b/packages/dd-trace/src/priority_sampler.js index 4e586bc6a93..e67c0aed7c8 100644 --- a/packages/dd-trace/src/priority_sampler.js +++ b/packages/dd-trace/src/priority_sampler.js @@ -37,13 +37,14 @@ const { const DEFAULT_KEY = 'service:,env:' /** - * Formats a sampling rate as a string with up to 6 significant digits and no trailing zeros. + * Formats a sampling rate as a string with up to 6 decimal digits and no trailing zeros. + * Rounds at the integer level to avoid IEEE 754 precision issues with toFixed. * * @param {number} rate * @returns {string} */ function formatKnuthRate (rate) { - return Number(rate.toPrecision(6)).toString() + return (Math.round(rate * 1e6) / 1e6).toFixed(6).replace(/\.?0+$/, '') } const defaultSampler = new Sampler(AUTO_KEEP) diff --git a/packages/dd-trace/test/priority_sampler.spec.js b/packages/dd-trace/test/priority_sampler.spec.js index 39bbf2f153e..4d28b6a9770 100644 --- a/packages/dd-trace/test/priority_sampler.spec.js +++ b/packages/dd-trace/test/priority_sampler.spec.js @@ -421,6 +421,45 @@ describe('PrioritySampler', () => { assert.strictEqual(typeof context._trace.tags[SAMPLING_KNUTH_RATE], 'string') }) + it('should format _dd.p.ksr with decimal notation for very small rates', () => { + Sampler.withArgs(0.000001).returns({ + isSampled: sinon.stub().returns(true), + rate: sinon.stub().returns(0.000001), + }) + prioritySampler = new PrioritySampler('test', { + sampleRate: 0.000001, + }) + prioritySampler.sample(span) + + assert.strictEqual(context._trace.tags[SAMPLING_KNUTH_RATE], '0.000001') + }) + + it('should round _dd.p.ksr to zero when rate is below 6 decimal precision', () => { + Sampler.withArgs(0.0000001).returns({ + isSampled: sinon.stub().returns(true), + rate: sinon.stub().returns(0.0000001), + }) + prioritySampler = new PrioritySampler('test', { + sampleRate: 0.0000001, + }) + prioritySampler.sample(span) + + assert.strictEqual(context._trace.tags[SAMPLING_KNUTH_RATE], '0') + }) + + it('should round _dd.p.ksr up to 0.000001 when rate rounds up', () => { + Sampler.withArgs(0.0000005).returns({ + isSampled: sinon.stub().returns(true), + rate: sinon.stub().returns(0.0000005), + }) + prioritySampler = new PrioritySampler('test', { + sampleRate: 0.0000005, + }) + prioritySampler.sample(span) + + assert.strictEqual(context._trace.tags[SAMPLING_KNUTH_RATE], '0.000001') + }) + it('should not set _dd.p.ksr tag for manual sampling', () => { context._tags[MANUAL_KEEP] = undefined From 0ee9c098f77a03f5ef5ea99a9203dbe94056ea07 Mon Sep 17 00:00:00 2001 From: bm1549 Date: Tue, 24 Mar 2026 09:20:28 -0400 Subject: [PATCH 2/5] fix(tracing): replace regex with loop in formatKnuthRate for hot-path perf MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Addresses PR review feedback: replaces the regex-based trailing-zero strip with a manual loop to avoid regex overhead on the hot path. Also adds Number() coercion as defensive measure per reviewer suggestion. Math.round pre-rounding is intentionally kept — toFixed(6) has imprecise rounding for sub-precision values in V8 (e.g. 0.0000005.toFixed(6) returns '0.000000' in Node 23). JSDoc updated to explain this. Co-Authored-By: Claude Sonnet 4.6 --- packages/dd-trace/src/priority_sampler.js | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/packages/dd-trace/src/priority_sampler.js b/packages/dd-trace/src/priority_sampler.js index e67c0aed7c8..2d32477e3cb 100644 --- a/packages/dd-trace/src/priority_sampler.js +++ b/packages/dd-trace/src/priority_sampler.js @@ -38,13 +38,19 @@ const DEFAULT_KEY = 'service:,env:' /** * Formats a sampling rate as a string with up to 6 decimal digits and no trailing zeros. - * Rounds at the integer level to avoid IEEE 754 precision issues with toFixed. + * Pre-rounds to 6 decimal places before calling toFixed to work around V8's imprecise + * rounding for sub-precision values (e.g. 0.0000005.toFixed(6) returns '0.000000'). * * @param {number} rate * @returns {string} */ function formatKnuthRate (rate) { - return (Math.round(rate * 1e6) / 1e6).toFixed(6).replace(/\.?0+$/, '') + const string = (Math.round(Number(rate) * 1e6) / 1e6).toFixed(6) + const dot = string.indexOf('.') + for (let i = string.length - 1; i > dot; i--) { + if (string[i] !== '0') return string.slice(0, i + 1) + } + return string.slice(0, dot) } const defaultSampler = new Sampler(AUTO_KEEP) From 338a44fbbf3e1fc2235e2e71317b28419c737202 Mon Sep 17 00:00:00 2001 From: bm1549 Date: Tue, 24 Mar 2026 10:46:02 -0400 Subject: [PATCH 3/5] fix(tracing): simplify formatKnuthRate loop per review feedback MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Drop Math.round pre-rounding — the edge case (0.0000005 rounding down in V8) is acceptable imprecision. Test updated to use 0.00000051 which rounds up unambiguously without Math.round. Drop indexOf('.') — check for '.' directly in the loop instead, which handles integer rates (0, 1) without the overhead of a separate scan. Co-Authored-By: Claude Sonnet 4.6 --- packages/dd-trace/src/priority_sampler.js | 10 ++++------ packages/dd-trace/test/priority_sampler.spec.js | 6 +++--- 2 files changed, 7 insertions(+), 9 deletions(-) diff --git a/packages/dd-trace/src/priority_sampler.js b/packages/dd-trace/src/priority_sampler.js index 2d32477e3cb..5a2e1d42626 100644 --- a/packages/dd-trace/src/priority_sampler.js +++ b/packages/dd-trace/src/priority_sampler.js @@ -38,19 +38,17 @@ const DEFAULT_KEY = 'service:,env:' /** * Formats a sampling rate as a string with up to 6 decimal digits and no trailing zeros. - * Pre-rounds to 6 decimal places before calling toFixed to work around V8's imprecise - * rounding for sub-precision values (e.g. 0.0000005.toFixed(6) returns '0.000000'). * * @param {number} rate * @returns {string} */ function formatKnuthRate (rate) { - const string = (Math.round(Number(rate) * 1e6) / 1e6).toFixed(6) - const dot = string.indexOf('.') - for (let i = string.length - 1; i > dot; i--) { + const string = Number(rate).toFixed(6) + for (let i = string.length - 1; i > 0; i--) { + if (string[i] === '.') return string.slice(0, i) if (string[i] !== '0') return string.slice(0, i + 1) } - return string.slice(0, dot) + return string } const defaultSampler = new Sampler(AUTO_KEEP) diff --git a/packages/dd-trace/test/priority_sampler.spec.js b/packages/dd-trace/test/priority_sampler.spec.js index 4d28b6a9770..7e2c821b012 100644 --- a/packages/dd-trace/test/priority_sampler.spec.js +++ b/packages/dd-trace/test/priority_sampler.spec.js @@ -448,12 +448,12 @@ describe('PrioritySampler', () => { }) it('should round _dd.p.ksr up to 0.000001 when rate rounds up', () => { - Sampler.withArgs(0.0000005).returns({ + Sampler.withArgs(0.00000051).returns({ isSampled: sinon.stub().returns(true), - rate: sinon.stub().returns(0.0000005), + rate: sinon.stub().returns(0.00000051), }) prioritySampler = new PrioritySampler('test', { - sampleRate: 0.0000005, + sampleRate: 0.00000051, }) prioritySampler.sample(span) From cab8813fdbfd1b5ae36e4772789c58a3e7226872 Mon Sep 17 00:00:00 2001 From: Brian Marks Date: Tue, 24 Mar 2026 11:04:10 -0400 Subject: [PATCH 4/5] Update packages/dd-trace/src/priority_sampler.js Co-authored-by: Ruben Bridgewater --- packages/dd-trace/src/priority_sampler.js | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/packages/dd-trace/src/priority_sampler.js b/packages/dd-trace/src/priority_sampler.js index 5a2e1d42626..bf6b4b5f541 100644 --- a/packages/dd-trace/src/priority_sampler.js +++ b/packages/dd-trace/src/priority_sampler.js @@ -45,10 +45,8 @@ const DEFAULT_KEY = 'service:,env:' function formatKnuthRate (rate) { const string = Number(rate).toFixed(6) for (let i = string.length - 1; i > 0; i--) { - if (string[i] === '.') return string.slice(0, i) - if (string[i] !== '0') return string.slice(0, i + 1) + if (string[i] !== '0') return string.slice(0, i + (string[i] !== '.' ? 1 : 0)) } - return string } const defaultSampler = new Sampler(AUTO_KEEP) From ec8b67278efcd0654e7f5315e54595124ce56070 Mon Sep 17 00:00:00 2001 From: bm1549 Date: Tue, 24 Mar 2026 17:55:57 -0400 Subject: [PATCH 5/5] fix(tracing): resolve lint errors in formatKnuthRate - Flip negated conditions to positive checks (unicorn/no-negated-condition) - Remove @returns tag since linter can't statically verify loop always returns (jsdoc/require-returns-check) - Preserve BridgeAR's single-check-per-iteration optimization Co-Authored-By: Claude Opus 4.6 (1M context) --- packages/dd-trace/src/priority_sampler.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/dd-trace/src/priority_sampler.js b/packages/dd-trace/src/priority_sampler.js index bf6b4b5f541..6edfc39c81a 100644 --- a/packages/dd-trace/src/priority_sampler.js +++ b/packages/dd-trace/src/priority_sampler.js @@ -40,12 +40,12 @@ const DEFAULT_KEY = 'service:,env:' * Formats a sampling rate as a string with up to 6 decimal digits and no trailing zeros. * * @param {number} rate - * @returns {string} */ function formatKnuthRate (rate) { const string = Number(rate).toFixed(6) for (let i = string.length - 1; i > 0; i--) { - if (string[i] !== '0') return string.slice(0, i + (string[i] !== '.' ? 1 : 0)) + if (string[i] === '0') continue + return string.slice(0, i + (string[i] === '.' ? 0 : 1)) } }