Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Exception serialization support for built-in messages #6297

Merged

Conversation

Aaronontheweb
Copy link
Member

@Aaronontheweb Aaronontheweb commented Dec 8, 2022

Changes

Fixes #3903

Adding support to any built-in messages that support Exception types. Won't be adding any serialization magic to Newtonsoft.Json for exception handling as that starts to get into CVE territory given the deep amount of context the serialization payloads contain.

Some of the designs we're considering for 1.5 / later, namely using source generators to create compile-time serialization for everything, will make this issue easier to manage in the future. For now I think it's good to preserve the location transparency of Akka.NET's own built-in messages for end-user convenience and consistency.

Checklist

For significant changes, please ensure that the following have been completed (delete if not relevant):


// a test where Sys starts a ParentActor and has it remotely deploy an EchoActor onto a second ActorSystem
[Fact]
public async Task ParentActor_should_be_able_to_deploy_EchoActor_to_remote_system()
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

End to end test that validates that system messages are currently covered already.

@Aaronontheweb
Copy link
Member Author

Aaronontheweb commented Dec 8, 2022

Going to define some new Protos for the following message types:

  • Status.Failure
  • Status.Success

If there are any other built-in messages that should be covered I'd be happy to handle those here - please let me know!

@Aaronontheweb Aaronontheweb marked this pull request as ready for review December 8, 2022 02:59
Copy link
Member Author

@Aaronontheweb Aaronontheweb left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Detailed my changes

[InlineData(null)]
[InlineData(1)]
[InlineData("hi")]
public void Can_serialize_StatusSuccess(object payload)
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Try with a variety of payloads for Status.Success

[InlineData(null)]
[InlineData(1)]
[InlineData("hi")]
public void Can_serialize_StatusFailure(object payload)
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Try with a variety of payloads for Status.Failure

@@ -70,3 +70,12 @@ message ExceptionData {
ExceptionData innerException = 5;
map<string, Payload> customFields = 6;
}

