triviumdb 0.4.91

A high-performance memory-mmap hybrid search engine built for AI, combining dense vector, sparse text, graph relations, and JSON metadata.
Documentation
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
# TriviumDB 安全特性详解


> 基于源码的完整文档,覆盖并发安全、数据完整性、内存安全、输入验证与跨平台 I/O 加固五个维度。

---

## 目录


- [并发安全模型]#并发安全模型
- [数据完整性保障]#数据完整性保障
- [内存安全与 unsafe 边界]#内存安全与-unsafe-边界
- [输入验证与参数防御]#输入验证与参数防御
- [跨平台 IO 加固]#跨平台-io-加固

---

## 并发安全模型


### 1. 进程级互斥锁(文件锁)


**实现位置**:`database.rs:169-181`

```rust
let lock_file = std::fs::OpenOptions::new()
    .create(true).write(true)
    .open(&lock_path)?;
lock_file.try_lock_exclusive().map_err(|_| {
    TriviumError::Generic(format!(
        "Database '{}' is already opened by another process. ...", path, lock_path
    ))
})?;
```

通过 `fs2::FileExt::try_lock_exclusive` 在 `<db_path>.lock` 文件上持有独占锁。锁由 `Database` 结构体的 `_lock_file` 字段持有,在 `Database` drop 时自动释放。

**保证**:
- 同一数据库文件**不可能**被两个进程同时写入
- 锁文件在进程异常退出后由 OS 自动释放(不同于 PID 文件,不会产生死锁残留)
- 在 Linux/macOS(`flock`)和 Windows(`LockFileEx`)上均有效

---

### 2. 线程级 `Arc<Mutex<T>>` + 中毒恢复

**实现位置**:`database.rs:114-121` + 全文所有读写操作

```rust
fn lock_or_recover<T>(mutex: &Mutex<T>) -> MutexGuard<'_, T> {
    mutex.lock().unwrap_or_else(|poisoned| {
        tracing::warn!("Mutex was poisoned, recovering...");
        poisoned.into_inner()
    })
}
```

`MemTable` 和 `Wal` 均包装在 `Arc<Mutex<T>>` 中。任何线程 `panic` 并持有锁时,Mutex 转为"中毒"状态。`lock_or_recover` 在此情形下不会 `unwrap` 崩溃,而是**主动剥离中毒标记,恢复内部数据继续服务**。

**保证**:
- 单线程 panic 不会导致整个进程崩溃
- 后续请求可以继续正常访问数据(数据状态由 WAL 保证一致性)
- 所有读写操作均通过此函数获取锁,**零例外**

---

### 3. WAL 写入的锁隔离策略


**实现位置**:`database.rs:279-293`(以 `insert` 为例)

```rust
// 先持有 memtable 锁写内存,再持有 wal 锁写日志
// 两把锁不同时持有,避免死锁
let id = {
    let mut mt = lock_or_recover(&self.memtable);
    mt.insert(vector, payload.clone())?
};
{
    let mut w = lock_or_recover(&self.wal);
    w.append(&WalEntry::Insert { ... })?;
}
```

`memtable` 锁和 `wal` 锁**从不同时持有**——先释放 memtable 锁,再获取 wal 锁。这是经典的锁顺序规则,消除了死锁的可能性。

**保证**:
- 任意并发调用组合下不会发生死锁
- 不同操作(insert、delete、link)遵循完全一致的锁获取顺序

---

### 4. Compaction 线程的并发安全


**实现位置**:`storage/compaction.rs`

后台 Compaction 线程通过 `Arc::clone` 共享 `memtable` 和 `wal` 的引用,同样使用 `lock_or_recover` 获取锁。Compaction 操作与前台写操作在同一个 Mutex 下序列化,**不存在竞态条件**。

---

## 数据完整性保障


### 5. WAL CRC32 逐条校验


**实现位置**:`storage/wal.rs:113-115`(写入),`wal.rs:232-244`(读取)

每条 WAL 记录的磁盘格式为:

```
[len: u32 (4B)] [bincode 序列化数据: len bytes] [crc32: u32 (4B)]
```

写入时计算,读取(崩溃恢复)时验证:

