Start learning free

LESSON 114 OF 188 · BACKEND: PAYMENTS AND OTHER SERVICES

A Worker

Practice

Since the last lesson, paying an order doesn't send the receipt. It adds a row to the jobs table, with the status 'waiting'. Somebody has to do those jobs: that is a worker. In a real shop it is a second program that takes the waiting jobs every few seconds. Yours is a route, POST /admin/run-jobs, so one request runs it.

A worker does three things: it takes the rows still to do, it does each one, and it marks each one done, so nothing is done twice. Here is the same shape on another table, reminders:

rows = db.execute("SELECT * FROM reminders WHERE sent = 0").fetchall()
for row in rows:
    print("Reminder for customer", row["customer_id"])
    db.execute("UPDATE reminders SET sent = 1 WHERE id = ?", (row["id"],))
    db.commit()

The commit sits inside the loop, after each row: if the program stops halfway, the rows already done stay done. For your jobs, that means no customer gets the same email twice. The route is a POST, because it changes things.

The tests pay two orders, run the worker twice, then pay a third order and run it again. They read /check/sent (the emails the mail service received) and /check/jobs (the jobs table):

POST /orders/8/pay    →  201  {"amount": 80, "order_id": 8, "payment_id": "pay_1"}
POST /orders/10/pay   →  201  {"amount": 21, "order_id": 10, "payment_id": "pay_2"}
GET /check/sent       →  200  []
POST /admin/run-jobs  →  200  {"ran": 2}
GET /check/sent       →  200  [{"subject": "Your order 8 is paid", "to": "eva@example.com", …},
                               {"subject": "Your order 10 is paid", "to": "chloe@example.com", …}]
GET /check/jobs       →  200  [{"id": 1, "order_id": 8, "status": "done"},
                               {"id": 2, "order_id": 10, "status": "done"}]
POST /admin/run-jobs  →  200  {"ran": 0}
POST /orders/11/pay   →  201  {"amount": 18, "order_id": 11, "payment_id": "pay_3"}
POST /admin/run-jobs  →  200  {"ran": 1}
GET /check/sent       →  200  [… the same two, then {"subject": "Your order 11 is paid", "to": "ana@example.com", …}]

Your task: at the end of the file, add POST /admin/run-jobs with a function run_jobs. It takes the waiting jobs, oldest first, sends each job's receipt with your helper send_receipt, marks the job 'done' and commits, then answers {"ran": ...} with the number of jobs it took.


Assignment
Follow the Coding Tutorial to write the worker route run_jobs.


Hint
Take the jobs with WHERE status = 'waiting' ORDER BY id. The UPDATE and the commit go inside the for loop; the return goes after it, with len(jobs).


STEP 1 OF 8
    Ctrl + Enter to check