-
Notifications
You must be signed in to change notification settings - Fork 423
feat: TraceIdRatioBasedSampler and re-sync cross agent tests
#3501
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -6,6 +6,7 @@ | |
| const AdaptiveSampler = require('./adaptive-sampler') | ||
| const AlwaysOffSampler = require('./always-off-sampler') | ||
| const AlwaysOnSampler = require('./always-on-sampler') | ||
| const TraceIdRatioBasedSampler = require('./ratio-based-sampler') | ||
|
|
||
| /** | ||
| * A helper function for the Agent to determine what sampler | ||
|
|
@@ -14,7 +15,7 @@ const AlwaysOnSampler = require('./always-on-sampler') | |
| * @param {Agent} params.agent The New Relic agent instance. | ||
| * @param {object} params.config The entire agent config. | ||
| * @param {string} params.sampler The sampler type to use: 'root', 'remote_parent_sampled', or 'remote_parent_not_sampled'. | ||
| * @returns {Sampler} A Sampler e.g. AdaptiveSampler | ||
| * @returns {Sampler} A Sampler e.g. AdaptiveSampler or TraceIdRatioBasedSampler | ||
| */ | ||
| function determineSampler({ agent, config, sampler = 'root' }) { | ||
| const samplerDefinition = config?.distributed_tracing?.sampler?.[sampler] | ||
|
|
@@ -29,6 +30,16 @@ function determineSampler({ agent, config, sampler = 'root' }) { | |
| return new AlwaysOffSampler() | ||
| } | ||
|
|
||
| // Is it TraceIdRatioBased? | ||
| const ratioBasedSampler = samplerDefinition?.trace_id_ratio_based | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. this logic should live in the config. we shouldn't have to check if it's a valid ratio here.
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I agree that we shouldn't but it's there as kind of a fail safe |
||
| const validRatio = (typeof ratioBasedSampler?.ratio === 'number' && ratioBasedSampler.ratio >= 0 && ratioBasedSampler.ratio <= 1) | ||
| if (validRatio) { | ||
| return new TraceIdRatioBasedSampler({ | ||
| agent, | ||
| ratio: ratioBasedSampler.ratio | ||
| }) | ||
| } | ||
|
|
||
| // Our default ^.^ | ||
| // We'll ignore https://github.com/newrelic/node-newrelic/issues/3519 for now 0.o | ||
| return new AdaptiveSampler({ | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,83 @@ | ||
| /* | ||
| * Copyright 2025 New Relic Corporation. All rights reserved. | ||
| * SPDX-License-Identifier: Apache-2.0 | ||
| */ | ||
|
|
||
| 'use strict' | ||
| const Sampler = require('./sampler') | ||
|
|
||
| /** | ||
| * Trace ID ratio-based sampler adapted for New Relic agent use, | ||
| * based on OpenTelemetry's sampler of the same name. | ||
| */ | ||
| class TraceIdRatioBasedSampler extends Sampler { | ||
| /** | ||
| * @param {object} opts Sampler options. | ||
| * @param {Agent} opts.agent - The New Relic agent instance. | ||
| * @param {number} opts.ratio - The sampling ratio (0 to 1). | ||
| */ | ||
| constructor(opts) { | ||
| super() // no-op | ||
| if (!opts.agent) { | ||
| throw new Error('must have an agent instance') | ||
| } | ||
| this._ratio = this._normalize(opts.ratio) | ||
| this._upperBound = Math.floor(this._ratio * 0xffffffff) | ||
| } | ||
|
|
||
| applySamplingDecision({ transaction }) { | ||
| if (!transaction) return | ||
| // eslint-disable-next-line sonarjs/pseudo-random | ||
| const initPriority = Math.random() | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This is the sample logic as the adaptive, it's just that it calls its
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I agree, that was my first thought too. However the parameters passed into |
||
| transaction.sampled = this.shouldSample(transaction.traceId) | ||
| transaction.priority = transaction.sampled ? initPriority + 1 : initPriority | ||
| // Truncate the priority after potentially modifying it to avoid floating | ||
| // point errors. | ||
| transaction.priority = ((transaction.priority * 1e6) | 0) / 1e6 | ||
| } | ||
|
|
||
| /** | ||
| * | ||
| * @param {string} traceId transaction trace id | ||
| * @returns {boolean} whether to sample a transaction based on given trace id | ||
| */ | ||
| shouldSample(traceId) { | ||
| const accumulated = this._accumulate(traceId) | ||
| return accumulated <= this._upperBound | ||
| } | ||
|
|
||
| toString() { | ||
| return 'TraceIdRatioBasedSampler' | ||
| } | ||
|
|
||
| /** | ||
| * Normalizes the sampling ratio. | ||
| * @param {number} ratio The ratio provided by the config. | ||
| * @returns {number} The normalized ratio [0, 1]. | ||
| */ | ||
| _normalize(ratio) { | ||
| if (typeof ratio !== 'number' || isNaN(ratio)) { | ||
| return 0 | ||
| } | ||
| if (ratio >= 1) return 1 | ||
|
|
||
| return ratio <= 0 ? 0 : ratio | ||
| } | ||
|
|
||
| /** | ||
| * Accumulates a value from the trace ID. | ||
| * @param {string} traceId The trace ID to accumulate. | ||
| * @returns {number} The accumulated value. | ||
| */ | ||
| _accumulate(traceId) { | ||
| let accumulation = 0 | ||
| for (let i = 0; i < traceId.length / 8; i++) { | ||
| const pos = i * 8 | ||
| const part = parseInt(traceId.slice(pos, pos + 8), 16) | ||
| accumulation = (accumulation ^ part) >>> 0 | ||
| } | ||
| return accumulation | ||
| } | ||
| } | ||
|
|
||
| module.exports = TraceIdRatioBasedSampler | ||
Uh oh!
There was an error while loading. Please reload this page.