Skip to content

Commit b9f887d

Browse files
committed
fix(webapp): stop api inheriting inbound sampled traceparents so trace sampling applies
The internal APM tracer's ParentBasedSampler left remoteParentSampled at its AlwaysOn default, so any request arriving with a sampled traceparent (SDK task-run traces propagated in from running tasks) was recorded in full, ignoring INTERNAL_OTEL_TRACE_SAMPLING_RATE. On api that is ~99.6% of spans, so the divisor was effectively inert. Register a non-inheriting propagator: inject still delegates to W3C trace+baggage so outbound propagation is unchanged, but extract drops the parent span, so every inbound request roots its own trace and the ratio sampler applies uniformly. This also stops api stitching onto (and inflating) the SDK's task-run traces, which is where the untrimmable multi-thousand-span chains came from. Also set remoteParentSampled to the ratio sampler as a fallback.
1 parent 72f50c2 commit b9f887d

2 files changed

Lines changed: 40 additions & 2 deletions

File tree

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
---
2+
area: webapp
3+
type: improvement
4+
---
5+
6+
Reduced internal overhead on the API under high load.

apps/webapp/app/v3/tracer.server.ts

Lines changed: 34 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,15 @@ import {
1414
trace,
1515
metrics,
1616
type Meter,
17+
type TextMapPropagator,
18+
type TextMapGetter,
19+
type TextMapSetter,
1720
} from "@opentelemetry/api";
21+
import {
22+
CompositePropagator,
23+
W3CBaggagePropagator,
24+
W3CTraceContextPropagator,
25+
} from "@opentelemetry/core";
1826
import sentryRemix from "@sentry/remix";
1927
import { logs, SeverityNumber } from "@opentelemetry/api-logs";
2028
import { OTLPTraceExporter } from "@opentelemetry/exporter-trace-otlp-http";
@@ -125,6 +133,24 @@ class CustomWebappSampler implements Sampler {
125133
}
126134
}
127135

136+
class NonInheritingTraceContextPropagator implements TextMapPropagator {
137+
private readonly _delegate = new CompositePropagator({
138+
propagators: [new W3CTraceContextPropagator(), new W3CBaggagePropagator()],
139+
});
140+
141+
inject(context: Context, carrier: unknown, setter: TextMapSetter): void {
142+
this._delegate.inject(context, carrier, setter);
143+
}
144+
145+
extract(context: Context, carrier: unknown, getter: TextMapGetter): Context {
146+
return trace.deleteSpan(this._delegate.extract(context, carrier, getter));
147+
}
148+
149+
fields(): string[] {
150+
return this._delegate.fields();
151+
}
152+
}
153+
128154
export const {
129155
tracer,
130156
logger: otelLogger,
@@ -281,11 +307,14 @@ function setupTelemetry() {
281307
}
282308
}
283309

310+
const ratioSampler = new TraceIdRatioBasedSampler(samplingRate);
311+
284312
const provider = new NodeTracerProvider({
285313
forceFlushTimeoutMillis: 15_000,
286314
resource: getResource(),
287315
sampler: new ParentBasedSampler({
288-
root: new CustomWebappSampler(new TraceIdRatioBasedSampler(samplingRate)),
316+
root: new CustomWebappSampler(ratioSampler),
317+
remoteParentSampled: ratioSampler,
289318
}),
290319
spanLimits: {
291320
attributeCountLimit: 1024,
@@ -324,7 +353,10 @@ function setupTelemetry() {
324353
);
325354
}
326355

327-
provider.register({ contextManager: createContextManager() });
356+
provider.register({
357+
contextManager: createContextManager(),
358+
propagator: new NonInheritingTraceContextPropagator(),
359+
});
328360

329361
let instrumentations: Instrumentation[] = [
330362
new AwsSdkInstrumentation({

0 commit comments

Comments
 (0)