在本站中
本站的 Cargo.toml 里没有 crossbeam,代码里也搜不到 crossbeam、Select、select!。Cargo.lock 里的五个 crossbeam crate 全是间接依赖。这一部分回答三个问题:谁把它们带进来、用了哪些 API、本站锁的版本和本卷差在哪。
- tantivy IndexWriter 的有界 channel:本站生产构建里唯一在运行的 crossbeam channel。常见坑:接收端都走了,有界 channel 里的消息还没析构 在这里什么条件下发生、消息留多久;结论是只在所有索引线程都出错退出时才有,且留到本站那次写入函数返回为止。
- 版本差异:本站锁的 channel 0.5.15、deque 0.8.6、queue 0.3.12、utils 0.8.21 到本卷版本之间的 CHANGELOG,逐条判断对本站有没有影响。
flowchart TB site["本站 app(server 构建)"] --> search["module-search"] search --> tantivy["tantivy 0.27.0"] tantivy --> chan["crossbeam-channel 0.5.15"] tantivy --> rayon["rayon 1.12.0"] rayon --> rcore["rayon-core 1.13.0"] rcore --> deque["crossbeam-deque 0.8.6"] deque --> epoch["crossbeam-epoch 0.9.21"] site --> seaorm["sea-orm 1.1.20"] seaorm --> sqlx["sqlx-core 0.8.6"] sqlx --> queue["crossbeam-queue 0.3.12"] sqlx --> el["event-listener → concurrent-queue"] site --> dxs["dioxus-server → dashmap 6.2.1"] gw["网关 rie-gateway"] --> pool["pingora-pool 0.9.0"] pool --> queue2["crossbeam-queue 0.3.12(网关自己的 lock)"] chan --> utils["crossbeam-utils 0.8.21"] deque --> utils queue --> utils el --> utils dxs --> utils
锁定的版本
本站仓库根目录的 Cargo.lock(写作时 main 的 3292b81)和网关自己的 crates/gateway/Cargo.lock:
| crate | 主 lock | 网关 lock | 本卷 |
|---|---|---|---|
| crossbeam-channel | 0.5.15 | — | 0.5.17 |
| crossbeam-deque | 0.8.6 | — | 0.8.8 |
| crossbeam-epoch | 0.9.21 | — | 0.9.21 |
| crossbeam-queue | 0.3.12 | 0.3.12 | 0.3.14 |
| crossbeam-utils | 0.8.21 | 0.8.21 | 0.8.23 |
| crossbeam(门面)、crossbeam-skiplist | 无 | 无 | 0.8.5、0.1.3 |
依赖链
在本站目录下用 cargo tree --offline --locked -i <crate> -e normal 反查(只解析 lock,不编译)。分两种口径:--all-features --target all 是 lock 里出现的全部路径;-p app -F server --target x86_64-unknown-linux-gnu 是生产服务器上实际编进去的。
| crate | 谁带进来 | 生产构建里有没有 | 用到的 API |
|---|---|---|---|
| crossbeam-channel | tantivy 0.27.0(module-search) | 有 | IndexWriter 的 bounded(10_000);warmer GC 的 tick、多线程 Executor 的 unbounded(本站都走不到,见下) |
tao、wry、muda、tray-icon、global-hotkey(经 dioxus-desktop) | 没有:只在打开 desktop feature 时进来 | 事件循环、菜单、托盘的 unbounded / bounded(1) | |
| crossbeam-deque、crossbeam-epoch | rayon-core 1.13.0 ← rayon 1.12.0 ← tantivy | 有 | rayon 的 Worker / Stealer / Injector;epoch 经 deque 间接进来 |
rayon 也经 image(av1 编码、exr)← dioxus-desktop | 没有 | 同上 | |
| crossbeam-queue | sqlx-core 0.8.6 ← sqlx ← sea-orm | 有 | 连接池的 idle_conns: ArrayQueue |
| pingora-pool 0.9.0(网关) | 有(网关进程) | 每个节点 16 格的热队列 ArrayQueue | |
| crossbeam-utils | 以上全部;另有 dashmap 6.2.1(← dioxus-server)、concurrent-queue 2.5.0(← event-listener ← sqlx-core;网关里 ← async-channel ← pingora-lru) | 有 | dashmap、concurrent-queue 只用 CachePadded |
几点说明:
- wasm 前端不含 crossbeam:
cargo tree -p app --target wasm32-unknown-unknown里一个 crossbeam crate 都没有。tantivy、sqlx 都只在 server 特性下进来。 - 生产构建只有一条 channel 在运行:tantivy 非测试代码里另外两处 crossbeam channel 本站都走不到。warmer GC 线程只在配置了 warmer 时才启动(
warm_new_searcher_generation里 warmer 为空就直接返回),本站的module-search没有配 warmer;Executor的unbounded只在多线程 executor 下用,本站没有调set_multithread_executor,搜索走默认的单线程 executor。剩下的就是IndexWriter的写入管道,下一页专门讲。 - rayon 在本站里是 tantivy 的线程池:
SegmentUpdater有两个 rayon 线程池,一个单线程的处理元数据变更,一个是合并线程池(tantivy 卷 IndexWriter)。所以本卷 05 章的Worker/Stealer/Injector在本站里调度的是段合并任务;本站每次写入后都wait_merging_threads等合并做完(tantivy 卷 本站的搜索模块)。 - sqlx 和 pingora 只用
ArrayQueue:idle_conns.pop()/push()(sqlx 卷 获取连接),sqlx 另记计数,不用ArrayQueue::len(),原因见 ArrayQueue 与 SegQueue;pingora 的热队列push失败(16 格满了)就放进一把锁保护的溢出表(pingora 卷 连接池)。 - crossbeam-utils 里本站间接用到的只有
CachePadded(dashmap、concurrent-queue),以及 channel、deque、queue 内部用的Backoff。AtomicCell、ShardedLock、WaitGroup、作用域线程,本站的依赖都没有用。
常见坑在本站
逐条对照 09 常见坑:
| 坑 | 本站会不会碰到 |
|---|---|
| 有界 channel 接收端走后消息不析构 | tantivy IndexWriter 的 bounded(10_000) 在「所有索引线程都出错退出」时会;影响是内存,持续到写入函数返回(下一页) |
select! 随机、tick 漂移、SelectedOperation | 不会:本站和依赖都不用 select! / Select;tick 只在 warmer 下用,本站没有 |
bounded(0) | 生产构建不会;tantivy 只在一个测试里用 |
| 超时仍成功 | 生产构建不会:tantivy 只在测试里用 recv_timeout;wry 的 macOS 后端用了一处,属于 desktop |
在 async 里阻塞 recv | 不直接碰到 crossbeam 的 recv;但同类问题在:本站每 10 分钟一次的增量重建(main 里 tokio::spawn 的循环)和管理员的 search_reindex 都在 async 函数里直接调 module-search 的同步写入函数,没有包 spawn_blocking。add_document 在 channel 积满一万批时阻塞在 crossbeam 的 send 上,commit 要 join 索引线程,这期间都占住一个 tokio worker。这和 crossbeam 的版本无关 |
| 长期 pin | rayon 的 deque 只在扩容时 defer 旧 buffer,pin 都很短;本站没有直接用 epoch |
Retry 当 Empty、Worker 不是 Sync | rayon 内部处理,正确(Steal、Retry 与批量窃取) |
AtomicCell、作用域线程 | 依赖里没有用到 |
小结
- 本站没有直接依赖 crossbeam;主 lock 是 channel 0.5.15、deque 0.8.6、epoch 0.9.21、queue 0.3.12、utils 0.8.21,网关 lock 是 queue 0.3.12、utils 0.8.21。
- 生产构建里在运行的:tantivy
IndexWriter的有界 channel、rayon(tantivy 的合并线程池)的 deque 和 epoch、sqlx 和 pingora 连接池的ArrayQueue、dashmap 和 concurrent-queue 的CachePadded。 - desktop 相关的五个 crate 和
image的 rayon 只在desktopfeature 下进来,生产构建没有。
💬 关联讨论
发起讨论加载中...