Refactored PendingRelease logic for performance.

This commit is contained in:
Taloth Saldono 2018-03-18 16:22:10 +01:00 committed by Taloth
parent 5b0e959d3f
commit 70afacee3f
2 changed files with 117 additions and 57 deletions

View File

@ -51,6 +51,34 @@ namespace NzbDrone.Common.Extensions
} }
} }
public static Dictionary<TKey, TItem> ToDictionaryIgnoreDuplicates<TItem, TKey>(this IEnumerable<TItem> src, Func<TItem, TKey> keySelector)
{
var result = new Dictionary<TKey, TItem>();
foreach (var item in src)
{
var key = keySelector(item);
if (!result.ContainsKey(key))
{
result[key] = item;
}
}
return result;
}
public static Dictionary<TKey, TValue> ToDictionaryIgnoreDuplicates<TItem, TKey, TValue>(this IEnumerable<TItem> src, Func<TItem, TKey> keySelector, Func<TItem, TValue> valueSelector)
{
var result = new Dictionary<TKey, TValue>();
foreach (var item in src)
{
var key = keySelector(item);
if (!result.ContainsKey(key))
{
result[key] = valueSelector(item);
}
}
return result;
}
public static void AddIfNotNull<TSource>(this List<TSource> source, TSource item) public static void AddIfNotNull<TSource>(this List<TSource> source, TSource item)
{ {
if (item == null) if (item == null)

View File

@ -1,6 +1,7 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using Marr.Data;
using NLog; using NLog;
using NzbDrone.Common.Crypto; using NzbDrone.Common.Crypto;
using NzbDrone.Common.Extensions; using NzbDrone.Common.Extensions;
@ -66,35 +67,30 @@ namespace NzbDrone.Core.Download.Pending
_logger = logger; _logger = logger;
} }
public void Add(DownloadDecision decision, PendingReleaseReason reason) public void Add(DownloadDecision decision, PendingReleaseReason reason)
{ {
var alreadyPending = _repository.AllBySeriesId(decision.RemoteEpisode.Series.Id); AddMany(new List<Tuple<DownloadDecision, PendingReleaseReason>> { Tuple.Create(decision, reason) });
alreadyPending = IncludeRemoteEpisodes(alreadyPending);
Add(alreadyPending, decision, reason);
} }
public void AddMany(List<Tuple<DownloadDecision, PendingReleaseReason>> decisions) public void AddMany(List<Tuple<DownloadDecision, PendingReleaseReason>> decisions)
{ {
var alreadyPending = decisions.Select(v => v.Item1.RemoteEpisode.Series.Id).Distinct().SelectMany(_repository.AllBySeriesId).ToList(); foreach (var seriesDecisions in decisions.GroupBy(v => v.Item1.RemoteEpisode.Series.Id))
alreadyPending = IncludeRemoteEpisodes(alreadyPending);
foreach (var pair in decisions)
{ {
Add(alreadyPending, pair.Item1, pair.Item2); var series = seriesDecisions.First().Item1.RemoteEpisode.Series;
} var alreadyPending = _repository.AllBySeriesId(series.Id);
}
private void Add(List<PendingRelease> alreadyPending, DownloadDecision decision, PendingReleaseReason reason) alreadyPending = IncludeRemoteEpisodes(alreadyPending, seriesDecisions.ToDictionaryIgnoreDuplicates(v => v.Item1.RemoteEpisode.Release.Title, v => v.Item1.RemoteEpisode));
var alreadyPendingByEpisode = CreateEpisodeLookup(alreadyPending);
foreach (var pair in seriesDecisions)
{ {
var decision = pair.Item1;
var reason = pair.Item2;
var episodeIds = decision.RemoteEpisode.Episodes.Select(e => e.Id); var episodeIds = decision.RemoteEpisode.Episodes.Select(e => e.Id);
var existingReports = alreadyPending.Where(r => r.RemoteEpisode.Episodes.Select(e => e.Id) var existingReports = episodeIds.SelectMany(v => alreadyPendingByEpisode[v] ?? Enumerable.Empty<PendingRelease>())
.Intersect(episodeIds) .Distinct().ToList();
.Any());
var matchingReports = existingReports.Where(MatchingReleasePredicate(decision.RemoteEpisode.Release)).ToList(); var matchingReports = existingReports.Where(MatchingReleasePredicate(decision.RemoteEpisode.Release)).ToList();
@ -121,15 +117,25 @@ namespace NzbDrone.Core.Download.Pending
{ {
_repository.Delete(duplicate.Id); _repository.Delete(duplicate.Id);
alreadyPending.Remove(duplicate); alreadyPending.Remove(duplicate);
alreadyPendingByEpisode = CreateEpisodeLookup(alreadyPending);
} }
} }
return; continue;
} }
_logger.Debug("Adding release {0} to pending releases with reason {1}", decision.RemoteEpisode, reason); _logger.Debug("Adding release {0} to pending releases with reason {1}", decision.RemoteEpisode, reason);
Insert(decision, reason); Insert(decision, reason);
} }
}
}
private ILookup<int, PendingRelease> CreateEpisodeLookup(IEnumerable<PendingRelease> alreadyPending)
{
return alreadyPending.SelectMany(v => v.RemoteEpisode.Episodes
.Select(d => new { Episode = d, PendingRelease = v }))
.ToLookup(v => v.Episode.Id, v => v.PendingRelease);
}
public List<ReleaseInfo> GetPending() public List<ReleaseInfo> GetPending()
{ {
@ -254,11 +260,27 @@ namespace NzbDrone.Core.Download.Pending
return IncludeRemoteEpisodes(_repository.AllBySeriesId(seriesId).ToList()); return IncludeRemoteEpisodes(_repository.AllBySeriesId(seriesId).ToList());
} }
private List<PendingRelease> IncludeRemoteEpisodes(List<PendingRelease> releases) private List<PendingRelease> IncludeRemoteEpisodes(List<PendingRelease> releases, Dictionary<string, RemoteEpisode> knownRemoteEpisodes = null)
{ {
var result = new List<PendingRelease>(); var result = new List<PendingRelease>();
var seriesMap = _seriesService.GetSeries(releases.Select(v => v.SeriesId).Distinct())
.ToDictionary(v => v.Id); var seriesMap = new Dictionary<int, Series>();
if (knownRemoteEpisodes != null)
{
foreach (var series in knownRemoteEpisodes.Values.Select(v => v.Series))
{
if (!seriesMap.ContainsKey(series.Id))
{
seriesMap[series.Id] = series;
}
}
}
foreach (var series in _seriesService.GetSeries(releases.Select(v => v.SeriesId).Distinct().Where(v => !seriesMap.ContainsKey(v))))
{
seriesMap[series.Id] = series;
}
foreach (var release in releases) foreach (var release in releases)
{ {
@ -267,7 +289,17 @@ namespace NzbDrone.Core.Download.Pending
// Just in case the series was removed, but wasn't cleaned up yet (housekeeper will clean it up) // Just in case the series was removed, but wasn't cleaned up yet (housekeeper will clean it up)
if (series == null) return null; if (series == null) return null;
var episodes = _parsingService.GetEpisodes(release.ParsedEpisodeInfo, series, true); List<Episode> episodes;
RemoteEpisode knownRemoteEpisode;
if (knownRemoteEpisodes != null && knownRemoteEpisodes.TryGetValue(release.Release.Title, out knownRemoteEpisode))
{
episodes = knownRemoteEpisode.Episodes;
}
else
{
episodes = _parsingService.GetEpisodes(release.ParsedEpisodeInfo, series, true);
}
release.RemoteEpisode = new RemoteEpisode release.RemoteEpisode = new RemoteEpisode
{ {