DB background worker cleanup, avoid issue with double cleanup. Faster write to disk.

This commit is contained in:
2026-01-27 22:21:33 +00:00
parent ddd70e6130
commit 9b505ff553
4 changed files with 10 additions and 39 deletions
+6 -32
View File
@@ -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:
+1 -1
View File
@@ -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]
+3 -1
View File
@@ -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)
-5
View File
@@ -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")