Start learning free

LESSON 113 OF 173 · BACKEND: PAYMENTS AND OTHER SERVICES

Do It Later: A Job Queue

Practice

Sending the email inside payOrder has two problems. The customer waits for the mail service before they see "paid", and a mail service can take seconds. Worse, if it is down, the email is lost, or the request crashes after the money has been taken.

So payOrder no longer sends the email (the call is gone from your code). It writes a note, "send the receipt for order 8", and answers at once; something else does the work later. Each note is a job, and a table of jobs waiting their turn is a job queue. A job needs little: its id, the order_id whose receipt to send, and a status such as 'waiting'.

Where you add the job matters. Each db.update is saved as soon as it runs, and a return doesn't undo it. So the job goes right after the UPDATE that marks the order paid: a declined card and a retry with the same idempotency key return earlier, so they add no job. The same shape on another table, a signup that queues a welcome email:

db.update("INSERT INTO users (email) VALUES (?)", email);
db.update("INSERT INTO tasks (kind, email, status) VALUES ('welcome', ?, 'waiting')", email);

The table is created in the constructor, which runs every time the app starts. SQL that answers nothing runs with db.execute. The checker adds GET /check/jobs and GET /check/sent, so you can see the jobs and the emails the mail service received:

POST /orders/8/pay  →  201  {"amount": 80, "order_id": 8, "payment_id": "pay_1"}
GET /check/jobs  →  200  [{"id": 1, "order_id": 8, "status": "waiting"}]
GET /check/sent  →  200  []
POST /orders/9/pay  →  402  {"error": "Payment declined", "reason": "card_declined"}
POST /orders/10/pay  (Idempotency-Key: 9b3e6f20-…)  →  201  {"amount": 21, "order_id": 10, "payment_id": "pay_2"}
POST /orders/10/pay  (the same key)  →  201  {"amount": 21, "order_id": 10, "payment_id": "pay_2"}
GET /check/jobs  →  200  [{"id": 1, "order_id": 8, "status": "waiting"}, {"id": 2, "order_id": 10, "status": "waiting"}]

Your task: in the constructor, create the table jobs with id INTEGER PRIMARY KEY, order_id INTEGER NOT NULL and status TEXT NOT NULL. In payOrder, right after the UPDATE that marks the order paid, add a 'waiting' job for orderId.


Assignment
Follow the Coding Tutorial to create the jobs table and queue a receipt job for every paid order.


Hint
Use db.execute with CREATE TABLE IF NOT EXISTS, so the line is safe every time the app starts. The INSERT INTO jobs is a db.update with orderId for its ?, right below the UPDATE.


STEP 1 OF 2
    Ctrl + Enter to check