From cf9d473abe16c52d8b27def0b89a18b9a6f1ce3f Mon Sep 17 00:00:00 2001 From: "google-labs-jules[bot]" <161369871+google-labs-jules[bot]@users.noreply.github.com> Date: Thu, 26 Feb 2026 17:48:27 +0000 Subject: [PATCH] perf: offload blocking import_vault operations to thread Refactors `import_vault` endpoint in `src/seedpass/api.py` to use `asyncio.to_thread` for blocking file I/O and synchronous `PasswordManager.handle_import_database` calls. Replaces synchronous `pm.sync_vault()` with `await pm.sync_vault_async()` (with fallback) to prevent blocking the event loop or triggering `asyncio.run` errors. This change ensures the API remains responsive during large vault imports. Verified with a custom benchmark script showing concurrent requests are no longer blocked. Co-authored-by: PR0M3TH3AN <53631862+PR0M3TH3AN@users.noreply.github.com> --- src/seedpass/api.py | 29 +++++++++++++++++++---------- 1 file changed, 19 insertions(+), 10 deletions(-) diff --git a/src/seedpass/api.py b/src/seedpass/api.py index 43e613fd..f76fc333 100644 --- a/src/seedpass/api.py +++ b/src/seedpass/api.py @@ -732,14 +732,18 @@ async def import_vault( status_code=413, detail=f"Uploaded file exceeds max size of {_MAX_IMPORT_BYTES} bytes", ) - with tempfile.NamedTemporaryFile(delete=False) as tmp: - tmp.write(data) - tmp_path = Path(tmp.name) - os.chmod(tmp_path, 0o600) - try: - pm.handle_import_database(tmp_path) - finally: - os.unlink(tmp_path) + + def _handle_upload(file_data: bytes) -> None: + with tempfile.NamedTemporaryFile(delete=False) as tmp: + tmp.write(file_data) + tmp_path = Path(tmp.name) + os.chmod(tmp_path, 0o600) + try: + pm.handle_import_database(tmp_path) + finally: + os.unlink(tmp_path) + + await asyncio.to_thread(_handle_upload, data) else: body = await request.json() path_str = body.get("path") @@ -754,8 +758,13 @@ async def import_vault( detail="Selected file must be a '.json.enc' backup", ) - pm.handle_import_database(path) - pm.sync_vault() + await asyncio.to_thread(pm.handle_import_database, path) + + if hasattr(pm, "sync_vault_async"): + await pm.sync_vault_async() + else: + # Fallback for older PM implementations or mocks + await asyncio.to_thread(pm.sync_vault) return {"status": "ok"}