/// <remarks>
        /// If the event store is deployed as a cluster, it may be possible to get transient read failures.
        /// If an event could not be found on the event stream, try again.
        ///</remarks>
        private async Task ProcessSingleMessageAsync(string stream, Type eventType, IEnumerable handlers, BasicEventInfo eventInfo, string subscriberId)
        {
            var maxAttempts = Math.Max(_eventNotFoundRetryCount, 1);

            object @event = null;
            for (var i = 0; i < maxAttempts; i++)
            {
                if (i > 0)
                {
                    await Task.Delay(_eventNotFoundRetryDelay);
                }   

                try
                {
                    Log.Debug(_log, "{0}|{1}: Processing event {2}. Attempt: {3}", stream, subscriberId ?? "default", eventInfo.Id, i + 1);

                    @event = await _connection.ReadEventBodyAsync(eventType, eventInfo.CanonicalEventLink);
                    if (@event != null)
                    {
                        break;
                    }
                }
                catch (EventNotFoundException ex)
                {
                    if (i < maxAttempts - 1)
                    {
                        Log.Warning(_log, "{0}|{1}: Event could not be found. Attempting to process the event again. {2}: {3}", stream, subscriberId ?? "default", eventInfo.Id, ex.Message);
                    }
                    else
                    {
                        Log.Error(_log, "{0}|{1}: Event could not be found after {2} attempts. {3}: {4}", stream, subscriberId ?? "default", i + 1, eventInfo.Id, ex.Message);
                        return;
                    }
                }
                catch (Exception ex)
                {
                    Log.Error(_log, "{0}|{1}: Error getting message {2}: {3}", stream, subscriberId ?? "default", eventInfo.Id, ex);
                    return;
                }
            }

            try
            {
                await InvokeMessageHandlersForEventMessageAsync(stream, eventType, handlers, @event, eventInfo);
            }
            catch (Exception ex)
            {
                Log.Error(_log, "{0}|{1}: Error invoking message handlers for message {2}: {3}", stream, subscriberId ?? "default", eventInfo.Id, ex);
            }
        }
        public async Task<IDictionary<Type, Exception>> InvokeMessageHandlersForEventMessageAsync(string stream, Type eventType, IEnumerable handlers, object @event, BasicEventInfo eventInfo)
        {
            var handlerCount = 0;
            var eventTitle = eventInfo.Title;
            var updated = eventInfo.Updated;
            var errors = new Dictionary<Type, Exception>();
            foreach (var handler in handlers)
            {
                handlerCount++;
                var handlerType = handler.GetType();

                var handleMethod = GetMethodFromHandler(handlerType, eventType, "Handle");

                if (handleMethod == null)
                {
                    Log.Warning(_log, "Could not find the handle method for: {0}", handlerType.FullName);
                    continue;
                }

                try
                {
                    try
                    {
                        var arguments = new[] {@event, eventInfo}.Take(handleMethod.GetParameters().Length);
                        await (Task)handleMethod.Invoke(handler, arguments.ToArray());
                    }
                    catch (Exception invokeException)
                    {
                        errors[handlerType] = invokeException.InnerException;

                        var errorMessage = string.Format("{0} thrown processing event {1}",
                            invokeException.GetType().FullName, eventTitle);
                        Log.Error(_log, errorMessage, invokeException);

                        var errorMethod = GetMethodFromHandler(handlerType, eventType, "OnError");
                        errorMethod.Invoke(handler, new[] { invokeException, @event });
                    }
                }
                catch (Exception errorHandlingException)
                {
                    var errorMessage = string.Format("{0} thrown whilst handling error from event {1}",
                        errorHandlingException.GetType().FullName, eventTitle);
                    Log.Error(_log, errorMessage, errorHandlingException);
                }
            }

            _performanceMonitors.AsParallel().ForAll(x => x.Accept(stream, eventType.FullName, updated, handlerCount, errors));

            return errors;
        }
 public async Task InvokeMessageHandlersForStreamMessageAsync(string stream, Type eventType, IEnumerable handlers, BasicEventInfo eventInfo)
 {
     var @event = await _connection.ReadEventBodyAsync(eventType, eventInfo.CanonicalEventLink);
     await InvokeMessageHandlersForEventMessageAsync(stream, eventType, handlers, @event, eventInfo);
 }