public SortedDictionary <string, long> Compare( DownloadData oldData, DownloadData newData) { if (newData.Count == 0) { throw new InvalidOperationException("The new data should not be empty."); } var stopwatch = Stopwatch.StartNew(); // We use a very simplistic algorithm here. Find the union of both ID sets and compare each download count. var uniqueIds = new HashSet <string>( oldData.Keys.Concat(newData.Keys), StringComparer.OrdinalIgnoreCase); _logger.LogInformation( "There are {OldCount} IDs in the old data, {NewCount} IDs in the new data, and {TotalCount} IDs in total.", oldData.Count, newData.Count, uniqueIds.Count); var result = new SortedDictionary <string, long>(StringComparer.OrdinalIgnoreCase); var decreaseCount = 0; foreach (var id in uniqueIds) { // Detect download count decreases and emit a metric. This is not necessarily wrong because there have // been times that we manually delete spoofed download counts. DetectDownloadCountDecreases(oldData, newData, id, ref decreaseCount); var oldCount = oldData.GetDownloadCount(id); var newCount = newData.GetDownloadCount(id); if (oldCount != newCount) { result.Add(id, newCount); } } _logger.LogInformation("There are {Count} package IDs with download count changes.", result.Count); _logger.LogInformation("There are {Count} package versions with download count decreases.", decreaseCount); if (decreaseCount > _options.Value.MaxDownloadCountDecreases) { throw new InvalidOperationException("Too many download count decreases are occurring."); } stopwatch.Stop(); _telemetryService.TrackDownloadSetComparison(oldData.Count, newData.Count, result.Count, stopwatch.Elapsed); return(result); }