Skip to content

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

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

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

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 19:51
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 app by preventing overselling during concurrent checkouts and ensuring the concurrent repro path no longer fails due to expected “out of stock” rejections.

Changes:

  • Add a post-delay availability guard in reserveStock to prevent committing negative inventory under concurrency.
  • Generate reservation metadata (token + snapshotVersion) from the committed inventory record state.
  • Update the concurrent checkout repro to tolerate expected OutOfStock failures via Promise.allSettled.

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 availability check and returns reservation metadata based on the committed record state.
demo_app/src/checkout/submit-order.ts Uses Promise.allSettled and filters expected out-of-stock rejections to keep the concurrent repro from failing.

💡 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 inline comment describing the post-snapshot delay as an “Intentional bug” is now misleading because the new post-delay record.available < quantity guard prevents the stale pre-check from causing an oversell. Please update/remove the comment so future readers don’t think the race is still intentionally present (or clarify that the delay is retained only to simulate concurrency while remaining safe).

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.

runCheckoutRaceScenario classifies “expected” failures by string-matching Error.message.startsWith("OutOfStock:"). This is brittle (message format changes will flip behavior) and couples control flow to human-readable text. Consider using a dedicated error type or a stable discriminator (e.g., error.name / code property) from reserveStock, and check that instead.

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