retry adds per-attempt timeout, retry/backoff, and optional semaphore-based concurrency control around async and sync callables. It works for ordinary functions without creating an EventBus.
Sync callables stay sync and block synchronously for retry sleeps and semaphore waits. Async callables stay async and await retry sleeps and semaphore waits.
Supported callable forms:
- Python: sync and async functions, methods, and lambdas via
@retry(...)orretry(...)(fn). - TypeScript: sync and async functions and arrow functions via
retry(...)(fn), plus sync and async class methods via@retry(...). - Rust: sync and async free functions and methods via
abxbus::retry!; call those retried functions from Rust closures such as EventBus handlers. - Go: retry is not implemented yet.
Signature
- Python
- TypeScript
- Rust
- Go
Options
| Option | Description |
|---|---|
max_attempts | Total attempts including the first call (1 disables retries). |
retry_after | Base delay between retries, in seconds. |
retry_backoff_factor | Delay multiplier applied after each failed attempt. |
retry_on_errors | Optional matcher list to restrict which errors are retried. |
timeout | Per-attempt timeout in seconds (None/undefined means no per-attempt timeout). |
slow_timeout | Warning threshold for a decorated call, in seconds. Warnings are throttled to at most one every 2 seconds per decorated method/function. |
semaphore_limit | Max concurrent executions sharing the same semaphore. |
semaphore_name | Semaphore key (string or function deriving a key from call args). |
semaphore_scope | Semaphore sharing scope (multiprocess, global, class, instance). |
semaphore_timeout | Max wait time for semaphore acquisition before timeout/lax fallback. |
semaphore_lax | If true, continue execution without semaphore limit when acquisition times out. |
Example: Standalone function
- Python
- TypeScript
- Rust
Example: Inline wrapper
- Python
- TypeScript
- Rust
- Go
Example: Decorated class method
- Python
- TypeScript
- Rust
- Go
Behavior
- Semaphore acquisition happens once per call, then all retry attempts run within that acquired slot.
- Backoff delay per retry is:
retry_after * retry_backoff_factor^(attempt - 1). - Retries stop immediately when the thrown error does not match
retry_on_errors. - Bus/event timeouts act as outer execution budgets;
retry.timeoutis per-attempt. - Sync wrappers return synchronously and block synchronously for retry sleeps and semaphore waits.
- Rust retry functions return
Result<T, E>whereE: From<abxbus::retry::RetryError>.
Runtime differences
- Python and TypeScript both support
multiprocess,global,class, andinstance. - Rust supports
multiprocess,global,class, andinstancethroughabxbus::retry!. - Go does not include
retryyet. - TypeScript uses async-context re-entrancy tracking in Node/Bun to avoid same-semaphore nested deadlocks.
retry_on_errorsmatching differs slightly:- Python: exception classes or compiled regex patterns (matched against
"ErrorClass: message"). - TypeScript: error constructors, error-name strings, or regex patterns.
- Rust: typed predicate function via
retry_if = should_retry.
- Python: exception classes or compiled regex patterns (matched against