attempt 0
t = 0 ms
first run
Every queue fails. Bidewell decides when it tries again.
Point your background jobs at Bidewell. It runs them, keeps the retry schedule, records every attempt, and tells you when a queue has stopped arriving at all.
£29 a month for 100,000 jobs · 14 days free · no card
attempt 1
t = 250 ms
waited 250 ms
The first retry is not an incident
Most failures are a timeout, a rate limit, or a lock held a second too long. The first retry clears those and nobody needs to know. Bidewell writes down the attempt, the error it came back with and the wait it is about to take, and it does not wake anyone up.
attempt 2
t = 750 ms
waited 500 ms
The schedule, drawn
One constant runs this whole site: 40 milliseconds to the pixel. Every gap you have scrolled past is a real wait at that scale, and so is every bar below. Two schedules drawn anywhere on this site can be held against each other, because there is only ever one scale.
The first two waits carry no figure beside them. 250 milliseconds is six pixels tall and a line of type is thirteen, so there is nowhere to put it. That is not a compromise. That is what a quarter of a second looks like standing next to thirty seconds.
| 0 | 250 ms | |
|---|---|---|
| 1 | 500 ms | |
| 2 | 1 s | |
| 3 | 2 s | |
| 4 | 4 s | |
| 5 | 8 s | |
| 6 | 16 s | |
| 7 | 30 s | |
| 8 | 30 s |
min(250 ms · 2ⁿ, 30 s) · nine attempts · dead-lettered at 1 min 31.75 s
All four policies, drawn at the same scale
attempt 3
t = 1.75 s
waited 1 s
Jitter, because everything comes back at once
When a dependency recovers, every job that failed against it retries at the same instant and knocks it over again. Full jitter draws each wait uniformly between zero and the ceiling the policy would have used, so a queue that failed together does not return together. The ceiling is unchanged; only the draw underneath it moves.
attempt 4
t = 3.75 s
waited 2 s
At least once, and what that costs you
A retry is a second delivery. If the job charges a card, sends an email or writes a row with no key on it, the retry does that a second time. Bidewell sends an idempotency key with every attempt and refuses to enqueue a job that has no way of accepting one, which is a worse developer experience and a better outcome.
attempt 5
t = 7.75 s
waited 4 s
The queue that stops arriving
The failure worth paying for is the quiet one. A worker that exited cleanly, a schedule that stopped being scheduled, a topic nobody publishes to any more. Nothing fails, so nothing retries, so nothing tells you. Bidewell learns each queue's arrival interval and raises it when the interval passes empty.
attempt 6
t = 15.75 s
waited 8 s
Three lines
A queue is a function and a policy. There is no console step, no YAML, and no agent to install on anything.
import { queue } from '@bidewell/node';
export const settle = queue('settle', { retries: 'exponential' },
async (job) => charge(job.data.invoice, { key: job.idempotencyKey }));
attempt 7
t = 31.75 s
waited 16 s
What it costs
| Plan | Jobs a month | History | Price |
|---|---|---|---|
| Yard | 100,000 | 7 days | £29 |
| Works | 1,000,000 | 30 days | £96 |
| Nightshift | metered | 90 days | on request |
attempt 8
t = 1 min 1.75 s
waited 30 s
The cap
The wait above this section and the wait below it are the same length. Past the eighth attempt the delay stops doubling and holds at 30 s, because a schedule that keeps doubling is at a fortnight by attempt twenty and nobody is waiting a fortnight. This is the last attempt, and below it is thirty seconds of nothing for the second time.
t = 1 min 31.75 s
no attempts left
The dead letter
Nine attempts and 1 min 31.75 s. After that the job is written to the dead letter with every attempt it made, the error each one returned and the wait it took, and it stays there until you replay it or delete it. Nothing is dropped and nothing expires on its own.