Skip to content

Commit 89bb6c3

Browse files
committed
perf(otlp): eliminate unnecessary SpanData clones in tonic and HTTP exporters
The tonic trace exporter deep-cloned every SpanData into a Vec on each export call, even though SpanBatch already provides borrowed access to the underlying slice. Replace Arc<Vec<SpanData>> with Arc<SpanBatch> to wrap the borrowed data directly (~free), matching the existing pattern used by the tonic logs exporter with Arc<LogBatch>. The HTTP trace exporter had the same unnecessary clone in build_trace_export_body — removed by using SpanBatch::as_slice() directly. Adds SpanBatch::as_slice() for callers needing &[SpanData].
1 parent 64d14d3 commit 89bb6c3

5 files changed

Lines changed: 10 additions & 6 deletions

File tree

opentelemetry-otlp/CHANGELOG.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
## vNext
44

5-
- Updated trace exporters to use borrowed `SpanBatch` interface, reducing allocations in the export path.
5+
- Updated trace exporters to use borrowed `SpanBatch` interface, eliminating unnecessary `SpanData` deep clones in tonic and HTTP trace exporters.
66
- Add `build()` directly on `SpanExporterBuilder`, `MetricExporterBuilder`, and `LogExporterBuilder`
77
(before selecting a transport), which auto-selects the transport based on the
88
`OTEL_EXPORTER_OTLP_PROTOCOL` environment variable or enabled features.

opentelemetry-otlp/src/exporter/http/mod.rs

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -587,8 +587,7 @@ impl OtlpHttpClient {
587587
spans: SpanBatch<'_>,
588588
) -> Result<(Vec<u8>, &'static str, Option<&'static str>), String> {
589589
use opentelemetry_proto::tonic::collector::trace::v1::ExportTraceServiceRequest;
590-
let span_data: Vec<_> = spans.iter().cloned().collect();
591-
let resource_spans = group_spans_by_resource_and_scope(&span_data, &self.resource);
590+
let resource_spans = group_spans_by_resource_and_scope(spans.as_slice(), &self.resource);
592591

593592
let req = ExportTraceServiceRequest { resource_spans };
594593
let (body, content_type) = match self.protocol {

opentelemetry-otlp/src/exporter/tonic/trace.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -72,7 +72,7 @@ impl TonicTracesClient {
7272

7373
impl SpanExporter for TonicTracesClient {
7474
async fn export(&self, batch: SpanBatch<'_>) -> OTelSdkResult {
75-
let batch: Arc<Vec<_>> = Arc::new(batch.iter().cloned().collect());
75+
let batch = Arc::new(batch);
7676

7777
match super::tonic_retry_with_backoff(
7878
#[cfg(feature = "experimental-grpc-retry")]
@@ -108,7 +108,7 @@ impl SpanExporter for TonicTracesClient {
108108
})?;
109109

110110
let resource_spans =
111-
group_spans_by_resource_and_scope(&batch_clone, &self.resource);
111+
group_spans_by_resource_and_scope(batch_clone.as_slice(), &self.resource);
112112

113113
otel_debug!(name: "TonicTracesClient.ExportStarted");
114114

opentelemetry-sdk/CHANGELOG.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
- **Breaking** The SDK `testing` feature is now runtime agnostic. [#3407][3407]
66
- `TokioSpanExporter` and `new_tokio_test_exporter` have been renamed to `TestSpanExporter` and `new_test_exporter`.
77
- The following transitive dependencies and features have been removed: `tokio/rt`, `tokio/time`, `tokio/macros`, `tokio/rt-multi-thread`, `tokio-stream`, `experimental_async_runtime`
8-
- **Breaking** `SpanExporter::export` now takes `SpanBatch<'_>` instead of `Vec<SpanData>`, matching the borrowed `LogBatch` pattern used in the logs pipeline. This eliminates per-export `Vec` allocations in `BatchSpanProcessor`. Implementors should update their `export` signatures and use `batch.iter()` to access spans.
8+
- **Breaking** `SpanExporter::export` now takes `SpanBatch<'_>` instead of `Vec<SpanData>`, matching the borrowed `LogBatch` pattern used in the logs pipeline. This eliminates per-export `Vec` allocations in `BatchSpanProcessor`. Implementors should update their `export` signatures and use `batch.iter()` or `batch.as_slice()` to access spans.
99
- Add 32-bit platform support by using `portable-atomic` for `AtomicI64` and `AtomicU64` in the metrics module. This enables compilation on 32-bit ARM targets (e.g., `armv5te-unknown-linux-gnueabi`, `armv7-unknown-linux-gnueabihf`).
1010
- `Aggregation` enum and `StreamBuilder::with_aggregation()` are now stable and no longer require the `spec_unstable_metrics_views` feature flag.
1111
- Fix `service.name` Resource attribute fallback to follow OpenTelemetry

opentelemetry-sdk/src/trace/export.rs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,11 @@ impl<'a> SpanBatch<'a> {
3030
pub fn iter(&self) -> impl Iterator<Item = &SpanData> {
3131
self.data.iter()
3232
}
33+
34+
/// Returns the underlying slice of span data.
35+
pub fn as_slice(&self) -> &'a [SpanData] {
36+
self.data
37+
}
3338
}
3439

3540
/// `SpanExporter` defines the interface that protocol-specific exporters must

0 commit comments

Comments
 (0)