You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
T2:SimpleSpanProcessor.export() is not serialized; concurrent on_end calls invoke the exporter concurrently (spec forbids). Fix: guard with a lock.
T3: BSP export timeout (OTEL_BSP_EXPORT_TIMEOUT) is read but never enforced; a hung export blocks forever. Fix: enforce a deadline around the export call.
📣 Public-facing draft — to be opened upstream in open-telemetry/opentelemetry-python. No internal references; copy verbatim.
Title:SimpleSpanProcessor exports concurrently, and BatchSpanProcessor export timeout is never enforced
Two related span-processor conformance bugs in opentelemetry-sdk/.../trace/export/__init__.py and the shared batch processor.
T2 — SimpleSpanProcessor.export() is not serialized
SimpleSpanProcessor.on_end calls self.span_exporter.export((span,)) with no lock. Two threads ending sampled spans can therefore call export on the same exporter concurrently. The spec (specification/trace/sdk.md) states that Export MUST NOT be called concurrently for the same exporter — BatchSpanProcessor already serializes via an export lock, but SimpleSpanProcessor does not. Fix: guard the export call with a per-processor lock.
T3 — BatchSpanProcessor export timeout is never enforced
OTEL_BSP_EXPORT_TIMEOUT is read into configuration but never applied. The code carried an explicit "No way currently to pass timeout to export" comment, and a hung exporter blocks the batch worker (and force_flush) indefinitely. The spec states Export MUST NOT block indefinitely; Go and Java enforce a deadline. Fix: enforce the configured timeout as a real deadline around the export call so a hung exporter cannot block forever.
Notes / limitations
Exporters are synchronous and Python offers no way to cancel a running call. A sound approach is to run the export on a dedicated single-worker executor and await it with the configured timeout; on timeout, log, record the failure, and stop draining rather than blocking. The abandoned export keeps running on the executor thread (bounded, single-thread back-pressure), but the worker and force_flush are unblocked, which is the guarantee the spec requires. Care is needed to preserve the instrumentation-suppression context across the executor thread (Python contextvars are not copied into ThreadPoolExecutor workers), otherwise the exporter's own transport calls could generate telemetry that feeds back into the processor. Because the timeout lives in the shared batch processor, it also applies to BatchLogRecordProcessor.
Testing
Add tests that (a) prove SimpleSpanProcessor never invokes export concurrently under parallel on_end, (b) prove a hung export is bounded by the timeout rather than hanging, and (c) prove instrumentation suppression is active during the export call.
Activity
ocelotl commentedon Jul 22, 2026
🔒 Internal (dash0) — not for upstream.
Tracking for the span-processor export serialization + timeout fix.
oss_14_processor_export_lock_timeout1900fc9d8(T2+T3),07255d44f(suppression-context fix caught in validation)opentelemetry-sdk+ changelog; full SDK suite 834 passed (incl. new suppression regression test).ocelotl commentedon Jul 22, 2026
📣 Public-facing draft — to be opened upstream in
open-telemetry/opentelemetry-python. No internal references; copy verbatim.Title:
SimpleSpanProcessorexports concurrently, andBatchSpanProcessorexport timeout is never enforcedTwo related span-processor conformance bugs in
opentelemetry-sdk/.../trace/export/__init__.pyand the shared batch processor.T2 —
SimpleSpanProcessor.export()is not serializedSimpleSpanProcessor.on_endcallsself.span_exporter.export((span,))with no lock. Two threads ending sampled spans can therefore callexporton the same exporter concurrently. The spec (specification/trace/sdk.md) states that Export MUST NOT be called concurrently for the same exporter —BatchSpanProcessoralready serializes via an export lock, butSimpleSpanProcessordoes not.Fix: guard the
exportcall with a per-processor lock.T3 —
BatchSpanProcessorexport timeout is never enforcedOTEL_BSP_EXPORT_TIMEOUTis read into configuration but never applied. The code carried an explicit "No way currently to pass timeout to export" comment, and a hung exporter blocks the batch worker (andforce_flush) indefinitely. The spec states Export MUST NOT block indefinitely; Go and Java enforce a deadline.Fix: enforce the configured timeout as a real deadline around the export call so a hung exporter cannot block forever.
Notes / limitations
Exporters are synchronous and Python offers no way to cancel a running call. A sound approach is to run the export on a dedicated single-worker executor and await it with the configured timeout; on timeout, log, record the failure, and stop draining rather than blocking. The abandoned export keeps running on the executor thread (bounded, single-thread back-pressure), but the worker and
force_flushare unblocked, which is the guarantee the spec requires. Care is needed to preserve the instrumentation-suppression context across the executor thread (Pythoncontextvarsare not copied intoThreadPoolExecutorworkers), otherwise the exporter's own transport calls could generate telemetry that feeds back into the processor. Because the timeout lives in the shared batch processor, it also applies toBatchLogRecordProcessor.Testing
Add tests that (a) prove
SimpleSpanProcessornever invokesexportconcurrently under parallelon_end, (b) prove a hung export is bounded by the timeout rather than hanging, and (c) prove instrumentation suppression is active during the export call.