Repository navigation
Description
Context: Follow-up from the code review of #48 (membership-edges rewrite of list_user_chats, merged as fe68b17). The two must-fix findings from that review were fixed before merge; this is the deferred should-fix, filed per the reviewer's recommendation to let the author choose the behavior policy explicitly rather than decide it silently.
Where: crates/storage/src/repo/thread_repo.rs, ThreadRepo::create_thread — the per-member edge-write loop appended after the METADATA put_item (for member in &thread.member_ids { self.put_chat_edge(...).await?; }).
Problem: The loop propagates the first put_chat_edge error with ? after the METADATA row (and any earlier members' edges) have already been committed. So a mid-loop failure (throttle, transient DynamoDB error) means:
- The route (
routes/chat.rs,POST /api/v1/chats) returns a 500 claiming the chat was not created. - But the
THREAD#<id>/METADATArow and the first n−1 edges are committed — the chat exists and is partially visible. - A client that retries on the 500 creates a duplicate chat, while the original sits half-indexed (some members can see it, the failed-at member and later members cannot until a membership change or a
backfill_chat_edgesre-run repairs it).
Unlike add_chat_member/remove_chat_member (fixed in #48 to a single transact_write), create_thread can't use one transaction: DynamoDB caps TransactWriteItems at 100 and group membership is unbounded (no MAX_MEMBER cap exists in routes/chat.rs), so the METADATA row + N edges can exceed the limit.
The decision to make (why this is a ticket, not an auto-fix): two reasonable policies —
- Best-effort edges (recommended): log-and-continue on individual
put_chat_edgefailures instead of returningErr; the chat is created and the missing edges self-heal viabackfill_chat_edgesor the next membership change. Matches the self-heal philosophy already used indelete_threadandlist_user_chats. Downside: a member could briefly not see a chat they're in. - Fail-loud (current behavior): keep returning
Erron any edge failure. Downside: the duplicate-chat-on-retry trap above, plus a committed-but-500'd chat.
Suggested approach: adopt policy 1 (best-effort with a warn! per failed edge carrying thread_id + user_id), OR — if fail-loud is preferred — make create_thread roll back the METADATA row on edge failure so the 500 is truthful (no orphan). Either way, add a test for the partial-failure path.
Severity: S4 — no current user-facing repro on the test stack (0 existing chats today), latent; matters as chat usage grows, especially group chats.
Non-blocking siblings from the same review (not filing separately unless wanted): (a) list_user_chats now does N sequential get_thread GetItems — bounded by the caller's own chat count, a candidate for a future batch_get_item helper; (b) chat membership add/remove write-paths don't emit SecurityAudit rows (pre-existing gap, unlike sharing.rs/documents.rs; per CLAUDE.md sharing-adjacent writes should).
Activity
fix(chat): create_thread edge writes are best-effort, not fail-loud (#49
fix(chat): create_thread edge writes are best-effort, not fail-loud (#49