Schedule — The Retry/Repeat Policy Type
A Schedule is not just a number of retries or a delay. It's a policy — a function that takes the current state (attempt count, elapsed time, last output) and decides whether to continue and how long to wait.
The Core Concept
#![allow(unused)] fn main() { use id_effect::Schedule; // A Schedule answers: "Given where we are, should we continue? And after how long?" // Input: attempt number, elapsed time, last result // Output: Continue(delay) or Done }
This abstraction is more powerful than "retry 3 times with 1-second delay." A Schedule can:
- Increase delay exponentially (standard backoff)
- Cap the total time regardless of attempts
- Stop after a maximum number of attempts
- Adjust based on the error type or last result
- Combine policies with
&&and||
Creating Schedules
#![allow(unused)] fn main() { use id_effect::Schedule; // Fixed delay: always wait the same amount let fixed = Schedule::spaced(Duration::from_secs(1)); // Exponential: 100ms, 200ms, 400ms, 800ms, ... let exponential = Schedule::exponential(Duration::from_millis(100)); // Fibonacci: 100ms, 100ms, 200ms, 300ms, 500ms, 800ms, ... let fibonacci = Schedule::fibonacci(Duration::from_millis(100)); // Forever: repeat indefinitely with no delay let forever = Schedule::forever(); // Once: run exactly once (useful for testing) let once = Schedule::once(); }
Combining Schedules
Schedules compose:
#![allow(unused)] fn main() { // Retry up to 5 times let max_5 = Schedule::exponential(100.ms()).take(5); // But stop after 30 seconds total let bounded = Schedule::exponential(100.ms()).until_total_duration(Duration::from_secs(30)); // Combine with &&: both conditions must agree to continue let safe = Schedule::exponential(100.ms()) .take(5) .until_total_duration(Duration::from_secs(30)); }
Schedule as a Value
Like effects, schedules are values. You can define them once and reuse them:
#![allow(unused)] fn main() { const DEFAULT_RETRY: Schedule = Schedule::exponential(Duration::from_millis(100)) .take(5) .with_jitter(Duration::from_millis(50)); fn call_external_api() -> Effect<Response, ApiError, HttpClient> { make_request().retry(DEFAULT_RETRY) } }
Jitter (random delay variation) reduces thundering-herd problems when many processes retry simultaneously. .with_jitter(d) adds a random delay in [0, d) to each wait.