Go C#

DB Identity Lookup

This strategy uses an external database to keep information about spawned actors in the cluster. Actor’s placement is first checked in the PID cache. If it is not present there then, is retrieved from the database. If actor information is not found there, then based on the member strategy new actor is spawned. After successful placement, information about it is saved to the database.

Characteristics

  • It limits the number of actors that need to move to a new node on topology change. Only actors hosted on the leaving member need to move.
  • Activation of an actor might take 2 database calls and 1 in-cluster network hop, which increases latency.
  • Adds an external dependency to the cluster.
  • The database needs to be scaled separately from the cluster.
  • Consistency guarantees of the database can be leveraged for ensuring that every cluster member has the same view of actor activations.

Implementations

Redis

static IIdentityLookup GetRedisIdentityLookup()
{
    var multiplexer = ConnectionMultiplexer.Connect("redis_connection_string");

    return new IdentityStorageLookup(
        new RedisIdentityStorage("mycluster", multiplexer, maxConcurrency: 50)
    );
}

Mongo

static IIdentityLookup GetMongoIdentityLookup()
{
    var client = new MongoClient("mongo_connection_string");
    var db = client.GetDatabase("ClusterIdentities");
    var pidsCollection = db.GetCollection<PidLookupEntity>("pids");

    return new IdentityStorageLookup(
        new MongoIdentityStorage("mycluster", pidsCollection, maxConcurrency: 50)
    );
}

Custom implementation

It is possible to add support for other databases by implementing the IIdentityStorage interface.

Limiting concurrency

Too many parallels calls to the database may overload it. Load spikes are expected when cluster topology changes or rolling upgrade of the application runs. This is why the identity storage lookups allow you to specify concurrency limits. Note: these limits are scoped to a single node, so the actual global limit is node limit * number of nodes.

Internally, AsyncSemaphore is used to enforce the limits. It can be reused by your own implementations as well.

Under the hood

From a consumer perspective, the IdentityLookup is called if the actor system currently has no knowledge of the location of a virtual actor. The following diagram shows how this is done in DB Identity Lookup:

sequenceDiagram

    participant DBIdentityLookup
    participant PidCache
    participant DB
    participant MemberList
    participant Activator
    participant VirtualActor


    DBIdentityLookup->>DB: Get Placement Info
    DB-->>DBIdentityLookup: Placement Info

    alt Placement is missing
        DBIdentityLookup->>MemberList: GetActivator(kind)
        MemberList-->>DBIdentityLookup: Member info
        DBIdentityLookup->>Activator: ActivationRequest
        note right of Activator: Spawn the virtual actor<br>and return the PID
        Activator->>VirtualActor: Spawn
        Activator-->>DBIdentityLookup: ActivationResponse(PID)
        DBIdentityLookup->>DB: Store Placement Info
        DB-->>DBIdentityLookup:Stored
        DBIdentityLookup->>PidCache: Store PID
        PidCache-->>DBIdentityLookup: Stored
    end


Icon