fix: add exponential recovery backoff#390
Conversation
|
|
||
| #[cfg(test)] | ||
| mod tests { | ||
| use super::*; | ||
|
|
||
| #[test] | ||
| fn defaults_use_exponential_retry_strategy_with_cap() { | ||
| let options = ArrowStreamConfigurationOptions::default(); | ||
|
|
||
| assert_eq!( | ||
| options.retry_strategy, | ||
| RetryStrategy::ExponentialBackoffWithJitter | ||
| ); | ||
| assert_eq!( | ||
| options.max_recovery_backoff_ms, | ||
| defaults::MAX_RECOVERY_BACKOFF_MS | ||
| ); | ||
| } | ||
| } |
There was a problem hiding this comment.
I'm not sure we need these types of tests, we don't have them for the rest of the configs.
|
|
||
| #[cfg(test)] | ||
| mod tests { | ||
| use super::*; | ||
|
|
||
| #[test] | ||
| fn defaults_use_exponential_retry_strategy_with_cap() { | ||
| let options = StreamConfigurationOptions::default(); | ||
|
|
||
| assert_eq!( | ||
| options.retry_strategy, | ||
| RetryStrategy::ExponentialBackoffWithJitter | ||
| ); | ||
| assert_eq!( | ||
| options.max_recovery_backoff_ms, | ||
| defaults::MAX_RECOVERY_BACKOFF_MS | ||
| ); | ||
| } | ||
| } |
There was a problem hiding this comment.
Same as in arrow_configuration file.
| pub enum RetryStrategy { | ||
| /// Retry after the configured `recovery_backoff_ms` interval. | ||
| Fixed, | ||
| /// Retry with exponential backoff and jitter, starting from `recovery_backoff_ms`. | ||
| #[default] | ||
| ExponentialBackoffWithJitter, | ||
| } |
There was a problem hiding this comment.
I think fixed should be kept as a default one until we add support for this in all SDKs because if we upgrade Rust version in other SDKs before exposing retry_strategy/max_recovery_backoff_ms in them, there would be no way for users to opt back to Fixed and it would be a silent behavioral change.
Otherwise we would have to commit to add support for this when we upgrade other SDKs. What do you think @teodordelibasic-db?
|
|
||
| #[cfg(test)] | ||
| mod tests { | ||
| use super::*; | ||
|
|
||
| #[test] | ||
| fn retry_strategy_defaults_to_exponential_with_jitter() { | ||
| assert_eq!( | ||
| RetryStrategy::default(), | ||
| RetryStrategy::ExponentialBackoffWithJitter | ||
| ); | ||
| assert_eq!(defaults::MAX_RECOVERY_BACKOFF_MS, 30_000); | ||
| } | ||
|
|
||
| #[test] | ||
| fn fixed_retry_strategy_uses_configured_interval() { | ||
| let mut strategy = recovery_retry_strategy(RetryStrategy::Fixed, 123, 30_000); | ||
|
|
||
| assert_eq!(strategy.next(), Some(Duration::from_millis(123))); | ||
| assert_eq!(strategy.next(), Some(Duration::from_millis(123))); | ||
| } | ||
|
|
||
| #[test] | ||
| fn exponential_retry_strategy_starts_from_initial_delay_and_caps() { | ||
| assert_eq!( | ||
| capped_exponential_delay(Duration::from_millis(2), 100, 1_000), | ||
| Duration::from_millis(100) | ||
| ); | ||
| assert_eq!( | ||
| capped_exponential_delay(Duration::from_millis(4), 100, 1_000), | ||
| Duration::from_millis(200) | ||
| ); | ||
| assert_eq!( | ||
| capped_exponential_delay(Duration::from_millis(16), 100, 250), | ||
| Duration::from_millis(250) | ||
| ); | ||
| } | ||
|
|
||
| #[test] | ||
| fn exponential_retry_strategy_applies_jitter_under_cap() { | ||
| let mut strategy = | ||
| recovery_retry_strategy(RetryStrategy::ExponentialBackoffWithJitter, 100, 250); | ||
|
|
||
| for _ in 0..10 { | ||
| assert!(strategy.next().unwrap() <= Duration::from_millis(250)); | ||
| } | ||
| } | ||
| } |
There was a problem hiding this comment.
I would change these tests for tests against the mock server (in rust/test), for consistency.
Summary
Closes #192.
This changes Rust stream creation and recovery retries from a fixed interval default to exponential backoff with full jitter.
recovery_backoff_msremains the fixed interval whenRetryStrategy::Fixedis selected, and becomes the base delay forRetryStrategy::ExponentialBackoffWithJitter;max_recovery_backoff_mscaps the computed exponential delay before jitter.The same retry helper is used by gRPC stream creation, Arrow Flight stream creation, and Arrow supervisor recovery sleeps so the behavior stays consistent across both stream implementations. The default strategy is now exponential backoff with jitter, so wrapper SDKs that rely on the Rust core inherit the safer retry behavior without new dependencies.
Verification
cargo fmt --manifest-path rust/sdk/Cargo.tomlcargo check -p databricks-zerobus-ingest-sdkcargo check -p databricks-zerobus-ingest-sdk --features arrow-flightcargo test -p databricks-zerobus-ingest-sdk --libcargo test -p databricks-zerobus-ingest-sdk --features arrow-flight --libgit diff --check