Example #1
0
        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."));
                    }
                }
Example #2
0
        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));
            }
        }