akka.netakka.net-persistence

Can I Read/Write from separate actors with same PersistenceId?


The Petabridge blog's Akka.Persistence intro makes it clear that you can't have multiple actors with the same PersistenceId:

The PersistenceId field is important - it uniquely identifies an entity that is persisting its state using Akka.Persistence, and there should be exactly one persistent actor at any given time for a single PersistenceId.

[...] so imagine if you have two actors with the same PersistenceId but different sequence numbers writing to the same store. It will be chaos and will inevitably error out - so that’s why it’s crucial that every PersistenceId be globally unique within your ActorSystem (at least for all actors writing to that store.)

I can think of a scenario where you would have two separate actors: one that takes care of saving persistence state to database (i.e. calls Persist()), and another one that replays messages from the journal when manually requested to do so (i.e. calls Recover()). The read and write operations would occur from different actors. Only one ever writes, and only one ever reads. However, both need the same PersistenceId.

I believe that in this scenario it should be safe to have two actors using the same PersistenceId. But given the above warnings quoted above, is there any reason why such an approach could be dangerous in practice?


Solution

  • I can think of a scenario where you would have two separate actors: one that takes care of saving persistence state to database (i.e. calls Persist()), and another one that replays messages from the journal when manually requested to do so (i.e. calls Recover()). The read and write operations would occur from different actors. Only one ever writes, and only one ever reads. However, both need the same PersistenceId.

    The behaviour you require is already exposed as Persistent Actors and Persistent Views. From the docs:

    While a persistent actor may be used to produce and persist events, views are used only to read internal state based on them. Like the persistent actor, a view has a PersistenceId to specify a collection of events to be resent to current view. This value should however be correlated with the PersistentId of an actor who is the producer of the events.

    Edit: updated to provide more info on how to access events in the Persistent View.

    You can load from a journal by overriding the Receive method of a Persistent View. The argument for this method is an object, so you'll need to cast that object to whatever event(s) you have persisted via the Persistent Actor.

    The Receive method also handles any other messages you pass to the View - e.g. a read request from the presentation layer. I usually store a list of events internally in the View and return a custom view model from these.

    protected override bool Receive(object message)
    {
        // if the message is a previously persisted event, update our internal list
        var e = message as MyEvent;
        if (e != null) _events.Add(e);
        return true;
    
        // if the message is a request for a view model, read from our list of stored events
        var r = message as ReadRequest;
        if (r == null) return false;
        Sender.Tell(new ViewModel(_events));
        return true;
    }