Conversation
There was a problem hiding this comment.
Pull request overview
Resolves the demo checkout oversell/race scenario by revalidating live inventory after an async gap and making the concurrent repro flow tolerate expected “out of stock” outcomes.
Changes:
- Add a post-delay (live record) availability guard in
reserveStockto prevent stale pre-check commits under concurrency. - Return the committed
record.snapshotVersionin reservation metadata to match the post-commit state. - Update concurrent checkout execution to use
Promise.allSettled, treatingOutOfStockas an expected rejection while surfacing unexpected failures.
Reviewed changes
Copilot reviewed 2 out of 2 changed files in this pull request and generated 3 comments.
| File | Description |
|---|---|
| demo_app/src/inventory/reserve-stock.ts | Adds live inventory recheck after delay and aligns returned snapshot/version values with committed state. |
| demo_app/src/checkout/submit-order.ts | Uses allSettled to handle concurrent checkout outcomes, filtering expected out-of-stock failures and rethrowing unexpected ones. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| const record = getInventoryRecord(sku); | ||
|
|
||
| if (record.available < quantity) { | ||
| throw new Error(`OutOfStock: ${sku}`); |
There was a problem hiding this comment.
The inline comment about the delay being an "Intentional bug" is now misleading because the new live-record availability recheck after the delay mitigates the stale pre-check problem. Please update the comment to reflect the current behavior (e.g., that the delay simulates an async gap and the code revalidates availability before committing).
| const hardFailure = settled.find( | ||
| (result) => | ||
| result.status === "rejected" && | ||
| !(result.reason instanceof Error && result.reason.message.startsWith("OutOfStock:")) | ||
| ); |
There was a problem hiding this comment.
This logic treats OutOfStock as an expected failure by parsing the error message prefix. Relying on string matching for control flow is brittle (message wording changes will alter behavior); consider using a dedicated error type or at least an error discriminator like error.name === "OutOfStock"/an error code property, and only fall back to message parsing if needed.
| if (hardFailure && hardFailure.status === "rejected") { | ||
| throw hardFailure.reason; | ||
| } |
There was a problem hiding this comment.
throw hardFailure.reason; may throw a non-Error value (Promise rejections can be any type), which makes upstream handling/logging inconsistent. Consider normalizing to an Error (wrapping non-Error reasons) before throwing.
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.