Repository navigation
perf(mobile): thread list resumes from the saved cache, and large gaps reload over gzip HTTP - #32287
juliusmarminge wants to merge 1 commit into
Conversation
…s reload over gzip HTTP On a cold start every client downloaded the full thread list even when its saved copy was a few events behind. The shell cache now records the server snapshot its rows descend from (`origin`: server version, database migration, omissions, and when the snapshot was taken), and a client whose cache matches the current subscription and is under 12 hours old resumes from the saved cursor instead. Replay never renews `snapshotAt`, so drift from skipped older-settled cuts and rebuilt projections stays bounded. When the gap is over the server's replay cap, the server used to answer the resume with a full snapshot over the socket, which iOS does not compress. A client that sends `httpSnapshotFallback` with `afterSequence` now gets a single `snapshot-required` item instead; it drops the origin, loads the gzip HTTP snapshot, and subscribes again from its cursor. Old servers ignore the flag and old clients never send it. Items buffered from a superseded session are dropped rather than applied to the next session's rows, and a resume that catches up bumps `generation` so thread families and search stop waiting for a snapshot. Thanks to @Bil0000 for #30732 and @emmems for #30317, which found the cold-start cost and the uncompressed socket fallback and shaped this fix. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
ApprovabilityVerdict: Approved at Macroscope's review found this PR approvable — Thread lists resume from recent, compatible cached snapshots and reload unreplayable gaps through HTTP. Provenance checks, a 12-hour age limit and authoritative snapshot fallback keep the synchronization change bounded. You can add or adjust custom eligibility rules. Learn more. |
Thread transfer impact✅ Thread transfer remains within every enforced ceiling.
Baseline: Scenario and decoded snapshot size10 historical turns, 5 command tools per turn, 878.9 KiB retained MCP result per historical turn, and a 1.05 MiB retained result in the measured turn.
Updated in place by a trusted workflow. PR artifacts are strictly validated and never executed. |
|
@coderabbitai review |
|
No actionable comments were generated in the recent review. Walkthrough
Merge Risk:
|
Action not completed
|
On a cold start every client downloaded the full thread list, even when its saved copy was a few events behind. When a client did resume from a cursor beyond the server's replay cap (1,000 events, 8 MB), the server answered with a full snapshot over the socket. iOS does not compress that, so it arrived at 1.37 MB instead of the 88 KB the gzip HTTP route costs.
Fix
origin: server version, database migration, omission flags, andsnapshotAt. A client whose cache matches its subscription and is under 12 hours old subscribes withafterSequenceinstead of loading a fresh snapshot. Replay never renewssnapshotAt, so drift stays bounded.snapshot-required. A resuming client sendshttpSnapshotFallback: true. If the server cannot replay that cursor (over the gap cap or pruned), it answers with onesnapshot-requireditem and doesn't send the snapshot. The client then loads the gzip HTTP snapshot and subscribes again from it.generation.Scope and approval
Maintainer-authored. It replaces https://git.cafe/pingdotgg/t3code/pulls/30732 at the maintainers' direction, after measuring replay against a fresh snapshot. Layer 1 of 2; https://git.cafe/pingdotgg/t3code/pulls/32288 builds on it.
Measurements
Measured on a real database with 2,279 threads:
Replay beats a snapshot on every metric up to the cap. Past roughly 1,200 dense events it costs about as much as a snapshot, which is why the cap stays at 1,000.
Real history decides how often the cap is hit: replay covers 96% of returns after 5 minutes away, 50% after 30 minutes, 25% after 2 hours, and 4% after 8 hours. Large gaps were the common case for heavy users, and those now reload over gzip HTTP.
Verification
shell-sync.test.tsgains a resume table and asnapshot-requiredcase, covering cold and warm. Reverting the loop guard fails 6 cases.ws.test.tsgains a pruned-boundary row.originround-trip.Credit
This replaces https://git.cafe/pingdotgg/t3code/pulls/30732 by @Bil0000 and builds on https://git.cafe/pingdotgg/t3code/pulls/30317 by @emmems. Both found the cold-start cost and the uncompressed socket fallback.
Built by Claude Fable 5.1 and Claude Opus 5.5 in Claude Code.
🤖 Generated with Claude Code