Refresh metadata when a title is added, not a day later #176

Closed
opened 2026-08-24 17:58:27 +01:00 by naps62-yolo · 1 comment
Owner

Adding a series shows "no seasons revealed yet — a metadata refresh fills this
in", and it stays that way for up to 24 hours.

The metadata lane runs on a daily interval:

pub const METADATA_INTERVAL: Duration = Duration::from_hours(24);
const METADATA_START_DELAY: Duration = Duration::from_secs(5);

A newly added series is written with metadata_refreshed_at = NULL, and
metadata_refresh_due already returns true for NULL — the query in
series_refresh.rs even sorts NULLs first. Nothing is wrong with the
eligibility logic. There is simply no way to ask the lane to run now, so a new
title waits for a tick that may be most of a day away.

Movies have the same defect. It is less obvious because their title and year
come from the add payload, so the page looks populated while the digital
release date and other TMDB fields are still missing.

Reuse the existing pattern rather than inventing one. AppState already
carries MovieCommand, EpisodeCommand and SeasonCommand channels that the
API sends on and the daemon drains, precisely so a manual action does not wait
for the reconcile tick. Metadata refresh never got an equivalent. Add one —
a metadata command naming the title to refresh — send it from the series and
movie create handlers, and drain it in the metadata lane alongside the
scheduled sweep.

Requirements:

  • POST /api/series and POST /api/movies return immediately. The refresh is
    asynchronous; adding a title must not block on a TMDB call, and must not fail
    because TMDB is slow or down.
  • The refresh runs within seconds of the add, not on the next daily tick.
  • A refresh that fails leaves the title added. metadata_refreshed_at stays
    NULL so the scheduled lane retries it — the add is not rolled back.
  • The scheduled 24-hour sweep keeps working unchanged for everything else.
  • Sending a command for a title that was deleted between add and drain is not
    an error.

Non-goals: no change to METADATA_INTERVAL, and no inline/synchronous refresh.
Shortening the tick was considered and rejected — it would still leave a visible
wait and would spend a TMDB call per title per tick.

Adding a series shows "no seasons revealed yet — a metadata refresh fills this in", and it stays that way for up to 24 hours. The metadata lane runs on a daily interval: ```rust pub const METADATA_INTERVAL: Duration = Duration::from_hours(24); const METADATA_START_DELAY: Duration = Duration::from_secs(5); ``` A newly added series is written with `metadata_refreshed_at = NULL`, and `metadata_refresh_due` already returns `true` for NULL — the query in `series_refresh.rs` even sorts NULLs first. Nothing is wrong with the eligibility logic. There is simply no way to ask the lane to run now, so a new title waits for a tick that may be most of a day away. Movies have the same defect. It is less obvious because their title and year come from the add payload, so the page looks populated while the digital release date and other TMDB fields are still missing. **Reuse the existing pattern rather than inventing one.** `AppState` already carries `MovieCommand`, `EpisodeCommand` and `SeasonCommand` channels that the API sends on and the daemon drains, precisely so a manual action does not wait for the reconcile tick. Metadata refresh never got an equivalent. Add one — a metadata command naming the title to refresh — send it from the series and movie create handlers, and drain it in the metadata lane alongside the scheduled sweep. Requirements: - `POST /api/series` and `POST /api/movies` return immediately. The refresh is asynchronous; adding a title must not block on a TMDB call, and must not fail because TMDB is slow or down. - The refresh runs within seconds of the add, not on the next daily tick. - A refresh that fails leaves the title added. `metadata_refreshed_at` stays NULL so the scheduled lane retries it — the add is not rolled back. - The scheduled 24-hour sweep keeps working unchanged for everything else. - Sending a command for a title that was deleted between add and drain is not an error. Non-goals: no change to `METADATA_INTERVAL`, and no inline/synchronous refresh. Shortening the tick was considered and rejected — it would still leave a visible wait and would spend a TMDB call per title per tick.
naps62-yolo added this to the Reliability and first run milestone 2026-08-24 17:58:27 +01:00
naps62-yolo added the area/daemondifficulty/moderatephase/6-tvtype/bug labels 2026-08-24 17:58:27 +01:00
naps62-yolo added a new dependency 2026-08-24 17:58:27 +01:00
Author
Owner

Implemented on reliability/176-refresh-on-add. One thing left in place that
touches requirement 1, deliberately.

POST /api/series and POST /api/movies still make a synchronous best-effort
TMDB call before the insert — lookup_tmdb_series and lookup_movie_artwork,
added by the §9.6 artwork work so a title added today has a poster. Neither can
fail the add, and with TMDB refusing connections the add measured 16 ms. But
against a TMDB that hangs rather than refuses, the request blocks up to the
client's 10-second timeout, which is not "must not block on a TMDB call".

Removing them would be correct now that the async refresh fills the same
columns within a second of the add, but it deletes behaviour two existing tests
assert (creating_a_series_stores_artwork_from_tmdb, and the movie equivalent),
so it belongs to whoever owns §9.6 rather than to this issue.

Implemented on `reliability/176-refresh-on-add`. One thing left in place that touches requirement 1, deliberately. `POST /api/series` and `POST /api/movies` still make a synchronous best-effort TMDB call before the insert — `lookup_tmdb_series` and `lookup_movie_artwork`, added by the §9.6 artwork work so a title added today has a poster. Neither can fail the add, and with TMDB refusing connections the add measured 16 ms. But against a TMDB that hangs rather than refuses, the request blocks up to the client's 10-second timeout, which is not "must not block on a TMDB call". Removing them would be correct now that the async refresh fills the same columns within a second of the add, but it deletes behaviour two existing tests assert (`creating_a_series_stores_artwork_from_tmdb`, and the movie equivalent), so it belongs to whoever owns §9.6 rather than to this issue.
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Reference: yolo/arr#176