Start learning free

LESSON 115 OF 173 · BACKEND: PAYMENTS AND OTHER SERVICES

When a Job Fails

Practice

The worker from the last lesson marks every job done, whatever the mail service answered. But a busy mail service answers 503 Service Unavailable ("too busy, try later"), and then the email never left. The job says done, so no run will try it again. A job that fails should stay 'waiting', so the next run retries it.

Not forever, though: an email to an address that doesn't exist fails at every run. So each job counts its attempts, in a new column attempts INTEGER NOT NULL DEFAULT 0. DEFAULT 0 fills it in for new jobs, so the INSERT in the pay route stays as it is. After the third failed attempt the job is 'failed': the worker leaves it alone, and a person can look at it.

Counting tries looks the same everywhere. A login that locks after 5 wrong passwords adds 1 first, then compares:

const tries = user.wrongPasswords + 1;
let state;
if (passwordOk) {
  state = "open";
} else if (tries < 5) {
  state = "try again";
} else {
  state = "locked";
}

The mail service answers 202 when it takes an email. In the tests, POST /check/fail-next makes it answer 503 to the next 4 emails, then it works again:

POST /orders/8/pay                  →  201  {"amount": 80, "order_id": 8, "payment_id": "pay_1"}
POST /check/fail-next {"times": 4}  →  200  {"failing": 4}
POST /admin/run-jobs                →  200  {"ran": 1}
POST /orders/10/pay                 →  201  {"amount": 21, "order_id": 10, "payment_id": "pay_2"}
POST /admin/run-jobs                →  200  {"ran": 2}
GET /check/jobs                     →  200  [{"attempts": 2, "id": 1, "order_id": 8, "status": "waiting"},
                                             {"attempts": 1, "id": 2, "order_id": 10, "status": "waiting"}]
POST /admin/run-jobs                →  200  {"ran": 2}
GET /check/jobs                     →  200  [{"attempts": 3, "id": 1, "order_id": 8, "status": "failed"},
                                             {"attempts": 2, "id": 2, "order_id": 10, "status": "done"}]
GET /check/sent                     →  200  [{"subject": "Your order 10 is paid", "to": "chloe@example.com", …}]
POST /admin/run-jobs                →  200  {"ran": 0}

Your task: the jobs table's line is gone from your code: write it again with the attempts column. In the worker's loop, keep the answer of sendReceipt, add 1 to the job's attempts, and choose the status: "done" after a 202, "waiting" while attempts is below 3, "failed" otherwise. Save the status and the attempts with one UPDATE.


Assignment
Follow the Coding Tutorial to count each job's attempts and retry a failed job up to 3 times.


Hint
Check the 202 first, so a job that got through is never sent again. Add 1 to the attempts before you compare with 3: the third failure must already count as 3. Declare the status with let, because it gets its value inside the if.


STEP 1 OF 11
    Ctrl + Enter to check