Prune LangGraph checkpoints per thread and delete them with the conversation #85

Merged
ryan merged 1 commits from perf/prune-checkpoints into main 2026-10-13 03:42:05 -04:00
Owner

The SQLite checkpointer stores a full conversation snapshot on every graph step and nothing pruned old ones (about 1.6 MB per checkpoint at 200 turns). Deleting a conversation also left its thread behind.

  • Pruning: after each turn, only the newest CHECKPOINT_KEEP_PER_THREAD (default 20) checkpoints of that thread are kept, plus their pending writes. It runs under the saver's own lock, so it never races a checkpoint write. State reads only use the newest checkpoint, and a test confirms aget_state still returns all 50 messages after pruning 25 turns down to 5. Measured at about 2 ms for a 200-checkpoint thread, so it runs inline. Invalid or < 1 values fall back to 20 with a warning.
  • Delete: deleting a conversation also deletes its LangGraph thread. A checkpoint-store error is logged and doesn't fail the request.
  • CHECKPOINT_KEEP_PER_THREAD is in .env.example and docker-compose.yml.

Out of scope: base64 images are still stored inside checkpoints.

Review: Sonnet worker plus two review rounds. Round 1 added the missing tests and env wiring, guarded against bad values, and asked for a latency measurement. Round 2 was clean.

🤖 Generated with Claude Code

The SQLite checkpointer stores a full conversation snapshot on every graph step and nothing pruned old ones (about 1.6 MB per checkpoint at 200 turns). Deleting a conversation also left its thread behind. - **Pruning:** after each turn, only the newest `CHECKPOINT_KEEP_PER_THREAD` (default 20) checkpoints of that thread are kept, plus their pending writes. It runs under the saver's own lock, so it never races a checkpoint write. State reads only use the newest checkpoint, and a test confirms `aget_state` still returns all 50 messages after pruning 25 turns down to 5. Measured at about 2 ms for a 200-checkpoint thread, so it runs inline. Invalid or `< 1` values fall back to 20 with a warning. - **Delete:** deleting a conversation also deletes its LangGraph thread. A checkpoint-store error is logged and doesn't fail the request. - `CHECKPOINT_KEEP_PER_THREAD` is in `.env.example` and `docker-compose.yml`. **Out of scope:** base64 images are still stored inside checkpoints. **Review:** Sonnet worker plus two review rounds. Round 1 added the missing tests and env wiring, guarded against bad values, and asked for a latency measurement. Round 2 was clean. 🤖 Generated with [Claude Code](https://claude.com/claude-code)
ryan added 1 commit 2026-10-13 03:42:02 -04:00
Prune LangGraph checkpoints per thread and delete them with the conversation
CI / backend (pull_request) Successful in 42s
CI / frontend (pull_request) Failing after 7s
CI / backend (push) Successful in 42s
CI / frontend (push) Failing after 7s
c329d15f7f
Every graph step stores a full snapshot of the conversation, nothing pruned
old ones, and deleting a conversation left its thread behind (about 1.6 MB per
checkpoint at 200 turns). After each turn, only the newest
CHECKPOINT_KEEP_PER_THREAD (default 20) checkpoints of that thread are kept,
plus their pending writes; state reads only use the newest, and pruning a
200-checkpoint thread takes about 2 ms. Deleting a conversation also deletes
its thread, without failing the request if the checkpoint store errors.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
ryan merged commit fe18a8371d into main 2026-10-13 03:42:05 -04:00
Sign in to join this conversation.
No Reviewers
No Label
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: ryan/simbarag#85