Skip to content

Commit 54afa6f

Browse files
authored
refactor(trace-utils): replace use_v05_format bool and remove infallible expect (#1946)
# What does this PR do? Replaces the `use_v05_format`: bool parameter in `collect_trace_chunks` with the existing TraceEncoding enum, and removes `#[allow(clippy::expect_used)] + .expect("infallible...")` in the v04 msgpack encoder. # Motivation Follow-up to [#1896](#1896), addressing review comments: - Passing a bare bool to `collect_trace_chunks` gave no context at the call site — you had to look up the function signature to understand what true/false meant. Using TraceEncoding makes the intent explicit. - The `collect_and_process_traces` match had a catch-all arm that computed a bool from the format — replaced with exhaustive V04/V05 arms. - The v04 encoder used `.expect("infallible...")` with a clippy allow to handle an error that can never occur. Replaced with `let _ = ...`, consistent with what was already done in the v1 encoder. # Additional Notes ⚠️ Should be merged after [#1896](#1896). # How to test the change? Existing tests. Co-authored-by: anais.raison <anais.raison@datadoghq.com>
1 parent be94f69 commit 54afa6f

7 files changed

Lines changed: 116 additions & 35 deletions

File tree

libdd-common/src/lib.rs

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -135,6 +135,40 @@ impl<T> RwLockExt<T> for RwLock<T> {
135135
}
136136
}
137137

