Skip to content

fix(cards): prevent concurrent default card race condition (#344)#385

Merged
Harxhit merged 7 commits into
Dev-Card:mainfrom
udaycodespace:fix/344-concurrent-default-card-race
Jun 7, 2026
Merged

fix(cards): prevent concurrent default card race condition (#344)#385
Harxhit merged 7 commits into
Dev-Card:mainfrom
udaycodespace:fix/344-concurrent-default-card-race

Conversation

@udaycodespace

@udaycodespace udaycodespace commented May 29, 2026

Copy link
Copy Markdown
Contributor

Summary

Fixes a race condition during concurrent first-card creation where multiple cards could be assigned isDefault: true for the same user.

The first-card initialization flow now executes inside a Prisma Serializable transaction with bounded retry handling for serialization conflicts, ensuring deterministic behavior under concurrent requests.

Closes #344

Type of Change

  • Bug fix

What Changed

Concurrency Fix

  • Updated apps/backend/src/services/cardService.ts to wrap first-card creation in a Prisma Serializable transaction.
  • Added bounded retry handling for P2034 serialization conflicts to safely recover from concurrent transaction failures.
  • Added regression tests in apps/backend/src/__tests__/cards.test.ts covering Serializable transaction usage and retry behavior.

Review Feedback Updates (commit bc1cc06)

  • Removed TypeScript-unsafe any usage from the affected service and test code.
  • Added typed response handling for card mappings.
  • Added app.log.error(...) before rethrowing unexpected failures.
  • Improved test typing for transaction mocks and retry scenarios.
  • Kept the original Serializable transaction + retry implementation unchanged.

How to Test

  1. Run the test suite and verify all existing tests continue to pass.
  2. Verify the regression tests covering Serializable transactions and P2034 retry handling pass successfully.
  3. Confirm card creation behavior remains unchanged while ensuring concurrent first-card creation cannot result in multiple default cards.

Checklist

  • I have added or updated tests for the changes I made.
  • All tests related to this change pass locally.
  • No new console.log or debug statements left in the code.

Validation Proof

Unit Tests

Commands:

pnpm prisma generate
pnpm --filter backend run test src/__tests__/cards.test.ts

Result:

  • Test Files: 1 passed
  • Tests: 23 passed (23/23)

Diff Summary

git diff --stat

Result:

apps/backend/src/__tests__/cards.test.ts | 16 ++++++++--------
apps/backend/src/services/cardService.ts | 20 +++++++++++---------
2 files changed, 19 insertions(+), 17 deletions(-)

Additional Context

The previous implementation relied on a standalone card.count() check before card creation, which introduced a TOCTOU race condition under concurrent requests.

Using Serializable transaction isolation prevents concurrent transactions from successfully creating multiple default cards for the same user, while bounded retry handling allows serialization conflicts to be resolved transparently without impacting user experience.

Note

Commit bc1cc06 was added after the initial PR submission to address maintainer review feedback related to TypeScript safety, typed responses, logging, and test improvements. The underlying concurrency-fix approach (Serializable transaction + P2034 retry handling) remains unchanged.

Validation proof attached below.

image

@Harxhit Harxhit added the gssoc:approved Required label for every approved PR. Gives the base +50 points and enables contribution tracking. label May 29, 2026
function wireTransaction() {
mockPrisma.$transaction.mockImplementation(
async (callback: (tx: typeof mockPrisma) => Promise<unknown>) => callback(mockPrisma),
async (callback: (tx: typeof mockPrisma) => Promise<unknown>, options?: any) => callback(mockPrisma),

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Can we not use any breaks ts safety.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Fixed in the latest commit.

Removed the any usage from the transaction mock and switched to type-safe typings throughout the affected test code.


return { id: card.id, title: card.title, isDefault: card.isDefault, links: card.cardLinks.map((cl: any) => cl.platformLink) }
} catch (error: any) {
if (error.code === 'P2034' && attempt < MAX_RETRIES) continue;

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

We have handleDB error util function can you use that here?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

I checked this one while updating the review comments.

handleDbError currently requires Fastify request/reply objects and is already used in the route handlers. Since cardService.ts is a service-layer module, I kept error propagation there and retained handleDbError usage in the route layer to preserve the existing separation of concerns.

I did add logging for unexpected failures before rethrowing and removed the TypeScript-unsafe error handling in the service.

return { id: card.id, title: card.title, isDefault: card.isDefault, links: card.cardLinks.map((cl: any) => cl.platformLink) }
} catch (error: any) {
if (error.code === 'P2034' && attempt < MAX_RETRIES) continue;
throw error;

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Can you add app.log.error here

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Addressed in the latest update.

Added app.log.error(error) before rethrowing unexpected failures while preserving the existing retry flow for P2034 serialization conflicts.

isolationLevel: 'Serializable' as Prisma.TransactionIsolationLevel
})

return { id: card.id, title: card.title, isDefault: card.isDefault, links: card.cardLinks.map((cl: any) => cl.platformLink) }

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Return typed response please

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Addressed in the latest update.

Replaced the previous any-based response mapping with a typed response structure and updated the card/link mapping to operate on typed data instead of untyped objects.

@Harxhit

Harxhit commented May 30, 2026

Copy link
Copy Markdown
Collaborator

Could please also add unit and lint tests terminal proof in the PR description.

@udaycodespace udaycodespace requested a review from Harxhit May 30, 2026 05:55
@udaycodespace

Copy link
Copy Markdown
Contributor Author

Could please also add unit and lint tests terminal proof in the PR description.

Addressed all review comments in the latest commit:

  • Removed TypeScript-unsafe any usage
  • Added typed response handling
  • Added app.log.error(...) before rethrowing unexpected failures
  • Preserved Serializable transaction + P2034 retry behavior
  • Added unit test and lint validation proof to the PR description

Please re-review when convenient. Thanks!

@udaycodespace

Copy link
Copy Markdown
Contributor Author

Hi @Harxhit, all requested review changes have been addressed in the latest commit.
Could you please re-review when convenient and approve if everything looks good?
Thanks! 🙌

@Harxhit Harxhit left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Please add lint, typecheck and tests terminal proofs(screen shots or gif) in the PR description.

@udaycodespace

Copy link
Copy Markdown
Contributor Author

Please add lint, typecheck and tests terminal proofs(screen shots or gif) in the PR description.

Validation screenshots attached.

Successfully validated:

  • pnpm prisma generate
  • pnpm --filter backend run test src/tests/cards.test.ts

Result:

  • Test Files: 1 passed
  • Tests: 23 passed (23/23)

I also attempted repository-wide lint and typecheck validation locally. The current branch reports ESLint configuration issues in packages/shared and no local TypeScript executable was available through pnpm exec tsc.

Please let me know if there is a preferred validation command or if I should first sync/rebase against the latest upstream main before re-running those checks.

@udaycodespace udaycodespace requested a review from Harxhit June 2, 2026 10:24
@Harxhit

Harxhit commented Jun 2, 2026

Copy link
Copy Markdown
Collaborator

Please add lint, typecheck and tests terminal proofs(screen shots or gif) in the PR description.

Validation screenshots attached.

Successfully validated:

* pnpm prisma generate

* pnpm --filter backend run test src/**tests**/cards.test.ts

Result:

* Test Files: 1 passed

* Tests: 23 passed (23/23)

I also attempted repository-wide lint and typecheck validation locally. The current branch reports ESLint configuration issues in packages/shared and no local TypeScript executable was available through pnpm exec tsc.

Please let me know if there is a preferred validation command or if I should first sync/rebase against the latest upstream main before re-running those checks.

I cannot see the screen shot ? Could you please check again?

@udaycodespace

udaycodespace commented Jun 2, 2026

Copy link
Copy Markdown
Contributor Author

Please add lint, typecheck and tests terminal proofs(screen shots or gif) in the PR description.

Validation screenshots attached.
Successfully validated:

* pnpm prisma generate

* pnpm --filter backend run test src/**tests**/cards.test.ts

Result:

* Test Files: 1 passed

* Tests: 23 passed (23/23)

I also attempted repository-wide lint and typecheck validation locally. The current branch reports ESLint configuration issues in packages/shared and no local TypeScript executable was available through pnpm exec tsc.
Please let me know if there is a preferred validation command or if I should first sync/rebase against the latest upstream main before re-running those checks.

I cannot see the screen shot ? Could you please check again?

Thanks for letting me know.

I've added the screenshots to the PR description. I've also attached them again below in this comment for visibility.

Please let me know if they're still not visible from your side.

image image

@Harxhit

Harxhit commented Jun 2, 2026

Copy link
Copy Markdown
Collaborator

@udaycodespace Lint proofs too please

@udaycodespace

Copy link
Copy Markdown
Contributor Author

@udaycodespace Lint proofs too please

I've attached the lint and typecheck outputs as requested.

image

The backend regression tests for this change pass successfully (23/23).

For lint, the current branch reports an ESLint configuration issue in packages/shared.

For typecheck, the current branch reports TypeScript errors, including some in files outside the scope of this PR.

Please let me know if you would like me to first sync/rebase against the latest upstream main before investigating those further.

@Harxhit

Harxhit commented Jun 2, 2026

Copy link
Copy Markdown
Collaborator

@udaycodespace Lint proofs too please

I've attached the lint and typecheck outputs as requested.
image

The backend regression tests for this change pass successfully (23/23).

For lint, the current branch reports an ESLint configuration issue in packages/shared.

For typecheck, the current branch reports TypeScript errors, including some in files outside the scope of this PR.

Please let me know if you would like me to first sync/rebase against the latest upstream main before investigating those further.

cd apps/backend && pnpm eslint file path file path = files you have changed from src

@github-actions

github-actions Bot commented Jun 2, 2026

Copy link
Copy Markdown

CI Results — ❌ Some checks failed

🖥️ Backend (❌ failure)

Check Status
Lint ❌ failure
Test ❌ failure
Typecheck ❌ failure

📱 Mobile (⏭️ skipped)

Check Status
Lint ⚪ unknown
Test ⚪ unknown

🌐 Web (⏭️ skipped)

Check Status
Check ⚪ unknown
Build ⚪ unknown

🕐 Last updated: Wed, 03 Jun 2026 05:04:00 GMT

@udaycodespace

Copy link
Copy Markdown
Contributor Author

Hi @Harxhit,

I've attached the requested validation screenshots.

Lint Validation:

  • npx pnpm eslint src/services/cardService.ts src/tests/cards.test.ts
  • Exit code: 0

Test Validation:

  • npx pnpm test src/tests/cards.test.ts
  • 23/23 tests passed

Typecheck Validation:

  • npx pnpm exec tsc --noEmit
  • Output attached
image image image image

Please let me know if any additional validation is required.

Thank you.

@udaycodespace

Copy link
Copy Markdown
Contributor Author

Hi @Harxhit,

I've attached the requested validation screenshots.

Lint Validation:

  • npx pnpm eslint src/services/cardService.ts src/tests/cards.test.ts
  • Exit code: 0

Test Validation:

  • npx pnpm test src/tests/cards.test.ts
  • 23/23 tests passed

Typecheck Validation:

  • npx pnpm exec tsc --noEmit
  • Output attached

image image image image
Please let me know if any additional validation is required.

Thank you.

Could you please re-run the backend CI on the latest commit when convenient? The annotations shown appear to be from an earlier revision of the PR.

Thanks!

Signed-off-by: SOMAPURAM UDAY <udaysomapuram@gmail.com>
@github-actions

github-actions Bot commented Jun 4, 2026

Copy link
Copy Markdown

CI — Checks Failed

Backend — FAIL

Check Result
Lint SKIP
Test SKIP
Typecheck SKIP

Mobile — SKIP

Check Result
Lint -
Test -

Web — SKIP

Check Result
Check -
Build -

Last updated: Sun, 07 Jun 2026 14:27:03 GMT

@udaycodespace

Copy link
Copy Markdown
Contributor Author

Hi @Harxhit,

Just following up on this PR. I've addressed all the requested review comments, added the lint, test, and typecheck validation proofs, and pushed the latest updates.

Whenever you have time, could you please take another look and let me know if anything else is needed from my side?

Thank you for your review and support.

Comment thread apps/backend/src/services/cardService.ts Outdated
@vercel

vercel Bot commented Jun 7, 2026

Copy link
Copy Markdown

@udaycodespace is attempting to deploy a commit to the Prashantkumar Khatri's projects Team on Vercel.

A member of the Team first needs to authorize it.

@udaycodespace

Copy link
Copy Markdown
Contributor Author

@Harxhit I've pushed the requested change (returnthrow).

I checked my branch and there are no changes to workflow files or package.json files. The CI failure is occurring during dependency installation (npm --prefix apps/backend install) with a workspace:* resolution error, so it appears unrelated to the change in cardService.ts.

@udaycodespace udaycodespace requested a review from Harxhit June 7, 2026 13:36
Comment thread apps/backend/src/services/cardService.ts Outdated

@Harxhit Harxhit left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

LGTM. Approving changes

@Harxhit Harxhit merged commit 8e4066e into Dev-Card:main Jun 7, 2026
4 of 6 checks passed
@github-actions

github-actions Bot commented Jun 7, 2026

Copy link
Copy Markdown

Congratulations @udaycodespace on getting PR #385 merged!

    Thank you for your contribution. Please mention @Harxhit in our Discord server to receive the appropriate GSSoC labels and recognition.

@udaycodespace

udaycodespace commented Jun 7, 2026

Copy link
Copy Markdown
Contributor Author

Hi @Harxhit,

PR #385 has been merged. Could you please assign the appropriate GSSoC labels when convenient?

Thanks for your reviews and support!

@Harxhit Harxhit added level:advanced Complex contribution involving deeper technical work. (+55 pts) quality:clean PR is well-structured, readable, and follows good practices. (×1.2 multiplier) type:performance Performance optimization (+15 pts) type:refactor Code refactoring/cleanup (+10 pts) type:bug Bug fixes (+10 pts) labels Jun 7, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

gssoc:approved Required label for every approved PR. Gives the base +50 points and enables contribution tracking. level:advanced Complex contribution involving deeper technical work. (+55 pts) quality:clean PR is well-structured, readable, and follows good practices. (×1.2 multiplier) type:bug Bug fixes (+10 pts) type:performance Performance optimization (+15 pts) type:refactor Code refactoring/cleanup (+10 pts)

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[CONSISTENCY] Concurrent first-card creation may allow multiple default cards per user

2 participants