Skip to content

Commit 359a517

Browse files
authored
ref(node): Cache undici trace propagation decisions (#8136)
1 parent a036a63 commit 359a517

File tree

1 file changed

+37
-8
lines changed
  • packages/node/src/integrations/undici

1 file changed

+37
-8
lines changed

packages/node/src/integrations/undici/index.ts

Lines changed: 37 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import {
66
stringMatchesSomePattern,
77
stripUrlQueryAndFragment,
88
} from '@sentry/utils';
9+
import { LRUMap } from 'lru_map';
910

1011
import type { NodeClient } from '../../client';
1112
import { NODE_VERSION } from '../../nodeVersion';
@@ -29,7 +30,7 @@ export interface UndiciOptions {
2930
* Function determining whether or not to create spans to track outgoing requests to the given URL.
3031
* By default, spans will be created for all outgoing requests.
3132
*/
32-
shouldCreateSpanForRequest: (url: string) => boolean;
33+
shouldCreateSpanForRequest?: (url: string) => boolean;
3334
}
3435

3536
// Please note that you cannot use `console.log` to debug the callbacks registered to the `diagnostics_channel` API.
@@ -57,10 +58,13 @@ export class Undici implements Integration {
5758

5859
private readonly _options: UndiciOptions;
5960

61+
private readonly _createSpanUrlMap: LRUMap<string, boolean> = new LRUMap(100);
62+
private readonly _headersUrlMap: LRUMap<string, boolean> = new LRUMap(100);
63+
6064
public constructor(_options: Partial<UndiciOptions> = {}) {
6165
this._options = {
6266
breadcrumbs: _options.breadcrumbs === undefined ? true : _options.breadcrumbs,
63-
shouldCreateSpanForRequest: _options.shouldCreateSpanForRequest || (() => true),
67+
shouldCreateSpanForRequest: _options.shouldCreateSpanForRequest,
6468
};
6569
}
6670

@@ -85,6 +89,21 @@ export class Undici implements Integration {
8589
return;
8690
}
8791

92+
const shouldCreateSpan = (url: string): boolean => {
93+
if (this._options.shouldCreateSpanForRequest === undefined) {
94+
return true;
95+
}
96+
97+
const cachedDecision = this._createSpanUrlMap.get(url);
98+
if (cachedDecision !== undefined) {
99+
return cachedDecision;
100+
}
101+
102+
const decision = this._options.shouldCreateSpanForRequest(url);
103+
this._createSpanUrlMap.set(url, decision);
104+
return decision;
105+
};
106+
88107
// https://github.com/nodejs/undici/blob/e6fc80f809d1217814c044f52ed40ef13f21e43c/docs/api/DiagnosticsChannel.md
89108
ds.subscribe(ChannelName.RequestCreate, message => {
90109
const hub = getCurrentHub();
@@ -108,9 +127,8 @@ export class Undici implements Integration {
108127

109128
if (activeSpan && client) {
110129
const clientOptions = client.getOptions();
111-
const shouldCreateSpan = this._options.shouldCreateSpanForRequest(stringUrl);
112130

113-
if (shouldCreateSpan) {
131+
if (shouldCreateSpan(stringUrl)) {
114132
const method = request.method || 'GET';
115133
const data: Record<string, unknown> = {
116134
'http.method': method,
@@ -129,11 +147,22 @@ export class Undici implements Integration {
129147
});
130148
request.__sentry__ = span;
131149

132-
const shouldPropagate = clientOptions.tracePropagationTargets
133-
? stringMatchesSomePattern(stringUrl, clientOptions.tracePropagationTargets)
134-
: true;
150+
const shouldAttachTraceData = (url: string): boolean => {
151+
if (clientOptions.tracePropagationTargets === undefined) {
152+
return true;
153+
}
154+
155+
const cachedDecision = this._headersUrlMap.get(url);
156+
if (cachedDecision !== undefined) {
157+
return cachedDecision;
158+
}
159+
160+
const decision = stringMatchesSomePattern(url, clientOptions.tracePropagationTargets);
161+
this._headersUrlMap.set(url, decision);
162+
return decision;
163+
};
135164

136-
if (shouldPropagate) {
165+
if (shouldAttachTraceData(stringUrl)) {
137166
request.addHeader('sentry-trace', span.toTraceparent());
138167
if (span.transaction) {
139168
const dynamicSamplingContext = span.transaction.getDynamicSamplingContext();

0 commit comments

Comments
 (0)