perf: run bcrypt on a thread so it doesn't block the event loop
verify_password / get_password_hash are CPU-bound and take ~250ms each at rounds=12. Called directly from async endpoints, they stall every other coroutine for that window — the single biggest single-worker bottleneck on the login path. Adds averify_password / ahash_password that wrap the sync versions in asyncio.to_thread. Sync versions stay put because _ensure_admin_user and tests still use them. 5 call sites updated: login, change-password, create-user, reset-password. tests/test_auth_async.py asserts parallel averify runs concurrently (~1x of a single verify, not 2x).
This commit is contained in:
@@ -3,7 +3,7 @@ import uuid as _uuid
|
||||
from fastapi import APIRouter, Depends, HTTPException
|
||||
|
||||
from decnet.telemetry import traced as _traced
|
||||
from decnet.web.auth import get_password_hash
|
||||
from decnet.web.auth import ahash_password
|
||||
from decnet.web.dependencies import require_admin, repo
|
||||
from decnet.web.db.models import (
|
||||
CreateUserRequest,
|
||||
@@ -39,7 +39,7 @@ async def api_create_user(
|
||||
await repo.create_user({
|
||||
"uuid": user_uuid,
|
||||
"username": req.username,
|
||||
"password_hash": get_password_hash(req.password),
|
||||
"password_hash": await ahash_password(req.password),
|
||||
"role": req.role,
|
||||
"must_change_password": True, # nosec B105 — not a password
|
||||
})
|
||||
@@ -125,7 +125,7 @@ async def api_reset_user_password(
|
||||
|
||||
await repo.update_user_password(
|
||||
user_uuid,
|
||||
get_password_hash(req.new_password),
|
||||
await ahash_password(req.new_password),
|
||||
must_change_password=True,
|
||||
)
|
||||
return {"message": "Password reset successfully"}
|
||||
|
||||
Reference in New Issue
Block a user