Triggering snapshot persistence in EventsourcedProcessor (akka-persistence)

268 views
Skip to first unread message

Andrew Easter

unread,
Nov 5, 2013, 8:20:00 PM11/5/13
to akka...@googlegroups.com
Hi,

Hope I'm not taking liberties by bombarding you with questions at the moment!

In the akka-persistence docs, there's an example of EventsourcedProcessor showing a basic implementation. It's up to the implementor to implement receiveReplay() and receiveCommand(). It's interesting that the concept of recovering from a snapshot is concrete in the API whereas triggering a snapshot to be saved is the responsibility of the implementor. This is sort of okay because it's not always true that implementors will want to use snapshots - thus it shouldn't be enforced. Also, even when using snapshots, the application will probably want to decide on snapshot scheduling (although they may want to configure this outside of the code - see below).

The thing that sort of sticks out in the example code is the use of become/unbecome. The actor, therefore, switches it's receive loop such that it does not handle a save snapshot message (until a further message received triggers unbecome). This kind of feels a bit weird. I suppose it's feasible that an actor, when in some specific state, may not wish to save a snapshot, but that might be an edge case. Am I being pedantic to suggest that the triggering of snapshot persistence could somehow become more transparent to the implementor?

My reason for asking is that this has come up in the side project I'm working on. In that case, snapshot persistence will be integrated into the framework and I don't want to burden the implementor with having to worry about it - ideally they'd be able to use features like become/unbecome without needing to even think about snapshot persistence (other than providing a way to expose current state). Possibly they'd configure snapshot persistence via app config - I want aggregate roots (actors) to focus as much as possible on just business logic rather than persistence concerns. I can think of some workarounds - and maybe this is just the nature of building an abstraction layer over the top of akka-persistence - but I'm interested to see whether you might agree with my observation.

Maybe this is more a question about patterns for working with become/unbecome where all potential states share some common message handling logic?

Andrew

Martin Krasser

unread,
Nov 6, 2013, 1:54:50 AM11/6/13
to akka...@googlegroups.com
Hi Andrew


On 06.11.13 02:20, Andrew Easter wrote:
Hi,

Hope I'm not taking liberties by bombarding you with questions at the moment!

In the akka-persistence docs, there's an example of EventsourcedProcessor showing a basic implementation. It's up to the implementor to implement receiveReplay() and receiveCommand(). It's interesting that the concept of recovering from a snapshot is concrete in the API whereas triggering a snapshot to be saved is the responsibility of the implementor. This is sort of okay because it's not always true that implementors will want to use snapshots - thus it shouldn't be enforced.

Right, only the application knows when it's the right time for saving a state snapshot (or saving one at all). Once snapshots have been saved for a processor, the default behavior during recovery is to recover from the latest snapshot. This is not hardcoded but can be customized. For example, applications can select an earlier snapshot or no snapshot at all (see Recover API docs and recovery customization)


Also, even when using snapshots, the application will probably want to decide on snapshot scheduling (although they may want to configure this outside of the code - see below).

The reason why snapshot saving with akka-persistence isn't done "outside of the code" is that akka-persistence doesn't make any assumption how a processor maintains state in memory (single var, multiple var, STM refs or whatever). This can only be externalized by an application/framework that makes assumptions on how state is kept in memory (and maybe also about its structure).



The thing that sort of sticks out in the example code is the use of become/unbecome. The actor, therefore, switches it's receive loop such that it does not handle a save snapshot message (until a further message received triggers unbecome). This kind of feels a bit weird.

That's just an example. It may make sense for other applications to react to snapshotting requests at any time. Anyway, only an application can know what's appropriate.


I suppose it's feasible that an actor, when in some specific state, may not wish to save a snapshot, but that might be an edge case. Am I being pedantic to suggest that the triggering of snapshot persistence could somehow become more transparent to the implementor?

See previous comments.


My reason for asking is that this has come up in the side project I'm working on. In that case, snapshot persistence will be integrated into the framework and I don't want to burden the implementor with having to worry about it - ideally they'd be able to use features like become/unbecome without needing to even think about snapshot persistence

If you want to decorate the different behaviors with common behavior (e.g. processing snapshot requests) you could do that like in the following example. Implementors then use switchTo instead of become/unbecome.

class MyProcessor extends EventsourcedProcessor {
  def switchTo(behavior: Receive) =
    context.become(takeSnapshot orElse behavior)

  val takeSnapshot: Receive = {
    case "snap" => saveSnapshot(...)
  }

  val defaultBehavior: Receive = {
    case "foo" => switchTo(otherBehavior)
  }

  val otherBehavior: Receive = {
    case "bar" => switchTo(defaultBehavior)
  }

  val receiveCommand: Receive = receiveSnapshotRequest orElse defaultBehavior

}

(other than providing a way to expose current state). Possibly they'd configure snapshot persistence via app config - I want aggregate roots (actors) to focus as much as possible on just business logic rather than persistence concerns. I can think of some workarounds - and maybe this is just the nature of building an abstraction layer over the top of akka-persistence - but I'm interested to see whether you might agree with my observation.

Maybe this is more a question about patterns for working with become/unbecome where all potential states share some common message handling logic?

Yes, see example above. If we want to allow applications to continue using become/unbecome while preserving common application-specific behavior, Actor.aroundReceive (introduced in Akka 2.3) would need to be made accessible for overriding (currently it is private[akka]). BTW, this receive interceptor allows a Processor to continue journaling even if implementors use become/unbecome and it was introduced for exactly this purpose. I'm not sure yet if it's a good idea to make it public, as it is a good candidate for being abused. Maybe a good compromise could be a public Processor-specific interceptor hook. Thoughts?

Cheers,
Martin


Andrew
--
You received this message because you are subscribed to the Google Groups "Akka Developer List" group.
To unsubscribe from this group and stop receiving emails from it, send an email to akka-dev+u...@googlegroups.com.
For more options, visit https://groups.google.com/groups/opt_out.

-- 
Martin Krasser

blog:    http://krasserm.blogspot.com
code:    http://github.com/krasserm
twitter: http://twitter.com/mrt1nz

Andrew Easter

unread,
Nov 6, 2013, 12:12:16 PM11/6/13
to akka...@googlegroups.com, kras...@googlemail.com
Hi Martin,

Yes, see example above. If we want to allow applications to continue using become/unbecome while preserving common application-specific behavior, Actor.aroundReceive (introduced in Akka 2.3) would need to be made accessible for overriding (currently it is private[akka]). BTW, this receive interceptor allows a Processor to continue journaling even if implementors use become/unbecome and it was introduced for exactly this purpose. I'm not sure yet if it's a good idea to make it public, as it is a good candidate for being abused. Maybe a good compromise could be a public Processor-specific interceptor hook. Thoughts?

I agree with you that making Actor.aroundReceive public would be a very questionable decision. An interceptor hook, however, would be great! 

Cheers,

Andrew  
Reply all
Reply to author
Forward
0 new messages