message StatusSuccess{
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

New .proto message formats for Status.Success and Status.Failure

@@ -39,6 +39,8 @@ akka {
"Akka.Actor.PoisonPill, Akka" = akka-misc
"Akka.Actor.Kill, Akka" = akka-misc
"Akka.Actor.PoisonPill, Akka" = akka-misc
"Akka.Actor.Status+Failure, Akka" = akka-misc
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

New serialization bindings for these types.

So in terms of wire implications:

  1. Any old Status.Failure / Status.Success messages sent over the network using Newtonsoft.Json will still be deserialized as Newtonsoft.Json when they arrive on an upgraded server - no issues there. Forwards-compatible.
  2. Any new Status.Failure / Status.Success messages sent using the Protobuf format to a server running Akka.NET v1.4.46 or earlier will see these messages dropped until they upgrade.

I can't really use a feature flag here since this is changing the bindings themselves - so users might hit that problem when they upgrade. Given that Status.Failure already mostly can't be serialized today I think this is likely a non-issue for most users. My vote is to just push this change out there.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can't we still use feature flags if we programmatically inject these bindings using Serialization.AddSerializationMap()?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I sincerely don't think the juice is worth the squeeze to do it, especially given that 1.5 is just around the corner.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good point

@Aaronontheweb
Copy link
Member Author

cc @Zetanova - what do you think of this?

Copy link
Contributor

@Arkatufus Arkatufus left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good overall, got a question.

@@ -39,6 +39,8 @@ akka {
"Akka.Actor.PoisonPill, Akka" = akka-misc
"Akka.Actor.Kill, Akka" = akka-misc
"Akka.Actor.PoisonPill, Akka" = akka-misc
"Akka.Actor.Status+Failure, Akka" = akka-misc
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can't we still use feature flags if we programmatically inject these bindings using Serialization.AddSerializationMap()?

@Zetanova
Copy link
Contributor

Zetanova commented Dec 8, 2022

@Aaronontheweb The issue itself is not addressed.
If the remote actor throws a unknown exception type or inner-exception
back to the caller. The caller will not receive it.

The remote actor itself does not know or better to say should not to know
because everything is "transparent"

The current effect is that the remote connection will fail with a deserializing exception
and the caller will never see a response message.
In a cluster this would lead to a disassociation and a required restart of the node.

@Zetanova
Copy link
Contributor

Zetanova commented Dec 8, 2022

@Aaronontheweb

public Exception ExceptionFromProtoNet(Proto.Msg.ExceptionData proto)
{
if (string.IsNullOrEmpty(proto.TypeName))
return null;
Type exceptionType = Type.GetType(proto.TypeName);
var serializationInfo = new SerializationInfo(exceptionType, _defaultFormatterConverter);
serializationInfo.AddValue("ClassName", proto.TypeName);
serializationInfo.AddValue("Message", ValueOrNull(proto.Message));
serializationInfo.AddValue("StackTraceString", ValueOrNull(proto.StackTrace));
serializationInfo.AddValue("Source", ValueOrNull(proto.Source));
serializationInfo.AddValue("InnerException", ExceptionFromProto(proto.InnerException));
serializationInfo.AddValue("HelpURL", string.Empty);
serializationInfo.AddValue("RemoteStackTraceString", null);
serializationInfo.AddValue("RemoteStackIndex", 0);
serializationInfo.AddValue("ExceptionMethod", null);
serializationInfo.AddValue("HResult", int.MinValue);

This method should only TryToDeserialize if the TypeName can be resolved.
If not a AkkaGenericException should be created

My Idea was to replace the Exception instance to a basic Reason instance on the producing actor.
It can be tagged by an Id and stored into a memory cache or something else defined by the users config (like elasticsearch)

For the receiver it would the be OPTIONAL to convert Reason back to an Exception instance
and this could always be over an TryConvertReasonToException pattern.

@Aaronontheweb
Copy link
Member Author

@Aaronontheweb The issue itself is not addressed. If the remote actor throws a unknown exception type or inner-exception back to the caller. The caller will not receive it.

The remote actor itself does not know or better to say should not to know because everything is "transparent"

We're not going to recursively nest exceptions all the way down the stack - sorry, but we have to draw the line somewhere. We don't want to be in the already messy business + dangerous of serializing exceptions.

The current effect is that the remote connection will fail with a deserializing exception and the caller will never see a response message. In a cluster this would lead to a disassociation and a required restart of the node.

This is not true - Akka.Remote does not drop associations when deserialization exceptions occur. That's been fixed a ways back.

@Aaronontheweb Aaronontheweb merged commit 998dcca into akkadotnet:v1.4 Dec 8, 2022
@Aaronontheweb Aaronontheweb deleted the remote-error-serialization branch December 8, 2022 23:53
@Zetanova
Copy link
Contributor

Zetanova commented Dec 9, 2022

We're not going to recursively nest exceptions all the way down the stack - sorry, but we have to draw the line somewhere. We don't want to be in the already messy business + dangerous of serializing exceptions.

My proposal to convert the exceptions to basic Reason messages is exactly the opposite.
This would already happen in the PostStop of the ActorBase and then escalated.

The Serializer/Wire would in the default config then never see a single exception.
The conversion back to an exception would be then optional and more or less for legacy use
and supported only with an extensions.

Instead of a simple Reason type an AkkaGenericException could be made.
It should only be a WellKnown type for all nodes and could hold an Error-Guid, Error-Code and Error-Message
and never a single InnerException

@Aaronontheweb
Copy link
Member Author

The Serializer/Wire would in the default config then never see a single exception.
The conversion back to an exception would be then optional and more or less for legacy use
and supported only with an extensions.

So we don't have a formal spec for this yet as we think it's out of scope for Akka.NET v1.5, but we're looking at replacing the "DIY" serialization system with something more authoritative (source generators) in the future. We'd have a formula for working with Exception types as part of that most likely.

Arkatufus pushed a commit to Arkatufus/akka.net that referenced this pull request Dec 9, 2022
)

* added end to end spec to validate error serialization for remote actor supervision

* defined `.proto`s for `Status.Failure` and `Status.Success`

* added `Status.,Failure` and `Status.Success` support to the `MiscMessageSerializer`

* added tests to `MiscMessageSerializerSpec`

* close akkadotnet#3903

(cherry picked from commit 998dcca)
Aaronontheweb added a commit that referenced this pull request Dec 9, 2022
* added end to end spec to validate error serialization for remote actor supervision

* defined `.proto`s for `Status.Failure` and `Status.Success`

* added `Status.,Failure` and `Status.Success` support to the `MiscMessageSerializer`

* added tests to `MiscMessageSerializerSpec`

* close #3903

(cherry picked from commit 998dcca)

Co-authored-by: Aaron Stannard <aaron@petabridge.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
akka.net v1.4 Issues affecting Akka.NET v1.4 akka-remote serialization
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants