11use crate :: {
2- raw:: IsUserLongPoll , Client , IsWorkerTaskLongPoll , NamespacedClient , Result , RetryConfig ,
2+ raw:: IsUserLongPoll , Client , IsWorkerTaskLongPoll , NamespacedClient , NoRetryOnMatching , Result ,
3+ RetryConfig ,
34} ;
45use backoff:: { backoff:: Backoff , exponential:: ExponentialBackoff , Clock , SystemClock } ;
56use futures_retry:: { ErrorHandler , FutureRetry , RetryPolicy } ;
@@ -58,24 +59,27 @@ impl<SG> RetryClient<SG> {
5859 request : Option < & Request < R > > ,
5960 ) -> CallInfo {
6061 let mut call_type = CallType :: Normal ;
62+ let mut retry_short_circuit = None ;
6163 if let Some ( r) = request. as_ref ( ) {
6264 let ext = r. extensions ( ) ;
6365 if ext. get :: < IsUserLongPoll > ( ) . is_some ( ) {
6466 call_type = CallType :: UserLongPoll ;
6567 } else if ext. get :: < IsWorkerTaskLongPoll > ( ) . is_some ( ) {
6668 call_type = CallType :: TaskLongPoll ;
6769 }
70+
71+ retry_short_circuit = ext. get :: < NoRetryOnMatching > ( ) . cloned ( ) ;
6872 }
6973 let retry_cfg = if call_type == CallType :: TaskLongPoll {
7074 RetryConfig :: task_poll_retry_policy ( )
7175 } else {
7276 ( * self . retry_config ) . clone ( )
7377 } ;
74- // TODO: Set retry short-circuits
7578 CallInfo {
7679 call_type,
7780 call_name,
7881 retry_cfg,
82+ retry_short_circuit,
7983 }
8084 }
8185
@@ -112,6 +116,7 @@ pub(crate) struct TonicErrorHandler<C: Clock> {
112116 call_type : CallType ,
113117 call_name : & ' static str ,
114118 have_retried_goaway_cancel : bool ,
119+ retry_short_circuit : Option < NoRetryOnMatching > ,
115120}
116121impl TonicErrorHandler < SystemClock > {
117122 fn new ( call_info : CallInfo , throttle_cfg : RetryConfig ) -> Self {
@@ -140,6 +145,7 @@ where
140145 backoff : call_info. retry_cfg . into_exp_backoff ( clock) ,
141146 throttle_backoff : throttle_cfg. into_exp_backoff ( throttle_clock) ,
142147 have_retried_goaway_cancel : false ,
148+ retry_short_circuit : call_info. retry_short_circuit ,
143149 }
144150 }
145151
@@ -165,11 +171,12 @@ where
165171 }
166172}
167173
168- #[ derive( Clone , Debug , PartialEq ) ]
174+ #[ derive( Clone , Debug ) ]
169175pub ( crate ) struct CallInfo {
170176 pub call_type : CallType ,
171177 call_name : & ' static str ,
172178 retry_cfg : RetryConfig ,
179+ retry_short_circuit : Option < NoRetryOnMatching > ,
173180}
174181
175182#[ doc( hidden) ]
@@ -200,6 +207,12 @@ where
200207 return RetryPolicy :: ForwardError ( e) ;
201208 }
202209
210+ if let Some ( sc) = self . retry_short_circuit . as_ref ( ) {
211+ if ( sc. predicate ) ( & e) {
212+ return RetryPolicy :: ForwardError ( e) ;
213+ }
214+ }
215+
203216 // Task polls are OK with being cancelled or running into the timeout because there's
204217 // nothing to do but retry anyway
205218 let long_poll_allowed = self . call_type == CallType :: TaskLongPoll
@@ -306,6 +319,7 @@ mod tests {
306319 call_type : CallType :: TaskLongPoll ,
307320 call_name,
308321 retry_cfg : TEST_RETRY_CONFIG ,
322+ retry_short_circuit : None ,
309323 } ,
310324 TEST_RETRY_CONFIG ,
311325 FixedClock ( Instant :: now ( ) ) ,
@@ -333,6 +347,7 @@ mod tests {
333347 call_type : CallType :: TaskLongPoll ,
334348 call_name,
335349 retry_cfg : TEST_RETRY_CONFIG ,
350+ retry_short_circuit : None ,
336351 } ,
337352 TEST_RETRY_CONFIG ,
338353 FixedClock ( Instant :: now ( ) ) ,
@@ -358,6 +373,7 @@ mod tests {
358373 call_type : CallType :: TaskLongPoll ,
359374 call_name : POLL_WORKFLOW_METH_NAME ,
360375 retry_cfg : TEST_RETRY_CONFIG ,
376+ retry_short_circuit : None ,
361377 } ,
362378 RetryConfig {
363379 initial_interval : Duration :: from_millis ( 2 ) ,
@@ -388,6 +404,25 @@ mod tests {
388404 }
389405 }
390406
407+ #[ tokio:: test]
408+ async fn retry_short_circuit ( ) {
409+ let mut err_handler = TonicErrorHandler :: new_with_clock (
410+ CallInfo {
411+ call_type : CallType :: TaskLongPoll ,
412+ call_name : POLL_WORKFLOW_METH_NAME ,
413+ retry_cfg : TEST_RETRY_CONFIG ,
414+ retry_short_circuit : Some ( NoRetryOnMatching {
415+ predicate : |s : & Status | s. code ( ) == Code :: ResourceExhausted ,
416+ } ) ,
417+ } ,
418+ TEST_RETRY_CONFIG ,
419+ FixedClock ( Instant :: now ( ) ) ,
420+ FixedClock ( Instant :: now ( ) ) ,
421+ ) ;
422+ let result = err_handler. handle ( 1 , Status :: new ( Code :: ResourceExhausted , "leave me alone" ) ) ;
423+ assert_matches ! ( result, RetryPolicy :: ForwardError ( _) )
424+ }
425+
391426 #[ rstest:: rstest]
392427 #[ tokio:: test]
393428 async fn task_poll_retries_forever < R > (
0 commit comments