From 441f6f140448e851eee99d3f7f555c2b94a0d1b7 Mon Sep 17 00:00:00 2001 From: Duncan Tourolle Date: Sat, 26 Sep 2026 12:10:31 -0400 Subject: [PATCH] Record why thumbnails are not queued MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit catalog.md §6 still described the design of 2026-08-09, where the grid enqueued Thumbnail jobs at Interactive and a runner drained them. It was never built that way: the grid asks a worker directly and the sweep's work list is what the thumbnail store lacks. The one enqueue that did exist fed a queue nobody claimed (#73). §6.1 now states the decision and its evidence: the store is shared between devices and is the only record that knows a thumbnail exists, metadata is owed through metadata_state the same way, the retired rows are dropped at open rather than by a migration so no older device loses the synced catalog, and every_queued_kind_has_a_consumer holds the rule. §6.3 notes that the priority ordering is had without the queue. outstanding.md's FR-PLAT-AND-4 paragraph said the scan's thumbnail jobs were the one reachable enqueue; it now says nothing enqueues, and that feeding the runner means a handler and its enqueue in the same change. Refs #73 --- docs/dev/catalog.md | 26 +++++++++++++++++++++++++- docs/dev/outstanding.md | 12 +++++++----- 2 files changed, 32 insertions(+), 6 deletions(-) diff --git a/docs/dev/catalog.md b/docs/dev/catalog.md index 8570b3d..b2fbaa5 100644 --- a/docs/dev/catalog.md +++ b/docs/dev/catalog.md @@ -420,6 +420,29 @@ pub enum JobKind { } ``` +**Thumbnails are not queued (decided 2026-09-26, #73).** `Thumbnail` is kept only so its number +stays taken, and is listed in `JobKind::RETIRED`. Up to 0.16.0 every remote scan enqueued one per +photograph, and nothing claimed the kind — no `JobHandler` was ever registered for it, on desktop or +Android (the same `dr-ui`), and the catalog snapshot carries `jobs` but the merge never reads them +(§8.2). The reference catalog held 23,582 such rows, about 1 MB with its indexes. Thumbnails are +made another way, and by the right source of truth: + +- the grid asks a worker for the cells it is drawing, which serves them from the thumbnail store or + range-fetches the embedded preview (§7.2); +- the thumbnail sweep's work list is *what the store does not hold* (`thumbnails_outstanding`). + +The store is shared between devices (§7.3), so it is the only thing that knows another device already +made a thumbnail; a per-device queue row cannot. A queue row would be a second, staler record of the +same debt. Metadata is owed the same way — `metadata_state < 2` is the sweep's work list — so the local +walk no longer enqueues `ExtractMetadata` either. + +The rows already queued are dropped by `jobs::drop_retired`, from `runner::recover` at every catalog +open, rather than by a migration: a schema bump would make a device still on an older build refuse the +synced snapshot, and "every open" rather than "once" because an older build sharing the catalog +queues them again on its next scan. With the rows gone it is one probe of the `(kind, subject_id)` +index. `every_queued_kind_has_a_consumer` (dr-catalog) holds the rule: it reads the shipping sources +and fails if any kind is enqueued that no handler or claim names. + ### 6.2 Coalescing is the point `UNIQUE(kind, subject_id)` on `jobs` means enqueueing is idempotent: an image touched five times @@ -443,7 +466,8 @@ priority governs the whole app: Visible-cell work is enqueued by the grid as it scrolls, at `Interactive`. The effect is that a freshly scanned library fills in *where the user is looking* first, and grinds through the rest -behind them. +behind them. (As built, thumbnails and metadata get this ordering without the queue — the grid +requests its visible cells directly and the sweeps take what is left; see §6.1.) ### 6.4 Durability and failure diff --git a/docs/dev/outstanding.md b/docs/dev/outstanding.md index a42cb52..058b063 100644 --- a/docs/dev/outstanding.md +++ b/docs/dev/outstanding.md @@ -258,11 +258,13 @@ the platform half — a foreground `Service`, `FOREGROUND_SERVICE` and `POST_NOT manifest, and a stated Doze behaviour. The build step that blocked it is no longer a blocker: the APK now compiles its own Java. -Note also that **no handler is registered**, deliberately. The only enqueue site reachable in the -shipping app produces remote thumbnail jobs already served by the async grid worker, and -`walk::scan_root` — which holds the other two enqueue sites — has no caller outside an example. -Wiring the sweep to claim from the queue is the honest next step and is an async rewrite of -`library.rs`. +Note also that **no handler is registered**, deliberately — and, since #73, nothing enqueues +either. The remote scan's per-photograph `Thumbnail` jobs, and `walk::scan_root`'s `Thumbnail` and +`ExtractMetadata` jobs, duplicated debts the thumbnail store and `metadata_state` already record, +and were never claimed; they were removed and `Thumbnail` retired ([catalog.md §6.1](catalog.md)). +Feeding FR-PLAT-AND-4's platform half is therefore a matter of choosing a kind whose work has no +better source of truth, registering its handler, and enqueuing it in the same change — +`every_queued_kind_has_a_consumer` refuses the enqueue without the handler. **FR-PLAT-AND-5 — built.** A tiered eviction registry drives GPU caches, then proxies, then thumbnails, from `MainEvent::LowMemory` and `MainEvent::Stop`.