Ejemplo n.º 1
0
        private async Task <IndexStatus> GetIndexStatusAsync(ISearchIndexClientWrapper index)
        {
            var documentCountResult = await Measure.DurationWithValueAsync(() => index.Documents.CountAsync());

            _telemetryService.TrackDocumentCountQuery(index.IndexName, documentCountResult.Value, documentCountResult.Duration);

            var lastCommitTimestampParameters = _parametersBuilder.LastCommitTimestamp();
            var lastCommitTimestampResult     = await Measure.DurationWithValueAsync(() => index.Documents.SearchAsync("*", lastCommitTimestampParameters));

            var lastCommitTimestamp = lastCommitTimestampResult
                                      .Value?
                                      .Results?
                                      .FirstOrDefault()?
                                      .Document[IndexFields.LastCommitTimestamp] as DateTimeOffset?;

            _telemetryService.TrackLastCommitTimestampQuery(index.IndexName, lastCommitTimestamp, lastCommitTimestampResult.Duration);

            var warmQueryDuration = await Measure.DurationAsync(() => index.Documents.SearchAsync("*", new SearchParameters()));

            _telemetryService.TrackWarmQuery(index.IndexName, warmQueryDuration);

            return(new IndexStatus
            {
                DocumentCount = documentCountResult.Value,
                DocumentCountDuration = documentCountResult.Duration,
                Name = index.IndexName,
                WarmQueryDuration = warmQueryDuration,
                LastCommitTimestamp = lastCommitTimestamp,
                LastCommitTimestampDuration = lastCommitTimestampResult.Duration,
            });
        }
Ejemplo n.º 2
0
 public AzureSearchService(
     IIndexOperationBuilder operationBuilder,
     ISearchIndexClientWrapper searchIndex,
     ISearchIndexClientWrapper hijackIndex,
     ISearchResponseBuilder responseBuilder,
     IAzureSearchTelemetryService telemetryService)
 {
     _operationBuilder = operationBuilder ?? throw new ArgumentNullException(nameof(operationBuilder));
     _searchIndex      = searchIndex ?? throw new ArgumentNullException(nameof(searchIndex));
     _hijackIndex      = hijackIndex ?? throw new ArgumentNullException(nameof(hijackIndex));
     _responseBuilder  = responseBuilder ?? throw new ArgumentNullException(nameof(responseBuilder));
     _telemetryService = telemetryService ?? throw new ArgumentNullException(nameof(telemetryService));
 }
Ejemplo n.º 3
0
        private async Task <List <string> > PushBatchesAsync(
            ISearchIndexClientWrapper indexClient,
            Queue <IdAndValue <IndexAction <KeyedDocument> > > actions,
            bool onlyFull)
        {
            var failedPackageIds = new List <string>();

            while ((onlyFull && actions.Count >= _options.Value.AzureSearchBatchSize) ||
                   (!onlyFull && actions.Count > 0))
            {
                var allFinished = new List <IdAndValue <ResultAndAccessCondition <VersionListData> > >();
                var batch       = new List <IndexAction <KeyedDocument> >();

                while (batch.Count < _options.Value.AzureSearchBatchSize && actions.Count > 0)
                {
                    var idAndValue = DequeueAndDecrement(actions, out int newCount);
                    batch.Add(idAndValue.Value);

                    if (newCount == 0)
                    {
                        allFinished.Add(NewIdAndValue(idAndValue.Id, _versionListDataResults[idAndValue.Id]));
                        Guard.Assert(_versionListDataResults.Remove(idAndValue.Id), "The version list data result should have existed.");
                    }
                }

                await IndexAsync(indexClient, batch);

                if (allFinished.Any())
                {
                    failedPackageIds.AddRange(await UpdateVersionListsAsync(allFinished));
                }
            }

            Guard.Assert(
                !_versionListDataResults
                .Keys
                .Except(_idReferenceCount.Keys)
                .Any(),
                "There are some version list data results without reference counts.");
            Guard.Assert(
                !_idReferenceCount
                .Keys
                .Except(_versionListDataResults.Keys)
                .Any(),
                "There are some reference counts without version list data results.");

            return(failedPackageIds);
        }
Ejemplo n.º 4
0
 public SearchStatusService(
     ISearchIndexClientWrapper searchIndex,
     ISearchIndexClientWrapper hijackIndex,
     ISearchParametersBuilder parametersBuilder,
     IAuxiliaryDataCache auxiliaryDataCache,
     ISecretRefresher secretRefresher,
     IOptionsSnapshot <SearchServiceConfiguration> options,
     IAzureSearchTelemetryService telemetryService,
     ILogger <SearchStatusService> logger)
 {
     _searchIndex        = searchIndex ?? throw new ArgumentNullException(nameof(searchIndex));
     _hijackIndex        = hijackIndex ?? throw new ArgumentNullException(nameof(hijackIndex));
     _parametersBuilder  = parametersBuilder ?? throw new ArgumentNullException(nameof(parametersBuilder));
     _auxiliaryDataCache = auxiliaryDataCache ?? throw new ArgumentNullException(nameof(auxiliaryDataCache));
     _secretRefresher    = secretRefresher ?? throw new ArgumentNullException(nameof(secretRefresher));
     _options            = options ?? throw new ArgumentNullException(nameof(options));
     _telemetryService   = telemetryService ?? throw new ArgumentNullException(nameof(telemetryService));
     _logger             = logger ?? throw new ArgumentNullException(nameof(logger));
 }
