Skip to content

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

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

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

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:36
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

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 reserveStock to prevent stale pre-check commits under concurrency.
  • Return the committed record.snapshotVersion in reservation metadata to match the post-commit state.
  • Update concurrent checkout execution to use Promise.allSettled, treating OutOfStock as 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.

Comment on lines 21 to +24
const record = getInventoryRecord(sku);

if (record.available < quantity) {
throw new Error(`OutOfStock: ${sku}`);
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 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).

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.

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.

Copilot uses AI. Check for mistakes.
Comment on lines +25 to +27
if (hardFailure && hardFailure.status === "rejected") {
throw hardFailure.reason;
}
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.

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.

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