diff --git a/paskia/db/background.py b/paskia/db/background.py index 8d7f950..9911358 100644 --- a/paskia/db/background.py +++ b/paskia/db/background.py @@ -8,42 +8,16 @@ import asyncio import logging from datetime import datetime, timezone -from paskia.db.operations import _db, _store +from paskia.db.operations import _store, cleanup_expired -# Flush changes to disk every N seconds -FLUSH_INTERVAL = 1 -# Cleanup expired items every N seconds (cheap when nothing to remove) -CLEANUP_INTERVAL = 1 +FLUSH_INTERVAL = 0.1 # Flush to disk +CLEANUP_INTERVAL = 1 # Expired item cleanup _logger = logging.getLogger(__name__) _background_task: asyncio.Task | None = None -def cleanup() -> None: - """Remove expired sessions and reset tokens from the database.""" - - if _db is None: - return - - with _db.transaction("expiry"): - current_time = datetime.now(timezone.utc) - - # Clean expired sessions - to_delete_sessions = [ - k for k, s in _db.sessions.items() if s.expiry < current_time - ] - for k in to_delete_sessions: - del _db.sessions[k] - - # Clean expired reset tokens - to_delete_tokens = [ - k for k, t in _db.reset_tokens.items() if t.expiry < current_time - ] - for k in to_delete_tokens: - del _db.reset_tokens[k] - - async def flush() -> None: """Write all pending database changes to disk.""" @@ -56,7 +30,7 @@ async def flush() -> None: async def _background_loop(): """Background task that periodically flushes changes and cleans up.""" # Run cleanup immediately on startup to clear old expired items - cleanup() + cleanup_expired() await flush() last_cleanup = datetime.now(timezone.utc) @@ -67,10 +41,10 @@ async def _background_loop(): # Flush pending changes to disk await flush() - # Run cleanup less frequently + # Run cleanup periodically now = datetime.now(timezone.utc) if (now - last_cleanup).total_seconds() >= CLEANUP_INTERVAL: - cleanup() + cleanup_expired() await flush() # Flush cleanup changes last_cleanup = now except asyncio.CancelledError: diff --git a/paskia/db/operations.py b/paskia/db/operations.py index 4bafc0e..fd47940 100644 --- a/paskia/db/operations.py +++ b/paskia/db/operations.py @@ -675,7 +675,7 @@ def cleanup_expired() -> int: """Remove expired sessions and reset tokens. Returns count removed.""" now = datetime.now(timezone.utc) count = 0 - with _db.transaction("admin:cleanup_expired"): + with _db.transaction("expiry"): expired_sessions = [k for k, s in _db.sessions.items() if s.expiry < now] for k in expired_sessions: del _db.sessions[k] diff --git a/paskia/fastapi/mainapp.py b/paskia/fastapi/mainapp.py index 22337d8..727482e 100644 --- a/paskia/fastapi/mainapp.py +++ b/paskia/fastapi/mainapp.py @@ -9,6 +9,7 @@ from fastapi.responses import FileResponse, RedirectResponse from fastapi_vue import Frontend from paskia import globals +from paskia.db import start_background, stop_background from paskia.fastapi import admin, api, auth_host, ws from paskia.fastapi.session import AUTH_COOKIE from paskia.util import hostutil, passphrase, vitedev @@ -32,7 +33,6 @@ async def lifespan(app: FastAPI): # pragma: no cover - startup path so that uvicorn reload / multiprocess workers inherit the settings. All keys are guaranteed to exist; values are already normalized by __main__.py. """ - config = json.loads(os.environ["PASKIA_CONFIG"]) try: @@ -54,7 +54,9 @@ async def lifespan(app: FastAPI): # pragma: no cover - startup path logging.getLogger("uvicorn.access").setLevel(logging.INFO) await frontend.load() + await start_background() yield + await stop_background() app = FastAPI(lifespan=lifespan, redirect_slashes=False) diff --git a/paskia/globals.py b/paskia/globals.py index 5c0d7d5..d2c4061 100644 --- a/paskia/globals.py +++ b/paskia/globals.py @@ -2,7 +2,6 @@ from typing import Generic, TypeVar from paskia import db, remoteauth from paskia.bootstrap import bootstrap_if_needed -from paskia.db import start_background from paskia.sansio import Passkey T = TypeVar("T") @@ -64,10 +63,6 @@ async def init( await bootstrap_if_needed() - # Start background flush/cleanup task after bootstrap - - await start_background() - # Global instances passkey = Manager[Passkey]("Passkey")