/// <summary>Creates service definition that can be registered with a server</summary> /// <param name="serviceImpl">An object implementing the server-side handling logic.</param> public static grpc::ServerServiceDefinition BindService(OrderCommandBase serviceImpl) { return(grpc::ServerServiceDefinition.CreateBuilder() .AddMethod(__Method_SubmitOrder, serviceImpl.SubmitOrder) .AddMethod(__Method_CancelOrder, serviceImpl.CancelOrder).Build()); }
/// <summary> /// Performs the tasks needed to process a dead letter message observed on one of the /// command queues. /// </summary> /// /// <param name="deadLetterLocation">The name of the location where the dead letter message was observed. For example, the process-order queue.</param> /// <param name="deadLetterMessage">The brokered message containing the command to process.</param> /// protected internal virtual async Task ProcessDeadLetterMessage(string deadLetterLocation, BrokeredMessage deadLetterMessage) { if (deadLetterMessage == null) { this.Log.Error("The {CommandType} brokered message was null.", "DeadLetter"); throw new ArgumentNullException(nameof(deadLetterMessage)); } ILogger log = null; OrderCommandBase command = null; try { // Attempt to retrieve the command from the brokered message. using (var bodyStream = deadLetterMessage.GetBody <Stream>()) using (var reader = new StreamReader(bodyStream)) using (var jsonReader = new JsonTextReader(reader)) { command = this.jsonSerializer.Deserialize <OrderCommandBase>(jsonReader); jsonReader.Close(); reader.Close(); bodyStream.Close(); }; // If the body was not a proper OrderCommandBase command, an empty command will be // returned. Verify that the Id and OrderId are not in their default states. if ((command.Id == Guid.Empty) && (command.OrderId == null)) { throw new MissingDependencyException("The command could not be extracted from the brokered message"); } // Process the order identified by the command. var correlationId = command.CorrelationId ?? deadLetterMessage.CorrelationId; log = this.Log.WithCorrelationId(correlationId); log.Information("A {CommandType} command was found on the dead letter {DeadLetterLocation} and notification is being sent. {Command}", nameof(OrderCommandBase), deadLetterLocation, command); var result = await this.notifier.NotifyDeadLetterMessageAsync(deadLetterLocation, command.PartnerCode, command.OrderId, correlationId); // Consider the result to complete handling. If processing was successful, then the message should be completed to // ensure that it is not retried. if (result?.Outcome == Outcome.Success) { await this.CompleteMessageAsync(deadLetterMessage); var sentEvent = (command?.CreateNewOrderEvent <NotificationSent>() ?? new NotificationSent { Id = Guid.NewGuid(), CorrelationId = correlationId, OccurredTimeUtc = this.clock.GetCurrentInstant().ToDateTimeUtc() }); this.eventPublisher.TryPublishAsync(sentEvent).FireAndForget(); log.Information("Notification was successfully sent for the {CommandType} found on the dead letter {DeadLetterLocation}. The fatal notification was sent for order {Partner}//{Order}.", nameof(NotifyOfFatalFailure), deadLetterLocation, command.PartnerCode, command.OrderId); } else { // Submission was not successful. Throw an exception to signal failure to the WebJob infrastructure // so that the queue message is retried. // // We should be applying better retry logic and requeuing the message on a delay for a backoff. This is a future // enhancement. log.Warning("Notification was not sent for the {CommandType} found on the dead letter {DeadLetterLocation}. The message was handled successfully, but the sending failed", nameof(NotifyOfFatalFailure), deadLetterLocation); throw new FailedtoHandleCommandException(); } } catch (Exception ex) when(!(ex is FailedtoHandleCommandException)) { (log ?? this.Log).Error(ex, "An exception occurred while sending notification for the {CommandType} found on the dead letter {DeadLetterLocation}.", nameof(NotifyOfFatalFailure), deadLetterLocation); var failedEvent = (command?.CreateNewOrderEvent <NotificationFailed>() ?? new NotificationFailed { Id = Guid.NewGuid(), CorrelationId = deadLetterMessage?.CorrelationId, OccurredTimeUtc = this.clock.GetCurrentInstant().ToDateTimeUtc() }); this.eventPublisher.TryPublishAsync(failedEvent).FireAndForget(); // The exception must bubble in order for the WebJob to mark the failure and perform the actions needed // to fail the message so that it retries. throw; } }