From d4c803c0a4431d933ffc9ed9cd936365729e2eb5 Mon Sep 17 00:00:00 2001 From: John Simons Date: Thu, 30 Jul 2026 08:00:34 +1000 Subject: [PATCH] Remove legacy retry confirmation path MarkMessageFailureResolvedByRetry and the MessageFailureResolvedByRetry incoming message were the pre-4.20 way for an audit instance to tell the error instance that a retry had succeeded. Since 4.20 that job belongs to the retry acknowledgement headers, which the error ingestion handles through RetryConfirmationProcessor. The legacy path is unreachable on any supported upgrade to 6.x. UpgradeInfo forces an instance below 4.33.0 through 4.33.0 before it can reach 5.x, and every version from 4.20 onward stamps ServiceControl.Retry.AcknowledgementQueue on retried messages. DetectSuccessfulRetriesEnricher only emitted the legacy command when that header was absent, so no version in a supported upgrade path can still produce a message that needs the handler. Endpoints on an NServiceBus too old to acknowledge retries themselves are not affected. That case is the hasAckQueue branch of DetectSuccessfulRetriesEnricher, where the audit instance synthesises the acknowledgement, and it is kept along with its When_a_successful_retry_at_old_endpoint_is_detected coverage. Also removes RetryHandlerForBackwardsCompatability with its PerformRetry and RegisterSuccessfulRetry internal messages, which only covered messages in transit across a pre-4.x upgrade and had no other references. The ReportSuccessfulRetriesToServiceControl test helper simulated the audit instance via the legacy command. It is redundant because PlatformRetryNotifications is enabled at all of its call sites, so those endpoints already send the acknowledgement natively. --- .../When_a_failed_message_is_retried.cs | 2 - ...e_from_unmonitored_endpoint_is_imported.cs | 1 - .../ExternalIntegrationAcceptanceTest.cs | 1 - .../Groups/When_a_group_is_archived.cs | 1 - .../Groups/When_a_group_is_retried.cs | 1 - .../When_a_message_has_failed.cs | 2 - .../When_a_retry_fails_to_be_sent.cs | 1 - ..._for_a_empty_body_message_is_successful.cs | 1 - ...When_a_retry_for_a_failed_message_fails.cs | 1 - ...etry_for_a_failed_message_is_successful.cs | 1 - .../When_all_messages_are_retried.cs | 1 - .../EndpointConfigurationExtensions.cs | 5 - .../ReportSuccessfulRetryToServiceControl.cs | 28 ---- ...uccessful_retry_from_old_SC_is_detected.cs | 56 -------- ...terceptMessagesDestinedToServiceControl.cs | 4 - .../InterceptedMessagesScenarioContext.cs | 2 - .../DetectSuccessfulRetriesEnricherTests.cs | 7 +- .../Infrastructure/NServiceBusFactory.cs | 2 - .../DetectSuccessfulRetriesEnricher.cs | 46 ------ .../MarkMessageFailureResolvedByRetry.cs | 10 -- ...t_from_old_sc_is_sent_to_audit_instance.cs | 134 ------------------ .../RetryConfirmationProcessorTests.cs | 59 +------- .../MarkMessageFailureResolvedByRetry.cs | 15 -- .../MessageFailureResolvedByRetry.Legacy.cs | 10 -- .../MessageFailureResolvedByRetry.cs | 2 +- .../LegacyMessageFailureResolvedHandler.cs | 68 --------- .../InternalMessages/PerformRetry.cs | 9 -- .../RegisterSuccessfulRetry.cs | 11 -- .../RetryHandlerForBackwardsCompatability.cs | 19 --- 29 files changed, 6 insertions(+), 494 deletions(-) delete mode 100644 src/ServiceControl.AcceptanceTests/TestSupport/ReportSuccessfulRetryToServiceControl.cs delete mode 100644 src/ServiceControl.Audit.AcceptanceTests/Recoverability/When_a_successful_retry_from_old_SC_is_detected.cs delete mode 100644 src/ServiceControl.Audit/Recoverability/MarkMessageFailureResolvedByRetry.cs delete mode 100644 src/ServiceControl.MultiInstance.AcceptanceTests/Recoverability/When_a_message_retry_audit_from_old_sc_is_sent_to_audit_instance.cs delete mode 100644 src/ServiceControl/Contracts/MessageFailures/MarkMessageFailureResolvedByRetry.cs delete mode 100644 src/ServiceControl/Contracts/MessageFailures/MessageFailureResolvedByRetry.Legacy.cs delete mode 100644 src/ServiceControl/MessageFailures/Handlers/LegacyMessageFailureResolvedHandler.cs delete mode 100644 src/ServiceControl/MessageFailures/InternalMessages/PerformRetry.cs delete mode 100644 src/ServiceControl/MessageFailures/InternalMessages/RegisterSuccessfulRetry.cs delete mode 100644 src/ServiceControl/Recoverability/Retrying/Handlers/RetryHandlerForBackwardsCompatability.cs diff --git a/src/ServiceControl.AcceptanceTests.RavenDB/Recoverability/MessageFailures/When_a_failed_message_is_retried.cs b/src/ServiceControl.AcceptanceTests.RavenDB/Recoverability/MessageFailures/When_a_failed_message_is_retried.cs index 979be69d17..368e530ec3 100644 --- a/src/ServiceControl.AcceptanceTests.RavenDB/Recoverability/MessageFailures/When_a_failed_message_is_retried.cs +++ b/src/ServiceControl.AcceptanceTests.RavenDB/Recoverability/MessageFailures/When_a_failed_message_is_retried.cs @@ -203,8 +203,6 @@ public FailingEndpoint() => c.RegisterStartupTask(new SendMessageAtStart()); - c.ReportSuccessfulRetriesToServiceControl(); - c.NoRetries(); }); diff --git a/src/ServiceControl.AcceptanceTests/Monitoring/When_a_failed_message_from_unmonitored_endpoint_is_imported.cs b/src/ServiceControl.AcceptanceTests/Monitoring/When_a_failed_message_from_unmonitored_endpoint_is_imported.cs index dcfb2ececa..319fa0b351 100644 --- a/src/ServiceControl.AcceptanceTests/Monitoring/When_a_failed_message_from_unmonitored_endpoint_is_imported.cs +++ b/src/ServiceControl.AcceptanceTests/Monitoring/When_a_failed_message_from_unmonitored_endpoint_is_imported.cs @@ -62,7 +62,6 @@ public Receiver() => EndpointSetup(c => { c.NoRetries(); - c.ReportSuccessfulRetriesToServiceControl(); }); [Handler] diff --git a/src/ServiceControl.AcceptanceTests/Recoverability/ExternalIntegration/ExternalIntegrationAcceptanceTest.cs b/src/ServiceControl.AcceptanceTests/Recoverability/ExternalIntegration/ExternalIntegrationAcceptanceTest.cs index 675169486e..2e0bf56a04 100644 --- a/src/ServiceControl.AcceptanceTests/Recoverability/ExternalIntegration/ExternalIntegrationAcceptanceTest.cs +++ b/src/ServiceControl.AcceptanceTests/Recoverability/ExternalIntegration/ExternalIntegrationAcceptanceTest.cs @@ -21,7 +21,6 @@ public ErrorSender() => { c.EnableFeature(); c.NoDelayedRetries(); - c.ReportSuccessfulRetriesToServiceControl(); }); [Handler] diff --git a/src/ServiceControl.AcceptanceTests/Recoverability/Groups/When_a_group_is_archived.cs b/src/ServiceControl.AcceptanceTests/Recoverability/Groups/When_a_group_is_archived.cs index 799726d724..2e3455cc29 100644 --- a/src/ServiceControl.AcceptanceTests/Recoverability/Groups/When_a_group_is_archived.cs +++ b/src/ServiceControl.AcceptanceTests/Recoverability/Groups/When_a_group_is_archived.cs @@ -231,7 +231,6 @@ public Receiver() => EndpointSetup(c => { c.NoDelayedRetries(); - c.ReportSuccessfulRetriesToServiceControl(); }); [Handler] diff --git a/src/ServiceControl.AcceptanceTests/Recoverability/Groups/When_a_group_is_retried.cs b/src/ServiceControl.AcceptanceTests/Recoverability/Groups/When_a_group_is_retried.cs index 3b01b993e7..3e32fba255 100644 --- a/src/ServiceControl.AcceptanceTests/Recoverability/Groups/When_a_group_is_retried.cs +++ b/src/ServiceControl.AcceptanceTests/Recoverability/Groups/When_a_group_is_retried.cs @@ -96,7 +96,6 @@ public Receiver() => EndpointSetup(c => { c.Recoverability().Delayed(x => x.NumberOfRetries(0)); - c.ReportSuccessfulRetriesToServiceControl(); }); [Handler] diff --git a/src/ServiceControl.AcceptanceTests/Recoverability/MessageFailures/When_a_message_has_failed.cs b/src/ServiceControl.AcceptanceTests/Recoverability/MessageFailures/When_a_message_has_failed.cs index 5490f97cb7..a26f067f09 100644 --- a/src/ServiceControl.AcceptanceTests/Recoverability/MessageFailures/When_a_message_has_failed.cs +++ b/src/ServiceControl.AcceptanceTests/Recoverability/MessageFailures/When_a_message_has_failed.cs @@ -227,7 +227,6 @@ public Receiver() => EndpointSetup(c => { c.NoRetries(); - c.ReportSuccessfulRetriesToServiceControl(); }); [Handler] @@ -253,7 +252,6 @@ public ReceiverWithCustomSerializer() => EndpointSetup(c => { c.NoRetries(); - c.ReportSuccessfulRetriesToServiceControl(); c.UseSerialization(); }); diff --git a/src/ServiceControl.AcceptanceTests/Recoverability/MessageFailures/When_a_retry_fails_to_be_sent.cs b/src/ServiceControl.AcceptanceTests/Recoverability/MessageFailures/When_a_retry_fails_to_be_sent.cs index 80dd123b4f..e367de15cc 100644 --- a/src/ServiceControl.AcceptanceTests/Recoverability/MessageFailures/When_a_retry_fails_to_be_sent.cs +++ b/src/ServiceControl.AcceptanceTests/Recoverability/MessageFailures/When_a_retry_fails_to_be_sent.cs @@ -84,7 +84,6 @@ public FailureEndpoint() => { c.EnableFeature(); c.NoRetries(); - c.ReportSuccessfulRetriesToServiceControl(); }); [Handler] diff --git a/src/ServiceControl.AcceptanceTests/Recoverability/MessageFailures/When_a_retry_for_a_empty_body_message_is_successful.cs b/src/ServiceControl.AcceptanceTests/Recoverability/MessageFailures/When_a_retry_for_a_empty_body_message_is_successful.cs index 807741be16..73ee896515 100644 --- a/src/ServiceControl.AcceptanceTests/Recoverability/MessageFailures/When_a_retry_for_a_empty_body_message_is_successful.cs +++ b/src/ServiceControl.AcceptanceTests/Recoverability/MessageFailures/When_a_retry_for_a_empty_body_message_is_successful.cs @@ -75,7 +75,6 @@ public FailureEndpoint() => { c.EnableFeature(); c.NoDelayedRetries(); - c.ReportSuccessfulRetriesToServiceControl(); c.Pipeline.Register(services => new LookForControlMessage(services.GetRequiredService()), "Look for control messages"); }); diff --git a/src/ServiceControl.AcceptanceTests/Recoverability/MessageFailures/When_a_retry_for_a_failed_message_fails.cs b/src/ServiceControl.AcceptanceTests/Recoverability/MessageFailures/When_a_retry_for_a_failed_message_fails.cs index 11480f1947..716f707c4f 100644 --- a/src/ServiceControl.AcceptanceTests/Recoverability/MessageFailures/When_a_retry_for_a_failed_message_fails.cs +++ b/src/ServiceControl.AcceptanceTests/Recoverability/MessageFailures/When_a_retry_for_a_failed_message_fails.cs @@ -104,7 +104,6 @@ public FailureEndpoint() => EndpointSetup(c => { c.NoRetries(); - c.ReportSuccessfulRetriesToServiceControl(); }); [Handler] diff --git a/src/ServiceControl.AcceptanceTests/Recoverability/MessageFailures/When_a_retry_for_a_failed_message_is_successful.cs b/src/ServiceControl.AcceptanceTests/Recoverability/MessageFailures/When_a_retry_for_a_failed_message_is_successful.cs index 2df42f9017..44a06395af 100644 --- a/src/ServiceControl.AcceptanceTests/Recoverability/MessageFailures/When_a_retry_for_a_failed_message_is_successful.cs +++ b/src/ServiceControl.AcceptanceTests/Recoverability/MessageFailures/When_a_retry_for_a_failed_message_is_successful.cs @@ -205,7 +205,6 @@ public FailureEndpoint() => EndpointSetup(c => { c.NoRetries(); - c.ReportSuccessfulRetriesToServiceControl(); }); [Handler] diff --git a/src/ServiceControl.AcceptanceTests/Recoverability/MessageFailures/When_all_messages_are_retried.cs b/src/ServiceControl.AcceptanceTests/Recoverability/MessageFailures/When_all_messages_are_retried.cs index f194b25fc8..d77fa5da40 100644 --- a/src/ServiceControl.AcceptanceTests/Recoverability/MessageFailures/When_all_messages_are_retried.cs +++ b/src/ServiceControl.AcceptanceTests/Recoverability/MessageFailures/When_all_messages_are_retried.cs @@ -96,7 +96,6 @@ public Receiver() => EndpointSetup(c => { c.NoRetries(); - c.ReportSuccessfulRetriesToServiceControl(); }); [Handler] diff --git a/src/ServiceControl.AcceptanceTests/TestSupport/EndpointConfigurationExtensions.cs b/src/ServiceControl.AcceptanceTests/TestSupport/EndpointConfigurationExtensions.cs index f10878310d..b547246e5c 100644 --- a/src/ServiceControl.AcceptanceTests/TestSupport/EndpointConfigurationExtensions.cs +++ b/src/ServiceControl.AcceptanceTests/TestSupport/EndpointConfigurationExtensions.cs @@ -8,11 +8,6 @@ public static class EndpointConfigurationExtensions { - public static void ReportSuccessfulRetriesToServiceControl(this EndpointConfiguration configuration) - { - configuration.Pipeline.Register(typeof(ReportSuccessfulRetryToServiceControl), "Simulate that the audit instance detects and reports successfull retries"); - } - public static void CustomizeServiceControlEndpointTesting(this EndpointConfiguration configuration, ScenarioContext context) { configuration.GetSettings().Set("SC.ScenarioContext", context); diff --git a/src/ServiceControl.AcceptanceTests/TestSupport/ReportSuccessfulRetryToServiceControl.cs b/src/ServiceControl.AcceptanceTests/TestSupport/ReportSuccessfulRetryToServiceControl.cs deleted file mode 100644 index dd2cd5603a..0000000000 --- a/src/ServiceControl.AcceptanceTests/TestSupport/ReportSuccessfulRetryToServiceControl.cs +++ /dev/null @@ -1,28 +0,0 @@ -namespace ServiceControl.AcceptanceTests.TestSupport -{ - using System; - using System.Threading.Tasks; - using Contracts.MessageFailures; - using NServiceBus; - using NServiceBus.Pipeline; - using ServiceBus.Management.Infrastructure.Settings; - - class ReportSuccessfulRetryToServiceControl : IBehavior - { - public async Task Invoke(IIncomingPhysicalMessageContext context, Func next) - { - await next(context); - - if (context.MessageHeaders.TryGetValue("ServiceControl.Retry.UniqueMessageId", out var messageId)) - { - var options = new SendOptions(); - options.SetDestination(Settings.DEFAULT_INSTANCE_NAME); - - await context.Send(new MarkMessageFailureResolvedByRetry - { - FailedMessageId = messageId - }, options); - } - } - } -} \ No newline at end of file diff --git a/src/ServiceControl.Audit.AcceptanceTests/Recoverability/When_a_successful_retry_from_old_SC_is_detected.cs b/src/ServiceControl.Audit.AcceptanceTests/Recoverability/When_a_successful_retry_from_old_SC_is_detected.cs deleted file mode 100644 index 743fdb608e..0000000000 --- a/src/ServiceControl.Audit.AcceptanceTests/Recoverability/When_a_successful_retry_from_old_SC_is_detected.cs +++ /dev/null @@ -1,56 +0,0 @@ -namespace ServiceControl.Audit.AcceptanceTests.Recoverability -{ - using System; - using System.Linq; - using System.Threading.Tasks; - using AcceptanceTesting.EndpointTemplates; - using NServiceBus; - using NServiceBus.AcceptanceTesting; - using NUnit.Framework; - using TestSupport; - - - class When_a_successful_retry_from_old_SC_is_detected : AcceptanceTest - { - [Test] - public async Task Should_raise_integration_event() - { - CustomConfiguration = endpointConfiguration => - { - endpointConfiguration.Pipeline.Register(typeof(InterceptMessagesDestinedToServiceControl), - "Intercepts messages destined to ServiceControl"); - }; - - var uniqueMessageIdHeaderName = "ServiceControl.Retry.UniqueMessageId"; - - var failedMessageId = Guid.NewGuid().ToString(); - var context = await Define() - .WithEndpoint(b => b.When(s => - { - var options = new SendOptions(); - - options.SetHeader(uniqueMessageIdHeaderName, failedMessageId); - options.RouteToThisEndpoint(); - return s.Send(new MyMessage(), options); - })) - .Done(c => c.SentMarkMessageFailureResolvedByRetriesCommands.Any()) - .Run(); - - var command = context.SentMarkMessageFailureResolvedByRetriesCommands.Single(); - Assert.That(command.FailedMessageId, Is.EqualTo(failedMessageId)); - } - - public class Receiver : EndpointConfigurationBuilder - { - public Receiver() => EndpointSetup(); - - [Handler] - public class MyMessageHandler : IHandleMessages - { - public Task Handle(MyMessage message, IMessageHandlerContext context) => Task.CompletedTask; - } - } - - public class MyMessage : ICommand; - } -} \ No newline at end of file diff --git a/src/ServiceControl.Audit.AcceptanceTests/TestSupport/InterceptMessagesDestinedToServiceControl.cs b/src/ServiceControl.Audit.AcceptanceTests/TestSupport/InterceptMessagesDestinedToServiceControl.cs index 80f1a14d37..9d605d44ca 100644 --- a/src/ServiceControl.Audit.AcceptanceTests/TestSupport/InterceptMessagesDestinedToServiceControl.cs +++ b/src/ServiceControl.Audit.AcceptanceTests/TestSupport/InterceptMessagesDestinedToServiceControl.cs @@ -3,7 +3,6 @@ namespace ServiceControl.Audit.AcceptanceTests.TestSupport using System; using System.Threading.Tasks; using Contracts.EndpointControl; - using Contracts.MessageFailures; using NServiceBus.AcceptanceTesting; using NServiceBus.Pipeline; @@ -17,9 +16,6 @@ public override Task Invoke(IOutgoingLogicalMessageContext context, Func n case RegisterNewEndpoint newEndpoint: interceptingContext?.SentRegisterEndpointCommands.Add(newEndpoint); return Task.CompletedTask; - case MarkMessageFailureResolvedByRetry markMessageFailureResolvedByRetry: - interceptingContext?.SentMarkMessageFailureResolvedByRetriesCommands.Add(markMessageFailureResolvedByRetry); - return Task.CompletedTask; default: return next(); } diff --git a/src/ServiceControl.Audit.AcceptanceTests/TestSupport/InterceptedMessagesScenarioContext.cs b/src/ServiceControl.Audit.AcceptanceTests/TestSupport/InterceptedMessagesScenarioContext.cs index d5fe7f7cab..ea94e537d9 100644 --- a/src/ServiceControl.Audit.AcceptanceTests/TestSupport/InterceptedMessagesScenarioContext.cs +++ b/src/ServiceControl.Audit.AcceptanceTests/TestSupport/InterceptedMessagesScenarioContext.cs @@ -2,12 +2,10 @@ namespace ServiceControl.Audit.AcceptanceTests.TestSupport { using System.Collections.Concurrent; using Contracts.EndpointControl; - using Contracts.MessageFailures; using NServiceBus.AcceptanceTesting; public class InterceptedMessagesScenarioContext : ScenarioContext { public ConcurrentBag SentRegisterEndpointCommands { get; } = []; - public ConcurrentBag SentMarkMessageFailureResolvedByRetriesCommands { get; } = []; } } \ No newline at end of file diff --git a/src/ServiceControl.Audit.UnitTests/Receoverability/DetectSuccessfulRetriesEnricherTests.cs b/src/ServiceControl.Audit.UnitTests/Receoverability/DetectSuccessfulRetriesEnricherTests.cs index caed52f01c..f00379d425 100644 --- a/src/ServiceControl.Audit.UnitTests/Receoverability/DetectSuccessfulRetriesEnricherTests.cs +++ b/src/ServiceControl.Audit.UnitTests/Receoverability/DetectSuccessfulRetriesEnricherTests.cs @@ -3,7 +3,6 @@ using System.Collections.Generic; using System.Linq; using Audit.Auditing; - using Contracts.MessageFailures; using NServiceBus; using NServiceBus.Transport; using NUnit.Framework; @@ -35,7 +34,7 @@ public void It_does_not_sent_acknowledgement_if_audit_comes_from_new_endpoint_ve } [Test] - public void It_sends_legacy_command_if_retry_comes_from_old_ServiceControl() + public void It_does_not_acknowledge_if_retry_comes_from_old_ServiceControl() { var enricher = new DetectSuccessfulRetriesEnricher(); @@ -50,9 +49,9 @@ public void It_sends_legacy_command_if_retry_comes_from_old_ServiceControl() enricher.Enrich(new AuditEnricherContext(headers, outgoingCommands, transportOperations, metadata)); - Assert.That(outgoingCommands, Is.Not.Empty); - Assert.That(outgoingCommands, Is.All.InstanceOf(typeof(MarkMessageFailureResolvedByRetry))); + Assert.That(outgoingCommands, Is.Empty); Assert.That(transportOperations, Is.Empty); + Assert.That(metadata["IsRetried"], Is.True); } [Test] diff --git a/src/ServiceControl.Audit/Infrastructure/NServiceBusFactory.cs b/src/ServiceControl.Audit/Infrastructure/NServiceBusFactory.cs index 1762b29f1f..e0648a0efa 100644 --- a/src/ServiceControl.Audit/Infrastructure/NServiceBusFactory.cs +++ b/src/ServiceControl.Audit/Infrastructure/NServiceBusFactory.cs @@ -5,7 +5,6 @@ namespace ServiceControl.Audit.Infrastructure using System.Threading.Tasks; using Auditing; using Contracts.EndpointControl; - using Contracts.MessageFailures; using NServiceBus; using NServiceBus.Configuration.AdvancedExtensibility; using Plugins; @@ -46,7 +45,6 @@ public static void Configure(Settings.Settings settings, ITransportCustomization var routing = new RoutingSettings(configuration.GetSettings()); routing.RouteToEndpoint(typeof(RegisterNewEndpoint), serviceControlLogicalQueue); - routing.RouteToEndpoint(typeof(MarkMessageFailureResolvedByRetry), serviceControlLogicalQueue); configuration.AddCustomCheck(); configuration.AddCustomCheck(); diff --git a/src/ServiceControl.Audit/Recoverability/DetectSuccessfulRetriesEnricher.cs b/src/ServiceControl.Audit/Recoverability/DetectSuccessfulRetriesEnricher.cs index 618c36c765..a97ab1af44 100644 --- a/src/ServiceControl.Audit/Recoverability/DetectSuccessfulRetriesEnricher.cs +++ b/src/ServiceControl.Audit/Recoverability/DetectSuccessfulRetriesEnricher.cs @@ -2,15 +2,10 @@ { using System; using System.Collections.Generic; - using System.Linq; using Auditing; - using Contracts.MessageFailures; - using Infrastructure; using NServiceBus; - using NServiceBus.Faults; using NServiceBus.Routing; using NServiceBus.Transport; - using ServiceControl.Audit.Persistence.Infrastructure; class DetectSuccessfulRetriesEnricher : IEnrichImportedAuditMessages { @@ -45,47 +40,6 @@ public void Enrich(AuditEnricherContext context) var ackOperation = new TransportOperation(ackMessage, new UnicastAddressTag(ackQueue)); context.AddForSend(ackOperation); } - else - { - //The message has been sent for retry from ServiceControl older than 4.20. Regardless which version the endpoint was, we need to send a legacy confirmation - //message because the main instance of ServiceControl may still be on version lower than 4.19. - context.AddForSend(new MarkMessageFailureResolvedByRetry - { - FailedMessageId = isOldRetry ? headers.UniqueId() : newRetryMessageId, - AlternativeFailedMessageIds = GetAlternativeUniqueMessageId(headers).ToArray() - }); - } - } - - IEnumerable GetAlternativeUniqueMessageId(IReadOnlyDictionary headers) - { - var messageId = headers.MessageId(); - if (headers.TryGetValue(Headers.ProcessingEndpoint, out var processingEndpoint)) - { - yield return DeterministicGuid.MakeId(messageId, processingEndpoint).ToString(); - } - - if (headers.TryGetValue(FaultsHeaderKeys.FailedQ, out var failedQ)) - { - yield return DeterministicGuid.MakeId(messageId, ExtractQueueNameForLegacyReasons(failedQ)).ToString(); - } - - if (headers.TryGetValue(Headers.ReplyToAddress, out var replyToAddress)) - { - yield return DeterministicGuid.MakeId(messageId, ExtractQueueNameForLegacyReasons(replyToAddress)).ToString(); - } - } - - static string ExtractQueueNameForLegacyReasons(string address) - { - var atIndex = address?.IndexOf("@", StringComparison.InvariantCulture); - - if (atIndex.HasValue && atIndex.Value > -1) - { - return address.Substring(0, atIndex.Value); - } - - return address; } } } \ No newline at end of file diff --git a/src/ServiceControl.Audit/Recoverability/MarkMessageFailureResolvedByRetry.cs b/src/ServiceControl.Audit/Recoverability/MarkMessageFailureResolvedByRetry.cs deleted file mode 100644 index 3e9a8111c2..0000000000 --- a/src/ServiceControl.Audit/Recoverability/MarkMessageFailureResolvedByRetry.cs +++ /dev/null @@ -1,10 +0,0 @@ -namespace ServiceControl.Contracts.MessageFailures -{ - using NServiceBus; - - public class MarkMessageFailureResolvedByRetry : ICommand - { - public string FailedMessageId { get; set; } - public string[] AlternativeFailedMessageIds { get; set; } - } -} \ No newline at end of file diff --git a/src/ServiceControl.MultiInstance.AcceptanceTests/Recoverability/When_a_message_retry_audit_from_old_sc_is_sent_to_audit_instance.cs b/src/ServiceControl.MultiInstance.AcceptanceTests/Recoverability/When_a_message_retry_audit_from_old_sc_is_sent_to_audit_instance.cs deleted file mode 100644 index 3255eb5d44..0000000000 --- a/src/ServiceControl.MultiInstance.AcceptanceTests/Recoverability/When_a_message_retry_audit_from_old_sc_is_sent_to_audit_instance.cs +++ /dev/null @@ -1,134 +0,0 @@ -namespace ServiceControl.MultiInstance.AcceptanceTests.Recoverability -{ - using System; - using System.Threading; - using System.Threading.Tasks; - using AcceptanceTesting; - using AcceptanceTesting.EndpointTemplates; - using MessageFailures; - using NServiceBus; - using NServiceBus.AcceptanceTesting; - using NServiceBus.Pipeline; - using NServiceBus.Settings; - using NUnit.Framework; - using ServiceControl.Infrastructure; - using TestSupport; - - - class When_a_message_retry_audit_from_old_sc_is_sent_to_audit_instance : AcceptanceTest - { - [Test] - [CancelAfter(120_000)] - public async Task Should_mark_as_resolved(CancellationToken cancellationToken) - { - FailedMessage failure; - - await Define() - .WithEndpoint(b => b.When(session => session.SendLocal(new MyMessage())).DoNotFailOnErrorMessages()) - .Done(async c => - { - // Give the other parts of the system a chance to work - await Task.Delay(500); - - var result = await GetFailedMessage(c); - failure = result; - if (!result) - { - return false; - } - - if (failure.Status == FailedMessageStatus.Unresolved) - { - await IssueRetry(c, () => this.Post($"/api/errors/{failure.UniqueMessageId}/retry", null, null, ServiceControlInstanceName)); - return false; - } - - return failure.Status == FailedMessageStatus.Resolved; - }) - .Run(cancellationToken); - } - - Task> GetFailedMessage(MyContext c) - { - if (c.MessageId == null) - { - return Task.FromResult(SingleResult.Empty); - } - - return this.TryGet("/api/errors/" + c.UniqueMessageId, msg => true, ServiceControlInstanceName); - } - - async Task IssueRetry(MyContext c, Func retryAction) - { - if (!c.RetryIssued) - { - c.RetryIssued = true; - await retryAction(); - } - } - - public class Failing : EndpointConfigurationBuilder - { - public Failing() => - EndpointSetup(c => - { - c.NoRetries(); - c.Pipeline.Register(new SimulateOldServiceControlBehavior(), "Simulates old SC behavior"); - }); - - public class SimulateOldServiceControlBehavior : Behavior - { - public override Task Invoke(ITransportReceiveContext context, Func next) - { - context.Message.Headers.Remove("ServiceControl.Retry.AcknowledgementQueue"); - return next(); - } - } - - [Handler] - public class MyMessageHandler : IHandleMessages - { - readonly MyContext testContext; - readonly IReadOnlySettings settings; - readonly ReceiveAddresses receiveAddresses; - - public MyMessageHandler(MyContext testContext, IReadOnlySettings settings, ReceiveAddresses receiveAddresses) - { - this.testContext = testContext; - this.settings = settings; - this.receiveAddresses = receiveAddresses; - } - - public Task Handle(MyMessage message, IMessageHandlerContext context) - { - Console.Out.WriteLine("Handling message"); - testContext.EndpointNameOfReceivingEndpoint = settings.EndpointName(); - testContext.LocalAddress = receiveAddresses.MainReceiveAddress; - testContext.MessageId = context.MessageId.Replace(@"\", "-"); - - if (!testContext.RetryIssued) //simulate that the exception will be resolved with the retry - { - Console.Out.WriteLine("Throwing exception for MyMessage"); - throw new Exception("Simulated exception"); - } - - return Task.CompletedTask; - } - } - } - - - public class MyMessage : ICommand; - - public class MyContext : ScenarioContext - { - public string MessageId { get; set; } - - public string EndpointNameOfReceivingEndpoint { get; set; } - - public string UniqueMessageId => DeterministicGuid.MakeId(MessageId, EndpointNameOfReceivingEndpoint).ToString(); - public string LocalAddress { get; set; } - public bool RetryIssued { get; set; } - } - } -} \ No newline at end of file diff --git a/src/ServiceControl.Persistence.Tests/Recoverability/RetryConfirmationProcessorTests.cs b/src/ServiceControl.Persistence.Tests/Recoverability/RetryConfirmationProcessorTests.cs index d3111e676a..78d87cd11d 100644 --- a/src/ServiceControl.Persistence.Tests/Recoverability/RetryConfirmationProcessorTests.cs +++ b/src/ServiceControl.Persistence.Tests/Recoverability/RetryConfirmationProcessorTests.cs @@ -1,13 +1,10 @@ -namespace ServiceControl.Persistence.Tests.Recoverability +namespace ServiceControl.Persistence.Tests.Recoverability { using System; using System.Collections.Generic; using System.Threading.Tasks; - using Contracts.MessageFailures; using MessageFailures; - using MessageFailures.Handlers; using NServiceBus.Extensibility; - using NServiceBus.Testing; using NServiceBus.Transport; using NUnit.Framework; using ServiceControl.Operations; @@ -15,7 +12,6 @@ class RetryConfirmationProcessorTests : PersistenceTestBase { RetryConfirmationProcessor Processor { get; set; } - LegacyMessageFailureResolvedHandler Handler { get; set; } [SetUp] public async Task Setup() @@ -23,8 +19,6 @@ public async Task Setup() var domainEvents = new FakeDomainEvents(); Processor = new RetryConfirmationProcessor(domainEvents); - Handler = new LegacyMessageFailureResolvedHandler(FailedMessageRetryStore, FailedMessageLifecycleStore, domainEvents); - await PersistenceTestsContext.InsertFailedMessages( new FailedMessage { @@ -52,55 +46,6 @@ public async Task Should_handle_multiple_retry_confirmations_in_the_error_ingest Assert.DoesNotThrowAsync(() => unitOfWork.Complete(TestContext.CurrentContext.CancellationToken)); } - [Test] - public async Task Should_handle_multiple_legacy_audit_instance_retry_confirmations() - { - await Handler.Handle(CreateLegacyRetryConfirmationCommand(), new TestableMessageHandlerContext()); - - Assert.DoesNotThrowAsync( - () => Handler.Handle(CreateLegacyRetryConfirmationCommand(), new TestableInvokeHandlerContext())); - } - - [Test] - public async Task Should_handle_retry_confirmation_followed_by_legacy_command() - { - var messageContexts = new List - { - CreateRetryAcknowledgementMessage() - }; - - var unitOfWork = await UnitOfWorkFactory.StartNew(); - await Processor.Process(messageContexts, unitOfWork); - await unitOfWork.Complete(TestContext.CurrentContext.CancellationToken); - - Assert.DoesNotThrowAsync( - () => Handler.Handle(CreateLegacyRetryConfirmationCommand(), new TestableInvokeHandlerContext())); - } - - [Test] - public async Task Should_handle_legacy_retry_confirmation_command_followed_by_new_acknowledgement() - { - await Handler.Handle(CreateLegacyRetryConfirmationCommand(), new TestableMessageHandlerContext()); - - var messageContexts = new List - { - CreateRetryAcknowledgementMessage() - }; - - var unitOfWork = await UnitOfWorkFactory.StartNew(); - await Processor.Process(messageContexts, unitOfWork); - Assert.DoesNotThrowAsync(() => unitOfWork.Complete(TestContext.CurrentContext.CancellationToken)); - } - - static MarkMessageFailureResolvedByRetry CreateLegacyRetryConfirmationCommand() - { - var retryConfirmation = new MarkMessageFailureResolvedByRetry - { - FailedMessageId = MessageId - }; - return retryConfirmation; - } - static MessageContext CreateRetryAcknowledgementMessage() { var headers = new Dictionary @@ -120,4 +65,4 @@ static MessageContext CreateRetryAcknowledgementMessage() const string MessageId = "83C73A86-A45E-4FDF-8C95-E292526166F5"; } -} \ No newline at end of file +} diff --git a/src/ServiceControl/Contracts/MessageFailures/MarkMessageFailureResolvedByRetry.cs b/src/ServiceControl/Contracts/MessageFailures/MarkMessageFailureResolvedByRetry.cs deleted file mode 100644 index 201a9abad3..0000000000 --- a/src/ServiceControl/Contracts/MessageFailures/MarkMessageFailureResolvedByRetry.cs +++ /dev/null @@ -1,15 +0,0 @@ -namespace ServiceControl.Contracts.MessageFailures -{ - using NServiceBus; - - /// - /// A command used before ServiceControl 4.20. Sent by the Audit instance to the Main instance when the Audit instances detected that the audited message has the ServiceControl. - /// When upgrading from 4.x to 4.20 and higher these legacy messages may still be in the input queue so a handler for these is needed. - /// retry header. - /// - public class MarkMessageFailureResolvedByRetry : ICommand - { - public string FailedMessageId { get; set; } - public string[] AlternativeFailedMessageIds { get; set; } - } -} \ No newline at end of file diff --git a/src/ServiceControl/Contracts/MessageFailures/MessageFailureResolvedByRetry.Legacy.cs b/src/ServiceControl/Contracts/MessageFailures/MessageFailureResolvedByRetry.Legacy.cs deleted file mode 100644 index 8701b4815a..0000000000 --- a/src/ServiceControl/Contracts/MessageFailures/MessageFailureResolvedByRetry.Legacy.cs +++ /dev/null @@ -1,10 +0,0 @@ -namespace ServiceControl.Contracts.MessageFailures -{ - using NServiceBus; - - /// - /// A message used by ServiceControl before Audit split (before 4.0) to inform that the audited message has been successfully retried. When upgrading from 3.x to 4.20 and higher - /// these legacy messages may still be in the input queue so a handler for these is needed. - /// - public partial class MessageFailureResolvedByRetry : IMessage; -} \ No newline at end of file diff --git a/src/ServiceControl/Contracts/MessageFailures/MessageFailureResolvedByRetry.cs b/src/ServiceControl/Contracts/MessageFailures/MessageFailureResolvedByRetry.cs index c4615e81fb..a0e037403b 100644 --- a/src/ServiceControl/Contracts/MessageFailures/MessageFailureResolvedByRetry.cs +++ b/src/ServiceControl/Contracts/MessageFailures/MessageFailureResolvedByRetry.cs @@ -1,4 +1,4 @@ -namespace ServiceControl.Contracts.MessageFailures +namespace ServiceControl.Contracts.MessageFailures { using Infrastructure.DomainEvents; diff --git a/src/ServiceControl/MessageFailures/Handlers/LegacyMessageFailureResolvedHandler.cs b/src/ServiceControl/MessageFailures/Handlers/LegacyMessageFailureResolvedHandler.cs deleted file mode 100644 index a0f05157d3..0000000000 --- a/src/ServiceControl/MessageFailures/Handlers/LegacyMessageFailureResolvedHandler.cs +++ /dev/null @@ -1,68 +0,0 @@ -namespace ServiceControl.MessageFailures.Handlers -{ - using System.Linq; - using System.Threading.Tasks; - using Contracts.MessageFailures; - using Infrastructure.DomainEvents; - using NServiceBus; - using Persistence; - - /// - /// This class handles legacy messages that mark a failed message as successfully retried. For further details go to message definitions. - /// - [Handler] - class LegacyMessageFailureResolvedHandler(IFailedMessageRetryDataStore retryStore, IFailedMessageLifecycleDataStore lifecycleStore, IDomainEvents domainEvents) : - IHandleMessages, - IHandleMessages - { - public async Task Handle(MarkMessageFailureResolvedByRetry message, IMessageHandlerContext context) - { - await MarkAsResolvedByRetry(message.FailedMessageId, message.AlternativeFailedMessageIds); - await domainEvents.Raise(new MessageFailureResolvedByRetry - { - AlternativeFailedMessageIds = message.AlternativeFailedMessageIds, - FailedMessageId = message.FailedMessageId - }, context.CancellationToken); - } - - // This is only needed because we might get this from legacy not yet converted instances - public async Task Handle(MessageFailureResolvedByRetry message, IMessageHandlerContext context) - { - await MarkAsResolvedByRetry(message.FailedMessageId, message.AlternativeFailedMessageIds); - await domainEvents.Raise(new MessageFailureResolvedByRetry - { - AlternativeFailedMessageIds = message.AlternativeFailedMessageIds, - FailedMessageId = message.FailedMessageId - }, context.CancellationToken); - } - - async Task MarkAsResolvedByRetry(string primaryId, string[] messageAlternativeFailedMessageIds) - { - await retryStore.RemoveFailedMessageRetry(primaryId); - - var primaryUpdated = await lifecycleStore.MarkAsResolved(primaryId); - - if (primaryUpdated) - { - return; - } - - if (messageAlternativeFailedMessageIds == null) - { - return; - } - - foreach (var alternative in messageAlternativeFailedMessageIds.Where(x => x != primaryId)) - { - await retryStore.RemoveFailedMessageRetry(alternative); - - var alternativeUpdated = await lifecycleStore.MarkAsResolved(alternative); - - if (alternativeUpdated) - { - return; - } - } - } - } -} \ No newline at end of file diff --git a/src/ServiceControl/MessageFailures/InternalMessages/PerformRetry.cs b/src/ServiceControl/MessageFailures/InternalMessages/PerformRetry.cs deleted file mode 100644 index b18f22b48b..0000000000 --- a/src/ServiceControl/MessageFailures/InternalMessages/PerformRetry.cs +++ /dev/null @@ -1,9 +0,0 @@ -namespace ServiceControl.MessageFailures.InternalMessages -{ - using NServiceBus; - - class PerformRetry : ICommand - { - public string FailedMessageId { get; set; } - } -} \ No newline at end of file diff --git a/src/ServiceControl/MessageFailures/InternalMessages/RegisterSuccessfulRetry.cs b/src/ServiceControl/MessageFailures/InternalMessages/RegisterSuccessfulRetry.cs deleted file mode 100644 index 5808774c5f..0000000000 --- a/src/ServiceControl/MessageFailures/InternalMessages/RegisterSuccessfulRetry.cs +++ /dev/null @@ -1,11 +0,0 @@ -namespace ServiceControl.MessageFailures.InternalMessages -{ - using System; - using NServiceBus; - - class RegisterSuccessfulRetry : ICommand - { - public string FailedMessageId { get; set; } - public Guid RetryId { get; set; } - } -} \ No newline at end of file diff --git a/src/ServiceControl/Recoverability/Retrying/Handlers/RetryHandlerForBackwardsCompatability.cs b/src/ServiceControl/Recoverability/Retrying/Handlers/RetryHandlerForBackwardsCompatability.cs deleted file mode 100644 index 43e8a438d9..0000000000 --- a/src/ServiceControl/Recoverability/Retrying/Handlers/RetryHandlerForBackwardsCompatability.cs +++ /dev/null @@ -1,19 +0,0 @@ -namespace ServiceControl.Recoverability -{ - using System.Threading.Tasks; - using Contracts.MessageFailures; - using MessageFailures.InternalMessages; - using NServiceBus; - - // This Handler only exists for messages which are in transit when SC is upgraded to use the new Retries facility - // Once these messages have been cleared out it is no longer required - [Handler] - class RetryHandlerForBackwardsCompatability : IHandleMessages, IHandleMessages - { - public Task Handle(PerformRetry message, IMessageHandlerContext context) => - context.SendLocal(m => - m.MessageUniqueIds = [message.FailedMessageId]); - - public Task Handle(RegisterSuccessfulRetry message, IMessageHandlerContext context) => context.SendLocal(m => { m.FailedMessageId = message.FailedMessageId; }); - } -} \ No newline at end of file