fix(cardService): wrap title + linkIds updates in single atomic transaction#477
Open
hariom888 wants to merge 2 commits into
Open
fix(cardService): wrap title + linkIds updates in single atomic transaction#477hariom888 wants to merge 2 commits into
hariom888 wants to merge 2 commits into
Conversation
…action updateCard previously executed card.update (title) and the cardLink delete/create cycle as two independent operations. A process crash, DB timeout, or FK violation between them left the card with a new title but stale links — a permanently inconsistent state with no rollback path. Changes: - Move both the card.update (title) and the cardLink.deleteMany / cardLink.createMany calls inside a single app.prisma. block so that either all changes commit or none do. - Hoist the platformLink ownership check to before the transaction is opened, eliminating the TOCTOU window where a concurrent request could delete a platformLink between validation and createMany. Ownership validation on user-owned immutable rows is safe to perform outside the transaction. Tests added (cards.test.ts): - Happy path: both title and links commit in one call. - Rollback path: createMany failure after card.update → 500, no findUnique called, both operations ran inside the same tx. - Pre-transaction 403: foreign linkId → never called, no writes of any kind. - Title-only update: linkIds absent → deleteMany not called. - Links-only update: title absent → card.update not called.
|
@hariom888 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. |
CI — Checks FailedBackend — FAIL
Mobile — SKIP
Web — SKIP
Last updated: |
Contributor
Author
Typecheck failure is pre-existing on
|
Harxhit
reviewed
Jun 7, 2026
Harxhit
left a comment
Collaborator
There was a problem hiding this comment.
Please address lint errors.
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 #437.
updateCardexecutedcard.update(title) and thecardLinkdelete/create cycle as two independent operations. A crash, DB timeout, or FK violation between them left the card with its new title but stale links — permanently inconsistent with no compensating write. Additionally, theplatformLinkownership check ran outside the inner transaction, creating a TOCTOU window where a concurrent request could delete aplatformLinkbetween validation andcreateMany.Root cause
Fix
$transaction— bothtx.card.update(title) andtx.cardLink.deleteMany/tx.cardLink.createMany(links) execute inside one Prisma interactive transaction. Either all changes commit or none do.platformLink.findManyruns before any transaction is opened. A 403 (ownership failure) now never consumes a transaction slot and never results in any write. Ownership validation is safe outside the transaction becauseplatformLinkrows are user-owned and not mutated within this request.Testing
Five new cases added to the
PUT /api/cards/:id — atomicitysuite:$transaction, bothcard.updateandcardLinkops calledcreateManyfails aftercard.updatefindUniquenot called, both ops ran inside same txlinkId(403)$transactionnever called, no writes at allcardLink.deleteManynot called,platformLink.findManynot calledcard.updatenot calledFiles changed
apps/backend/src/services/cardService.tsapps/backend/src/__tests__/cards.test.ts