yggdrasil/src/db/pool.rs
xfy d826afbe15
Some checks failed
CI / check (push) Failing after 6m5s
CI / build (push) Has been skipped
fix(db): 为 deadpool-postgres 连接池指定 Tokio1 runtime
2026-06-17 11:07:23 +08:00

73 lines
2.6 KiB
Rust

//! PostgreSQL 连接池实现。
//!
//! 仅在启用 `server` feature 时编译,使用 deadpool-postgres 管理连接池,
//! 并通过 `std::sync::LazyLock` 在首次访问时延迟初始化全局连接池。
//! `get_conn` 失败时按固定 2 秒间隔进行简单重试,以应对瞬时连接失败。
use std::sync::LazyLock;
use std::time::Duration;
use deadpool_postgres::{Manager, ManagerConfig, Pool, RecyclingMethod, Runtime};
use tokio_postgres::NoTls;
/// 全局数据库连接池,基于 `DATABASE_URL` 环境变量延迟初始化。
///
/// 最大连接数可通过 `DB_POOL_SIZE` 环境变量调整,默认 20。
pub static DB_POOL: LazyLock<Pool> = LazyLock::new(|| {
let db_url = std::env::var("DATABASE_URL").expect("DATABASE_URL environment variable not set");
let pg_cfg = db_url
.parse::<tokio_postgres::Config>()
.expect("Invalid DATABASE_URL format");
// 使用 Verified 回收策略,确保归还的连接仍然可用,避免 DB 重启后拿到死连接。
let mgr_cfg = ManagerConfig {
recycling_method: RecyclingMethod::Verified,
};
let mgr = Manager::from_config(pg_cfg, NoTls, mgr_cfg);
Pool::builder(mgr)
.max_size(
std::env::var("DB_POOL_SIZE")
.ok()
.and_then(|s| s.parse().ok())
.unwrap_or(20),
)
.wait_timeout(Some(Duration::from_secs(10)))
.create_timeout(Some(Duration::from_secs(10)))
.recycle_timeout(Some(Duration::from_secs(5)))
.runtime(Runtime::Tokio1)
.build()
.expect("Failed to create database connection pool")
});
/// 最大重试次数。
const MAX_RETRIES: u32 = 3;
/// 每次重试之间的固定等待时间。
const RETRY_DELAY: Duration = Duration::from_secs(2);
/// 从全局连接池获取一个数据库连接,失败时按 `MAX_RETRIES` 进行重试。
///
/// 若所有尝试均失败,则返回最后一次遇到的 PoolError。
pub async fn get_conn() -> Result<deadpool_postgres::Object, deadpool_postgres::PoolError> {
let mut last_err = None;
for attempt in 0..=MAX_RETRIES {
match DB_POOL.get().await {
Ok(conn) => return Ok(conn),
Err(e) => {
if attempt < MAX_RETRIES {
tracing::warn!(
"DB connection attempt {} failed, retrying in {:?}: {:?}",
attempt + 1,
RETRY_DELAY,
e
);
tokio::time::sleep(RETRY_DELAY).await;
}
last_err = Some(e);
}
}
}
Err(last_err.unwrap())
}