Files
pgsql-jellyfin/docs/PHASE3_SESSION_ISOLATION_COMPLETE.md
wjones 77e30685bb Complete multi-instance support: Phases 3–6 & deployment
- Implements Phases 3–6: session isolation, cache coordination, primary election, and file system monitor coordination for Jellyfin with PostgreSQL.
- Adds new database entities (Instance, DistributedLock, FileSystemChange) and EF model configurations.
- Includes SQL migration scripts and EF migration for all required tables, columns, and helper functions.
- Updates Device entity and JellyfinDbContext for multi-instance tracking.
- Integrates new DI services for instance registry, distributed locks, cache coordinator, and primary election.
- Adds publishing profiles (Win/Linux/FrameworkDependent) and automation script for deployment.
- Extensive documentation for architecture, setup, and publishing.
- All changes are backward compatible and build successfully.
2026-03-05 16:10:26 -05:00

7.6 KiB

Phase 3 Implementation Summary: Session Isolation

Overview

Phase 3 of multi-instance support ensures that user sessions are properly isolated between Jellyfin instances in a multi-instance deployment. Each instance now tracks which sessions belong to it and only manages its own sessions.

Completed Tasks

1. Database Schema Updates

  • Added InstanceId column to Device entity (src/Jellyfin.Database/Jellyfin.Database.Implementations/Entities/Security/Device.cs)

    • Nullable Guid? for backward compatibility
    • Tracks which instance last authenticated the device
  • Added InstanceId property to SessionInfo class (MediaBrowser.Controller/Session/SessionInfo.cs)

    • Non-nullable Guid property
    • Set automatically when sessions are created
    • Used for filtering sessions by instance

2. Instance Registry Service

  • Created IInstanceRegistry interface (Jellyfin.Server.Implementations/Clustering/IInstanceRegistry.cs)

    • Methods: RegisterInstanceAsync, UpdateHeartbeatAsync, ShutdownInstanceAsync
    • GetActiveInstancesAsync, GetInstanceAsync, CleanupStaleInstancesAsync
    • IsPrimaryInstanceAsync for primary election
  • Implemented InstanceRegistry service (Jellyfin.Server.Implementations/Clustering/InstanceRegistry.cs)

    • Generates unique InstanceId per instance (GUID)
    • Automatic heartbeat mechanism (every 30 seconds)
    • Registers instance on startup with hostname, ports, version
    • Marks instance as shutdown on disposal

3. SessionManager Integration

  • Updated SessionManager constructor (Emby.Server.Implementations/Session/SessionManager.cs)

    • Added IInstanceRegistry parameter (optional for backward compatibility)
    • Stores reference to instance registry
  • Modified session creation (CreateSessionInfo method)

    • Sets InstanceId on new SessionInfo objects
    • Uses _instanceRegistry.CurrentInstanceId if available
    • Falls back to Guid.Empty for single-instance deployments
  • Updated Sessions property

    • In multi-instance mode: filters sessions by CurrentInstanceId
    • In single-instance mode: returns all sessions (backward compatible)
    • Ensures each instance only sees its own sessions

4. Dependency Injection Registration

  • Registered services in ApplicationHost (Emby.Server.Implementations/ApplicationHost.cs)
    • Added IInstanceRegistryInstanceRegistry (singleton)
    • Added IDistributedLockManagerDistributedLockManager (singleton)
    • Registered before SessionManager to ensure availability

5. Startup Integration

  • Modified RunStartupTasksAsync (Emby.Server.Implementations/ApplicationHost.cs)

    • Changed from synchronous to async (Task instead of Task.CompletedTask)
    • Calls RegisterInstanceAsync after core initialization
  • Created RegisterInstanceAsync method

    • Retrieves network configuration for ports
    • Registers instance in database with hostname, process ID, ports, version
    • Logs successful registration with InstanceId
    • Handles registration failures gracefully

6. Database Migration Updates

  • Updated EF Core migration (20260305000000_AddMultiInstanceSupport.cs)

    • Added InstanceId column to Devices table
    • Created index on Devices.InstanceId
    • Updated Down migration to drop column
  • Updated SQL migration script (sql/add_multi_instance_support.sql)

    • Added ALTER TABLE Devices ADD COLUMN InstanceId UUID
    • Created idx_devices_instance index
    • Added verification query for Devices table

