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

Serialization payload #6

Open
Zetanova opened this issue Sep 7, 2019 · 7 comments
Open

Serialization payload #6

Zetanova opened this issue Sep 7, 2019 · 7 comments

Comments

@Zetanova
Copy link

Zetanova commented Sep 7, 2019

If there are multiple nodes with different roles and assemblies in the cluster.
Currently Reminder can only accept a payload where the type is known to every node.
Else reminder could not receive and/or deserialize the payload message.

But for the functionality of the reminder service it is not important to deserialize it.
Only the receiving node should be able to deserialize the payload back.

@Horusiath
Copy link
Collaborator

Horusiath commented Sep 8, 2019

@Zetanova underneat the Reminder is just an actor. Like any actor it communicates by sending messages. If any recipient actor is supposed to receive the message from reminder, the details of serialization/deserialization must be known to both sides (just like when two actors talk with each other).

If you want to send serializer-agnostic message, you can serialize/deserialize it explicitly like:

var message = new MyMessage();
var payload = system.Serialization.FindSerializerFor(message).ToBytes(message, typeof(MyMessage));

var task = new Reminder.Schedule(taskId, recipient.Path, payload, delay);
reminder.Tell(task);

// then in the recipient actor
class Recipient : ReceiveActor
{
    public Recipient()
    {
        var serializer = Context.System.Serialization.FindSerializerForType(typeof(MyMessage));
        Receive<byte[]>(raw =>{
            var messager = (MyMessage)serializer.FromBytes(raw);
        });
    }
}

@Zetanova
Copy link
Author

Zetanova commented Sep 8, 2019

I thought about some kind of surrogate type defined by akka.net
Is there somekind of mechanismus to lazy deserialize a received massage?

@Horusiath
Copy link
Collaborator

In theory you could do some kind of wrapper message, that's internally uses ISurrogate/ISurrogated mechanism to do late serialization, but that's only shifting the problem - instead of knowing how to deserialize your destination message, now you need to know how to deserialize its wrapper.

@Zetanova
Copy link
Author

It looks like a generic problem that should be resolved over the akka.net base libery
The gateway pattern would profit from a lazy deserialized payload-message-type too.

I will investigate more.

@Zetanova
Copy link
Author

I have made now one payload message type.
But what is missing is an auto unwarp-feature in akka for dual-pass a message
with somekind of interface-flag like "IPayload"

This could then be used for sender defined remote success/failure messages
or just a muti hope/forward route
or in Akka.Persistence.Reminders as Payload

Example of auto unwarp/dual-pass in ActorBase:

internal protected virtual bool AroundReceive(Receive receive, object message)
{
    var wasHandled = receive(message);

    //try to unwarp payload message
    if(!wasHandled && message is IPayload payload)
    {
        if(payload.TryDeserialize(var pmsg)) 
        {
             wasHandled = receive(pmsg);
        }
    }
    if(!wasHandled)
    {
         Unhandled(message);
    }
    return wasHandled;
}

Payload class

public sealed class Payload : ISurrogated, IPayload
    {
        sealed class Surrogate : ISurrogate
        {
            public readonly int SerializerId;

            public readonly string Manifest;

            public readonly byte[] Data;

            public Surrogate(byte[] data, int serializerId, string manifest)
            {
                SerializerId = serializerId;
                Manifest = manifest;
                Data = data;
            }

            ISurrogated ISurrogate.FromSurrogate(ActorSystem system)
            {
                return new Payload(this, system);
            }

            public object Deserialize(ActorSystem system)
            {
                return system.Serialization.Deserialize(Data, SerializerId, Manifest);
            }
        }

        Surrogate _surrogate;
        ActorSystem _system;
        object _content;

        public Object Content => _content ?? (_content = _surrogate?.Deserialize(_system));

        public Payload(Object content)
        {
            _content = content;
        }

        private Payload(Surrogate surrogate, ActorSystem system)
        {
            _surrogate = surrogate;
            _system = system;
        }

        ISurrogate ISurrogated.ToSurrogate(ActorSystem system)
        {
            if(_surrogate is null)
            {
                String manifest;
                Byte[] data;
                int serializerId;

                switch (system.Serialization.FindSerializerFor(Content))
                {
                    case SerializerWithStringManifest serial:
                        manifest = serial.Manifest(Content);
                        data = serial.ToBinary(Content);
                        serializerId = serial.Identifier;
                        break;
                    case Serializer serial when serial.IncludeManifest:
                        manifest = Content.GetType().TypeQualifiedName();
                        data = serial.ToBinary(Content);
                        serializerId = serial.Identifier;
                        break;
                    case var serial:
                        manifest = String.Empty;
                        data = serial.ToBinary(Content);
                        serializerId = serial.Identifier;
                        break;
                }

                _surrogate = new Surrogate(data, serializerId, manifest);
            }

            return _surrogate;
        }

        public bool TryDeserialize(out object value)
        {
            try
            {
                value = Content;
                return true;
            }
            catch (SerializationException _)
            {
                value = null;
                return false;
            }
        }
    }

@Horusiath
Copy link
Collaborator

@Zetanova I think, that the right place to include feature like that would be either in core akka projects or directly as serializer feature.

@Zetanova
Copy link
Author

@Horusiath Yes, i thinking of the same, posted it know into the akkadotnet/akka.net#3811

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants