private async Task ProcessEventsAsync(OperationContext context, List <EventData> messages) { // Tracking raw messages count. Counters[ReceivedEventHubEventsCount].Add(messages.Count); CacheActivityTracker.AddValue(CaSaaSActivityTrackingCounters.ReceivedEventHubMessages, messages.Count); // Creating nested context for all the processing operations. context = context.CreateNested(nameof(EventHubContentLocationEventStore)); if (messages.Count == 0) { // This probably does not actually occur, but just in case, ignore empty message batch. // NOTE: We do this after logging to ensure we notice if the we are getting empty message batches. return; } var state = new SharedEventProcessingState(context, this, messages); if (_eventProcessingBlocks != null) { await context .CreateOperation(Tracer, () => sendToActionBlockAsync(state)) .WithOptions(traceOperationStarted: false, endMessageFactory: r => $"TotalQueueSize={Interlocked.Read(ref _queueSize)}") .RunAsync(caller: "SendToActionBlockAsync") .TraceIfFailure(context); } else { await ProcessEventsCoreAsync(new ProcessEventsInput(state, messages, actionBlockIndex : -1, store : this), EventDataSerializer); } async Task <BoolResult> sendToActionBlockAsync(SharedEventProcessingState st) { // This local function "sends" a message into an action block based on the sender's hash code to process events in parallel from different machines. // (keep in mind, that the data from the same machine should be processed sequentially, because events order matters). // Then, it creates a local counter for each processing operation to track the results for the entire batch. foreach (var messageGroup in messages.GroupBy(GetProcessingIndex)) { int actionBlockIndex = messageGroup.Key; var eventProcessingBlock = _eventProcessingBlocks ![actionBlockIndex]; var input = new ProcessEventsInput(st, messageGroup, actionBlockIndex, this); var sendAsyncTask = eventProcessingBlock.SendAsync(input); if (sendAsyncTask.Status == TaskStatus.WaitingForActivation) { // The action block is busy. It means that its most likely full. Tracer.Debug(context, $"Action block {actionBlockIndex} is busy. Block's queue size={eventProcessingBlock.InputCount}."); } bool success = await sendAsyncTask; if (!success) { // NOTE: This case should not actually occur. // Complete the operation in case we couldn't send to the action block to prevent pending event queue from getting backlogged. input.Complete(); return(new BoolResult("Failed to add message to an action block.")); } }
private async Task ProcessEventsAsync(OperationContext context, List <EventData> messages) { // Creating nested context for all the processing operations. context = context.CreateNested(); string asyncProcessing = _eventProcessingBlocks != null ? "on" : "off"; Tracer.Info(context, $"{Tracer.Name}: Received {messages.Count} events from Event Hub. Async processing is '{asyncProcessing}'."); if (messages.Count == 0) { // This probably does not actually occur, but just in case, ignore empty message batch. // NOTE: We do this after logging to ensure we notice if the we are getting empty message batches. return; } var state = new SharedEventProcessingState(context, this, messages); if (_eventProcessingBlocks != null) { // Creating nested context to correlate all the processing operations. context = context.CreateNested(); await context.PerformOperationAsync( Tracer, () => sendToActionBlockAsync(), traceOperationStarted : false).TraceIfFailure(context); } else { await ProcessEventsCoreAsync(new ProcessEventsInput(state, messages), EventDataSerializer, index : 0); } async Task <SendToActionBlockResult> sendToActionBlockAsync() { // This local function "sends" a message into an action block based on the sender's hash code to process events in parallel from different machines. // (keep in mind, that the data from the same machine should be processed sequentially, because events order matters). // Then, it creates a local counter for each processing operation to track the results for the entire batch. var operationTasks = new List <Task <OperationCounters> >(); foreach (var messageGroup in messages.GroupBy(GetProcessingIndex)) { var eventProcessingBlock = _eventProcessingBlocks[messageGroup.Key]; var input = new ProcessEventsInput(state, messageGroup); bool success = await eventProcessingBlock.SendAsync(input); if (!success) { // NOTE: This case should not actually occur. // Complete the operation in case we couldn't send to the action block to prevent pending event queue from getting backlogged. input.Complete(); return(new SendToActionBlockResult("Failed to add message to an action block.")); } } return(new SendToActionBlockResult(operationTasks)); } }