diff --git a/libs/checkpoint-postgres/langgraph/checkpoint/postgres/__init__.py b/libs/checkpoint-postgres/langgraph/checkpoint/postgres/__init__.py index c2a8ea985..aa20f5339 100644 --- a/libs/checkpoint-postgres/langgraph/checkpoint/postgres/__init__.py +++ b/libs/checkpoint-postgres/langgraph/checkpoint/postgres/__init__.py @@ -448,11 +448,12 @@ class PostgresSaver(BasePostgresSaver): Two-stage query, both stages cover ALL requested channels: - * Stage 1 (paged): dynamic SELECT over `checkpoints` with K parallel - JSONB key lookups (one column pair per channel) — no subquery, no - aggregation. Pages newest-first by `checkpoint_id` with a cursor; - page size is `_DELTA_PAGE_SIZE`. Stops paging when every channel - has found its seed or the chain is exhausted. + * Stage 1 (paged): dynamic SELECT over `checkpoints` with three + columns per channel: its version, an `EXISTS` probe for a stored + blob at that version, and its inline value. Pages newest-first by + `checkpoint_id` with a cursor; page size is `_DELTA_PAGE_SIZE`. + Stops paging when every channel has found its seed or a page comes + back short. * Stage 2 (per-channel UNION ALL): one branch per channel reading `checkpoint_writes` filtered to that channel's specific diff --git a/libs/checkpoint-postgres/langgraph/checkpoint/postgres/base.py b/libs/checkpoint-postgres/langgraph/checkpoint/postgres/base.py index 607c6d58d..00019c5a9 100644 --- a/libs/checkpoint-postgres/langgraph/checkpoint/postgres/base.py +++ b/libs/checkpoint-postgres/langgraph/checkpoint/postgres/base.py @@ -431,9 +431,11 @@ class BasePostgresSaver(BaseCheckpointSaver[str]): (a) it found a stored value for its channel — a blob or an inline primitive (channel becomes seeded), (b) it reached a real root (parent_of[cid] is None — fully - materialized at this point), or + materialized at this point), (c) the next ancestor cid isn't in `parent_of` yet (waiting for - a later page; the cursor stays put). + a later page; the cursor stays put), or + (d) the target's own row isn't in `parent_of` yet (the walk has + not started; no cursor is set, so a later page retries). Mutates `chain_by_ch`, `seed_ver_by_ch`, `seed_inline_by_ch`, `walk_cursor_by_ch`, and `seeded` in place.