Ejemplo n.º 5
0
        public BatchPusher(
            ISearchIndexClientWrapper searchIndexClient,
            ISearchIndexClientWrapper hijackIndexClient,
            IVersionListDataClient versionListDataClient,
            IOptionsSnapshot <AzureSearchJobConfiguration> options,
            IOptionsSnapshot <AzureSearchJobDevelopmentConfiguration> developmentOptions,
            IAzureSearchTelemetryService telemetryService,
            ILogger <BatchPusher> logger)
        {
            _searchIndexClient     = searchIndexClient ?? throw new ArgumentNullException(nameof(searchIndexClient));
            _hijackIndexClient     = hijackIndexClient ?? throw new ArgumentNullException(nameof(hijackIndexClient));
            _versionListDataClient = versionListDataClient ?? throw new ArgumentNullException(nameof(versionListDataClient));
            _options            = options ?? throw new ArgumentNullException(nameof(options));
            _developmentOptions = developmentOptions ?? throw new ArgumentNullException(nameof(developmentOptions));
            _telemetryService   = telemetryService ?? throw new ArgumentNullException(nameof(telemetryService));
            _logger             = logger ?? throw new ArgumentNullException(nameof(logger));
            _idReferenceCount   = new Dictionary <string, int>(StringComparer.OrdinalIgnoreCase);

            _searchActions          = new Queue <IdAndValue <IndexAction <KeyedDocument> > >();
            _hijackActions          = new Queue <IdAndValue <IndexAction <KeyedDocument> > >();
            _versionListDataResults = new Dictionary <string, ResultAndAccessCondition <VersionListData> >();

            if (_options.Value.MaxConcurrentVersionListWriters <= 0)
            {
                throw new ArgumentOutOfRangeException(
                          nameof(options),
                          $"The {nameof(AzureSearchJobConfiguration.MaxConcurrentVersionListWriters)} must be greater than zero.");
            }

            if (_options.Value.AzureSearchBatchSize <= 0)
            {
                throw new ArgumentOutOfRangeException(
                          nameof(options),
                          $"The {nameof(AzureSearchJobConfiguration.AzureSearchBatchSize)} must be greater than zero.");
            }
        }
Ejemplo n.º 6
0
        private async Task IndexAsync(
            ISearchIndexClientWrapper indexClient,
            IReadOnlyCollection <IndexAction <KeyedDocument> > batch)
        {
            if (batch.Count == 0)
            {
                return;
            }

            if (batch.Count > _options.Value.AzureSearchBatchSize)
            {
                throw new ArgumentException("The provided batch is too large.");
            }

            _logger.LogInformation(
                "Pushing batch of {BatchSize} to index {IndexName}.",
                batch.Count,
                indexClient.IndexName);

            IList <IndexingResult> indexingResults = null;
            Exception innerException = null;
            var       stopwatch      = Stopwatch.StartNew();

            try
            {
                var batchResults = await indexClient.Documents.IndexAsync(new IndexBatch <KeyedDocument>(batch));

                indexingResults = batchResults.Results;

                stopwatch.Stop();
                _telemetryService.TrackIndexPushSuccess(indexClient.IndexName, batch.Count, stopwatch.Elapsed);
            }
            catch (IndexBatchException ex)
            {
                stopwatch.Stop();
                _telemetryService.TrackIndexPushFailure(indexClient.IndexName, batch.Count, stopwatch.Elapsed);

                _logger.LogError(
                    0,
                    ex,
                    "An exception was thrown while sending documents to index {IndexName}.",
                    indexClient.IndexName);
                indexingResults = ex.IndexingResults;
                innerException  = ex;
            }
            catch (CloudException ex) when(ex.Response.StatusCode == HttpStatusCode.RequestEntityTooLarge && batch.Count > 1)
            {
                stopwatch.Stop();
                _telemetryService.TrackIndexPushSplit(indexClient.IndexName, batch.Count);

                var halfCount = batch.Count / 2;
                var halfA     = batch.Take(halfCount).ToList();
                var halfB     = batch.Skip(halfCount).ToList();

                _logger.LogWarning(
                    0,
                    ex,
                    "The request body for a batch of {BatchSize} was too large. Splitting into two batches of size " +
                    "{HalfA} and {HalfB}.",
                    batch.Count,
                    halfA.Count,
                    halfB.Count);

                await IndexAsync(indexClient, halfA);
                await IndexAsync(indexClient, halfB);
            }

            if (indexingResults != null)
            {
                const int errorsToLog = 5;
                var       errorCount  = 0;
                foreach (var result in indexingResults)
                {
                    if (!result.Succeeded)
                    {
                        if (errorCount < errorsToLog)
                        {
                            _logger.LogError(
                                "Indexing document with key {Key} failed for index {IndexName}. {StatusCode}: {ErrorMessage}",
                                result.Key,
                                indexClient.IndexName,
                                result.StatusCode,
                                result.ErrorMessage);
                        }

                        errorCount++;
                    }
                }

                if (errorCount > 0)
                {
                    _logger.LogError(
                        "{ErrorCount} errors were found when indexing a batch for index {IndexName}. {LoggedErrors} were logged.",
                        errorCount,
                        indexClient.IndexName,
                        Math.Min(errorCount, errorsToLog));
                    throw new InvalidOperationException(
                              $"Errors were found when indexing a batch. Up to {errorsToLog} errors get logged.",
                              innerException);
                }
            }
        }