Skip to content

fix(checkout-api): resolve checkout-race-condition#10

Open
Spkap wants to merge 1 commit intomainfrom
replayx/incident-checkout-race-001-mo64ii1s
Open

fix(checkout-api): resolve checkout-race-condition#10
Spkap wants to merge 1 commit intomainfrom
replayx/incident-checkout-race-001-mo64ii1s

Conversation

@Spkap
Copy link
Copy Markdown
Collaborator

@Spkap Spkap commented Apr 19, 2026

Summary

Resolve checkout-race-condition for checkout-api with a validated ReplayX patch candidate.

Changed Files

  • demo_app/src/inventory/reserve-stock.ts
  • demo_app/src/checkout/submit-order.ts

Validation

  • Concurrent checkout repro: exit 0
  • Sequential checkout sanity: exit 0

Rollback

Revert the live inventory guard and concurrent checkout settlement handling.

Copilot AI review requested due to automatic review settings April 19, 2026 18:52
Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR addresses the checkout-race-condition incident in the demo checkout-api flow by preventing overselling during concurrent reservations and by making the concurrent repro scenario tolerate expected “out of stock” outcomes.

Changes:

  • Add a commit-time inventory availability check in reserveStock to prevent concurrent oversell.
  • Return reservation metadata based on the committed inventory record version (not the pre-check snapshot).
  • Update the concurrent checkout scenario to use Promise.allSettled and only fail the scenario for non-OutOfStock errors.

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated 2 comments.

File Description
demo_app/src/inventory/reserve-stock.ts Adds a post-delay guard against stale snapshot checks and returns committed snapshot version/token.
demo_app/src/checkout/submit-order.ts Treats OutOfStock as an expected concurrent outcome while still surfacing unexpected failures.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines 19 to +23
await delay(25);

const record = getInventoryRecord(sku);

if (record.available < quantity) {
Copy link

Copilot AI Apr 19, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The concurrent-staleness comment immediately above this delay reads like the race is still an intentional bug, but the new post-delay availability guard changes that story. Please update the comment to describe the current behavior (snapshot pre-check can be stale, but the commit-time record check prevents overselling) so future readers don't assume the function is still intentionally incorrect.

Copilot uses AI. Check for mistakes.
Comment on lines +19 to +23
const hardFailure = settled.find(
(result) =>
result.status === "rejected" &&
!(result.reason instanceof Error && result.reason.message.startsWith("OutOfStock:"))
);
Copy link

Copilot AI Apr 19, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Using error.message.startsWith("OutOfStock:") to classify expected rejections is brittle (it couples control flow to a string that can change and doesn’t work for non-Error rejections). Prefer a structured signal (e.g., a dedicated OutOfStockError class, error.name, or an error code field) so this filtering remains stable as messages evolve.

Copilot uses AI. Check for mistakes.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants