Fix the backoff schedule for a retry wrapper
Your task
Your service wraps an idempotent remote call in a retry helper with exponential backoff. The first version slept after the last failed attempt, then returned null instead of the error, and it retried every kind of error. The new version first works out the list of sleeps a call goes through, so the policy can be tested without a clock.
An assistant wrote retry_sleep_schedule(outcomes, max_attempts, base_delay_ms, cap_delay_ms). It is in the editor. It has a bug. Find it and fix it.
Input and output:
outcomes: what each attempt returns, in order:"ok","retryable"or any other string, such as"terminal"or"auth_error".max_attempts: the most attempts allowed.base_delay_ms: the first sleep, in milliseconds.cap_delay_ms: the longest sleep allowed.- Return the sleeps, in milliseconds, in the order they happen.
Rules the tests check:
- If
max_attemptsis below 1, return[]. - The ceiling is
cap_delay_ms, or 3600000 (one hour) whencap_delay_msis 0 or less. - The first delay is
base_delay_ms, or 0 if that is negative, and never more than the ceiling. Each later delay is double the one before, and never more than the ceiling. - Attempt k, counting from 1, has the outcome
outcomes[k - 1]. Past the end of the list, every attempt is"retryable". - After each attempt, in this order:
"ok": stop.- Anything other than
"retryable": stop. Such errors are not worth retrying. - It was attempt number
max_attempts: stop. The caller gets the last error now, with no sleep. - Otherwise sleep for the current delay, then try again.
Examples
retry_sleep_schedule(outcomes: list[str], max_attempts: int, base_delay_ms: int, cap_delay_ms: int) -> list[int]Example 1
Input
retry_sleep_schedule(["retryable", "retryable", "ok"], 5, 1000, 8000)Output
[1000, 2000]Example 2
Input
retry_sleep_schedule(["retryable", "terminal"], 5, 1000, 8000)Output
[1000]Example 3
Input
retry_sleep_schedule(["retryable", "retryable", "retryable"], 3, 100, 10000)Output
[100, 200]