Summary
Prevent possible race conditions during concurrent first-card creation where multiple cards may temporarily or permanently become isDefault: true for the same user.
Contexts
Current first-card initialization logic in apps/backend/src/routes/cards.ts relies on a standalone count() query before card creation:
const cardCount = await app.prisma.card.count({
where: { userId }
});
isDefault: cardCount === 0
Under concurrent request conditions:
- two create-card requests may execute simultaneously
- both requests may observe
cardCount === 0
- both requests may create cards with
isDefault: true
The current implementation already improves transactional consistency for:
- card link replacement
- default-card switching
However, first-card initialization still appears vulnerable to concurrent state inconsistencies depending on DB isolation behavior and request timing.
Tasks
Acceptance Criteria
Proposed Approach
Initial investigation suggests the issue originates from relying on a non-atomic count() check before insert creation.
A possible direction is to move first-card initialization into a transaction-safe flow so concurrent requests cannot independently decide they should both become the default card.
Potential implementation strategies to investigate:
- wrap default-card initialization inside a Prisma transaction with stronger isolation guarantees
- replace the current
count()-based approach with existence checks that execute atomically with creation logic
- enforce a database-level consistency guarantee so only one
isDefault: true card can exist per user
- handle transactional conflicts/retries gracefully during simultaneous create requests
Possible DB-level approaches may include:
- partial unique index/constraint scoped to
(userId, isDefault=true)
- transactional update/create sequencing
- optimistic retry handling on unique constraint violations
The implementation should preserve existing behavior for:
- manual default-card switching
- card updates/deletion flows
- card link replacement logic
Regression testing should simulate concurrent create-card requests to verify deterministic behavior under race conditions.
Area
backend
Difficulty
advanced
Summary
Prevent possible race conditions during concurrent first-card creation where multiple cards may temporarily or permanently become
isDefault: truefor the same user.Contexts
Current first-card initialization logic in
apps/backend/src/routes/cards.tsrelies on a standalonecount()query before card creation:Under concurrent request conditions:
cardCount === 0isDefault: trueThe current implementation already improves transactional consistency for:
However, first-card initialization still appears vulnerable to concurrent state inconsistencies depending on DB isolation behavior and request timing.
Tasks
Acceptance Criteria
Proposed Approach
Initial investigation suggests the issue originates from relying on a non-atomic
count()check before insert creation.A possible direction is to move first-card initialization into a transaction-safe flow so concurrent requests cannot independently decide they should both become the default card.
Potential implementation strategies to investigate:
count()-based approach with existence checks that execute atomically with creation logicisDefault: truecard can exist per userPossible DB-level approaches may include:
(userId, isDefault=true)The implementation should preserve existing behavior for:
Regression testing should simulate concurrent create-card requests to verify deterministic behavior under race conditions.
Area
backendDifficulty
advanced