- 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.
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
InstanceIdcolumn toDeviceentity (src/Jellyfin.Database/Jellyfin.Database.Implementations/Entities/Security/Device.cs)- Nullable
Guid?for backward compatibility - Tracks which instance last authenticated the device
- Nullable
-
Added
InstanceIdproperty toSessionInfoclass (MediaBrowser.Controller/Session/SessionInfo.cs)- Non-nullable
Guidproperty - Set automatically when sessions are created
- Used for filtering sessions by instance
- Non-nullable
2. ✅ Instance Registry Service
-
Created
IInstanceRegistryinterface (Jellyfin.Server.Implementations/Clustering/IInstanceRegistry.cs)- Methods: RegisterInstanceAsync, UpdateHeartbeatAsync, ShutdownInstanceAsync
- GetActiveInstancesAsync, GetInstanceAsync, CleanupStaleInstancesAsync
- IsPrimaryInstanceAsync for primary election
-
Implemented
InstanceRegistryservice (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
SessionManagerconstructor (Emby.Server.Implementations/Session/SessionManager.cs)- Added
IInstanceRegistryparameter (optional for backward compatibility) - Stores reference to instance registry
- Added
-
Modified session creation (
CreateSessionInfomethod)- Sets
InstanceIdon newSessionInfoobjects - Uses
_instanceRegistry.CurrentInstanceIdif available - Falls back to
Guid.Emptyfor single-instance deployments
- Sets
-
Updated
Sessionsproperty- 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
IInstanceRegistry→InstanceRegistry(singleton) - Added
IDistributedLockManager→DistributedLockManager(singleton) - Registered before SessionManager to ensure availability
- Added
5. ✅ Startup Integration
-
Modified
RunStartupTasksAsync(Emby.Server.Implementations/ApplicationHost.cs)- Changed from synchronous to async (
Taskinstead ofTask.CompletedTask) - Calls
RegisterInstanceAsyncafter core initialization
- Changed from synchronous to async (
-
Created
RegisterInstanceAsyncmethod- 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
InstanceIdcolumn toDevicestable - Created index on
Devices.InstanceId - Updated Down migration to drop column
- Added
-
Updated SQL migration script (
sql/add_multi_instance_support.sql)- Added
ALTER TABLE Devices ADD COLUMN InstanceId UUID - Created
idx_devices_instanceindex - Added verification query for Devices table
- Added
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:
IInstanceRegistryparameter 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
-
Apply database migration:
psql -U jellyfin -d jellyfin -f sql/add_multi_instance_support.sql -
Start multiple Jellyfin instances:
- Each gets a unique InstanceId automatically
- Each registers itself in the database
- Each only manages its own sessions
-
Monitor instances:
SELECT * FROM library."Instances" WHERE "Status" = 'Active'; -
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.csJellyfin.Server.Implementations/Clustering/InstanceRegistry.cs
Files Modified
src/Jellyfin.Database/Jellyfin.Database.Implementations/Entities/Security/Device.cs- Added InstanceId propertyMediaBrowser.Controller/Session/SessionInfo.cs- Added InstanceId propertyEmby.Server.Implementations/Session/SessionManager.cs- Integrated InstanceRegistry, session filteringEmby.Server.Implementations/ApplicationHost.cs- Service registration, instance registration on startupsrc/Jellyfin.Database/Jellyfin.Database.Providers.Postgres/Migrations/20260305000000_AddMultiInstanceSupport.cs- Added Devices.InstanceIdsql/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