Name the two operations
This case has two different customers trying to reserve one remaining unit. It is not the same as one payment event delivered twice. Keep synthetic order identities separate and define which stock state counts as reserved, when it expires and whether failed payment releases it. The product owner decides that lifecycle before implementation.
- Start the isolated database with exactly one invented unit.
- Use a controlled barrier to make the two requests reach the availability decision together.
- Disable real gateways, fulfilment and customer email; a reservation test does not require a charge.
Protect the decision and its update together
Laravel documents lockForUpdate and recommends a surrounding transaction. The availability read and reservation update need to share the agreed protected operation. A lock acquired after an earlier unprotected availability read may be too late. All competing reservation paths must follow compatible rules; adding one lock call does not prove an entire stock system is correct.
- Confirm actual database and transaction behaviour on the agreed test backend.
- Keep network/payment calls outside a long stock-lock critical section unless a separately designed protocol requires otherwise.
- Define lock timeout/deadlock outcomes as a controlled retry or safe failure, not a success message.
Assert the losing result as well as the winner
An authored regression starts both synthetic reservations together and asserts that exactly one claims the unit, stock is not negative and the other receives the agreed unavailable outcome. Repeat the sequential baseline and transaction-failure case. This is a proposed deterministic harness, not a load test, payment reconciliation or executed concurrency proof.
- Check stored reservations and response outcomes, not only a final stock number.
- If the owner permits backorders, use that explicit rule rather than imposing rejection.
- Releasing reservations and reconciling charged orders are separate lifecycle tests.
Non-fit and priced route
One ordinary stock-decision bug with a controlled synthetic reproduction may fit fix-one-bug-with-regression-test, from £295 after bounded reproduction and a fixed repair quote. If the problem remains intermittent, affects multiple stock systems or requires real charges/data, that scope does not fit: the existing operator must establish a safe reproduction or a separate reservation/reconciliation design. A security disclosure, financial recovery or performance/load-testing project is also excluded.
- Initial inputs are the invented one-unit rule, request sequence and expected outcomes, not order histories, code or credentials.
- No production data correction, payment action or live release is authorised; the price is an untested proposal.
Sources and limits
- Laravel 12.x query builder: pessimistic locking; MIT-licensed documentation Checked 2026-10-11.
- lockForUpdate provides a pessimistic lock on selected records.
- The guide recommends wrapping pessimistic locks in a transaction so the retrieved data remains protected through the operation.
- A transaction releases acquired locks on completion or rollback.
- Current single-bug synthetic reproduction and exclusions Checked 2026-10-11.