```rust
// 写入
let checksum = crc32fast::hash(&data);
writer.write_all(&checksum.to_le_bytes())?;

// 恢复
let computed_crc = crc32fast::hash(&data);
if stored_crc != computed_crc {
    tracing::error!("WAL CRC mismatch ... Stopping recovery.");
    break; // 停止回放,丢弃后续数据
}
```

**保证**:磁盘坏块、OS 写半条记录均可被检测。CRC 不匹配时**停止回放**而非跳过,防止损坏数据静默渗入。

---

### 6. WAL 单条记录大小上界检查


**实现位置**:`storage/wal.rs:214-217`

```rust
// 单条不超过 256MB
if len > 256 * 1024 * 1024 {
    break; // 损坏的 len 字段
}
```

防止损坏的 `len` 字段触发天量内存分配(OOM 或 DoS)。

---

### 7. WAL 事务原子性(TxBegin / TxCommit 封条)


**实现位置**:`storage/wal.rs:260-297`

崩溃恢复时对事务做两阶段过滤:只有见到匹配 `TxCommit` 的事务才会回放,否则整体丢弃:

| 状态 | 行为 |
|---|---|
| TxBegin + 匹配 TxCommit | 全量回放 |
| TxBegin,无 TxCommit(掉电) | **整体丢弃,并物理截断 WAL 尾部(Partial Truncation)** |
| 无事务边界的独立操作(旧格式) | 直接回放(向后兼容),推进安全游标 |

**极限防御:LSN (Log Sequence Number) 与物理截断**
如果在回放时发现未闭合的事务(通常因为机器暴力断电),系统不仅在内存中丢弃它们,还会通过计算**最后一个完美闭环事务的精确物理字节偏移量 (safe_commit_offset)**,在重播前直接调用内置 `set_len()`。这彻底切断了具有传染性的“幽灵尾部”,防止系统下次启动接收正常追加后,由于 `in_tx=true` 的状态污染,将新的健康数据错吞进旧的失效事务里。

**保证**:要么全部回放,要么全部丢弃;不出现"插入了 5 条、应该 10 条"的部分状态。并且绝对防止失效事务封条污染后续追加数据。

---

### 8. Mmap 双文件一致性标记(`.flush_ok`

**实现位置**:`storage/file_format.rs:86-103`(写),`file_format.rs:282-326`(读)

Mmap 模式下,`.tdb` 和 `.vec` 均写入成功后,才原子写 `.flush_ok` 标记,内含两文件的**精确字节大小**:

```
[tdb_size: u64 (8B)] [vec_size: u64 (8B)]
```

加载时交叉校验,失败则**降级为安全模式**(忽略 `.vec`,仅从 `.tdb` 骨架恢复 + WAL 回放)。

---

### 9. 文件魔数与最小尺寸校验


**实现位置**:`storage/file_format.rs:257-267`

```rust
const MAGIC: &[u8; 4] = b"TVDB";

if mmap.len() < HEADER_SIZE as usize {
    return Err(TriviumError::Generic("File too small for header".into()));
}
if &bytes[0..4] != MAGIC {
    return Err(TriviumError::Generic(
        format!("Invalid file magic: expected TVDB, got {:?}", &bytes[0..4])
    ));
}
```

防止加载非 TriviumDB 文件或截断损坏文件,同时避免后续偏移量计算出现越界读取。

---

### 10. 原子写入协议:write-tmp → fsync → rename


**实现位置**:`storage/file_format.rs:142-240`,`storage/vec_pool.rs:flush_rewrite()`

所有持久化路径均遵循:

```
① 写 .tmp 临时文件 → ② sync_all() → ③ robust_rename(tmp → 正式文件)
```

任何步骤崩溃,旧文件完好。`.tmp` 在下次启动时可安全忽略。

---

### 11. WAL 三级落盘模式


**实现位置**:`storage/wal.rs:48-68`

```rust
pub enum SyncMode {
    Full,    // 每条后 fsync — 防 OS 崩溃,金融级
    Normal,  // 每条后 flush 到 OS 缓冲 — 防进程崩溃(默认)
    Off,     // 不主动 flush — 仅测试
}
```

---

### 12. WAL Drop 安全刷盘


**实现位置**:`storage/wal.rs:336-341`

`Database::drop` 时调用 `flush_writer()`,将 `BufWriter` 缓冲区主动刷入磁盘,防止正常退出时因 `Arc<Mutex<Wal>>` 析构链导致的静默数据丢失。

