Skip to content

Commit 13481a1

Browse files
Fix voucher redemption race condition using pessimistic locking (#1399)
Co-authored-by: Mario Behling <[email protected]>
1 parent b3c50bf commit 13481a1

File tree

1 file changed

+8
-1
lines changed

1 file changed

+8
-1
lines changed

app/eventyay/base/services/cart.py

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1000,7 +1000,9 @@ def _get_voucher_availability(self):
10001000
vouchers_ok = {}
10011001
self._voucher_depend_on_cart = set()
10021002
for voucher, count in self._voucher_use_diff.items():
1003-
voucher.refresh_from_db()
1003+
# Use pessimistic locking to prevent race conditions
1004+
# This ensures only one thread can check and modify voucher availability at a time
1005+
voucher = Voucher.objects.select_for_update().get(pk=voucher.pk)
10041006

10051007
if voucher.valid_until is not None and voucher.valid_until < self.now_dt:
10061008
raise CartError(error_messages['voucher_expired'])
@@ -1010,6 +1012,11 @@ def _get_voucher_availability(self):
10101012
).exclude(pk__in=[op.position.id for op in self._operations if isinstance(op, self.ExtendOperation)])
10111013
cart_count = redeemed_in_carts.count()
10121014
v_avail = voucher.max_usages - voucher.redeemed - cart_count
1015+
1016+
# Validate availability after acquiring lock to prevent over-redemption
1017+
if v_avail < count:
1018+
raise CartError(error_messages['voucher_redeemed'])
1019+
10131020
if cart_count > 0:
10141021
self._voucher_depend_on_cart.add(voucher)
10151022
vouchers_ok[voucher] = v_avail

0 commit comments

Comments
 (0)