Rust in Everything
基于 sqlx 0.9.0

连接池

Pool<DB> 和 Postgres 无关,代码都在 sqlx-core/src/pool/ 里:inner.rs 623 行(PoolInner:借、还、建连接、后台任务)、connection.rs 421 行(PoolConnection 和它的 Drop)、options.rs 596 行(PoolOptions,一半是文档)、mod.rs 668 行(Pool 的公开方法,大半是文档)。许可的计数交给 sqlx-core/src/sync.rs 的 AsyncSemaphore,在 tokio 下它就是 tokio::sync::Semaphore。驱动只提供三样东西:connect、ping、close,Postgres 的 ping 是一个 Sync 来回。

三页按一条连接的一生排:先看池的配置,再看借出,最后看归还和后台回收。实验都在 M2 Max、macOS、rustc 1.99.0、Postgres 16.14 上跑,池里有几条连接用 pool.size()、pool.num_idle() 和服务端的 pg_stat_activity 一起看,线上发了什么用一次查询的全过程里那个打印协议消息的代理看。

  1. PoolOptions 与默认值:默认值都写在 PoolOptions::new 里(10 条、30 秒、10 分钟、30 分钟、借出时 ping);三个回调 after_connect / before_acquire / after_release 各在什么时候跑、拿到什么元数据;connect 建池时至少连一条,connect_lazy 一条都不连;fair 在 tokio 下不能关。
  2. 获取连接:acquire 的循环:拿许可(先到先得),弹空闲连接并 ping,没有就加计数、新建;连接被拒或服务端还在启动时从 10 毫秒开始指数退避重试;一切都在 acquire_timeout 之内,超时一律报 PoolTimedOut;超过 2 秒打一条 WARN。
  3. 归还、Drop 与维护任务:Drop 只 spawn 一个任务,归还时再 ping 一次,先推回空闲队列再放许可;close_on_drop、detach、leak 对计数和许可的影响;后台任务按两个超时中较小的那个周期回收连接,只在关掉连接之后补 min_connections;Pool::close 要等所有借出的连接回来。
flowchart TB
  opts["PoolOptions 配置"] --> acq["acquire 借出"]
  acq --> use["用户使用连接"]
  use --> drp["Drop 后 spawn 归还"]
  drp --> idle["空闲队列"]
  idle --> acq
  idle --> reap["维护任务回收"]
  reap --> fill["补足最小连接数"]

事务怎么借用池里的连接、Transaction 被 drop 时排队的 ROLLBACK 什么时候发出去,见事务;信号量本身怎么做到先到先得,见 tokio 卷的 batch_semaphore。

💬 关联讨论

发起讨论
加载中...