akkadotnet / akka.net

Canonical actor model implementation for .NET with local + distributed actors in C# and F#.
http://getakka.net
Other
4.7k stars 1.04k forks source link

Akka.Persistence enhancements #1956

Open Horusiath opened 8 years ago

Horusiath commented 8 years ago

This is proposal of few possible changes that we could/should introduce in Akka.Persistence plugin to make it work in more reliable, user-friendly way.

Allow to use the same journal/snapshot store under different settings

This was mentioned many times already. It should be possible for one i.e. to use SqlServerJournal in two different scenarios (like custom domain events and cluster sharding), each of those working under different settings, writing events to different databases/tables.

Give ability to set persistence plugins programatically

Since we have no real support for HOCON, and it's whole schema is dynamic and not validated, there is a lot of config-related issues when working with persistence. To solve them, we could introduce gradual movement towards some more code-as-configuration way of working with settings.

Example:

using(var system = ActorSystem.Create("sys") {
    // set default journal, using default configuration path: akka.persistence.journal.sql-server
    system.UseJournal<SqlServer>(isDefault: true);
    // refer to some custom path  
    system.UseJournal<SqlServer>("akka.persistence.journal.cluster-sharding");
}
Journal serialization should work with non-type manifests

Right now many of the journal/snapshot implementations works only with manifests being qualified assembly type names. This is in many cases problematic. It should be possible to use string-based manifest not associated with types to serialize/deserialize corresponding events.

Any other ideas?

kantora commented 8 years ago

Is it possible to set journal/snapshot configuration with use of akka.actor.deployment configuration section? So it can be actor specific.

Horusiath commented 8 years ago

@kantora You can already set journal/snapshot store for each persistent actor using JournalPluginId and SnapshotPluginId. What would you need deployment options for?

kantora commented 8 years ago

Oops, sorry. Just missed it :(.

Danthar commented 8 years ago

@Horusiath I like this idea. +1 from me.

Horusiath commented 7 years ago

Ok some further ideas. Those could be used to ease the users experience through better async/await interop. Things like event persistence, snapshot related methods and recovery procedure all works in request/response model, where every request type has at least one success/failure acknowledgement. Example of an alternate persistent actor base class:

abstract class PersistentActor<TState, TCommand, TEvent>
{
    protected PersistentActor(string persistenceId) { }

    /// Persistent state of an actor.
    public TState State { get; set; }

    /// Unique identifier of a persistent actor.
    public string PersistenceId { get; }

    /// Persistent actor's equivalent of Receive
    protected abstract Task OnCommand(TCommand command);

    /// Method called once persistent actor starts recovery procedure
    protected abstract void OnRecover(TEvent e);

    /// Method called when persistent actor's snapshot has been found.
    /// By default it just assigns it to <see cref="State"/>
    protected virtual void OnSnapshot(TState snapshot);

    /// Persists an event and completes once persist was successfully confirmed.
    protected Task Persist(TEvent e) { }

    /// Saves a snapshot in the database.
    protected Task SaveSnapshot(TState state) { }

    /// Removes all persisted state (events, snapshots and metadata) associated
    /// with current persistent actor.
    protected Task ClearAll() { }
}

Simplified example:

sealed class User : PersistentActor<UserState, IUserCommand, IUserEvent>
{
    public User(string userId) : base(userId) { }

    protected override void OnRecover(IUserEvent e) => UpdateState(e)

    protected override async Task OnCommand(IUserCommand cmd) 
    {
        switch (cmd)
        {
            case CreateUser create:
                var e = new UserCreated(create.UserName, create.Password);
                await Persist(e);
                // at this point event persistence has been confirmed
                UpdateState(e);                

            // ...other cases
        }
    }

    private void UpdateState(IUserEvent) { ... }
}

I belive we'll be able to achieve something like that. The case here is that for communication between event journal and persistent actor we'd need a side channel/mailbox, because await acknowledgements would happen aside of core processing logic coming from user actors, and it had to be awaited and processed first.

daniellittledev commented 7 years ago

@Horusiath @kantora how do you set journal/snapshot store such that each type of persistent actor can use a different JournalPluginId and SnapshotPluginId?

kantora commented 7 years ago

@Lavinski To be honest, never used it, but as far as I understood you can set JournalPluginId and SnapshotPluginId properties in your actor to point to specific configurations.