fix NPE in SnapshotStore, #26580

* access of context.system from Future callback
* also changed import context.dispatcher, shouldn't be needed but
  rather safe than sorry
This commit is contained in:
Patrik Nordwall 2019-03-20 13:10:52 +01:00
parent 886088f03b
commit 610a89c2d0
2 changed files with 84 additions and 74 deletions

View file

@ -5,14 +5,17 @@
package akka.persistence.journal package akka.persistence.journal
import scala.concurrent.duration._ import scala.concurrent.duration._
import akka.actor._ import akka.actor._
import akka.pattern.pipe import akka.pattern.pipe
import akka.persistence._ import akka.persistence._
import akka.util.Helpers.toRootLowerCase import akka.util.Helpers.toRootLowerCase
import scala.collection.immutable import scala.collection.immutable
import scala.concurrent.ExecutionContext
import scala.concurrent.Future import scala.concurrent.Future
import scala.util.{ Failure, Success, Try } import scala.util.{ Failure, Success, Try }
import scala.util.control.NonFatal import scala.util.control.NonFatal
import akka.pattern.CircuitBreaker import akka.pattern.CircuitBreaker
/** /**
@ -21,7 +24,6 @@ import akka.pattern.CircuitBreaker
trait AsyncWriteJournal extends Actor with WriteJournalBase with AsyncRecovery { trait AsyncWriteJournal extends Actor with WriteJournalBase with AsyncRecovery {
import AsyncWriteJournal._ import AsyncWriteJournal._
import JournalProtocol._ import JournalProtocol._
import context.dispatcher
private val extension = Persistence(context.system) private val extension = Persistence(context.system)
private val publish = extension.settings.internal.publishPluginCommands private val publish = extension.settings.internal.publishPluginCommands
@ -56,6 +58,8 @@ trait AsyncWriteJournal extends Actor with WriteJournalBase with AsyncRecovery {
final val receiveWriteJournal: Actor.Receive = { final val receiveWriteJournal: Actor.Receive = {
// cannot be a val in the trait due to binary compatibility // cannot be a val in the trait due to binary compatibility
val replayDebugEnabled: Boolean = config.getBoolean("replay-filter.debug") val replayDebugEnabled: Boolean = config.getBoolean("replay-filter.debug")
val eventStream = context.system.eventStream // used from Future callbacks
implicit val ec: ExecutionContext = context.dispatcher
{ {
case WriteMessages(messages, persistentActor, actorInstanceId) => case WriteMessages(messages, persistentActor, actorInstanceId) =>
@ -71,7 +75,7 @@ trait AsyncWriteJournal extends Actor with WriteJournalBase with AsyncRecovery {
catch { case NonFatal(e) => Future.failed(e) } catch { case NonFatal(e) => Future.failed(e) }
case f @ Failure(_) => case f @ Failure(_) =>
// exception from preparePersistentBatch => rejected // exception from preparePersistentBatch => rejected
Future.successful(messages.collect { case a: AtomicWrite => f }) Future.successful(messages.collect { case _: AtomicWrite => f })
}).map { results => }).map { results =>
if (results.nonEmpty && results.size != atomicWriteCount) if (results.nonEmpty && results.size != atomicWriteCount)
throw new IllegalStateException( throw new IllegalStateException(
@ -171,7 +175,7 @@ trait AsyncWriteJournal extends Actor with WriteJournalBase with AsyncRecovery {
} }
.pipeTo(replyTo) .pipeTo(replyTo)
.foreach { _ => .foreach { _ =>
if (publish) context.system.eventStream.publish(r) if (publish) eventStream.publish(r)
} }
case d @ DeleteMessagesTo(persistenceId, toSequenceNr, persistentActor) => case d @ DeleteMessagesTo(persistenceId, toSequenceNr, persistentActor) =>
@ -185,7 +189,7 @@ trait AsyncWriteJournal extends Actor with WriteJournalBase with AsyncRecovery {
} }
.pipeTo(persistentActor) .pipeTo(persistentActor)
.onComplete { _ => .onComplete { _ =>
if (publish) context.system.eventStream.publish(d) if (publish) eventStream.publish(d)
} }
} }
} }

View file

@ -4,8 +4,10 @@
package akka.persistence.snapshot package akka.persistence.snapshot
import scala.concurrent.ExecutionContext
import scala.concurrent.duration._ import scala.concurrent.duration._
import scala.concurrent.Future import scala.concurrent.Future
import akka.actor._ import akka.actor._
import akka.pattern.pipe import akka.pattern.pipe
import akka.persistence._ import akka.persistence._
@ -16,7 +18,6 @@ import akka.pattern.CircuitBreaker
*/ */
trait SnapshotStore extends Actor with ActorLogging { trait SnapshotStore extends Actor with ActorLogging {
import SnapshotProtocol._ import SnapshotProtocol._
import context.dispatcher
private val extension = Persistence(context.system) private val extension = Persistence(context.system)
private val publish = extension.settings.internal.publishPluginCommands private val publish = extension.settings.internal.publishPluginCommands
@ -32,6 +33,10 @@ trait SnapshotStore extends Actor with ActorLogging {
final def receive = receiveSnapshotStore.orElse[Any, Unit](receivePluginInternal) final def receive = receiveSnapshotStore.orElse[Any, Unit](receivePluginInternal)
final val receiveSnapshotStore: Actor.Receive = { 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) => case LoadSnapshot(persistenceId, criteria, toSequenceNr) =>
if (criteria == SnapshotSelectionCriteria.None) { if (criteria == SnapshotSelectionCriteria.None) {
senderPersistentActor() ! LoadSnapshotResult(snapshot = None, toSequenceNr) senderPersistentActor() ! LoadSnapshotResult(snapshot = None, toSequenceNr)
@ -78,7 +83,7 @@ trait SnapshotStore extends Actor with ActorLogging {
} }
.pipeTo(self)(senderPersistentActor()) .pipeTo(self)(senderPersistentActor())
.onComplete { .onComplete {
case _ => if (publish) context.system.eventStream.publish(d) case _ => if (publish) eventStream.publish(d)
} }
case evt: DeleteSnapshotSuccess => case evt: DeleteSnapshotSuccess =>
@ -99,7 +104,7 @@ trait SnapshotStore extends Actor with ActorLogging {
} }
.pipeTo(self)(senderPersistentActor()) .pipeTo(self)(senderPersistentActor())
.onComplete { .onComplete {
case _ => if (publish) context.system.eventStream.publish(d) case _ => if (publish) eventStream.publish(d)
} }
case evt: DeleteSnapshotsFailure => case evt: DeleteSnapshotsFailure =>
@ -109,6 +114,7 @@ trait SnapshotStore extends Actor with ActorLogging {
try tryReceivePluginInternal(evt) try tryReceivePluginInternal(evt)
finally senderPersistentActor() ! evt finally senderPersistentActor() ! evt
} }
}
/** Documents intent that the sender() is expected to be the PersistentActor */ /** Documents intent that the sender() is expected to be the PersistentActor */
@inline private final def senderPersistentActor(): ActorRef = sender() @inline private final def senderPersistentActor(): ActorRef = sender()