SochDB Complete Architecture & API Reference
Core engine version: 2.0.3 Β |Β Python SDK: 0.5.9 Β |Β Node.js SDK: 0.5.3 Β |Β Go SDK: 0.4.5
License: The core engine (Rust workspace, the sochdb crate, gRPC server, MCP server) is AGPL-3.0-or-later with commercial licensing available. The language SDKs (Python, Node.js, Go) are Apache-2.0.
Repository: https://github.com/sochdb/sochdb
This is a genuine per-component split, not a full Apache relicense. The Rust core remains AGPL-3.0-or-later on disk; only the language SDKs are Apache-2.0.
Table of Contentsβ
- Design Philosophy
- Executive Summary
- Deployment Model: Thick Server / Thin Client
- System Architecture
- gRPC Service Set
- Module Structure
- SQL Execution Engine
- Storage Engine
- Auth & Security Layer
- Client SDK API
- TOON Format Internals
- MCP Protocol & Tools API
- Vector Search & HNSW Subsystem
- Transaction & MVCC API
- Context Query API
- Query Processing Pipeline
- Memory Management
- Concurrency Model
- Python SDK Architecture
- Performance Characteristics
- Configuration Reference
Design Philosophyβ
SochDB is built around four core principles:
1. Token Efficiency Firstβ
Every design decision prioritizes minimizing tokens when data is consumed by LLMs:
Traditional: LLM β JSON β SQL Result β Query Optimizer β B-Tree
~150 tokens for 3 rows
SochDB: LLM β TOON β Columnar Scan β Path Resolution
~50 tokens for 3 rows (66% reduction)
2. Path-Based Accessβ
O(|path|) resolution instead of O(log N) tree traversal:
Path: "users/42/profile/avatar"
TCH Resolution:
ββ users β Table lookup (O(1) hash)
β ββ 42 β Row index (O(1) direct)
β ββ profile β Column group (O(1))
β ββ avatar β Column offset (O(1))
Total: O(4) = O(|path|), regardless of table size
3. Columnar by Defaultβ
Read only what you need - 50% I/O reduction for typical queries.
4. Embeddable & Extensibleβ
Single-file deployment (~1.5MB) with optional plugin architecture.
Executive Summaryβ
SochDB is an AI-native database designed from the ground up for LLM applications and autonomous agents. As of v2.0 it can run either embedded (in-process, SQLite-like) or as a thick gRPC server with thin language clients. Key differentiators:
| Feature | Description | Benefit |
|---|---|---|
| Trie-Columnar Hybrid (TCH) | O(|path|) lookups via radix-compressed trie | Constant-time path resolution |
| HNSW Vector Index | SIMD/flat-scan crossover + approximate HNSW graph search | Sub-millisecond k-NN at small scale, scalable at large scale |
| Hybrid Search | Vector + BM25 + grep lanes fused via RRF (k = 60.0) | One ranked result set across modalities |
| TOON Format | Typically 40-66% fewer tokens than equivalent JSON | Significant LLM cost savings |
| MCP Protocol | Native LLM tool integration over stdio | Seamless agent orchestration |
| Dual Deployment | Embedded in-process OR thick gRPC server | Same data model from laptop to cluster |
Deployment Model: Thick Server / Thin Clientβ
v2.0 introduces a "Thick Server / Thin Client" architecture (sochdb-grpc/src/main.rs). The server binary sochdb-grpc-server holds the storage engine, SQL executor, vector indexes, and security layer; language SDKs are thin clients that speak gRPC (plus optional gateways). The same engine still runs embedded in-process for SQLite-like single-file deployments.
ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β sochdb-grpc-server (thick) β
β β
β gRPC :50051 metrics :9090 WebSocket :8080 pg-wire :5433 β
β β β β β β
β βββββ΄βββββββββββββββ΄βββββββββββββββββ΄ββββββββββββββββ΄βββββββββββ β
β β Auth / Security interceptor (optional --auth) β β
β βββββββββββββββββββββββββββββββββ¬βββββββββββββββββββββββββββββββ β
β βΌ β
β ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ β
β β 12 gRPC services + SQL executor + HNSW index + storage β β
β ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ β
ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β² β² β² β²
thin Python SDK thin Node SDK thin Go SDK psql / grpcurl
The unified connect() API (documented in the SDKs) selects a transport from a URI scheme:
file://./data β embedded on-disk database (in-process)
ipc:///tmp/sochdb.sock β local IPC over a Unix domain socket
grpc://localhost:50051 β gRPC to a SochDB server (plaintext)
grpcs://prod.example.com:443 β gRPC over TLS
Default ports (main.rs; 0 disables a gateway):
| Service | Flag | Default port |
|---|---|---|
| gRPC | -p, --port | 50051 |
Prometheus metrics (GET /metrics) | --metrics-port | 9090 |
| WebSocket gateway | --ws-port | 8080 |
| PostgreSQL wire protocol | --pg-port | 5433 |
make server-run / --config path is staleThe top-level make server-run target passes --config sochdb-server-config.toml, but the server's clap Args has no --config flag (it configures via flags + env vars only). The bundled sochdb-server-config.toml (port 9600) is not consumed by the current binary. Configure with flags/env, not the TOML file.
System Architectureβ
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β SochDB Architecture β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ€
β β
β βββββββββββββββ ββββββββββββ ββββββββββββ ββββββββββββ Gateways β
β β MCP Server β β gRPC API β β WebSocketβ β pg-wire β β
β ββββββββ¬βββββββ ββββββ¬ββββββ ββββββ¬ββββββ ββββββ¬ββββββ β
β β β β β β
β ββββββββ΄ββββββββββββββ΄βββββββββββββ΄βββββββββββββ΄βββββββ β
β β Auth / Security interceptor (optional --auth) β β
β βββββββββββββββββββββββββββββ¬βββββββββββββββββββββββββββ β
β βΌ β
β βββββββββββββββββββββββββββββββββββββββββββββββββββββββ β
β β Query Processing Engine β β
β β βββββββββββ βββββββββββ βββββββββββββββββββββββ β β
β β β SochQL/ β β Context β β Volcano SQL β β β
β β β SQL β β Builder β β Executor (iterator)β β β
β β β Parser β β β β + sochdb-fusion β β β
β β ββββββ¬βββββ ββββββ¬βββββ ββββββββββββ¬βββββββββββ β β
β βββββββββΌβββββββββββββΌββββββββββββββββββΌβββββββββββββββ β
β β β β β
β βββββββββ΄βββββββββββββ΄ββββββββββββββββββ΄βββββββββββββββ β
β β Unified Storage Layer β β
β β ββββββββββββ ββββββββββββ ββββββββββββββββ β β
β β β TCH β β HNSW β β MVCC + SSI β β β
β β β Storage β β Index β β Transactions β β β
β β ββββββ¬ββββββ ββββββ¬ββββββ ββββββββ¬ββββββββ β β
β βββββββββΌββββββββββββββΌββββββββββββββββΌββββββββββββββββ β
β β β β β
β βββββββββ΄ββββββββββββββ΄ββββββββββββββββ΄ββββββββββββββββ β
β β Durability Layer (WAL + CDC) β β
β β ββββββββββββ ββββββββββββ ββββββββββββββββ β β
β β β WAL β β Group β β Crash β β β
β β β Writer β β Commit β β Recovery β β β
β β ββββββββββββ ββββββββββββ ββββββββββββββββ β β
β ββββββββββββββββββββββββββββββββββββββββββββββββββββββββ β
β β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
Design Principlesβ
- Sync-First Storage: Core storage uses synchronous I/O (like SQLite) for predictable latency and simpler embedding
- Optional Async: Tokio runtime only required for server mode (MCP, gRPC, gateways)
- Lock-Free Reads: Hazard pointer protection for true lock-free read paths
- AI-Native Formatting: TOON format as first-class output for token efficiency
- Thick Server / Thin Client: heavy compute lives in the server; SDKs stay thin
gRPC Service Setβ
The server registers 12 gRPC services (main.rs), plus a separate tonic health service for Kubernetes probes. Every service except VectorIndexService runs behind the auth interceptor when --auth is enabled.
| Service | Purpose | Notes |
|---|---|---|
VectorIndexService | Create/search HNSW indexes | No auth interceptor; 64 MB max gRPC message (encode + decode) |
GraphService | Graph / relationship operations | auth |
PolicyService | Policy management | auth |
ContextService | Token-budgeted context queries | auth |
CollectionService | Collection CRUD | auth |
NamespaceService | Namespace management | auth |
SemanticCacheService | Semantic cache | auth |
TraceService | Tracing | auth |
CheckpointService | Checkpoint operations | auth |
McpService | MCP surface inside the server | auth |
KvService | Key/value access | auth |
SubscriptionService | CDC streaming subscriptions | auth |
Plus a tonic_health service (empty service name set to Serving) mounted at the gRPC port, not behind auth, so probes can reach it.
Only VectorIndexService raises the gRPC message limit to 64 MB (encode and decode). All other services use the tonic default (4 MB decode).
CDC & Subscriptionsβ
SubscriptionService streams change events derived from the WAL via a log-structured ring buffer (sochdb-storage/src/cdc.rs, default capacity 65,536, after-image only). A Subscribe request can filter by tables and by operations, and resume from a start_sequence.
where_predicate is accepted but not yet enforcedSubscribeRequest includes a where_predicate (SQL WHERE) field, but the streaming handler does not read or apply it. Table filtering and operation-type filtering ARE enforced; SQL predicate filtering is not yet implemented.
The WebSocket gateway also advertises a subscribe message type, but in the default binary its CDC log is wired as None, so WS subscriptions are not connected to CDC.
Gatewaysβ
- Prometheus metrics (
--metrics-port, default9090):GET /metrics(Prometheus text format) andGET /health, served on a dedicated OS thread. Metric families includesochdb_grpc_requests_total,sochdb_grpc_request_duration_seconds,sochdb_sql_queries_total,sochdb_transactions_total,sochdb_wal_writes_total, andsochdb_build_info. - WebSocket gateway (
--ws-port, default8080): JSON message protocol at path/with message typessql,kv_get,kv_put,kv_delete,subscribe,ping. - PostgreSQL wire protocol (
--pg-port, default5433): connect withpsql -h 127.0.0.1 -p 5433 -d sochdb.
The PostgreSQL wire endpoint implements only the Simple Query Protocol (no extended/prepared statements), has no SSL/TLS (cleartext) and no password (trust auth). It is intended for loopback use only. Without --pg-data-dir it uses a placeholder echo executor; pass --pg-data-dir to run real SQL (SELECT/INSERT/UPDATE/DELETE/DDL including JOINs) against an on-disk database. The server logs a loud warning if --host is non-loopback, because the writable SQL endpoint would then be exposed unauthenticated.
Module Structureβ
Crate Dependency Graphβ
sochdb-studio (GUI, Tauri desktop)
β
βΌ
ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β Application Layer β
β ββββββββββββββ ββββββββββββββ ββββββββββββββ β
β β sochdb-mcp β βsochdb-grpc β βsochdb-wasm β β
β βββββββ¬βββββββ βββββββ¬βββββββ βββββββ¬βββββββ β
ββββββββββΌββββββββββββββββΌββββββββββββββββΌββββββββββββββββββββββ
βΌ βΌ βΌ
ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β Client Layer β
β βββββββββββββββββββ βββββββββββββββββββ β
β β sochdb (client) β β sochdb-python β β
β ββββββββββ¬βββββββββ ββββββββββ¬βββββββββ β
βββββββββββββΌβββββββββββββββββββββΌββββββββββββββββββββββββββββββ
βΌ βΌ
ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β Query Layer β
β ββββββββββββββ ββββββββββββββββ ββββββββββββββ β
β βsochdb-queryβ βsochdb-fusion β βsochdb-memoryβ β
β βββββββ¬βββββββ ββββββββ¬ββββββββ βββββββ¬βββββββ β
β ββββββββββββββ ββββββββββββββββββββ β
β βsochdb-toolsβ β sochdb-simulationβ β
β βββββββ¬βββββββ ββββββββββββββββββββ β
ββββββββββΌββββββββββββββββΌββββββββββββββββΌββββββββββββββββββββββ
βΌ βΌ βΌ
ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β Execution Layer β
β ββββββββββββββ ββββββββββββββ ββββββββββββββ β
β βsochdb-indexβ βsochdb-vectorβ βsochdb-kernelβ β
β βββββββ¬βββββββ βββββββ¬βββββββ βββββββ¬βββββββ β
ββββββββββΌββββββββββββββββΌββββββββββββββββΌββββββββββββββββββββββ
βΌ βΌ βΌ
ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β Storage Layer β
β βββββββββββββββββββββββββββββββ β
β β sochdb-storage β β
β β (WAL + LSCS + MVCC + CDC + β β
β β compression + encryption) β β
β ββββββββββββ¬βββββββββββββββββββ β
ββββββββββββββββββββββββββββΌββββββββββββββββββββββββββββββββββββ
βΌ
ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β Core Layer β
β βββββββββββββββββββββββ β
β β sochdb-core β β
β β (Types, Codec, Trie)β β
β βββββββββββββββββββββββ β
ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
Crate Responsibilitiesβ
| Crate | Purpose | Key Types |
|---|---|---|
sochdb-core | Foundational types, codecs, trie, TOON | SochValue, SochSchema, SochTable |
sochdb-storage | WAL, LSCS, MVCC, GC, CDC, compression, encryption | Database, WalManager, CdcLog |
sochdb-kernel | Query execution, table operations | Kernel, TableHandle, ScanIterator |
sochdb-index | HNSW vector index and index utilities | HnswIndex, HnswConfig |
sochdb-vector | CPU-first ANN vector search engine, BM25, RRF | BM25Config, RRFConfig |
sochdb-query | SQL/SochQL parser, volcano executor, fusion | SqlBridge, PlanNode, FusionConfig |
sochdb-fusion | Fused compositional query execution (ART + HNSW + CSR in one pipeline) | fused query pipeline |
sochdb-memory | Bi-temporal agent memory with write-time lexical recall | memory layer |
sochdb-simulation | Standalone & distributed topology modeling with score validation | simulation harness |
sochdb (client crate) | High-level SDK, context queries | DurableConnection, ContextQueryBuilder |
sochdb-mcp | MCP protocol server (stdio) | McpServer, ToolExecutor |
sochdb-grpc | Thick gRPC server + gateways | sochdb-grpc-server binary |
sochdb-python | Python native bindings (PyO3) | HnswIndex, BM25Index, TableDatabase |
sochdb-wasm | Browser WASM build | WasmVectorIndex |
sochdbThe high-level Rust SDK crate lives in sochdb-client/ but is published under the name sochdb (cargo add sochdb, currently 2.0.3). This name collides with the PyPI distribution sochdb.
SQL Execution Engineβ
SochDB executes SQL through a Volcano (iterator) executor (sochdb-query/src/executor/). Each operator implements a PlanNode trait with schema(), next() -> Result<Option<Row>>, and reset(), pulling rows one at a time from its children.
Volcano operatorsβ
SeqScan / IndexSeek β Filter (WHERE) β HashAggregate (GROUP BY)
β Filter (HAVING) β Project (SELECT) β Sort (ORDER BY) β Limit (LIMIT/OFFSET)
Available nodes: SeqScan, IndexSeek, Filter, Project, Sort, Limit, HashJoin, NestedLoopJoin, MergeJoin, HashAggregate, Explain, Values, Empty.
- Joins: the executor implements
HashJoin(INNER/LEFT/RIGHT/FULL equi-joins),NestedLoopJoin(theta + CROSS), andMergeJoin(sorted inputs).ON a=bplans a hash join; non-equiONplans a nested-loop join; multipleFROMtables plan an implicit CROSS join. - Aggregates: the volcano path supports
COUNT,COUNT DISTINCT,SUM,AVG,MIN,MAX. - EXPLAIN is served by the volcano path (the
ExplainNodeemits a textualQUERY PLAN).
For the full statement coverage path, the SqlBridge (sochdb-query/src/sql/bridge.rs) dispatches CREATE/DROP TABLE, CREATE/DROP INDEX, ALTER TABLE, INSERT/UPDATE/DELETE, transactions, and scope/permission DDL against storage.
- There are three SQL code paths (the production
SqlBridge, the volcano executor, and an in-memory referenceSqlExecutorthat rejects multi-tableFROM). The bridge is the full path. MEDIANandSTDDEVexist only in the separatesql/aggregate.rsengine (sample stddev via Welford), not in the volcano aggregate operator.DISTINCT(as a SELECT modifier), window functions, CTEs, and subqueries inWHERE/SELECTare not yet implemented in the planner.CASTcurrently passes the inner value through without real type coercion.
sochdb-fusion provides fused compositional query execution (ART + HNSW + CSR in a single pipeline) for combined relational / vector / graph workloads.
Storage Engineβ
Log-Structured Column Store (LSCS)β
ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β Storage Engine β
ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ€
β β
β Write-Ahead Log (WAL) β
β ββββββββββββ ββββββββββββ ββββββββββββ ββββββββββββ β
β β Active β β Sealed β β Sealed β β Archived β β
β β Segment β β Segment β β Segment β β Segments β β
β β (writes) β β (full) β β (full) β β (backup) β β
β ββββββ¬ββββββ ββββββ¬ββββββ ββββββ¬ββββββ ββββββ¬ββββββ β
β β β β β β
β βΌ βΌ βΌ βΌ β
β ββββββββββββββββββββββββββββββββββββββββββββββββββββββ β
β β Group Commit Buffer β β
β β Batches transactions for efficient fsync β β
β ββββββββββββββββββββββββββββββββββββββββββββββββββββββ β
β β
β Log-Structured Column Store (LSCS) β
β βββββββββββββββ βββββββββββββββ βββββββββββββββ β
β β MemTable β β MemTable β β MemTable β β
β β (Active) β β (Immutable) β β (Flushing) β β
β ββββββββ¬βββββββ ββββββββ¬βββββββ ββββββββ¬βββββββ β
β βΌ βΌ βΌ β
β βββββββββββββββββββββββββββββββββββββββββββββββββββββ β
β β Sorted Runs β β
β β L0: ββββ ββββ ββββ (recently flushed) β β
β β L1: ββββββββββββββββ (merged) β β
β β L2: ββββββββββββββββββββββββββββ (compacted) β β
β βββββββββββββββββββββββββββββββββββββββββββββββββββββ β
β β
β Background Workers β
β ββββββββββββ ββββββββββββ ββββββββββββ β
β β Compactorβ β GC β βCheckpointβ β
β β Thread β β Thread β β Thread β β
β ββββββββββββ ββββββββββββ ββββββββββββ β
β β
ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
Lock-Free MemTableβ
The MemTable uses hazard pointers for true lock-free reads:
pub struct LockFreeMemTable {
data: DashMap<Vec<u8>, LockFreeVersionChain>,
hazard_domain: HazardDomain,
size_bytes: AtomicUsize,
}
impl LockFreeMemTable {
/// Read with zero-copy callback (optimal path)
pub fn read_with<F, R>(
&self,
key: &[u8],
snapshot_ts: u64,
txn_id: Option<u64>,
f: F,
) -> Option<R>
where
F: FnOnce(&[u8]) -> R;
/// Write a value (creates uncommitted version)
pub fn write(&self, key: Vec<u8>, value: Option<Vec<u8>>, txn_id: u64) -> Result<()>;
/// Commit a transaction's writes
pub fn commit(&self, txn_id: u64, commit_ts: u64, keys: &[Vec<u8>]);
}
Scalability: Lock-free design achieves 23% better scaling vs RwLock at 8 threads.
SST File Formatβ
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β SST File Structure β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ€
β Data Blocks β
β βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ β
β β Block 0: [key1:val1][key2:val2]...[keyN:valN][trailer] β β
β β Block 1: [key1:val1][key2:val2]...[keyN:valN][trailer] β β
β βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ€
β Meta Blocks β
β βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ β
β β Bloom Filter Block β β
β β Column Stats Block β β
β β Compression Dict Block (optional) β β
β βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ€
β Index Block β
β βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ β
β β [first_key_0, offset_0, size_0] β β
β β [first_key_1, offset_1, size_1] β β
β βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ€
β Footer (48 bytes) β
β ββββββββββββββββββ¬βββββββββββββββββ¬ββββββββββββββββββββββββββ
β β Meta Index β Index Handle β Magic + Version ββ
β β BlockHandle β BlockHandle β ββ
β ββββββββββββββββββ΄βββββββββββββββββ΄ββββββββββββββββββββββββββ
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
Compaction Strategyβ
Level 0 (L0): Recent flushes, may overlap
ββββββ ββββββ ββββββ ββββββ
βSST1β βSST2β βSST3β βSST4β β 4 files, overlapping key ranges
ββββββ ββββββ ββββββ ββββββ
β
βΌ Compaction (merge sort)
Level 1 (L1): Non-overlapping, sorted
ββββββββββββββββββββββββββββββββββββββββ
β SST (merged) β
ββββββββββββββββββββββββββββββββββββββββ
β
βΌ Size-triggered compaction
Level 2 (L2): 10x larger budget
ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β SST files β
ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
WAL Record Formatβ
ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β WAL Record Format β
ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ€
β ββββββββββββ¬βββββββββββ¬βββββββββββ¬βββββββββββ¬βββββββββββββββ β
β β CRC32 β Length β Type β TxnID β Data β β
β β (4 bytes)β (4 bytes)β (1 byte) β (8 bytes)β (variable) β β
β ββββββββββββ΄βββββββββββ΄βββββββββββ΄βββββββββββ΄βββββββββββββββ β
β β
β Record Types: β
β β’ 0x01: PUT (key, value) β
β β’ 0x02: DELETE (key) β
β β’ 0x03: BEGIN_TXN (txn_id) β
β β’ 0x04: COMMIT_TXN (txn_id, commit_ts) β
β β’ 0x05: ABORT_TXN (txn_id) β
β β’ 0x06: CHECKPOINT (LSN, active_txns) β
ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
Recovery Processβ
fn recover(&self) -> Result<RecoveryStats> {
// 1. Find latest checkpoint
let checkpoint = self.find_latest_checkpoint()?;
// 2. Replay WAL from checkpoint
let mut wal_reader = WalReader::open_from(checkpoint.lsn)?;
let mut active_txns: HashSet<u64> = checkpoint.active_txns;
while let Some(record) = wal_reader.next()? {
match record.record_type {
RecordType::BeginTxn => {
active_txns.insert(record.txn_id);
}
RecordType::Put => {
if active_txns.contains(&record.txn_id) {
self.replay_put(&record)?;
}
}
RecordType::CommitTxn => {
active_txns.remove(&record.txn_id);
}
RecordType::AbortTxn => {
self.rollback_txn(record.txn_id)?;
active_txns.remove(&record.txn_id);
}
_ => {}
}
}
// 3. Abort incomplete transactions
for txn_id in active_txns {
self.rollback_txn(txn_id)?;
}
Ok(stats)
}
Storage feature summary (v2.0)β
| Feature | State | Notes |
|---|---|---|
| MVCC | Shipped | Lock-free version chains; snapshot isolation |
| WAL + group commit | Shipped | CRC-checked records, crash recovery via checkpoint replay |
| SSI (serializable isolation) | Shipped | rw-antidependency cycle detection |
| Compression | Shipped | LZ4 / Zstd (workspace deps); columnar blocks |
| CDC (change data capture) | Shipped | WAL-derived ring buffer (CdcLog), after-image only |
| At-rest encryption | Library API only | See caution below |
EncryptionEngine (sochdb-storage/src/encryption.rs) implements AES-256-GCM-SIV (nonce-misuse-resistant AEAD) over data blocks, WAL entries, and checkpoint files, with a 32-byte key. It is unit-tested and reachable as an API, but there is no CLI flag on sochdb-grpc-server to enable it and main.rs does not construct an EncryptionEngine. Treat it as an available API with planned server wiring, not a runtime toggle.
Auth & Security Layerβ
When --auth is passed, the server installs an auth interceptor on every service except VectorIndexService (sochdb-grpc/src/auth_interceptor.rs, security.rs).
Credentials over gRPCβ
authorization: Bearer <token>β preferred. Interpreted as a JWT when JWT is enabled, otherwise as an API key.x-api-key: <key>β fallback; rewritten internally toBearer <key>.
The interceptor pipeline is: authenticate β rate-limit β inject a Principal into request extensions. When --auth is not passed, requests resolve to an anonymous principal with Read + Write + ManageCollections capabilities. Default rate limit is 1000 req/s with a burst of 100 per tenant; audit logging is on by default.
RBAC rolesβ
The actual roles are Owner, Editor, Viewer (plus a Custom { name, capabilities } variant):
| Role | Capabilities |
|---|---|
Owner | Admin, Read, Write, ManageCollections, ManageIndexes, ViewMetrics, ManageBackups, ManageUsers (all) |
Editor | Read, Write, ManageCollections, ManageIndexes |
Viewer | Read, ViewMetrics |
Roles can be scoped Global, Namespace(name), or Collection { namespace, collection } via bind_role; effective_capabilities(principal, namespace) unions all applicable bindings.
JWT (validation only)β
The server validates HS256 JWTs (jsonwebtoken::decode), checking exp and optionally iss/aud, and derives a Principal from the role/capabilities claims. There is no token-minting API in the server β tokens must be issued by an external IdP or the calling application. The verification secret comes from the jwt-secret Kubernetes secret or the SOCHDB_JWT_SECRET env var.
API keys and passwordsβ
API keys are stored as SHA-256(key) by default, or HMAC-SHA256(pepper, key) when SOCHDB_API_KEY_PEPPER is set β never plaintext, and not argon2. argon2 is used only for user passwords (register_user / verify_password).
TLS / mTLS and secretsβ
- TLS is enabled when both
--tls-certand--tls-keyare set; adding--tls-caenables mTLS client-certificate verification. Certs support mtime-based hot reload. - A
SecretsProviderreads Kubernetes secret mounts (--secrets-path) or env vars:jwt-secret,api-keys,encryption-key(base64, 32 bytes),tls-cert,tls-key,tls-ca.
Client SDK APIβ
SochClient (In-Memory)β
For testing and development without durability requirements:
use sochdb::prelude::*;
// Open database
let client = SochClient::open("./mydb")?;
// Configure token budget for LLM responses
let client = client.with_token_budget(4096);
// Path-based queries (O(|path|) resolution)
let result = client.query("/users/123").execute()?;
// Execute SochQL
let rows = client.execute("SELECT * FROM users WHERE active = true")?;
// Begin transaction
let txn = client.begin()?;
// Vector operations
let vectors = client.vectors("embeddings")?;
vectors.add(&["doc1", "doc2"], &[vec1, vec2])?;
let results = vectors.search(&query_embedding, 10)?;
DurableSochClient (Production)β
Full WAL/MVCC support for production workloads:
use sochdb::prelude::*;
// Open with durability
let client = DurableSochClient::open("./mydb")?;
// Path-based CRUD
client.put("/users/123", b"{\"name\": \"Alice\"}")?;
let data = client.get("/users/123")?;
client.delete("/users/123")?;
// Scan with prefix
let results = client.scan("/users/")?;
// Transaction support
client.begin()?;
client.put("/users/1", value1)?;
client.put("/users/2", value2)?;
let commit_ts = client.commit()?;
// Force durability
client.fsync()?;
PathQuery Builderβ
Leverages TCH's O(|path|) resolution:
use sochdb::path_query::{PathQuery, CompareOp};
// Fluent query builder
let results = client.query("/users")
.filter("score", CompareOp::Gt, SochValue::Int(80))
.filter("active", CompareOp::Eq, SochValue::Bool(true))
.select(&["name", "email", "score"])
.order_by("score", SortDirection::Desc)
.limit(10)
.execute()?;
Comparison Operatorsβ
pub enum CompareOp {
Eq, // =
Ne, // !=
Lt, // <
Le, // <=
Gt, // >
Ge, // >=
Like, // LIKE pattern matching
In, // IN (array)
IsNull, // IS NULL
IsNotNull, // IS NOT NULL
}
Output Formatsβ
pub enum OutputFormat {
Soch, // Default: typically 40-66% fewer tokens than JSON
Json, // Standard JSON for compatibility
Columnar, // Raw columnar for analytics
}
TOON Format Internalsβ
Text Format Grammarβ
document ::= table_header newline row*
table_header ::= name "[" count "]" "{" fields "}" ":"
row ::= value ("," value)* newline
value ::= null | bool | number | string | array | ref
null ::= "β
"
bool ::= "T" | "F"
number ::= integer | float
string ::= raw_string | quoted_string
array ::= "[" value ("," value)* "]"
ref ::= "ref(" identifier "," integer ")"
Binary Format Structureβ
ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β TOON Binary Format β
ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ€
β Header (16 bytes) β
β ββββββββββββ¬βββββββββββ¬βββββββββββ¬βββββββββββ β
β β Magic β Version β Flags β Row Countβ β
β β (4 bytes)β (2 bytes)β (2 bytes)β (8 bytes)β β
β ββββββββββββ΄βββββββββββ΄βββββββββββ΄βββββββββββ β
ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ€
β Schema Section β
β ββββββββββββ¬βββββββββββββββββββββββββββββββββββ β
β β Name Len β Table Name (UTF-8) β β
β ββββββββββββΌβββββββββββββββββββββββββββββββββββ€ β
β β Col Countβ [Column Definitions...] β β
β ββββββββββββ΄βββββββββββββββββββββββββββββββββββ β
ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ€
β Data Section (columnar) β
β ββββββββββββββββββββββββββββββββββββββββββββββββ β
β β Column 0: [type_tag][values...] β β
β β Column 1: [type_tag][values...] β β
β ββββββββββββββββββββββββββββββββββββββββββββββββ β
ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
Type Tagsβ
#[repr(u8)]
pub enum SochTypeTag {
Null = 0x00,
False = 0x01,
True = 0x02,
PosFixint = 0x10, // 0-15 in lower nibble
NegFixint = 0x20, // -16 to -1 in lower nibble
Int8 = 0x30,
Int16 = 0x31,
Int32 = 0x32,
Int64 = 0x33,
Float32 = 0x40,
Float64 = 0x41,
FixStr = 0x50, // 0-15 char length in lower nibble
Str8 = 0x60,
Str16 = 0x61,
Str32 = 0x62,
Array = 0x70,
Ref = 0x80,
}
Varint Encodingβ
fn encode_varint(mut value: u64, buf: &mut Vec<u8>) {
while value >= 0x80 {
buf.push((value as u8) | 0x80);
value >>= 7;
}
buf.push(value as u8);
}
fn decode_varint(buf: &[u8]) -> (u64, usize) {
let mut result = 0u64;
let mut shift = 0;
for (i, &byte) in buf.iter().enumerate() {
result |= ((byte & 0x7F) as u64) << shift;
if byte & 0x80 == 0 {
return (result, i + 1);
}
shift += 7;
}
(result, buf.len())
}
MCP Protocol & Tools APIβ
Server Lifecycleβ
Client Server
β β
βββββ initialize βββββββββββββββΊβ
β β Create SochDB connection
βββββ capabilities ββββββββββββββ
β β
βββββ initialized ββββββββββββββΊβ
β β
βββββ tools/list βββββββββββββββΊβ
β β Return built-in tools
βββββ tool definitions ββββββββββ
β β
βββββ tools/call βββββββββββββββΊβ
β { "name": "sochdb_query", β
β "arguments": {...} } β
β β Execute query
βββββ result (TOON format) ββββββ
MCP Server Implementationβ
impl McpServer {
pub fn new(conn: Arc<EmbeddedConnection>) -> Self;
/// Dispatch JSON-RPC request
pub fn dispatch(&self, req: &RpcRequest) -> RpcResponse;
/// Get database statistics
pub fn db_stats(&self) -> DatabaseStats;
}
Built-in Tools Referenceβ
Core Database Toolsβ
| Tool | Description | Required Args |
|---|---|---|
sochdb_context_query | AI-optimized context with token budgeting | sections |
sochdb_query | Execute SochQL query | query |
sochdb_get | Get value at path | path |
sochdb_put | Set value at path | path, value |
sochdb_delete | Delete at path | path |
sochdb_list_tables | List tables with metadata | - |
sochdb_describe | Get table schema | table |
Memory Tools (Episode/Entity Schema)β
| Tool | Description | Required Args |
|---|---|---|
memory_search_episodes | Semantic episode search | query |
memory_get_episode_timeline | Event timeline for episode | episode_id |
memory_search_entities | Entity search | query |
memory_get_entity_facts | Entity details | entity_id |
memory_build_context | One-shot context packing | goal, token_budget |
Log Toolsβ
| Tool | Description | Required Args |
|---|---|---|
logs_tail | Get last N rows | table |
logs_timeline | Events in a time range for an entity | entity_id |
Agentic Search Toolsβ
| Tool | Description | Required Args |
|---|---|---|
sochdb_grep | Indexed grep over an in-memory corpus (trigram-index lane) | pattern |
sochdb_peek | Read a line range from a document | doc_id, start_line, end_line |
sochdb_expand | Expand Β±N lines around a hit | doc_id, line |
The authoritative tool list is the Rust source (tools.rs::get_built_in_tools()), which uses underscore names (sochdb_query, sochdb_get, memory_search_episodes, sochdb_grep, ...). The dot-named catalog in mcp.json and the MCP README (sochdb.query, memory.search_episodes) is stale/illustrative β use the underscore names.
Tool Schemasβ
sochdb_context_queryβ
{
"name": "sochdb_context_query",
"description": "Fetch AI-optimized context from SochDB with token budgeting",
"inputSchema": {
"type": "object",
"properties": {
"sections": {
"type": "array",
"items": {
"type": "object",
"properties": {
"name": { "type": "string" },
"kind": { "type": "string", "enum": ["literal", "get", "last", "search"] },
"text": { "type": "string", "description": "For kind=literal" },
"path": { "type": "string", "description": "For kind=get" },
"table": { "type": "string", "description": "For kind=last/search" },
"query": { "type": "string", "description": "For kind=search" },
"top_k": { "type": "integer", "default": 10 }
},
"required": ["name", "kind"]
}
},
"token_budget": { "type": "integer", "default": 4096 },
"format": { "type": "string", "enum": ["toon", "json", "markdown"], "default": "toon" },
"truncation": { "type": "string", "enum": ["tail_drop", "head_drop", "proportional"], "default": "tail_drop" }
},
"required": ["sections"]
}
}
sochdb_queryβ
{
"name": "sochdb_query",
"description": "Execute a SochQL query. Returns results in TOON format.",
"inputSchema": {
"type": "object",
"properties": {
"query": { "type": "string", "description": "SochQL query" },
"format": { "type": "string", "enum": ["toon", "json"], "default": "toon" },
"limit": { "type": "integer", "default": 100 }
},
"required": ["query"]
}
}
memory_search_episodesβ
{
"name": "memory_search_episodes",
"description": "Search for similar past episodes by semantic similarity",
"inputSchema": {
"type": "object",
"properties": {
"query": { "type": "string", "description": "Natural language query" },
"k": { "type": "integer", "default": 5 },
"episode_type": {
"type": "string",
"enum": ["conversation", "task", "workflow", "debug", "agent_interaction"]
},
"entity_id": { "type": "string", "description": "Filter by entity" }
},
"required": ["query"]
}
}
memory_build_contextβ
{
"name": "memory_build_context",
"description": "Build optimized LLM context from memory automatically",
"inputSchema": {
"type": "object",
"properties": {
"goal": { "type": "string", "description": "What the context will be used for" },
"token_budget": { "type": "integer", "default": 4096 },
"session_id": { "type": "string" },
"episode_id": { "type": "string" },
"entity_ids": { "type": "array", "items": { "type": "string" } },
"include_schema": { "type": "boolean", "default": false }
},
"required": ["goal", "token_budget"]
}
}
Vector Search & HNSW Subsystemβ
The vector subsystem lives in sochdb-index (HnswIndex, HnswConfig) and sochdb-vector (CPU-first ANN, BM25, RRF fusion). Search picks between an exact parallel SIMD flat scan for small datasets and the approximate HNSW graph for larger ones.
Dimension-aware flat-scan crossoverβ
The dimension-aware logic in the core is the flat-scan (brute-force) threshold β when the node count is at or below the threshold, search uses an exact parallel SIMD scan (faster than HNSW for small N):
flat_scan_threshold = if dimension <= 128 { 10_000 }
else if dimension <= 384 { 4_000 }
else { 1_000 } // 768D+
search_smart(query, k, exact_threshold) routes to search_exact when the dataset is at or below the threshold (default 1000), otherwise to adaptive HNSW search.
ef_search splitThere is no dimension-aware 500/1500 ef_search split in the core. The HNSW ef_search default is a single fixed 500. The dimension-aware behavior is the flat-scan crossover above (10000 / 4000 / 1000), not ef_search.
VectorCollection APIβ
pub struct VectorCollection {
dimension: usize,
backend: VectorBackend,
pq: Option<ProductQuantizer>,
id_map: RwLock<HashMap<String, usize>>,
reverse_map: RwLock<HashMap<usize, String>>,
}
impl VectorCollection {
/// Open or create a vector collection
pub fn open(conn: &SochConnection, name: &str) -> Result<Self>;
/// Add vectors in batch
pub fn add(&mut self, ids: &[&str], vectors: &[Vec<f32>]) -> Result<()>;
/// Add a single vector
pub fn add_one(&mut self, id: &str, vector: Vec<f32>) -> Result<()>;
/// Search for nearest neighbors
pub fn search(&self, query: &[f32], k: usize) -> Result<Vec<SearchResult>>;
/// Get vector by ID
pub fn get(&self, id: &str) -> Option<Vec<f32>>;
/// Delete vector by ID
pub fn delete(&mut self, id: &str) -> Result<bool>;
/// Get collection statistics
pub fn stats(&self) -> VectorStats;
/// Get compression ratio (if PQ trained)
pub fn compression_ratio(&self) -> Option<f32>;
/// Migrate batch during idle time (for hybrid mode)
pub fn migrate_batch(&mut self) -> Result<usize>;
}
pub struct SearchResult {
pub id: String,
pub distance: f32,
pub metadata: Option<Value>,
}
pub struct VectorStats {
pub count: usize,
pub dimension: usize,
pub backend: String,
pub memory_bytes: usize,
pub pq_enabled: bool,
pub migration_progress: Option<f32>,
}
HNSW Configuration & Defaultsβ
HnswConfig::default() (sochdb-index/src/hnsw.rs):
| Parameter | Field | Default | Description |
|---|---|---|---|
M | max_connections | 32 | Max connections per node (raised from 16; m=32 clears 95% recall out of the box) |
M0 | max_connections_layer0 | 64 | Max connections at layer 0 (2Β·M) |
mL | level_multiplier | 1/ln(32) | Level-generation multiplier |
ef_construction | ef_construction | 256 | Build-time search width (raised from 200 for hard high-dim embeddings) |
ef_search | ef_search | 500 | Query-time search width (single fixed value) |
| Metric | metric | Cosine | Also Euclidean, DotProduct |
| Precision | quantization_precision | Some(F32) | Also F16, BF16 (half the bytes) |
Reported recall (from in-source benchmarks): deep-1M recall@10 β 0.967 at m=16/m0=32 β 0.988 at m=32; the header claims roughly 250x speedup over brute force.
AdaptiveSearchConfig can binary-search the minimum ef that meets a target recall (default target_recall = 0.95, min_ef = 10, max_ef = 500).
MultiShardHnswIndex in the core engineThere is no MultiShardHnswIndex (or HNSW-level scatter-gather shard router) in the Rust core. A MultiShardHnswIndex exists only in the Python native package as a threaded scatter-gather wrapper β it is not a server/core feature.
Quantizationβ
Precision is F32 / F16 / BF16 (4 / 2 / 2 bytes per element, via the half crate). Product Quantization is also available (HnswConfig.enable_product_quantization, default false, intended for very large / high-dimensional collections).
HNSW Graph Structureβ
ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β HNSW Graph Structure β
ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ€
β β
β Layer 2: βββββββββββββββββββββββββ β
β β β β
β Layer 1: βββββββββββββββββββββββββββββββββ β
β β β β β β β
β Layer 0: βββββββββββββββββββββββββββββββββββββββββ β
β v0 v1 v2 v3 v4 v5 v6 v7 v8 v9 v10 β
β β
β Search: Start at top layer, greedily descend β
β Insert: Random level, connect at each layer β
ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
Level Generation Algorithmβ
/// Per HNSW paper: level = floor(-ln(uniform(0,1)) * mL)
fn random_level(&self) -> usize {
let uniform: f32 = rand::random();
let level = (-uniform.ln() * self.level_multiplier).floor() as usize;
level.min(16) // Cap at 16 layers
}
// Distribution for M=32, mL = 1/ln(32) β 0.288:
// most nodes live at level 0; higher levels are exponentially rarer
Search Algorithmβ
fn search_internal(&self, query: &[f32], k: usize) -> Vec<(u64, f32)> {
let entry_point = self.entry_point?;
// Navigate from top layer to layer 1
let mut current = entry_point;
for layer in (1..=self.max_layer).rev() {
current = self.search_layer_single(query, current, layer);
}
// Search layer 0 with ef_search candidates
let candidates = self.search_layer(query, current, self.ef_search.max(k), 0);
// Return top-k results
candidates.into_iter()
.take(k)
.map(|(dist, idx)| (self.nodes[idx].id, dist))
.collect()
}
WASM Vector Index (Browser)β
import init, { WasmVectorIndex } from 'sochdb-wasm';
async function main() {
await init();
// Create index: dimension=768, M=16, ef_construction=100
const index = new WasmVectorIndex(768, 16, 100);
// Insert vectors
const ids = BigUint64Array.from([1n, 2n, 3n]);
const vectors = new Float32Array(768 * 3);
const inserted = index.insertBatch(ids, vectors);
// Search
const query = new Float32Array(768);
const results = index.search(query, 10);
}
Hybrid Search (Vector + BM25 + Grep via RRF)β
sochdb-vector and sochdb-query provide hybrid retrieval that fuses up to three ranked lanes β vector, BM25 lexical, and grep (trigram-indexed regex) β using Reciprocal Rank Fusion:
RRF_score(d) = Ξ£ weightα΅’ / (k + rankα΅’(d)) // rank is 1-indexed
Defaults: BM25 k1 = 1.2, b = 0.75; RRF k = 60.0 with vector and lexical weights of 1.0. Presets include semantic_focused (0.7 / 0.3) and keyword_focused (0.3 / 0.7). The fusion config defaults to 100 candidates per modality and a final top-k of 10.
The RRF k is a fixed 60.0 in the Rust core (both fusion implementations). An adaptive_rrf_k=True option exists only in the Python HybridSearchIndex β adaptivity is a Python-SDK concept, not a core feature.
Transaction & MVCC APIβ
Transaction Lifecycleβ
// Begin transaction
client.begin()?;
// Operations within transaction
client.put("/users/1", value1)?;
client.put("/users/2", value2)?;
let data = client.get("/users/1")?;
// Commit or abort
let commit_ts = client.commit()?; // Returns commit timestamp
// OR
client.abort()?; // Discard all changes
Isolation Levelsβ
pub enum IsolationLevel {
ReadCommitted, // See committed data at statement start
RepeatableRead, // See committed data at transaction start
Serializable, // SSI - Full serializability
}
// Begin with specific isolation
let txn = client.begin_with_isolation(IsolationLevel::Serializable)?;
MVCC Internalsβ
Version chain structure:
pub struct LockFreeVersion {
storage: VersionStorage, // Inline (<48B) or heap
txn_id: AtomicU64, // Writing transaction
commit_ts: AtomicU64, // Commit timestamp (0 if uncommitted)
next: AtomicPtr<LockFreeVersion>, // Next older version
}
Visibility rules:
- Own writes: Visible if
txn_idmatches current transaction - Committed writes: Visible if
commit_ts <= snapshot_ts - Conflict detection: SSI validation checks for rw-antidependency cycles
MVCC Version Chainβ
struct VersionedValue {
value: Option<Vec<u8>>, // None = tombstone
txn_id: u64, // Transaction that wrote this
timestamp: u64, // Commit timestamp
next: Option<Box<VersionedValue>>, // Older versions
}
impl MVCCStore {
fn get(&self, key: &Key, snapshot_ts: u64) -> Option<&[u8]> {
let mut version = self.current.get(key)?;
// Find visible version
while version.timestamp > snapshot_ts {
version = version.next.as_ref()?;
}
version.value.as_deref()
}
}
Group Commit Optimizationβ
/// Optimal batch size: N* = β(2 Γ L_fsync Γ Ξ» / C_wait)
struct GroupCommitBuffer {
pending: VecDeque<PendingCommit>,
config: GroupCommitConfig,
}
impl GroupCommitBuffer {
fn optimal_batch_size(&self, arrival_rate: f64, wait_cost: f64) -> usize {
let l_fsync = self.config.fsync_latency_us as f64 / 1_000_000.0;
let n_star = (2.0 * l_fsync * arrival_rate / wait_cost).sqrt();
(n_star as usize).clamp(1, self.config.max_batch_size)
}
}
Context Query APIβ
ContextQueryBuilderβ
Build AI-optimized context with automatic token budgeting:
pub struct ContextQueryBuilder {
sections: Vec<ContextSection>,
token_budget: usize,
format: ContextFormat,
truncation: TruncationStrategy,
}
pub enum SectionKind {
Literal { text: String },
Get { path: String },
Last { table: String, top_k: usize, filter: Option<Filter> },
Search { query: String, collection: String, top_k: usize },
ToolRegistry { include_schema: bool },
}
pub enum TruncationStrategy {
TailDrop, // Drop lowest priority sections first
HeadDrop, // Drop highest priority sections first
Proportional, // Reduce all sections proportionally
}
Usage Exampleβ
let context = ContextQueryBuilder::new()
.section("system", SectionKind::Literal {
text: "You are a helpful assistant.".to_string()
})
.section("history", SectionKind::Last {
table: "messages".to_string(),
top_k: 10,
filter: None,
})
.section("knowledge", SectionKind::Search {
query: user_message.clone(),
collection: "docs".to_string(),
top_k: 5,
})
.with_budget(4096)
.with_format(ContextFormat::Soch)
.with_truncation(TruncationStrategy::TailDrop)
.execute()?;
TOON Format Token Savingsβ
JSON (52 chars β 13 tokens):
{"user":{"name":"Alice","age":30,"active":true}}
TOON (47 chars β 12 tokens, 8% savings):
user.name="Alice" user.age=30 user.active=true
TOON Table Format (40%+ savings for arrays):
[users]
name | age | active
"Alice" | 30 | true
"Bob" | 25 | false
Performance Characteristicsβ
Complexity Analysisβ
| Operation | SochDB | B-Tree (SQLite) | Notes |
|---|---|---|---|
| Point Read | O(|path|) | O(log N) | TCH path-based |
| Point Write | O(|path|) | O(log N) | + WAL |
| Range Scan | O(|path| + K) | O(log N + K) | K = result count |
| Vector Search (HNSW) | O(log N) | N/A | ef-dependent (approximate) |
| Vector Search (flat scan) | O(N) | N/A | exact; used at or below the dimension-aware threshold |
| Full Scan | O(N) | O(N) | Columnar advantage |
Memory Budgetβ
| Component | Default | Configuration |
|---|---|---|
| MemTable | 64MB | memtable_size |
| Block Cache | 128MB | block_cache_size |
| HNSW Index | ~1.5KB/vec | M, ef_construction |
| PQ Codes (optional) | 32B/vec | subquantizers, centroids |
| WAL Buffer | 16MB | wal_buffer_size |
Latency Targetsβ
| Operation | Target | P99 |
|---|---|---|
| Point Read (cached) | <100ΞΌs | <500ΞΌs |
| Point Read (disk) | <1ms | <5ms |
| Point Write | <100ΞΌs | <1ms |
| Transaction Commit | <1ms | <5ms |
| Vector Search (10K) | <1ms | <5ms |
| Vector Search (1M) | <10ms | <50ms |
| Context Query | <50ms | <200ms |
Configuration Referenceβ
DatabaseConfigβ
pub struct DatabaseConfig {
// WAL settings
pub wal_segment_size: usize, // Default: 64MB
pub wal_sync_mode: SyncMode, // Fsync, FsyncDelayed, None
pub group_commit: bool, // Batch commits for throughput
pub group_commit_delay_us: u64, // Max wait time
// LSCS settings
pub memtable_size: usize, // Default: 64MB
pub level_ratio: usize, // Default: 10
pub max_levels: usize, // Default: 7
// GC settings
pub gc_interval_secs: u64, // Default: 60
pub min_versions_to_keep: usize, // Default: 2
}
ClientConfigβ
pub struct ClientConfig {
/// Maximum tokens per response (for LLM context management)
pub token_budget: Option<usize>,
/// Enable streaming output
pub streaming: bool,
/// Default output format
pub output_format: OutputFormat,
/// Connection pool size
pub pool_size: usize,
}
SyncMode Optionsβ
pub enum SyncMode {
Fsync, // fsync after every commit (safest)
FsyncDelayed, // fsync after group_commit_delay_us
None, // No fsync (fastest, risk of data loss)
}
Workspace Dependenciesβ
Key dependencies from Cargo.toml:
[workspace.dependencies]
# Serialization
serde = { version = "1.0", features = ["derive"] }
serde_json = "1.0"
bincode = "1.3"
# Compression
lz4 = "1.24"
zstd = "0.13"
# Data structures
crossbeam-skiplist = "0.1"
parking_lot = "0.12"
dashmap = "5.5"
# Hashing
blake3 = "1.5"
twox-hash = "1.6"
# Vectors
ndarray = "0.15"
# Error handling
thiserror = "1.0"
anyhow = "1.0"
# Async (optional)
tokio = { version = "1.35", optional = true }
Query Processing Pipelineβ
ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β Query Pipeline β
ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ€
β 1. PARSE β
β Input: conn.query("users").where_eq("status", "active") β
β Output: QueryAST { table, predicates, projections } β
β β
β 2. PLAN β
β β’ Choose access method (scan vs index) β
β β’ Push down predicates β
β Output: LogicalPlan β
β β
β 3. OPTIMIZE β
β β’ Cost-based index selection β
β β’ Predicate ordering by selectivity β
β Output: PhysicalPlan β
β β
β 4. EXECUTE β
β β’ Open column readers β
β β’ Apply predicates (vectorized) β
β Output: QueryResult β
β β
β 5. FORMAT β
β β’ TOON: users[N]{cols}:row1;row2;... β
β β’ JSON: [{"col": val}, ...] β
ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
Predicate Pushdownβ
fn push_predicates(plan: &mut ScanPlan, predicates: &[Predicate]) {
for pred in predicates {
match pred {
// Push to index lookup
Predicate::Eq(col, val) if is_indexed(col) => {
plan.index_lookup = Some(IndexLookup { column: col, value: val });
}
// Push to block-level filtering
Predicate::Range(col, min, max) => {
plan.block_filters.push(BlockFilter { column: col, min, max });
}
// Late filter (after scan)
_ => plan.late_filters.push(pred.clone()),
}
}
}
Memory Managementβ
Buddy Allocatorβ
ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β Buddy Allocator β
ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ€
β Order 10 (1KB): [ββββββββββββββββββββββββββββββββ] β
β β β
β ββββββββββ΄βββββββββ β
β Order 9 (512B): [ββββββββββββββββ] [________________] β
β β β
β βββββββ΄ββββββ β
β Order 8 (256B): [ββββββββ] [ββββ] ... β
β β
β Allocation: Find smallest power-of-2 block, split if needed β
β Deallocation: Coalesce with buddy if both free β
ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
Arena Allocatorβ
struct BuddyArena {
buddy: BuddyAllocator,
current_block: Mutex<Option<ArenaBlock>>,
block_size: usize,
}
impl BuddyArena {
fn allocate(&self, size: usize, align: usize) -> Result<usize> {
let mut current = self.current_block.lock();
// Try current block first
if let Some(ref mut block) = *current {
let aligned = (block.offset + align - 1) & !(align - 1);
if aligned + size <= block.size {
block.offset = aligned + size;
return Ok(block.base + aligned);
}
}
// Allocate new block from buddy allocator
let new_size = size.max(self.block_size).next_power_of_two();
let base = self.buddy.allocate(new_size)?;
*current = Some(ArenaBlock { base, offset: size, size: new_size });
Ok(base)
}
fn reset(&self) {
// Free all blocks at once - O(1) reset
self.current_block.lock().take();
}
}
Concurrency Modelβ
Lock Hierarchyβ
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β Lock Acquisition Order β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ€
β 1. Catalog Lock (RwLock) - Table schema changes β
β 2. Table Lock (per-table RwLock) - DDL operations β
β 3. Transaction Manager Lock (Mutex) - Begin/commit/abort β
β 4. WAL Lock (Mutex) - Append to write-ahead log β
β 5. Memtable Lock (RwLock) - In-memory writes β
β 6. Index Lock (per-index RwLock) - Index modifications β
β β
β ALWAYS acquire in this order to prevent deadlocks β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
Lock-Free Reads via MVCCβ
impl Database {
fn read(&self, key: &[u8], snapshot: Snapshot) -> Option<Value> {
// No locks needed - snapshot isolation
let version = self.mvcc.get_visible(key, snapshot.timestamp);
version.map(|v| v.value.clone())
}
}
// Snapshot is just a timestamp
struct Snapshot {
timestamp: u64,
txn_id: u64,
}
Python SDK Architectureβ
Access Modesβ
ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β Python Application β
ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ€
β sochdb (PyPI) β
β ββββββββββββββ ββββββββββββββ βββββββββββββββββββββββββββββββ β
β β Embedded β β IPC β β Bulk API β β
β β FFI β β Client β β (subprocess β sochdb-bulk) β β
β βββββββ¬βββββββ βββββββ¬βββββββ ββββββββββββββββ¬βββββββββββββββ β
ββββββββββΌββββββββββββββββΌββββββββββββββββββββββββββΌβββββββββββββββββ
β β β
ββββββΌβββββ βββββββΌββββββ βββββββββΌββββββββ
β Rust β β IPC β β sochdb-bulk β
β FFI β β Server β β binary β
β (.so) β β β β β
βββββββββββ βββββββββββββ βββββββββββββββββ
Distribution Model (uv-style wheels)β
Wheels contain pre-built Rust binaries - no compilation required:
sochdb-0.5.9-py3-none-manylinux_2_17_x86_64.whl
βββ sochdb/
β βββ __init__.py
β βββ database.py # Embedded FFI
β βββ ipc.py # IPC client
β βββ bulk.py # Bulk operations
β βββ _bin/
β βββ linux-x86_64/
β βββ sochdb-bulk # Pre-built binary
Platform matrix:
manylinux_2_17_x86_64- Linux glibc β₯ 2.17manylinux_2_17_aarch64- Linux ARM64macosx_11_0_universal2- macOS Intel + Apple Siliconwin_amd64- Windows x64
Bulk API FFI Bypassβ
For vector-heavy workloads, the Bulk API avoids FFI overhead:
Python FFI path (130 vec/s):
βββββββββββ memcpy ββββββββ
β numpy β ββββββββββββββ Rust β β repeated N times
βββββββββββ per batch ββββββββ
Bulk API path (1,600 vec/s):
βββββββββββ mmap ββββββββββββββββ fork ββββββββββββββββ
β numpy β βββββββββ β temp file β βββββββββ β sochdb-bulk β
βββββββββββ 1 write ββββββββββββββββ 1 proc βββββββββββββββββ
Result: 12Γ throughput improvement for bulk vector operations.
Summaryβ
SochDB's architecture delivers:
-
AI-Native Design:
- TOON format (typically 40-66% token savings vs JSON)
- Context queries with automatic token budgeting
- MCP integration for seamless LLM tool use
-
Dual Deployment:
- Embedded, SQLite-like in-process database
- Thick gRPC server with thin language clients
- Unified
connect()API acrossfile://,ipc://,grpc://,grpcs://
-
Hybrid Retrieval:
- HNSW approximate search with an exact SIMD flat-scan crossover
- Vector + BM25 + grep lanes fused via RRF (
k = 60.0) - Quantization (F32 / F16 / BF16, optional PQ)
-
Production-Ready:
- WAL durability with group commit and CDC
- MVCC transactions with SSI isolation
- Crash recovery with checkpoint/replay
- Optional auth (JWT validation, API keys), RBAC (Owner/Editor/Viewer), TLS/mTLS
-
High Performance:
- O(|path|) lookups via TCH (Trie-Columnar Hybrid)
- Lock-free reads via hazard pointers
- Volcano iterator SQL executor +
sochdb-fusionpipeline
This enables SochDB to replace the traditional AI stack (PostgreSQL + Pinecone + Redis + custom RAG) with a single, optimized system.