Rust in Everything
基于 pingora-core 0.9.0

Worker threads cannot be set to 0 / assertion failed: threads != 0

主线位置:服务主线的「Server::new 读配置」和「run_forever 给每个服务建运行时」。ServerConf 各字段的默认值和读它的代码见配置与命令行,两种运行时见运行时。本页讲 threads: 0 一路被放行到哪里才出错。

flowchart TB
  yaml["threads 0"] --> parse["from_yaml"]
  parse --> val["validate 不查"]
  val --> test{"带 -t"}
  test -->|是| pass["Server Test passed"]
  test -->|否| boot["bootstrap"]
  boot --> rt["create_runtime"]
  rt --> ws{"work_stealing"}
  ws -->|true| p1["tokio Builder panic"]
  ws -->|false| p2["NoSteal assert"]

实验 crate exp-pingora/pita-lab,bin svc:一个 ServeHttp 服务,Server::new(Some(Opt::parse_args()));M2 Max、macOS 27、rustc 1.99.0,debug,tokio 1.53.2。

现象

两份配置,conf/t0.yaml 是 threads: 0,conf/t0ns.yaml 再加 work_stealing: false:

== t0
[  0.011s INFO svc] conf threads=0 work_stealing=true grace=None shutdown_timeout=None pid=41536
[  0.011s INFO pingora_core::server::bootstrap_services] Bootstrap starting
[  0.011s INFO pingora_core::server::bootstrap_services] Bootstrap done
[  0.012s INFO pingora_core::server] Server starting
[  0.012s INFO pingora_core::server] Starting services in dependency order: ["lab"]
[  0.012s INFO pingora_core::server] Starting service: lab

thread 'main' (5859152) panicked at ~/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/pingora-runtime-0.9.0/src/lib.rs:471:14:
Worker threads cannot be set to 0
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
exit=101
== t0ns
...
thread 'main' (5859156) panicked at ~/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/pingora-runtime-0.9.0/src/lib.rs:611:9:
assertion failed: threads != 0
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
exit=101
  • 配置读进来了(第一行打出 threads=0),Bootstrap done,服务也开始启动,panic 在建运行时的那一刻。
  • 同一个配置错误,两种运行时报的是两句不相干的话:Steal 的那句来自 tokio 的 Builder::worker_threads,NoSteal 的是 pingora 自己的 assert!。搜日志时要两句都搜。
  • 用 -t 检查这份配置:
[  0.000s INFO pingora_core::server::bootstrap_services] Bootstrap starting
[  0.000s INFO pingora_core::server::bootstrap_services] Server Test passed, exiting
exit=0

-t 在 bootstrap 里就退出了,走不到建运行时那一步,所以部署前用 -t 做的配置检查放过了它。

出处

ServerConf::from_yaml 解析完调 validate,它只查 max_blocking_threads 和两个运行时直方图参数,不看 threads:

    pub fn validate(self) -> Result<Self> {
        if self.max_blocking_threads == Some(0) {
            return Error::e_explain(ReadError, "max_blocking_threads must be greater than zero");
        }
        // ... two histogram checks ...
        Ok(self)
    }

threads 一直走到 run 里:每个服务取 service.threads().unwrap_or(conf.threads),交给 run_service → create_runtime。Steal 走 build_work_stealing_tokio_builder,第 471 行 .worker_threads(self.threads) 由 tokio 断言;NoSteal 走 NoStealRuntime::new,文档注释写着「Panic if threads is 0」,第 611 行 assert!(threads != 0)。一个服务自己用 threads() 返回 Some(0) 也是同一条路。0.9.0 之后的 main(84f21ee)上 validate 和这两处都没变。

📝NOTE
冷知识:validate 在 0.1.0 到 0.8.0 一直是个空函数

0.1.0 的 validate 只有两行:// TODO: do the validation 和 Ok(self),0.8.0 还是这样。b994854(2026-03-16,「Add config for tokio blocking pool options」,没有 PR 编号,首次进入 0.9.0)给 ServerConf 加了 max_blocking_threads 和 blocking_threads_ttl_seconds,顺手把那行 TODO 换成了第一条检查:max_blocking_threads == Some(0) 返回 ReadError。也就是说,0.9.0 里 validate 拦下的只是这个提交新加的字段,从 0.1.0 就有的 threads 仍然没人检查;它的 0 会一直传到 tokio 或 NoStealRuntime 里。

修法

pingora 不查,就在 bootstrap 之前自己查,配置错误时干净地退出(svc 的 SVC_CHECK=1):

  if server.configuration.threads == 0 {
    eprintln!("config error: threads must be at least 1");
    std::process::exit(2);
  }
  server.bootstrap();
[  0.011s INFO svc] conf threads=0 work_stealing=true grace=None shutdown_timeout=None pid=42090
config error: threads must be at least 1
exit=2

放在 bootstrap 之前,-t 也会经过它,配置检查就能拦下这个值。改成 threads: 2 照常启动。

怎么确认没中招

  • 配置里的 threads 如果来自模板或环境变量替换,空值、0 都要防:grep -n 'threads' conf/*.yaml,再看每个服务有没有覆盖 threads()。
  • 不要只靠 -t:它只证明 Server::new 能读通配置、bootstrap 之前的代码能跑。启动后真的发一个请求,才知道运行时建起来了。
  • 日志里 Worker threads cannot be set to 0(Steal)和 assertion failed: threads != 0(NoSteal)是同一个原因。

小结

  • ServerConf::validate 不查 threads;threads: 0 能解析、能过 -t,到 run 给服务建运行时时主线程 panic,进程以 101 退出。
  • Steal 报 tokio 的 Worker threads cannot be set to 0(pingora-runtime lib.rs:471),NoSteal 报 assertion failed: threads != 0(lib.rs:611)。
  • validate 到 0.9.0 才有第一条检查(b994854),查的是新字段 max_blocking_threads。在 bootstrap 之前自己检查 threads。

💬 关联讨论

发起讨论
加载中...