---

## 内存安全与 unsafe 边界


### 13. mmap 的 unsafe 安全契约


**实现位置**:`storage/vec_pool.rs:open()` 和 `flush_append()`

```rust
// SAFETY: MAP_PRIVATE (copy-on-write)
//   - VectorType 要求 T: Pod + Zeroable,字节对齐和全零初始化安全
//   - len 由 expected_count * dim * size_of::<T>() 精确计算,不超出文件大小
let mmap = unsafe {
    memmap2::MmapOptions::new().len(expected_size).map_copy(&file)?
};
```

`MAP_PRIVATE` 映射:写入只产生进程私有 COW 页,不影响底层文件,其他进程/映射不受影响。

---

### 14. mmap 字节到 `&[T]` 转换安全性


**实现位置**:`storage/vec_pool.rs:get()` 和 `rebuild_merged_cache()`

```rust
let ptr = bytes.as_ptr();
if (ptr as usize) % std::mem::align_of::<T>() == 0 {
    unsafe { std::slice::from_raw_parts(ptr as *const T, self.dim) }
} else {
    // 非对齐:bytemuck::pod_read_unaligned 安全回退
}
```

mmap 返回的地址始终页对齐(4096B);`f32` 需 4B 对齐、`u64` 需 8B 对齐,均严格满足。代码中有运行时对齐检查和安全回退路径。

---

### 15. AVX2 SIMD 运行时 CPU 特性检测


**实现位置**:`vector.rs:130-141`

```rust
if is_x86_feature_detected!("avx2") && is_x86_feature_detected!("fma") {
    // SAFETY: 运行时已确认 CPU 支持
    return unsafe { cosine_similarity_avx2(a, b) };
}
cosine_similarity_scalar(a, b) // 安全标量回退
```

不支持 AVX2 的 CPU 自动回退到纯 Rust 路径,**不会执行非法指令**。

---

### 16. `bytemuck::Pod` 编译期内存安全约束


**实现位置**:`vector.rs:13-14`

```rust
pub trait VectorType: bytemuck::Zeroable + bytemuck::Pod + ...
```

`Pod`(Plain Old Data)是编译期保证:无指针、无引用、无 padding 歧义、全零合法。使得 `bytemuck::cast_slice` 在编译期被证明安全,完全不需要运行时 unsafe。

---

### 17. 数组边界守卫


**实现位置**:`database.rs`(BQ 精排分支)

```rust
if offset + dim <= vectors.len() {
    let score = T::similarity(query_vector, &vectors[offset..offset + dim]);
}
```

访问 `flat_vectors` 时,始终通过 `offset + dim <= len` 守卫,防止 `mmap_count` 与实际数组大小不一致时的越界访问。

---

## 资源配额与恶意负载防御 (Anti-DoS & OOM)


### 18. Cypher 引擎的 Lazy Evaluation 防 OOM 内存大爆炸


**实现位置**:`query/executor.rs:eval_expr_by_id()` 和路径扩展逻辑

```rust
// 🚀 OOM 防御:我们只在中间计算层存储 NodeId,绝对不克隆包含 Vector 和 Payload 的巨型 Node 结构!
let mut bindings_set: Vec<HashMap<String, u64>> = Vec::new();
```

由于图查询匹配(如 `(a)-[]->(b)-[]->(c)`)会产生爆炸性的笛卡尔积中间结果,若在每一步扩展中传递深拷贝的实体节点数据(包含高维向量、全量 JSON Payload),1 万条路径即会瞬间击穿几个 GB 的内存。
TriviumDB 执行器在整条中间路径遍历中**仅保留轻量级 `u64` IDs**,所有的 `WHERE` 财产过滤条件使用即时查询计算,仅仅在最后的 `RETURN` 吐出最终小切片结果时,才去执行昂贵的 `build_node` 数据装填,物理上隔离了内存爆炸。

---

### 19. 向量维度强校验


**实现位置**:`storage/memtable.rs`,`database.rs:Transaction::commit()`

```rust
if vector.len() != self.dim {
    return Err(TriviumError::DimensionMismatch { expected: dim, got: vector.len() });
}
```

