1
votes

We are using Azure Service Fabric and are using actors to model specific devices, using the id of the device as the ActorId. Service Fabric will instantiate a new actor instance when we request an actor for a given id if it is not already instantiated, but I cannot seem to find an api that allows me to query if a specific device id already has an instantiated actor.

I understand that there might be some distributed/timing issues in obtaining the point-in-time truth but for our specific purpose, we do not need a hard realtime answer to this but can settle for a best guess. We would just like to, in theory, contact the current primary for the specific partition resolved by the ActorId and get back whether or not the device has an instantiated actor.

Ideally it is a fast/performant call, essentially faster than e.g. instantiating the actor and calling a method to understand if it has been initialized correctly and is not just an "empty" actor.

You can use the ActorServiceProxy to iterate through the information for a specific partition but that does not seem to be a very performant way of obtaining the information.

Anyone with insights into this?

2
Currently there is no way to do this. A workaround might be to have an actor store its ID in an azure table , stateful service or other store and call that store to get this information. You won't have to activate deactived actors to get their existance this way.Peter Bons

2 Answers

1
votes

The only official way you can check if the actor has been activated in any Service Partition previously is using the ActorServiceProxy query, like described here:

IActorService actorServiceProxy = ActorServiceProxy.Create(
    new Uri("fabric:/MyApp/MyService"), partitionKey);

ContinuationToken continuationToken = null;
do
{
    PagedResult<ActorInformation> page = await actorServiceProxy.GetActorsAsync(continuationToken, cancellationToken);

    var actor = page.Items.FirstOrDefault(x => x.ActorId == idToFind);

    continuationToken = page.ContinuationToken;
}
while (continuationToken != null);

By the nature of SF Actors, they are virtual, that means they always exist, even though you didn't activated then previously, so it make a bit harder to do this check.

As you said, it is not performant to query all actors, so, the other workarounds you could try is:

  1. Store the IDs in a Reliable Dictionary elsewhere, every time an Actor is activated you raise an event and insert the ActorIDs in the Dictionary if not there yet.

    • You can use the OnActivateAsync() actor event to notify it's creation, or
    • You can use the custom actor factory in the ActorService to register actor activation
    • You can store the dictionary in another actor, or another StatefulService
  2. Create a property in the actor that is set by the actor itself when it is activated.

    • The OnActivateAsync() check if this property has been set before
    • If not set yet, you set a new value and store in a variable (a non persisted value) to say the actor is new
    • Whenever you interact with actor you set this to indicate it is not new anymore
    • The next activation, the property will be already set, and nothing should happen.
  3. Create a custom IActorStateProvider to do the same as mentioned in the option 2, instead of handle it in the actor it will handle a level underneath it. Honestly I think it is a bit of work, would only be handy if you have to do the same for many actor types, the option 1 and 2 would be much easier.

  4. Do as Peter Bons Suggested, store the ActorID outside the ActorService, like in a DB, I would only suggest this option if you have to check this from outside the cluster.

.

The following snipped can help you if you want to manage these events outside the actor.

private static void Main()
{
    try
    {
        ActorRuntime.RegisterActorAsync<NetCoreActorService>(
           (context, actorType) => new ActorService(context, actorType,
                    new Func<ActorService, ActorId, ActorBase>((actorService, actorId) =>
                    {
                        RegisterActor(actorId);//The custom method to register the actor if new
                        return (ActorBase)Activator.CreateInstance(actorType.ImplementationType, actorService, actorId);
                    })
                )).GetAwaiter().GetResult();

        Thread.Sleep(Timeout.Infinite);
    }
    catch (Exception e)
    {
        ActorEventSource.Current.ActorHostInitializationFailed(e.ToString());
        throw;
    }
}

private static void RegisterActor(ActorId actorId) 
{
    //Here you will put the logic to register elsewhere the actor creation
}
0
votes

Alternatively, you could create a stateful DeviceActorStatusActor which would be notified (called) by DeviceActor as soon as it's created. (Share the ActorId for correlation.)

Depending on your needs you can also register multiple Actors with the same status-tracking actor.

You'll have great performance and near real-time information.