Permalink
Cannot retrieve contributors at this time
Name already in use
A tag already exists with the provided branch name. Many Git commands accept both tag and branch names, so creating this branch may cause unexpected behavior. Are you sure you want to create this branch?
akka/akka-persistence/src/main/scala/akka/persistence/snapshot/SnapshotStore.scala
Go to fileThis commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
180 lines (157 sloc)
6.01 KB
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
* Copyright (C) 2009-2021 Lightbend Inc. <https://www.lightbend.com> | |
*/ | |
package akka.persistence.snapshot | |
import scala.concurrent.ExecutionContext | |
import scala.concurrent.Future | |
import scala.concurrent.duration._ | |
import akka.actor._ | |
import akka.pattern.CircuitBreaker | |
import akka.pattern.pipe | |
import akka.persistence._ | |
/** | |
* Abstract snapshot store. | |
*/ | |
trait SnapshotStore extends Actor with ActorLogging { | |
import SnapshotProtocol._ | |
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 = { | |
val eventStream = context.system.eventStream // used from Future callbacks | |
implicit val ec: ExecutionContext = context.dispatcher | |
{ | |
case LoadSnapshot(persistenceId, criteria, toSequenceNr) => | |
if (criteria == SnapshotSelectionCriteria.None) { | |
senderPersistentActor() ! LoadSnapshotResult(snapshot = None, toSequenceNr) | |
} else { | |
breaker | |
.withCircuitBreaker(loadAsync(persistenceId, criteria.limit(toSequenceNr))) | |
.map { sso => | |
LoadSnapshotResult(sso, toSequenceNr) | |
} | |
.recover { | |
case e => LoadSnapshotFailed(e) | |
} | |
.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) 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) 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. | |
* | |
* If the future `Option` is `None` then all events will be replayed, | |
* i.e. there was no snapshot. If snapshot could not be loaded the `Future` | |
* should be completed with failure. That is important because events may | |
* have been deleted and just replaying the events might not result in a valid | |
* state. | |
* | |
* 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 | |
} |