Dates: the backfill drain — convert mixed-convention rows, progress surface, day-boundary invalidation #118
Notifications
Due Date
No due date set.
Blocks
Depends on
Reference: Apps/ImageApi#118
Reference in new issue
Block a user
Parent
ImageApi #113 — SPEC: true-instant date contract (ADR-0001).
What to build
The backfill drain that converts the existing mixed-convention rows to true instants, following the exact shape of the repo's existing per-tick drains: runs on every watcher tick, capped per tick, idempotent, library-online-gated, errors don't burn the cap, and it batches external-tool calls (one subprocess per batch, as the existing date drain does). It selects rows where the new instant column is still null, runs the re-based waterfall (all four sources: EXIF, container, filename class, filesystem time), and writes the new columns — it never touches the legacy columns.
It also owns the two operational surfaces: a progress count (rows remaining to convert) exposed where the operator already looks at status, and day-boundary invalidation — when a converted instant lands on a different display-zone calendar day than the row's legacy value implied, that day's derived day data (daily summaries, pre-generated reels) is marked stale for the existing nightly regeneration, and rows whose day is unchanged invalidate nothing.
Videos are in scope: they drain through the same waterfall (container branch) in the same pass.
Acceptance criteria
Blocked by
Resolved by PR #124 (
feature/date-backfill-drain).What landed
backfill::backfill_utc_datesruns at the existing date-drain seam, right afterbackfill_missing_date_taken, inside the per-library online gate.date_utc IS NULL AND date_taken IS NOT NULL, backed by the new partial indexidx_image_exif_utc_backfill.resolve_dates_batch(paths, {}): one exiftool subprocess per batch, the same call shape as the #117 audit.DATE_BACKFILL_MAX_PER_TICK, shared with the legacy drain.imageserver_date_backfill_remaining, refreshed every tick.date_backfill_remainingon eachGET /librariesentry, a live count ofdate_utc IS NULL.user_ai_prefs.display_tz) between the legacy value, rendered as an instant the way the legacy/memoriesSQL did, anddate_utc. When the day differs, the drain deletes and unlinks theday-spanprecomputed_reelsrows for this library andallthat cover either month-day. The nightly pre-gen regenerates them; no new scheduler.Decisions made during implementation
daily_summary_job.rsgroupsSmsMessageby date) and don't depend on photo dates.manualrows convert throughcamera_tz::manual_date_resolution, the same path a manual edit uses today. They are never re-resolved from the file.fs_time. This covers exiftool being missing or failing on that file.Heads-up for #119 / #120
/memories, still use legacydate_taken. Until #119 lands, a regenerated day reel selects the same media, and a deleted day reel for today stays absent until the next nightly run.tz_offset_minutes. The drain uses the IANA display zone. These agree except around DST or when the prefs disagree. #119/#120 should move pre-gen ontodisplay_tz.reels::fixed_tz_offsetstores seconds rather than minutes (local_minus_utc()). The drain tolerates this, but it's a latent bug.