Rust in Everything
基于 crossbeam 0.8.5

在本站中

本站的 Cargo.toml 里没有 crossbeam,代码里也搜不到 crossbeam、Select、select!。Cargo.lock 里的五个 crossbeam crate 全是间接依赖。这一部分回答三个问题:谁把它们带进来、用了哪些 API、本站锁的版本和本卷差在哪。

  1. tantivy IndexWriter 的有界 channel:本站生产构建里唯一在运行的 crossbeam channel。常见坑:接收端都走了,有界 channel 里的消息还没析构 在这里什么条件下发生、消息留多久;结论是只在所有索引线程都出错退出时才有,且留到本站那次写入函数返回为止。
  2. 版本差异:本站锁的 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-channel0.5.15—0.5.17
crossbeam-deque0.8.6—0.8.8
crossbeam-epoch0.9.21—0.9.21
crossbeam-queue0.3.120.3.120.3.14
crossbeam-utils0.8.210.8.210.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-channeltantivy 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-epochrayon-core 1.13.0 ← rayon 1.12.0 ← tantivy有rayon 的 Worker / Stealer / Injector;epoch 经 deque 间接进来
rayon 也经 image(av1 编码、exr)← dioxus-desktop没有同上
crossbeam-queuesqlx-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 的版本无关
长期 pinrayon 的 deque 只在扩容时 defer 旧 buffer,pin 都很短;本站没有直接用 epoch
Retry 当 Empty、Worker 不是 Syncrayon 内部处理,正确(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 只在 desktop feature 下进来,生产构建没有。

💬 关联讨论

发起讨论
加载中...