chrono_machines 0.5.2 → 0.9.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.md +86 -0
- data/Cargo.lock +10 -22
- data/README.md +26 -0
- data/ext/chrono_machines_native/core/Cargo.toml +7 -4
- data/ext/chrono_machines_native/core/src/backoff.rs +58 -92
- data/ext/chrono_machines_native/core/src/dsl.rs +1 -38
- data/ext/chrono_machines_native/core/src/lib.rs +36 -149
- data/ext/chrono_machines_native/core/src/retry.rs +340 -905
- data/ext/chrono_machines_native/core/src/sleep.rs +65 -31
- data/ext/chrono_machines_native/ffi/Cargo.toml +3 -3
- data/ext/chrono_machines_native/ffi/src/lib.rs +8 -68
- data/lib/chrono_machines/configuration.rb +1 -0
- data/lib/chrono_machines/executor.rb +25 -1
- data/lib/chrono_machines/version.rb +1 -1
- metadata +3 -3
|
@@ -43,12 +43,36 @@ pub mod policy;
|
|
|
43
43
|
pub mod retry;
|
|
44
44
|
pub mod sleep;
|
|
45
45
|
|
|
46
|
+
/// The `rand` crate this one was built against.
|
|
47
|
+
///
|
|
48
|
+
/// [`BackoffStrategy::delay`] is generic over [`rand::Rng`], which puts that
|
|
49
|
+
/// trait in the public API: a downstream strategy cannot be written without
|
|
50
|
+
/// naming it. Re-exporting it here means implementors do not have to add their
|
|
51
|
+
/// own `rand` dependency and keep its version in lockstep with this crate's by
|
|
52
|
+
/// hand — a mismatch there produces two incompatible `Rng` traits and an error
|
|
53
|
+
/// that says nothing about the real cause.
|
|
54
|
+
///
|
|
55
|
+
/// ```rust
|
|
56
|
+
/// use chrono_machines::{rand::Rng, BackoffStrategy};
|
|
57
|
+
///
|
|
58
|
+
/// struct Immediate;
|
|
59
|
+
///
|
|
60
|
+
/// impl BackoffStrategy for Immediate {
|
|
61
|
+
/// fn delay<R: Rng>(&self, attempt: u8, _rng: &mut R) -> Option<u64> {
|
|
62
|
+
/// self.should_retry(attempt).then_some(0)
|
|
63
|
+
/// }
|
|
64
|
+
/// fn should_retry(&self, attempt: u8) -> bool { attempt < self.max_attempts() }
|
|
65
|
+
/// fn max_attempts(&self) -> u8 { 3 }
|
|
66
|
+
/// }
|
|
67
|
+
/// ```
|
|
68
|
+
pub use ::rand;
|
|
69
|
+
|
|
46
70
|
pub use backoff::{
|
|
47
|
-
|
|
48
|
-
|
|
71
|
+
BackoffPolicy, BackoffStrategy, ConstantBackoff, ExponentialBackoff, FibonacciBackoff,
|
|
72
|
+
fibonacci,
|
|
49
73
|
};
|
|
50
74
|
#[cfg(feature = "std")]
|
|
51
|
-
pub use dsl::{builder_for_policy, retry_with_policy
|
|
75
|
+
pub use dsl::{DslError, builder_for_policy, retry_with_policy};
|
|
52
76
|
#[cfg(any(feature = "std", feature = "alloc"))]
|
|
53
77
|
pub use policy::PolicyRegistry;
|
|
54
78
|
#[cfg(feature = "std")]
|
|
@@ -56,15 +80,21 @@ pub use policy::{
|
|
|
56
80
|
clear_global_policies, get_global_policy, list_global_policies, register_global_policy,
|
|
57
81
|
remove_global_policy,
|
|
58
82
|
};
|
|
83
|
+
#[cfg(feature = "async")]
|
|
84
|
+
pub use retry::AsyncRetryable;
|
|
59
85
|
#[cfg(any(feature = "std", feature = "alloc"))]
|
|
60
|
-
pub use retry::{
|
|
86
|
+
pub use retry::{
|
|
87
|
+
DelayHint, RetryBuilder, RetryContext, RetryError, RetryOutcome, Retryable, RetryableExt,
|
|
88
|
+
};
|
|
89
|
+
#[cfg(feature = "async")]
|
|
90
|
+
pub use sleep::AsyncSleeper;
|
|
61
91
|
#[cfg(feature = "std")]
|
|
62
92
|
pub use sleep::StdSleeper;
|
|
63
93
|
pub use sleep::{FnSleeper, Sleeper};
|
|
64
94
|
|
|
95
|
+
use rand::RngExt;
|
|
65
96
|
#[cfg(feature = "std")]
|
|
66
97
|
use rand::rngs::StdRng;
|
|
67
|
-
use rand::RngExt;
|
|
68
98
|
|
|
69
99
|
use rand::Rng;
|
|
70
100
|
|
|
@@ -209,147 +239,4 @@ impl Default for Policy {
|
|
|
209
239
|
}
|
|
210
240
|
|
|
211
241
|
#[cfg(test)]
|
|
212
|
-
mod tests
|
|
213
|
-
use super::*;
|
|
214
|
-
use rand::rngs::StdRng;
|
|
215
|
-
use rand::SeedableRng;
|
|
216
|
-
|
|
217
|
-
#[test]
|
|
218
|
-
fn test_policy_default() {
|
|
219
|
-
let policy = Policy::default();
|
|
220
|
-
assert_eq!(policy.max_attempts, 3);
|
|
221
|
-
assert_eq!(policy.base_delay_ms, 100);
|
|
222
|
-
assert_eq!(policy.multiplier, 2.0);
|
|
223
|
-
assert_eq!(policy.max_delay_ms, 10_000);
|
|
224
|
-
}
|
|
225
|
-
|
|
226
|
-
#[test]
|
|
227
|
-
fn test_calculate_delay_bounds() {
|
|
228
|
-
let policy = Policy {
|
|
229
|
-
max_attempts: 5,
|
|
230
|
-
base_delay_ms: 100,
|
|
231
|
-
multiplier: 2.0,
|
|
232
|
-
max_delay_ms: 1000,
|
|
233
|
-
};
|
|
234
|
-
|
|
235
|
-
let mut rng = StdRng::seed_from_u64(42);
|
|
236
|
-
|
|
237
|
-
// First attempt with full jitter: delay should be between 0 and 100ms
|
|
238
|
-
let delay1 = policy.calculate_delay_with_rng(1, 1.0, &mut rng);
|
|
239
|
-
assert!(delay1 <= 100);
|
|
240
|
-
|
|
241
|
-
// Second attempt with full jitter: delay should be between 0 and 200ms
|
|
242
|
-
let delay2 = policy.calculate_delay_with_rng(2, 1.0, &mut rng);
|
|
243
|
-
assert!(delay2 <= 200);
|
|
244
|
-
|
|
245
|
-
// Fifth attempt with full jitter: delay should be capped at max_delay_ms (1000ms)
|
|
246
|
-
let delay5 = policy.calculate_delay_with_rng(5, 1.0, &mut rng);
|
|
247
|
-
assert!(delay5 <= 1000);
|
|
248
|
-
}
|
|
249
|
-
|
|
250
|
-
#[test]
|
|
251
|
-
fn test_should_retry() {
|
|
252
|
-
let policy = Policy {
|
|
253
|
-
max_attempts: 3,
|
|
254
|
-
..Policy::default()
|
|
255
|
-
};
|
|
256
|
-
|
|
257
|
-
assert!(policy.should_retry(1));
|
|
258
|
-
assert!(policy.should_retry(2));
|
|
259
|
-
assert!(!policy.should_retry(3));
|
|
260
|
-
assert!(!policy.should_retry(4));
|
|
261
|
-
}
|
|
262
|
-
|
|
263
|
-
#[test]
|
|
264
|
-
fn test_max_delay_cap() {
|
|
265
|
-
let policy = Policy {
|
|
266
|
-
max_attempts: 10,
|
|
267
|
-
base_delay_ms: 100,
|
|
268
|
-
multiplier: 2.0,
|
|
269
|
-
max_delay_ms: 500,
|
|
270
|
-
};
|
|
271
|
-
|
|
272
|
-
let mut rng = StdRng::seed_from_u64(42);
|
|
273
|
-
|
|
274
|
-
// High attempt number should still be capped
|
|
275
|
-
let delay = policy.calculate_delay_with_rng(10, 1.0, &mut rng);
|
|
276
|
-
assert!(delay <= 500);
|
|
277
|
-
}
|
|
278
|
-
|
|
279
|
-
#[test]
|
|
280
|
-
fn test_zero_multiplier() {
|
|
281
|
-
let policy = Policy {
|
|
282
|
-
max_attempts: 5,
|
|
283
|
-
base_delay_ms: 100,
|
|
284
|
-
multiplier: 1.0, // No exponential growth
|
|
285
|
-
max_delay_ms: 10_000,
|
|
286
|
-
};
|
|
287
|
-
|
|
288
|
-
let mut rng = StdRng::seed_from_u64(42);
|
|
289
|
-
|
|
290
|
-
// All delays with full jitter should be between 0 and base_delay_ms
|
|
291
|
-
for attempt in 1..=5 {
|
|
292
|
-
let delay = policy.calculate_delay_with_rng(attempt, 1.0, &mut rng);
|
|
293
|
-
assert!(delay <= 100);
|
|
294
|
-
}
|
|
295
|
-
}
|
|
296
|
-
|
|
297
|
-
#[test]
|
|
298
|
-
fn test_jitter_factor() {
|
|
299
|
-
let policy = Policy {
|
|
300
|
-
max_attempts: 5,
|
|
301
|
-
base_delay_ms: 1000,
|
|
302
|
-
multiplier: 1.0,
|
|
303
|
-
max_delay_ms: 10_000,
|
|
304
|
-
};
|
|
305
|
-
|
|
306
|
-
let mut rng = StdRng::seed_from_u64(42);
|
|
307
|
-
|
|
308
|
-
// 10% jitter: delay should be between 900ms (90%) and 1000ms (100%)
|
|
309
|
-
let delay = policy.calculate_delay_with_rng(1, 0.1, &mut rng);
|
|
310
|
-
assert!(
|
|
311
|
-
delay >= 900 && delay <= 1000,
|
|
312
|
-
"delay {} not in range 900-1000",
|
|
313
|
-
delay
|
|
314
|
-
);
|
|
315
|
-
|
|
316
|
-
// No jitter: delay should be exactly base_delay_ms
|
|
317
|
-
let delay = policy.calculate_delay_with_rng(1, 0.0, &mut rng);
|
|
318
|
-
assert_eq!(delay, 1000);
|
|
319
|
-
|
|
320
|
-
// Full jitter: delay should be between 0 and 1000ms
|
|
321
|
-
let delay = policy.calculate_delay_with_rng(1, 1.0, &mut rng);
|
|
322
|
-
assert!(delay <= 1000);
|
|
323
|
-
}
|
|
324
|
-
|
|
325
|
-
#[test]
|
|
326
|
-
fn test_jitter_factor_clamping() {
|
|
327
|
-
let policy = Policy {
|
|
328
|
-
max_attempts: 5,
|
|
329
|
-
base_delay_ms: 1000,
|
|
330
|
-
multiplier: 1.0,
|
|
331
|
-
max_delay_ms: 10_000,
|
|
332
|
-
};
|
|
333
|
-
|
|
334
|
-
let mut rng = StdRng::seed_from_u64(42);
|
|
335
|
-
|
|
336
|
-
// Negative jitter_factor should be clamped to 0.0
|
|
337
|
-
let delay = policy.calculate_delay_with_rng(1, -0.5, &mut rng);
|
|
338
|
-
assert_eq!(delay, 1000, "negative jitter_factor should clamp to 0.0");
|
|
339
|
-
|
|
340
|
-
// jitter_factor > 1.0 should be clamped to 1.0
|
|
341
|
-
let delay = policy.calculate_delay_with_rng(1, 2.0, &mut rng);
|
|
342
|
-
assert!(
|
|
343
|
-
delay <= 1000,
|
|
344
|
-
"jitter_factor > 1.0 should clamp to 1.0, got delay {}",
|
|
345
|
-
delay
|
|
346
|
-
);
|
|
347
|
-
|
|
348
|
-
// Extreme values should still be clamped
|
|
349
|
-
let delay = policy.calculate_delay_with_rng(1, 999.0, &mut rng);
|
|
350
|
-
assert!(delay <= 1000, "extreme jitter_factor should be clamped");
|
|
351
|
-
|
|
352
|
-
let delay = policy.calculate_delay_with_rng(1, -999.0, &mut rng);
|
|
353
|
-
assert_eq!(delay, 1000, "extreme negative should clamp to 0.0");
|
|
354
|
-
}
|
|
355
|
-
}
|
|
242
|
+
mod tests;
|