Skip to content

Gen-USearch: generational vector index for MemoChunk / RiverMemo - #485

Closed
JENN2046 wants to merge 280 commits into
lioensky:mainfrom
JENN2046:codex/gen-usearch-g0-f2-upstream-main
Closed

JENN2046 wants to merge 280 commits into
lioensky:mainfrom
JENN2046:codex/gen-usearch-g0-f2-upstream-main

Conversation

@JENN2046

@JENN2046 JENN2046 commented Oct 4, 2026 •

Copy link
Copy Markdown

概要

这个 PR 引入了 Gen-USearch,它是给 MemoChunk / RiverMemo 准备的一套“分代式向量索引”底层能力。

它的目标不是马上替换现在用户正在用的 KnowledgeBase 搜索,而是先把一套真正能长期运行的底座补齐,包括:

  • 文档、Chunk、向量都有稳定且持久的 ID;
  • 用 MVCC 管理数据版本和生命周期;
  • Gen0 内存索引和实际物理覆盖关系;
  • 不可变 Segment 的生成和 Manifest 管理;
  • 查询时使用 QueryReadView,保证一次查询看到的是同一个一致快照;
  • 用 reader lease 和 runtime fence 防止并发、跨进程和旧运行实例破坏一致性;
  • 安全地判断什么时候旧向量可以进入 GC;
  • 在满足条件后释放恢复数据;
  • 原生向量 ID 使用完整 signed-int64,不经过会丢精度的 JavaScript Number;
  • 对仓库里实际提交的各个平台 native 二进制做 ABI 验证。

在正式交给 upstream 作者审查之前,我们已经把原来的内部工程包做了一次收敛。

现在 PR 中留下的主要只有:

  • 正式生产代码;
  • 一份长期架构文档;
  • 功能 / 对抗 / 边界回归测试;
  • 两条长期维护的 CI。

内部 Gate、审查日志、authority-lock、临时验证脚手架等都已经移除。

当前审查基线

当前 exact head:
1731e8b4652179f0c05ad46edbc4ccfc00362524

基线:
d98912a637d927a0678c6b54f1fdc7e24f1d4122

变更文件:
37

PR 状态:
OPEN / READY FOR REVIEW / MERGEABLE

为什么要做 Gen-USearch

现在已有的 Chunk 分代能力可以解决一部分问题,但如果要做真正可靠的分代向量索引,还需要额外解决:

  • ID 不能因为文件移动或 Chunk 换位置就乱掉;
  • 查询必须拿到一个一致的版本快照;
  • 内存索引和磁盘 Segment 的物理状态需要有权威来源;
  • Segment 发布过程中断电或崩溃不能留下半套状态;
  • 查询中的 reader 还没结束时,不能提前把旧数据 GC 掉;
  • 必须明确什么情况下旧数据才允许被安全释放。

所以 Gen-USearch 把不同类型的“权威”拆开:

  • Canonical Source:原始内容本身的权威来源;
  • SQLite Metadata:ID 和生命周期状态的权威来源;
  • SQLite Manifest:磁盘 Segment 拓扑的权威来源;
  • QueryReadView:一次查询所看到的逻辑快照;
  • 被 pin 住的 Gen0 MemTable 和 immutable Segment:真正提供候选向量的物理来源。

逻辑版本变化和物理拓扑变化使用两个独立计数器:

  • visibility_seq
  • manifest_epoch

完整的长期架构和安全规则整理在:

docs/GEN_USEARCH_ARCHITECTURE.md

这个 PR 具体实现了什么

1. 稳定 ID + MVCC

现在:

  • 文档有稳定 doc_id;
  • Chunk 有稳定 chunk_id;
  • 向量有完整 signed-int64 vector_id;
  • 向量 ID 分配器持久化、单调递增、不复用;
  • Chunk 版本使用下面的生命周期:
PREPARED
→ EMBEDDING
→ VECTOR_STAGED
→ ACTIVE
→ RETIRED
→ GC_ELIGIBLE

早期状态也可以进入 ABORTED。

当前版本切换使用 SQLite CAS。

恢复用的精确向量数据会一直保留,直到系统能证明这个向量已经安全存在于持久化 Segment 中。

源内容 reconciliation 只接受“已经完整提交、字节稳定”的 source view。

正式 admission 使用 withCommittedSourceView():

也就是说,从“看到这份 source”到“生成 plan 并正式写入”这一段时间里,这份 source 必须保持有效,避免刚读完 A,源已经变成 B,我们却还把 A 当成最新内容写进去。

2. Gen0 内存物理层

Gen0 使用运行时本地的 native Vexus MemTable。

流程是:

