Skip to content

fix(contacts): make concurrent first-contact upserts safe - #469

Open
vladbisceanu wants to merge 3 commits into
useplunk:nextfrom
vladbisceanu:upstream/10-first-contact-race
Open

fix(contacts): make concurrent first-contact upserts safe#469
vladbisceanu wants to merge 3 commits into
useplunk:nextfrom
vladbisceanu:upstream/10-first-contact-race

Conversation

@vladbisceanu

Copy link
Copy Markdown

Concurrent first-seen tracking requests can all observe a missing contact, then race on ContactService.upsert. The unique (projectId, email) constraint correctly selects one row, but every losing contact.create currently surfaces Prisma P2002 and turns an otherwise valid track request into a 500.

This change treats P2002 as the election result: losers read and continue with the elected contact. They never replay their stale data over the winning row. An explicit concurrent unsubscribe is preserved with a compare-and-set update, and subscription events are emitted only by the request that actually changes the stored state.

The service regression suite forces eight first-seen upserts through the same missing-row observation, covers both winner orderings, and verifies that stale concurrent unsubscribe observers emit only one state-change event. Existing normalization and cross-project isolation coverage remains in the same suite.

Verification:

  • yarn vitest run apps/api/src/services/__tests__/ContactService.test.ts — 63 passed
  • yarn eslint --config apps/api/eslint.config.mjs apps/api/src/services/ContactService.ts apps/api/src/services/__tests__/ContactService.test.ts
  • yarn workspace api build

This is the portable counterpart to vladbisceanu#10.

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.

1 participant