docs: sync from backend 2c5e80d — db audit waves 5-6

This commit is contained in:
Siavash Sameni
2026-06-07 06:02:49 +04:00
parent 9f246be2b8
commit 822cc4e1d5
2 changed files with 15 additions and 3 deletions

View File

@@ -66,6 +66,8 @@ updated: 2026-06-06
| H12: `updateReferralStats` count/update outside a transaction → serializable row-lock transaction | `885745e` v2.9.20 |
| H26: `processReferralReward` independent points + referralStats writes → one idempotent `grantReferralReward` transaction for points, referralStats, and ledger row | `885745e` v2.9.20 |
| H24: `verifyAndMarkDeliveryCodeUsed` read-check-then-write race → one conditional `UPDATE ... RETURNING` decides delivery-code consumption, with post-miss read only for failure reason | `f22794a` / `51ca048` v2.9.21 |
| H13: `DrizzleChatRepo.create` insert-then-update welcome message → build initial system message, lastMessage, and unread counts in the INSERT payload | `2c5e80d` v2.9.24 |
| H25: `updatePoints` + `createPointTransaction` could run outside one transaction → runtime guard requires a transaction-bound repo or explicit tx for both money writes | `2c5e80d` v2.9.24 |
---
@@ -609,7 +611,7 @@ The method reads the current row (line 558), performs an atomic CAS UPDATE with
### 3. findPaymentById issues two separate SELECT queries for buyer and seller instead of a JOIN
> **Category:** N+1 Query | **File:** `src/db/repositories/drizzle/DrizzleMarketplaceRepo.ts:490-533`
> **Category:** N+1 Query | **File:** `src/db/repositories/drizzle/DrizzleMarketplaceRepo.ts:490-533` | **FIXED** `61aa42a` v2.9.20
`findPaymentById` does SELECT * FROM payments WHERE id = $1, then conditionally fires one SELECT for the buyer and one for the seller. This is 3 sequential queries per call. Since `findAllPayments` calls this once per row, the cost multiplies to 1 + 2N.
@@ -679,7 +681,7 @@ The method calls `this.findRows({...})` (full table scan), deserialises every ch
### 10. ChatService.addParticipant fires N individual userRepo.findById queries for participant validation
> **Category:** N+1 Query | **File:** `src/services/chat/ChatService.ts:782-788`
> **Category:** N+1 Query | **File:** `src/services/chat/ChatService.ts:782-788` | **FIXED**
`Promise.all(participantIds.map((id) => userRepo.findById(id)))` fires one SELECT per participant concurrently. While Promise.all avoids sequential waits, it still sends N separate queries to the database, opening N statements from the pool.
@@ -689,7 +691,7 @@ The method calls `this.findRows({...})` (full table scan), deserialises every ch
### 11. resolveUserUuid issues a separate DB round-trip per legacy ObjectId in payment create and filter paths
> **Category:** N+1 Query | **File:** `src/db/repositories/drizzle/DrizzlePaymentRepo.ts:88-100, 116-129, 786-793`
> **Category:** N+1 Query | **File:** `src/db/repositories/drizzle/DrizzlePaymentRepo.ts:88-100, 116-129, 786-793` | **FIXED**
`resolveUserUuid()` executes one SELECT per legacy ObjectId. `create()` calls it twice (buyer + seller). `normalizeUserFilter` calls it sequentially for buyerId then sellerId. No batching or caching: creating multiple payments in a loop issues at least two extra round-trips per iteration.