Architecture¶
Understanding the internal structure of langchain-cockroachdb.
Package Structure¶
langchain_cockroachdb/
├── __init__.py # Public API exports
├── engine.py # Connection management
├── async_vectorstore.py # Async vector store implementation
├── vectorstores.py # Sync wrapper
├── indexes.py # Index abstractions
├── hybrid_search_config.py # Hybrid search configuration
├── chat_message_histories.py # Chat persistence
├── retry.py # Retry logic
├── _guard.py # Hang guard (attempt timeout)
├── _loop.py # Background event loop for sync methods
├── _connection.py # Connection helpers
├── _sql.py # SQL generation
└── _filters.py # Metadata filter translation
Core Components¶
CockroachDBEngine¶
Purpose: Connection and transaction management
Key Features: - AsyncEngine from SQLAlchemy - Connection pooling - Retry configuration - Table initialization
Pattern:
engine = CockroachDBEngine.from_connection_string(url)
# Engine manages connection lifecycle
await engine.aclose()
AsyncCockroachDBVectorStore¶
Purpose: Primary vector store implementation
Key Features: - Async-first design - Batch operations - Metadata filtering - Index management
Pattern:
vectorstore = AsyncCockroachDBVectorStore(
engine=engine,
embeddings=embeddings,
collection_name="docs",
)
await vectorstore.aadd_texts(texts)
CockroachDBVectorStore¶
Purpose: Synchronous wrapper for async operations
Implementation:
- Runs async methods on one background event loop
- Same API as async version (without a prefix)
- Safe from any number of threads and from inside a running event loop
Pattern:
Design Patterns¶
Async-First Architecture¶
All I/O operations are async by default:
# Core operations
async def aadd_texts(texts: list[str]) -> list[str]:
async with self.engine.engine.connect() as conn:
# Async database operations
...
Benefits: - High concurrency - Better resource utilization - Natural fit for modern Python
Retry Decorators¶
Each idempotent statement is wrapped by RetryPolicy.arun. The callable passed to arun checks out a fresh connection per attempt:
async def aadd_texts(texts: list[str]) -> list[str]:
async def attempt():
async with self.engine._connect() as conn:
return await conn.execute(insert_statement, params)
await self.retry_policy.arun(attempt, label="insert into documents")
Benefits: - Transparent retry logic - Configurable per component - Handles transient CockroachDB errors automatically - Fresh connection per retry
Dependency Injection¶
Engine is injected into vector store:
vectorstore = AsyncCockroachDBVectorStore(
engine=engine, # Injected dependency
embeddings=embeddings,
collection_name="docs",
)
Benefits: - Testable (can mock engine) - Flexible configuration - Connection reuse
Database Schema¶
Vector Store Table¶
CREATE TABLE {table_name} (
id UUID PRIMARY KEY,
content TEXT,
embedding VECTOR(n),
metadata JSONB,
created_at TIMESTAMPTZ DEFAULT now()
);
Chat History Table¶
CREATE TABLE message_store (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
session_id TEXT NOT NULL,
message JSONB NOT NULL,
created_at TIMESTAMPTZ DEFAULT now()
);
CREATE INDEX ON message_store (session_id);
SQL Generation¶
Filter Translation¶
Metadata filters are translated to SQL:
filter = {"category": "tech", "year": {"$gte": 2023}}
# Becomes:
WHERE metadata->>'category' = 'tech'
AND CAST(metadata->>'year' AS INTEGER) >= 2023
Distance Operators¶
Different distance strategies use different operators:
| Strategy | Operator | SQL |
|---|---|---|
| COSINE | <=> |
ORDER BY embedding <=> :query |
| EUCLIDEAN | <-> |
ORDER BY embedding <-> :query |
| INNER_PRODUCT | <#> |
ORDER BY embedding <#> :query |
Error Handling¶
Retryable Errors¶
| SQLSTATE | Meaning | Why a retry is safe |
|---|---|---|
40001 |
Serialization failure ("restart transaction") | The transaction did not commit |
40003 |
Result is ambiguous | Every write the library issues is idempotent |
08xxx |
Connection exceptions, including CockroachDB sqlproxy's 08C00 |
Idempotent writes, and the pool replaces the connection |
57P01 |
Node draining or shutting down | Idempotent writes |
58C00 |
Range unavailable (CockroachDB) | Usually clears once leases move |
58C01 |
Connection failure between CockroachDB nodes | Usually clears once leases move |
53300 |
Too many connections | Connection slots free up |
Errors without a SQLSTATE and CockroachDB's uncategorized codes (XXUUU, XX000) are matched against known connection-loss phrases.
See Retries and Resilience for full details.
Testing Strategy¶
Unit Tests¶
Test logic without database: - Filter SQL generation - Index configuration - Retry logic
Integration Tests¶
Test with real CockroachDB: - Vector operations - Index creation - Concurrent operations
Testcontainers¶
Use Docker containers for integration tests:
@pytest.fixture
async def crdb_container():
container = CockroachDBContainer()
container.start()
yield container.get_connection_url()
container.stop()
Performance Considerations¶
Connection Pooling¶
- Pool size: 10 (default)
- Max overflow: 20 (default)
- Pre-ping: Enabled
Batch Operations¶
- Default batch size: 100
- Configurable per operation
- Smaller batches for large vectors
Query Optimization¶
- Use indexes for large datasets
- Filter before vector search when possible
- Tune beam size for accuracy/speed tradeoff
Extension Points¶
Custom Distance Functions¶
Extend with custom distance strategies:
Custom Filters¶
Extend filter translator for custom operators:
Next Steps¶
- Contributing - Join development
- Testing - Write tests
- API Reference - Detailed API