pekko/akka-persistence/src/test/scala/akka/persistence/PerformanceSpec.scala

259 lines
8.7 KiB
Scala
Raw Normal View History

/**
* Copyright (C) 2009-2014 Typesafe Inc. <http://www.typesafe.com>
*/
package akka.persistence
import scala.concurrent.duration._
import scala.language.postfixOps
import com.typesafe.config.ConfigFactory
import akka.actor._
import akka.testkit._
object PerformanceSpec {
// multiply cycles with 200 for more
// accurate throughput measurements
val config =
"""
akka.persistence.performance.cycles.warmup = 300
akka.persistence.performance.cycles.load = 1000
"""
case object StartMeasure
case object StopMeasure
2014-03-07 13:20:01 +01:00
final case class FailAt(sequenceNr: Long)
trait Measure extends { this: Actor
val NanoToSecond = 1000.0 * 1000 * 1000
var startTime: Long = 0L
var stopTime: Long = 0L
!per #15230 rename processorId => persistentId * This is NOT binary compatible, we're in an *experimental* module. * disabled binary compat checks for package akka.persistence * Source compatibility is retained, but users should migrate do the new method name ASAP. * Plugin APIs were migrated in a way that allows the old plugins to compile agains 2.3.4 without having to change anything. Hopefuly this will help authors migrate to 2.3.4 sooner. This is only source level compatible, not binary compatible. * added deprecation warnings on all processorId methods and provided bridges where possible * for users, the migration should be painless, they can still override the old method, and it'll work. But we encourage them to move to persistenceId; All delegation code will have to be removed afterwards ofc. Conflicts: akka-persistence/src/main/scala/akka/persistence/Channel.scala akka-persistence/src/main/scala/akka/persistence/JournalProtocol.scala akka-persistence/src/main/scala/akka/persistence/Persistent.scala akka-persistence/src/main/scala/akka/persistence/PersistentChannel.scala akka-persistence/src/main/scala/akka/persistence/Processor.scala akka-persistence/src/main/scala/akka/persistence/Snapshot.scala akka-persistence/src/main/scala/akka/persistence/journal/AsyncWriteProxy.scala akka-persistence/src/main/scala/akka/persistence/journal/inmem/InmemJournal.scala akka-persistence/src/main/scala/akka/persistence/journal/leveldb/LeveldbKey.scala akka-persistence/src/main/scala/akka/persistence/snapshot/SnapshotStore.scala akka-persistence/src/test/scala/akka/persistence/serialization/SerializerSpec.scala project/AkkaBuild.scala
2014-06-23 14:33:35 +02:00
var startSequenceNr = 0L
var stopSequenceNr = 0L
def startMeasure(): Unit = {
startSequenceNr = lastSequenceNr
startTime = System.nanoTime
}
def stopMeasure(): Unit = {
stopSequenceNr = lastSequenceNr
stopTime = System.nanoTime
sender() ! (NanoToSecond * (stopSequenceNr - startSequenceNr) / (stopTime - startTime))
}
def lastSequenceNr: Long
}
class PerformanceTestDestination extends Actor with Measure {
var lastSequenceNr = 0L
val confirm: PartialFunction[Any, Any] = {
case cp @ ConfirmablePersistent(payload, sequenceNr, _)
lastSequenceNr = sequenceNr
cp.confirm()
payload
}
def receive = confirm andThen {
case StartMeasure startMeasure()
case StopMeasure stopMeasure()
case m if (lastSequenceNr % 1000 == 0) print(".")
}
}
abstract class PerformanceTestProcessor(name: String) extends NamedProcessor(name) with Measure {
var failAt: Long = -1
val controlBehavior: Receive = {
case StartMeasure startMeasure()
case StopMeasure stopMeasure()
case FailAt(sequenceNr) failAt = sequenceNr
}
override def postRestart(reason: Throwable) {
super.postRestart(reason)
receive(StartMeasure)
}
}
class CommandsourcedTestProcessor(name: String) extends PerformanceTestProcessor(name) {
def receive = controlBehavior orElse {
case p: Persistent
if (lastSequenceNr % 1000 == 0) if (recoveryRunning) print("r") else print(".")
if (lastSequenceNr == failAt) throw new TestException("boom")
}
}
class CommandsourcedTestPersistentActor(name: String) extends PerformanceTestProcessor(name) with PersistentActor {
override val controlBehavior: Receive = {
case StartMeasure startMeasure()
case StopMeasure defer(StopMeasure)(_ stopMeasure())
case FailAt(sequenceNr) failAt = sequenceNr
}
val receiveRecover: Receive = {
case _ if (lastSequenceNr % 1000 == 0) print("r")
}
val receiveCommand: Receive = controlBehavior orElse {
case cmd persistAsync(cmd) { _
if (lastSequenceNr % 1000 == 0) print(".")
if (lastSequenceNr == failAt) throw new TestException("boom")
}
}
}
!per persistAsync Breaks binary compatibility because adding new methods to Eventsourced trait. Since akka-persistence is experimental this is ok, yet source-level compatibility has been perserved thankfuly :-) Deprecates: * Rename of EventsourcedProcessor -> PersistentActor * Processor -> suggest using PersistentActor * Migration guide for akka-persistence is separate, as wel'll deprecate in minor versions (its experimental) * Persistent as well as ConfirmablePersistent - since Processor, their main user will be removed soon. Other changes: * persistAsync works as expected when mixed with persist * A counter must be kept for pending stashing invocations * Uses only 1 shared list buffer for persit / persistAsync * Includes small benchmark * Docs also include info about not using Persistent() wrapper * uses java LinkedList, for best performance of append / head on persistInvocations; the get(0) is safe, because these msgs only come in response to persistInvocations * Renamed internal *MessagesSuccess/Failure messages because we kept small mistakes seeing the class "with s" and "without s" as the same * Updated everything that refered to EventsourcedProcessor to PersistentActor, including samples Refs #15227 Conflicts: akka-docs/rst/project/migration-guides.rst akka-persistence/src/main/scala/akka/persistence/JournalProtocol.scala akka-persistence/src/main/scala/akka/persistence/Persistent.scala akka-persistence/src/test/scala/akka/persistence/PersistentActorSpec.scala project/AkkaBuild.scala
2014-05-21 01:35:21 +02:00
class EventsourcedTestProcessor(name: String) extends PerformanceTestProcessor(name) with PersistentActor {
val receiveRecover: Receive = {
case _ if (lastSequenceNr % 1000 == 0) print("r")
}
val receiveCommand: Receive = controlBehavior orElse {
case cmd persist(cmd) { _
if (lastSequenceNr % 1000 == 0) print(".")
if (lastSequenceNr == failAt) throw new TestException("boom")
}
}
}
!per persistAsync Breaks binary compatibility because adding new methods to Eventsourced trait. Since akka-persistence is experimental this is ok, yet source-level compatibility has been perserved thankfuly :-) Deprecates: * Rename of EventsourcedProcessor -> PersistentActor * Processor -> suggest using PersistentActor * Migration guide for akka-persistence is separate, as wel'll deprecate in minor versions (its experimental) * Persistent as well as ConfirmablePersistent - since Processor, their main user will be removed soon. Other changes: * persistAsync works as expected when mixed with persist * A counter must be kept for pending stashing invocations * Uses only 1 shared list buffer for persit / persistAsync * Includes small benchmark * Docs also include info about not using Persistent() wrapper * uses java LinkedList, for best performance of append / head on persistInvocations; the get(0) is safe, because these msgs only come in response to persistInvocations * Renamed internal *MessagesSuccess/Failure messages because we kept small mistakes seeing the class "with s" and "without s" as the same * Updated everything that refered to EventsourcedProcessor to PersistentActor, including samples Refs #15227 Conflicts: akka-docs/rst/project/migration-guides.rst akka-persistence/src/main/scala/akka/persistence/JournalProtocol.scala akka-persistence/src/main/scala/akka/persistence/Persistent.scala akka-persistence/src/test/scala/akka/persistence/PersistentActorSpec.scala project/AkkaBuild.scala
2014-05-21 01:35:21 +02:00
class StashingEventsourcedTestProcessor(name: String) extends PerformanceTestProcessor(name) with PersistentActor {
val receiveRecover: Receive = {
case _ if (lastSequenceNr % 1000 == 0) print("r")
}
val printProgress: PartialFunction[Any, Any] = {
case m if (lastSequenceNr % 1000 == 0) print("."); m
}
val receiveCommand: Receive = printProgress andThen (controlBehavior orElse {
case "a" persist("a")(_ context.become(processC))
case "b" persist("b")(_ ())
})
val processC: Receive = printProgress andThen {
case "c"
persist("c")(_ context.unbecome())
unstashAll()
case other stash()
}
}
}
class PerformanceSpec extends AkkaSpec(PersistenceSpec.config("leveldb", "PerformanceSpec", serialization = "off").withFallback(ConfigFactory.parseString(PerformanceSpec.config))) with PersistenceSpec with ImplicitSender {
import PerformanceSpec._
val warmupCycles = system.settings.config.getInt("akka.persistence.performance.cycles.warmup")
val loadCycles = system.settings.config.getInt("akka.persistence.performance.cycles.load")
def stressCommandsourcedProcessor(failAt: Option[Long]): Unit = {
val processor = namedProcessor[CommandsourcedTestProcessor]
failAt foreach { processor ! FailAt(_) }
1 to warmupCycles foreach { i processor ! Persistent(s"msg${i}") }
processor ! StartMeasure
1 to loadCycles foreach { i processor ! Persistent(s"msg${i}") }
processor ! StopMeasure
expectMsgPF(100 seconds) {
case throughput: Double println(f"\nthroughput = $throughput%.2f persistent processor commands per second")
}
}
def stressCommandsourcedPersistentActor(failAt: Option[Long]): Unit = {
val processor = namedProcessor[CommandsourcedTestPersistentActor]
failAt foreach { processor ! FailAt(_) }
1 to warmupCycles foreach { i processor ! s"msg${i}" }
processor ! StartMeasure
1 to loadCycles foreach { i processor ! s"msg${i}" }
processor ! StopMeasure
expectMsgPF(100 seconds) {
case throughput: Double println(f"\nthroughput = $throughput%.2f persistent actor commands per second")
}
}
!per persistAsync Breaks binary compatibility because adding new methods to Eventsourced trait. Since akka-persistence is experimental this is ok, yet source-level compatibility has been perserved thankfuly :-) Deprecates: * Rename of EventsourcedProcessor -> PersistentActor * Processor -> suggest using PersistentActor * Migration guide for akka-persistence is separate, as wel'll deprecate in minor versions (its experimental) * Persistent as well as ConfirmablePersistent - since Processor, their main user will be removed soon. Other changes: * persistAsync works as expected when mixed with persist * A counter must be kept for pending stashing invocations * Uses only 1 shared list buffer for persit / persistAsync * Includes small benchmark * Docs also include info about not using Persistent() wrapper * uses java LinkedList, for best performance of append / head on persistInvocations; the get(0) is safe, because these msgs only come in response to persistInvocations * Renamed internal *MessagesSuccess/Failure messages because we kept small mistakes seeing the class "with s" and "without s" as the same * Updated everything that refered to EventsourcedProcessor to PersistentActor, including samples Refs #15227 Conflicts: akka-docs/rst/project/migration-guides.rst akka-persistence/src/main/scala/akka/persistence/JournalProtocol.scala akka-persistence/src/main/scala/akka/persistence/Persistent.scala akka-persistence/src/test/scala/akka/persistence/PersistentActorSpec.scala project/AkkaBuild.scala
2014-05-21 01:35:21 +02:00
def stressPersistentActor(failAt: Option[Long]): Unit = {
val processor = namedProcessor[EventsourcedTestProcessor]
failAt foreach { processor ! FailAt(_) }
1 to warmupCycles foreach { i processor ! s"msg${i}" }
processor ! StartMeasure
1 to loadCycles foreach { i processor ! s"msg${i}" }
processor ! StopMeasure
expectMsgPF(100 seconds) {
case throughput: Double println(f"\nthroughput = $throughput%.2f persistent events per second")
}
}
!per persistAsync Breaks binary compatibility because adding new methods to Eventsourced trait. Since akka-persistence is experimental this is ok, yet source-level compatibility has been perserved thankfuly :-) Deprecates: * Rename of EventsourcedProcessor -> PersistentActor * Processor -> suggest using PersistentActor * Migration guide for akka-persistence is separate, as wel'll deprecate in minor versions (its experimental) * Persistent as well as ConfirmablePersistent - since Processor, their main user will be removed soon. Other changes: * persistAsync works as expected when mixed with persist * A counter must be kept for pending stashing invocations * Uses only 1 shared list buffer for persit / persistAsync * Includes small benchmark * Docs also include info about not using Persistent() wrapper * uses java LinkedList, for best performance of append / head on persistInvocations; the get(0) is safe, because these msgs only come in response to persistInvocations * Renamed internal *MessagesSuccess/Failure messages because we kept small mistakes seeing the class "with s" and "without s" as the same * Updated everything that refered to EventsourcedProcessor to PersistentActor, including samples Refs #15227 Conflicts: akka-docs/rst/project/migration-guides.rst akka-persistence/src/main/scala/akka/persistence/JournalProtocol.scala akka-persistence/src/main/scala/akka/persistence/Persistent.scala akka-persistence/src/test/scala/akka/persistence/PersistentActorSpec.scala project/AkkaBuild.scala
2014-05-21 01:35:21 +02:00
def stressStashingPersistentActor(): Unit = {
val processor = namedProcessor[StashingEventsourcedTestProcessor]
1 to warmupCycles foreach { i processor ! "b" }
processor ! StartMeasure
val cmds = 1 to (loadCycles / 3) flatMap (_ List("a", "b", "c"))
processor ! StartMeasure
cmds foreach (processor ! _)
processor ! StopMeasure
expectMsgPF(100 seconds) {
case throughput: Double println(f"\nthroughput = $throughput%.2f persistent events per second")
}
}
def stressPersistentChannel(): Unit = {
val channel = system.actorOf(PersistentChannel.props())
val destination = system.actorOf(Props[PerformanceTestDestination])
!per #15230 rename processorId => persistentId * This is NOT binary compatible, we're in an *experimental* module. * disabled binary compat checks for package akka.persistence * Source compatibility is retained, but users should migrate do the new method name ASAP. * Plugin APIs were migrated in a way that allows the old plugins to compile agains 2.3.4 without having to change anything. Hopefuly this will help authors migrate to 2.3.4 sooner. This is only source level compatible, not binary compatible. * added deprecation warnings on all processorId methods and provided bridges where possible * for users, the migration should be painless, they can still override the old method, and it'll work. But we encourage them to move to persistenceId; All delegation code will have to be removed afterwards ofc. Conflicts: akka-persistence/src/main/scala/akka/persistence/Channel.scala akka-persistence/src/main/scala/akka/persistence/JournalProtocol.scala akka-persistence/src/main/scala/akka/persistence/Persistent.scala akka-persistence/src/main/scala/akka/persistence/PersistentChannel.scala akka-persistence/src/main/scala/akka/persistence/Processor.scala akka-persistence/src/main/scala/akka/persistence/Snapshot.scala akka-persistence/src/main/scala/akka/persistence/journal/AsyncWriteProxy.scala akka-persistence/src/main/scala/akka/persistence/journal/inmem/InmemJournal.scala akka-persistence/src/main/scala/akka/persistence/journal/leveldb/LeveldbKey.scala akka-persistence/src/main/scala/akka/persistence/snapshot/SnapshotStore.scala akka-persistence/src/test/scala/akka/persistence/serialization/SerializerSpec.scala project/AkkaBuild.scala
2014-06-23 14:33:35 +02:00
1 to warmupCycles foreach { i channel ! Deliver(PersistentRepr(s"msg${i}", persistenceId = "test"), destination.path) }
channel ! Deliver(Persistent(StartMeasure), destination.path)
!per #15230 rename processorId => persistentId * This is NOT binary compatible, we're in an *experimental* module. * disabled binary compat checks for package akka.persistence * Source compatibility is retained, but users should migrate do the new method name ASAP. * Plugin APIs were migrated in a way that allows the old plugins to compile agains 2.3.4 without having to change anything. Hopefuly this will help authors migrate to 2.3.4 sooner. This is only source level compatible, not binary compatible. * added deprecation warnings on all processorId methods and provided bridges where possible * for users, the migration should be painless, they can still override the old method, and it'll work. But we encourage them to move to persistenceId; All delegation code will have to be removed afterwards ofc. Conflicts: akka-persistence/src/main/scala/akka/persistence/Channel.scala akka-persistence/src/main/scala/akka/persistence/JournalProtocol.scala akka-persistence/src/main/scala/akka/persistence/Persistent.scala akka-persistence/src/main/scala/akka/persistence/PersistentChannel.scala akka-persistence/src/main/scala/akka/persistence/Processor.scala akka-persistence/src/main/scala/akka/persistence/Snapshot.scala akka-persistence/src/main/scala/akka/persistence/journal/AsyncWriteProxy.scala akka-persistence/src/main/scala/akka/persistence/journal/inmem/InmemJournal.scala akka-persistence/src/main/scala/akka/persistence/journal/leveldb/LeveldbKey.scala akka-persistence/src/main/scala/akka/persistence/snapshot/SnapshotStore.scala akka-persistence/src/test/scala/akka/persistence/serialization/SerializerSpec.scala project/AkkaBuild.scala
2014-06-23 14:33:35 +02:00
1 to loadCycles foreach { i channel ! Deliver(PersistentRepr(s"msg${i}", persistenceId = "test"), destination.path) }
channel ! Deliver(Persistent(StopMeasure), destination.path)
expectMsgPF(100 seconds) {
case throughput: Double println(f"\nthroughput = $throughput%.2f persistent messages per second")
}
}
def subscribeToConfirmation(probe: TestProbe): Unit =
system.eventStream.subscribe(probe.ref, classOf[DeliveredByPersistentChannel])
def awaitConfirmation(probe: TestProbe): Unit =
probe.expectMsgType[DeliveredByPersistentChannel]
"A command sourced processor" should {
"have some reasonable throughput" in {
stressCommandsourcedProcessor(None)
}
"have some reasonable throughput under failure conditions" in {
stressCommandsourcedProcessor(Some(warmupCycles + loadCycles / 10))
}
}
"A command sourced persistent actor" should {
"have some reasonable throughput" in {
stressCommandsourcedPersistentActor(None)
}
}
"An event sourced persistent actor" should {
"have some reasonable throughput" in {
!per persistAsync Breaks binary compatibility because adding new methods to Eventsourced trait. Since akka-persistence is experimental this is ok, yet source-level compatibility has been perserved thankfuly :-) Deprecates: * Rename of EventsourcedProcessor -> PersistentActor * Processor -> suggest using PersistentActor * Migration guide for akka-persistence is separate, as wel'll deprecate in minor versions (its experimental) * Persistent as well as ConfirmablePersistent - since Processor, their main user will be removed soon. Other changes: * persistAsync works as expected when mixed with persist * A counter must be kept for pending stashing invocations * Uses only 1 shared list buffer for persit / persistAsync * Includes small benchmark * Docs also include info about not using Persistent() wrapper * uses java LinkedList, for best performance of append / head on persistInvocations; the get(0) is safe, because these msgs only come in response to persistInvocations * Renamed internal *MessagesSuccess/Failure messages because we kept small mistakes seeing the class "with s" and "without s" as the same * Updated everything that refered to EventsourcedProcessor to PersistentActor, including samples Refs #15227 Conflicts: akka-docs/rst/project/migration-guides.rst akka-persistence/src/main/scala/akka/persistence/JournalProtocol.scala akka-persistence/src/main/scala/akka/persistence/Persistent.scala akka-persistence/src/test/scala/akka/persistence/PersistentActorSpec.scala project/AkkaBuild.scala
2014-05-21 01:35:21 +02:00
stressPersistentActor(None)
}
"have some reasonable throughput under failure conditions" in {
!per persistAsync Breaks binary compatibility because adding new methods to Eventsourced trait. Since akka-persistence is experimental this is ok, yet source-level compatibility has been perserved thankfuly :-) Deprecates: * Rename of EventsourcedProcessor -> PersistentActor * Processor -> suggest using PersistentActor * Migration guide for akka-persistence is separate, as wel'll deprecate in minor versions (its experimental) * Persistent as well as ConfirmablePersistent - since Processor, their main user will be removed soon. Other changes: * persistAsync works as expected when mixed with persist * A counter must be kept for pending stashing invocations * Uses only 1 shared list buffer for persit / persistAsync * Includes small benchmark * Docs also include info about not using Persistent() wrapper * uses java LinkedList, for best performance of append / head on persistInvocations; the get(0) is safe, because these msgs only come in response to persistInvocations * Renamed internal *MessagesSuccess/Failure messages because we kept small mistakes seeing the class "with s" and "without s" as the same * Updated everything that refered to EventsourcedProcessor to PersistentActor, including samples Refs #15227 Conflicts: akka-docs/rst/project/migration-guides.rst akka-persistence/src/main/scala/akka/persistence/JournalProtocol.scala akka-persistence/src/main/scala/akka/persistence/Persistent.scala akka-persistence/src/test/scala/akka/persistence/PersistentActorSpec.scala project/AkkaBuild.scala
2014-05-21 01:35:21 +02:00
stressPersistentActor(Some(warmupCycles + loadCycles / 10))
}
"have some reasonable throughput with stashing and unstashing every 3rd command" in {
!per persistAsync Breaks binary compatibility because adding new methods to Eventsourced trait. Since akka-persistence is experimental this is ok, yet source-level compatibility has been perserved thankfuly :-) Deprecates: * Rename of EventsourcedProcessor -> PersistentActor * Processor -> suggest using PersistentActor * Migration guide for akka-persistence is separate, as wel'll deprecate in minor versions (its experimental) * Persistent as well as ConfirmablePersistent - since Processor, their main user will be removed soon. Other changes: * persistAsync works as expected when mixed with persist * A counter must be kept for pending stashing invocations * Uses only 1 shared list buffer for persit / persistAsync * Includes small benchmark * Docs also include info about not using Persistent() wrapper * uses java LinkedList, for best performance of append / head on persistInvocations; the get(0) is safe, because these msgs only come in response to persistInvocations * Renamed internal *MessagesSuccess/Failure messages because we kept small mistakes seeing the class "with s" and "without s" as the same * Updated everything that refered to EventsourcedProcessor to PersistentActor, including samples Refs #15227 Conflicts: akka-docs/rst/project/migration-guides.rst akka-persistence/src/main/scala/akka/persistence/JournalProtocol.scala akka-persistence/src/main/scala/akka/persistence/Persistent.scala akka-persistence/src/test/scala/akka/persistence/PersistentActorSpec.scala project/AkkaBuild.scala
2014-05-21 01:35:21 +02:00
stressStashingPersistentActor()
}
}
"A persistent channel" should {
"have some reasonable throughput" in {
val probe = TestProbe()
subscribeToConfirmation(probe)
stressPersistentChannel()
probe.fishForMessage(100.seconds) {
case DeliveredByPersistentChannel(_, snr, _, _) snr == warmupCycles + loadCycles + 2
}
}
}
}