SQLite 中已有 VECTOR_STAGED
→ native MemTable 真正接收向量
→ SQLite 写入 MEMTABLE QUERY_VISIBLE coverage
→ 之后才允许成为逻辑 current head

也就是说:

不能只因为 SQLite 里说“有这个向量”,就假设 native 内存里真的有。

启动恢复时,可以用之前保存的精确 recovery bytes 重建 Gen0。

另外增加了跨进程 writer 保护:

writer 不只看 runtime owner 和 fence,还必须持有对应的 durable process lease。

所以同一个 fence 下,另一个进程不能偷偷变成第二个权威 writer。

3. Immutable Segment + Manifest

一个 SEALED 的 Gen0 generation 可以被构造成不可变 Segment。

Segment 发布时会验证:

  • Segment ID;
  • artifact 路径;
  • SHA-256;
  • dimension;
  • vector count;
  • 里面实际包含哪些 key;
  • embedding fingerprint;
  • 对应 SQLite coverage。

最终 artifact 必须先真正落盘,再允许 SQLite 把它写进当前 Manifest。

segmentRoot 也不再自动创建,而是要求调用方显式提供一个:

  • 已存在;
  • 专用;
  • 非 symlink;

的目录。

Windows 上发布 Segment 时使用 write-through 语义,避免文件名看起来已经替换成功,但磁盘上实际上还没真正持久化。

4. QueryReadView + 查询

每一次查询都会创建一个 QueryReadView。

它会冻结这次查询需要看到的:

  • visibility_seq;
  • metadata snapshot;
  • manifest snapshot;
  • 当前可查询的 coverage;
  • MemTable generation;
  • runtime owner / fence;
  • 创建时间;
  • deadline。

所以一次查询不会出现:

前半段看到旧世界,后半段又突然切到新世界。

reader 的生命周期有两层保护:

  • SQLite 中的 durable read-view lease;
  • 当前进程里的 physical pin。

deadline 到了以后会请求取消,但不会偷偷释放 pin。

进入 QUIESCING 后不允许继续开始新的查询工作。

只有 worker 真正结束,才允许释放 pin。

最终返回结果前,还会再次验证:

  • runtime fence 有没有变;
  • QueryReadView 有没有已经过期。

5. GC 安全 + recovery 数据释放

现在支持:

RETIRED
→ GC_ELIGIBLE

以及:

SEGMENT_COVERED
→ RECOVERY_RECLAIMABLE
→ RECOVERY_RELEASED

但是只有在能够证明:

  • 当前版本已经不是这个旧版本;
  • 没有旧 QueryReadView 还能看到它;
  • 对应恢复数据已经安全转移到当前有效 Segment;

之后,旧版本才可以进入 GC_ELIGIBLE。

同样,recovery bytes 也只有在重新验证以下内容都正确后才能释放:

  • 当前 Manifest 中仍然存在对应 Segment;
  • Segment 状态正确;
  • artifact 路径正确;
  • SHA-256 正确;
  • dimension / count 正确;
  • coverage 正确;
  • key 真的存在;
  • embedding fingerprint 一致。

整个过程默认 fail-closed。

也就是说,只要哪里证明不了,就不删。

兼容性和加固

这个 PR 还包括:

  • 对当前 upstream 数据库保持 additive schema 初始化;
  • 对早期 Gen-USearch 开发版本数据库提供明确 migration;
  • 关键持久化操作拒绝在 ambient SQLite transaction 里偷偷执行;
  • 跨进程 runtime process lease;
  • 跨进程 durable QueryReadView lease;
  • current-head、manifest、recovery 等关键状态的 postcondition 检查;
  • 实际提交到仓库里的 native binary 做跨平台 ABI 验证。

目前覆盖的平台包括:

  • Linux x64 glibc
  • Linux arm64 glibc
  • Linux x64 musl
  • Linux arm64 musl
  • macOS arm64
  • Windows x64

旧的:

rust-vexus-lite/vexus-lite.node

也已经删除。

这个文件实际上是一个旧 Windows PE binary,而现在自动生成的 NAPI loader 使用的是明确的平台文件名,所以继续保留 generic 文件反而容易留下过期二进制。

明确不在这个 PR 里做的事情

这个 PR 不会直接把 Gen-USearch 接进当前用户正在使用的 KnowledgeBase 搜索路径。

本 PR 不负责:

  • 接入现有 KnowledgeBaseManager;
  • 接入现有 ingestion;
  • 接入当前 searchService;
  • 开启 GENERATIONAL_SHADOW;
  • 开启 GENERATIONAL_ACTIVE;
  • runtime ownership 获取、转移、crash takeover;
  • 物理 MemTable reclaim;
  • Segment compaction;
  • Segment 物理回收;
  • 删除 immutable Segment 文件。

