Two operational details that matter as much as the algorithm.
Cap the total attempts and send failures somewhere. After the final attempt, the job goes to a dead letter queue rather than being retried forever or dropped. Then a person can look at what failed and requeue deliberately when the dependency is genuinely healthy. Draining a dead letter queue on purpose is completely different from a backlog releasing itself.
Make retries visible. A metric for retry count per dependency, and an alert when it rises sharply. In your incident the retry rate would have gone up minutes before anything else looked wrong, and it is the earliest signal you can get. Most people only alert on failures, which fires later.
On the specific shape I would use for your case: exponential backoff with full jitter, a small number of attempts for genuinely transient failures, no retries at all for permanent ones, a concurrency cap on the upstream, and a breaker.
And one thing to check while you are in there: make sure retries are not nested. An HTTP client that retries three times, inside a job that retries three times, is nine calls per job and nobody planned it. Layered retries are how a modest policy becomes an accidental flood, and they are very easy to acquire without noticing.