11// Copyright 2024-Present Datadog, Inc. https://www.datadoghq.com/
22// SPDX-License-Identifier: Apache-2.0
33
4+ use std:: sync:: atomic:: { AtomicUsize , Ordering } ;
5+
46use crate :: trace_exporter:: agent_response:: {
57 AgentResponsePayloadVersion , DATADOG_RATES_PAYLOAD_VERSION ,
68} ;
@@ -16,6 +18,9 @@ use libdd_trace_utils::span::{v04::Span, TraceData};
1618use libdd_trace_utils:: trace_utils:: { self , TracerHeaderTags } ;
1719use libdd_trace_utils:: tracer_payload;
1820
21+ /// Minimal capacity of fresh buffers allocated to encode traces, in bytes.
22+ const MIN_BUFFER_CAPACITY : usize = 1024 ;
23+
1924/// Prepared traces payload ready for sending to the agent
2025pub ( super ) struct PreparedTracesPayload {
2126 /// Serialized msgpack payload
@@ -27,20 +32,18 @@ pub(super) struct PreparedTracesPayload {
2732}
2833
2934/// Trace serialization client for handling payload preparation
30- pub ( super ) struct TraceSerializer < ' a > {
35+ #[ derive( Debug ) ]
36+ pub ( super ) struct TraceSerializer {
37+ previous_serialised_len : AtomicUsize ,
3138 output_format : TraceExporterOutputFormat ,
32- agent_payload_response_version : Option < & ' a AgentResponsePayloadVersion > ,
3339}
3440
35- impl < ' a > TraceSerializer < ' a > {
41+ impl TraceSerializer {
3642 /// Create a new trace serializer
37- pub ( super ) fn new (
38- output_format : TraceExporterOutputFormat ,
39- agent_payload_response_version : Option < & ' a AgentResponsePayloadVersion > ,
40- ) -> Self {
43+ pub ( super ) fn new ( output_format : TraceExporterOutputFormat ) -> Self {
4144 Self {
45+ previous_serialised_len : AtomicUsize :: new ( MIN_BUFFER_CAPACITY ) ,
4246 output_format,
43- agent_payload_response_version,
4447 }
4548 }
4649
@@ -49,10 +52,12 @@ impl<'a> TraceSerializer<'a> {
4952 & self ,
5053 traces : Vec < Vec < Span < T > > > ,
5154 header_tags : TracerHeaderTags ,
55+ agent_payload_response_version : Option < & AgentResponsePayloadVersion > ,
5256 ) -> Result < PreparedTracesPayload , TraceExporterError > {
5357 let payload = self . collect_and_process_traces ( traces) ?;
5458 let chunks = payload. size ( ) ;
55- let headers = self . build_traces_headers ( header_tags, chunks) ;
59+ let headers =
60+ self . build_traces_headers ( header_tags, chunks, agent_payload_response_version) ;
5661 let mp_payload = self . serialize_payload ( & payload) ?;
5762
5863 Ok ( PreparedTracesPayload {
@@ -77,13 +82,18 @@ impl<'a> TraceSerializer<'a> {
7782 }
7883
7984 /// Build HTTP headers for traces request
80- fn build_traces_headers ( & self , header_tags : TracerHeaderTags , chunk_count : usize ) -> HeaderMap {
85+ fn build_traces_headers (
86+ & self ,
87+ header_tags : TracerHeaderTags ,
88+ chunk_count : usize ,
89+ agent_payload_response_version : Option < & AgentResponsePayloadVersion > ,
90+ ) -> HeaderMap {
8191 let mut headers: HeaderMap = header_tags. into ( ) ;
8292 headers. reserve ( 4 ) ;
8393 headers. insert ( DATADOG_SEND_REAL_HTTP_STATUS , HeaderValue :: from_static ( "1" ) ) ;
8494 headers. insert ( DATADOG_TRACE_COUNT , chunk_count. into ( ) ) ;
8595 headers. insert ( CONTENT_TYPE , APPLICATION_MSGPACK ) ;
86- if let Some ( agent_payload_response_version) = & self . agent_payload_response_version {
96+ if let Some ( agent_payload_response_version) = agent_payload_response_version {
8797 // should never fail, as the version should only contain visible ascii chars
8898 let _ = HeaderValue :: try_from ( agent_payload_response_version. header_value ( ) )
8999 . map ( |v| headers. insert ( DATADOG_RATES_PAYLOAD_VERSION , v) ) ;
@@ -96,12 +106,24 @@ impl<'a> TraceSerializer<'a> {
96106 & self ,
97107 payload : & tracer_payload:: TraceChunks < T > ,
98108 ) -> Result < Vec < u8 > , TraceExporterError > {
99- match payload {
100- tracer_payload:: TraceChunks :: V04 ( p) => Ok ( msgpack_encoder:: v04:: to_vec ( p) ) ,
109+ let capacity = self
110+ . previous_serialised_len
111+ . load ( Ordering :: Relaxed )
112+ . max ( MIN_BUFFER_CAPACITY ) ;
113+ let buff = match payload {
114+ tracer_payload:: TraceChunks :: V04 ( p) => {
115+ msgpack_encoder:: v04:: to_vec_with_capacity ( p, capacity as u32 )
116+ }
101117 tracer_payload:: TraceChunks :: V05 ( p) => {
102- rmp_serde:: to_vec ( p) . map_err ( TraceExporterError :: Serialization )
118+ let mut buff = Vec :: with_capacity ( capacity) ;
119+ rmp_serde:: encode:: write ( & mut buff, p)
120+ . map_err ( TraceExporterError :: Serialization ) ?;
121+ buff
103122 }
104- }
123+ } ;
124+ self . previous_serialised_len
125+ . store ( buff. len ( ) , Ordering :: Relaxed ) ;
126+ Ok ( buff)
105127 }
106128}
107129
@@ -146,30 +168,27 @@ mod tests {
146168
147169 #[ test]
148170 fn test_trace_serializer_new ( ) {
149- let serializer = TraceSerializer :: new ( TraceExporterOutputFormat :: V04 , None ) ;
171+ let serializer = TraceSerializer :: new ( TraceExporterOutputFormat :: V04 ) ;
150172 assert ! ( matches!(
151173 serializer. output_format,
152174 TraceExporterOutputFormat :: V04
153175 ) ) ;
154- assert ! ( serializer. agent_payload_response_version. is_none( ) ) ;
155176 }
156177
157178 #[ test]
158179 fn test_trace_serializer_new_with_agent_version ( ) {
159- let agent_version = AgentResponsePayloadVersion :: new ( ) ;
160- let serializer = TraceSerializer :: new ( TraceExporterOutputFormat :: V05 , Some ( & agent_version) ) ;
180+ let serializer = TraceSerializer :: new ( TraceExporterOutputFormat :: V05 ) ;
161181 assert ! ( matches!(
162182 serializer. output_format,
163183 TraceExporterOutputFormat :: V05
164184 ) ) ;
165- assert ! ( serializer. agent_payload_response_version. is_some( ) ) ;
166185 }
167186
168187 #[ test]
169188 fn test_build_traces_headers ( ) {
170- let serializer = TraceSerializer :: new ( TraceExporterOutputFormat :: V04 , None ) ;
189+ let serializer = TraceSerializer :: new ( TraceExporterOutputFormat :: V04 ) ;
171190 let header_tags = create_test_header_tags ( ) ;
172- let headers = serializer. build_traces_headers ( header_tags, 3 ) ;
191+ let headers = serializer. build_traces_headers ( header_tags, 3 , None ) ;
173192
174193 // Check basic headers are present
175194 assert_eq ! ( headers. get( DATADOG_SEND_REAL_HTTP_STATUS ) . unwrap( ) , "1" ) ;
@@ -197,9 +216,9 @@ mod tests {
197216 #[ test]
198217 fn test_build_traces_headers_with_agent_version ( ) {
199218 let agent_version = AgentResponsePayloadVersion :: new ( ) ;
200- let serializer = TraceSerializer :: new ( TraceExporterOutputFormat :: V04 , Some ( & agent_version ) ) ;
219+ let serializer = TraceSerializer :: new ( TraceExporterOutputFormat :: V04 ) ;
201220 let header_tags = create_test_header_tags ( ) ;
202- let headers = serializer. build_traces_headers ( header_tags, 2 ) ;
221+ let headers = serializer. build_traces_headers ( header_tags, 2 , Some ( & agent_version ) ) ;
203222
204223 // Check that agent payload version header is included
205224 assert ! ( headers. contains_key( DATADOG_RATES_PAYLOAD_VERSION ) ) ;
@@ -208,7 +227,7 @@ mod tests {
208227
209228 #[ test]
210229 fn test_collect_and_process_traces_v04 ( ) {
211- let serializer = TraceSerializer :: new ( TraceExporterOutputFormat :: V04 , None ) ;
230+ let serializer = TraceSerializer :: new ( TraceExporterOutputFormat :: V04 ) ;
212231 let traces = vec ! [ vec![ create_test_span( ) ] ] ;
213232
214233 let result = serializer. collect_and_process_traces ( traces) ;
@@ -221,7 +240,7 @@ mod tests {
221240
222241 #[ test]
223242 fn test_collect_and_process_traces_v05 ( ) {
224- let serializer = TraceSerializer :: new ( TraceExporterOutputFormat :: V05 , None ) ;
243+ let serializer = TraceSerializer :: new ( TraceExporterOutputFormat :: V05 ) ;
225244 let traces = vec ! [ vec![ create_test_span( ) ] ] ;
226245
227246 let result = serializer. collect_and_process_traces ( traces) ;
@@ -234,7 +253,7 @@ mod tests {
234253
235254 #[ test]
236255 fn test_collect_and_process_traces_multiple_chunks ( ) {
237- let serializer = TraceSerializer :: new ( TraceExporterOutputFormat :: V04 , None ) ;
256+ let serializer = TraceSerializer :: new ( TraceExporterOutputFormat :: V04 ) ;
238257 let traces = vec ! [
239258 vec![ create_test_span( ) ] ,
240259 vec![ create_test_span( ) , create_test_span( ) ] ,
@@ -250,7 +269,7 @@ mod tests {
250269
251270 #[ test]
252271 fn test_serialize_payload_v04 ( ) {
253- let serializer = TraceSerializer :: new ( TraceExporterOutputFormat :: V04 , None ) ;
272+ let serializer = TraceSerializer :: new ( TraceExporterOutputFormat :: V04 ) ;
254273 let original_traces = vec ! [ vec![ create_test_span( ) ] ] ;
255274 let payload = serializer
256275 . collect_and_process_traces ( original_traces. clone ( ) )
@@ -285,7 +304,7 @@ mod tests {
285304
286305 #[ test]
287306 fn test_serialize_payload_v05 ( ) {
288- let serializer = TraceSerializer :: new ( TraceExporterOutputFormat :: V05 , None ) ;
307+ let serializer = TraceSerializer :: new ( TraceExporterOutputFormat :: V05 ) ;
289308 let original_traces = vec ! [ vec![ create_test_span( ) ] ] ;
290309 let payload = serializer
291310 . collect_and_process_traces ( original_traces. clone ( ) )
@@ -320,14 +339,14 @@ mod tests {
320339
321340 #[ test]
322341 fn test_prepare_traces_payload_v04 ( ) {
323- let serializer = TraceSerializer :: new ( TraceExporterOutputFormat :: V04 , None ) ;
342+ let serializer = TraceSerializer :: new ( TraceExporterOutputFormat :: V04 ) ;
324343 let traces = vec ! [
325344 vec![ create_test_span( ) ] ,
326345 vec![ create_test_span( ) , create_test_span( ) ] ,
327346 ] ;
328347 let header_tags = create_test_header_tags ( ) ;
329348
330- let result = serializer. prepare_traces_payload ( traces, header_tags) ;
349+ let result = serializer. prepare_traces_payload ( traces, header_tags, None ) ;
331350 assert ! ( result. is_ok( ) ) ;
332351
333352 let prepared = result. unwrap ( ) ;
@@ -342,11 +361,11 @@ mod tests {
342361
343362 #[ test]
344363 fn test_prepare_traces_payload_v05 ( ) {
345- let serializer = TraceSerializer :: new ( TraceExporterOutputFormat :: V05 , None ) ;
364+ let serializer = TraceSerializer :: new ( TraceExporterOutputFormat :: V05 ) ;
346365 let traces = vec ! [ vec![ create_test_span( ) ] ] ;
347366 let header_tags = create_test_header_tags ( ) ;
348367
349- let result = serializer. prepare_traces_payload ( traces, header_tags) ;
368+ let result = serializer. prepare_traces_payload ( traces, header_tags, None ) ;
350369 assert ! ( result. is_ok( ) ) ;
351370
352371 let prepared = result. unwrap ( ) ;
@@ -358,11 +377,11 @@ mod tests {
358377 #[ test]
359378 fn test_prepare_traces_payload_with_agent_version ( ) {
360379 let agent_version = AgentResponsePayloadVersion :: new ( ) ;
361- let serializer = TraceSerializer :: new ( TraceExporterOutputFormat :: V04 , Some ( & agent_version ) ) ;
380+ let serializer = TraceSerializer :: new ( TraceExporterOutputFormat :: V04 ) ;
362381 let traces = vec ! [ vec![ create_test_span( ) ] ] ;
363382 let header_tags = create_test_header_tags ( ) ;
364383
365- let result = serializer. prepare_traces_payload ( traces, header_tags) ;
384+ let result = serializer. prepare_traces_payload ( traces, header_tags, Some ( & agent_version ) ) ;
366385 assert ! ( result. is_ok( ) ) ;
367386
368387 let prepared = result. unwrap ( ) ;
@@ -372,11 +391,11 @@ mod tests {
372391
373392 #[ test]
374393 fn test_prepare_traces_payload_empty_traces ( ) {
375- let serializer = TraceSerializer :: new ( TraceExporterOutputFormat :: V04 , None ) ;
394+ let serializer = TraceSerializer :: new ( TraceExporterOutputFormat :: V04 ) ;
376395 let traces: Vec < Vec < SpanBytes > > = vec ! [ ] ;
377396 let header_tags = create_test_header_tags ( ) ;
378397
379- let result = serializer. prepare_traces_payload ( traces, header_tags) ;
398+ let result = serializer. prepare_traces_payload ( traces, header_tags, None ) ;
380399 assert ! ( result. is_ok( ) ) ;
381400
382401 let prepared = result. unwrap ( ) ;
@@ -398,8 +417,8 @@ mod tests {
398417 ..Default :: default ( )
399418 } ;
400419
401- let serializer = TraceSerializer :: new ( TraceExporterOutputFormat :: V04 , None ) ;
402- let headers = serializer. build_traces_headers ( header_tags, 1 ) ;
420+ let serializer = TraceSerializer :: new ( TraceExporterOutputFormat :: V04 ) ;
421+ let headers = serializer. build_traces_headers ( header_tags, 1 , None ) ;
403422
404423 assert_eq ! ( headers. get( "datadog-meta-lang" ) . unwrap( ) , "python" ) ;
405424 assert_eq ! ( headers. get( "datadog-meta-lang-version" ) . unwrap( ) , "3.9.0" ) ;
0 commit comments