Fix memory leak in ActiveDownloads dictionary

- Changed ActiveDownloads from Dictionary to ConcurrentDictionary for thread safety
- Added automatic cleanup of completed downloads after 5 minutes
- Added automatic cleanup of failed downloads after 2 minutes
- This fixes the 929MB -> 10MB memory issue where downloads were never removed from tracking
This commit is contained in:
2026-02-05 09:19:32 -05:00
parent 3fd13b855d
commit 25bbf45cbb

View File

@@ -5,6 +5,7 @@ using allstarr.Models.Search;
using allstarr.Models.Subsonic;
using allstarr.Services.Local;
using allstarr.Services.Subsonic;
using System.Collections.Concurrent;
using TagLib;
using IOFile = System.IO.File;
@@ -27,7 +28,7 @@ public abstract class BaseDownloadService : IDownloadService
protected readonly string DownloadPath;
protected readonly string CachePath;
protected readonly Dictionary<string, DownloadInfo> ActiveDownloads = new();
protected readonly ConcurrentDictionary<string, DownloadInfo> ActiveDownloads = new();
protected readonly SemaphoreSlim DownloadLock = new(1, 1);
/// <summary>
@@ -298,6 +299,14 @@ public abstract class BaseDownloadService : IDownloadService
song.LocalPath = localPath;
// Clean up completed download from tracking after a short delay
_ = Task.Run(async () =>
{
await Task.Delay(TimeSpan.FromMinutes(5)); // Keep for 5 minutes for status checks
ActiveDownloads.TryRemove(songId, out _);
Logger.LogDebug("Cleaned up completed download tracking for {SongId}", songId);
});
// Register BEFORE releasing lock to prevent race conditions (both cache and download modes)
await LocalLibraryService.RegisterDownloadedSongAsync(song, localPath);
@@ -360,6 +369,14 @@ public abstract class BaseDownloadService : IDownloadService
{
downloadInfo.Status = DownloadStatus.Failed;
downloadInfo.ErrorMessage = ex.Message;
// Clean up failed download from tracking after a short delay
_ = Task.Run(async () =>
{
await Task.Delay(TimeSpan.FromMinutes(2)); // Keep for 2 minutes for error reporting
ActiveDownloads.TryRemove(songId, out _);
Logger.LogDebug("Cleaned up failed download tracking for {SongId}", songId);
});
}
Logger.LogError(ex, "Download failed for {SongId}", songId);
throw;