[Bug]: Cached-thumbnail hydrate on directory open is O(n^2) via unindexed OFFSET pagination #190
Labels
No labels
Kind/Bug
Kind/Feature
Priority/High
Priority/Medium
Reviewed/Confirmed
Compat/Breaking
Kind/Bug
Kind/Documentation
Kind/Enhancement
Kind/Feature
Kind/Security
Kind/Testing
Priority
Critical
Priority
High
Priority
Low
Priority
Medium
Reviewed
Confirmed
Reviewed
Duplicate
Reviewed
Invalid
Reviewed
Won't Fix
Status
Abandoned
Status
Blocked
Status
Need More Info
No milestone
No project
No assignees
1 participant
Notifications
Due date
No due date set.
Dependencies
No dependencies set
Reference
ai-collab/bulk-image-organizer#190
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?
Affected area
Directory-open cache hydrate (
CachedThumbnailLoadWorker/WorkingDB.iter_thumbnail_blob_dicts()), separate from the live metadata/thumbnail scan (scan_metadata_ordered_and_upsert, issue #184).Report (from live testing on #184's follow-up branch)
On a ~125,000-image network workspace (Windows 11, remote share, thumbnail thread count 16) that had been scanned/tested repeatedly before (so a large fraction of images already have cached
thumbnail_blobs from prior runs — ~75,000 of 125,000 in the reproduction), reopening the directory shows the first ~276 alphabetically-sorted placeholder rows on screen with no thumbnails populated, for far longer than the live scan's ~30 img/s rate would predict (the live scan is correctly only processing the ~50,000 genuinely-pending images, confirmed via its own progress counter). User's own diagnosis: the process that displays already-cached thumbnails appears to be "buried" and should run before/independently of new generation — which is exactly right.Root cause
WorkingDB.iter_thumbnail_blob_dicts()(db/working_db.py) pages through cached rows with:imageshas no index coveringfilename(onlyrelative_path,exact_hash,perceptual_hash). Every chunk call therefore does a full-table scan + temp-b-tree sort of the wholeimagestable, then discardsOFFSETrows before returningchunk_size(default 200). Cost per chunk grows withOFFSET, making a full drain O(n^2) in image count — the same anti-pattern class as issue #184, but in the cache-hydrate path (CachedThumbnailLoadWorker, started fromopen_directory()off the main thread) rather than the live enrichment queue.Measured locally (125k rows, 75k pre-cached, chunk_size=200, same shape as the report):
This is a different bug from #184: #184's O(n^2) was in the pending-enrichment dispatch loop and is already fixed; this one is in the separate already-cached-thumbnail hydrate path that #184 did not touch.
Suggested fix direction
(filename, relative_path)onimages(bumpWORKING_DB_VERSION, additive migration).iter_thumbnail_blob_dicts()'s OFFSET pagination with keyset/seek pagination (WHERE (filename, relative_path) > (?, ?)), so each chunk is an index seek instead of a rescan from the start.Acceptance criteria
iter_thumbnail_blob_dicts()chunk cost stays roughly flat (no growth with offset) as the cached set grows.test_iter_thumbnail_blob_dicts_chunks) still passes.