@@ -18,6 +18,12 @@ use crate::{ExportConfig, OTEL_EXPORTER_OTLP_ENDPOINT, OTEL_EXPORTER_OTLP_HEADER
18
18
19
19
#[ cfg( feature = "grpc-tonic" ) ]
20
20
use opentelemetry_sdk:: retry:: RetryPolicy ;
21
+ #[ cfg( feature = "grpc-retry" ) ]
22
+ use opentelemetry_sdk:: retry:: retry_with_backoff;
23
+ #[ cfg( feature = "grpc-tonic" ) ]
24
+ use opentelemetry_sdk:: runtime:: Runtime ;
25
+ #[ cfg( feature = "grpc-tonic" ) ]
26
+ use std:: future:: Future ;
21
27
22
28
#[ cfg( feature = "logs" ) ]
23
29
pub ( crate ) mod logs;
@@ -44,7 +50,7 @@ pub struct TonicConfig {
44
50
pub ( crate ) channel : Option < tonic:: transport:: Channel > ,
45
51
pub ( crate ) interceptor : Option < BoxInterceptor > ,
46
52
/// The retry policy to use for gRPC requests.
47
- #[ cfg( feature = "grpc-tonic " ) ]
53
+ #[ cfg( feature = "grpc-retry " ) ]
48
54
pub ( crate ) retry_policy : Option < RetryPolicy > ,
49
55
}
50
56
@@ -138,7 +144,7 @@ impl Default for TonicExporterBuilder {
138
144
compression : None ,
139
145
channel : Option :: default ( ) ,
140
146
interceptor : Option :: default ( ) ,
141
- #[ cfg( feature = "grpc-tonic " ) ]
147
+ #[ cfg( feature = "grpc-retry " ) ]
142
148
retry_policy : None ,
143
149
} ,
144
150
exporter_config : ExportConfig {
@@ -190,14 +196,14 @@ impl TonicExporterBuilder {
190
196
} ;
191
197
192
198
// Get retry policy before consuming self
193
- #[ cfg( feature = "grpc-tonic " ) ]
199
+ #[ cfg( feature = "grpc-retry " ) ]
194
200
let retry_policy = self . tonic_config . retry_policy . clone ( ) ;
195
201
196
202
// If a custom channel was provided, use that channel instead of creating one
197
203
if let Some ( channel) = self . tonic_config . channel {
198
204
return Ok ( ( channel, interceptor, compression,
199
- #[ cfg ( feature = "grpc-tonic " ) ] retry_policy,
200
- #[ cfg( not( feature = "grpc-tonic " ) ) ] None ) ) ;
205
+ #[ cfg ( feature = "grpc-retry " ) ] retry_policy,
206
+ #[ cfg( not( feature = "grpc-retry " ) ) ] None ) ) ;
201
207
}
202
208
203
209
let config = self . exporter_config ;
@@ -226,8 +232,8 @@ impl TonicExporterBuilder {
226
232
227
233
otel_debug ! ( name: "TonicChannelBuilt" , endpoint = endpoint_clone, timeout_in_millisecs = timeout. as_millis( ) , compression = format!( "{:?}" , compression) , headers = format!( "{:?}" , headers_for_logging) ) ;
228
234
Ok ( ( channel, interceptor, compression,
229
- #[ cfg ( feature = "grpc-tonic " ) ] retry_policy,
230
- #[ cfg( not( feature = "grpc-tonic " ) ) ] None ) )
235
+ #[ cfg ( feature = "grpc-retry " ) ] retry_policy,
236
+ #[ cfg( not( feature = "grpc-retry " ) ) ] None ) )
231
237
}
232
238
233
239
fn resolve_endpoint ( default_endpoint_var : & str , provided_endpoint : Option < String > ) -> String {
@@ -320,6 +326,40 @@ impl TonicExporterBuilder {
320
326
}
321
327
}
322
328
329
+ /// Wrapper for retry functionality in tonic exporters.
330
+ /// Provides a unified call path that either uses retry_with_backoff when grpc-retry
331
+ /// feature is enabled, or executes the operation once when it's not.
332
+ #[ cfg( feature = "grpc-tonic" ) ]
333
+ async fn tonic_retry_with_backoff < R , F , Fut , T > (
334
+ runtime : R ,
335
+ policy : RetryPolicy ,
336
+ classify_fn : fn ( & tonic:: Status ) -> opentelemetry_sdk:: retry:: RetryErrorType ,
337
+ operation_name : & ' static str ,
338
+ operation : F ,
339
+ ) -> Result < T , tonic:: Status >
340
+ where
341
+ R : Runtime ,
342
+ F : Fn ( ) -> Fut ,
343
+ Fut : Future < Output = Result < T , tonic:: Status > > ,
344
+ {
345
+ #[ cfg( feature = "grpc-retry" ) ]
346
+ {
347
+ retry_with_backoff ( runtime, policy, classify_fn, operation_name, operation) . await
348
+ }
349
+
350
+ #[ cfg( not( feature = "grpc-retry" ) ) ]
351
+ {
352
+ // When retry feature is not enabled, execute operation once
353
+ // avoid unused param warnings ...
354
+ let _ = runtime;
355
+ let _ = policy;
356
+ let _ = classify_fn;
357
+ let _ = operation_name;
358
+
359
+ operation ( ) . await
360
+ }
361
+ }
362
+
323
363
fn merge_metadata_with_headers_from_env (
324
364
metadata : MetadataMap ,
325
365
headers_from_env : HeaderMap ,
@@ -493,7 +533,7 @@ pub trait WithTonicConfig {
493
533
I : tonic:: service:: Interceptor + Clone + Send + Sync + ' static ;
494
534
495
535
/// Set the retry policy for gRPC requests.
496
- #[ cfg( feature = "grpc-tonic " ) ]
536
+ #[ cfg( feature = "grpc-retry " ) ]
497
537
fn with_retry_policy ( self , policy : RetryPolicy ) -> Self ;
498
538
}
499
539
@@ -537,7 +577,7 @@ impl<B: HasTonicConfig> WithTonicConfig for B {
537
577
self
538
578
}
539
579
540
- #[ cfg( feature = "grpc-tonic " ) ]
580
+ #[ cfg( feature = "grpc-retry " ) ]
541
581
fn with_retry_policy ( mut self , policy : RetryPolicy ) -> Self {
542
582
self . tonic_config ( ) . retry_policy = Some ( policy) ;
543
583
self
@@ -775,7 +815,7 @@ mod tests {
775
815
} ) ;
776
816
}
777
817
778
- #[ cfg( feature = "grpc-tonic " ) ]
818
+ #[ cfg( feature = "grpc-retry " ) ]
779
819
#[ test]
780
820
fn test_with_retry_policy ( ) {
781
821
use crate :: WithTonicConfig ;
@@ -798,7 +838,7 @@ mod tests {
798
838
assert_eq ! ( retry_policy. jitter_ms, 50 ) ;
799
839
}
800
840
801
- #[ cfg( feature = "grpc-tonic " ) ]
841
+ #[ cfg( feature = "grpc-retry " ) ]
802
842
#[ test]
803
843
fn test_default_retry_policy_when_none_configured ( ) {
804
844
// This test requires us to create a tonic client, but we can't easily do that without
0 commit comments