Актер, контролируемый BackoffSupervisor, теряет спрятанные сообщения после перезапуска

У меня есть актер с использованием тайника. Иногда при сбое теряются все спрятанные сообщения. Я обнаружил, что это зависит от того, какую логику контроля я использую.

Я написал простой пример.

Актер с заначкой:

case object WrongMessage
case object TestMessage
case object InitialMessage

class TestActor extends Actor with Stash {
  override def receive: Receive = uninitializedReceive

  def uninitializedReceive: Receive = {
    case TestMessage =>
      println(s"stash test message")
      stash()

    case WrongMessage =>
      println(s"wrong message")
      throw new Throwable("wrong message")

    case InitialMessage =>
      println(s"initial message")
      context.become(initializedReceive)
      unstashAll()
  }

  def initializedReceive: Receive = {
    case TestMessage =>
      println(s"test message")
  }
}

В следующем коде TestActor никогда не получает спрятанное TestMessage:

object Test1 extends App {
  implicit val system: ActorSystem = ActorSystem()
  val actorRef = system.actorOf(BackoffSupervisor.props(Backoff.onFailure(
      Props[TestActor], "TestActor", 1 seconds, 1 seconds, 0
  ).withSupervisorStrategy(OneForOneStrategy()({
    case _ => SupervisorStrategy.Restart
  }))))
  actorRef ! TestMessage
  Thread.sleep(5000L)
  actorRef ! WrongMessage
  Thread.sleep(5000L)
  actorRef ! InitialMessage
}

Но этот код работает хорошо:

class SupervisionActor extends Actor {
  val testActorRef: ActorRef = context.actorOf(Props[TestActor])

  override def supervisorStrategy: SupervisorStrategy = OneForOneStrategy()({
    case _ => SupervisorStrategy.Restart
  })

  override def receive: Receive = {
    case message => testActorRef forward message
  }
}

object Test2 extends App {
  implicit val system: ActorSystem = ActorSystem()
  val actorRef = system.actorOf(Props(classOf[SupervisionActor]))
  actorRef ! TestMessage
  Thread.sleep(5000L)
  actorRef ! WrongMessage
  Thread.sleep(5000L)
  actorRef ! InitialMessage
}

Я изучил источники и обнаружил, что надзор за акторами использует LocalActorRef.restart, поддерживаемый логика системного диспетчера, но BackoffSupervisor просто создает нового актера после прекращение действия старого. Есть ли способ обойти это?


person Aleksey Isachenkov    schedule 26.12.2018    source источник


Ответы (1)


Я не уверен, что можно заставить restart под BackoffSupervisor правильно отправлять спрятанные сообщения без каких-либо дополнительных усилий по повторной реализации.

Как вы уже заметили, BackoffSupervisor делает свое собственное restart, которое обходит стандартный жизненный цикл актера. Фактически, это явно указано в BackoffOnRestartSupervisor исходный код:

Какой бы ни была окончательная Директива, мы переведем все перезапуски в наши собственные перезапуски, что предполагает остановку дочернего элемента.

Если вы не читали об этой сообщенной проблеме, есть соответствующее обсуждение : проблема с Backoff.onFailure.

Backoff.onStop также даст желаемую функцию BackoffSupervisor, но, к сожалению, это имеет свои собственные варианты использования и не будет запускаться исключением.

person Leo C    schedule 28.12.2018
comment
Спасибо за ссылки. Кажется, что спрятать просто не работает с BackoffSupervisor. - person Aleksey Isachenkov; 05.01.2019