-
Notifications
You must be signed in to change notification settings - Fork 1k
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
Akka.Serialization: INoSerializationVerificationNeeded
does not handle IWrappedMessage
correctly
#7010
Akka.Serialization: INoSerializationVerificationNeeded
does not handle IWrappedMessage
correctly
#7010
Conversation
`INoSerializationVerificationNeeded` does not handle `IWrappedMessage` correctly
Added a repro for this; working on a fix now. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Detailed my changes.
} | ||
|
||
// add an unserializable member, such as a delegate | ||
public Action MyAction { get; } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Newtonsoft.Json and Hyperion will both barf on delegate serialization, so this message will not be received when the parent IWrappedMessage
is not handled correctly.
|
||
public class SerializeAllMessagesSpec : AkkaSpec | ||
{ | ||
public SerializeAllMessagesSpec(ITestOutputHelper output) : base("akka.actor.serialize-messages = on", output) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Have to enable akka.actor.serialize-messages=on
in order for this to even be an issue.
{ | ||
// Arrange | ||
var myProbe = CreateTestProbe(); | ||
var action = () => { myProbe.Tell("worked"); }; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Invoking the delegate closes on the loop on this spec by messaging the closed-over TestProbe
.
{ | ||
// Act | ||
myActor.Tell(wrappedMessage); | ||
await myProbe.ExpectMsgAsync("worked"); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Both the EventFilter
will fail and the ExpectMsgAsync
will fail if the ActorCell
of the recipient attempts to serialize this message - which is exactly what happened before I applied my fix.
src/core/Akka/Actor/ActorCell.cs
Outdated
var unwrapped = (deadLetter = envelope.Message as DeadLetter) != null ? deadLetter.Message : envelope.Message; | ||
var unwrapped = envelope.Message; | ||
|
||
if(envelope.Message is IWrappedMessage wrapped) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
for all IWrappedMessage
s: if the underlying message doesn't want to be serialized (typically because it can't be), don't try it. Just return the message without performing any additional work.
src/core/Akka/Actor/ActorCell.cs
Outdated
@@ -534,7 +537,8 @@ private Envelope SerializeAndDeserialize(Envelope envelope) | |||
throw new SerializationException($"Failed to serialize and deserialize payload object [{unwrapped.GetType()}]. Envelope: [{envelope}], Actor type: [{Actor.GetType()}]", e); | |||
} | |||
|
|||
if (deadLetter != null) | |||
// special case handling for DeadLetters | |||
if (envelope.Message is DeadLetter deadLetter) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Restore the special-case handling for DeadLetter
s that we had before.
src/core/Akka/Actor/ActorCell.cs
Outdated
@@ -534,7 +537,8 @@ private Envelope SerializeAndDeserialize(Envelope envelope) | |||
throw new SerializationException($"Failed to serialize and deserialize payload object [{unwrapped.GetType()}]. Envelope: [{envelope}], Actor type: [{Actor.GetType()}]", e); | |||
} | |||
|
|||
if (deadLetter != null) | |||
// special case handling for DeadLetters | |||
if (envelope.Message is DeadLetter deadLetter) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It seems like we're missing a round of unwrap here, this code will return an Envelope
that wraps a DeadLetter
that wraps a DeadLetter
.
The DeadLetter
message still needs to be deserialized like it was before and then re-wrapped inside a new DeadLetter
message.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good point, I can use the Unwrapped
utility from earlier to recursively unwrap it - let me look into that. Currently doing a full test suite run locally so I can fix some of the outstanding .NET 8 errors in the suite.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Self review
while (message is IWrappedMessage wm) | ||
{ | ||
message = wm.Message; | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Unwrap tail recursion into loop
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM
src/core/Akka/Actor/ActorCell.cs
Outdated
// special case handling for DeadLetters | ||
return deadLetter is not null | ||
? new Envelope(new DeadLetter(deserializedMsg, deadLetter.Sender, deadLetter.Recipient), envelope.Sender) | ||
: new Envelope(deserializedMsg, envelope.Sender); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This code behaves exactly like the old code, only modernized
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks fine to me
src/core/Akka/Actor/ActorCell.cs
Outdated
var deadLetter = envelope.Message as DeadLetter; | ||
|
||
// recursively unwraps message | ||
var unwrapped = WrappedMessage.Unwrap(deadLetter is not null ? deadLetter.Message : envelope.Message); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If the envelope message is a DeadLetter
, grab the dead letter message instead.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Also unwraps the message recursively if it is an IWrappedMessage
. There is no need to check if the message is IWrappedMessage
or not, it is done inside the Unwrap
static function.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Don't need to check for a DeadLetter
here as it implements IWrappedMessage
.
src/core/Akka/Actor/ActorCell.cs
Outdated
DeadLetter deadLetter; | ||
var unwrapped = (deadLetter = envelope.Message as DeadLetter) != null ? deadLetter.Message : envelope.Message; | ||
|
||
var deadLetter = envelope.Message as DeadLetter; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Check to see if the envelope message is a DeadLetter
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Just one nitpick on the duplicate check for a DeadLetter
@Arkatufus
src/core/Akka/Actor/ActorCell.cs
Outdated
var deadLetter = envelope.Message as DeadLetter; | ||
|
||
// recursively unwraps message | ||
var unwrapped = WrappedMessage.Unwrap(deadLetter is not null ? deadLetter.Message : envelope.Message); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Don't need to check for a DeadLetter
here as it implements IWrappedMessage
.
src/core/Akka/Actor/ActorCell.cs
Outdated
// special case handling for DeadLetters | ||
return deadLetter is not null | ||
? new Envelope(new DeadLetter(deserializedMsg, deadLetter.Sender, deadLetter.Recipient), envelope.Sender) | ||
: new Envelope(deserializedMsg, envelope.Sender); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks fine to me
need to serialize both the envelope and the underlying type when `serialize-messages=on`
…ng type when using `serialize-messages=on` (#7200) * Fix bug introduced by #7010 need to serialize both the envelope and the underlying type when `serialize-messages=on` * handle edge case with `IWrappedMessage` that implements `INoSerializationVerificationNeeded` * Add unit test * Fix pub-sub router spec --------- Co-authored-by: Gregorius Soedharmo <arkatufus@yahoo.com>
Changes
INoSerializationVerificationNeeded
does not handleIWrappedMessage
correctly. We handleDeadLetter
correctly as a special case, but not any of the otherIWrappedMessage
implementations. We should handle this generically.Checklist
For significant changes, please ensure that the following have been completed (delete if not relevant):