SochDB Architecture
Deep technical documentation for SochDB's internal architecture.
Table of Contentsโ
- Design Philosophy
- Data Model
- TOON Format Internals
- Storage Engine
- Transaction System
- Index Structures
- Query Processing
- Memory Management
- Concurrency Model
- Recovery & Durability
- Python SDK Architecture
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 approach:
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ LLM โ JSON โ SQL Result โ Query Optimizer โ B-Tree โ
โ ~150 tokens for 3 rows โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
SochDB approach:
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ 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 (Trie-Columnar Hybrid):
โโ 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:
SELECT name, email FROM users WHERE id = 42;
Row Store (read all columns):
โโโโโโโฌโโโโโโโฌโโโโโโโโฌโโโโโโฌโโโโโโโโโโโโโโฌโโโโโโโโโ
โ id โ name โ email โ age โ preferences โ avatar โ
โโโโโโโดโโโโโโโดโโโโโโโโดโโโโโโดโโโโโโโโโโโโโโดโโโโโโโโโ
โ Read 6 columns ร row size
Columnar Store (read only needed):
โโโโโโโ โโโโโโโโ โโโโโโโโโ
โ id โ โ name โ โ email โ
โโโโโโโ โโโโโโโโ โโโโโโโโโ
Read 3 columns only = 50% I/O reduction
4. Embeddable & Extensibleโ
Single-file deployment with plugin architecture:
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ Your Application โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโค
โ SochDB (embedded) โ
โ โโโโโโโโโโโโโโ โโโโโโโโโโโโโโโ โโโโโโโโโโโโโโโโโโโ โ
โ โ Core โ โ Kernel โ โ Plugins โ โ
โ โ ~500 KB โ โ ~1 MB โ โ (optional) โ โ
โ โโโโโโโโโโโโโโ โโโโโโโโโโโโโโโ โโโโโโโโโโโโโโโโโโโ โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
Data Modelโ
Trie-Columnar Hybrid (TCH)โ
TCH combines trie-based path resolution with columnar storage:
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ Path Trie โ
โ โ
โ [root] โ
โ / \ โ
โ users orders โ
โ / \ \ โ
โ [id] [*] [id] โ
โ / | \ | โ
โ name email age amount โ
โโโโโโโโโโฌโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ
โโโโโโโโโโผโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ Column Store โ
โ โ
โ users.id [1, 2, 3, 4, 5, ...] โ
โ users.name [Alice, Bob, ...] โ
โ users.email [a@e.com, b@e.com, ...] โ
โ users.age [25, 30, 28, ...] โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
Path Resolution Algorithmโ
fn resolve(&self, path: &str) -> PathResolution {
let parts: Vec<&str> = path.split('/').collect();
// Navigate trie
let mut node = &self.root;
for part in &parts[..parts.len()-1] {
node = match node.children.get(*part) {
Some(child) => child,
None => return PathResolution::NotFound,
};
}
// Final part determines resolution type
let final_part = parts.last().unwrap();
match node.node_type {
NodeType::Table => PathResolution::Array { ... },
NodeType::Row => PathResolution::Value { ... },
NodeType::Column => {
// Direct column access
let col_idx = self.column_index(node, final_part);
PathResolution::Column { idx: col_idx }
}
}
}
Schema Representationโ
struct TableInfo {
schema: ArraySchema,
columns: Vec<ColumnRef>,
next_row_id: u64,
indexes: HashMap<String, IndexRef>,
}
struct ColumnRef {
id: u32,
name: String,
field_type: FieldType,
compression: Compression,
encoding: Encoding,
}
enum FieldType {
Int64,
UInt64,
Float64,
Text,
Bytes,
Bool,
Vector(usize),
}
TOON Format Internalsโ
Text Format Grammarโ
document ::= table_header newline row*
table_header ::= name "[" count "]" "{" fields "}" ":"
name ::= identifier
count ::= integer
fields ::= field ("," field)*
field ::= identifier
row ::= value ("," value)* newline
value ::= null | bool | number | string | array | ref
null ::= "โ
"
bool ::= "T" | "F"
number ::= integer | float
integer ::= "-"? digit+
float ::= "-"? digit+ "." digit+
string ::= raw_string | quoted_string
raw_string ::= [^,;\n"]+
quoted_string::= '"' ([^"\\] | escape)* '"'
array ::= "[" value ("," value)* "]"
ref ::= "ref(" identifier "," integer ")"
Binary Formatโ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ 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 ToonTypeTag {
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;
let mut bytes_read = 0;
for &byte in buf {
bytes_read += 1;
result |= ((byte & 0x7F) as u64) << shift;
if byte & 0x80 == 0 {
break;
}
shift += 7;
}
(result, bytes_read)
}
Storage Engineโ
Log-Structured Columnar Store (LSCS)โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ LSCS Architecture โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโค
โ โ
โ Write Path: โ
โ โโโโโโโโโโโ โโโโโโโโโโโ โโโโโโโโโโโ โโโโโโโโโโโ โ
โ โ Write โ โ โ WAL โ โ โMemtable โ โ โ SST โ โ
โ โ Request โ โ (Append)โ โ(In-mem) โ โ (Disk) โ โ
โ โโโโโโโโโโโ โโโโโโโโโโโ โโโโโโโโโโโ โโโโโโโโโโโ โ
โ โ
โ Read Path: โ
โ โโโโโโโโโโโ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ โ
โ โ Read โ โ โ Memtable โ L0 SSTs โ L1 โ L2 โ ... โ โ
โ โ Request โ โ (Bloom filter + block cache) โ โ
โ โโโโโโโโโโโ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ โ
โ โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
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 โ โโ
โ โโโโโโโโโโโโโโโโโโดโโโโโโโโโโโโโโโโโดโโโโโโโโโโโโโโโโโโโโโโโโโโ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
Block Checksumsโ
/// CRC32C with hardware acceleration (SSE4.2)
pub fn crc32c(data: &[u8]) -> u32 {
let mut crc: u32 = !0;
// Process 8 bytes at a time using CRC32Q
let chunks = data.chunks_exact(8);
let remainder = chunks.remainder();
for chunk in chunks {
let val = u64::from_le_bytes(chunk.try_into().unwrap());
crc = unsafe { _mm_crc32_u64(crc as u64, val) as u32 };
}
// Handle remaining bytes
for &byte in remainder {
crc = unsafe { _mm_crc32_u8(crc, byte) };
}
!crc
}
/// Mask CRC to avoid all-zeros problem
pub fn mask_crc(crc: u32) -> u32 {
((crc >> 15) | (crc << 17)).wrapping_add(0xa282ead8)
}
Compactionโ
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 โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
Transaction Systemโ
MVCC Implementationโ
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
}
struct MVCCStore {
current: HashMap<Key, VersionedValue>,
gc_watermark: AtomicU64,
}
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()
}
fn put(&self, key: Key, value: Vec<u8>, txn: &Transaction) {
let new_version = VersionedValue {
value: Some(value),
txn_id: txn.id,
timestamp: txn.commit_ts,
next: self.current.remove(&key).map(Box::new),
};
self.current.insert(key, new_version);
}
}
Transaction Lifecycleโ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ Transaction Lifecycle โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโค
โ โ
โ BEGIN โ
โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ โ
โ โ 1. Allocate txn_id (atomic increment) โ โ
โ โ 2. Take snapshot_ts = current_ts โ โ
โ โ 3. Add to active_transactions set โ โ
โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ โ
โ โ โ
โ โผ โ
โ OPERATIONS (read/write) โ
โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ โ
โ โ Reads: Use snapshot_ts for visibility โ โ
โ โ Writes: Buffer in transaction-local write set โ โ
โ โ Check for write-write conflicts โ โ
โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ โ
โ โ โ
โ โโโโโโโโโโโดโโโโโโโโโโ โ
โ โผ โผ โ
โ COMMIT ROLLBACK โ
โ โโโโโโโโโโโโโโโโโโโโ โโโโโโโโโโโโโโโโโโโโ โ
โ โ 1. Validate โ โ 1. Discard write โ โ
โ โ 2. Write to WAL โ โ set โ โ
โ โ 3. Apply writes โ โ 2. Remove from โ โ
โ โ 4. Advance ts โ โ active set โ โ
โ โ 5. Remove from โ โ 3. Release locks โ โ
โ โ active set โ โโโโโโโโโโโโโโโโโโโโ โ
โ โโโโโโโโโโโโโโโโโโโโ โ
โ โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
Group Commitโ
/// Optimal batch size: N* = โ(2 ร L_fsync ร ฮป / C_wait)
struct GroupCommitBuffer {
pending: VecDeque<PendingCommit>,
batch_id: u64,
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)
}
fn submit_and_wait(&self, op_id: u64) -> Result<u64> {
let mut inner = self.inner.lock();
inner.pending.push_back(PendingCommit {
id: op_id,
batch_id: inner.batch_id,
committed: false,
});
// Flush if batch is full
if inner.pending.len() >= self.config.target_batch_size {
self.flush_batch(&mut inner)?;
}
// Wait for commit (with timeout)
self.condvar.wait_for(&mut inner, self.config.max_wait);
Ok(inner.batch_id)
}
}
Index Structuresโ
HNSW (Vector Index)โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ 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 โ
โ โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
HNSW Parametersโ
struct HNSWConfig {
/// M: Max edges per node per layer (except layer 0)
/// Higher M = better recall, more memory
m: usize, // default: 16
/// M_max0: Max edges at layer 0
/// Usually 2*M
m_max: usize, // default: 32
/// ef_construction: Search width during build
/// Higher = better graph quality, slower build
ef_construction: usize, // default: 200
/// ef_search: Search width during query
/// Higher = better recall, slower query
ef_search: usize, // default: 50
/// Level multiplier: mL = 1/ln(M)
/// Controls probability of higher levels
ml: f32, // default: 1/ln(16) โ 0.36
}
Level Generationโ
/// Per HNSW paper: level = floor(-ln(uniform(0,1)) * mL)
fn random_level(&self) -> usize {
let uniform: f32 = thread_rng().gen();
let level = if uniform > 0.0 {
(-uniform.ln() * self.ml).floor() as usize
} else {
0
};
level.min(MAX_LEVEL)
}
// Distribution for M=16, mLโ0.36:
// P(level=0) โ 70%
// P(level=1) โ 21%
// P(level=2) โ 6%
// P(levelโฅ3) โ 3%
Search Algorithmโ
fn search(&self, query: &[f32], k: usize) -> Vec<(u64, f32)> {
let nodes = self.nodes.read();
let entry = self.entry_point.load(Ordering::Acquire);
if nodes.is_empty() {
return vec![];
}
let mut current = entry;
let mut current_dist = self.distance(query, &nodes[current].vector);
// Traverse from top to layer 1
for layer in (1..=self.max_level).rev() {
loop {
let mut changed = false;
for &neighbor in &nodes[current].layers[layer] {
let dist = self.distance(query, &nodes[neighbor].vector);
if dist < current_dist {
current = neighbor;
current_dist = dist;
changed = true;
}
}
if !changed { break; }
}
}
// Search at layer 0 with ef candidates
self.search_layer(&nodes, query, current, 0, self.ef_search)
.into_iter()
.take(k)
.map(|idx| (nodes[idx].edge_id, self.distance(query, &nodes[idx].vector)))
.collect()
}
Query Processingโ
Query 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 โ โ
โ โ โข Plan column projections โ โ
โ โ Output: LogicalPlan โ โ
โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ โ
โ โ โ
โ 3. OPTIMIZE โผ โ
โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ โ
โ โ โข Cost-based index selection โ โ
โ โ โข Predicate ordering (selectivity) โ โ
โ โ โข Join ordering (if applicable) โ โ
โ โ Output: PhysicalPlan โ โ
โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ โ
โ โ โ
โ 4. EXECUTE โผ โ
โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ โ
โ โ โข Open column readers โ โ
โ โ โข Apply predicates (vectorized) โ โ
โ โ โข Materialize results โ โ
โ โ 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 {
// Can push to storage layer
Predicate::Eq(col, val) if is_indexed(col) => {
plan.index_lookup = Some(IndexLookup {
column: col.clone(),
value: val.clone(),
});
}
// Push to block-level filtering
Predicate::Range(col, min, max) => {
plan.block_filters.push(BlockFilter {
column: col.clone(),
min: min.clone(),
max: max.clone(),
});
}
// 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
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
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
self.current_block.lock().take();
for block in self.blocks.drain(..) {
self.buddy.deallocate(block);
}
}
}
Concurrency Modelโ
Lock Hierarchyโ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ Lock Acquisition Order โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโค
โ โ
โ 1. Catalog Lock (RwLock) โ
โ โโโ Table schema changes โ
โ โ
โ 2. Table Lock (per-table RwLock) โ
โ โโโ DDL operations on specific table โ
โ โ
โ 3. Transaction Manager Lock (Mutex) โ
โ โโโ Begin/commit/abort transactions โ
โ โ
โ 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โ
// MVCC enables lock-free reads via snapshots
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,
}
Recovery & Durabilityโ
WAL 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> {
let mut stats = RecoveryStats::default();
// 1. Find latest checkpoint
let checkpoint = self.find_latest_checkpoint()?;
stats.checkpoint_lsn = checkpoint.lsn;
// 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) {
// Skip aborted transaction
continue;
}
self.replay_put(&record)?;
stats.records_replayed += 1;
}
RecordType::CommitTxn => {
active_txns.remove(&record.txn_id);
stats.transactions_recovered += 1;
}
RecordType::AbortTxn => {
// Roll back buffered writes
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)?;
stats.transactions_aborted += 1;
}
Ok(stats)
}
Checkpointingโ
fn checkpoint(&self) -> Result<u64> {
// 1. Acquire checkpoint lock
let _guard = self.checkpoint_lock.lock();
// 2. Get current LSN
let checkpoint_lsn = self.wal.current_lsn();
// 3. Flush memtable to SST
let immutable = self.memtable.freeze();
self.flush_memtable_to_sst(&immutable)?;
// 4. Write checkpoint record
let active_txns = self.txn_manager.active_transactions();
self.wal.append(WalRecord::Checkpoint {
lsn: checkpoint_lsn,
active_txns,
})?;
// 5. Sync WAL
self.wal.sync()?;
// 6. Remove old WAL segments
self.wal.truncate_before(checkpoint_lsn)?;
Ok(checkpoint_lsn)
}
SDK Architectureโ
SochDB provides official SDKs for multiple languages. All SDKs communicate with the SochDB server via IPC (Unix domain sockets on Linux/macOS, named pipes on Windows).
Client-Server Architectureโ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ Client Applications โ
โ โโโโโโโโโโโโโโโ โโโโโโโโโโโโโโโโโโโ โโโโโโโโโโโโโโโโ โโโโโโโโโโโโโโ โ
โ โ Python โ โ JavaScript โ โ Go โ โ Rust โ โ
โ โ sochdb-py โ โ @sushanth/ โ โ sochdb-go โ โ sochdb โ โ
โ โ โ โ sochdb โ โ โ โ crate โ โ
โ โโโโโโโโฌโโโโโโโ โโโโโโโโโฌโโโโโโโโโโ โโโโโโโโฌโโโโโโโโ โโโโโโโฌโโโโโโโ โ
โโโโโโโโโโโผโโโโโโโโโโโโโโโโโโผโโโโโโโโโโโโโโโโโโโโโผโโโโโโโโโโโโโโโโโโผโโโโโโโโ
โ โ โ โ
โโโโโโโโโโโโโโโโโโโผโโโโโโโโโโโโโโโโโโโโโผโโโโโโโโโโโโโโโโโโ
โ IPC/Socket โ
โ (sochdb.sock) โ
โผ โผ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ SochDB Server โ
โ (sochdb-mcp) โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโค
โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ โ
โ โ Protocol Handler โ โ
โ โ (Wire Protocol Parser) โ โ
โ โโโโโโโโโโโโโโโฌโโโโโโโโโโโโโโโโ โ
โ โ โ
โ โโโโโโโโโโโโโโโผโโโโโโโโโโโโโโโโ โ
โ โ Query Engine โ โ
โ โ (sochdb-query crate) โ โ
โ โโโโโโโโโโโโโโโฌโโโโโโโโโโโโโโโโ โ
โ โ โ
โ โโโโโโโโโโโโโโโผโโโโโโโโโโโโโโโโ โ
โ โ Storage Engine โ โ
โ โ (LSM + WAL + Memtable) โ โ
โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
SDK Features Comparisonโ
| Feature | Python | JavaScript | Go | Rust |
|---|---|---|---|---|
| Embedded Mode | โ Auto | โ Auto | โ Manual | โ Direct |
| IPC Client | โ | โ | โ | โ |
| Vector Search | โ | โ | โ | โ |
| Transactions | โ | โ | โ | โ |
| Query Builder | โ | โ | โ | โ |
| Pre-built Binaries | โ | โ | N/A | N/A |
Embedded vs External Server Modeโ
Embedded Mode (Python & JavaScript):
The SDK automatically spawns and manages a sochdb-mcp server process. The server is started on Database.open() and stopped on Database.close().
# Python - embedded mode (default)
db = SochDB.open("./my_database")
# Server started automatically
db.close()
# Server stopped automatically
// JavaScript - embedded mode (default)
const db = await Database.open('./my_database');
// Server started automatically
await db.close();
// Server stopped automatically
External Server Mode (Go): The Go SDK requires manual server startup:
# Start server first
./sochdb-mcp serve --db ./my_database
// Then connect from Go
db, err := sochdb.Open("./my_database")
Python SDK Architectureโ
The Python SDK provides multiple access patterns to SochDB:
Access Modesโ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ Python Application โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโค
โ sochdb (PyPI) โ
โ โโโโโโโโโโโโโโ โโโโโโโโโโโโโโ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ โ
โ โ Embedded โ โ IPC โ โ Bulk API โ โ
โ โ FFI โ โ Client โ โ (subprocess โ sochdb-bulk) โ โ
โ โโโโโโโฌโโโโโโโ โโโโโโโฌโโโโโโโ โโโโโโโโโโโโโโโโฌโโโโโโโโโโโโโโโ โ
โ โ โ โ โ
โโโโโโโโโโผโโโโโโโโโโโโโโโโผโโโโโโโโโโโโโโโโโโโโโโโโโโผโโโโโโโโโโโโโโโโโ
โ โ โ
โโโโโโผโโโโโ โโโโโโโผโโโโโโ โโโโโโโโโผโโโโโโโโ
โ Rust โ โ IPC โ โ sochdb-bulk โ
โ FFI โ โ Server โ โ binary โ
โ (.so) โ โ โ โ โ
โโโโโโฌโโโโโ โโโโโโโฌโโโโโโ โโโโโโโโโฌโโโโโโโโ
โ โ โ
โโโโโโโโโโโโโโโโโดโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ
โโโโโโโผโโโโโโ
โ SochDB โ
โ Core โ
โโโโโโโโโโโโโ
Distribution Model (uv-style)โ
Wheels contain pre-built Rust binaries, eliminating compilation requirements:
sochdb-0.2.3-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
โโโ METADATA
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 โโโโโโโโโโโโโโโโ
See PYTHON_DISTRIBUTION.md for full distribution architecture.
This document describes SochDB v0.2.4 internals. Implementation details may change.