How to pass data to `OnActivateAsync()` to initialize my stateful actor?

1.4k views Asked by At

I am creating a reliable, stateful, service actor.

Question:

Is there a way to pass initialization data during the actor proxy creation (ActorProxy.Create())? Basically an equivalent to a constructor for my actor.

Current thoughts:

I can achieve this by following up the proxy creation call with an actor method call in charge of initializing the state.

E.g.

//Danger, the following calls are not atomic
ITokenManager tokenActor = ActorProxy.Create<IMyActor>(actorId, "AppName");
//Something could happen here and leave my actor in an unknown state
await tokenActor.InitializeAsync(desiredInitialState);

My concern with such approach:

  • This operation is not atomic. It may leave my actor in an inconsistent state
  • This initialization method is now available throughout the life of the actor, which is undesired.
3

There are 3 answers

0
Jesse Carter On BEST ANSWER

A couple thoughts for you here. For one, is the data that you need to do initialization really not available to the actor itself during OnActivateAsync? Normally if I rely on getting some initial data into my actor's state this is how I would do it.

protected override Task OnActivateAsync()
{
   if (State == null)
   {
       var initialState = await externalSource.GetSomeState();
       // simplified here but map the values properly onto the actual actor state
       this.State = initialState;
       return base.OnActivateAsync();
   }
}

The other thought is that if you truly can't have the actor retrieve the data during it's own activation it's very easy for you to create a boolean property that is part of the actor state indicating whether or the other activation you're talking about has ever occurred.

 public Task InitializeAsync(State someState)
 {
     if (State.IsActivated)
     {
         // log out here that someone is attempting to reactivate when they shouldn't
         return Task.CompletedTask;
     }

     State = someState;
     State.IsActivated = true;
     return Task.CompletedTask;
 }

This way while technically the method will be available to be called for the lifetime of the actor, you have a single threaded guarantee that it will only actually do something the very first time it is called.

4
Mikhail Shilkov On

Proxy creation is not equivalent to constructor. In Service Fabric the client is not supposed to know whether the actor has already been created or not, and the lifecycle is managed by the runtime.

So the actor itself should initialize to some default state. It's the job of actor implementation to prevent other calls before initialization calls and prevent multiple initialization if needed. As actors are always single-threaded, it can be easily achieved with something like boolean flags.

0
Mauricio Aviles On

It seems like the best approach to have an atomic initialization is keep the initialization data in some external store, and during OnActivateAsync() consume this data from that store.