public Task ExecuteAsync(
     ProcessingContext context,
     byte[] payload,
     IDictionary<string, string> properties)
 {
     return this.onExecute(payload, properties);
 }
 public Task ExecuteAsync(
     ProcessingContext context,
     byte[] payload,
     IDictionary<string, string> properties)
 {
     throw new NotImplementedException();
 }
 public async Task ExecuteAsync(
     ProcessingContext context, 
     byte[] payload,
     IDictionary<string, string> properties)
 {
     Logger.Info("Long running handler for {0}", _duration);
     await Task.Delay(_duration);
 }
 public Task ExecuteAsync(
     ProcessingContext context,
     byte[] payload, 
     IDictionary<string, string> properties)
 {
     Logger.Info("Intentionally throwing exception.");
     throw new JustForTestingException();
 }
 public async Task ExecuteAsync(
     ProcessingContext context, 
     byte[] payload,
     IDictionary<string, string> properties)
 {
     // This handler is intentionally imcomplete.
     // In an actual handler, we would use the provided data
     // to retrieve and update the state of a particular
     // vehicle.
     await Task.Delay(TimeSpan.FromMilliseconds(50));
 }
        public async Task WhenHandlingAnything_ThenThrowsWellKnownException()
        {
            var handler = new ThrowsExceptionHandler();

            var body = new byte[] { };
            var headers = new Dictionary<string, string>();
            var context = new ProcessingContext("", "", "");

            await AssertExt.ThrowsAsync<JustForTestingException>(
                () => handler.ExecuteAsync(context, body, headers)
                );
        }
        public async Task WhenHandlingAnything_ThenDelaysTheSpecifiedDuration()
        {
            var duration = TimeSpan.FromSeconds(2);

            var handler = new LongRunningHandler(duration);

            var context = new ProcessingContext("", "", "");
            var body = new byte[] { };
            var headers = new Dictionary<string, string>();

            await AssertExt.TaskRanForAtLeast(() => handler.ExecuteAsync(context, body, headers), duration);
        }
        public async Task ExecuteAsync(
            ProcessingContext context,
            byte[] payload,
            IDictionary<string, string> properties)
        {
            var headers = properties
                .Select(p => string.Format("{0}:{1}\n", p.Key, p.Value))
                .ToArray();
                
            Logger.Warning(
                "No handler was found for {0}. The event contained the following properties:\n {1}",
                context,
                string.Join(Environment.NewLine, headers));

            await _handler
                .PublishAsync(FailureMode.UnknownPayload, context, payload, properties)
                .ConfigureAwait(false);
        }
        public static async Task LogHandlerTimeout(this ILogger logger,
            IMessageHandler handler,
            ProcessingContext context,
            byte[] payload,
            IDictionary<string, string> properties)
        {
            logger.Warning(
                "The handler, {0}, has exceeded the expected duration ({1}) for {2}.",
                handler.Name,
                handler.Timeout,
                context
            );

            await Handler.Value.PublishAsync(
                FailureMode.Timeout, 
                context,
                payload,
                properties);
        }
        public Task PublishAsync(
            FailureMode failureMode,
            ProcessingContext context,
            byte[] payload,
            IDictionary<string, string> properties,
            Exception ex = null)
        {
            _messages.Add(new PoisonMessage()
            {
                Mode = failureMode,
                Payload = payload,
                Properties = properties,
                ExceptionObject = ex
            });
            Console.WriteLine("Writing message to mock poison queue.. count = {0}",
                _messages.Count);
            Thread.Sleep(1);

            return _onPublishAsync(failureMode, payload, properties, ex);
        }
        public static async Task HandlerThrewException(this ILogger log,
                                                       string handlerName,
                                                       ProcessingContext context,
                                                       byte[] payload,
                                                       IDictionary <string, string> properties,
                                                       Exception ex)

        {
            log.Warning(
                "The handler, {0}, has threw an exception for {1}: {2}",
                handlerName,
                context,
                ex
                );

            await Handler.Value.PublishAsync(
                FailureMode.Error,
                context,
                payload,
                properties,
                ex
                );
        }
        public static async Task HandlerThrewException(this ILogger log,
            string handlerName,
            ProcessingContext context,
            byte[] payload,
            IDictionary<string, string> properties,            
            Exception ex)

        {
            log.Warning(
                "The handler, {0}, has threw an exception for {1}: {2}",
                handlerName,
                context,
                ex
            );

            await Handler.Value.PublishAsync( 
                FailureMode.Error,
                context,
                payload,
                properties,       
                ex
            );
        }
        public async Task PublishAsync(
            FailureMode failureMode, 
            ProcessingContext context, 
            byte[] payload,
            IDictionary<string, string> properties, 
            Exception ex = null)
        {
            var blobMetaData = new Dictionary<string, string>();

            // Construct a name for the blob that helps communicate 
            // the nature of the failure. We append a Guid in order
            // to avoid any naming collisions.
            var blobName = string.Concat(
                failureMode,
                "/",
                context.EventHubName,
                "/",
                context.PartitionId,
                "/",
                context.EventDataOffset,
                "_",
                Guid.NewGuid().ToString("N"));

            try
            {
                var blob = _blobContainer.GetBlockBlobReference(blobName);

                // Add the property dictionary as a JSON object
                var messageProperties = JsonConvert.SerializeObject(properties);
                blobMetaData.Add("messageProperties", messageProperties);

                // Upload the blob and properties
                await blob.UploadFromByteArrayAsync(payload, 0, payload.Length);
                foreach (var p in blobMetaData)
                {
                    blob.Metadata.Add(p.Key, p.Value);
                }
                await blob.SetMetadataAsync();

                // If the message has an attached exception publish as an associated blob
                if (ex != null)
                {
                    var blobEx = _blobContainer.GetBlockBlobReference(blobName + "_exception");
                    await blobEx.UploadTextAsync(ex.ToString());
                }
            }
            catch (Exception ex0)
            {
                var sb = new StringBuilder();
                foreach (var pair in blobMetaData)
                {
                    sb.AppendFormat("{0}:{1}", pair.Key, pair.Value);
                    sb.AppendLine();
                }

                Logger.Error(ex0,
                    "Could not publish poison message to blob container. Properties from message:" + sb
                    );
            }
        }
