public async Task TestAzureServiceBusTransactionalOutboxWithJsonPayload() { //***************************************************************************************** //* STEP 1 - Prepare the Test Payload Item to be Published //***************************************************************************************** var testPayload = new { PublishTarget = TestConfiguration.AzureServiceBusTopic, To = "CajunCoding", FifoGroupingId = nameof(TestAzureServiceBusDirectPublishingAndReceiving), ContentType = MessageContentTypes.PlainText, Body = $"Testing publishing of Json Payload with PlainText Body for [{nameof(TestAzureServiceBusTransactionalOutboxWithJsonPayload)}]!", Headers = new { IntegrationTestName = nameof(TestAzureServiceBusDirectPublishingAndReceiving), IntegrationTestExecutionDateTime = DateTimeOffset.UtcNow } }; var jsonText = JsonConvert.SerializeObject(testPayload); //***************************************************************************************** //* STEP 2 - Store the Dynamic Payload into the Outbox! //***************************************************************************************** var sqlConnection = await SqlConnectionHelper.CreateMicrosoftDataSqlConnectionAsync(); await sqlConnection.TruncateTransactionalOutboxTableAsync(); var outboxItem = await sqlConnection .AddTransactionalOutboxPendingItemAsync(jsonText) .ConfigureAwait(false); //***************************************************************************************** //* STEP 3 - Now Publish the Outbox payloads (processing all pending items)... //***************************************************************************************** await using var azureServiceBusPublisher = new DefaultAzureServiceBusOutboxPublisher( TestConfiguration.AzureServiceBusConnectionString, new AzureServiceBusPublishingOptions() { LogDebugCallback = s => TestContext.WriteLine(s), ErrorHandlerCallback = e => TestContext.WriteLine(e.Message + e.InnerException?.Message) } ); var processedResults = await sqlConnection.ProcessPendingOutboxItemsAsync(azureServiceBusPublisher, new OutboxProcessingOptions() { FifoEnforcedPublishingEnabled = true, LogDebugCallback = s => TestContext.WriteLine(s), ErrorHandlerCallback = e => TestContext.WriteLine(e.Message + e.InnerException?.Message), MaxPublishingAttempts = 1, TimeSpanToLive = TimeSpan.FromMinutes(5) }); //***************************************************************************************** //* STEP 4 - Attempt to Retrieve/Receive the Message & Validate after Arrival! //***************************************************************************************** await AssertReceiptAndValidationOfThePublishedItem(outboxItem); }
public async Task TestAzureServiceBusDirectPublishingAndReceiving() { //***************************************************************************************** //* STEP 1 - Prepare the Test Outbox Item to be Published //***************************************************************************************** var testPayload = new { To = "CajunCoding", FifoGroupingId = nameof(TestAzureServiceBusDirectPublishingAndReceiving), ContentType = MessageContentTypes.PlainText, Body = $"Testing publishing of Json Payload with PlainText Body for [{nameof(TestAzureServiceBusDirectPublishingAndReceiving)}]!", Headers = new { IntegrationTestName = nameof(TestAzureServiceBusDirectPublishingAndReceiving), IntegrationTestExecutionDateTime = DateTimeOffset.UtcNow, } }; var jsonPayload = JsonConvert.SerializeObject(testPayload); var outboxItemFactory = new DefaultOutboxItemFactory <string>(); var uniqueIdGuidFactory = outboxItemFactory.UniqueIdentifierFactory; var outboxItem = outboxItemFactory.CreateExistingOutboxItem( uniqueIdentifier: uniqueIdGuidFactory.CreateUniqueIdentifier().ToString(), createdDateTimeUtc: DateTimeOffset.UtcNow, status: OutboxItemStatus.Pending.ToString(), fifoGroupingIdentifier: testPayload.FifoGroupingId, publishAttempts: 0, publishTarget: TestConfiguration.AzureServiceBusTopic, serializedPayload: jsonPayload ); //***************************************************************************************** //* STEP 2 - Publish the receivedItem to Azure Service Bus! //***************************************************************************************** await using var azureServiceBusPublisher = new DefaultAzureServiceBusOutboxPublisher( TestConfiguration.AzureServiceBusConnectionString, new AzureServiceBusPublishingOptions() { LogDebugCallback = s => TestContext.WriteLine(s), ErrorHandlerCallback = e => TestContext.WriteLine(e.Message + e.InnerException?.Message) } ); //Execute the publish to Azure... outboxItem.PublishAttempts++; await azureServiceBusPublisher.PublishOutboxItemAsync(outboxItem); //NOTE: Because we manually incremented the outbox item and mutated it (e.g. vs deep clone initialized from the DB) // we now need to decrement it for Test Assertions to work as expected against the deep clone that will be received // from the Azure Event Bus... outboxItem.PublishAttempts--; //***************************************************************************************** //* STEP 3 - Attempt to Retrieve/Receive the Message & Validate after Arrival! //***************************************************************************************** await AssertReceiptAndValidationOfThePublishedItem(outboxItem); }
public static async Task Run([TimerTrigger("%TransactionalOutboxAgentCronSchedule%")] TimerInfo myTimer, ILogger log) { log.LogInformation($"Transactional Outbox Agent initiating process at: {DateTime.Now}"); var configSettings = new SampleAppConfig(); var azureServiceBusPublisher = new DefaultAzureServiceBusOutboxPublisher( configSettings.AzureServiceBusConnectionString, new AzureServiceBusPublishingOptions() { SenderApplicationName = $"{typeof(TransactionalOutboxAgentFunction).Assembly.GetName().Name}.{nameof(TransactionalOutboxAgentFunction)}", LogDebugCallback = (s) => log.LogDebug(s), ErrorHandlerCallback = (e) => log.LogError(e, "Unexpected Exception occurred while Processing the Transactional Outbox.") } ); var outboxProcessingOptions = new OutboxProcessingOptions() { //ItemProcessingBatchSize = 200, //Only process the top X items to keep this function responsive! FifoEnforcedPublishingEnabled = true, //The Service Bus Topic is Session Enabled so we must processes it with FIFO Processing Enabled! LogDebugCallback = (m) => log.LogDebug(m), ErrorHandlerCallback = (e) => log.LogError(e, "Transactional Outbox Processing Exception"), MaxPublishingAttempts = configSettings.OutboxMaxPublishingRetryAttempts, TimeSpanToLive = configSettings.OutboxMaxTimeToLiveTimeSpan }; //************************************************************ //*** Execute processing of the Transactional Outbox... //************************************************************ await using var sqlConnection = new SqlConnection(configSettings.SqlConnectionString); await sqlConnection.OpenAsync().ConfigureAwait(false); await sqlConnection .ProcessPendingOutboxItemsAsync(azureServiceBusPublisher, outboxProcessingOptions) .ConfigureAwait(false); //************************************************************ //*** Execute Cleanup of Historical Outbox Data... //************************************************************ await sqlConnection .CleanupHistoricalOutboxItemsAsync(configSettings.OutboxHistoryToKeepTimeSpan) .ConfigureAwait(false); }
public OutboxProcessor(SampleAppConfig configSettings) { configSettings.AssertNotNull(nameof(configSettings)); var errorHandlerCallback = new Action <Exception>((e) => { Console.WriteLine($" ERROR => {e.GetMessagesRecursively()}"); OutboxHelpers.DefaultLogErrorCallback(e); }); //We Need a Publisher to publish Outbox Items... // NOTE: this is AsyncDisposable so we Keep a Reference for Disposal! OutboxPublisher = new DefaultAzureServiceBusOutboxPublisher( configSettings.AzureServiceBusConnectionString, new AzureServiceBusPublishingOptions() { SenderApplicationName = typeof(OutboxHelpers).Assembly.GetName().Name, LogDebugCallback = OutboxHelpers.DefaultLogDebugCallback, ErrorHandlerCallback = errorHandlerCallback } ); //Finally We Need the Processing Agent to process the Outbox on a background (Async) thread... // NOTE: this is AsyncDisposable so we Keep a Reference for Disposal! OutboxProcessingAgent = new AsyncThreadOutboxProcessingAgent( TimeSpan.FromSeconds(20), TimeSpan.FromDays(1), configSettings.SqlConnectionString, OutboxPublisher, //We Need Processing Options for the Agent... outboxProcessingOptions: new OutboxProcessingOptions() { //ItemProcessingBatchSize = 200, //Only process the top X items to keep this function responsive! FifoEnforcedPublishingEnabled = true, //The Service Bus Topic is Session Enabled so we must processes it with FIFO Processing Enabled! LogDebugCallback = OutboxHelpers.DefaultLogDebugCallback, ErrorHandlerCallback = errorHandlerCallback, MaxPublishingAttempts = configSettings.OutboxMaxPublishingRetryAttempts, TimeSpanToLive = configSettings.OutboxMaxTimeToLiveTimeSpan } ); }