Skip to content
echogeco
Connectors · Reliability

The retry loop that retries a 400

Find the three defects one weekend's log exposes in a contractor's retry wrapper, cost the worst, and replace it with a policy sorted by status.

critiqueeasyExercise · 3 min · 37 of 75
On this page

Ten lines of wrapper decide how every call in the invoice run fails. One weekend's log shows them failing three different ways, and only one of the three would show up in a test.

The situation

The agency's invoice run calls the accounting system through a wrapper a contractor wrote:

def call(method, url, body=None):
    for attempt in range(6):
        try:
            r = http(method, url, json=body, timeout=10)
            if r.status < 300:
                return r
        except Timeout:
            pass
        sleep(2)
    raise Failed(url)

The run restarts every 15 minutes until every invoice has gone out. The API allows 30 requests a minute per account, shared with the overdue reminders. It accepts no idempotency key, but each invoice carries its timesheet id in a reference field. The weekend's log shows three things:

  • One client's invoice returned 422 tax_number missing six times per run, from 17:00 Friday to 09:00 Monday.
  • A POST /invoices for another client timed out at 10 seconds; the retry returned 201. INV-0533 and INV-0534 now exist for the same week.
  • In the 17:00 burst, nine calls got 429 with Retry-After: 20. Each retried five times, 2 seconds apart, and failed.

Assume staff time at $30 an hour. The lesson is rate limits, retries and backoff.

The question

Find the three defects the log exposes, put a number on the first, and say what replaces the wrapper.

A strong answer · Rubric · Where answers go wrong

Write your answer first, then open