2013-09-14 14:19:18 +02:00
/* *
2014-02-02 19:05:45 -06:00
* Copyright ( C ) 2009 - 2014 Typesafe Inc . < http : //www.typesafe.com>
2013-09-14 14:19:18 +02:00
*/
package akka.persistence
2014-01-17 06:58:25 +01:00
import akka.AkkaException
2013-09-14 14:19:18 +02:00
import akka.actor._
import akka.dispatch._
/* *
* An actor that persists ( journals ) messages of type [ [ Persistent ] ] . Messages of other type s are not persisted .
*
* { { {
* import akka.persistence. { Persistent , Processor }
*
* class MyProcessor extends Processor {
* def receive = {
* case Persistent ( payload , sequenceNr ) => // message has been written to journal
* case other => // message has not been written to journal
* }
* }
*
* val processor = actorOf ( Props [ MyProcessor ] , name = "myProcessor" )
*
* processor ! Persistent ( "foo" )
* processor ! "bar"
* } } }
*
* During start and restart , persistent messages are replayed to a processor so that it can recover internal
* state from these messages . New messages sent to a processor during recovery do not interfere with replayed
* messages , hence applications don 't need to wait for a processor to complete its recovery .
*
2013-09-26 09:14:43 +02:00
* Automated recovery can be turned off or customized by overriding the [ [ preStart ] ] and [ [ preRestart ] ] life
* cycle hooks . If automated recovery is turned off , an application can explicitly recover a processor by
* sending it a [ [ Recover ] ] message .
2013-09-14 14:19:18 +02:00
*
* [ [ Persistent ] ] messages are assigned sequence numbers that are generated on a per - processor basis . A sequence
2013-09-26 09:14:43 +02:00
* starts at `1L` and doesn 't contain gaps unless a processor ( logically ) deletes a message
2013-09-14 14:19:18 +02:00
*
* During recovery , a processor internally buffers new messages until recovery completes , so that new messages
* do not interfere with replayed messages . This internal buffer ( the ' 'processor stash ' ' ) is isolated from the
* ' 'user stash ' ' inherited by `akka.actor.Stash` . `Processor` implementation classes can therefore use the
* ' 'user stash ' ' for stashing / unstashing both persistent and transient messages .
*
2013-10-08 11:46:02 +02:00
* Processors can also store snapshots of internal state by calling [ [ saveSnapshot ] ] . During recovery , a saved
* snapshot is offered to the processor with a [ [ SnapshotOffer ] ] message , followed by replayed messages , if any ,
* that are younger than the snapshot . Default is to offer the latest saved snapshot .
*
2013-09-14 14:19:18 +02:00
* @see [ [ UntypedProcessor ] ]
2013-10-08 11:46:02 +02:00
* @see [ [ Recover ] ]
2013-10-27 08:01:14 +01:00
* @see [ [ PersistentBatch ] ]
2013-09-14 14:19:18 +02:00
*/
2014-05-21 01:35:21 +02:00
@deprecated ( "Processor will be removed. Instead extend `akka.persistence.PersistentActor` and use it's `persistAsync(command)(callback)` method to get equivalent semantics." , since = "2.3.4" )
2014-06-26 13:56:01 +02:00
trait Processor extends ProcessorImpl {
/* *
* Persistence id . Defaults to this persistent - actors 's path and can be overridden .
*/
override def persistenceId : String = processorId
}
/* * INTERNAL API */
@deprecated ( "Processor will be removed. Instead extend `akka.persistence.PersistentActor` and use it's `persistAsync(command)(callback)` method to get equivalent semantics." , since = "2.3.4" )
private [ akka ] trait ProcessorImpl extends Actor with Recovery {
// TODO: remove Processor in favor of PersistentActor #15230
2014-05-21 01:35:21 +02:00
2013-10-08 11:46:02 +02:00
import JournalProtocol._
2013-09-14 14:19:18 +02:00
/* *
2014-01-17 06:58:25 +01:00
* Processes the highest stored sequence number response from the journal and then switches
* to `processing` state .
2013-09-14 14:19:18 +02:00
*/
2014-01-17 06:58:25 +01:00
private val initializing = new State {
override def toString : String = "initializing"
def aroundReceive ( receive : Receive , message : Any ) = message match {
case ReadHighestSequenceNrSuccess ( highest ) ⇒
_currentState = processing
sequenceNr = highest
receiverStash . unstashAll ( )
2014-06-05 14:07:17 +02:00
onRecoveryCompleted ( receive )
2014-01-17 06:58:25 +01:00
case ReadHighestSequenceNrFailure ( cause ) ⇒
onRecoveryFailure ( receive , cause )
case other ⇒
receiverStash . stash ( )
2013-09-14 14:19:18 +02:00
}
}
/* *
* Journals and processes new messages , both persistent and transient .
*/
2014-01-17 06:58:25 +01:00
private val processing = new State {
override def toString : String = "processing"
2013-09-14 14:19:18 +02:00
2013-11-20 13:47:42 +01:00
private var batching = false
2014-01-17 06:58:25 +01:00
def aroundReceive ( receive : Receive , message : Any ) = message match {
2014-06-03 16:40:44 +02:00
case r : Recover ⇒ // ignore
case ReplayedMessage ( p ) ⇒ processPersistent ( receive , p ) // can occur after unstash from user stash
case WriteMessageSuccess ( p : PersistentRepr ) ⇒ processPersistent ( receive , p )
case WriteMessageSuccess ( r : Resequenceable ) ⇒ process ( receive , r )
2014-06-25 12:51:21 +02:00
case WriteMessageFailure ( p , cause ) ⇒
2014-06-05 14:07:17 +02:00
process ( receive , PersistenceFailure ( p . payload , p . sequenceNr , cause ) )
2014-01-17 06:58:25 +01:00
case LoopMessageSuccess ( m ) ⇒ process ( receive , m )
2014-05-21 01:35:21 +02:00
case WriteMessagesSuccessful | WriteMessagesFailed ( _ ) ⇒
2013-11-20 13:47:42 +01:00
if ( processorBatch . isEmpty ) batching = false else journalBatch ( )
case p : PersistentRepr ⇒
addToBatch ( p )
if ( ! batching || maxBatchSizeReached ) journalBatch ( )
case pb : PersistentBatch ⇒
// submit all batched messages before submitting this user batch (isolated)
if ( ! processorBatch . isEmpty ) journalBatch ( )
addToBatch ( pb )
journalBatch ( )
case m ⇒
// submit all batched messages before looping this message
if ( processorBatch . isEmpty ) batching = false else journalBatch ( )
2014-01-17 06:58:25 +01:00
journal forward LoopMessage ( m , self )
2013-11-20 13:47:42 +01:00
}
2014-06-03 16:40:44 +02:00
def addToBatch ( p : Resequenceable ) : Unit = p match {
case p : PersistentRepr ⇒
2014-06-23 14:33:35 +02:00
processorBatch = processorBatch : + p . update ( persistenceId = persistenceId , sequenceNr = nextSequenceNr ( ) , sender = sender ( ) )
2014-06-03 16:40:44 +02:00
case r ⇒
processorBatch = processorBatch : + r
}
2013-11-20 13:47:42 +01:00
def addToBatch ( pb : PersistentBatch ) : Unit =
2014-06-03 16:40:44 +02:00
pb . batch . foreach ( addToBatch )
2013-11-20 13:47:42 +01:00
def maxBatchSizeReached : Boolean =
2014-01-17 06:58:25 +01:00
processorBatch . length >= extension . settings . journal . maxMessageBatchSize
2013-11-20 13:47:42 +01:00
def journalBatch ( ) : Unit = {
2014-01-17 06:58:25 +01:00
journal ! WriteMessages ( processorBatch , self )
2013-11-20 13:47:42 +01:00
processorBatch = Vector . empty
batching = true
2013-09-14 14:19:18 +02:00
}
}
/* *
2014-01-17 06:58:25 +01:00
* INTERNAL API .
*
* Switches to `initializing` state and requests the highest stored sequence number from the journal .
2013-09-14 14:19:18 +02:00
*/
2014-01-17 06:58:25 +01:00
private [ persistence ] def onReplaySuccess ( receive : Receive , awaitReplay : Boolean ) : Unit = {
_currentState = initializing
2014-06-23 14:33:35 +02:00
journal ! ReadHighestSequenceNr ( lastSequenceNr , persistenceId , self )
2013-09-14 14:19:18 +02:00
}
/* *
2014-01-17 06:58:25 +01:00
* INTERNAL API .
2013-09-14 14:19:18 +02:00
*/
2014-01-17 06:58:25 +01:00
private [ persistence ] def onReplayFailure ( receive : Receive , awaitReplay : Boolean , cause : Throwable ) : Unit =
onRecoveryFailure ( receive , cause )
2013-09-14 14:19:18 +02:00
2014-01-17 06:58:25 +01:00
/* *
2014-06-05 14:07:17 +02:00
* Invokes this processor 's behavior with a `RecoveryFailure` message .
2014-01-17 06:58:25 +01:00
*/
2014-06-05 14:07:17 +02:00
private def onRecoveryFailure ( receive : Receive , cause : Throwable ) : Unit =
receive . applyOrElse ( RecoveryFailure ( cause ) , unhandled )
/* *
* Invokes this processor 's behavior with a `RecoveryFinished` message .
*/
private def onRecoveryCompleted ( receive : Receive ) : Unit =
receive . applyOrElse ( RecoveryCompleted , unhandled )
2014-06-25 12:51:21 +02:00
2014-06-23 14:33:35 +02:00
private val _persistenceId = extension . persistenceId ( self )
2013-09-14 14:19:18 +02:00
2014-06-03 16:40:44 +02:00
private var processorBatch = Vector . empty [ Resequenceable ]
2014-01-17 06:58:25 +01:00
private var sequenceNr : Long = 0L
2013-09-14 14:19:18 +02:00
/* *
* Processor id . Defaults to this processor 's path and can be overridden .
*/
2014-06-23 14:33:35 +02:00
@deprecated ( "Override `persistenceId: String` instead. Processor will be removed." , since = "2.3.4" )
override def processorId : String = _persistenceId // TODO: remove processorId
2013-09-14 14:19:18 +02:00
2014-06-23 14:33:35 +02:00
/* *
* Returns `persistenceId` .
*/
def snapshotterId : String = persistenceId
2013-09-14 14:19:18 +02:00
/* *
* Returns `true` if this processor is currently recovering .
*/
def recoveryRunning : Boolean =
2014-01-17 06:58:25 +01:00
_currentState != processing
2013-09-14 14:19:18 +02:00
/* *
* Returns `true` if this processor has successfully finished recovery .
*/
def recoveryFinished : Boolean =
2014-01-17 06:58:25 +01:00
_currentState == processing
2013-09-14 14:19:18 +02:00
/* *
2013-11-12 09:02:02 +01:00
* Marks a persistent message , identified by `sequenceNr` , as deleted . A message marked as deleted is
* not replayed during recovery . This method is usually called inside `preRestartProcessor` when a
* persistent message caused an exception . Processors that want to re - receive that persistent message
* during recovery should not call this method .
2013-11-07 10:45:02 +01:00
*
2013-11-12 09:02:02 +01:00
* @param sequenceNr sequence number of the persistent message to be deleted .
2013-11-07 10:45:02 +01:00
*/
2013-11-12 09:02:02 +01:00
def deleteMessage ( sequenceNr : Long ) : Unit = {
2014-06-23 14:33:35 +02:00
deleteMessage ( sequenceNr , permanent = false )
2013-11-07 10:45:02 +01:00
}
/* *
2013-11-12 09:02:02 +01:00
* Deletes a persistent message identified by `sequenceNr` . If `permanent` is set to `false` ,
* the persistent message is marked as deleted in the journal , otherwise it is permanently
2013-11-07 10:45:02 +01:00
* deleted from the journal . A deleted message is not replayed during recovery . This method
* is usually called inside `preRestartProcessor` when a persistent message caused an exception .
* Processors that want to re - receive that persistent message during recovery should not call
* this method .
*
* @param sequenceNr sequence number of the persistent message to be deleted .
2013-11-12 09:02:02 +01:00
* @param permanent if `false` , the message is marked as deleted , otherwise it is permanently deleted .
*/
def deleteMessage ( sequenceNr : Long , permanent : Boolean ) : Unit = {
2014-06-23 14:33:35 +02:00
journal ! DeleteMessages ( List ( PersistenceIdImpl ( persistenceId , sequenceNr ) ) , permanent )
2013-11-12 09:02:02 +01:00
}
/* *
2014-01-17 06:58:25 +01:00
* Permanently deletes all persistent messages with sequence numbers less than or equal `toSequenceNr` .
2013-11-12 09:02:02 +01:00
*
* @param toSequenceNr upper sequence number bound of persistent messages to be deleted .
*/
def deleteMessages ( toSequenceNr : Long ) : Unit = {
2014-06-23 14:33:35 +02:00
deleteMessages ( toSequenceNr , permanent = true )
2013-11-12 09:02:02 +01:00
}
/* *
* Deletes all persistent messages with sequence numbers less than or equal `toSequenceNr` . If `permanent`
* is set to `false` , the persistent messages are marked as deleted in the journal , otherwise
* they permanently deleted from the journal .
*
* @param toSequenceNr upper sequence number bound of persistent messages to be deleted .
* @param permanent if `false` , the message is marked as deleted , otherwise it is permanently deleted .
2013-11-07 10:45:02 +01:00
*/
2013-11-12 09:02:02 +01:00
def deleteMessages ( toSequenceNr : Long , permanent : Boolean ) : Unit = {
2014-06-23 14:33:35 +02:00
journal ! DeleteMessagesTo ( persistenceId , toSequenceNr , permanent )
2013-09-14 14:19:18 +02:00
}
/* *
2013-09-15 09:04:05 +02:00
* INTERNAL API .
2013-09-14 14:19:18 +02:00
*/
2014-06-03 15:10:56 +02:00
override protected [ akka ] def aroundPreStart ( ) : Unit = {
2013-09-15 09:04:05 +02:00
try preStart ( ) finally super . preStart ( )
2013-09-14 14:19:18 +02:00
}
/* *
2013-09-15 09:04:05 +02:00
* INTERNAL API .
2013-09-14 14:19:18 +02:00
*/
2014-06-03 15:10:56 +02:00
override protected [ akka ] def aroundPostStop ( ) : Unit = {
2013-09-15 09:04:05 +02:00
try unstashAll ( unstashFilterPredicate ) finally postStop ( )
2013-09-14 14:19:18 +02:00
}
/* *
2013-09-15 09:04:05 +02:00
* INTERNAL API .
2013-09-14 14:19:18 +02:00
*/
2014-06-03 15:10:56 +02:00
override protected [ akka ] def aroundPreRestart ( reason : Throwable , message : Option [ Any ] ) : Unit = {
2013-09-15 09:04:05 +02:00
try {
2014-01-17 06:58:25 +01:00
receiverStash . prepend ( processorBatch . map ( p ⇒ Envelope ( p , p . sender , context . system ) ) )
receiverStash . unstashAll ( )
2013-11-20 13:47:42 +01:00
unstashAll ( unstashFilterPredicate )
2013-09-15 09:04:05 +02:00
} finally {
message match {
2014-01-17 06:58:25 +01:00
case Some ( WriteMessageSuccess ( m ) ) ⇒ preRestartDefault ( reason , Some ( m ) )
case Some ( LoopMessageSuccess ( m ) ) ⇒ preRestartDefault ( reason , Some ( m ) )
case Some ( ReplayedMessage ( m ) ) ⇒ preRestartDefault ( reason , Some ( m ) )
case mo ⇒ preRestartDefault ( reason , None )
2013-09-15 09:04:05 +02:00
}
}
2013-09-14 14:19:18 +02:00
}
/* *
2013-09-15 09:04:05 +02:00
* User - overridable callback . Called when a processor is started . Default implementation sends
* a `Recover()` to `self` .
2013-09-14 14:19:18 +02:00
*/
2013-09-15 09:04:05 +02:00
@throws ( classOf [ Exception ] )
override def preStart ( ) : Unit = {
self ! Recover ( )
2013-09-14 14:19:18 +02:00
}
/* *
2013-09-15 09:04:05 +02:00
* User - overridable callback . Called before a processor is restarted . Default implementation sends
* a `Recover(lastSequenceNr)` message to `self` if `message` is defined , `Recover() otherwise` .
2013-09-14 14:19:18 +02:00
*/
2013-09-15 09:04:05 +02:00
override def preRestart ( reason : Throwable , message : Option [ Any ] ) : Unit = {
2013-09-14 14:19:18 +02:00
message match {
2013-09-26 09:14:43 +02:00
case Some ( _ ) ⇒ self ! Recover ( toSequenceNr = lastSequenceNr )
2013-09-15 09:04:05 +02:00
case None ⇒ self ! Recover ( )
2013-09-14 14:19:18 +02:00
}
}
/* *
2013-09-15 09:04:05 +02:00
* Calls [ [ preRestart ] ] and then `super.preRestart()` . If processor implementation classes want to
* opt out from stopping child actors , they should override this method and call [ [ preRestart ] ] only .
2013-09-14 14:19:18 +02:00
*/
2013-09-15 09:04:05 +02:00
def preRestartDefault ( reason : Throwable , message : Option [ Any ] ) : Unit = {
try preRestart ( reason , message ) finally super . preRestart ( reason , message )
}
2014-06-05 14:07:17 +02:00
override def unhandled ( message : Any ) : Unit = {
message match {
case RecoveryCompleted ⇒ // mute
case RecoveryFailure ( cause ) ⇒
2014-06-23 14:33:35 +02:00
val errorMsg = s" Processor killed after recovery failure (persisten id = [ ${ persistenceId } ]). " +
2014-06-05 14:07:17 +02:00
"To avoid killing processors on recovery failure, a processor must handle RecoveryFailure messages. " +
"RecoveryFailure was caused by: " + cause
throw new ActorKilledException ( errorMsg )
case PersistenceFailure ( payload , sequenceNumber , cause ) ⇒
val errorMsg = "Processor killed after persistence failure " +
2014-06-23 14:33:35 +02:00
s" (persistent id = [ ${ persistenceId } ], sequence nr = [ ${ sequenceNumber } ], payload class = [ ${ payload . getClass . getName } ]). " +
2014-06-05 14:07:17 +02:00
"To avoid killing processors on persistence failure, a processor must handle PersistenceFailure messages. " +
"PersistenceFailure was caused by: " + cause
throw new ActorKilledException ( errorMsg )
case m ⇒ super . unhandled ( m )
}
}
2013-09-15 09:04:05 +02:00
private def nextSequenceNr ( ) : Long = {
2014-01-17 06:58:25 +01:00
sequenceNr += 1L
sequenceNr
2013-09-14 14:19:18 +02:00
}
2013-10-15 09:01:07 +02:00
private val unstashFilterPredicate : Any ⇒ Boolean = {
2014-01-17 06:58:25 +01:00
case _ : WriteMessageSuccess ⇒ false
case _ : ReplayedMessage ⇒ false
case _ ⇒ true
2013-09-14 14:19:18 +02:00
}
2013-10-15 09:01:07 +02:00
}
2013-09-14 14:19:18 +02:00
2013-10-15 09:01:07 +02:00
/* *
2014-01-17 06:58:25 +01:00
* Sent to a [ [ Processor ] ] if a journal fails to write a [ [ Persistent ] ] message . If
2013-11-07 10:45:02 +01:00
* not handled , an `akka.actor.ActorKilledException` is thrown by that processor .
2013-10-15 09:01:07 +02:00
*
2013-11-07 10:45:02 +01:00
* @param payload payload of the persistent message .
* @param sequenceNr sequence number of the persistent message .
* @param cause failure cause .
2013-10-15 09:01:07 +02:00
*/
2014-01-17 06:58:25 +01:00
@SerialVersionUID ( 1L )
2014-06-03 16:40:44 +02:00
case class PersistenceFailure ( payload : Any , sequenceNr : Long , cause : Throwable )
2013-09-14 14:19:18 +02:00
2014-01-17 06:58:25 +01:00
/* *
* Sent to a [ [ Processor ] ] if a journal fails to replay messages or fetch that processor 's
2014-06-05 14:07:17 +02:00
* highest sequence number . If not handled , the prossor will be stopped .
2014-01-17 06:58:25 +01:00
*/
@SerialVersionUID ( 1L )
2014-06-03 16:40:44 +02:00
case class RecoveryFailure ( cause : Throwable )
2014-01-17 06:58:25 +01:00
2014-06-05 14:07:17 +02:00
abstract class RecoveryCompleted
2014-01-17 06:58:25 +01:00
/* *
2014-06-05 14:07:17 +02:00
* Sent to a [ [ Processor ] ] when the journal replay has been finished .
2014-01-17 06:58:25 +01:00
*/
@SerialVersionUID ( 1L )
2014-06-05 14:07:17 +02:00
case object RecoveryCompleted extends RecoveryCompleted {
/* *
* Java API : get the singleton instance
*/
def getInstance = this
}
2014-01-17 06:58:25 +01:00
2013-09-14 14:19:18 +02:00
/* *
2013-11-07 10:45:02 +01:00
* Java API : an actor that persists ( journals ) messages of type [ [ Persistent ] ] . Messages of other type s
* are not persisted .
2013-09-14 14:19:18 +02:00
*
* { { {
* import akka.persistence.Persistent ;
* import akka.persistence.Processor ;
*
* class MyProcessor extends UntypedProcessor {
* public void onReceive ( Object message ) throws Exception {
* if ( message instanceof Persistent ) {
* // message has been written to journal
* Persistent persistent = ( Persistent ) message ;
* Object payload = persistent . payload ( ) ;
* Long sequenceNr = persistent . sequenceNr ( ) ;
* // ...
* } else {
* // message has not been written to journal
* }
* }
* }
*
* // ...
*
* ActorRef processor = getContext ( ) . actorOf ( Props . create ( MyProcessor . class ) , "myProcessor" ) ;
*
* processor . tell ( Persistent . create ( "foo" ) , null ) ;
* processor . tell ( "bar" , null ) ;
* } } }
*
* During start and restart , persistent messages are replayed to a processor so that it can recover internal
* state from these messages . New messages sent to a processor during recovery do not interfere with replayed
* messages , hence applications don 't need to wait for a processor to complete its recovery .
*
2013-09-26 09:14:43 +02:00
* Automated recovery can be turned off or customized by overriding the [ [ preStart ] ] and [ [ preRestart ] ] life
* cycle hooks . If automated recovery is turned off , an application can explicitly recover a processor by
* sending it a [ [ Recover ] ] message .
2013-09-14 14:19:18 +02:00
*
* [ [ Persistent ] ] messages are assigned sequence numbers that are generated on a per - processor basis . A sequence
2013-09-26 09:14:43 +02:00
* starts at `1L` and doesn 't contain gaps unless a processor ( logically ) deletes a message .
2013-09-14 14:19:18 +02:00
*
* During recovery , a processor internally buffers new messages until recovery completes , so that new messages
* do not interfere with replayed messages . This internal buffer ( the ' 'processor stash ' ' ) is isolated from the
* ' 'user stash ' ' inherited by `akka.actor.Stash` . `Processor` implementation classes can therefore use the
* ' 'user stash ' ' for stashing / unstashing both persistent and transient messages .
*
2013-10-08 11:46:02 +02:00
* Processors can also store snapshots of internal state by calling [ [ saveSnapshot ] ] . During recovery , a saved
* snapshot is offered to the processor with a [ [ SnapshotOffer ] ] message , followed by replayed messages , if any ,
* that are younger than the snapshot . Default is to offer the latest saved snapshot .
*
2013-09-14 14:19:18 +02:00
* @see [ [ Processor ] ]
2013-10-08 11:46:02 +02:00
* @see [ [ Recover ] ]
2013-10-27 08:01:14 +01:00
* @see [ [ PersistentBatch ] ]
2013-09-14 14:19:18 +02:00
*/
2014-05-21 01:35:21 +02:00
@deprecated ( "UntypedProcessor will be removed. Instead extend `akka.persistence.UntypedPersistentActor` and use it's `persistAsync(command)(callback)` method to get equivalent semantics." , since = "2.3.4" )
2014-01-17 06:58:25 +01:00
abstract class UntypedProcessor extends UntypedActor with Processor
2014-02-15 23:44:00 -05:00
/* *
2014-03-20 12:05:32 +01:00
* Java API : compatible with lambda expressions
*
2014-02-15 23:44:00 -05:00
* An actor that persists ( journals ) messages of type [ [ Persistent ] ] . Messages of other type s
* are not persisted .
2014-03-20 12:05:32 +01:00
* < p />
* Example :
* < pre >
2014-02-15 23:44:00 -05:00
* class MyProcessor extends AbstractProcessor {
2014-03-20 12:05:32 +01:00
* public MyProcessor ( ) {
* receive ( ReceiveBuilder .
* match ( Persistent . class , p -> {
* Object payload = p . payload ( ) ;
* Long sequenceNr = p . sequenceNr ( ) ;
2014-02-15 23:44:00 -05:00
* // ...
2014-03-20 12:05:32 +01:00
* } ) . build ( )
* ) ;
* }
2014-02-15 23:44:00 -05:00
* }
*
* // ...
*
* ActorRef processor = context ( ) . actorOf ( Props . create ( MyProcessor . class ) , "myProcessor" ) ;
*
* processor . tell ( Persistent . create ( "foo" ) , null ) ;
* processor . tell ( "bar" , null ) ;
2014-03-20 12:05:32 +01:00
* </ pre >
2014-02-15 23:44:00 -05:00
*
* During start and restart , persistent messages are replayed to a processor so that it can recover internal
* state from these messages . New messages sent to a processor during recovery do not interfere with replayed
* messages , hence applications don 't need to wait for a processor to complete its recovery .
*
* Automated recovery can be turned off or customized by overriding the [ [ preStart ] ] and [ [ preRestart ] ] life
* cycle hooks . If automated recovery is turned off , an application can explicitly recover a processor by
* sending it a [ [ Recover ] ] message .
*
* [ [ Persistent ] ] messages are assigned sequence numbers that are generated on a per - processor basis . A sequence
* starts at `1L` and doesn 't contain gaps unless a processor ( logically ) deletes a message .
*
* During recovery , a processor internally buffers new messages until recovery completes , so that new messages
* do not interfere with replayed messages . This internal buffer ( the ' 'processor stash ' ' ) is isolated from the
* ' 'user stash ' ' inherited by `akka.actor.Stash` . `Processor` implementation classes can therefore use the
* ' 'user stash ' ' for stashing / unstashing both persistent and transient messages .
*
* Processors can also store snapshots of internal state by calling [ [ saveSnapshot ] ] . During recovery , a saved
* snapshot is offered to the processor with a [ [ SnapshotOffer ] ] message , followed by replayed messages , if any ,
* that are younger than the snapshot . Default is to offer the latest saved snapshot .
*
* @see [ [ Processor ] ]
* @see [ [ Recover ] ]
* @see [ [ PersistentBatch ] ]
*/
2014-05-21 01:35:21 +02:00
@deprecated ( "AbstractProcessor will be removed. Instead extend `akka.persistence.AbstractPersistentActor` and use it's `persistAsync(command)(callback)` method to get equivalent semantics." , since = "2.3.4" )
2014-03-04 16:09:52 +01:00
abstract class AbstractProcessor extends AbstractActor with Processor