2013-10-08 11:46:02 +02:00
|
|
|
/**
|
2013-11-07 10:45:02 +01:00
|
|
|
* Copyright (C) 2009-2013 Typesafe Inc. <http://www.typesafe.com>
|
2013-10-08 11:46:02 +02:00
|
|
|
* Copyright (C) 2012-2013 Eligotech BV.
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
package akka.persistence.journal
|
|
|
|
|
|
2013-10-27 08:01:14 +01:00
|
|
|
import scala.collection.immutable
|
2013-10-08 11:46:02 +02:00
|
|
|
import scala.concurrent.Future
|
|
|
|
|
import scala.util._
|
|
|
|
|
|
|
|
|
|
import akka.actor._
|
|
|
|
|
import akka.pattern.{ pipe, PromiseActorRef }
|
|
|
|
|
import akka.persistence._
|
|
|
|
|
import akka.persistence.JournalProtocol._
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Abstract journal, optimized for asynchronous, non-blocking writes.
|
|
|
|
|
*/
|
|
|
|
|
trait AsyncWriteJournal extends Actor with AsyncReplay {
|
|
|
|
|
import AsyncWriteJournal._
|
|
|
|
|
import context.dispatcher
|
|
|
|
|
|
2013-11-12 09:02:02 +01:00
|
|
|
private val extension = Persistence(context.system)
|
|
|
|
|
|
2013-10-08 11:46:02 +02:00
|
|
|
private val resequencer = context.actorOf(Props[Resequencer])
|
|
|
|
|
private var resequencerCounter = 1L
|
|
|
|
|
|
2013-11-25 12:02:29 +01:00
|
|
|
def receive = {
|
2013-11-20 13:47:42 +01:00
|
|
|
case WriteBatch(persistentBatch, processor) ⇒
|
2013-10-08 11:46:02 +02:00
|
|
|
val cctr = resequencerCounter
|
2013-11-07 10:45:02 +01:00
|
|
|
def resequence(f: PersistentRepr ⇒ Any) = persistentBatch.zipWithIndex.foreach {
|
2013-11-20 13:47:42 +01:00
|
|
|
case (p, i) ⇒ resequencer ! Desequenced(f(p), cctr + i + 1, processor, p.sender)
|
2013-10-27 08:01:14 +01:00
|
|
|
}
|
2013-11-20 13:47:42 +01:00
|
|
|
writeAsync(persistentBatch.map(_.prepareWrite())) onComplete {
|
|
|
|
|
case Success(_) ⇒
|
|
|
|
|
resequencer ! Desequenced(WriteBatchSuccess, cctr, processor, self)
|
|
|
|
|
resequence(WriteSuccess(_))
|
|
|
|
|
case Failure(e) ⇒
|
|
|
|
|
resequencer ! Desequenced(WriteBatchFailure(e), cctr, processor, self)
|
|
|
|
|
resequence(WriteFailure(_, e))
|
2013-10-27 08:01:14 +01:00
|
|
|
}
|
2013-11-20 13:47:42 +01:00
|
|
|
resequencerCounter += persistentBatch.length + 1
|
|
|
|
|
case Replay(fromSequenceNr, toSequenceNr, processorId, processor) ⇒
|
2013-10-08 11:46:02 +02:00
|
|
|
// Send replayed messages and replay result to processor directly. No need
|
|
|
|
|
// to resequence replayed messages relative to written and looped messages.
|
|
|
|
|
replayAsync(processorId, fromSequenceNr, toSequenceNr) { p ⇒
|
2013-10-09 13:11:53 +02:00
|
|
|
if (!p.deleted) processor.tell(Replayed(p), p.sender)
|
2013-10-08 11:46:02 +02:00
|
|
|
} map {
|
|
|
|
|
maxSnr ⇒ ReplaySuccess(maxSnr)
|
|
|
|
|
} recover {
|
|
|
|
|
case e ⇒ ReplayFailure(e)
|
|
|
|
|
} pipeTo (processor)
|
2013-11-20 13:47:42 +01:00
|
|
|
case c @ Confirm(processorId, sequenceNr, channelId) ⇒
|
2013-10-08 11:46:02 +02:00
|
|
|
confirmAsync(processorId, sequenceNr, channelId) onComplete {
|
2013-11-12 09:02:02 +01:00
|
|
|
case Success(_) ⇒ if (extension.publishPluginCommands) context.system.eventStream.publish(c)
|
2013-10-08 11:46:02 +02:00
|
|
|
case Failure(e) ⇒ // TODO: publish failure to event stream
|
2013-11-20 13:47:42 +01:00
|
|
|
context.system.eventStream.publish(c)
|
2013-10-08 11:46:02 +02:00
|
|
|
}
|
2013-11-20 13:47:42 +01:00
|
|
|
case d @ Delete(processorId, fromSequenceNr, toSequenceNr, permanent) ⇒
|
2013-11-12 09:02:02 +01:00
|
|
|
deleteAsync(processorId, fromSequenceNr, toSequenceNr, permanent) onComplete {
|
|
|
|
|
case Success(_) ⇒ if (extension.publishPluginCommands) context.system.eventStream.publish(d)
|
2013-10-08 11:46:02 +02:00
|
|
|
case Failure(e) ⇒ // TODO: publish failure to event stream
|
|
|
|
|
}
|
2013-11-20 13:47:42 +01:00
|
|
|
case Loop(message, processor) ⇒
|
2013-10-08 11:46:02 +02:00
|
|
|
resequencer ! Desequenced(LoopSuccess(message), resequencerCounter, processor, sender)
|
|
|
|
|
resequencerCounter += 1
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//#journal-plugin-api
|
2013-10-27 08:01:14 +01:00
|
|
|
/**
|
2013-11-07 10:45:02 +01:00
|
|
|
* Plugin API: asynchronously writes a batch of persistent messages to the journal.
|
|
|
|
|
* The batch write must be atomic i.e. either all persistent messages in the batch
|
|
|
|
|
* are written or none.
|
2013-10-27 08:01:14 +01:00
|
|
|
*/
|
2013-11-20 13:47:42 +01:00
|
|
|
def writeAsync(persistentBatch: immutable.Seq[PersistentRepr]): Future[Unit]
|
2013-10-27 08:01:14 +01:00
|
|
|
|
2013-10-08 11:46:02 +02:00
|
|
|
/**
|
2013-11-12 09:02:02 +01:00
|
|
|
* Plugin API: asynchronously deletes all persistent messages within the range from
|
|
|
|
|
* `fromSequenceNr` to `toSequenceNr` (both inclusive). If `permanent` is set to
|
|
|
|
|
* `false`, the persistent messages are marked as deleted, otherwise they are
|
|
|
|
|
* permanently deleted.
|
|
|
|
|
*
|
|
|
|
|
* @see [[AsyncReplay]]
|
2013-10-08 11:46:02 +02:00
|
|
|
*/
|
2013-11-12 09:02:02 +01:00
|
|
|
def deleteAsync(processorId: String, fromSequenceNr: Long, toSequenceNr: Long, permanent: Boolean): Future[Unit]
|
2013-10-08 11:46:02 +02:00
|
|
|
|
|
|
|
|
/**
|
2013-11-07 10:45:02 +01:00
|
|
|
* Plugin API: asynchronously writes a delivery confirmation to the journal.
|
2013-10-08 11:46:02 +02:00
|
|
|
*/
|
|
|
|
|
def confirmAsync(processorId: String, sequenceNr: Long, channelId: String): Future[Unit]
|
|
|
|
|
//#journal-plugin-api
|
|
|
|
|
}
|
|
|
|
|
|
2013-11-25 12:02:29 +01:00
|
|
|
/**
|
|
|
|
|
* INTERNAL API.
|
|
|
|
|
*/
|
2013-10-08 11:46:02 +02:00
|
|
|
private[persistence] object AsyncWriteJournal {
|
|
|
|
|
case class Desequenced(msg: Any, snr: Long, target: ActorRef, sender: ActorRef)
|
|
|
|
|
|
|
|
|
|
class Resequencer extends Actor {
|
|
|
|
|
import scala.collection.mutable.Map
|
|
|
|
|
|
|
|
|
|
private val delayed = Map.empty[Long, Desequenced]
|
|
|
|
|
private var delivered = 0L
|
|
|
|
|
|
|
|
|
|
def receive = {
|
|
|
|
|
case d: Desequenced ⇒ resequence(d)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@scala.annotation.tailrec
|
|
|
|
|
private def resequence(d: Desequenced) {
|
|
|
|
|
if (d.snr == delivered + 1) {
|
|
|
|
|
delivered = d.snr
|
|
|
|
|
d.target tell (d.msg, d.sender)
|
|
|
|
|
} else {
|
|
|
|
|
delayed += (d.snr -> d)
|
|
|
|
|
}
|
|
|
|
|
val ro = delayed.remove(delivered + 1)
|
|
|
|
|
if (ro.isDefined) resequence(ro.get)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|