Architecture Benefits

Session Isolation

  • Resource Separation: Each instance manages only its own transcoding processes, network connections, and WebSocket sessions
  • No Interference: One instance cannot accidentally terminate or modify another instance's active sessions
  • Clean Shutdown: When an instance shuts down, only its sessions are affected

Backward Compatibility

  • Optional Registry: IInstanceRegistry parameter is optional, allowing single-instance deployments to continue working
  • Nullable Device.InstanceId: Existing devices without InstanceId can still function
  • Fallback Logic: SessionManager gracefully handles missing instance registry

Scalability

  • Independent Scaling: Add more instances without session conflicts
  • Load Balancer Support: Users can connect to any instance, but their session is managed by one instance
  • Sticky Sessions: Future enhancement - route users to the instance managing their session

Usage

Single Instance (Backward Compatible)

No configuration changes needed. The instance registry will register the instance, but session filtering is transparent.

Multi-Instance Deployment

  1. Apply database migration:

    psql -U jellyfin -d jellyfin -f sql/add_multi_instance_support.sql
    
  2. Start multiple Jellyfin instances:

    • Each gets a unique InstanceId automatically
    • Each registers itself in the database
    • Each only manages its own sessions
  3. Monitor instances:

    SELECT * FROM library."Instances" WHERE "Status" = 'Active';
    
  4. View sessions by instance:

    SELECT s."Id", s."DeviceName", i."Hostname", i."HttpPort"
    FROM library."Devices" s
    LEFT JOIN library."Instances" i ON s."InstanceId" = i."InstanceId"
    WHERE i."Status" = 'Active';
    

Testing Checklist

  • Start single instance - verify it registers
  • Create a session - verify InstanceId is set
  • View sessions API - verify sessions are returned
  • Shutdown instance - verify status updated to 'Shutdown'
  • Start two instances - verify both register with different InstanceIds
  • Create session on Instance A - verify Instance B doesn't see it
  • Create session on Instance B - verify Instance A doesn't see it
  • Stop Instance A - verify Instance B continues working
  • Check heartbeat mechanism - verify LastHeartbeat updates every 30s
  • Test stale instance detection - stop instance without graceful shutdown, verify marked as 'Failed' after 2 minutes

Next Steps

Phase 4: Cache Coordination

  • Implement PostgreSQL LISTEN/NOTIFY for cache invalidation
  • Create CacheCoordinator service
  • Hook into item update events
  • Enable multi-instance cache consistency

Phase 5: Primary Instance Election

  • Implement primary election algorithm
  • Coordinate scheduled tasks to run only on primary
  • Coordinate database migrations
  • Handle primary failover

Phase 6: File System Monitor Coordination

  • Create FileSystemChanges table
  • Update LibraryMonitor to write to database
  • Reduce duplicate file scanning across instances

Files Modified

New Files Created

  • Jellyfin.Server.Implementations/Clustering/IInstanceRegistry.cs
  • Jellyfin.Server.Implementations/Clustering/InstanceRegistry.cs

Files Modified

  • src/Jellyfin.Database/Jellyfin.Database.Implementations/Entities/Security/Device.cs - Added InstanceId property
  • MediaBrowser.Controller/Session/SessionInfo.cs - Added InstanceId property
  • Emby.Server.Implementations/Session/SessionManager.cs - Integrated InstanceRegistry, session filtering
  • Emby.Server.Implementations/ApplicationHost.cs - Service registration, instance registration on startup
  • src/Jellyfin.Database/Jellyfin.Database.Providers.Postgres/Migrations/20260305000000_AddMultiInstanceSupport.cs - Added Devices.InstanceId
  • sql/add_multi_instance_support.sql - Added Devices.InstanceId

Build Status

Build successful (only IDE warnings, no compilation errors)

Compatibility

  • Backward compatible with single-instance deployments
  • No breaking changes to existing APIs
  • Nullable columns for incremental adoption
  • Optional DI parameters for gradual integration