Fix #89: bump DB pool and add auth-path in-process caches#90
Conversation
- shard_core/database/connection.py: max_size=20, timeout=10 — prevents pool exhaustion when Traefik forwardAuth receives request bursts - shard_core/web/internal/auth.py: cache _get_identity and _find_app results in-process; invalidate on on_identity_update / on_apps_update signals - shard_core/util/signals.py: add on_identity_update signal - shard_core/service/identity.py + web/protected/identities.py: fire on_identity_update after any identity mutation - shard_core/service/app_tools.py: docker_start_app accepts optional status to skip the per-call DB read when status is already known - shard_core/service/app_lifecycle.py: pass cached app.status to docker_start_app Steady-state /internal/auth now makes zero DB queries for the common path. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Two test failures from the cache layer: - test_auth.py::test_headers failed because the module-level _identity_cache survives across tests. init_default_identity creates a new identity per test (fresh DB) but never fired on_identity_update, so the cache held the previous test's identity. Fire the signal after creating the initial default identity. - test_app_last_access.py::test_app_last_access_is_debounced failed because the cached InstalledApp's last_access field is never refreshed. The debounce check in update_last_access reads app.last_access from the cached object, which stays at the value captured when the cache was populated, so the debounce window never short-circuits. Mutate app.last_access = now after the DB update so the cached object reflects reality. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
…status param - Move on_identity_update firing to service layer exclusively: add update_identity() to service/identity.py; web/protected/identities.py now calls the service function instead of firing the signal directly. The DB layer stays dependency-free; the signal has a single fire-site. - Revert docker_start_app status parameter: the function always reads fresh status from DB (throttled to once per 5 s), eliminating any stale-cache concern. The throttle is still required to prevent excessive docker-compose subprocess calls from burst auth traffic. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
|
Pushed fixes for both review comments. Signal layer ( You're right that having the signal fire from two layers (service and web) was inconsistent. I've added Stale cache / throttle ( Reverted the The throttle is still needed and I've kept it: it prevents |
Summary
Closes #89
Parallel request bursts (e.g. Actual Budget loading ~30 SQLite migration files simultaneously) exhaust the 4-connection default pool in 2–3 in-flight forwardAuth calls. Remaining calls block for 30 s then 500. This PR applies all four fixes described in the issue:
AsyncConnectionPool(max_size=20, timeout=10)— headroom for realistic bursts;timeout=10fails fast instead of stacking 30 s waits._get_identity()inauth.pycaches the defaultSafeIdentityin-process. Invalidated via a newon_identity_updatesignal fired from every identity-mutation path (identity.make_default,identity.enrich_identity_from_profile,PUT /protected/identities)._find_app()caches theInstalledAppby name. Invalidated on the existingon_apps_updatesignal (already fires on install, uninstall, start, stop, shutdown).ensure_app_is_runningDB read removed:docker_start_appnow accepts an optionalstatusparam.ensure_app_is_runningpasses the cached app's status, skipping the extraget_by_namecall.Steady-state result:
/internal/authmakes zero DB queries for the common path.Recommended reading order
shard_core/database/connection.py— pool parametersshard_core/util/signals.py— newon_identity_updatesignalshard_core/service/identity.py+shard_core/web/protected/identities.py— signal fire sitesshard_core/web/internal/auth.py— caches + invalidation handlersshard_core/service/app_tools.py— optionalstatusparam ondocker_start_appshard_core/service/app_lifecycle.py— pass cached statusTest plan
PUT /protected/identities, verify headers still reflect new identity on next auth requestGET /internal/authfor unknown app still returns 404🤖 Generated with Claude Code