public async Task <SearchResult <IPackageSearchMetadata> > RefreshSearchAsync(RefreshToken refreshToken, CancellationToken cancellationToken)
        {
            var searchToken = refreshToken as AggregatedRefreshToken;

            if (searchToken == null)
            {
                throw new InvalidOperationException("Invalid token");
            }

            return(await WaitForCompletionOrBailOutAsync(
                       searchToken.SearchString,
                       searchToken.SearchTasks,
                       searchToken.TelemetryState,
                       cancellationToken));
        }
        private async Task <SearchResult <IPackageSearchMetadata> > WaitForCompletionOrBailOutAsync(
            string searchText,
            IDictionary <string, Task <SearchResult <IPackageSearchMetadata> > > searchTasks,
            TelemetryState telemetryState,
            CancellationToken cancellationToken)
        {
            cancellationToken.ThrowIfCancellationRequested();

            if (searchTasks.Count == 0)
            {
                return(SearchResult.Empty <IPackageSearchMetadata>());
            }

            var aggregatedTask = Task.WhenAll(searchTasks.Values);

            RefreshToken refreshToken = null;

            if (aggregatedTask != await Task.WhenAny(aggregatedTask, Task.Delay(DefaultTimeout)))
            {
                refreshToken = new AggregatedRefreshToken
                {
                    TelemetryState = telemetryState,
                    SearchString   = searchText,
                    SearchTasks    = searchTasks,
                    RetryAfter     = DefaultTimeout
                };
            }

            var partitionedTasks = searchTasks
                                   .ToLookup(t => t.Value.Status == TaskStatus.RanToCompletion);

            var completedOnly = partitionedTasks[true];

            SearchResult <IPackageSearchMetadata> aggregated;
            IEnumerable <TimeSpan> timings = null;
            var timeAggregation            = new Stopwatch();

            if (completedOnly.Any())
            {
                var results = await Task.WhenAll(completedOnly.Select(kv => kv.Value));

                timings = results.Select(e => e.Duration);
                timeAggregation.Start();
                aggregated = await AggregateSearchResultsAsync(searchText, results, telemetryState);

                timeAggregation.Stop();
            }
            else
            {
                timings    = Enumerable.Empty <TimeSpan>();
                aggregated = SearchResult.Empty <IPackageSearchMetadata>();
            }

            aggregated.OperationId  = telemetryState?.OperationId;
            aggregated.RefreshToken = refreshToken;

            var notCompleted = partitionedTasks[false];

            if (notCompleted.Any())
            {
                var statuses = notCompleted.ToDictionary(
                    kv => kv.Key,
                    kv => GetLoadingStatus(kv.Value.Status));

                foreach (var item in statuses)
                {
                    aggregated.SourceSearchStatus.Add(item);
                }

                var exceptions = notCompleted
                                 .Where(kv => kv.Value.Exception != null)
                                 .ToDictionary(
                    kv => kv.Key,
                    kv => (Exception)kv.Value.Exception);

                foreach (var item in exceptions)
                {
                    aggregated.SourceSearchException.Add(item);
                }
            }

            if (_telemetryService != null &&
                aggregated.SourceSearchStatus != null &&
                aggregated.SourceSearchStatus.Values != null &&
                telemetryState != null)
            {
                var loadingStatus = aggregated.SourceSearchStatus.Values.Aggregate();
                if (loadingStatus != LoadingStatus.Loading &&
                    telemetryState.ShouldEmit)
                {
                    telemetryState.Duration.Stop();
                    _telemetryService.EmitTelemetryEvent(new SearchPageTelemetryEvent(
                                                             telemetryState.OperationId,
                                                             telemetryState.PageIndex,
                                                             aggregated.Items?.Count ?? 0,
                                                             telemetryState.Duration.Elapsed,
                                                             timings,
                                                             timeAggregation.Elapsed,
                                                             loadingStatus));
                }
            }

            return(aggregated);
        }
Exemple #3
0
 public Task <SearchResult <IPackageSearchMetadata> > RefreshSearchAsync(RefreshToken refreshToken, CancellationToken cancellationToken)
 => Task.FromResult(SearchResult.Empty <IPackageSearchMetadata>());