fix(cards): prevent concurrent deletions from removing all user cards#376
Open
Ridanshi wants to merge 1 commit into
Open
fix(cards): prevent concurrent deletions from removing all user cards#376Ridanshi wants to merge 1 commit into
Ridanshi wants to merge 1 commit into
Conversation
dea2add to
1f7baae
Compare
Collaborator
|
@Ridanshi Fix merge conflicts and please add terminal screen short proof for unit tests. |
The DELETE /api/cards/:id handler performed an ownership lookup, a card count check, and the delete as three separate non-transactional operations. Under concurrent requests both could observe count > 1 and both proceed to delete, leaving the user with zero cards. Move the count guard, optional default-card promotion, and the delete into a single Prisma $transaction with Serializable isolation. The database now serializes concurrent count reads against the write, rolling back the second conflicting transaction so the invariant (user always retains at least one card) cannot be violated even under load. Adds a focused test suite covering normal deletion, last-card rejection, default-card promotion, and four concurrency-guard scenarios.
1f7baae to
a16b19a
Compare
Contributor
Author
|
Conflicts resolved and branch updated. Resolution summary:
Unit tests rerun successfully: npx vitest run src/tests/cards.test.ts --reporter=verbose Result: This includes the concurrency regression coverage added in this PR:
|
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.

Summary
Fixes a transactional race condition in card deletion that could allow concurrent requests to remove all cards belonging to a user despite intended last-card protection.
Root Cause
The delete flow previously executed:
as separate non-transactional database operations.
Under concurrent requests, multiple deletes could simultaneously observe the same card count and proceed, resulting in users ending up with zero cards.
Fix
Moved the deletion guard and delete flow into a single Prisma
$transactionusingSerializableisolation.The transaction now atomically performs:
This guarantees that concurrent delete requests cannot violate the invariant that a user must always retain at least one card.
Concurrency Behavior
Before:
After:
Tests Added
Added focused regression coverage for:
Files Changed
apps/backend/src/routes/cards.tsapps/backend/src/__tests__/cards.test.tsCloses #356