5

Is there a way to require the system to spawn certain actors on startup.

Currently I activate the set of actors that I need in Program.cs after the actor registration.

This is working ok, but I occasionally get a ReminderLoadInProgressException because the actor that is being activated needs to register a reminder but not all reminders have been loaded at the time it tries to do that.

Is there standard way to seed the cluster?

abatishchev
  • 98,240
  • 88
  • 296
  • 433

2 Answers2

9

No, you need something to activate your actors.

Program.cs isn't the best place to do it because its execution doesn't really line up with your service's lifecycle, as you've probably noticed.

if you need to activate some actors when your actor service starts up, a good place to do that would be in the RunAsync method of your actor service. You can customize your actor service by writing a service that derives from it, same as you do when you write a stateful service:

EDIT: make sure you call and await base.RunAsync() first!

class MyActorService : ActorService
{
    public MyActorService(StatefulServiceContext context, ActorTypeInformation typeInfo, Func<ActorBase> newActor)
        : base(context, typeInfo, newActor)
    { }

    protected async override Task RunAsync(CancellationToken cancellationToken)
    {
        await base.RunAsync(cancellationToken);

        var proxy = ActorProxy.Create<IMyActor>(new ActorId(0));
        await proxy.StartDoingStuff();
    }
}

And then register your actor service so the runtime knows to use it to host your actor instances:

static class Program
{
    private static void Main()
    {
        ActorRuntime.RegisterActorAsync<MyActor>(
            (context, actorType) => new MyActorService(context, actorType, () => new MyActor()))
            .GetAwaiter().GetResult();

        Thread.Sleep(Timeout.Infinite);
    }
}

Just make sure that actor method is idempotent, because this will execute every time the primary replica of your actor service is started up (after failover, resource balancing, application upgrade, etc), but the nice thing is it won't execute before your actor service is ready and it will always execute when the service comes up.

See here for more info on how to work with the actor service: https://azure.microsoft.com/en-us/documentation/articles/service-fabric-reliable-actors-platform/

Vaclav Turecek
  • 9,020
  • 24
  • 29
  • We're using this solution in order to launch (for now) about 1500 Actor instance. As we are still in the testing phase, the actors are not doing anything. However on a 5 Nodes cluster (A1 machines) all the nodes are consuming a very high amount of CPU (most of the time at 100% usage). Do you think that it is normal? We plan to use this technique to spawn 20K instances actually doing something (meaning doing real work, not just waiting as for now). Could you advise on "the good way" to do that? – Georges Legros Jul 27 '16 at 12:23
  • Calling `base.RunAsync` in custom `ActorService` subclass is not sufficient - you will still get occasional `ReminderLoadInProgressException` as illustrated in this sample app - https://github.com/PaloMraz/SpawnActorsApp. Run it from VS on local 5Node dev cluster a couple of times and you will surely see the “********* RegisterReminderAsync failed” message in the “Diagnostic Events” window… – Palo Mraz Mar 03 '20 at 16:26
  • In the above sample, I have implemented a workaround using actor timers to retry the reminder registration. If you want to see the erroneous behavior discused above, please checkout the previous commit https://github.com/PaloMraz/SpawnActorsApp/commit/1c3e2b8265341b92c0686eaba4718253ec76eaaf – Palo Mraz Mar 04 '20 at 15:34
6

Cannot comment so I'm putting the comment here:

For the sake of completeness: if your ActorService subclass does not have the default name (from the ServiceManifest.xml - for example you generate service instances dynamically), you will have to use the two-parameter ActorProxy.Create overload like this:

var actor = ActorProxy.Create<IMyActor>(new ActorId(0), this.Context.ServiceName);

Otherwise you would get "Service does not exist" exception:

System.Fabric.FabricServiceNotFoundException: Service does not exist. 
---> System.Runtime.InteropServices.COMException: Exception from HRESULT: 0x80071BCD at
System.Fabric.Interop.NativeClient.IFabricServiceManagementClient4
.EndResolveServicePartition(IFabricAsyncOperationContext context)
...
Palo Mraz
  • 625
  • 5
  • 16