From 822cc4e1d542c550a1e649a6fa17be01975e82ac Mon Sep 17 00:00:00 2001 From: Siavash Sameni Date: Sun, 7 Jun 2026 06:02:49 +0400 Subject: [PATCH] =?UTF-8?q?docs:=20sync=20from=20backend=202c5e80d=20?= =?UTF-8?q?=E2=80=94=20db=20audit=20waves=205-6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- 09 - Audits/Activity Log.md | 10 ++++++++++ 09 - Audits/DB Query & Schema Audit - 2026-06-06.md | 8 +++++--- 2 files changed, 15 insertions(+), 3 deletions(-) diff --git a/09 - Audits/Activity Log.md b/09 - Audits/Activity Log.md index d8082e2..be0c3a3 100644 --- a/09 - Audits/Activity Log.md +++ b/09 - Audits/Activity Log.md @@ -11,6 +11,16 @@ entries on top. Maintained by agents per the rule in `../AGENTS.md`. --- +### 2026-06-07 — backend@2c5e80d, frontend@1f8fdc9 — DB audit Waves 5-6 chat create and points transaction enforcement + +**Commits:** `2c5e80d` `1f8fdc9` +**Touched:** backend `src/db/repositories/drizzle/DrizzleChatRepo.ts`, `src/db/repositories/drizzle/DrizzleUserRepo.ts`, `src/db/repositories/drizzle/DrizzlePaymentRepo.ts`, `__tests__/drizzle-chat-repo.test.ts`, `__tests__/drizzle-user-repo.test.ts`, `package.json`, `package-lock.json`; frontend `package.json`; docs `09 - Audits/DB Query & Schema Audit - 2026-06-06.md`, `09 - Audits/Activity Log.md` +**Why:** Continue the 8-wave Critical/High plan after pulling Mojtaba's Forgejo changes (`backend@6e097c5`, `frontend@bcf9f03`). Wave 5 closes H13 by inserting the chat welcome message atomically with the chat row; Wave 6 closes H25 by refusing point balance/ledger writes on an unbound repo. The backend commit also keeps the pulled PaymentRepo user-id batch resolver null-safe for typecheck. +**Verification:** backend `npm test -- --runTestsByPath __tests__/drizzle-chat-repo.test.ts __tests__/drizzle-user-repo.test.ts --runInBand`, `npm test -- --runTestsByPath __tests__/drizzle-payment-repo-export.test.ts __tests__/drizzle-chat-repo.test.ts __tests__/drizzle-user-repo.test.ts --runInBand`, `BASE_URL=http://127.0.0.1:5001 scripts/smoke/db-audit-service-regressions.sh` (14 suites / 43 tests), `npm run typecheck`, `git diff --check`; frontend `git diff --check` for package bump. Pushed to Forgejo; direct `dev` SSH remote timed out and `origin` remained intentionally skipped. +**Linked docs updated:** [[09 - Audits/DB Query & Schema Audit - 2026-06-06]] + +--- + ### 2026-06-06 — backend@f22794a/51ca048, frontend@4a86dc7 — DB audit Wave 4 delivery-code atomicity **Commits:** `f22794a` `51ca048` `4a86dc7` diff --git a/09 - Audits/DB Query & Schema Audit - 2026-06-06.md b/09 - Audits/DB Query & Schema Audit - 2026-06-06.md index d1fc9a9..1c978c5 100644 --- a/09 - Audits/DB Query & Schema Audit - 2026-06-06.md +++ b/09 - Audits/DB Query & Schema Audit - 2026-06-06.md @@ -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.