@@ -38,7 +38,6 @@ const HTTP_STATUS_OK: u8 = 200;
3838
3939// Fetch the certs every fifteen minutes, the responses are always
4040// valid for a couple of hours so that should be enough margin.
41- #[ cfg( not( test) ) ]
4241const FETCH_CERTS_INTERVAL : u64 = 60 * 15 ; // 15 minutes in seconds
4342
4443// A JWT is only valid for a very small window, even if the JWT itself says it's valid for longer,
@@ -177,6 +176,29 @@ impl Provider {
177176 }
178177}
179178
179+ fn compute_next_certs_fetch_delay < T , E > (
180+ result : & Result < T , E > ,
181+ current_delay : Option < u64 > ,
182+ ) -> Option < u64 > {
183+ use std:: cmp:: { max, min} ;
184+
185+ const MAX_DELAY : u64 = FETCH_CERTS_INTERVAL ;
186+ const MIN_DELAY : u64 = 60 ;
187+
188+ match result {
189+ // Reset delay to None so default (`FETCH_CERTS_INTERVAL`) delay is used.
190+ Ok ( _) => None ,
191+ // Try again earlier with backoff if fetch failed, the HTTP outcall responses
192+ // aren't the same across nodes when we fetch at the moment of key rotation.
193+ //
194+ // The delay should be at most `MAX_DELAY` and at minimum `MIN_DELAY` * 2.
195+ Err ( _) => Some ( min (
196+ MAX_DELAY ,
197+ max ( MIN_DELAY , current_delay. unwrap_or ( MIN_DELAY ) ) * 2 ,
198+ ) ) ,
199+ }
200+ }
201+
180202#[ cfg( not( test) ) ]
181203fn schedule_fetch_certs (
182204 jwks_uri : String ,
@@ -185,26 +207,18 @@ fn schedule_fetch_certs(
185207) {
186208 use ic_cdk:: spawn;
187209 use ic_cdk_timers:: set_timer;
188- use std:: cmp:: { max, min} ;
189210 use std:: time:: Duration ;
190211
191212 set_timer (
192213 Duration :: from_secs ( delay. unwrap_or ( FETCH_CERTS_INTERVAL ) ) ,
193214 move || {
194215 spawn ( async move {
195- let new_delay = match fetch_certs ( jwks_uri. clone ( ) ) . await {
196- Ok ( certs) => {
197- certs_reference. replace ( certs) ;
198- // Reset delay to None so default `FETCH_CERTS_INTERVAL` delay is used.
199- None
200- }
201- // Try again earlier with backoff if fetch failed, the HTTP outcall responses
202- // aren't the same across nodes when we fetch at the moment of key rotation.
203- //
204- // The delay should be at most `FETCH_CERTS_INTERVAL` and at minimum 60 * 2.
205- Err ( _) => Some ( min ( FETCH_CERTS_INTERVAL , max ( 60 , delay. unwrap_or ( 60 ) ) * 2 ) ) ,
206- } ;
207- schedule_fetch_certs ( jwks_uri, certs_reference, new_delay) ;
216+ let result = fetch_certs ( jwks_uri. clone ( ) ) . await ;
217+ let next_delay = compute_next_certs_fetch_delay ( & result, delay) ;
218+ if let Ok ( certs) = result {
219+ certs_reference. replace ( certs) ;
220+ }
221+ schedule_fetch_certs ( jwks_uri, certs_reference, next_delay) ;
208222 } ) ;
209223 } ,
210224 ) ;
@@ -647,3 +661,26 @@ fn should_return_error_when_name_too_long() {
647661 ) )
648662 ) ;
649663}
664+ #[ test]
665+ fn should_compute_next_certs_fetch_delay ( ) {
666+ let success: Result < ( ) , ( ) > = Ok ( ( ) ) ;
667+ let error: Result < ( ) , ( ) > = Err ( ( ) ) ;
668+
669+ for ( current_delay, expected_next_delay_on_error) in [
670+ ( None , Some ( 120 ) ) ,
671+ ( Some ( 0 ) , Some ( 120 ) ) ,
672+ ( Some ( 120 ) , Some ( 240 ) ) ,
673+ ( Some ( 240 ) , Some ( 480 ) ) ,
674+ ( Some ( 480 ) , Some ( FETCH_CERTS_INTERVAL ) ) ,
675+ ( Some ( FETCH_CERTS_INTERVAL * 2 ) , Some ( FETCH_CERTS_INTERVAL ) ) ,
676+ ] {
677+ assert_eq ! (
678+ compute_next_certs_fetch_delay( & success, current_delay) ,
679+ None
680+ ) ;
681+ assert_eq ! (
682+ compute_next_certs_fetch_delay( & error, current_delay) ,
683+ expected_next_delay_on_error
684+ ) ;
685+ }
686+ }
0 commit comments