Example #14
0
        /// <summary>
        /// Process the message contained in the event data object, 
        /// handling timeouts, errors and telemetry
        /// </summary>
        private async Task ProcessAsync(EventData msg, string partitionId)
        {
            var context = new ProcessingContext(_eventHubName, partitionId, msg.Offset);

            Exception ex = null;
            string handlerName = "[handler not yet resolved]";
            byte[] content = EmptyContent;
            Dictionary<string, string> props = null;

            try
            {
                // The message stream can only be read once, extract the body up front
                content = msg.GetBytes();

                // if only some properties are used in the happy path but all are needed in some cases.
                props = msg.Properties.ToDictionary(
                    k => k.Key,
                    v => v.Value.ToString());

                // Promote standard EventHub properties into the property collection (for logging)
                var msgId = String.Concat(_eventHubName, "/", msg.PartitionKey, "/", msg.Offset);
                props.Add(Constants.ID, msgId);
                props.Add(Constants.MSG_OFFSET, msg.Offset);
                props.Add(Constants.PARTITION, msg.PartitionKey);

                // Look up the message type handler, using the payload and 
                // associated properties
                var handler = _handlerResolver.GetHandler(props, msgId);
                handlerName = handler.Name;

                // Create a task to track timeout
                var sw = Stopwatch.StartNew();
                var timeoutTask = Task.Delay(handler.Timeout);

                // Invoke the handler, using the continuation token to handle exceptions and
                // elapsed time logging.  Note: if this method throws synchronously (a misbehaving
                // handler) then we handle it in the catch statement below

                var handlerTask = handler
                    .ExecuteAsync(context, content, props)
                    .ContinueWith(async t => await HandleCompletionAsync(t, sw, handlerName, content, props, context).ConfigureAwait(false));

                _instrumentationPublisher.TaskStarted();
                _circuitBreaker.Increment();

                // Wait for either the timeout task or the handler to complete
                await Task.WhenAny(handlerTask, timeoutTask).ConfigureAwait(false);

                // If the message did not complete before the timeout, flag for further review
                if (handlerTask.Status != TaskStatus.RanToCompletion)
                {
                    props.Add("timeoutDuration", handler.Timeout.ToString());

                    _instrumentationPublisher.TimeoutOccured();
                    await Logger.LogHandlerTimeout(handler, context, content, props);
                }
            }
            catch (Exception ex0)
            {
                ex = ex0;
            }

            // Misbehaving handler threw a synchronous exception
            if (ex != null)
            {
                // We can't await in a catch block
                await Logger.HandlerThrewException(
                    handlerName,
                    context,
                    content,
                    props ?? new Dictionary<string, string>(),
                    ex);
            }
        }
Example #15
0
        /// <summary>
        /// Handle completion of the preceding task, including error logging,
        /// and timeout logging (poison publishing in case of a timeout message
        /// is handled in the main message flow).
        /// </summary>        
        private async Task HandleCompletionAsync(
            Task t,
            Stopwatch sw,
            string handlerName,
            byte[] content,
            IDictionary<string, string> props,
            ProcessingContext context)
        {
            sw.Stop();
            _instrumentationPublisher.TaskEnded();
            _instrumentationPublisher.MessageProcessed();
            _circuitBreaker.Decrement();

            // If the preceding task failed with an exception, flag the exception and
            // log the message body/properties 
            if (t.IsFaulted)
            {
                _instrumentationPublisher.TaskFaulted();
                await HandleExceptionAsync(handlerName, context, sw.Elapsed, content, props, t.Exception).ConfigureAwait(false);
            }
            // Otherwise log the execution time (if the message is in a timeout condition, 
            // it will be logged in the primary flow as a warning)
            else
            {
                Logger.TraceApi(
                    handlerName,
                    sw.Elapsed,
                    "{0}/{1}/OK",
                    context.PartitionId,
                    context.EventDataOffset);
            }
        }
