Changelog¶
All notable changes to langchain-cockroachdb will be documented in this file.
The format is based on Keep a Changelog, and this project adheres to Semantic Versioning.
Unreleased¶
[0.5.1] - 2026-10-28¶
Fixed¶
- A checkpointer or store could fail with
prepared statement "_pg3_N" does not exist(SQLSTATE 26000) after a batch failed before its statements ran, even when the error that failed the batch was retryable. psycopg prepares a statement on its sixth run on a connection (prepare_threshold=5, the default), and in pipeline mode it records the statement as prepared before the server confirms it. When an earlier statement in the batch failed, such as BEGIN refused by CockroachDB's license limit, the server never prepared it, but psycopg kept using it. A checkpoint write then failed on its retry. A store read failed on that connection every time until the connection closed, so a pool could hand out the broken connection until it was replaced, by default within an hour. Statements sent in a pipeline are no longer prepared. Statements outside a pipeline, such as the checkpointers' reads on a pool or on a connection withoutpipeline=True, still are, and every connection keeps its ownprepare_threshold. examples/long_term_memory.pyand the store guide no longer print pydantic serializer warnings. Their tools now annotate the runtime asToolRuntime[Context, dict]. A plainToolRuntimeexpects no context, andToolRuntime[Context]fails on Python 3.10, which cannot fill in the default of the second type argument.- The checkpointer guide said PostgresSaver's
prepare_threshold=0turns prepared statements off. It prepares every query from its first run.
[0.5.0] - 2026-10-26¶
Added¶
CockroachDBStoreandAsyncCockroachDBStore, LangGraph long-term memory stores on CockroachDB. They implement LangGraph'sBaseStorewith the API of langgraph-checkpoint-postgres'sPostgresStore(from_conn_stringwithpipelineandpool_config,setup(),indexandttl), so they work withcreate_agent(store=...),StateGraph.compile(store=...)and Deep Agents'StoreBackend.from_conn_stringalso takesretry_policy,connect_timeout,prepare_thresholdandconnect_kwargs, as the checkpointers do. The two stores,CockroachDBIndexConfig,CockroachDBTTLConfigandPoolConfigare exported fromlangchain_cockroachdb.store, and all of them butPoolConfigfromlangchain_cockroachdbtoo.- Semantic search on CockroachDB's
VECTORtype, with a C-SPANN index that has the namespace as a prefix column.indextakes LangGraph'sdims,embedandfields, plusdistance_type(cosine,l2orinner_product),cspannpartition sizes,flatto skip the index, andbeam_size, which is set for each search. A search under a namespace first lists the namespaces at or under it, then ranks with the index within them, and runs a second, exact query when a filter or expired items leave its page short, unless a count shows that the index returned every vector of those namespaces. A search of the whole store, every search of aflatstore, and a search over more than 1000 namespaces (MAX_SEARCH_NAMESPACES) rank every match exactly instead. So a filter or expired items never leave a page short while more matches exist. - Expiry through CockroachDB row-level TTL. With a
ttlconfiguration,setup()turns on the TTL job for the items and vectors tables, on a schedule fromsweep_interval_minutesorttl_job_cron(@hourlyby default), so no sweeper thread is needed. Vectors carry their item's expiry. Expired items are left out of reads, searches and listings by default, even by a store built withoutttl;ttl={"omit_expired": False}turns that off. Withrefresh_on_read, a read pushes back the expiry of the items it returns, in the same statement.sweep_ttl(),start_ttl_sweeper()andstop_ttl_sweeper()work as in PostgresStore. A store built withoutttlapplies no default expiry, never refreshes one, and itssetup()leaves an existing TTL setting alone. On CockroachDB v25.4,disable_ttl(),adisable_ttl()and the TTL step ofsetup()retry while another process changes the same table. delete_namespace(namespace_prefix)andadelete_namespace, which delete every item and vector in a namespace and its descendants in one transaction and return the number of items deleted.- The sync methods of
AsyncCockroachDBStore(batch,get,put,search,deleteandlist_namespaces) raiseRuntimeErrorinstead of hanging when the store's event loop is closed or not running, or when called from a forked child, as those ofAsyncCockroachDBSaverdo. From the loop's own thread they raiseasyncio.InvalidStateError. - Differences from PostgresStore: the puts and deletes of one batch commit in one transaction; every call retries transient errors under the store's
RetryPolicy; aCockroachDBStoreon a pool runs batches concurrently; putting an item again or deleting it removes its old vectors; a filter that compares with a number matches numeric values only (PostgresStore compares text), nested dictionaries match nested keys, and$nematches items without the key; results with the same score orupdated_atare ordered by namespace and key, so paging is stable;table_nameandschemaconfigure the tables (defaultsstore,store_vectorsandstore_migrationsinpublic); aqueryon a store withoutindexlogs a WARNING and runs a plain search; aftersetup(), a changeddimsordistance_typeraises, and other partition sizes, orflatwith an index present, log a WARNING;disable_ttl()andadisable_ttl()remove the table TTL setting, anddrop_vector_index()andadrop_vector_index()drop the vector index so thatsetup()can build it again, for example with another distance; and there is nodeserializer=argument, because psycopg decodes JSONB before the store sees it. examples/long_term_memory.py, a LangGraph agent that saves a memory in one thread and recalls it in another, and a guide, an example page and an API page for the store in the documentation.
Fixed¶
enable_ttlanddisable_ttl(andaenable_ttlandadisable_ttl) on the checkpointers no longer fail when several processes run them at once on CockroachDB v25.4. That version refuses to change a table's TTL settings while another schema change on it runs, with SQLSTATE 0A000, which was not retried. These TTL changes now retry that error under the saver's retry policy, even if itsretry_onwould refuse it;max_attempts=1turns this off. The code and phrase are exported asSCHEMA_CHANGE_BUSY_SQLSTATEandSCHEMA_CHANGE_BUSY_MESSAGES, withis_schema_change_busy()andschema_change_policy().- A checkpointer or store on one connection in pipeline mode (
pipe=, orfrom_conn_string(..., pipeline=True)) could hang when a large write met a slow link, such as TLS to a distant cluster. psycopg's pipeline sync can leave the last bytes of a write unsent, so the server waited for them while the client waited for the server. The library now sends them before it waits.CockroachDBSaverandAsyncCockroachDBSaverhad this since 0.2.0;CockroachDBStoreandAsyncCockroachDBStorehave the fix from the start. Pools, and single connections without a pipeline, were not affected.
[0.4.1] - 2026-10-16¶
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".
[0.4.0] - 2026-10-14¶
Added¶
RetryPolicy: one configurable object for which errors are retried and how, set on the engine and inherited by the vector store and chat history; checkpointers take one too. Exported defaults (RETRYABLE_SQLSTATES,TRANSIENT_MESSAGES, ...) andget_sqlstate().- Retries for 40001, 40003, every 08xxx code, 57P01, 58C00, 58C01 and 53300, plus connection loss reported without a code. Failed logins and SSL or configuration errors are never retried.
- Optional hang guard (
attempt_timeout,ddl_attempt_timeout) that abandons and retries an attempt the server never answers. aadd_embeddings/add_embeddings,amax_marginal_relevance_search_by_vector, the full langchain-postgres filter grammar ($between,$exists,$like,$ilike,$not).CockroachDBChatMessageHistoryaccepts a sharedCockroachDBEngineand has a publicacreate_table_if_not_exists().- Checkpointer
from_conn_stringoptions:retry_policy,connect_timeout,prepare_threshold,connect_kwargs. connect_timeoutonCockroachDBEngine.from_connection_string(default 10 seconds).CockroachDBEngine.from_engine(engine, loop=...).postgresql://andcockroachdb://URL spellings are normalized to the psycopg driver.examples/resilience.py, end-to-end tests for every example, LangGraph checkpointer conformance tests, and integration tests that inject real CockroachDB faults.
Fixed¶
- Errors were classified by psycopg2's
pgcode, which psycopg 3 does not set, and by words like "connection" and "timeout" in any exception. Missing tables and statement timeouts were retried. - The vector store never applied its retry settings; chat history and both checkpointers had no retries.
- Sync methods failed under concurrency ("bound to a different event loop") because every call ran its own event loop against a shared pool. They now share one background loop.
- Metadata filters broke on apostrophes, could carry injected SQL, and failed on boolean and null values. All values are bound parameters now.
- Vector indexes ignored the distance strategy and were always
vector_l2_ops, so cosine and inner product searches never used them. vector_search_beam_sizestayed set on pooled connections after a search.- Chat history
add_messagescould leave partial writes and could duplicate messages on retry. - MMR re-embedded every candidate through the embeddings API.
from_textsfailed withidsor vector store arguments.- Concurrent checkpointer
setup()could fail on a duplicate migration version, and calling the saver while iteratinglist()deadlocked. - Running setup a second time failed with SQLSTATE 57000 when
autocommit_before_ddlwas off and the table was schema_locked, which is the default for new tables on v26. This hitinit_vectorstore_tablewithnamespace_columnorcreate_tsvector, and chat historycreate_table_if_not_exists. Schema setup now runs one statement per implicit transaction. - In pipeline mode, checkpointer
setup()andenable_ttl()/disable_ttl()failed withautocommit_before_ddloff. That covered a fresh database and an upgrade that adds migrations. Every schema statement now runs in its own implicit transaction. - After a session is canceled (
CANCEL SESSION, or a connection dropped by a load balancer or drain), CockroachDB can answer the next query with SQLSTATE XXUUU "read tcp ...: i/o timeout". v25.4 always does; v26.3 sometimes does. The checkpointers raised it. It is now retried on a fresh connection. - In a vector store with a namespace, adding a document whose id already belonged to another namespace overwrote that row and moved it into the writing namespace. Such ids now raise
ValueErrorbefore anything is written, and the upsert never updates a row in another namespace. - New connections set
gssencmode=disableunless the URL or connect args choose otherwise. CockroachDB has no GSS encryption, and this avoids a Kerberos lookup that can crash forked workers on macOS. adrop_vector_index()anddrop_vector_index()can be called without an index to drop the default-named index, as the upgrade note says.from_texts(namespace=...)now creates the namespace column.- Two spellings of the same UUID in one call are rejected as duplicates.
Changed¶
- psycopg 3 only:
psycopg2-binaryis no longer a dependency. Requireslangchain-core1.x and caps every dependency at its next major version. Python 3.10 to 3.14. - Supports CockroachDB v25.4 or later; tested on v25.4 LTS and v26.3.
- The vector store's retry settings default to the engine's (5 attempts) instead of 3.
max_retries < 1raisesValueErrorinstead of never calling the function.- Table, schema, column and index names must be plain SQL identifiers.
- Indexes created by 0.3.x for COSINE or INNER_PRODUCT should be dropped and applied again.
add_texts/add_embeddingsreject duplicate ids within one call (ValueError), and metadata that is not JSON serializable, including NaN and infinity (TypeError). Both checks run before anything is written. They used to fail partway, after earlier batches had committed.- Range filters (
$gt,$gte,$lt,$lte,$between) with a string value now compare as text, and only match string fields, like langchain-postgres. In 0.3.0,{"year": {"$gte": "2020"}}matched numeric fields; pass a number to compare numerically. - Top-level keys next to
$and/$orused to be ignored. They are now combined with AND, so results can narrow. - The
retry_*attributes on the engine and vector store are now read-only properties. Set them throughretry_policyor the constructor. - Chat message ids are generated by the client before the first attempt.
batch_sizeis capped at 16,383. Long id lists are split automatically.
[0.3.0] - 2026-07-15¶
Fixed¶
- Hybrid search now actually executes. Setting
hybrid_search_configpreviously had no effect: only vector similarity ran and the FTS half never fired (#7). The regular search methods now run both searches in parallel and fuse the results.
Added¶
fts_languageparameter onainit_vectorstore_tablefor non-English tsvector columnsfetch_kkeyword argument to control the hybrid candidate pool sizefts_rank_normalizationoption exposing the ts_rank bitmask- Score normalization for weighted sum fusion
Changed¶
- Default fusion method is now reciprocal rank fusion. Pass
fusion_type="weighted_sum"for the previous default. HybridSearchConfigvalidatesfts_query_language
0.2.1 - 2026-03-24¶
Changed¶
- Checkpointer query performance optimization: replaced correlated subqueries with separate lightweight queries and raw BYTEA deserialization.
- Batch fetching in
list(): 3 total queries instead of 2N+1. - Prepared statement caching (
prepare_threshold=5) for query plan reuse.
Added¶
- CockroachDB row-level TTL:
enable_ttl()/disable_ttl()(sync and async) - Checkpointer performance benchmark script
0.2.0 - 2026-02-19¶
Added¶
- LangGraph checkpointer:
CockroachDBSaverandAsyncCockroachDBSaverfor persisting LangGraph workflow state (short-term memory, human-in-the-loop, fault tolerance) - Multi-tenancy: opt-in namespace column on vectorstore for tenant isolation
- Vectorstore standard tests compliance (25/25 LangChain standard tests passing)
- Checkpointer guide, multi-tenancy guide, API reference, runnable examples
Changed¶
- Clarified isolation level support: works with both SERIALIZABLE (default, recommended) and READ COMMITTED
- Test suite expanded from 92 to 177 tests
0.1.0 - 2024-01-31¶
Added¶
- Initial release of langchain-cockroachdb
CockroachDBEnginefor connection managementAsyncCockroachDBVectorStorefor async vector operationsCockroachDBVectorStoresync wrapperCSPANNIndexfor C-SPANN vector indexesHybridSearchConfigfor FTS + vector searchCockroachDBChatMessageHistoryfor chat persistence- Automatic retry logic with exponential backoff
- Comprehensive test suite (92 tests)
- Full documentation with mkdocs
- 7 working examples
- Support for Python 3.10-3.12
Features¶
- Native CockroachDB VECTOR type support
- C-SPANN distributed vector indexes
- Advanced metadata filtering
- Connection pooling with health checks
- Configurable retry parameters
- Both async and sync APIs
- Multi-tenant index support