77e30685bb
- 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.
173 lines
7.6 KiB
Markdown
173 lines
7.6 KiB
Markdown
# 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
|