Merge pull request #17843 from akka/wip-17799-deleteMessages-refactoring-bug-patriknw
+per #17799 Fix refactoring bug in deleteMessages
This commit is contained in:
commit
b0c2817a36
8 changed files with 61 additions and 8 deletions
|
|
@ -291,6 +291,9 @@ next message.
|
||||||
If there is a problem with recovering the state of the actor from the journal when the actor is
|
If there is a problem with recovering the state of the actor from the journal when the actor is
|
||||||
started, ``onReplayFailure`` is called (logging the error by default) and the actor will be stopped.
|
started, ``onReplayFailure`` is called (logging the error by default) and the actor will be stopped.
|
||||||
|
|
||||||
|
If the ``deleteMessages`` fails ``onDeleteMessagesFailure`` will be called (logging a warning by default)
|
||||||
|
and the actor continues with next message.
|
||||||
|
|
||||||
Atomic writes
|
Atomic writes
|
||||||
-------------
|
-------------
|
||||||
|
|
||||||
|
|
@ -323,6 +326,9 @@ Message deletion
|
||||||
To delete all messages (journaled by a single persistent actor) up to a specified sequence number,
|
To delete all messages (journaled by a single persistent actor) up to a specified sequence number,
|
||||||
persistent actors may call the ``deleteMessages`` method.
|
persistent actors may call the ``deleteMessages`` method.
|
||||||
|
|
||||||
|
If the delete fails ``onDeleteMessagesFailure`` will be called (logging a warning by default)
|
||||||
|
and the actor continues with next message.
|
||||||
|
|
||||||
.. _persistent-views-java-lambda:
|
.. _persistent-views-java-lambda:
|
||||||
|
|
||||||
Views
|
Views
|
||||||
|
|
|
||||||
|
|
@ -326,6 +326,12 @@ Message deletion
|
||||||
To delete all messages (journaled by a single persistent actor) up to a specified sequence number,
|
To delete all messages (journaled by a single persistent actor) up to a specified sequence number,
|
||||||
persistent actors may call the ``deleteMessages`` method.
|
persistent actors may call the ``deleteMessages`` method.
|
||||||
|
|
||||||
|
If the delete fails ``onDeleteMessagesFailure`` will be called (logging a warning by default)
|
||||||
|
and the actor continues with next message.
|
||||||
|
|
||||||
|
If the ``deleteMessages`` fails ``onDeleteMessagesFailure`` will be called (logging a warning by default)
|
||||||
|
and the actor continues with next message.
|
||||||
|
|
||||||
.. _persistent-views-java:
|
.. _persistent-views-java:
|
||||||
|
|
||||||
Persistent Views
|
Persistent Views
|
||||||
|
|
|
||||||
|
|
@ -280,6 +280,9 @@ next message.
|
||||||
If there is a problem with recovering the state of the actor from the journal when the actor is
|
If there is a problem with recovering the state of the actor from the journal when the actor is
|
||||||
started, ``onReplayFailure`` is called (logging the error by default) and the actor will be stopped.
|
started, ``onReplayFailure`` is called (logging the error by default) and the actor will be stopped.
|
||||||
|
|
||||||
|
If the ``deleteMessages`` fails ``onDeleteMessagesFailure`` will be called (logging a warning by default)
|
||||||
|
and the actor continues with next message.
|
||||||
|
|
||||||
Atomic writes
|
Atomic writes
|
||||||
-------------
|
-------------
|
||||||
|
|
||||||
|
|
@ -315,6 +318,9 @@ Message deletion
|
||||||
To delete all messages (journaled by a single persistent actor) up to a specified sequence number,
|
To delete all messages (journaled by a single persistent actor) up to a specified sequence number,
|
||||||
persistent actors may call the ``deleteMessages`` method.
|
persistent actors may call the ``deleteMessages`` method.
|
||||||
|
|
||||||
|
If the delete fails ``onDeleteMessagesFailure`` will be called (logging a warning by default)
|
||||||
|
and the actor continues with next message.
|
||||||
|
|
||||||
.. _persistent-views:
|
.. _persistent-views:
|
||||||
|
|
||||||
Persistent Views
|
Persistent Views
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
package akka.persistence.journal
|
package akka.persistence.journal
|
||||||
|
|
||||||
|
import scala.concurrent.duration._
|
||||||
import scala.collection.immutable.Seq
|
import scala.collection.immutable.Seq
|
||||||
|
|
||||||
import akka.actor._
|
import akka.actor._
|
||||||
|
|
@ -137,7 +138,8 @@ abstract class JournalSpec(config: Config) extends PluginSpec(config) {
|
||||||
receiverProbe.expectMsg(ReplayMessagesSuccess)
|
receiverProbe.expectMsg(ReplayMessagesSuccess)
|
||||||
}
|
}
|
||||||
"not replay permanently deleted messages (range deletion)" in {
|
"not replay permanently deleted messages (range deletion)" in {
|
||||||
val cmd = DeleteMessagesTo(pid, 3)
|
val receiverProbe2 = TestProbe()
|
||||||
|
val cmd = DeleteMessagesTo(pid, 3, receiverProbe2.ref)
|
||||||
val sub = TestProbe()
|
val sub = TestProbe()
|
||||||
|
|
||||||
subscribe[DeleteMessagesTo](sub.ref)
|
subscribe[DeleteMessagesTo](sub.ref)
|
||||||
|
|
@ -146,6 +148,8 @@ abstract class JournalSpec(config: Config) extends PluginSpec(config) {
|
||||||
|
|
||||||
journal ! ReplayMessages(1, Long.MaxValue, Long.MaxValue, pid, receiverProbe.ref)
|
journal ! ReplayMessages(1, Long.MaxValue, Long.MaxValue, pid, receiverProbe.ref)
|
||||||
List(4, 5) foreach { i ⇒ receiverProbe.expectMsg(replayedMessage(i)) }
|
List(4, 5) foreach { i ⇒ receiverProbe.expectMsg(replayedMessage(i)) }
|
||||||
|
|
||||||
|
receiverProbe2.expectNoMsg(200.millis)
|
||||||
}
|
}
|
||||||
|
|
||||||
"return a highest stored sequence number > 0 if the persistent actor has already written messages and the message log is non-empty" in {
|
"return a highest stored sequence number > 0 if the persistent actor has already written messages and the message log is non-empty" in {
|
||||||
|
|
|
||||||
|
|
@ -147,6 +147,18 @@ private[persistence] trait Eventsourced extends Snapshotter with Stash with Stas
|
||||||
event.getClass.getName, seqNr, persistenceId, cause.getMessage)
|
event.getClass.getName, seqNr, persistenceId, cause.getMessage)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Called when ``deleteMessages`` failed. By default this method logs the problem
|
||||||
|
* as a warning, and the actor continues.
|
||||||
|
*
|
||||||
|
* @param cause failure cause
|
||||||
|
* @param toSequenceNr the sequence number parameter of the ``deleteMessages`` call
|
||||||
|
*/
|
||||||
|
protected def onDeleteMessagesFailure(cause: Throwable, toSequenceNr: Long): Unit = {
|
||||||
|
log.warning("Failed to deleteMessages toSequenceNr [{}] for persistenceId [{}] due to [{}].",
|
||||||
|
toSequenceNr, persistenceId, cause.getMessage)
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* User-overridable callback. Called when a persistent actor is started or restarted.
|
* User-overridable callback. Called when a persistent actor is started or restarted.
|
||||||
* Default implementation sends a `Recover()` to `self`. Note that if you override
|
* Default implementation sends a `Recover()` to `self`. Note that if you override
|
||||||
|
|
@ -378,10 +390,12 @@ private[persistence] trait Eventsourced extends Snapshotter with Stash with Stas
|
||||||
/**
|
/**
|
||||||
* Permanently deletes all persistent messages with sequence numbers less than or equal `toSequenceNr`.
|
* Permanently deletes all persistent messages with sequence numbers less than or equal `toSequenceNr`.
|
||||||
*
|
*
|
||||||
|
* If the delete fails [[#onDeleteMessagesFailure]] will be called.
|
||||||
|
*
|
||||||
* @param toSequenceNr upper sequence number bound of persistent messages to be deleted.
|
* @param toSequenceNr upper sequence number bound of persistent messages to be deleted.
|
||||||
*/
|
*/
|
||||||
def deleteMessages(toSequenceNr: Long): Unit =
|
def deleteMessages(toSequenceNr: Long): Unit =
|
||||||
deleteMessages(toSequenceNr)
|
journal ! DeleteMessagesTo(persistenceId, toSequenceNr, self)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns `true` if this persistent actor is currently recovering.
|
* Returns `true` if this persistent actor is currently recovering.
|
||||||
|
|
@ -566,6 +580,10 @@ private[persistence] trait Eventsourced extends Snapshotter with Stash with Stas
|
||||||
|
|
||||||
case WriteMessagesFailed(_) ⇒
|
case WriteMessagesFailed(_) ⇒
|
||||||
() // it will be stopped by the first WriteMessageFailure message
|
() // it will be stopped by the first WriteMessageFailure message
|
||||||
|
|
||||||
|
case DeleteMessagesFailure(e, toSequenceNr) ⇒
|
||||||
|
onDeleteMessagesFailure(e, toSequenceNr)
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
def onWriteMessageComplete(err: Boolean): Unit =
|
def onWriteMessageComplete(err: Boolean): Unit =
|
||||||
|
|
|
||||||
|
|
@ -25,14 +25,14 @@ private[persistence] object JournalProtocol {
|
||||||
/**
|
/**
|
||||||
* Reply message to a failed [[DeleteMessagesTo]] request.
|
* Reply message to a failed [[DeleteMessagesTo]] request.
|
||||||
*/
|
*/
|
||||||
final case class DeleteMessagesFailure(cause: Throwable)
|
final case class DeleteMessagesFailure(cause: Throwable, toSequenceNr: Long)
|
||||||
extends Response
|
extends Response
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Request to delete all persistent messages with sequence numbers up to `toSequenceNr`
|
* Request to delete all persistent messages with sequence numbers up to `toSequenceNr`
|
||||||
* (inclusive).
|
* (inclusive).
|
||||||
*/
|
*/
|
||||||
final case class DeleteMessagesTo(persistenceId: String, toSequenceNr: Long)
|
final case class DeleteMessagesTo(persistenceId: String, toSequenceNr: Long, persistentActor: ActorRef)
|
||||||
extends Request
|
extends Request
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
||||||
|
|
@ -117,10 +117,10 @@ trait AsyncWriteJournal extends Actor with WriteJournalBase with AsyncRecovery {
|
||||||
case e ⇒ ReadHighestSequenceNrFailure(e)
|
case e ⇒ ReadHighestSequenceNrFailure(e)
|
||||||
} pipeTo persistentActor
|
} pipeTo persistentActor
|
||||||
|
|
||||||
case d @ DeleteMessagesTo(persistenceId, toSequenceNr) ⇒
|
case d @ DeleteMessagesTo(persistenceId, toSequenceNr, persistentActor) ⇒
|
||||||
asyncDeleteMessagesTo(persistenceId, toSequenceNr) onComplete {
|
asyncDeleteMessagesTo(persistenceId, toSequenceNr) onComplete {
|
||||||
case Success(_) ⇒ if (publish) context.system.eventStream.publish(d)
|
case Success(_) ⇒ if (publish) context.system.eventStream.publish(d)
|
||||||
case Failure(e) ⇒
|
case Failure(e) ⇒ persistentActor ! DeleteMessagesFailure(e, toSequenceNr)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -20,6 +20,7 @@ object PersistentActorSpec {
|
||||||
final case class Cmd(data: Any)
|
final case class Cmd(data: Any)
|
||||||
final case class Evt(data: Any)
|
final case class Evt(data: Any)
|
||||||
final case class LatchCmd(latch: TestLatch, data: Any) extends NoSerializationVerificationNeeded
|
final case class LatchCmd(latch: TestLatch, data: Any) extends NoSerializationVerificationNeeded
|
||||||
|
final case class Delete(toSequenceNr: Long)
|
||||||
|
|
||||||
abstract class ExamplePersistentActor(name: String) extends NamedPersistentActor(name) with PersistentActor {
|
abstract class ExamplePersistentActor(name: String) extends NamedPersistentActor(name) with PersistentActor {
|
||||||
var events: List[Any] = Nil
|
var events: List[Any] = Nil
|
||||||
|
|
@ -29,8 +30,9 @@ object PersistentActorSpec {
|
||||||
}
|
}
|
||||||
|
|
||||||
val commonBehavior: Receive = {
|
val commonBehavior: Receive = {
|
||||||
case "boom" ⇒ throw new TestException("boom")
|
case "boom" ⇒ throw new TestException("boom")
|
||||||
case GetState ⇒ sender() ! events.reverse
|
case Delete(toSequenceNr) ⇒ deleteMessages(toSequenceNr)
|
||||||
|
case GetState ⇒ sender() ! events.reverse
|
||||||
}
|
}
|
||||||
|
|
||||||
def receiveRecover = updateState
|
def receiveRecover = updateState
|
||||||
|
|
@ -892,6 +894,17 @@ abstract class PersistentActorSpec(config: Config) extends PersistenceSpec(confi
|
||||||
expectNoMsg(100.millis)
|
expectNoMsg(100.millis)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
"be able to delete events" in {
|
||||||
|
val persistentActor = namedPersistentActor[Behavior1PersistentActor]
|
||||||
|
persistentActor ! Cmd("b")
|
||||||
|
persistentActor ! GetState
|
||||||
|
expectMsg(List("a-1", "a-2", "b-1", "b-2"))
|
||||||
|
persistentActor ! Delete(2L) // delete "a-1" and "a-2"
|
||||||
|
persistentActor ! "boom" // restart, recover
|
||||||
|
persistentActor ! GetState
|
||||||
|
expectMsg(List("b-1", "b-2"))
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue