Skip to content

fix(cover): follow connect-URL flips in library cover backfill#952

Merged
cucadmuh merged 4 commits into
mainfrom
fix/cover-backfill-connect-url
Jun 2, 2026
Merged

fix(cover): follow connect-URL flips in library cover backfill#952
cucadmuh merged 4 commits into
mainfrom
fix/cover-backfill-connect-url

Conversation

@cucadmuh
Copy link
Copy Markdown
Collaborator

@cucadmuh cucadmuh commented Jun 2, 2026

Problem

On a dual-address server (LAN + public), the smart endpoint switch picks the reachable connect URL and keeps it sticky in a session cache. Playback and on-demand UI covers rebuild restBaseUrl per request from that cache, so they follow a flip. Library cover backfill is configured once in Rust with a snapshot of the URL and is never re-triggered.

Result: when a client is off the LAN — either started that way, or moved off mid-session (internet still up, music playing via the public address) — backfill keeps hitting the now-unreachable http://192.168.x.x:4533 and floods the runtime log with error sending request for url ... for every album/artist cover.

The boot case is the tricky one: at startup the first backfill pass begins on the primary URL before the reachability probe finishes (the LAN ping has to time out first). When the probe then flips the cache to public, a naive reconfigure's forced pass is dropped by the pass_running guard, and the slow LAN pass (every cover timing out) runs to completion with nothing re-running on the reachable address.

Fix

Frontend

  • Make the connect cache (serverEndpoint.ts) observable — notify subscribers only on an effective change (sticky URL set to a different endpoint, dropped on all-fail, or invalidated).
  • useLibraryCoverBackfill reads the resolved connect URL via useSyncExternalStore and reconfigures the native session when it flips, forcing a pass so the .fetch-failed backoff from the stale address is cleared and those covers retry.

Rust backfill worker

  • set_session bumps a session generation; the running pass captures it and abandons promptly on every focus gate when it changes (same server_index_key, new rest_base_url) — so the stale-address pass stops instead of grinding through every cover.
  • try_schedule_full_pass records a rerun when a pass is already in flight and drains it once the abandoned pass returns — so the boot-time LAN→public flip still yields a fresh forced pass on the reachable address.

UI/playback paths already follow the switch (connectBaseUrlForServer per request) — unchanged.

Test plan

  • serverEndpoint.test.ts: notifies on first resolve + on flip; silent when the sticky URL is unchanged; notifies on invalidation only when an entry existed. 61/61 pass.
  • tsc --noEmit clean; cargo check -p psysonic clean.
  • Manual (mid-session): play on LAN, drop off the LAN (keep internet) — backfill switches to public, stops logging local-address failures.
  • Manual (boot): launch already off the LAN — initial pass may briefly attempt the primary URL, then abandons and reruns on the public address.

cucadmuh added 4 commits June 2, 2026 14:21
The native cover backfill was configured once with a snapshot of the runtime
connect URL. When a laptop moved off the LAN, the smart endpoint switch flipped
the sticky connect URL to the public address (playback/UI covers rebuild it per
request and followed it), but backfill kept fetching from the now-unreachable
local address — flooding the log with "error sending request" failures.

Make the connect cache observable (notify on effective flips) and have the
backfill hook reconfigure when the resolved URL changes, forcing a pass so the
.fetch-failed backoff from the stale address is cleared and those covers retry
on the reachable endpoint.
Frontend reconfigure alone didn't fully cover the boot case: at startup the
first backfill pass starts on the primary (LAN) URL before the reachability
probe resolves, so when the probe flips to public the forced rerun was dropped
by the pass_running guard and the slow LAN pass (every cover timing out) ran to
completion with nothing re-running on the reachable address.

set_session now bumps a session generation; the running pass checks it on every
focus gate and abandons promptly when the URL flips (same server_index_key, new
rest_base_url). try_schedule_full_pass records a rerun when a pass is in flight
and drains it once the abandoned pass returns, so a fresh forced pass runs on
the new address.
…it into the queue

The backfill worklist no longer carries a URL. Each cover fetch reads the
current reachable address live from a single worker cell, so a LAN↔public flip
is honoured even by the pass already in flight — its remaining covers download
against the new endpoint without aborting/rebuilding the worklist.

- Drop rest_base_url from CoverBackfillSession; add live base_url cell read in
  ensure_one. Remove the session-generation abort machinery (no longer needed).
- New lightweight library_cover_backfill_set_base_url command pushes the URL on
  every connect-cache flip; a real change clears the stale .fetch-failed backoff
  and runs a forced pass so covers that timed out on the old address retry.
- Split useLibraryCoverBackfill into a configure effect (server/creds/strategy)
  and a flip effect that only pushes the URL.
- Keep a single rerun_pending flag for the boot case (flip mid-pass), since the
  finished pass re-arms the idle gate.
@cucadmuh cucadmuh merged commit 4783263 into main Jun 2, 2026
7 checks passed
@cucadmuh cucadmuh deleted the fix/cover-backfill-connect-url branch June 2, 2026 13:01
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant