SOFTWARE / SYSTEMS / AIEngineering news. Technical depth.
Architecture / 4 MIN READ

When should a distributed tracing span end for async and streaming work?

Choose span boundaries for queued, detached, retried, canceled, and streaming work without distorting latency or hiding the real failing step.

End the original span when the operation it owns is complete. For a request handler, that usually means the synchronous work and any subtasks the caller is actively waiting on. If the handler hands work to a queue, scheduler, or long-lived stream and returns before that later work finishes, end the request span at the handoff boundary and represent later execution with new spans, connected by propagated context or links rather than one span left open indefinitely. OpenTelemetry traces W3C Trace Context

What should a span actually own?

A useful tracing rule is simple: one span should represent one operation, with one owner and one completion condition. OpenTelemetry defines a span as a unit of work or operation and includes start and end timestamps, parentage, events, links, and status. OpenTelemetry traces OpenTelemetry Trace API

That means a request span should measure the work the request itself is responsible for. If your handler starts parallel database calls and waits for them before replying, those calls belong under the request span because they are still on the caller’s critical path. If your handler publishes a job and returns 202, the request is not responsible for the worker finishing later.

Where should a request span end for queue or background handoff?

End it when the handoff meets your code’s success condition: after the enqueue call succeeds, after the broker acknowledges the publish, or after your application has durably recorded the handoff. Record the handoff as attributes or events, such as destination, message or job ID, and publish outcome.

Then start a new span when a worker actually begins processing. Preserve correlation by propagating trace context in message metadata; Trace Context standardizes the propagation format for distributed tracing. W3C Trace Context

Use parent-child relationships when the later operation is still a direct continuation of the active execution. Use span links when the relationship is causal but no longer strict nesting, such as scheduled jobs, fan-out processing, dead-letter reprocessing, or retries started later. OpenTelemetry spans explicitly support links for that purpose. OpenTelemetry traces

Worked example: an upload API stores metadata, publishes a thumbnail job, and returns immediately. The HTTP span ends after storage and successful publish. A worker later creates thumbnail.generate when processing starts. If the job retries twice, create separate spans for each attempt and attach the shared job ID. Do not keep one giant span open across all attempts; it hides which attempt actually failed and inflates perceived request latency.

How should streaming and long-lived responses be traced?

Do not let a normal request span run for hours just because a websocket or server-sent event stream stays open. That makes request latency meaningless and buries useful spans under one connection-shaped block.

Instead, use a short setup span for negotiation and acceptance. After that, create spans for meaningful units of work: a delivered message, a batch flush, a periodic fetch, or a downstream computation triggered by stream activity. An optional connection-level span can still help diagnose churn, disconnects, or idle time, but it should complement per-unit spans, not replace them.

What breaks when span boundaries are wrong?

Two failure modes are common. First, spans that outlive the owned operation inflate latency and make dashboards blame the request path for work the user was not waiting on. Second, spans that end too early hide where errors, cancellations, or retries actually happened.

Messaging conventions exist for messaging spans, attributes, and names, which helps keep producer and consumer traces consistent across systems. OpenTelemetry messaging semantic conventions

Two follow-up questions:

Should detached work ever stay inside the original span?
Only if the caller is still blocked on it. If the response can complete before that work finishes, use a new span.

Should a retry reuse the first span?
No. A retry is a new execution attempt. Give it a new span and connect attempts with links, IDs, and retry attributes.

Decision checklist: identify who owns completion, what the caller is waiting for, where success becomes durable, whether later work changes user-visible latency, and which span an operator should open first to find the failure. Instrument that boundary explicitly next.

Reviewed: 2026-09-05.

SOURCES & REVIEW

Sources are linked throughout this guide. Product capabilities can change; consult the linked documentation for your deployment.

Read our editorial approach ↗