2015-04-16 2 views
0

J'ai un PersistentActor qui, pour un événement donné, peut muter son état et/ou son comportement. Pour persister l'état il n'y a pas de problème, mais il s'avère que j'ai besoin de persister le comportement aussi dans le cadre de l'état, sinon il ne sera pas initialisé correctement en cas de défaillance d'un snapshot, puisque l'instantané état et non le comportement. Comment y parvenir correctement?Comment persister comportement dans Akka Persistence?

Actuellement, ce que je fais est de sauvegarder un tuple de l'état et du comportement, mais je ne sais pas si c'est la bonne façon de le faire. Je suis ouvert à toutes sortes de suggestions et d'idées. FWIW, le cas d'utilisation de ce type d'acteur est comme une entrée pour le partitionnement de cluster, où j'ai besoin que chaque entrée passe par plusieurs états en utilisant (devenir/ne pas devenir) et persiste l'état des entrées ainsi que le comportement dans lequel elles se trouvaient. Merci beaucoup.

le code est ici illustrant le problème:

class FooActor extends PersistentActor with ActorLogging { 
    import FooActor._ 

    var state: List[String] = Nil 

    def updateState(e: FooEvent): Unit = e match { 
    case Initialized ⇒ context become initialized 
    case Uninitialized ⇒ context become uninitialized 
    case Fooed(data) ⇒ state = data :: state 
    } 

    def uninitialized: Receive = { 
    case Init  ⇒ persist(Initialized)(updateState) 
    } 

    def initialized: Receive = { 
    case Foo(data) ⇒ persist(Fooed(data))(updateState) 
    case Uninit ⇒ persist(Uninitialized)(updateState) 
    case Snap  ⇒ saveSnapshot((state, receiveCommand)) // how to persist current behavior also? 
    } 

    def receiveRecover: Receive = { 
    case e: FooEvent        ⇒ updateState(e) 
    // here, if I don't persist the behavior also, when the state is 
    // recovered, the actor would be in the uninitialized behavior and 
    // thus will not process any Foo commands 
    case SnapshotOffer(_, (_state: List[String], behavior: Receive)) ⇒ 
     state = _state 
     context become behavior 
    } 

    def receiveCommand: Receive = uninitialized 

    val persistenceId = "foo-pid" 
} 

object FooActor { 
    case object Init 
    case object Uninit 
    case object Snap 
    case class Foo(data: String) 
    trait FooEvent 
    case object Initialized extends FooEvent 
    case object Uninitialized extends FooEvent 
    case class Fooed(data: String) extends FooEvent 
} 
+1

Vous pourriez envisager d'utiliser [Akka Persistent FSM] (http://doc.akka.io/docs/akka/snapshot/scala/persistence.html#Persistent_FSM) pour cela, bien qu'il soit encore expérimental. Il conserve les événements générés, ainsi que les transitions d'état, qui peuvent correspondre aux changements de comportements dont vous avez besoin pour votre application. – acjay

Répondre

1

Ce que vous faites semble bon, juste que je pense en général, vous devez d'abord changer votre comportement au cours de récupérer et mettre à jour l'état (ce n'est pas le cas ici , mais il y a peut-être des contraintes de validation en général sur votre état). La seule autre option à laquelle je pense est de surcharger le gestionnaire de désabonnement et de garantir que vous vous êtes unitalisé et sauvegardé un instantané avant de descendre, ainsi qu'un message init. De cette façon, vous êtes garanti que lors de la réapparition, vous n'êtes pas initialisé et recevrez le message init. Quelque chose comme

onShutdown { 
    context become uninit 
    saveSnapshot(state) 
    persist(Init) 
} 

Lors du redémarrage, votre instantané de UNINIT recevrait d'abord un message d'initialisation, puis tout ce qui vient ensuite. Bien que je ne sois pas sûr que ce soit beaucoup mieux, vous devriez être capable d'éviter de stocker le comportement, mais alors vous devriez faire attention à ne jamais mourir sans le faire.