Repository navigation
Release v0.4.1
Performance
- Checkpointers on a psycopg_pool pool now run operations concurrently, each on its own pooled connection, so one saver can serve many agents, threads or tasks. Before, every operation in the process waited on one lock, which on a multi-region cluster capped a saver at a few operations per second. A saver on a single connection (including
pipeline=True) still runs one operation at a time. Concurrency is bounded by the pool'smax_size; when every connection is busy, operations wait for the pool (30 s by default) and aPoolTimeoutis not retried. On a 9-node, 3-region CockroachDB cluster (Docker, with simulated cross-region latency and a stubbed model) with 16 LangGraph agents sharing one pooled saver, throughput went from 0.27 turns per second (median turn 55 s) in 0.4.0 to about 7.5 turns per second (median turn about 1.2 s) in 0.4.1.
Fixed
putwith channel blobs,put_writeswith several writes, anddelete_threadnow send BEGIN, their statements and COMMIT as one batch, so each write is saved completely or not at all, even when the caller is cancelled or fails partway. Before, an interrupted write could leave blob rows without their checkpoint, part of a task's pending writes (which LangGraph treats as a finished task), or a half-deleted thread. A write still takes one round trip, and CockroachDB can still retry it on the server. Single-statement writes are unchanged. Connections not in autocommit mode use psycopg'sconn.transaction()(the docs already requireautocommit=True).- On a single connection that the caller also uses, a write made inside a transaction the caller opened joins it as a savepoint, so the caller's commit or rollback decides. The saver never rolls back a transaction it did not open, and an error inside the caller's transaction is raised at once, without retries, so the caller's own retry loop can restart the whole transaction.
- Pooled checkpointers retry when psycopg reports the connection broken, even when the error text is unfamiliar. This fixed checkpointer retries over TLS: on CockroachDB Cloud a dropped connection reads "SSL error: unexpected eof while reading" (OpenSSL 3), which was not recognized, so a pooled saver gave up instead of retrying. That wording is now also in
TRANSIENT_MESSAGES. A definite SQLSTATE still decides first, as on the SQLAlchemy path. AsyncCockroachDBSaver's sync methods raise a clearRuntimeErrorinstead of hanging when the saver's loop is closed, stopped, or the process forked, and they cancel the abandoned work.putandput_writescalled from the saver's own loop thread now raiseInvalidStateErrorinstead of deadlocking (the other sync methods already did).- Engines built with
loop=(CockroachDBEngine(engine, loop=...)orCockroachDBEngine.from_engine(engine, loop=...)) fail fast with a clearRuntimeErrorinstead of hanging when that loop is closed, never started, stops or closes during a call, or the process forked (including an engine rebuilt in a child around the parent's loop). A loop that has not started yet gets a short grace period (LOOP_START_TIMEOUT, 1 s). RetryPolicyrejects NaN and infinity in its numeric fields, anddelay()can no longer overflow for large attempt numbers or multipliers.- The flat
jitter/retry_jitterargument now raisesTypeErrorfor anything other thanTrue,False,0or1. It used to be read by truthiness, so 0.3 became a jitter of 0.5. UseRetryPolicy(jitter=...)for a ratio. - Retry warnings name the underlying driver error when an application wraps it, and never include SQL text or parameter values.
aadd_texts,add_texts,afrom_textsandfrom_textsvalidate input (lengths, ids, duplicates, metadata JSON andbatch_size) before calling the embedding model, so invalid input no longer pays for embedding calls. The namespace conflict check is a database query and still runs after embedding.- Applying a vector index whose name already exists with different prefix columns (order or count) or a different partition size you set now logs a WARNING naming each difference, like the existing opclass warning, instead of doing nothing silently. Applying the same index again logs nothing. A partition size you leave unset accepts whatever the index has.
- Checkpointer setup logs its bootstrap step as "checkpointer setup" instead of a second "checkpointer migration 0".
What's Changed
- fix: concurrent and atomic checkpointers, TLS retries and the 0.4.0 leftovers by @viragtripathi in #15
Full Changelog: v0.4.0...v0.4.1