138+
/// Extension trait that extracts the value from a `Result` whose error type is uninhabited.
139+
///
140+
/// The signature constrains callers at compile time: the method is only available when the
141+
/// error type is [`core::convert::Infallible`]. No panics — the compiler proves the `Err`
142+
/// arm unreachable from the type.
143+
///
144+
/// # Examples
145+
///
146+
/// ```
147+
/// use libdd_common::ResultInfallibleExt;
148+
/// use std::convert::Infallible;
149+
///
150+
/// let result: Result<i32, Infallible> = Ok(42);
151+
/// assert_eq!(result.unwrap_infallible(), 42);
152+
/// ```
153+
pub trait ResultInfallibleExt<T>: sealed::Sealed {
154+
fn unwrap_infallible(self) -> T;
155+
}
156+
157+
impl<T> ResultInfallibleExt<T> for Result<T, core::convert::Infallible> {
158+
#[inline(always)]
159+
fn unwrap_infallible(self) -> T {
160+
match self {
161+
Ok(value) => value,
162+
Err(never) => match never {},
163+
}
164+
}
165+
}
166+
167+
mod sealed {
168+
pub trait Sealed {}
169+
impl<T> Sealed for Result<T, core::convert::Infallible> {}
170+
}
171+
138172
pub mod header {
139173
#![allow(clippy::declare_interior_mutable_const)]
140174
use http::{header::HeaderName, HeaderValue};

libdd-data-pipeline/src/trace_exporter/trace_serializer.rs

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ use libdd_trace_utils::msgpack_encoder;
1717
use libdd_trace_utils::span::{v04::Span, TraceData};
1818
use libdd_trace_utils::trace_utils::{self, TracerHeaderTags};
1919
use libdd_trace_utils::tracer_metadata::TracerMetadata;
20-
use libdd_trace_utils::tracer_payload;
20+
use libdd_trace_utils::tracer_payload::{self, TraceEncoding};
2121

2222
/// Minimal capacity of fresh buffers allocated to encode traces, in bytes.
2323
const MIN_BUFFER_CAPACITY: usize = 1024;
@@ -76,9 +76,13 @@ impl TraceSerializer {
7676
) -> Result<tracer_payload::TraceChunks<T>, TraceExporterError> {
7777
match self.output_format {
7878
TraceExporterOutputFormat::V1 => Ok(tracer_payload::TraceChunks::V1(traces)),
79-
format => {
80-
let use_v05_format = matches!(format, TraceExporterOutputFormat::V05);
81-
trace_utils::collect_trace_chunks(traces, use_v05_format).map_err(|e| {
79+
TraceExporterOutputFormat::V04 => {
80+
trace_utils::collect_trace_chunks(traces, TraceEncoding::V04).map_err(|e| {
81+
TraceExporterError::Deserialization(DecodeError::InvalidFormat(e.to_string()))
82+
})
83+
}
84+
TraceExporterOutputFormat::V05 => {
85+
trace_utils::collect_trace_chunks(traces, TraceEncoding::V05).map_err(|e| {
8286
TraceExporterError::Deserialization(DecodeError::InvalidFormat(e.to_string()))
8387
})
8488
}

libdd-trace-utils/src/msgpack_encoder/mod.rs

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,21 @@
44
pub mod v04;
55
pub mod v1;
66

7+
use rmp::encode::ValueWriteError;
8+
use std::convert::Infallible;
9+
10+
/// Flatten `ValueWriteError<Infallible>` (uninhabited because both variants wrap
11+
/// `Infallible`) into the bare `Infallible` so callers can use
12+
/// [`libdd_common::ResultInfallibleExt`].
13+
#[inline(always)]
14+
pub(crate) fn flatten_value_write_infallible(err: ValueWriteError<Infallible>) -> Infallible {
15+
match err {
16+
ValueWriteError::InvalidMarkerWrite(never) | ValueWriteError::InvalidDataWrite(never) => {
17+
never
18+
}
19+
}
20+
}
21+
722
/// A writer that counts bytes without storing them, used to compute encoded payload size.
823
pub(crate) struct CountLength(u32);
924

libdd-trace-utils/src/msgpack_encoder/v04/mod.rs

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33

44
use crate::span::v04::Span;
55
use crate::span::TraceData;
6+
use libdd_common::ResultInfallibleExt;
67
use rmp::encode::{write_array_len, ByteBuf, RmpWrite, ValueWriteError};
78

89
mod span;
@@ -123,8 +124,9 @@ pub fn to_vec_with_capacity<T: TraceData, S: AsRef<[Span<T>]>>(
123124
capacity: u32,
124125
) -> Vec<u8> {
125126
let mut buf = ByteBuf::with_capacity(capacity as usize);
126-
#[allow(clippy::expect_used)]
127-
to_writer(&mut buf, traces).expect("infallible: the error is std::convert::Infallible");
127+
to_writer(&mut buf, traces)
128+
.map_err(super::flatten_value_write_infallible)
129+
.unwrap_infallible();
128130
buf.into_vec()
129131
}
130132

@@ -158,7 +160,11 @@ pub fn to_vec_with_capacity<T: TraceData, S: AsRef<[Span<T>]>>(
158160
/// ```
159161
pub fn to_encoded_byte_len<T: TraceData, S: AsRef<[Span<T>]>>(traces: &[S]) -> u32 {
160162
let mut counter = super::CountLength(0);
161-
#[allow(clippy::expect_used)]
162-
to_writer(&mut counter, traces).expect("infallible: CountLength never fails");
163+
// `CountLength` impls `std::io::Write` (whose error type is `std::io::Error`, not
164+
// `Infallible`), so we can't statically prove infallibility via `unwrap_infallible`
165+
// the way we do for `ByteBuf`. In practice `CountLength::write*` only ever return
166+
// `Ok`, so the error path here is unreachable today; should `CountLength` ever grow
167+
// a fallible code path, fuzz tests on the msgpack encoded length would catch it.
168+
let _ = to_writer(&mut counter, traces);
163169
counter.0
164170
}

libdd-trace-utils/src/msgpack_encoder/v1/mod.rs

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ mod span_v04;
66
use crate::span::v04::Span;
77
use crate::span::TraceData;
88
use crate::tracer_metadata::TracerMetadata;
9+
use libdd_common::ResultInfallibleExt;
910
use rmp::encode::{
1011
write_array_len, write_bin, write_map_len, write_sint, write_str, write_uint, write_uint8,
1112
ByteBuf, RmpWrite, ValueWriteError,
@@ -399,7 +400,9 @@ pub fn to_vec_with_capacity<T: TraceData, S: AsRef<[Span<T>]>>(
399400
metadata: &TracerMetadata,
400401
) -> Vec<u8> {
401402
let mut buf = ByteBuf::with_capacity(capacity as usize);
402-
let _ = encode_payload(&mut buf, traces, metadata); // infallible: ByteBuf write never fails
403+
encode_payload(&mut buf, traces, metadata)
404+
.map_err(super::flatten_value_write_infallible)
405+
.unwrap_infallible();
403406
buf.into_vec()
404407
}
405408

@@ -409,7 +412,12 @@ pub fn to_encoded_byte_len<T: TraceData, S: AsRef<[Span<T>]>>(
409412
metadata: &TracerMetadata,
410413
) -> u32 {
411414
let mut counter = super::CountLength(0);
412-
let _ = encode_payload(&mut counter, traces, metadata); // infallible: CountLength write never fails
415+
// `CountLength` impls `std::io::Write` (whose error type is `std::io::Error`, not
416+
// `Infallible`), so we can't statically prove infallibility via `unwrap_infallible`
417+
// the way we do for `ByteBuf`. In practice `CountLength::write*` only ever return
418+
// `Ok`, so the error path here is unreachable today; should `CountLength` ever grow
419+
// a fallible code path, fuzz tests on the msgpack encoded length would catch it.
420+
let _ = encode_payload(&mut counter, traces, metadata);
413421
counter.0
414422
}
415423

libdd-trace-utils/src/trace_utils.rs

Lines changed: 37 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ use crate::span::v05::dict::SharedDict;
77
use crate::span::{v05, TraceData};
88
pub use crate::tracer_header_tags::TracerHeaderTags;
99
use crate::tracer_payload::TracerPayloadCollection;
10-
use crate::tracer_payload::{self, TraceChunks};
10+
use crate::tracer_payload::{self, TraceChunks, TraceEncoding};
1111
use anyhow::anyhow;
1212
use bytes::buf::Reader;
1313
use bytes::Buf;
@@ -592,26 +592,22 @@ pub fn enrich_span_with_azure_function_metadata(span: &mut pb::Span) {
592592

593593
pub fn collect_trace_chunks<T: TraceData>(
594594
traces: Vec<Vec<crate::span::v04::Span<T>>>,
595-
use_v05_format: bool,
595+
format: TraceEncoding,
596596
) -> anyhow::Result<TraceChunks<T>> {
597-
if use_v05_format {
598-
let mut shared_dict = SharedDict::default();
599-
let mut v05_traces: Vec<Vec<v05::Span>> = Vec::with_capacity(traces.len());
600-
for trace in traces {
601-
let trace_len = trace.len();
602-
let v05_trace = trace.into_iter().try_fold(
603-
Vec::with_capacity(trace_len),
604-
|mut acc, span| -> anyhow::Result<Vec<v05::Span>> {
605-
acc.push(v05::from_v04_span(span, &mut shared_dict)?);
606-
Ok(acc)
607-
},
608-
)?;
609-
610-
v05_traces.push(v05_trace);
597+
match format {
598+
TraceEncoding::V05 => {
599+
let mut shared_dict = SharedDict::default();
600+
let mut v05_traces: Vec<Vec<v05::Span>> = Vec::with_capacity(traces.len());
601+
for trace in traces {
602+
let v05_trace = trace
603+
.into_iter()
604+
.map(|span| v05::from_v04_span(span, &mut shared_dict))
605+
.collect::<anyhow::Result<Vec<_>>>()?;
606+
v05_traces.push(v05_trace);
607+
}
608+
Ok(TraceChunks::V05((shared_dict, v05_traces)))
611609
}
612-
Ok(TraceChunks::V05((shared_dict, v05_traces)))
613-
} else {
614-
Ok(TraceChunks::V04(traces))
610+
TraceEncoding::V04 => Ok(TraceChunks::V04(traces)),
615611
}
616612
}
617613

@@ -1132,7 +1128,7 @@ mod tests {
11321128
fn test_collect_trace_chunks_v05() {
11331129
let chunk = vec![create_test_no_alloc_span(123, 456, 789, 1, true)];
11341130

1135-
let collection = collect_trace_chunks(vec![chunk], true).unwrap();
1131+
let collection = collect_trace_chunks(vec![chunk], TraceEncoding::V05).unwrap();
11361132

11371133
let (dict, traces) = match collection {
11381134
TraceChunks::V05(payload) => payload,
@@ -1203,6 +1199,27 @@ mod tests {
12031199
);
12041200
}
12051201

1202+
#[test]
1203+
fn test_collect_trace_chunks_v04() {
1204+
let chunk = vec![create_test_no_alloc_span(123, 456, 789, 1, true)];
1205+
1206+
let collection = collect_trace_chunks(vec![chunk], TraceEncoding::V04).unwrap();
1207+
1208+
let traces = match collection {
1209+
TraceChunks::V04(traces) => traces,
1210+
_ => panic!("Unexpected type"),
1211+
};
1212+
1213+
assert_eq!(traces.len(), 1);
1214+
assert_eq!(traces[0].len(), 1);
1215+
let span = &traces[0][0];
1216+
assert_eq!(span.trace_id, 123);
1217+
assert_eq!(span.span_id, 456);
1218+
assert_eq!(span.parent_id, 789);
1219+
assert_eq!(span.start, 1);
1220+
assert_eq!(span.error, 0);
1221+
}
1222+
12061223
#[test]
12071224
fn test_rmp_serde_deserialize_meta_with_null_values() {
12081225
// Create a JSON representation with null value in meta

libdd-trace-utils/src/tracer_payload.rs

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ use std::iter::Iterator;
1212
pub type TracerPayloadV04 = Vec<v04::SpanBytes>;
1313
pub type TracerPayloadV05 = Vec<v05::Span>;
1414

15-
#[derive(Debug, Clone)]
15+
#[derive(Debug, Clone, Copy)]
1616
/// Enumerates the different encoding types.
1717
pub enum TraceEncoding {
1818
/// v0.4 encoding (TracerPayloadV04).
@@ -234,10 +234,7 @@ pub fn decode_to_trace_chunks(
234234
}
235235
.map_err(|e| anyhow::format_err!("Error deserializing trace from request body: {e}"))?;
236236

237-
Ok((
238-
collect_trace_chunks(data, matches!(encoding_type, TraceEncoding::V05))?,
239-
size,
240-
))
237+
Ok((collect_trace_chunks(data, encoding_type)?, size))
241238
}
242239

243240
#[cfg(test)]

0 commit comments

Comments
 (0)