---
title: "Span Buffer"
url: https://develop.sentry.dev/sdk/telemetry/spans/span-buffer/
---

# Span Buffer

This document uses key words such as "MUST", "SHOULD", and "MAY" as defined in [RFC 2119](https://www.ietf.org/rfc/rfc2119.txt) to indicate requirement levels.

The span buffer is responsible for batching spans, constructing span envelopes and forwarding them to the transport. It is used in the [Span Streaming](https://develop.sentry.dev/sdk/telemetry/spans/implementation.md) flow, where spans are captured via `captureSpan` and enqueued into the span buffer instead of being sent as transaction events.

This page specifies requirements for the span buffer. It intentionally does not specify as much as the [Telemetry Processor](https://develop.sentry.dev/sdk/foundations/processing/telemetry-processor.md) page. SDKs MAY implement and extend the span buffer with platform-specific behaviour, as long as the core requirements adquately are met.

## [Span Buffer Requirements](https://develop.sentry.dev/sdk/telemetry/spans/span-buffer.md#span-buffer-requirements)

1. The buffer MUST [bucket spans by trace ID](https://develop.sentry.dev/sdk/telemetry/spans/span-buffer.md#buckets-per-trace-id). When flushing spans (i.e. forwarding span envelopes to the transport), the buffer MUST create distinct envelopes for each trace ID.

2. When creating span envelopes the buffer MUST NOT add more than 1000 spans to an envelope. If more than 1000 spans are currently held in memory, the buffer MUST batch the spans into multiple envelopes.

3. When the buffer drops spans, it MUST record a client report, containing the exact number of spans dropped.

4. For the time being, the buffer MAY ignore priority-based scheduling with other telemetry item categories.

5. The buffer MUST implement the following flushing behaviour:

   * Flush on a regular interval, every 5 seconds (SDKs MAY choose a different value based on platform-specific needs)
   * Flush a trace bucket when it reaches the 1000 spans limit
   * Flush when the trace bucket has reached a size of 5MB (SDKs MAY choose a different value based on platform-specific needs, but the value MUST NOT exceed 10MB)
   * Flush when `SentrySDK.flush()` is called
   * Flush and stop further flushes when `SentrySDK.close()` is called. The buffer MUST stop accepting new spans at this time to prevent infinite memory consumption.

### [Buckets per Trace ID](https://develop.sentry.dev/sdk/telemetry/spans/span-buffer.md#buckets-per-trace-id)

A recommended simple design is a map of **trace ID → list of spans** (buckets per trace). SDKs **MAY** use other structures (e.g. a fixed ring buffer) as long as the requirements above are met.

```bash
spanBuffer = {
  "trace-a": [span1, span2, span3],
  "trace-b": [span4],
  "trace-c": [span5, span6]
}
```

Requirements for buckets per trace ID:

1. When the span buffer adds a span, it **MUST** add it to the bucket for that span's trace ID.
2. When no bucket exists for that trace ID, the span buffer **MUST** create a new bucket.
3. After forwarding the spans in a bucket, the span buffer **MUST** remove all spans from that bucket and delete the bucket.

### [Serialization and Dynamic Sampling Context (DSC)](https://develop.sentry.dev/sdk/telemetry/spans/span-buffer.md#serialization-and-dynamic-sampling-context-dsc)

To ensure the best [Dynamic Sampling Context](https://develop.sentry.dev/sdk/foundations/trace-propagation/dynamic-sampling-context.md) (DSC) consistency, SDKs **SHOULD** materialize and freeze the DSC as late as possible. In practice:

* The span buffer **SHOULD** enqueue spans but only create the final envelope at **flush time**, not at enqueue time.
* At flush time, the span buffer **SHOULD** materialize and freeze the DSC on the segment span if not already done. That way the `trace` envelope header (e.g. for transaction names in the DSC) reflects the latest data.
