Skip to content

Commit 9f965b0

Browse files
obecnyvmarchaud
andauthored
chore: batch processor, aligning with latest spec changes for environments variables (#1918)
Co-authored-by: Valentin Marchaud <[email protected]>
1 parent 06c7ec7 commit 9f965b0

File tree

5 files changed

+205
-97
lines changed

5 files changed

+205
-97
lines changed

packages/opentelemetry-core/src/utils/environment.ts

+8-4
Original file line numberDiff line numberDiff line change
@@ -23,8 +23,10 @@ const DEFAULT_LIST_SEPARATOR = ',';
2323
*/
2424

2525
const ENVIRONMENT_NUMBERS_KEYS = [
26-
'OTEL_BSP_MAX_BATCH_SIZE',
27-
'OTEL_BSP_SCHEDULE_DELAY_MILLIS',
26+
'OTEL_BSP_EXPORT_TIMEOUT',
27+
'OTEL_BSP_MAX_EXPORT_BATCH_SIZE',
28+
'OTEL_BSP_MAX_QUEUE_SIZE',
29+
'OTEL_BSP_SCHEDULE_DELAY',
2830
'OTEL_SAMPLING_PROBABILITY',
2931
'OTEL_SPAN_ATTRIBUTE_COUNT_LIMIT',
3032
'OTEL_SPAN_EVENT_COUNT_LIMIT',
@@ -92,8 +94,10 @@ export const DEFAULT_ENVIRONMENT: Required<ENVIRONMENT> = {
9294
OTEL_SPAN_ATTRIBUTE_COUNT_LIMIT: 1000,
9395
OTEL_SPAN_EVENT_COUNT_LIMIT: 1000,
9496
OTEL_SPAN_LINK_COUNT_LIMIT: 1000,
95-
OTEL_BSP_MAX_BATCH_SIZE: 512,
96-
OTEL_BSP_SCHEDULE_DELAY_MILLIS: 5000,
97+
OTEL_BSP_EXPORT_TIMEOUT: 30000,
98+
OTEL_BSP_MAX_EXPORT_BATCH_SIZE: 512,
99+
OTEL_BSP_MAX_QUEUE_SIZE: 2048,
100+
OTEL_BSP_SCHEDULE_DELAY: 5000,
97101
};
98102

99103
/**

packages/opentelemetry-core/test/utils/environment.test.ts

+4-4
Original file line numberDiff line numberDiff line change
@@ -80,8 +80,8 @@ describe('environment', () => {
8080
HOSTNAME: 'hostname',
8181
KUBERNETES_SERVICE_HOST: 'https://k8s.host/',
8282
NAMESPACE: 'namespace',
83-
OTEL_BSP_MAX_BATCH_SIZE: 40,
84-
OTEL_BSP_SCHEDULE_DELAY_MILLIS: 50,
83+
OTEL_BSP_MAX_EXPORT_BATCH_SIZE: 40,
84+
OTEL_BSP_SCHEDULE_DELAY: 50,
8585
OTEL_EXPORTER_JAEGER_AGENT_HOST: 'host.domain.com',
8686
OTEL_EXPORTER_JAEGER_ENDPOINT: 'https://example.com/endpoint',
8787
OTEL_EXPORTER_JAEGER_PASSWORD: 'secret',
@@ -121,8 +121,8 @@ describe('environment', () => {
121121
assert.strictEqual(env.CONTAINER_NAME, 'container-1');
122122
assert.strictEqual(env.KUBERNETES_SERVICE_HOST, 'https://k8s.host/');
123123
assert.strictEqual(env.OTEL_RESOURCE_ATTRIBUTES, '<attrs>');
124-
assert.strictEqual(env.OTEL_BSP_MAX_BATCH_SIZE, 40);
125-
assert.strictEqual(env.OTEL_BSP_SCHEDULE_DELAY_MILLIS, 50);
124+
assert.strictEqual(env.OTEL_BSP_MAX_EXPORT_BATCH_SIZE, 40);
125+
assert.strictEqual(env.OTEL_BSP_SCHEDULE_DELAY, 50);
126126
});
127127

128128
it('should match invalid values to closest valid equivalent', () => {

packages/opentelemetry-tracing/src/export/BatchSpanProcessor.ts

+78-33
Original file line numberDiff line numberDiff line change
@@ -32,8 +32,10 @@ import { SpanExporter } from './SpanExporter';
3232
* the SDK then pushes them to the exporter pipeline.
3333
*/
3434
export class BatchSpanProcessor implements SpanProcessor {
35-
private readonly _bufferSize: number;
36-
private readonly _bufferTimeout: number;
35+
private readonly _maxExportBatchSize: number;
36+
private readonly _maxQueueSize: number;
37+
private readonly _scheduledDelayMillis: number;
38+
private readonly _exportTimeoutMillis: number;
3739

3840
private _finishedSpans: ReadableSpan[] = [];
3941
private _timer: NodeJS.Timeout | undefined;
@@ -42,21 +44,29 @@ export class BatchSpanProcessor implements SpanProcessor {
4244

4345
constructor(private readonly _exporter: SpanExporter, config?: BufferConfig) {
4446
const env = getEnv();
45-
this._bufferSize =
46-
config && config.bufferSize
47-
? config.bufferSize
48-
: env.OTEL_BSP_MAX_BATCH_SIZE;
49-
this._bufferTimeout =
50-
config && typeof config.bufferTimeout === 'number'
51-
? config.bufferTimeout
52-
: env.OTEL_BSP_SCHEDULE_DELAY_MILLIS;
47+
this._maxExportBatchSize =
48+
typeof config?.maxExportBatchSize === 'number'
49+
? config.maxExportBatchSize
50+
: env.OTEL_BSP_MAX_EXPORT_BATCH_SIZE;
51+
this._maxQueueSize =
52+
typeof config?.maxQueueSize === 'number'
53+
? config?.maxQueueSize
54+
: env.OTEL_BSP_MAX_QUEUE_SIZE;
55+
this._scheduledDelayMillis =
56+
typeof config?.scheduledDelayMillis === 'number'
57+
? config?.scheduledDelayMillis
58+
: env.OTEL_BSP_SCHEDULE_DELAY;
59+
this._exportTimeoutMillis =
60+
typeof config?.exportTimeoutMillis === 'number'
61+
? config?.exportTimeoutMillis
62+
: env.OTEL_BSP_EXPORT_TIMEOUT;
5363
}
5464

5565
forceFlush(): Promise<void> {
5666
if (this._isShutdown) {
5767
return this._shuttingDownPromise;
5868
}
59-
return this._flush();
69+
return this._flushAll();
6070
}
6171

6272
// does nothing.
@@ -77,7 +87,7 @@ export class BatchSpanProcessor implements SpanProcessor {
7787
this._shuttingDownPromise = new Promise((resolve, reject) => {
7888
Promise.resolve()
7989
.then(() => {
80-
return this._flush();
90+
return this._flushAll();
8191
})
8292
.then(() => {
8393
return this._exporter.shutdown();
@@ -92,49 +102,84 @@ export class BatchSpanProcessor implements SpanProcessor {
92102

93103
/** Add a span in the buffer. */
94104
private _addToBuffer(span: ReadableSpan) {
105+
if (this._finishedSpans.length >= this._maxQueueSize) {
106+
// limit reached, drop span
107+
return;
108+
}
95109
this._finishedSpans.push(span);
96110
this._maybeStartTimer();
97-
if (this._finishedSpans.length > this._bufferSize) {
98-
this._flush().catch(e => {
99-
globalErrorHandler(e);
100-
});
101-
}
102111
}
103112

104-
/** Send the span data list to exporter */
105-
private _flush(): Promise<void> {
113+
/**
114+
* Send all spans to the exporter respecting the batch size limit
115+
* This function is used only on forceFlush or shutdown,
116+
* for all other cases _flush should be used
117+
* */
118+
private _flushAll(): Promise<void> {
119+
return new Promise((resolve, reject) => {
120+
const promises = [];
121+
// calculate number of batches
122+
const count = Math.ceil(
123+
this._finishedSpans.length / this._maxExportBatchSize
124+
);
125+
for (let i = 0, j = count; i < j; i++) {
126+
promises.push(this._flushOneBatch());
127+
}
128+
Promise.all(promises)
129+
.then(() => {
130+
resolve();
131+
})
132+
.catch(reject);
133+
});
134+
}
135+
136+
private _flushOneBatch(): Promise<void> {
106137
this._clearTimer();
107138
if (this._finishedSpans.length === 0) {
108139
return Promise.resolve();
109140
}
110141
return new Promise((resolve, reject) => {
142+
const timer = setTimeout(() => {
143+
// don't wait anymore for export, this way the next batch can start
144+
reject(new Error('Timeout'));
145+
}, this._exportTimeoutMillis);
111146
// prevent downstream exporter calls from generating spans
112147
context.with(suppressInstrumentation(context.active()), () => {
113148
// Reset the finished spans buffer here because the next invocations of the _flush method
114149
// could pass the same finished spans to the exporter if the buffer is cleared
115150
// outside of the execution of this callback.
116-
this._exporter.export(this._finishedSpans.splice(0), result => {
117-
if (result.code === ExportResultCode.SUCCESS) {
118-
resolve();
119-
} else {
120-
reject(
121-
result.error ??
122-
new Error('BatchSpanProcessor: span export failed')
123-
);
151+
this._exporter.export(
152+
this._finishedSpans.splice(0, this._maxExportBatchSize),
153+
result => {
154+
clearTimeout(timer);
155+
if (result.code === ExportResultCode.SUCCESS) {
156+
resolve();
157+
} else {
158+
reject(
159+
result.error ??
160+
new Error('BatchSpanProcessor: span export failed')
161+
);
162+
}
124163
}
125-
});
164+
);
126165
});
127166
});
128167
}
129168

130169
private _maybeStartTimer() {
131170
if (this._timer !== undefined) return;
132-
133171
this._timer = setTimeout(() => {
134-
this._flush().catch(e => {
135-
globalErrorHandler(e);
136-
});
137-
}, this._bufferTimeout);
172+
this._flushOneBatch()
173+
.catch(e => {
174+
globalErrorHandler(e);
175+
})
176+
.then(() => {
177+
if (this._finishedSpans.length > 0) {
178+
this._clearTimer();
179+
this._maybeStartTimer();
180+
}
181+
});
182+
}, this._scheduledDelayMillis);
138183
unrefTimer(this._timer);
139184
}
140185

packages/opentelemetry-tracing/src/types.ts

+15-4
Original file line numberDiff line numberDiff line change
@@ -75,8 +75,19 @@ export interface TraceParams {
7575

7676
/** Interface configuration for a buffer. */
7777
export interface BufferConfig {
78-
/** Maximum size of a buffer. */
79-
bufferSize?: number;
80-
/** Max time for a buffer can wait before being sent */
81-
bufferTimeout?: number;
78+
/** The maximum batch size of every export. It must be smaller or equal to
79+
* maxQueueSize. The default value is 512. */
80+
maxExportBatchSize?: number;
81+
82+
/** The delay interval in milliseconds between two consecutive exports.
83+
* The default value is 5000ms. */
84+
scheduledDelayMillis?: number;
85+
86+
/** How long the export can run before it is cancelled.
87+
* The default value is 30000ms */
88+
exportTimeoutMillis?: number;
89+
90+
/** The maximum queue size. After the size is reached spans are dropped.
91+
* The default value is 2048. */
92+
maxQueueSize?: number;
8293
}

0 commit comments

Comments
 (0)