No shared session directory: cross-pod session operations fail #16

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/Session/SessionManager.cs:64-67 keeps _activeConnections process-local, with no shared record of which pod owns which session.

  • The admin session list shows only the sessions held by the pod that happened to serve the request, so half the active playback is invisible.
  • Remote control and "send message to session" silently cannot reach a device connected to another pod — the call succeeds and nothing happens.

The WebSocket connection itself is legitimately process-local. The gap is the directory, not the connection.

Proposal

Publish a session → owning-pod directory to valkey: session id, user, device, owning pod identity, with a TTL refreshed by the existing session heartbeat. Route a command for a non-local session to the owning pod rather than dropping it — either a valkey pub/sub channel per pod, or direct pod-to-pod HTTP using the directory entry. Pub/sub avoids needing pod-addressable networking.

This is a prerequisite for #17 (SyncPlay) and #20 (live WebSocket notifications).

Acceptance

  • Two independently constructed instances sharing one valkey and one real PostgreSQL: a session registered on A appears in B's session list, and a message sent via B is delivered to the connection held by A.
  • Directory entries expire when a pod stops heartbeating, so a crashed pod's sessions do not linger in the list.
  • 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 `Emby.Server.Implementations/Session/SessionManager.cs:64-67` keeps `_activeConnections` process-local, with no shared record of which pod owns which session. - The admin session list shows only the sessions held by the pod that happened to serve the request, so half the active playback is invisible. - Remote control and "send message to session" silently cannot reach a device connected to another pod — the call succeeds and nothing happens. The WebSocket connection itself is legitimately process-local. The gap is the directory, not the connection. ## Proposal Publish a session → owning-pod directory to valkey: session id, user, device, owning pod identity, with a TTL refreshed by the existing session heartbeat. Route a command for a non-local session to the owning pod rather than dropping it — either a valkey pub/sub channel per pod, or direct pod-to-pod HTTP using the directory entry. Pub/sub avoids needing pod-addressable networking. This is a **prerequisite** for #17 (SyncPlay) and #20 (live WebSocket notifications). ## Acceptance - Two independently constructed instances sharing one valkey and one real PostgreSQL: a session registered on A appears in B's session list, and a message sent via B is delivered to the connection held by A. - Directory entries expire when a pod stops heartbeating, so a crashed pod's sessions do not linger in the list. - 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#16