Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

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.
like image 786
Mauricio Aviles Avatar asked Oct 19 '22 15:10

Mauricio Aviles


2 Answers

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.

like image 146
Jesse Carter Avatar answered Oct 21 '22 09:10

Jesse Carter


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.

like image 45
Mauricio Aviles Avatar answered Oct 21 '22 10:10

Mauricio Aviles