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 ); } }
/// <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); } }
/// <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); } }
/// <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); } }
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 ); } }