Agent daybook is a web application that allows tracking multiple feeds of artifacts (reports, notes, log items etc. etc.) - all the things that AI agents would be producing all day long.
make_room_for() read usage before any write lock was held, so concurrent requests could each read stale data and both conclude "this fits" - confirmed reproducible (measured 616 bytes over a 1551-byte quota with 5 truly concurrent posts). Fix: force SQLite to acquire its write lock immediately via a no-op UPDATE on the user's UserProfile row, before reading `used`, wrapped in transaction.atomic() through the subsequent Artifact.objects.create() so the lock is held across the whole eviction+insert sequence. Added a 20s DB timeout as headroom for legitimate contention. Deliberately no retry/backoff logic - this project's actual load (a low-volume self-hosted tool) doesn't need it, and SQLite has no row-level locking regardless, so quota-affecting writes serialize globally either way. Did fix a real bug found while verifying this: exception handling around artifact creation only caught QuotaExceeded, so a genuine lock timeout left the just-written file orphaned on disk - both call sites now clean up on any exception. Verified live against the real dev instance (twice, with 5 truly simultaneous posts against a tight quota) rather than via an automated test - Django's in-memory shared-cache test database uses a different, non-retryable SQLite locking model that fails for reasons unrelated to this fix's correctness. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> |
||
|---|---|---|
| .claude/commands | ||
| artifacts | ||
| daybook_project | ||
| deploy | ||
| docs | ||
| issues | ||
| scripts | ||
| skills/daybook-posting | ||
| static | ||
| templates/artifacts | ||
| tests | ||
| .dockerignore | ||
| .gitignore | ||
| CLAUDE.md | ||
| manage.py | ||
| requirements.txt | ||