mirror of
https://github.com/langchain-ai/langgraph.git
synced 2026-08-17 21:25:46 +02:00
The Postgres store removes expired items only via the background TTL sweeper, so between sweeps a read can still return a logically expired row. Adds an opt-in flag so reads can filter expired rows at query time, closing the window without depending on sweep cadence. ## Changes - Add `omit_expired: bool` to `TTLConfig` (default `False`). When unset or `False`, behavior is unchanged - When enabled, inject `(expires_at IS NULL OR expires_at > NOW())` into the read query builders on `BasePostgresStore`, so `get`/`search`/`list_namespaces` do not surface an expired row: - **GET** (`_get_batch_GET_ops_queries`): predicate on both the final SELECT *and* the refresh `UPDATE` — the update is driven from an unfiltered key list, so gating only the SELECT would hide an expired row yet still refresh it back to life. - **SEARCH** (`_prepare_batch_search_queries`): inside the inner scans (before `LIMIT`/`OFFSET`), which also gates the refresh `UPDATE` fed by `search_results` and keeps pagination correct - **list_namespaces** (`_get_batch_list_namespaces_queries`): predicate appended to the existing scan conditions. ## Testing - Four behaviors covered sync + async, across the `default`/`pipe`/`pool` fixtures: expired-unswept row omitted from all read paths (with a raw SQL check proving the row is still physically present), default/explicit-`False` still returns it, `refresh_ttl=True` doesn't resurrect an expired row while still extending live ones, and search pagination stays correct when an expired row falls inside the page window. - Full `checkpoint-postgres` suite green on PG16 (210 passed); `ruff format`/`check` clean on both packages.