Postgres perf, path migration, and shutdown race fixes

- Major query performance boost: replaced correlated subqueries with DistinctBy() in BaseItemRepository, added episode deduplication index, and increased default command timeout to 120s.
- Fixed LibraryMonitor shutdown race: added disposal checks and exception handling in FileRefresher to prevent ObjectDisposedException.
- Added PowerShell and SQL utilities for fixing Linux paths in config files and database; new scripts for WebDir and path migration.
- Auto-fix for WebDir in startup.json on load; new helper scripts and docs.
- Added automated weekly DB performance monitoring scripts and reporting.
- Expanded documentation and README for all fixes, scripts, and migration steps.
- Updated SQL scripts for index creation and diagnostics; improved output handling.
- Updated db-config defaults and added new diagnostic/report files.
This commit is contained in:
2026-03-05 08:56:42 -05:00
parent 0eb44818ff
commit 7eb2b445cb
44 changed files with 3504 additions and 23 deletions
@@ -109,6 +109,12 @@ namespace Emby.Server.Implementations.IO
private void OnTimerCallback(object? state)
{
// Check if disposed before processing
if (_disposed)
{
return;
}
List<string> paths;
lock (_timerLock)
@@ -121,6 +127,12 @@ namespace Emby.Server.Implementations.IO
DisposeTimer();
Completed?.Invoke(this, EventArgs.Empty);
// Double-check disposal after timer is disposed
if (_disposed)
{
return;
}
try
{
ProcessPathChanges(paths);
@@ -133,11 +145,28 @@ namespace Emby.Server.Implementations.IO
private void ProcessPathChanges(List<string> paths)
{
IEnumerable<BaseItem> itemsToRefresh = paths
.Distinct()
.Select(GetAffectedBaseItem)
.Where(item => item is not null)
.DistinctBy(x => x!.Id)!; // Removed null values in the previous .Where()
// Check if disposed before accessing services
if (_disposed)
{
return;
}
IEnumerable<BaseItem> itemsToRefresh;
try
{
itemsToRefresh = paths
.Distinct()
.Select(GetAffectedBaseItem)
.Where(item => item is not null)
.DistinctBy(x => x!.Id)!; // Removed null values in the previous .Where()
}
catch (ObjectDisposedException)
{
// Service provider has been disposed during shutdown, skip processing
_logger.LogDebug("Service provider disposed during path change processing, skipping refresh");
return;
}
foreach (var item in itemsToRefresh)
{