mirror of
				https://github.com/juspay/hyperswitch.git
				synced 2025-10-31 18:17:13 +08:00 
			
		
		
		
	feat(webhooks): implement automatic retries for failed webhook deliveries using scheduler (#3842)
This commit is contained in:
		| @ -342,24 +342,48 @@ pub fn get_pm_schedule_time( | ||||
|     } | ||||
| } | ||||
|  | ||||
| /// Get the delay based on the retry count | ||||
| fn get_delay<'a>( | ||||
| pub fn get_outgoing_webhook_retry_schedule_time( | ||||
|     mapping: process_data::OutgoingWebhookRetryProcessTrackerMapping, | ||||
|     merchant_name: &str, | ||||
|     retry_count: i32, | ||||
|     mut array: impl Iterator<Item = (&'a i32, &'a i32)>, | ||||
| ) -> Option<i32> { | ||||
|     match array.next() { | ||||
|         Some(ele) => { | ||||
|             let v = retry_count - ele.0; | ||||
|             if v <= 0 { | ||||
|                 Some(*ele.1) | ||||
|             } else { | ||||
|                 get_delay(v, array) | ||||
|             } | ||||
|         } | ||||
|         None => None, | ||||
|     let retry_mapping = match mapping.custom_merchant_mapping.get(merchant_name) { | ||||
|         Some(map) => map.clone(), | ||||
|         None => mapping.default_mapping, | ||||
|     }; | ||||
|  | ||||
|     // For first try, get the `start_after` time | ||||
|     if retry_count == 0 { | ||||
|         Some(retry_mapping.start_after) | ||||
|     } else { | ||||
|         get_delay( | ||||
|             retry_count, | ||||
|             retry_mapping | ||||
|                 .count | ||||
|                 .iter() | ||||
|                 .zip(retry_mapping.frequency.iter()), | ||||
|         ) | ||||
|     } | ||||
| } | ||||
|  | ||||
| /// Get the delay based on the retry count | ||||
| fn get_delay<'a>(retry_count: i32, array: impl Iterator<Item = (&'a i32, &'a i32)>) -> Option<i32> { | ||||
|     // Preferably, fix this by using unsigned ints | ||||
|     if retry_count <= 0 { | ||||
|         return None; | ||||
|     } | ||||
|  | ||||
|     let mut cumulative_count = 0; | ||||
|     for (&count, &frequency) in array { | ||||
|         cumulative_count += count; | ||||
|         if cumulative_count >= retry_count { | ||||
|             return Some(frequency); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     None | ||||
| } | ||||
|  | ||||
| pub(crate) async fn lock_acquire_release<T, F, Fut>( | ||||
|     state: &T, | ||||
|     settings: &SchedulerSettings, | ||||
| @ -392,3 +416,38 @@ where | ||||
|         Ok(()) | ||||
|     } | ||||
| } | ||||
|  | ||||
| #[cfg(test)] | ||||
| mod tests { | ||||
|     use super::*; | ||||
|  | ||||
|     #[test] | ||||
|     fn test_get_delay() { | ||||
|         let count = [10, 5, 3, 2]; | ||||
|         let frequency = [300, 600, 1800, 3600]; | ||||
|  | ||||
|         let retry_counts_and_expected_delays = [ | ||||
|             (-4, None), | ||||
|             (-2, None), | ||||
|             (0, None), | ||||
|             (4, Some(300)), | ||||
|             (7, Some(300)), | ||||
|             (10, Some(300)), | ||||
|             (12, Some(600)), | ||||
|             (16, Some(1800)), | ||||
|             (18, Some(1800)), | ||||
|             (20, Some(3600)), | ||||
|             (24, None), | ||||
|             (30, None), | ||||
|         ]; | ||||
|  | ||||
|         for (retry_count, expected_delay) in retry_counts_and_expected_delays { | ||||
|             let delay = get_delay(retry_count, count.iter().zip(frequency.iter())); | ||||
|  | ||||
|             assert_eq!( | ||||
|                 delay, expected_delay, | ||||
|                 "Delay and expected delay differ for `retry_count` = {retry_count}" | ||||
|             ); | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
		Reference in New Issue
	
	Block a user
	 Sanchith Hegde
					Sanchith Hegde