예제 #1
0
        private async Task <bool> ExecuteConcrete(ConsumableEvent ce)
        {
            ConsumeResult result       = null;
            bool          mustRollback = false;

            try
            {
                LogTrace("Processing event with id {Id} and functional key {FunctionalKey}.",
                         ce.Id, ce.FunctionalKey != null ? ce.FunctionalKey : "n/a");
                result = await _consumeAction(ce).ConfigureAwait(false);
            }
            catch (Exception procEx)
            {
                // Unhandled exception is translated to Failed
                result = ConsumeResult.Failed(procEx.ToString());
            }

            LogTrace("Processing done for event with id {Id} and functional key {FunctionalKey}. Result: {result}",
                     ce.Id, ce.FunctionalKey != null ? ce.FunctionalKey : "n/a", result);

            switch (result.ResultType)
            {
            case ConsumeResultType.Succeeded:
            {
                bool markedConsumed = false;

                try
                {
                    await _eventConsumer.MarkConsumedAsync(ce.Id, ce.DeliveryKey).ConfigureAwait(false);

                    markedConsumed = true;
                    LogTrace("Event consumption succeeded for event with id {Id} and functional key {FunctionalKey}.",
                             ce.Id, ce.FunctionalKey != null ? ce.FunctionalKey : "n/a");
                }
                catch (Exception ex)
                {
                    LogError("Failed to mark event consumed with id {Id} and functional key {FunctionalKey}, cause event to be processes again! Details: {Exception}.",
                             ce.Id, ce.FunctionalKey != null ? ce.FunctionalKey : "n/a", ex);
                    // mustRollback doesn't actually need to be set, since markedConsumed will no longer be true
                    mustRollback = true;
                }

                if (!markedConsumed)
                {
                    mustRollback = true;
                }
            }
            break;

            case ConsumeResultType.Failed:
            {
                mustRollback = true;
                LogError("Exception occurred while processing event with id {Id} and functional key {FunctionalKey}: {Reason}.",
                         ce.Id, ce.FunctionalKey != null ? ce.FunctionalKey : "n/a", result.Reason);

                try
                {
                    await _eventConsumer.MarkFailedAsync(ce.Id, ce.DeliveryKey, Reason.Other(result.Reason)).ConfigureAwait(false);
                }
                catch (Exception) { }         // Swallow, since it's not a disaster if this gets processed again
            }
            break;

            case ConsumeResultType.MustSuspend:
            {
                mustRollback = true;
                var suspendedUntilUtc = this.Suspend(result.SuspendDuration.GetValueOrDefault(TimeSpan.FromSeconds(60)));
                LogError("Event consumption failed for event with id {Id} and functional key {FunctionalKey}. Processing should (continue to) suspend. Details: {Reason}.",
                         ce.Id, ce.FunctionalKey != null ? ce.FunctionalKey : "n/a", result.Reason);
            }
            break;

            case ConsumeResultType.MustRetry:
            {
                mustRollback = true;
                LogWarning("Retry requested while processing event with id {Id} and functional key {FunctionalKey}: {Reason}.",
                           ce.Id, ce.FunctionalKey != null ? ce.FunctionalKey : "n/a", result.Reason);  // Logged as warning, since a requested retry is not (yet) an error
            }
            break;

            default:
                // No special handling for other cases
                break;
            }

            return(result.ResultType == ConsumeResultType.Succeeded && !mustRollback);
        }
예제 #2
0
        private async Task <bool> ExecuteConcrete(IEnumerable <ConsumableEvent> ces)
        {
            IDictionary <Int64, ConsumeResult> result = null;

            try
            {
                LogTrace("Processing batch of {batchSize} events", ces.Count());
                result = await _consumeBatchAction(ces).ConfigureAwait(false);

                if (result == null)
                {
                    throw new InvalidOperationException("consumeBatchAction returned invalid result (null)");
                }
            }
            catch (Exception procEx)
            {
                // Unhandled exception is translated to Failed for the whole batch
                result = ces.ToDictionary(ce => ce.Id, ce => ConsumeResult.Failed(procEx.ToString()));
            }

            var succeededResults = result
                                   .Where(r => r.Value.ResultType == ConsumeResultType.Succeeded)
                                   .Select(r => ces.Single(ce => ce.Id == r.Key))
                                   .Cast <ConsumableEventId>();

            if (succeededResults.Count() > 0)
            {
                LogTrace("Event consumption succeeded for {succeededCount} event(s) in the batch of {totalCount}.",
                         succeededResults.Count(), ces.Count());

                try
                {
                    await _eventConsumer.MarkConsumedAsync(succeededResults, transactional : !_subscription.Ordered).ConfigureAwait(false); // Transactional is not required when processing is ordered: the events will be retried and cannot be overtaken.
                }
                catch (Exception ex)
                {
                    LogError("Failed to mark set of {succeededCount} consumed, cause event(s) to be processes again! Details: {Exception}.",
                             succeededResults.Count(), ex);
                }
            }

            var failedResults = result
                                .Where(r => r.Value.ResultType == ConsumeResultType.Failed)
                                .Select(r => new
            {
                ConsumableEvent = ces.Single(ce => ce.Id == r.Key),
                ConsumeResult   = r.Value,
            });

            if (failedResults.Count() > 0)
            {
                foreach (var failedResult in failedResults)
                {
                    LogError("Exception occurred while processing event with id {Id} and functional key {FunctionalKey}: {Reason}.",
                             failedResult.ConsumableEvent.Id, failedResult.ConsumableEvent.FunctionalKey != null ? failedResult.ConsumableEvent.FunctionalKey : "n/a", failedResult.ConsumeResult.Reason);
                    try
                    {
                        await _eventConsumer.MarkFailedAsync(failedResult.ConsumableEvent.Id, failedResult.ConsumableEvent.DeliveryKey, Reason.Other(failedResult.ConsumeResult.Reason));
                    }
                    catch (Exception) { } // Swallow, since it's not a disaster if this gets processed again
                }
            }

            var suspendResultsCount = result
                                      .Where(r => r.Value.ResultType == ConsumeResultType.MustSuspend)
                                      .Count();

            if (suspendResultsCount > 0)
            {
                var firstSuspendResult = result.First(r => r.Value.ResultType == ConsumeResultType.MustSuspend);
                var suspendedUntilUtc  = this.Suspend(firstSuspendResult.Value.SuspendDuration.GetValueOrDefault(TimeSpan.FromSeconds(60)));
                LogError("Event consumption failed for {suspendResultsCount} event(s). Processing should (continue to) suspend. Details: {Reason}.",
                         suspendResultsCount, firstSuspendResult.Value.Reason);
            }

            var retryResultsCount = result
                                    .Where(r => r.Value.ResultType == ConsumeResultType.MustRetry)
                                    .Count();

            if (retryResultsCount > 0)
            {
                var firstRetryResult = result.First(r => r.Value.ResultType == ConsumeResultType.MustRetry);
                LogWarning("Retry requested for {retryResultsCount} event(s). Reason for first event: {Reason}.",
                           retryResultsCount, firstRetryResult.Value.Reason); // Logged as warning, since a requested retry is not (yet) an error
            }

            return(succeededResults.Count() < ces.Count());  // False when not all have succeeded
        }