Search code examples
c#azure-service-fabricservice-fabric-actor

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.

Solution

  • 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.