5

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.
abatishchev
  • 98,240
  • 88
  • 296
  • 433
Mauricio Aviles
  • 1,074
  • 9
  • 24
  • Please provide an example of what you mean by undesired state after not atomic operation. Actor does not exist before the first call, and each call can be made atomic. – Mikhail Shilkov Feb 23 '16 at 22:30
  • Just edited the question to add an example. – Mauricio Aviles Feb 24 '16 at 18:29
  • @Mikhail -- Your last comment is misleading. Not only the call to create the actor proxy will create/activate the actor, but it will also result in an invocation to OnActivateAsync if this is the first time the actor is activated. I have this working on a test project. – Mauricio Aviles Feb 24 '16 at 20:29

3 Answers3

2

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.

Jesse Carter
  • 20,062
  • 7
  • 64
  • 101
1

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.

Mauricio Aviles
  • 1,074
  • 9
  • 24
0

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.

Mikhail Shilkov
  • 34,128
  • 3
  • 68
  • 107
  • I agree, proxy creation is not equivalent to constructors. However, I'd like to imagine that the framework provides some reliable and atomic operation to initialize actors, and to pass the necessary context to do so in the right state and not in a "default state". I disagree with the statement: "In Service Fabric the client is not supposed to know whether the actor has been created" after all, the client must keep a directory of the actors and hence it should know when it is requesting a proxy for a new actor vs. a proxy for an existing actor. – Mauricio Aviles Feb 23 '16 at 20:34
  • I appreciate thoughts, but this is not an answer to the question. As far as you can tell, you are not sure if this is possible or not. Since this is not answer, I think this is better suited for a comment on the question. Hence the -1. – Mauricio Aviles Feb 23 '16 at 22:06
  • I just noticed you down-voted my question after the -1 on your answer... That does not seem very ethical nor mature. The question raise a valid problem on Service Fabric. – Mauricio Aviles Feb 23 '16 at 22:09
  • @MauricioAviles, just FYI, you should not keep a dictionary/list of your Actors. Whenever you want to call it, you should resolve it with the ActorProxy. The reason for this is that SF might have moved it in between, and the ActorProxy is super cheap to call. The same goes for ServiceProxy. – anderso Feb 26 '16 at 12:20