예제 #1
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);
                }
            }
        }