private async Task FlushCurrentBufferAsync(int?ownedDocumentIndex) { IEnumerable <BulkInsertItemState> response = null; try { response = await Client.ExecuteStoredProcedureAsync <IEnumerable <BulkInsertItemState> >(storedProcedureLink, surrogate, Configuration.DisableIdGeneration); } catch (Exception exception) { // In case of a stored procedure failure - fail all items: sorry, it's a bulk operation :) FailAllActiveBatches(exception, ownedDocumentIndex.HasValue); // If executing as part of the write - rethrow, otherwise ignore - will be reported on the item's task if (ownedDocumentIndex.HasValue) { throw; } return; } // Clean-up the buffer, because as soon as we start reporting completion - more items will be added, and buffer is not thread-safe buffer.SkipForward(response.Count()); Exception ownedDocumentError = null; foreach (var item in response) { var itemError = String.IsNullOrEmpty(item.ErrorMessage) ? null : Errors.FailedToCreateDocument(item.ErrorMessage); TaskCompletionSource <object> waitHandle; if (!activeBulkItems.TryRemove(item.DocumentIndex, out waitHandle)) { // We are in the task that is responsible to report the state of the document, preserve the error if (item.DocumentIndex == ownedDocumentIndex && itemError != null) { ownedDocumentError = itemError; } continue; } if (itemError == null) { waitHandle.SetResult(null); } else { waitHandle.SetException(itemError); } } if (ownedDocumentError != null) { throw ownedDocumentError; } }
private async Task FlushWhileAsync(Func <bool> condition, bool lockAcquired, CancellationToken cancellationToken) { // NOTE: This task will run in the background, so we need to make sure it is robust enough try { while (condition()) { if (lockAcquired) { // Actual lock will be released in the finally lockAcquired = false; } else { await flushSemaphore.WaitAsync(cancellationToken); } IEnumerable <BulkInsertItemState> response; try { if (!condition()) { continue; } response = await FlushCurrentBufferAsync(); buffer.SkipForward(response.Count()); } finally { flushSemaphore.Release(); } ReportTasksStatus(response); } } finally { if (lockAcquired) { // If while condition was not satisfied - finally block did not execute, release the lock flushSemaphore.Release(); } } }