These are chat archives for akkadotnet/akka.net

8th
Nov 2017
jalchr
@jalchr
Nov 08 2017 10:43

Hi,
I have the following scenario:
1) 2 nodes clustered
2) Multiple singleton actors for folder watchers
3) Single api master in each node with routers (router = broadcast-group) that is aimed to distribute work across nodes
4) Multiple file handlers as Cluster-shards

Question is: how can I make the "Api Master" persistent and at the same time work on multiple nodes ?
I need to "remember" all files sent for processing.

Thanks,

Jessie Wadman
@JessieWadman
Nov 08 2017 12:06

@jalchr If I understand you right, you have one actor on each cluster node (API master) that you want to route messages through to reach/create folder watcher actors. And you want API master to know about the folder watchers on its node, so if the node restarts, they should spin up those folder watchers again? If so, then I've faced a similar headache. The way I solved it was to -not- make it persistent, and instead handle the "folder watchers" as cluster-shards and set config akka.cluster.sharding.remember-entities = on. That way Akka will remember them and restart them when they move nodes, or when nodes get added/removed and so on. You can have the folder watchers Tell the ApiMaster that they exist when they start/stop instead, so that each API master will know which folder watchers are running on that node -- instead of using persistence on the Api master. In other words, turn it backwards :)

So, instead of using a ReceivePersistentActor for your API master, and "recover" list of folder watchers, build it on the fly when the folder watchers are started/stopped.

NODE 1

API master (Receive Actor) Folderwatcher (Sharded entity)
<- Hello, I have been started!
Add to list of folder watchers
<- Goodbye, I've been stopped
Remove from list of folder watchers

You can have the API master forward the message to a cluster singleton if you want to keep track of all folder watchers in your entire cluster in a single place. Or you can use Fan/Collect to Ask all of the API masters in the cluster what folder watchers are there. Both should work.

Like so:
Folder watcher starts
Send Tell to local API master on same node
API master adds folder watcher to list of local folder watchers
Forward to cluster singleton
Cluster singleton adds folder watcher to global list of all folder watchers

If anyone else has a hat tric up their sleeve for solving this in some other way, that would be very interesting too.

I hope this helps (and that I understood your problem correct!)

Jessie Wadman
@JessieWadman
Nov 08 2017 12:29
@jalchr Here's some pseudocode that might explain it better. I wrote it in a markdown editor, so it probably won't compile, but you might get the idea :-)
public class FolderWatcher : ReceivePersistentActor
{
   private readonly string folder;

   public FolderWatcher(string folder)
   {
       this.folder = folder;
   }

   protected override void PreStart()
   {
       base.PreStart();
       Context.ActorSelection("/user/api-master").Tell(folder);
   }
}

public class APIMaster : ReceiveActor
{
    private HashSet<IActorRef> localFolderWatchers = new HashSet<IActorRef>();

    public APIMaster(IActorRef clusterSingleton)
    {
        Receive<string>(folder => 
        {
            this.localFolderWatchers.Add(Sender);
            Context.Watch(Sender);
            clusterSingleton.Tell(new FolderwatcherStarted(Sender, folder));
        });

        Receive<Terminated>(t => 
        {
            localFolderWatchers.Remove(t.ActorRef);
            clusterSingleton.Tell(new FolderwatcherStopped(t.ActorRef));
        });
    }
}

public class FolderwatcherStarted
{
    public FolderwatcherStarted(IActorRef folderWatcher, string folder)
    {
        this.Actor = folderWatcher;
        this.Folder = folder;
    }

    public IActorRef Actor { get; private set; }
    public string Folder { get; private set; }
}

public class FolderwatcherStopped
{
    public FolderwatcherStopped(IActorRef folderWatcher)
    {
        this.Actor = folderWatcher;        
    }

    public IActorRef Actor { get; private set; }    
}

public class FolderwatcherDirectory : ReceiveActor
{
    private readonly Dictionary<IActorRef, string> foldersWatched = new Dictionary<IActorRef, string>();

    public FolderwatcherDirectory()
    {
        Receive<FolderwatcherStarted>(msg => foldersWatched[msg.Actor] = msg.Folder);
        Receive<FolderwatcherStopped>(msg => foldersWatched.Remove(msg.Actor));

        Receive<CheckIfFolderIsWatched>(msg => Sender.Tell(foldersWatched.ContainsValue(msg.Folder));
    }
}
Abdelmawla Mohamed
@abdomohamed
Nov 08 2017 13:25
Let's say I've an actor that do maintin it's state using snapshots, so in case of any failure actor will be restarted and snapshot will be played again. Adding to the previous this actor will be creating stream for doing some data migration processing. The source of this stream will be populated from data saved in the snapshot offer, if there is any. if not will be fetching it from some where else (APi call). Based on that scenario, i'd like to restart the actor if any failure happened within the stream, so I'm wondering how this can be achieved ? Also is this a good approach for solving the problem ?
jalchr
@jalchr
Nov 08 2017 14:14

Hey @JessieWadman ... thanks for the extensive effort in bringing this out.
However, I thing we have a slight difference. Yes, I agree that ApiMaster can not be set to persist.
Rather, I'm thinking of creating a "RememberActor" which should be a singleton and persistent.
I'm planning to forward all messages to it from watchers.
Then forward them to ApiMaster, which distributes the load across nodes ...

Thoughts, anyone ?

Stijn Herreman
@stijnherreman
Nov 08 2017 16:13
I've an actor that takes an IActorRef in the constructor. What's the proper way to stub this dependency and have it respond to certain messages, is it DelegateAutoPilot? I could also use some generic library like NSubstitute and stub Tell(), but it makes it a bit harder not having a Sender available.
Bart de Boer
@boekabart
Nov 08 2017 21:15
Definitely AutoPilot