fix(server): bootstrap attachment cleanup without decoding the event log - #11183
ThomasCrund wants to merge 2 commits into
Conversation
The attachment-cleanup bootstrap added in pingdotgg#9871 replayed every event after its cursor and filtered for thread.deleted and thread.reverted in JS. Even after pingdotgg#10777 released consumed pages, a single 500-row page can hold over a gigabyte of serialized payload (historical session records carrying a 73 MB lastError), so the backend still exhausts its heap before the cursor moves and the desktop app crash-loops at startup on affected profiles. Cleanup now reads only deleted and reverted events through a new type-filtered event store query, so unrelated rows are never decoded. The head sequence is captured before replay and the cleanup cursor advances to it after a successful pass, including when no cleanup events exist. Ordering, the last-wins dedupe per thread, the recreate check, and the retry-on-failure cursor behavior are unchanged. Regression tests append a caught-up history containing an undecodable row of another type and assert bootstrap succeeds, removes the deleted thread's attachment, and lands the cursor on the head; the event store tests cover the typed reader's range and type filtering and the head query. Done with Claude Fable 5.1 in Claude Code. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
ApprovabilityVerdict: Approved at Macroscope's review found this PR approvable — This is a focused startup cleanup bug fix that filters unrelated event payloads in SQL while preserving existing projector replay, attachment cleanup, deduplication, and retry behavior. It adds targeted tests and does not change product defaults, schemas, deployment, or static-analysis configuration. You can add or adjust custom eligibility rules. Learn more. |
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: Path: .coderabbit.yaml Review profile: CHILL Plan: Advanced Run ID: 📒 Files selected for processing (6)
Included review availability: Your plan provides up to 10 included reviews per hour; 9 remain after this review. 📝 WalkthroughWalkthroughThe event store now supports typed range reads and head lookup without decoding unrelated payloads. Attachment cleanup uses these operations during bootstrap. Tests cover malformed history, cursor advancement, repeated bootstrap, empty stores, and updated fixtures. ChangesOrchestration cleanup replay
Priority: ⬆️ High Estimated code review effort: 3 (Moderate) | ~25 minutes Change: Bug fix · Severity of issue fixed: High Sequence Diagram(s)sequenceDiagram
participant ProjectionPipeline
participant OrchestrationEventStore
participant Database
participant AttachmentCleanupProjector
ProjectionPipeline->>OrchestrationEventStore: getHead()
OrchestrationEventStore->>Database: query latest event metadata
Database-->>OrchestrationEventStore: head sequence and occurredAt
ProjectionPipeline->>OrchestrationEventStore: readEventsOfTypes()
OrchestrationEventStore->>Database: query deletion and revert events
Database-->>OrchestrationEventStore: filtered event rows
OrchestrationEventStore-->>AttachmentCleanupProjector: typed cleanup events
AttachmentCleanupProjector-->>ProjectionPipeline: cleanup completed
ProjectionPipeline->>ProjectionPipeline: advance cleanup cursor
Suggested reviewers: Merge Risk: ⚪ Minimal · up to Attachment cleanup now avoids decoding unrelated event payloads while retaining bounded replay and successful cursor advancement behavior. No current merge-blocking risk was identified. 🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✨ Finishing Touches🧪 Generate unit tests (beta)
Comment |
…p-filtered-replay
Bugbot is paused — on-demand spend limit reachedBugbot uses usage-based billing for this team and has hit its on-demand spend limit. A team admin can raise the spend limit in the Cursor dashboard, or wait for the next billing cycle to continue. |
|
Thanks for the PR. We're not taking changes to the orchestration and provider layers right now: that part of the server is being rewritten for V2, and merging into the current code would either conflict with or be thrown away by that work. Closing for now. If this is still an issue once V2 lands, please reopen (or open a fresh PR against the new code) and we'll take a proper look. |
What Changed
Attachment cleanup at startup no longer replays the whole event log. A new event store query,
readEventsOfTypes, returns onlythread.deletedandthread.revertedrows, filtered in SQL.getHeadcaptures the newest sequence before replay. Bootstrap streams those rows into the same dedupe map and advances the cleanup cursor to the captured head after a successful pass, even when nothing matched. Ordering, the recreate check, and retry on a failed cleanup are unchanged.Tests cover the typed reader and the head query, plus a pipeline case where a caught-up history holds an undecodable row of another type. The old bootstrap fails it with
PersistenceDecodeError. The new one succeeds and lands the cursor on the head.Why
Fixes #11182. Follow-up to #10777.
#10777 released consumed pages, but one 500-row page can still hold over a gigabyte of JSON. On the affected profile, page 42001 to 42500 holds 22
thread.session-setevents that each carry a 73 MBlastError. Decoding that page exhausts the heap before the cursor moves, so nightlies 1400 through 1507 crash-loop at startup. Only 17 events in that history matter to cleanup.A/B on one fresh copy of the profile through
dev:desktopwith an isolated home: the parent commit, which includes #10777, hitJavaScript heap out of memoryat 3.8 GB about 4 s after spawn, three times in a row. This branch reachedbackend readyon the same copy. Cursor went from 0 to 225560, thread count unchanged.The three touched server suites pass (71 tests). Typecheck and lint are clean.
Done with Claude Fable 5.1 in Claude Code.
Checklist
🤖 Generated with Claude Code
Summary by CodeRabbit