LibraryManager item cache serves stale metadata across replicas #19

Open
opened 2026-09-13 13:33:16 +10:00 by unkin-agent · 0 comments
Member

Context: multi-replica stateless deployment (two 2-replica apps, cheeztv and fafflix, each one PostgreSQL plus valkey, no sticky sessions). Not a 12.0 regression — live in the deployed build. Tracking: #12.

Problem

Emby.Server.Implementations/Library/LibraryManager.cs:90 — GetItemById is cache-first with no freshness check against the database.

A pod that cached an item before a metadata or artwork correction keeps serving the stale version. The staleness window is bounded only by CacheSize eviction, which on a large library is a long time. Wrong titles, descriptions and images persist on some replicas and not others, so a refresh that "fixes" an item appears to randomly un-fix itself.

Degradation rather than corruption — no data is lost.

Proposal

The same invalidation mechanism as #13 (UserDataManager): a concurrency token compared on read, or valkey pub/sub on item save. These two are likely one piece of work — do them together.

Acceptance

  • Two independently constructed instances against real PostgreSQL: A updates an item's metadata, and B — which has already cached that item — returns the updated values from GetItemById without a restart.
  • Test uses PostgreSqlTestServer in tests/Jellyfin.Server.Tests and runs in the postgres-migration-chain Woodpecker step (JELLYFIN_TEST_POSTGRES). In-memory providers have hidden different semantics and are how three bugs reached production.
Context: multi-replica stateless deployment (two 2-replica apps, `cheeztv` and `fafflix`, each one PostgreSQL plus valkey, no sticky sessions). Not a 12.0 regression — live in the deployed build. Tracking: #12. ## Problem `Emby.Server.Implementations/Library/LibraryManager.cs:90` — `GetItemById` is cache-first with no freshness check against the database. A pod that cached an item before a metadata or artwork correction keeps serving the stale version. The staleness window is bounded only by `CacheSize` eviction, which on a large library is a long time. Wrong titles, descriptions and images persist on some replicas and not others, so a refresh that "fixes" an item appears to randomly un-fix itself. Degradation rather than corruption — no data is lost. ## Proposal The same invalidation mechanism as #13 (`UserDataManager`): a concurrency token compared on read, or valkey pub/sub on item save. These two are likely one piece of work — do them together. ## Acceptance - Two independently constructed instances against real PostgreSQL: A updates an item's metadata, and B — which has already cached that item — returns the updated values from `GetItemById` without a restart. - Test uses `PostgreSqlTestServer` in `tests/Jellyfin.Server.Tests` and runs in the `postgres-migration-chain` Woodpecker step (`JELLYFIN_TEST_POSTGRES`). In-memory providers have hidden different semantics and are how three bugs reached production.
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: unkin/jellyfin-ha-src#19