pekko/akka-persistence/src/main/scala/akka/persistence/snapshot/SnapshotStore.scala
Patrik Nordwall c5851600f0 +per #17837 Detect (and repair) corrupt event streams
refactoring of plugin config
2015-08-14 14:01:45 +02:00

142 lines
5.1 KiB
Scala

/**
* Copyright (C) 2009-2015 Typesafe Inc. <http://www.typesafe.com>
* Copyright (C) 2012-2013 Eligotech BV.
*/
package akka.persistence.snapshot
import scala.concurrent.duration._
import scala.concurrent.Future
import akka.actor._
import akka.pattern.pipe
import akka.persistence._
import akka.pattern.CircuitBreaker
/**
* Abstract snapshot store.
*/
trait SnapshotStore extends Actor with ActorLogging {
import SnapshotProtocol._
import context.dispatcher
private val extension = Persistence(context.system)
private val publish = extension.settings.internal.publishPluginCommands
private val breaker = {
val cfg = extension.configFor(self)
val maxFailures = cfg.getInt("circuit-breaker.max-failures")
val callTimeout = cfg.getDuration("circuit-breaker.call-timeout", MILLISECONDS).millis
val resetTimeout = cfg.getDuration("circuit-breaker.reset-timeout", MILLISECONDS).millis
CircuitBreaker(context.system.scheduler, maxFailures, callTimeout, resetTimeout)
}
final def receive = receiveSnapshotStore.orElse[Any, Unit](receivePluginInternal)
final val receiveSnapshotStore: Actor.Receive = {
case LoadSnapshot(persistenceId, criteria, toSequenceNr)
breaker.withCircuitBreaker(loadAsync(persistenceId, criteria.limit(toSequenceNr))) map {
sso LoadSnapshotResult(sso, toSequenceNr)
} recover {
case e LoadSnapshotResult(None, toSequenceNr)
} pipeTo senderPersistentActor()
case SaveSnapshot(metadata, snapshot)
val md = metadata.copy(timestamp = System.currentTimeMillis)
breaker.withCircuitBreaker(saveAsync(md, snapshot)) map {
_ SaveSnapshotSuccess(md)
} recover {
case e SaveSnapshotFailure(metadata, e)
} to (self, senderPersistentActor())
case evt: SaveSnapshotSuccess
try tryReceivePluginInternal(evt) finally senderPersistentActor ! evt // sender is persistentActor
case evt @ SaveSnapshotFailure(metadata, _)
try {
tryReceivePluginInternal(evt)
breaker.withCircuitBreaker(deleteAsync(metadata))
} finally senderPersistentActor() ! evt // sender is persistentActor
case d @ DeleteSnapshot(metadata)
breaker.withCircuitBreaker(deleteAsync(metadata)).map {
case _ DeleteSnapshotSuccess(metadata)
}.recover {
case e DeleteSnapshotFailure(metadata, e)
}.pipeTo(self)(senderPersistentActor()).onComplete {
case _ if (publish) context.system.eventStream.publish(d)
}
case evt: DeleteSnapshotSuccess
try tryReceivePluginInternal(evt) finally senderPersistentActor() ! evt
case evt: DeleteSnapshotFailure
try tryReceivePluginInternal(evt) finally senderPersistentActor() ! evt
case d @ DeleteSnapshots(persistenceId, criteria)
breaker.withCircuitBreaker(deleteAsync(persistenceId, criteria)).map {
case _ DeleteSnapshotsSuccess(criteria)
}.recover {
case e DeleteSnapshotsFailure(criteria, e)
}.pipeTo(self)(senderPersistentActor()).onComplete {
case _ if (publish) context.system.eventStream.publish(d)
}
case evt: DeleteSnapshotsFailure
try tryReceivePluginInternal(evt) finally senderPersistentActor() ! evt // sender is persistentActor
case evt: DeleteSnapshotsSuccess
try tryReceivePluginInternal(evt) finally senderPersistentActor() ! evt
}
/** Documents intent that the sender() is expected to be the PersistentActor */
@inline private final def senderPersistentActor(): ActorRef = sender()
private def tryReceivePluginInternal(evt: Any): Unit =
if (receivePluginInternal.isDefinedAt(evt)) receivePluginInternal(evt)
//#snapshot-store-plugin-api
/**
* Plugin API: asynchronously loads a snapshot.
*
* This call is protected with a circuit-breaker.
*
* @param persistenceId id of the persistent actor.
* @param criteria selection criteria for loading.
*/
def loadAsync(persistenceId: String, criteria: SnapshotSelectionCriteria): Future[Option[SelectedSnapshot]]
/**
* Plugin API: asynchronously saves a snapshot.
*
* This call is protected with a circuit-breaker.
*
* @param metadata snapshot metadata.
* @param snapshot snapshot.
*/
def saveAsync(metadata: SnapshotMetadata, snapshot: Any): Future[Unit]
/**
* Plugin API: deletes the snapshot identified by `metadata`.
*
* This call is protected with a circuit-breaker.
*
* @param metadata snapshot metadata.
*/
def deleteAsync(metadata: SnapshotMetadata): Future[Unit]
/**
* Plugin API: deletes all snapshots matching `criteria`.
*
* This call is protected with a circuit-breaker.
*
* @param persistenceId id of the persistent actor.
* @param criteria selection criteria for deleting.
*/
def deleteAsync(persistenceId: String, criteria: SnapshotSelectionCriteria): Future[Unit]
/**
* Plugin API
* Allows plugin implementers to use `f pipeTo self` and
* handle additional messages for implementing advanced features
*/
def receivePluginInternal: Actor.Receive = Actor.emptyBehavior
//#snapshot-store-plugin-api
}