Актер, контролируемый 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 просто создает нового актера после прекращения действия старого. Есть ли способ обойти это?

1 ответ

Решение

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

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

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

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

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

Другие вопросы по тегам