Shared-config and library-visibility changes do not propagate between replicas #15

Closed
opened 2026-09-13 13:33:15 +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

Two caches over the same shared /config volume never invalidate:

  • Emby.Server.Implementations/AppBase/BaseConfigurationManager.cs:22,31 reads the shared /config/*.xml once and caches it. ConfigurationUpdated is an in-process event only.
  • MediaBrowser.Controller/Entities/CollectionFolder.cs:35 holds a static _libraryOptions dictionary (GetOrAdd at :173, Clear() at :194, called from only two local sites). It gates per-user library visibility via IsVisible().

So an admin who disables a library, remaps a library path, or tightens a security setting on one pod sees it apply on that pod and silently not apply on the others until they restart. Library visibility is a permissions boundary, so the stale pods keep serving content that is supposed to be hidden.

Filed as one issue because both need the same fix.

Proposal

One invalidation mechanism covering both caches:

  • File-watch the shared /config directory and clear both caches on change. No new dependency, but change notification is unreliable on some shared volume types, so it needs a polling fallback.
  • Or valkey pub/sub published whenever configuration or library options are saved. Explicit and reliable, but puts a valkey dependency on the config path and needs defined behaviour when valkey is unavailable.

Acceptance

  • Two independently constructed instances against real PostgreSQL and a shared config directory: A disables a library, and B's IsVisible() for an affected user returns false without a restart.
  • Equivalent test for a setting read through BaseConfigurationManager.
  • Tests use PostgreSqlTestServer in tests/Jellyfin.Server.Tests and run 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 Two caches over the same shared `/config` volume never invalidate: - `Emby.Server.Implementations/AppBase/BaseConfigurationManager.cs:22,31` reads the shared `/config/*.xml` once and caches it. `ConfigurationUpdated` is an in-process event only. - `MediaBrowser.Controller/Entities/CollectionFolder.cs:35` holds a **`static`** `_libraryOptions` dictionary (`GetOrAdd` at `:173`, `Clear()` at `:194`, called from only two local sites). It gates per-user library visibility via `IsVisible()`. So an admin who disables a library, remaps a library path, or tightens a security setting on one pod sees it apply on that pod and silently not apply on the others until they restart. Library visibility is a permissions boundary, so the stale pods keep serving content that is supposed to be hidden. Filed as one issue because both need the same fix. ## Proposal One invalidation mechanism covering both caches: - File-watch the shared `/config` directory and clear both caches on change. No new dependency, but change notification is unreliable on some shared volume types, so it needs a polling fallback. - Or valkey pub/sub published whenever configuration or library options are saved. Explicit and reliable, but puts a valkey dependency on the config path and needs defined behaviour when valkey is unavailable. ## Acceptance - Two independently constructed instances against real PostgreSQL and a shared config directory: A disables a library, and B's `IsVisible()` for an affected user returns false without a restart. - Equivalent test for a setting read through `BaseConfigurationManager`. - Tests use `PostgreSqlTestServer` in `tests/Jellyfin.Server.Tests` and run 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#15