Skip to content

Commit f43b9b7

Browse files
Akka.Remote: log all layers of wrapped messages during errors (#6818)
Make sure we capture what the real underlying message types are when we encounter issues like serialization errors, oversized payload exceptions, and so on.
1 parent 613878b commit f43b9b7

File tree

3 files changed

+62
-6
lines changed

3 files changed

+62
-6
lines changed

src/core/Akka.Remote.Tests/RemotingSpec.cs

+16
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
using Akka.Actor;
1212
using Akka.Actor.Dsl;
1313
using Akka.Configuration;
14+
using Akka.Event;
1415
using Akka.Remote.Transport;
1516
using Akka.Routing;
1617
using Akka.TestKit;
@@ -651,6 +652,21 @@ await EventFilter.Exception<OversizedPayloadException>(start: "Discarding oversi
651652
});
652653
}
653654

655+
/// <summary>
656+
/// Validate that we can accurately log wrapped messages that fail to be delivered
657+
/// </summary>
658+
[Fact]
659+
public void Log_Wrapped_messages_that_fail_to_Send()
660+
{
661+
// 2x wrapped message
662+
var wrappedMessage =
663+
new DeadLetter(new ActorSelectionMessage("hit", Array.Empty<SelectionPathElement>(), false), TestActor,
664+
TestActor);
665+
666+
var loggedType = EndpointWriter.LogPossiblyWrappedMessageType(wrappedMessage);
667+
loggedType.Should().Contain("DeadLetter").And.Contain("ActorSelectionMessage").And.Contain("String");
668+
}
669+
654670
[Fact]
655671
public async Task Drop_received_messages_over_payload_size()
656672
{

src/core/Akka.Remote.Tests/RemotingTerminatorSpecs.cs

+7-3
Original file line numberDiff line numberDiff line change
@@ -119,9 +119,13 @@ await EventFilter.Exception<ShutDownAssociation>().ExpectAsync(0,
119119
(await associated.Ask<ActorIdentity>(new Identify("foo"), RemainingOrDefault)).MessageId.ShouldBe("foo");
120120

121121
// terminate the DEPLOYED system
122-
Assert.True(await _sys2.Terminate().AwaitWithTimeout(10.Seconds()), "Expected to terminate within 10 seconds, but didn't.");
123-
await ExpectTerminatedAsync(associated); // expect that the remote deployed actor is dead
124-
122+
await WithinAsync(TimeSpan.FromSeconds(10), async () =>
123+
{
124+
var terminationTask = _sys2.Terminate(); // start termination process
125+
await ExpectTerminatedAsync(associated); // expect that the remote deployed actor is dead
126+
Assert.True(await terminationTask.AwaitWithTimeout(RemainingOrDefault), "Expected to terminate within 10 seconds, but didn't.");
127+
});
128+
125129
// now terminate the DEPLOYER system
126130
Assert.True(await Sys.Terminate().AwaitWithTimeout(10.Seconds()), "Expected to terminate within 10 seconds, but didn't.");
127131
});

src/core/Akka.Remote/Endpoint.cs

+39-3
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
using System.Diagnostics;
1212
using System.Linq;
1313
using System.Runtime.Serialization;
14+
using System.Text;
1415
using System.Threading;
1516
using System.Threading.Tasks;
1617
using Akka.Actor;
@@ -1462,6 +1463,41 @@ private void BecomeWritingOrSendBufferedMessages()
14621463
}
14631464
}
14641465

1466+
/// <summary>
1467+
/// Unwraps <see cref="IWrappedMessage"/> in order to help make it easier to troubleshoot
1468+
/// which oversized message was sent.
1469+
/// </summary>
1470+
/// <returns>The formatted type string.</returns>
1471+
/// <remarks>
1472+
/// Internal for testing purposes only.
1473+
/// </remarks>
1474+
internal static string LogPossiblyWrappedMessageType(object failedMsg)
1475+
{
1476+
if (failedMsg is IWrappedMessage wrappedMessage)
1477+
{
1478+
static void LogWrapped(StringBuilder builder, IWrappedMessage nextMsg)
1479+
{
1480+
builder.Append($"{nextMsg.GetType()}-->");
1481+
if (nextMsg.Message is IWrappedMessage wrappedAgain)
1482+
{
1483+
builder.Append('(');
1484+
LogWrapped(builder, wrappedAgain); // recursively iterate through all layers of wrapping
1485+
builder.Append(')');
1486+
}
1487+
else
1488+
{
1489+
builder.Append(nextMsg.Message.GetType());
1490+
}
1491+
}
1492+
1493+
var builder = new StringBuilder();
1494+
LogWrapped(builder, wrappedMessage);
1495+
return builder.ToString();
1496+
}
1497+
1498+
return failedMsg.GetType().ToString();
1499+
}
1500+
14651501
private bool WriteSend(EndpointManager.Send send)
14661502
{
14671503
try
@@ -1486,7 +1522,7 @@ private bool WriteSend(EndpointManager.Send send)
14861522
string.Format("Discarding oversized payload sent to {0}: max allowed size {1} bytes, actual size of encoded {2} was {3} bytes.",
14871523
send.Recipient,
14881524
Transport.MaximumPayloadBytes,
1489-
send.Message.GetType(),
1525+
LogPossiblyWrappedMessageType(send.Message),
14901526
pdu.Length));
14911527
_log.Error(reason, "Transient association error (association remains live)");
14921528
return true;
@@ -1509,15 +1545,15 @@ private bool WriteSend(EndpointManager.Send send)
15091545
_log.Error(
15101546
ex,
15111547
"Serialization failed for message [{0}]. Transient association error (association remains live)",
1512-
send.Message.GetType());
1548+
LogPossiblyWrappedMessageType(send.Message));
15131549
return true;
15141550
}
15151551
catch (ArgumentException ex)
15161552
{
15171553
_log.Error(
15181554
ex,
15191555
"Serializer threw ArgumentException for message type [{0}]. Transient association error (association remains live)",
1520-
send.Message.GetType());
1556+
LogPossiblyWrappedMessageType(send.Message));
15211557
return true;
15221558
}
15231559
catch (EndpointException ex)

0 commit comments

Comments
 (0)