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.
This commit is contained in:
@@ -0,0 +1,172 @@
|
||||
# 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 `IInstanceRegistry` → `InstanceRegistry` (singleton)
|
||||
- Added `IDistributedLockManager` → `DistributedLockManager` (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:
|
||||
```bash
|
||||
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:
|
||||
```sql
|
||||
SELECT * FROM library."Instances" WHERE "Status" = 'Active';
|
||||
```
|
||||
|
||||
4. View sessions by instance:
|
||||
```sql
|
||||
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
|
||||
Reference in New Issue
Block a user