所有写入向量路径(`insert`、`insert_with_id`、`update_vector`、事务 Dry-Run)均强校验维度,返回类型化错误而非 panic。

---

### 20. 查询向量 NaN / Infinity 检测


**实现位置**:`database.rs:462-475`

```rust
for item in qv {
    let f = item.to_f32();
    if f.is_nan() || f.is_infinite() {
        return Err(TriviumError::Generic("Query vector contains NaN or Infinity".into()));
    }
}
```

NaN 进入余弦计算会传染整个结果。在检索管线 L0 层直接拦截,防止毒素向量污染后续所有计算。

---

### 21. 认知管线参数钳位(防数学奇点)


**实现位置**:`database.rs:478-485`

```rust
safe_cfg.top_k              = safe_cfg.top_k.max(1);
safe_cfg.fista_lambda       = safe_cfg.fista_lambda.clamp(1e-5, 100.0);
safe_cfg.teleport_alpha     = safe_cfg.teleport_alpha.clamp(0.0, 1.0);
safe_cfg.dpp_quality_weight = safe_cfg.dpp_quality_weight.clamp(0.0, 10.0);
safe_cfg.bq_candidate_ratio = safe_cfg.bq_candidate_ratio.clamp(0.0, 1.0);
```

所有数学参数强制钳入合法范围:`fista_lambda` 过大全变 0,`teleport_alpha` 超出 `[0,1]` PPR 概率失去意义,`dpp_quality_weight` 过大导致 float 溢出。

---

### 22. 事务 Dry-Run 预检与极巨载荷拦截(零损伤回滚)


**实现位置**:`database.rs:Transaction::commit()` 第一阶段及其他直写 API

所有业务验证(节点是否存在、ID 是否冲突、维度是否匹配、是否包含 NaN/Inf)在**纯内存虚拟状态**上完成,不触碰 MemTable 或 WAL。

**防OOM极巨载荷拦截 (Payload Limiting)**:为防止恶意构造或意外产生的数以百兆计的超大 Payload 文本占用物理内存和撑爆日志,`Database::insert`、`update_payload` 和 `Transaction::commit` 会强制性对 JSON 载荷施加 **8MB 大小限制** (`MAX_PAYLOAD_SIZE`)。超越此数值的数据写入将被直接判定失败,保护操作系统的内存水位和文件视窗。

任何验证失败直接返回 `Err`,MemTable 和 WAL 零损伤。只有 Dry-Run 全部通过,才进入不可失败的 WAL 写入 → MemTable 应用路径。

---

### 23. Tombstone 节点的防误更新


**实现位置**:`storage/memtable.rs:update_vector()`

```rust
// 必须检查 payload 存在性,而非 ids_to_indices
// delete() 移除 payload 但 ids_to_indices 中的槽位仍存在(指向已置零位置)
if !self.payloads.contains_key(&id) {
    return Err(TriviumError::NodeNotFound(id));
}
```

防止对已逻辑删除的节点进行向量更新。

---

### 24. BQ 索引对删除操作的安全保证(零 Ghost Node)


**实现位置**:`database.rs:delete()`,`storage/memtable.rs`,`index/bq.rs`

传统图索引(如 HNSW)在节点被逻辑删除后,会在索引图中留下幽灵引用,导致检索结果污染和性能退化。BQ 从架构上彻底消除了这个问题:

- **纯扁平结构**:BQ 不是图索引,底层是连续的二进制指纹数组(`Vec<u64>`),不存在"边引用"的概念。删除节点等价于向量池置零 + Payload 移除。
- **Compaction 时原子切换**:下一次 Compaction 触发 BQ 重建时,置零节点不会进入新的指纹计算;新索引通过直接赋值原子切换,旧索引在 `Option` drop 时安全回收。
- **搜索时二次 ID 校验**`search()` 路径在 BQ 返回候选后,会用 `mt.get_id_by_index(idx) != 0` 对每个候选做活跃性检验,已删除节点(ID == 0)被静默过滤,不会出现在 `SearchHit` 中。

**保证**:删除操作对检索质量零副作用,无需用户手动触发重建,无 Tombstone GC 阈值配置负担。

---

### 25. 图谱扩散防爆炸截断


**实现位置**:`graph/traversal.rs:69-83`

