Skip to content

v1.21.19 — LangGraph Postgres connection pooling (ADR-111)

Choose a tag to compare

@jgouviergmail jgouviergmail released this 08 Jul 10:37

LangGraph Postgres connection pooling (ADR-111)

A performance & resilience release lifting the #1 scalability bottleneck from the 2026-07 audit (S2/A7): the LangGraph checkpointer and store each ran on a single persistent PostgreSQL connection per worker, and langgraph serializes every operation of a connection behind an instance-level asyncio.Lock — so all concurrent conversations of a worker queued on one mutex for every checkpoint read/write.

Changed

  • Checkpointer & store on psycopg AsyncConnectionPools — settings-driven per-worker sizes (LANGGRAPH_CHECKPOINT_POOL_MIN/MAX_SIZE=1/8, LANGGRAPH_STORE_POOL_MIN/MAX_SIZE=1/4), identical connection kwargs, fail-fast boot open, documented connection budget vs max_connections=200.
  • Pool-aware _cursor override in InstrumentedAsyncPostgresSaver: in langgraph-checkpoint-postgres==3.1.0 only the store bypasses the shared lock on pools — the saver holds it unconditionally (langgraph#7259). The override replicates the store's own pattern verbatim; two canary tests order its removal once upstream ships the fix.
  • Saturation behavior is now bounded and observable (PoolTimeoutcheckpoint_errors_total{error_type="timeout"}) instead of an invisible unbounded queue.

Fixed

  • Dead-connection lockup: a connection dropped while idle (e.g. PostgreSQL restart) no longer disables conversation persistence until the API restarts — checkout validation replaces it transparently.

Proven

  • Benchmark (20 concurrent tasks × 5 rounds, real PostgreSQL): ×1.10 at 4 KB payloads, ×1.42 at 64 KB — the speedup grows with the I/O-bound share.
  • 20 concurrent compiled-graph invocations: every checkpoint persisted and resumable.
  • tests/unit 8,916 passed · tests/agents 958 passed (HITL replay/resumption included) · MyPy strict on 848 files · Docker boot healthy, pooled=true on both factories.
  • Rollback without redeploy: LANGGRAPH_CHECKPOINT_POOL_MAX_SIZE=1 reproduces the former serialized behavior.

Full details: CHANGELOG.md and docs/architecture/ADR-111-LangGraph-Postgres-Connection-Pooling.md.