Example #16
0
        /// <summary>
        /// Process the message contained in the event data object,
        /// handling timeouts, errors and telemetry
        /// </summary>
        private async Task ProcessAsync(EventData msg, string partitionId)
        {
            var context = new ProcessingContext(_eventHubName, partitionId, msg.Offset);

            Exception ex          = null;
            string    handlerName = "[handler not yet resolved]";

            byte[] content = EmptyContent;
            Dictionary <string, string> props = null;

            try
            {
                // The message stream can only be read once, extract the body up front
                content = msg.GetBytes();

                // if only some properties are used in the happy path but all are needed in some cases.
                props = msg.Properties.ToDictionary(
                    k => k.Key,
                    v => v.Value.ToString());

                // Promote standard EventHub properties into the property collection (for logging)
                var msgId = String.Concat(_eventHubName, "/", msg.PartitionKey, "/", msg.Offset);
                props.Add(Constants.ID, msgId);
                props.Add(Constants.MSG_OFFSET, msg.Offset);
                props.Add(Constants.PARTITION, msg.PartitionKey);

                // Look up the message type handler, using the payload and
                // associated properties
                var handler = _handlerResolver.GetHandler(props, msgId);
                handlerName = handler.Name;

                // Create a task to track timeout
                var sw          = Stopwatch.StartNew();
                var timeoutTask = Task.Delay(handler.Timeout);

                // Invoke the handler, using the continuation token to handle exceptions and
                // elapsed time logging.  Note: if this method throws synchronously (a misbehaving
                // handler) then we handle it in the catch statement below

                var handlerTask = handler
                                  .ExecuteAsync(context, content, props)
                                  .ContinueWith(async t => await HandleCompletionAsync(t, sw, handlerName, content, props, context).ConfigureAwait(false));

                _instrumentationPublisher.TaskStarted();
                _circuitBreaker.Increment();

                // Wait for either the timeout task or the handler to complete
                await Task.WhenAny(handlerTask, timeoutTask).ConfigureAwait(false);

                // If the message did not complete before the timeout, flag for further review
                if (handlerTask.Status != TaskStatus.RanToCompletion)
                {
                    props.Add("timeoutDuration", handler.Timeout.ToString());

                    _instrumentationPublisher.TimeoutOccured();
                    await Logger.LogHandlerTimeout(handler, context, content, props);
                }
            }
            catch (Exception ex0)
            {
                ex = ex0;
            }

            // Misbehaving handler threw a synchronous exception
            if (ex != null)
            {
                // We can't await in a catch block
                await Logger.HandlerThrewException(
                    handlerName,
                    context,
                    content,
                    props ?? new Dictionary <string, string>(),
                    ex);
            }
        }
Example #17
0
        private static async Task HandleExceptionAsync(
            string handlerName,
            ProcessingContext context,
            TimeSpan elapsed,
            byte[] content,
            IDictionary<string, string> props,
            Exception exception)
        {
            await Logger.HandlerThrewException(handlerName, context, content, props, exception);

            Logger.TraceApi(
                handlerName,
                elapsed,
                "{0}/{1}/ERR",
                context.PartitionId,
                context.EventDataOffset);
        }
        public async Task PublishAsync(
            FailureMode failureMode,
            ProcessingContext context,
            byte[] payload,
            IDictionary <string, string> properties,
            Exception ex = null)
        {
            var blobMetaData = new Dictionary <string, string>();

            // Construct a name for the blob that helps communicate
            // the nature of the failure. We append a Guid in order
            // to avoid any naming collisions.
            var blobName = string.Concat(
                failureMode,
                "/",
                context.EventHubName,
                "/",
                context.PartitionId,
                "/",
                context.EventDataOffset,
                "_",
                Guid.NewGuid().ToString("N"));

            try
            {
                var blob = _blobContainer.GetBlockBlobReference(blobName);

                // Add the property dictionary as a JSON object
                var messageProperties = JsonConvert.SerializeObject(properties);
                blobMetaData.Add("messageProperties", messageProperties);

                // Upload the blob and properties
                await blob.UploadFromByteArrayAsync(payload, 0, payload.Length);

                foreach (var p in blobMetaData)
                {
                    blob.Metadata.Add(p.Key, p.Value);
                }
                await blob.SetMetadataAsync();

                // If the message has an attached exception publish as an associated blob
                if (ex != null)
                {
                    var blobEx = _blobContainer.GetBlockBlobReference(blobName + "_exception");
                    await blobEx.UploadTextAsync(ex.ToString());
                }
            }
            catch (Exception ex0)
            {
                var sb = new StringBuilder();
                foreach (var pair in blobMetaData)
                {
                    sb.AppendFormat("{0}:{1}", pair.Key, pair.Value);
                    sb.AppendLine();
                }

                Logger.Error(ex0,
                             "Could not publish poison message to blob container. Properties from message:" + sb
                             );
            }
        }