```rust
// 能量阈值守护:得分 ≤ 0 的节点不再传播(防负反馈循环)
next_tier.retain(|_, energy| *energy > 0.0);

// 侧向抑制 Top-K(防稠密图 OOM)
if lateral_inhibition_threshold > 0 && next_tier.len() > lateral_inhibition_threshold {
    sorted_tier.truncate(lateral_inhibition_threshold);
}

if next_tier.is_empty() { break; } // 能量衰竭,提前终止
```

两道独立截断:能量守护防负反馈循环,侧向抑制 Top-K 防稠密图爆炸性展开导致 OOM。

---

## 跨平台 I/O 加固(Windows 兼容性)


### 24. mmap 释放先于 rename(P0 修复)


**实现位置**:`storage/vec_pool.rs:flush_rewrite()` 和 `flush_append()`

```rust
self.mmap = None;           // 先解除内核映射锁
robust_rename(&tmp, dst)?;  // 再执行原子替换
```

Windows 强制锁定语义:映射存活时 rename 目标文件必定 `ERROR_ACCESS_DENIED`。先 Drop mmap,COW 私有脏页安全丢弃(数据已写入 .tmp)。

---

### 25. 杀毒软件瞬态锁定重试(robust_rename)


**实现位置**:`storage/file_format.rs` 和 `storage/vec_pool.rs`

Windows 杀毒软件在文件关闭瞬间抢占扫描,通常几毫秒后自动释放。实现指数退避重试(1→2→4→…→50ms,最多 10 次)仅针对 `ERROR_ACCESS_DENIED(5)` 和 `ERROR_SHARING_VIOLATION(32)`,其他错误立即快速失败。非 Windows 平台编译为直接调用 `std::fs::rename`,零开销。

---

### 29. WAL 清空使用 truncate 而非 remove+create


**实现位置**:`storage/wal.rs:307-330`

`truncate(true)` 将文件截断为零字节但保留 inode,不触发杀软的"新文件扫描",避免 WAL clear 期间再次产生文件锁冲突。

---

### 30. OS 页面缓存的安全收回 `madvise`


**实现位置**:`storage/vec_pool.rs:advise_dontneed()`

```rust
// 通知 OS 立即回收刚刚写入磁盘的高维物理页,阻止污染 VFS 文件缓存
#[cfg(target_os = "linux")]

libc::madvise(ptr, len, libc::MADV_DONTNEED);
```
对于数十 GB 的向量基库,单纯依靠 OS 自我调节 LRU 会引发主机端周期性严重卡顿(Threshing)。引擎使用安全封装的非阻塞 FFI 建议系统,配合 Windows 的 `VirtualUnlock` 提供安全回收,以极低的成本维持了 60 帧 0 卡顿的主机交互体验。

---

## 附录:unsafe 使用汇总


| 位置 | unsafe 操作 | 安全契约 |
|---|---|---|
| `vec_pool.rs:open()` | `MmapOptions::map_copy()` | T: Pod+Zeroable;MAP_PRIVATE;len ≤ 文件实际大小 |
| `vec_pool.rs:flush_append()` | `MmapOptions::map_copy()` | 同上;重映射前旧 mmap 已释放 |
| `vec_pool.rs:get()` | `slice::from_raw_parts()` | 运行时对齐检查;index < mmap_count 守卫 |
| `vec_pool.rs:rebuild_merged_cache()` | `slice::from_raw_parts()` | 同上 |
| `file_format.rs:load()` | `Mmap::map()` | 仅读;mmap Drop 前不删文件 |
| `file_format.rs:load_bq()` | `ptr::copy_nonoverlapping()` | bytemuck Pod 对齐;dst Vec 已预分配足够容量;src 长度精确边界 |
| `vector.rs:cosine_similarity_avx2()` | AVX2 SIMD 指令 | 运行时 `is_x86_feature_detected!` 检测通过才调用 |
| `index/bq.rs:popcount_distance()` | CPU 原生 `popcnt` 指令 | 运行时自动检测 CPU 支持;纯数学运算,无内存安全风险 |

所有 `unsafe` 块均附有明确的 `// SAFETY:` 注释。整个代码库没有 `unsafe impl Send/Sync`——`Send + Sync` 由 `Arc<Mutex<T>>` 自动推导,类型系统级别安全。