这些应该是后续单独的集成 / 运维决策,而不是因为底层能力已经存在,就自动启用。

最终 upstream 交付面

现在这个 PR 只保留长期值得维护的东西:

  • Gen-USearch 正式代码和 schema 修改;
  • Rust Vexus 源码、ABI 修改和各平台 native artifacts;
  • docs/GEN_USEARCH_ARCHITECTURE.md;
  • 功能测试;
  • 对抗测试;
  • architecture invariant 测试;
  • authority boundary 测试;
  • .github/workflows/gen-usearch.yml;
  • .github/workflows/gen-usearch-native-package.yml。

以下内部工程材料已经在正式 review 前移除:

  • G0-G5 内部 Gate contract;
  • authority-lock;
  • traceability / acceptance machine schema;
  • review 历史和 evidence;
  • 每个 Gate 各自一条的 CI;
  • 重复 test package;
  • 一次性的 native rebuild workflow。

Exact-head 验证

当前所有最终 CI 都已经在:

1731e8b4652179f0c05ad46edbc4ccfc00362524

这一颗 exact head 上通过。

验证 Run 结果
Gen-USearch / regression 37352410057 SUCCESS
Gen-USearch / windows-native 37352410057 SUCCESS
Packaged Native ABI / Linux x64 glibc 37352410082 SUCCESS
Packaged Native ABI / Linux arm64 glibc 37352410082 SUCCESS
Packaged Native ABI / Linux x64 musl 37352410082 SUCCESS
Packaged Native ABI / Linux arm64 musl 37352410082 SUCCESS
Packaged Native ABI / macOS arm64 37352410082 SUCCESS
Packaged Native ABI / Windows x64 37352410082 SUCCESS

其他交付检查:

local / remote / PR head = MATCH
worktree                 = CLEAN
diff --check             = PASS
内部审查状态文案残留       = 0
changed files            = 37

希望作者重点帮忙看的地方

我们认为下面这些地方最值得 upstream maintainer 从长期架构角度判断:

  1. stable identity / MVCC / manifest / QueryReadView 这样的职责拆分,是否符合 KnowledgeBase 未来的方向;
  2. withCommittedSourceView() 是否适合作为 canonical source 的 admission 边界;
  3. durable runtime-process lease 和 durable read-view lease 是否适合作为 upstream 长期基础能力;
  4. schema migration 和跨平台 native binary 的维护成本是否可以接受;
  5. 当前 Segment durability / Manifest publication 的模型是否符合 upstream 的运维预期;
  6. 这套 Gen-USearch 是否适合继续作为一个完整 PR,还是作者更希望进一步拆分或调整结构。

当前审查状态

目前实现已经可以进入 upstream maintainer review。

如果作者 review 后提出修改意见,我们会基于新的 exact head 修复,并重新跑:

  • Gen-USearch regression;
  • Windows native;
  • 全平台 Packaged Native ABI;

然后再决定后续 merge。

最终是否合并,由 upstream maintainer 决定。

@JENN2046
JENN2046 marked this pull request as ready for review October 5, 2026 11:51
@JENN2046
JENN2046 marked this pull request as draft October 5, 2026 12:09
@JENN2046
JENN2046 marked this pull request as ready for review October 5, 2026 12:22
@JENN2046
JENN2046 marked this pull request as draft October 5, 2026 15:32
@JENN2046
JENN2046 marked this pull request as ready for review October 5, 2026 17:28
@lioensky

lioensky commented Oct 6, 2026

Copy link
Copy Markdown
Owner

1.这个查询实现是否太重了,尤其是万到十万级索引的多agent交互并发侧,目前的实现框架真的可用吗?
2.热路径上的SQLite Lease 写入在当前设计是否存在高频竟态?
3.MemTable 这个实现有问题吧?为何变回了JS侧的单循环?

@lioensky

lioensky commented Oct 6, 2026

Copy link
Copy Markdown
Owner

在当前架构设计里,usearch的幽灵向量也是伪命题,因为服务器不以usearch为真相,实际查询来自sql内部的blob,usearch天然只做暖机缓存,它每次都是全量刷新,里面几乎不可能产生幽灵内存指针。感觉所谓的指针头设计也是无必要设计。

@lioensky

lioensky commented Oct 6, 2026

Copy link
Copy Markdown
Owner

感觉最大问题是系统架构建立在错误假设的前提上。

@lioensky

lioensky commented Oct 6, 2026

Copy link
Copy Markdown
Owner

审议后不予通过。

@lioensky lioensky closed this Oct 6, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants