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, }); }
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)); }
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); }
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)); }
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."); } }
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); } } }