Skip to content

Commit 4cff5c6

Browse files
authored
Span links stored as Vector instead of EvictedQueue (#1313)
1 parent 47881b2 commit 4cff5c6

File tree

15 files changed

+188
-43
lines changed

15 files changed

+188
-43
lines changed

opentelemetry-datadog/src/exporter/model/mod.rs

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -193,7 +193,11 @@ pub(crate) mod tests {
193193
trace::{SpanContext, SpanId, SpanKind, Status, TraceFlags, TraceId, TraceState},
194194
KeyValue,
195195
};
196-
use opentelemetry_sdk::{self, trace::EvictedQueue, InstrumentationLibrary, Resource};
196+
use opentelemetry_sdk::{
197+
self,
198+
trace::{EvictedQueue, SpanLinks},
199+
InstrumentationLibrary, Resource,
200+
};
197201
use std::borrow::Cow;
198202
use std::time::{Duration, SystemTime};
199203

@@ -216,7 +220,7 @@ pub(crate) mod tests {
216220
let capacity = 3;
217221
let attributes = vec![KeyValue::new("span.type", "web")];
218222
let events = EvictedQueue::new(capacity);
219-
let links = EvictedQueue::new(capacity);
223+
let links = SpanLinks::default();
220224
let resource = Resource::new(vec![KeyValue::new("host.name", "test")]);
221225

222226
trace::SpanData {

opentelemetry-jaeger/src/exporter/mod.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -102,7 +102,7 @@ impl SpanExporter for Exporter {
102102
}
103103
}
104104

105-
fn links_to_references(links: EvictedQueue<Link>) -> Option<Vec<jaeger::SpanRef>> {
105+
fn links_to_references(links: &[Link]) -> Option<Vec<jaeger::SpanRef>> {
106106
if !links.is_empty() {
107107
let refs = links
108108
.iter()
@@ -139,7 +139,7 @@ fn convert_otel_span_into_jaeger_span(span: SpanData, export_instrument_lib: boo
139139
span_id: i64::from_be_bytes(span.span_context.span_id().to_bytes()),
140140
parent_span_id: i64::from_be_bytes(span.parent_span_id.to_bytes()),
141141
operation_name: span.name.into_owned(),
142-
references: links_to_references(span.links),
142+
references: links_to_references(span.links.as_ref()),
143143
flags: span.span_context.trace_flags().to_u8() as i32,
144144
start_time: span
145145
.start_time

opentelemetry-proto/src/transform/trace.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -93,7 +93,7 @@ pub mod tonic {
9393
dropped_attributes_count: event.dropped_attributes_count,
9494
})
9595
.collect(),
96-
dropped_links_count: source_span.links.dropped_count(),
96+
dropped_links_count: source_span.links.dropped_count,
9797
links: source_span.links.into_iter().map(Into::into).collect(),
9898
status: Some(Status {
9999
code: status::StatusCode::from(&source_span.status).into(),
@@ -204,7 +204,7 @@ pub mod grpcio {
204204
dropped_attributes_count: event.dropped_attributes_count,
205205
})
206206
.collect(),
207-
dropped_links_count: source_span.links.dropped_count(),
207+
dropped_links_count: source_span.links.dropped_count,
208208
links: source_span.links.into_iter().map(Into::into).collect(),
209209
status: Some(Status {
210210
code: status::StatusCode::from(&source_span.status).into(),

opentelemetry-sdk/CHANGELOG.md

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,22 @@
22

33
## vNext
44

5+
### Changed
6+
7+
- **Breaking**
8+
[#1313](https://github.com/open-telemetry/opentelemetry-rust/issues/1313)
9+
Changes how Span links are stored to achieve performance gains. See below for
10+
details:
11+
12+
*Behavior Change*: When enforcing `max_links_per_span` from `SpanLimits`,
13+
links are kept in the first-come order. The previous "eviction" based approach
14+
is no longer performed.
15+
16+
*Breaking Change Affecting Exporter authors*:
17+
18+
`SpanData` now stores `links` as `SpanLinks` instead of `EvictedQueue` where
19+
`SpanLinks` is a struct with a `Vec` of links and `dropped_count`.
20+
521
## v0.21.0
622

723
### Added

opentelemetry-sdk/benches/batch_span_processor.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ use opentelemetry::trace::{
55
use opentelemetry_sdk::export::trace::SpanData;
66
use opentelemetry_sdk::runtime::Tokio;
77
use opentelemetry_sdk::testing::trace::NoopSpanExporter;
8-
use opentelemetry_sdk::trace::{BatchSpanProcessor, EvictedQueue, SpanProcessor};
8+
use opentelemetry_sdk::trace::{BatchSpanProcessor, EvictedQueue, SpanLinks, SpanProcessor};
99
use opentelemetry_sdk::Resource;
1010
use std::borrow::Cow;
1111
use std::sync::Arc;
@@ -30,7 +30,7 @@ fn get_span_data() -> Vec<SpanData> {
3030
attributes: Vec::new(),
3131
dropped_attributes_count: 0,
3232
events: EvictedQueue::new(12),
33-
links: EvictedQueue::new(12),
33+
links: SpanLinks::default(),
3434
status: Status::Unset,
3535
resource: Cow::Owned(Resource::empty()),
3636
instrumentation_lib: Default::default(),

opentelemetry-sdk/src/export/trace.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
//! Trace exporters
22
use crate::Resource;
33
use futures_util::future::BoxFuture;
4-
use opentelemetry::trace::{Event, Link, SpanContext, SpanId, SpanKind, Status, TraceError};
4+
use opentelemetry::trace::{Event, SpanContext, SpanId, SpanKind, Status, TraceError};
55
use opentelemetry::KeyValue;
66
use std::borrow::Cow;
77
use std::fmt::Debug;
@@ -89,7 +89,7 @@ pub struct SpanData {
8989
/// Span events
9090
pub events: crate::trace::EvictedQueue<Event>,
9191
/// Span Links
92-
pub links: crate::trace::EvictedQueue<Link>,
92+
pub links: crate::trace::SpanLinks,
9393
/// Span status
9494
pub status: Status,
9595
/// Resource contains attributes representing an entity that produced this span.

opentelemetry-sdk/src/testing/trace/mod.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ use crate::{
77
trace::{ExportResult, SpanData, SpanExporter},
88
ExportError,
99
},
10-
trace::{Config, EvictedQueue},
10+
trace::{Config, EvictedQueue, SpanLinks},
1111
InstrumentationLibrary,
1212
};
1313
use async_trait::async_trait;
@@ -37,7 +37,7 @@ pub fn new_test_export_span_data() -> SpanData {
3737
attributes: Vec::new(),
3838
dropped_attributes_count: 0,
3939
events: EvictedQueue::new(config.span_limits.max_events_per_span),
40-
links: EvictedQueue::new(config.span_limits.max_links_per_span),
40+
links: SpanLinks::default(),
4141
status: Status::Unset,
4242
resource: config.resource,
4343
instrumentation_lib: InstrumentationLibrary::default(),

opentelemetry-sdk/src/trace/links.rs

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
//! # Span Links
2+
3+
use std::ops::Deref;
4+
5+
use opentelemetry::trace::Link;
6+
/// Stores span links along with dropped count.
7+
#[derive(Clone, Debug, Default, PartialEq)]
8+
#[non_exhaustive]
9+
pub struct SpanLinks {
10+
/// The links stored as a vector. Could be empty if there are no links.
11+
pub links: Vec<Link>,
12+
/// The number of links dropped from the span.
13+
pub dropped_count: u32,
14+
}
15+
16+
impl Deref for SpanLinks {
17+
type Target = [Link];
18+
19+
fn deref(&self) -> &Self::Target {
20+
&self.links
21+
}
22+
}
23+
24+
impl IntoIterator for SpanLinks {
25+
type Item = Link;
26+
type IntoIter = std::vec::IntoIter<Self::Item>;
27+
28+
fn into_iter(self) -> Self::IntoIter {
29+
self.links.into_iter()
30+
}
31+
}

opentelemetry-sdk/src/trace/mod.rs

Lines changed: 51 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ mod config;
1010
mod evicted_hash_map;
1111
mod evicted_queue;
1212
mod id_generator;
13+
mod links;
1314
mod provider;
1415
mod sampler;
1516
mod span;
@@ -21,6 +22,7 @@ pub use config::{config, Config};
2122
pub use evicted_hash_map::EvictedHashMap;
2223
pub use evicted_queue::EvictedQueue;
2324
pub use id_generator::{aws::XrayIdGenerator, IdGenerator, RandomIdGenerator};
25+
pub use links::SpanLinks;
2426
pub use provider::{Builder, TracerProvider};
2527
pub use sampler::{Sampler, ShouldSample};
2628
pub use span::Span;
@@ -39,14 +41,19 @@ mod runtime_tests;
3941
#[cfg(all(test, feature = "testing"))]
4042
mod tests {
4143
use super::*;
42-
use crate::testing::trace::InMemorySpanExporterBuilder;
44+
use crate::{
45+
testing::trace::InMemorySpanExporterBuilder, trace::span_limit::DEFAULT_MAX_LINKS_PER_SPAN,
46+
};
4347
use opentelemetry::{
44-
trace::{Span, Tracer, TracerProvider as _},
48+
trace::{
49+
Link, Span, SpanBuilder, SpanContext, SpanId, TraceFlags, TraceId, Tracer,
50+
TracerProvider as _,
51+
},
4552
KeyValue,
4653
};
4754

4855
#[test]
49-
fn tracing_in_span() {
56+
fn in_span() {
5057
// Arrange
5158
let exporter = InMemorySpanExporterBuilder::new().build();
5259
let provider = TracerProvider::builder()
@@ -70,7 +77,7 @@ mod tests {
7077
}
7178

7279
#[test]
73-
fn tracing_tracer_start() {
80+
fn tracer_start() {
7481
// Arrange
7582
let exporter = InMemorySpanExporterBuilder::new().build();
7683
let provider = TracerProvider::builder()
@@ -93,4 +100,44 @@ mod tests {
93100
assert_eq!(span.name, "span_name");
94101
assert_eq!(span.instrumentation_lib.name, "test_tracer");
95102
}
103+
104+
#[test]
105+
fn exceed_span_links_limit() {
106+
// Arrange
107+
let exporter = InMemorySpanExporterBuilder::new().build();
108+
let provider = TracerProvider::builder()
109+
.with_span_processor(SimpleSpanProcessor::new(Box::new(exporter.clone())))
110+
.build();
111+
112+
// Act
113+
let tracer = provider.tracer("test_tracer");
114+
115+
let mut links = Vec::new();
116+
for _i in 0..(DEFAULT_MAX_LINKS_PER_SPAN * 2) {
117+
links.push(Link::new(
118+
SpanContext::new(
119+
TraceId::from_u128(12),
120+
SpanId::from_u64(12),
121+
TraceFlags::default(),
122+
false,
123+
Default::default(),
124+
),
125+
Vec::new(),
126+
))
127+
}
128+
129+
let span_builder = SpanBuilder::from_name("span_name").with_links(links);
130+
let mut span = tracer.build(span_builder);
131+
span.end();
132+
provider.force_flush();
133+
134+
// Assert
135+
let exported_spans = exporter
136+
.get_finished_spans()
137+
.expect("Spans are expected to be exported.");
138+
assert_eq!(exported_spans.len(), 1);
139+
let span = &exported_spans[0];
140+
assert_eq!(span.name, "span_name");
141+
assert_eq!(span.links.len(), DEFAULT_MAX_LINKS_PER_SPAN as usize);
142+
}
96143
}

opentelemetry-sdk/src/trace/span.rs

Lines changed: 38 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,7 @@ pub(crate) struct SpanData {
4444
/// Span events
4545
pub(crate) events: crate::trace::EvictedQueue<trace::Event>,
4646
/// Span Links
47-
pub(crate) links: crate::trace::EvictedQueue<trace::Link>,
47+
pub(crate) links: crate::trace::SpanLinks,
4848
/// Span status
4949
pub(crate) status: Status,
5050
}
@@ -252,8 +252,9 @@ mod tests {
252252
use crate::testing::trace::NoopSpanExporter;
253253
use crate::trace::span_limit::{
254254
DEFAULT_MAX_ATTRIBUTES_PER_EVENT, DEFAULT_MAX_ATTRIBUTES_PER_LINK,
255-
DEFAULT_MAX_ATTRIBUTES_PER_SPAN,
255+
DEFAULT_MAX_ATTRIBUTES_PER_SPAN, DEFAULT_MAX_LINKS_PER_SPAN,
256256
};
257+
use crate::trace::SpanLinks;
257258
use opentelemetry::trace::{Link, SpanBuilder, TraceFlags, TraceId, Tracer};
258259
use opentelemetry::{trace::Span as _, trace::TracerProvider, KeyValue};
259260
use std::time::Duration;
@@ -272,7 +273,7 @@ mod tests {
272273
attributes: Vec::new(),
273274
dropped_attributes_count: 0,
274275
events: crate::trace::EvictedQueue::new(config.span_limits.max_events_per_span),
275-
links: crate::trace::EvictedQueue::new(config.span_limits.max_links_per_span),
276+
links: SpanLinks::default(),
276277
status: Status::Unset,
277278
};
278279
(tracer, data)
@@ -610,11 +611,44 @@ mod tests {
610611
.clone()
611612
.expect("span data should not be empty as we already set it before")
612613
.links;
613-
let link_vec: Vec<_> = link_queue.iter().collect();
614+
let link_vec: Vec<_> = link_queue.links;
614615
let processed_link = link_vec.get(0).expect("should have at least one link");
615616
assert_eq!(processed_link.attributes.len(), 128);
616617
}
617618

619+
#[test]
620+
fn exceed_span_links_limit() {
621+
let exporter = NoopSpanExporter::new();
622+
let provider_builder =
623+
crate::trace::TracerProvider::builder().with_simple_exporter(exporter);
624+
let provider = provider_builder.build();
625+
let tracer = provider.tracer("opentelemetry-test");
626+
627+
let mut links = Vec::new();
628+
for _i in 0..(DEFAULT_MAX_LINKS_PER_SPAN * 2) {
629+
links.push(Link::new(
630+
SpanContext::new(
631+
TraceId::from_u128(12),
632+
SpanId::from_u64(12),
633+
TraceFlags::default(),
634+
false,
635+
Default::default(),
636+
),
637+
Vec::new(),
638+
))
639+
}
640+
641+
let span_builder = tracer.span_builder("test").with_links(links);
642+
let span = tracer.build(span_builder);
643+
let link_queue = span
644+
.data
645+
.clone()
646+
.expect("span data should not be empty as we already set it before")
647+
.links;
648+
let link_vec: Vec<_> = link_queue.links;
649+
assert_eq!(link_vec.len(), DEFAULT_MAX_LINKS_PER_SPAN as usize);
650+
}
651+
618652
#[test]
619653
fn test_span_exported_data() {
620654
let provider = crate::trace::TracerProvider::builder()

opentelemetry-sdk/src/trace/span_processor.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -721,7 +721,7 @@ mod tests {
721721
use crate::testing::trace::{
722722
new_test_export_span_data, new_test_exporter, new_tokio_test_exporter,
723723
};
724-
use crate::trace::{BatchConfig, EvictedQueue};
724+
use crate::trace::{BatchConfig, EvictedQueue, SpanLinks};
725725
use async_trait::async_trait;
726726
use opentelemetry::trace::{SpanContext, SpanId, SpanKind, Status};
727727
use std::fmt::Debug;
@@ -751,7 +751,7 @@ mod tests {
751751
attributes: Vec::new(),
752752
dropped_attributes_count: 0,
753753
events: EvictedQueue::new(0),
754-
links: EvictedQueue::new(0),
754+
links: SpanLinks::default(),
755755
status: Status::Unset,
756756
resource: Default::default(),
757757
instrumentation_lib: Default::default(),

0 commit comments

Comments
 (0)