Skip to content

oss_14_processor_export_lock_timeout #12

Description

@ocelotl

Tracks span-processor export serialization + timeout (Linear OSS-14, findings T2/T3).

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.

Branch / worktree: oss_14_processor_export_lock_timeout

Linear issue: https://linear.app/dash0/issue/OSS-14/t2-t3-simplespanprocessor-concurrent-export-bsp-export-timeout-not

Activity

  1. ocelotl commented on Jul 22, 2026

    @ocelotl
    OwnerAuthor

    🔒 Internal (dash0) — not for upstream.

    Tracking for the span-processor export serialization + timeout fix.

  2. ocelotl commented on Jul 22, 2026

    @ocelotl
    OwnerAuthor

    📣 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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions