perf(auth): offload Argon2 hash/verify to spawn_blocking

Argon2 是 memory-hard 计算,登录/注册时同步执行会阻塞 Tokio worker
数百毫秒。改为在阻塞线程池执行,JoinError 兜底任务 panic。
This commit is contained in:
xfy 2026-06-18 11:16:13 +08:00
parent 79cb809010
commit 62600a6687

View File

@ -113,8 +113,14 @@ pub async fn register(
let client = get_conn().await.map_err(AppError::db_conn)?; let client = get_conn().await.map_err(AppError::db_conn)?;
let password_hash = // Argon2 是 memory-hard 计算,必须在 spawn_blocking 中执行,避免阻塞 Tokio worker。
password::hash_password(&password).map_err(|_| AppError::Internal("密码处理失败"))?; let pw_for_hash = password.clone();
let password_hash = tokio::task::spawn_blocking(move || {
password::hash_password(&pw_for_hash)
})
.await
.map_err(|_| AppError::Internal("密码处理任务失败"))?
.map_err(|_| AppError::Internal("密码处理失败"))?;
// 使用 INSERT ON CONFLICT 原子性地完成“首个用户成为 admin”的竞争。 // 使用 INSERT ON CONFLICT 原子性地完成“首个用户成为 admin”的竞争。
// 若已有 admin 或用户名/邮箱冲突RETURNING 将返回空。 // 若已有 admin 或用户名/邮箱冲突RETURNING 将返回空。
@ -203,7 +209,14 @@ pub async fn login(username: String, password: String) -> Result<AuthResponse, S
}; };
let password_hash: String = row.get("password_hash"); let password_hash: String = row.get("password_hash");
let valid = password::verify_password(&password, &password_hash) // Argon2 校验同样在 spawn_blocking 中执行。
let pw_for_verify = password.clone();
let hash_for_verify = password_hash.clone();
let valid = tokio::task::spawn_blocking(move || {
password::verify_password(&pw_for_verify, &hash_for_verify)
})
.await
.map_err(|_| AppError::Internal("密码处理任务失败"))?
.map_err(|_| AppError::Internal("密码处理失败"))?; .map_err(|_| AppError::Internal("密码处理失败"))?;
if !valid { if !valid {