aboutsummaryrefslogtreecommitdiff
path: root/Jellyfin.Server
diff options
context:
space:
mode:
authorJoshua M. Boniface <joshua@boniface.me>2026-06-21 23:06:45 -0400
committerJoshua M. Boniface <joshua@boniface.me>2026-06-21 23:31:25 -0400
commitb60c535c84a382d06eab5b0c38ee279103b06cf2 (patch)
treef952070278cdd9c02400d3aa08a278fba38e45b1 /Jellyfin.Server
parent069eb40ebfdca4030d0c87d56f4398f6f5d1b55e (diff)
Add progress logging and batch deletion for logs
After resolving duplicates the migration deleted all items in one silent pass (per-id GetItemById plus a single DeleteItemsUnsafeFast), which looks hung for minutes on large libraries. Delete in batches of 500 and log progress per batch, which also avoids one oversized delete transaction.
Diffstat (limited to 'Jellyfin.Server')
-rw-r--r--Jellyfin.Server/Migrations/Routines/20260115120000_FixIncorrectOwnerIdRelationships.cs43
1 files changed, 31 insertions, 12 deletions
diff --git a/Jellyfin.Server/Migrations/Routines/20260115120000_FixIncorrectOwnerIdRelationships.cs b/Jellyfin.Server/Migrations/Routines/20260115120000_FixIncorrectOwnerIdRelationships.cs
index 0baf261a2e..e34182fd5d 100644
--- a/Jellyfin.Server/Migrations/Routines/20260115120000_FixIncorrectOwnerIdRelationships.cs
+++ b/Jellyfin.Server/Migrations/Routines/20260115120000_FixIncorrectOwnerIdRelationships.cs
@@ -136,19 +136,38 @@ public class FixIncorrectOwnerIdRelationships : IAsyncMigrationRoutine
if (allIdsToDelete.Count > 0)
{
- // Batch-resolve items for metadata path cleanup, then delete all at once
- var itemsToDelete = allIdsToDelete
- .Select(id => _libraryManager.GetItemById(id))
- .Where(item => item is not null)
- .ToList();
- _libraryManager.DeleteItemsUnsafeFast(itemsToDelete!);
-
- // Fall back to direct DB deletion for any items that couldn't be resolved via LibraryManager
- var deletedIds = itemsToDelete.Select(i => i!.Id).ToHashSet();
- var unresolvedIds = allIdsToDelete.Where(id => !deletedIds.Contains(id)).ToList();
- if (unresolvedIds.Count > 0)
+ _logger.LogInformation("Deleting {Count} duplicate database entries...", allIdsToDelete.Count);
+
+ // Delete in batches so progress is visible (item resolution and deletion can take a
+ // long time on large libraries) and so we never issue one massive delete transaction.
+ const int deleteBatchSize = 500;
+ var deletedSoFar = 0;
+ for (var offset = 0; offset < allIdsToDelete.Count; offset += deleteBatchSize)
{
- _persistenceService.DeleteItem(unresolvedIds);
+ cancellationToken.ThrowIfCancellationRequested();
+
+ var batchIds = allIdsToDelete.GetRange(offset, Math.Min(deleteBatchSize, allIdsToDelete.Count - offset));
+
+ // Resolve items for metadata path cleanup, then delete this batch
+ var itemsToDelete = batchIds
+ .Select(id => _libraryManager.GetItemById(id))
+ .Where(item => item is not null)
+ .ToList();
+ if (itemsToDelete.Count > 0)
+ {
+ _libraryManager.DeleteItemsUnsafeFast(itemsToDelete!);
+ }
+
+ // Fall back to direct DB deletion for any items that couldn't be resolved via LibraryManager
+ var deletedIds = itemsToDelete.Select(i => i!.Id).ToHashSet();
+ var unresolvedIds = batchIds.Where(id => !deletedIds.Contains(id)).ToList();
+ if (unresolvedIds.Count > 0)
+ {
+ _persistenceService.DeleteItem(unresolvedIds);
+ }
+
+ deletedSoFar += batchIds.Count;
+ _logger.LogInformation("Deleting duplicates: {Deleted}/{Total} items", deletedSoFar, allIdsToDelete.Count);
}
}