feat(api): correct batch-workout race reconciliation under contention

Concurrent-write integration coverage that validates the W10 fix-C
reconciliation pattern carries over correctly to the
/api/workouts/batch endpoint:

- Two batches with overlapping (userId, source, externalId) tuples
  posted in parallel resolve to exactly one DB row per tuple (the
  composite unique index enforces it; createMany.skipDuplicates
  absorbs duplicate-key conflicts).
- Each response body's per-entry envelope sums to its own
  aggregate counts — the invariant the iOS sync cursor relies on
  to checkpoint correctly.
- Aggregate inserted counts across both responses equal the row
  count actually present, cross-validating that createMany.count
  is the truth source rather than the older
  'stored vs not-stored' diff (which was a no-op under standard
  Postgres semantics because every attempted row IS in the DB
  after the call, written by whichever batch won the race).
- A two-batch shared-key fixture asserts the shared tuple lands
  exactly once across both responses.

Also adds workouts and workout_routes to the integration suite's
truncate list so beforeEach() actually clears them between tests
rather than relying on CASCADE from users.
This commit is contained in:
Marc-André Bombeck committed 2026-05-14 18:14:30 +02:00
1 parent 45bbfe4c9b
commit 6ed925fee8
2 files changed
+222

No files matched your search

+2
View File
@@ -67,6 +67,8 @@ export async function truncateAllTables(client: PrismaClient): Promise<void> {
"user_achievements",
"users",
"withings_connections",
"workout_routes",
"workouts",
];
const quoted = tables.map((t) => `"${t}"`).join(", ");