Scheduler: the orphan sweep walks the whole media tree every five minutes for files that must be a day old to qualify #1119
Labels
No labels
accessibility
authentication
breaking change
bug
documentation
enhancement
interface
internationalisation
observability
performance
security
tier
1
tier
2
tier
3
tier/4
No project
No assignees
1 participant
Notifications
Due date
No due date set.
Dependencies
No dependencies set
Reference
Postulo/postulo#1119
Loading…
Reference in a new issue
No description provided.
Delete branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
What exists today
src/postulo/notifications/management/commands/send_due_reminders.py:231callsscrub.sweep()on every pass (every 300 s).src/postulo/documents/scrub.py:112-148lists the strays throughmedia.unreferenced(core/media.py:41-55), which first loads every file name every row names (referenced_names, lines 22-38: onevalues_listquery perFileFieldon every model) and then walks the media root withfilestore.listing(documents/filestore.py:95-106):listdirper directory and, per file,get_modified_timeandsize, twostatcalls. When more thanSWEEP_SMALL(10) old strays are found,sweepwalks the tree a second time just to count the files (scrub.py:129).A file qualifies only when it is older than the grace period,
POSTULO_MEDIA_SWEEP_GRACE_HOURS, default 24 (config/settings/base.py:733). Running the sweep every five minutes therefore finds, at best, a file that became eligible in the last five minutes of a 24-hour wait; once an hour would delete every orphan at most an hour later than today.Measured:
filestore.listing()over a scratch media root of 2,000 one-byte files in 20 directories, under the test settings withMEDIA_ROOToverridden, took 299 ms on this machine (NTFS; a Linux ext4 or overlay volume is likely several times faster). Cost scales linearly with the number of kept files.Correction to the scout: the second walk happens only when more than 10 aged strays exist, which on a healthy instance is rare, so the steady cost is one walk per pass, not two.
What would change
grace / 24): remember the last sweep in the cache (cache.add("postulo:media-sweep", 1, 3600)returns False while the hour is not over) or in the heartbeat-style file the scheduler already keeps. The pass skips it otherwise.sweep, count the kept files during the same walk instead of walking again: makeunreferencedreturn both the strays and the total, or collectlisting()once into a list and derive both.No library needed.
What it saves or protects
small: one full media walk (about 300 ms per 2,000 files measured on this Windows machine) plus one query per file field, 288 times a day, cut to once an hour or less
Accessibility
none
Decisions
Found by the efficiency and reliability audit of 2026-10-30 (finding B3; index postulo/postulo#1108). Checked against
mainatdaee30b15(2026-10-30).