PoisonPill вызывает мертвые письма с настойчивым актером

Я тестирую постоянного актера (который хранит сущность) с помощью набора тестов:

      val testActor = system.actorOf((IMPersistentActor.props("id1")))

      testActor ! AddElementCommand(refVal)
//      expectMsg(AddElementResponse(Success(refVal))) // no dead letters if I uncomment this

          testActor ! PoisonPill

который дает:

Message [app.server.persistence.IMPersistentActor$AddElementCommand]
from Actor[akka://IMPersistentActorTest/system/testActor-1#182376248]
to Actor[akka://IMPersistentActorTest/user/$a#-899768724] was not delivered. 

Я думал, что AddElementCommand(refVal) должно прийти раньше PoisonPill и должно быть обработано. Как можно объяснить такое поведение? Почему я получаю это мертвое письмо?

Интересно, однако, что если я раскомментирую expectMsg(AddElementResponse(Success(refVal))), я не получу мертвых букв, и тест пройден. Почему это ?


Вот полный код:

class IMPersistentActorTest
  extends TestKit(ActorSystem("IMPersistentActorTest"))
  with WordSpecLike
  with Matchers
  with BeforeAndAfterAll
  with ImplicitSender {


  override
  def afterAll: Unit = {
    TestKit.shutdownActorSystem(system)
  }

  "Actor" should {

    val el = Line(title = Some("bla"))
    val refVal: RefVal[Line] = RefVal.makeWithNewUUID[Line](el)

    "add an item to the list and preserve it after restart" in {
      //      val testActor = system.actorOf(Props(new IMPersistentActor("sc-000001") with RestartableActor))
      val testActor = system.actorOf((IMPersistentActor.props("id1")))

      testActor ! AddElementCommand(refVal)


      val state: State = IMPersistentActor.addEntity(IMPersistentActor.initState, refVal)


      testActor ! PoisonPill

      val testActor2: ActorRef = system.actorOf((IMPersistentActor.props("id1")))

      testActor2 ! GetElementsRequest


            expectMsg(GetElementsResponse(state))
    }
  }
}





class IMPersistentActor(id: String) extends PersistentActor with ActorLogging {

  private var state: State           =initState
  override def persistenceId: String = id

  override def receiveCommand: Receive = {
    case AddElementCommand(item) =>
      persist(EntityAdded(item)) { evt =>
        state = applyEvent(evt)
        sender() ! AddElementResponse(Success(item))
      }

    case GetElementsRequest => sender() ! GetElementsResponse(state)

  }

  override def receiveRecover: Receive = {
    case evt: Event        => state = applyEvent(evt)
    case RecoveryCompleted => log.info("Recovery completed!")
  }

  private def applyEvent(event: Event): State = event match {
    case EntityAdded(refVal: (RefValDyn)) => addEntity(state, refVal)
  }

}


object IMPersistentActor {
  type State = Map[RefDyn, RefValDyn]

  def initState:State=Map.empty

  def addEntity(s: State, refVal: RefValDyn): State = s + (refVal.r -> refVal)

  def props(id: String): Props = Props(new IMPersistentActor(id))


//  protocol
  case class AddElementCommand(entity: RefValDyn)
  case class AddElementResponse(entity: Try[RefValDyn])


  case object GetElementsRequest
  case class GetElementsResponse(items: State)

  // events
  sealed trait Event

  case class EntityAdded(entity: RefValDyn) extends Event

}

trait IMPersistentActorComm[T]

person jhegedus    schedule 09.05.2017    source источник


Ответы (1)


Вам следует ознакомиться с этой частью документация. Короткая версия заключается в том, что постоянство обрабатывается асинхронно, поэтому сообщение PoisonPill принимается перед сообщением, сообщающим актору, что событие было сохранено.

person Cyrille Corpet    schedule 09.05.2017
comment
@jhegedus Я не думаю, что это правильно. Что происходит в этом случае, так это то, что обычное сообщение получено, затем актор умирает, прежде чем сохранить его из-за PPill. В вашем случае обычное сообщение не доставляется. Я думаю, что это порядок сообщений bc гарантируется между одним и тем же отправителем и полученным. В вашем случае я предполагаю, что вы отправляете сообщения от двух разных анонимных отправителей (поскольку вы отправляете их не от одного и того же актера, а напрямую с помощью сообщения). Но я могу ошибаться. - person Diego Martinoia; 10.05.2017
comment
Я думаю, что ответ Кирилла правильный. Вот раздел, в котором подробно объясняется взаимодействие с PoisonPills в постоянстве akka: doc.akka.io/docs/akka/current/scala/ - person Michal Borowiecki; 25.06.2017