Debugging races: make the bug happen on purpose
Race conditions are bugs that depend on timing, so they vanish when you look at them. Clicking through the site never shows them; a busy Tuesday does. The skill this section trains is refusing to reason about a race in your head and instead building a small experiment that makes it happen every time.
Reproduce with load, not with clicks
A race needs two things to overlap. Send many identical requests at once (a thread pool and a short script is enough) against a state where only one of them should win: a product with 5 units in stock, an email address that may be registered once. Then assert an invariant about the data afterwards: stock never negative, sold units equal to stock removed, exactly one account per email. If the invariant breaks, you have a reproduction. If it never breaks, make the window bigger (more requests, a slower step in the middle) rather than concluding it is fine.
Read-modify-write is the classic shape
Most races in web apps have the same shape: read a value, decide in Python, write a value back. Between the read and the write, someone else's request does the same thing, and one of the writes silently wins (a lost update). The same shape appears as check-then-insert: "does this email exist? no, so insert it", where two requests both see "no".
The cure is to make the check and the change one step the database guarantees. A conditional UPDATE ... WHERE stock >= n with an F() expression is atomic; select_for_update() inside a transaction locks the row while you work; a unique constraint is the only reliable arbiter of "at most one".
Let the constraint decide, and handle its answer
When the rule is "at most one", enforce it with a unique constraint and make the code expect the failure: insert inside an atomic block, catch the integrity error, and turn it into the normal answer (409, "already registered"). A pre-check is still fine as a friendly fast path, but it can never be the guard. And be suspicious of in-process fixes: a Python lock only serialises threads of one process, and production runs several workers on several machines.
In both labs the graders run the same experiment you should: many concurrent requests plus an invariant check, and a deterministic test that forces the interleaving.