grpc: run authenticate's PBKDF2 verify off the engine lock
All checks were successful
CI / check (push) Successful in 3m54s

The Authenticate RPC held the engine Mutex across scram verify_plain, which at
production iteration counts (1.2M) is ~0.8s of PBKDF2 on the async reactor —
freezing the whole daemon (IRC link, gossip, every RPC) for the duration and
serializing all logins. Fetch the verifier under the lock (cheap), then run
verify_plain on spawn_blocking, lock released. 6 concurrent logins: 823ms (was
~4460ms serialized). set_password/register already offload this way; drop the
now-unused authority_authenticate.
This commit is contained in:
Jean Chevronnet 2026-07-16 18:40:30 +00:00
parent 2e92f75a90
commit 63fc2fb2c0
No known key found for this signature in database
2 changed files with 23 additions and 5 deletions

View file

@ -291,9 +291,22 @@ impl Accounts for AccountsService {
async fn authenticate(&self, req: Request<AuthenticateRequest>) -> Result<Response<AuthenticateReply>, Status> {
authorize(&req, &self.token)?;
let msg = req.into_inner();
match self.engine.lock().await.authority_authenticate(&msg.name, &msg.password) {
Some(account) => Ok(Response::new(AuthenticateReply { status: PbStatus::Ok as i32, account })),
None => Ok(Response::new(AuthenticateReply { status: PbStatus::Invalid as i32, account: String::new() })),
// Fetch the verifier under the lock (cheap), then run the ~1s PBKDF2 on a
// blocking thread — never hold the engine lock across it, or every login
// freezes the whole daemon (IRC link, gossip, all RPCs).
let Some((account, verifier)) = self.engine.lock().await.scram_verifier(&msg.name) else {
return Ok(Response::new(AuthenticateReply { status: PbStatus::Invalid as i32, account: String::new() }));
};
let password = msg.password;
let ok = tokio::task::spawn_blocking(move || {
crate::engine::scram::verify_plain(crate::engine::scram::Hash::Sha256, &verifier, &password)
})
.await
.unwrap_or(false);
if ok {
Ok(Response::new(AuthenticateReply { status: PbStatus::Ok as i32, account }))
} else {
Ok(Response::new(AuthenticateReply { status: PbStatus::Invalid as i32, account: String::new() }))
}
}