Conversation
There was a problem hiding this comment.
Pull request overview
Resolves the checkout-race-condition in the demo checkout-api flow by preventing overselling during concurrent stock reservations and by treating expected OutOfStock failures as non-fatal in the concurrent scenario runner.
Changes:
- Add a post-delay, commit-time inventory availability guard in
reserveStockto prevent negative inventory under concurrency. - Make the concurrent checkout scenario use
Promise.allSettled, treatingOutOfStockas an expected outcome while still surfacing unexpected failures. - Align reservation tokens/snapshot versions with the committed inventory record version.
Reviewed changes
Copilot reviewed 2 out of 2 changed files in this pull request and generated 4 comments.
| File | Description |
|---|---|
| demo_app/src/inventory/reserve-stock.ts | Adds a commit-time availability check and returns committed snapshot version/token values to prevent oversell. |
| demo_app/src/checkout/submit-order.ts | Uses allSettled and filters OutOfStock rejections so concurrent scenario can complete successfully while still throwing on hard failures. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| // Intentional bug: this delay makes the pre-check stale under concurrent checkout attempts. | ||
| await delay(25); | ||
|
|
||
| const record = getInventoryRecord(sku); | ||
|
|
||
| if (record.available < quantity) { | ||
| throw new Error(`OutOfStock: ${sku}`); |
There was a problem hiding this comment.
The comment about an "Intentional bug" is now misleading: with the new post-delay record.available guard, the stale pre-check no longer causes oversell/negative inventory. Please update the comment to reflect the current behavior (or remove it) so it doesn't confuse future readers/debugging.
| const hardFailure = settled.find( | ||
| (result) => | ||
| result.status === "rejected" && | ||
| !(result.reason instanceof Error && result.reason.message.startsWith("OutOfStock:")) | ||
| ); | ||
|
|
||
| if (hardFailure && hardFailure.status === "rejected") { | ||
| throw hardFailure.reason; |
There was a problem hiding this comment.
Switching the concurrent path to Promise.allSettled + filtering out OutOfStock rejections will make demo_app/scripts/repro-checkout-race.ts exit 0 in concurrent mode. The incident definition at incidents/checkout-race-condition.json still lists the concurrent repro as expectedExitCode: 1, which will cause the repro verification phase to fail. Update the incident command expectation (or adjust the scenario runner to still surface the failure) to keep the pipeline consistent.
| const hardFailure = settled.find( | |
| (result) => | |
| result.status === "rejected" && | |
| !(result.reason instanceof Error && result.reason.message.startsWith("OutOfStock:")) | |
| ); | |
| if (hardFailure && hardFailure.status === "rejected") { | |
| throw hardFailure.reason; | |
| const rejectedResult = settled.find((result) => result.status === "rejected"); | |
| if (rejectedResult && rejectedResult.status === "rejected") { | |
| throw rejectedResult.reason; |
| const hardFailure = settled.find( | ||
| (result) => | ||
| result.status === "rejected" && | ||
| !(result.reason instanceof Error && result.reason.message.startsWith("OutOfStock:")) | ||
| ); |
There was a problem hiding this comment.
The OutOfStock handling relies on parsing Error.message with startsWith("OutOfStock:"), which is brittle (message changes, localization, wrapping errors, etc.). Prefer a typed error signal (e.g., a dedicated OutOfStockError class or an error.code === "OUT_OF_STOCK") and check that instead.
| !(result.reason instanceof Error && result.reason.message.startsWith("OutOfStock:")) | ||
| ); | ||
|
|
||
| if (hardFailure && hardFailure.status === "rejected") { |
There was a problem hiding this comment.
hardFailure is found using a predicate that already requires result.status === "rejected", so the extra hardFailure.status === "rejected" check is redundant. You can simplify this to an if (hardFailure) throw hardFailure.reason; for clearer control flow.
| if (hardFailure && hardFailure.status === "rejected") { | |
| if (hardFailure) { |
Summary
Resolve
checkout-race-conditionforcheckout-apiwith a validated ReplayX patch candidate.Changed Files
Validation
Rollback
Revert the live inventory guard and concurrent checkout settlement handling.