docs: record H34-H36 audit closure

This commit is contained in:
Siavash Sameni
2026-06-07 16:40:44 +04:00
parent af9bce3967
commit ffc0d83c60
2 changed files with 19 additions and 6 deletions

View File

@@ -91,6 +91,9 @@ updated: 2026-06-07
| H17: template payment completion per-request status loop → one serializable bulk status update with an all-rows-updated guard | `957c356` v2.9.38 |
| H18: email-code registration split referrer/user/temp writes → one serializable auth-store registration transaction, with referral bonus left post-commit best-effort | `957c356` v2.9.38 |
| H38: `blog_posts.status` stored as text instead of pgEnum → runtime schema and migration `0027_blog_post_status_enum.sql` enforce `blog_post_status` | `e8cb64c` / `05b402c` v2.10.1 |
| H34: chat `messages` JSONB array hot path → normalized `chat_messages` table, backfill migration, row-level send/read/edit/delete methods, DB pagination | `04de406` v2.10.3 |
| H35: chat `participants`/`unreadCounts` JSONB arrays → normalized `chat_participants` + `chat_unread_counts` tables and relational list/count predicates | `04de406` v2.10.3 |
| H36: dispute `messages` JSONB array → normalized `dispute_messages` table with migration backfill and relational hydration | `04de406` v2.10.3 |
---
@@ -520,33 +523,33 @@ When `input.limit` is not supplied (the notification fan-out path), `findSellers
---
### 34. Chat messages stored as a JSONB array column — full-document rewrite on every message send, no DB-level pagination
### 34. Chat messages stored as a JSONB array column — full-document rewrite on every message send, no DB-level pagination | **FIXED** `04de406` v2.10.3
> **Category:** Wrong Schema | **File:** `src/db/schema/chat.ts:100-103`
All messages for a chat are in a single `messages jsonb` column. Every `sendMessage()`, `editMessage()`, `deleteMessage()`, or `markMessagesAsRead()` does a full read-modify-write of the entire array. For a chat with 10,000 messages this means writing ~10 MB of JSON on each send. `getChatMessages()` in ChatService.ts (lines 444-451) fetches all messages then sorts and slices in JavaScript — there is no way to paginate at the DB level.
**Fix:** Migrate messages to a separate `chat_messages` table (chat_id FK, sender_id, content, message_type, timestamp, is_read, is_edited, reply_to_id). This enables server-side cursor/keyset pagination, targeted UPDATE for mark-as-read, atomic INSERT for new messages, and GIN index on content for search. Move unread_counts to a `chat_unread_counts` table similarly.
**Fix:** `04de406` v2.10.3 adds `chat_messages` with chat/sender/type/timestamp/read/edit/reply indexes plus migration backfill from `chats.messages`. `ChatService` now calls row-level repo methods for send, page reads, mark-read, edit, and delete; the JSON column remains as a compatibility mirror only.
---
### 35. Chat participants and unread counts stored as JSONB arrays instead of relational tables
### 35. Chat participants and unread counts stored as JSONB arrays instead of relational tables | **FIXED** `04de406` v2.10.3
> **Category:** Wrong Schema | **File:** `src/db/schema/chat.ts:95-114`
The `participants` and `unreadCounts` JSONB columns are queried for specific userIds and counts on every user-facing chat request. These are fundamentally relational: each participant belongs to one chat, has a userId FK, role, and isActive flag. Storing them as JSONB means membership lookups are full table scans and the DB cannot enforce FK integrity or use B-tree indexes on userId.
**Fix:** Create a `chat_participants` table (chat_id, user_id, role, is_active, joined_at, last_seen) and a `chat_unread_counts` table (chat_id, user_id, count). Index `(user_id, is_active)` on participants and `(user_id, count)` on unread_counts. This converts O(N) JSONB scans to O(1) indexed lookups.
**Fix:** `04de406` v2.10.3 adds `chat_participants` and `chat_unread_counts` with backfill from the old JSONB arrays. `DrizzleChatRepo` hydrates returned chats from those rows, uses relational `EXISTS` predicates for membership/unread list/count queries, and syncs normalized rows on create/save.
---
### 36. Dispute messages stored as JSONB array — unbounded growth, no sender/read-status filtering without full scan
### 36. Dispute messages stored as JSONB array — unbounded growth, no sender/read-status filtering without full scan | **FIXED** `04de406` v2.10.3
> **Category:** Wrong Schema | **File:** `src/db/schema/dispute.ts:99-102`
The `messages` column stores all dispute messages in a single JSONB array. Any filter by `senderId`, `isRead`, or timestamp must deserialise the entire array. There is no GIN index. Every message append requires a full read-modify-write of the blob.
**Fix:** Create a `dispute_messages` table (dispute_id, sender_id, content, timestamp, is_read, attachments). Index on `(dispute_id, timestamp)`. This enables efficient per-dispute pagination and sender filtering.
**Fix:** `04de406` v2.10.3 adds `dispute_messages` with `(dispute_id, timestamp)`, sender/timestamp, and read-status indexes plus migration backfill from `disputes.messages`. `DrizzleDisputeRepo` batch-hydrates dispute messages from normalized rows, falling back to JSON only when rows do not exist yet.
---