diff --git a/src/Directory.Packages.props b/src/Directory.Packages.props
index ec2eba2ceb..571507d059 100644
--- a/src/Directory.Packages.props
+++ b/src/Directory.Packages.props
@@ -47,7 +47,7 @@
-
+
diff --git a/src/ServiceControl.AcceptanceTests/Recoverability/MessageFailures/When_ingesting_failed_message_with_missing_headers.cs b/src/ServiceControl.AcceptanceTests/Recoverability/MessageFailures/When_ingesting_failed_message_with_missing_headers.cs
index f0d933885c..cb0b280106 100644
--- a/src/ServiceControl.AcceptanceTests/Recoverability/MessageFailures/When_ingesting_failed_message_with_missing_headers.cs
+++ b/src/ServiceControl.AcceptanceTests/Recoverability/MessageFailures/When_ingesting_failed_message_with_missing_headers.cs
@@ -34,8 +34,8 @@ public async Task Should_be_ingested_when_minimal_required_headers_is_present()
//No failure time will result in utc now being used
Assert.That(failure.TimeOfFailure, Is.GreaterThan(testStartTime));
- // Both host and endpoint name is currently needed so this will be null since no host can be detected from the failed q header
- Assert.That(failure.ReceivingEndpoint, Is.Null);
+ Assert.That(failure.ReceivingEndpoint, Is.Not.Null);
+ Assert.That(failure.ReceivingEndpoint.Name, Is.EqualTo(context.EndpointNameOfReceivingEndpoint));
}
[Test]
@@ -45,10 +45,6 @@ public async Task Should_include_headers_required_by_ServicePulse()
{
c.AddMinimalRequiredHeaders();
- // This is needed for ServiceControl to be able to detect both endpoint (via failed q header) and host via the processing machine header
- // Missing endpoint or host will cause a null ref in ServicePulse
- c.Headers[Headers.ProcessingMachine] = "MyMachine";
-
c.Headers[FaultsHeaderKeys.ExceptionType] = "SomeExceptionType";
c.Headers[FaultsHeaderKeys.Message] = "Some message";
})
@@ -62,8 +58,6 @@ public async Task Should_include_headers_required_by_ServicePulse()
// ServicePulse assumes that the receiving endpoint name is present
Assert.That(failure.ReceivingEndpoint, Is.Not.Null);
- Assert.That(failure.ReceivingEndpoint.Name, Is.EqualTo(context.EndpointNameOfReceivingEndpoint));
- Assert.That(failure.ReceivingEndpoint.Host, Is.EqualTo("MyMachine"));
// ServicePulse needs both an exception type and description to render the UI in a resonable way
Assert.That(failure.Exception.ExceptionType, Is.EqualTo("SomeExceptionType"));
diff --git a/src/ServiceControl.Transports/TransportCustomization.cs b/src/ServiceControl.Transports/TransportCustomization.cs
index 500a9c5846..720d00ff5f 100644
--- a/src/ServiceControl.Transports/TransportCustomization.cs
+++ b/src/ServiceControl.Transports/TransportCustomization.cs
@@ -138,15 +138,24 @@ public virtual async Task ProvisionQueues(TransportSettings transportSettings, I
true,
null); //null means "not hosted by core", transport SHOULD adjust accordingly to not assume things
- var receivers = new[]{
+ var receiveQueueName = transportSettings.EndpointName;
+ var receivers = new[]
+ {
new ReceiveSettings(
transportSettings.EndpointName,
- new QueueAddress(transportSettings.EndpointName),
+ new QueueAddress(receiveQueueName),
false,
false,
- transportSettings.ErrorQueue)};
+ transportSettings.ErrorQueue)
+ };
+
+ var additionalQueuesToProvision = additionalQueues.Where(queueName => queueName != receiveQueueName)
+ .Union([transportSettings.ErrorQueue])
+ .Select(ToTransportQualifiedQueueNameCore)
+ .Distinct()
+ .ToArray();
- var transportInfrastructure = await transport.Initialize(hostSettings, receivers, additionalQueues.Union([transportSettings.ErrorQueue]).Select(ToTransportQualifiedQueueNameCore).ToArray());
+ var transportInfrastructure = await transport.Initialize(hostSettings, receivers, additionalQueuesToProvision);
await transportInfrastructure.Shutdown();
}
diff --git a/src/ServiceControl.UnitTests/Operations/When_parsing_receive_endpoint.cs b/src/ServiceControl.UnitTests/Operations/When_parsing_receive_endpoint.cs
new file mode 100644
index 0000000000..e8e78a3218
--- /dev/null
+++ b/src/ServiceControl.UnitTests/Operations/When_parsing_receive_endpoint.cs
@@ -0,0 +1,49 @@
+namespace ServiceControl.UnitTests.Operations;
+
+using System.Collections.Generic;
+using NServiceBus.Faults;
+using NUnit.Framework;
+using ServiceControl.Contracts.Operations;
+using ServiceControl.Infrastructure;
+
+[TestFixture]
+public class When_parsing_receive_endpoint
+{
+ [Test]
+ public void Should_infer_host_from_machine_name_in_failed_queue_when_host_header_is_missing()
+ {
+ var headers = new Dictionary
+ {
+ { FaultsHeaderKeys.FailedQ, "Sales@backend-01" }
+ };
+
+ var endpoint = EndpointDetailsParser.ReceivingEndpoint(headers);
+
+ Assert.Multiple(() =>
+ {
+ Assert.That(endpoint, Is.Not.Null);
+ Assert.That(endpoint.Name, Is.EqualTo("Sales"));
+ Assert.That(endpoint.Host, Is.EqualTo("backend-01"));
+ Assert.That(endpoint.HostId, Is.EqualTo(DeterministicGuid.MakeId("Sales", "backend-01")));
+ });
+ }
+
+ [Test]
+ public void Should_fallback_to_unknown_if_host_can_not_be_determined()
+ {
+ var headers = new Dictionary
+ {
+ { FaultsHeaderKeys.FailedQ, "Billing" }
+ };
+
+ var endpoint = EndpointDetailsParser.ReceivingEndpoint(headers);
+
+ Assert.Multiple(() =>
+ {
+ Assert.That(endpoint, Is.Not.Null);
+ Assert.That(endpoint.Name, Is.EqualTo("Billing"));
+ Assert.That(endpoint.Host, Is.EqualTo("unknown"));
+ Assert.That(endpoint.HostId, Is.EqualTo(DeterministicGuid.MakeId("Billing", "unknown")));
+ });
+ }
+}
\ No newline at end of file
diff --git a/src/ServiceControl.UnitTests/Recoverability/ExceptionTypeAndStackTraceFailureClassifierTest.cs b/src/ServiceControl.UnitTests/Recoverability/ExceptionTypeAndStackTraceFailureClassifierTest.cs
index d669039557..ebea27ce41 100644
--- a/src/ServiceControl.UnitTests/Recoverability/ExceptionTypeAndStackTraceFailureClassifierTest.cs
+++ b/src/ServiceControl.UnitTests/Recoverability/ExceptionTypeAndStackTraceFailureClassifierTest.cs
@@ -7,6 +7,8 @@
[TestFixture]
public class ExceptionTypeAndStackTraceFailureClassifierTest
{
+ const string noStackTraceClassification = "exceptionType: No stacktrace";
+
[Test]
public void Failure_Without_ExceptionDetails_should_not_group()
{
@@ -23,7 +25,7 @@ public void Empty_stack_trace_should_group_by_exception_type()
var failureWithEmptyStackTrace = CreateFailureDetailsWithStackTrace(string.Empty);
var classification = classifier.ClassifyFailure(failureWithEmptyStackTrace);
- Assert.That(classification, Is.EqualTo("exceptionType: 0"));
+ Assert.That(classification, Is.EqualTo(noStackTraceClassification));
}
[Test]
@@ -33,7 +35,7 @@ public void Null_stack_trace_should_group_by_exception_type()
var failureWithNullStackTrace = CreateFailureDetailsWithStackTrace(null);
var classification = classifier.ClassifyFailure(failureWithNullStackTrace);
- Assert.That(classification, Is.EqualTo("exceptionType: 0"));
+ Assert.That(classification, Is.EqualTo(noStackTraceClassification));
}
[Test]
@@ -43,7 +45,7 @@ public void Non_standard_stack_trace_format_should_group_by_exception_type()
var failureWithNonStandardStackTrace = CreateFailureDetailsWithStackTrace("something other than a normal stack trace");
var classification = classifier.ClassifyFailure(failureWithNonStandardStackTrace);
- Assert.That(classification, Is.EqualTo("exceptionType: 0"));
+ Assert.That(classification, Is.EqualTo(noStackTraceClassification));
}
[Test]
@@ -53,7 +55,7 @@ public void Null_message_should_group_by_exception_type()
var failureWithNullMessage = CreateFailureDetailsWithMessage(null);
var classification = classifier.ClassifyFailure(failureWithNullMessage);
- Assert.That(classification, Is.EqualTo("exceptionType: 0"));
+ Assert.That(classification, Is.EqualTo(noStackTraceClassification));
}
[Test]
@@ -63,7 +65,7 @@ public void Empty_message_should_group_by_exception_type()
var failureWithEmptyMessage = CreateFailureDetailsWithMessage(string.Empty);
var classification = classifier.ClassifyFailure(failureWithEmptyMessage);
- Assert.That(classification, Is.EqualTo("exceptionType: 0"));
+ Assert.That(classification, Is.EqualTo(noStackTraceClassification));
}
[Test]
@@ -73,7 +75,7 @@ public void Whitespace_message_should_group_by_exception_type()
var failureWithWhitespaceMessage = CreateFailureDetailsWithMessage(" ");
var classification = classifier.ClassifyFailure(failureWithWhitespaceMessage);
- Assert.That(classification, Is.EqualTo("exceptionType: 0"));
+ Assert.That(classification, Is.EqualTo(noStackTraceClassification));
}
[Test]
@@ -154,4 +156,4 @@ static ClassifiableMessageDetails CreateFailureDetailsWithMessage(string message
return new ClassifiableMessageDetails(null, failure, null);
}
}
-}
\ No newline at end of file
+}
diff --git a/src/ServiceControl/Operations/EndpointDetailsParser.cs b/src/ServiceControl/Operations/EndpointDetailsParser.cs
index c2edcef186..7801a0fe1e 100644
--- a/src/ServiceControl/Operations/EndpointDetailsParser.cs
+++ b/src/ServiceControl/Operations/EndpointDetailsParser.cs
@@ -77,12 +77,17 @@ public static EndpointDetails ReceivingEndpoint(IReadOnlyDictionary new StackFrame
@@ -54,10 +55,7 @@ public string ClassifyFailure(ClassifiableMessageDetails failure)
return GetNonStandardClassification(exception.ExceptionType);
}
- static string GetNonStandardClassification(string exceptionType)
- {
- return exceptionType + ": 0";
- }
+ static string GetNonStandardClassification(string exceptionType) => exceptionType + ": No stacktrace";
public const string Id = "Exception Type and Stack Trace";