Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Akka Persistent FSM を使ってみる。

Akka Persistent FSM を使ってみる。

Akka Persistent FSM でプロセスマネージャーを実装した話

Avatar for satoshi-m8a

satoshi-m8a

March 04, 2016
Tweet

More Decks by satoshi-m8a

Other Decks in Programming

Transcript

  1. ྫɿΧϯϑΝϨϯε༧໿γεςϜ • CreateOrder → MakeReservation → MakePayment ͷॱʹ࣮ߦ͢Δɻ • Reservation͕ग़དྷͳ͚Ε͹ɺMakePayment

    ͸࣮ߦ ͤͣʹɺWaitListʹొ࿥͢Δɻ • CreateOrder, MakeReservation, MakePayment ͕ ࣦഊ΍λΠϜΞ΢τͨ͠ΒશͯͷॲཧΛऔΓফ͢ɻ
  2. ࣮૷(1) object OrderProcessManager {
 def props(receiver: ActorRef, order: ActorRef, reservation:

    ActorRef, payment: ActorRef, waitList: ActorRef) =
 Props(new OrderProcessManager(receiver,order, reservation, payment, waitList))
 
 object States {
 
 sealed trait OrderProcessState extends FSMState
 
 case object Untouched extends OrderProcessState {
 override def identifier: String = "untouched"
 }
 
 case object OrderProcessing extends OrderProcessState {
 override def identifier: String = "order-processing"
 }
 
 case object ReservationProcessing extends OrderProcessState {
 override def identifier: String = "reservation-processing"
 }
 
 case object PaymentProcessing extends OrderProcessState {
 override def identifier: String = "payment-processing"
 }
 
 case object AddToWaitListProcessing extends OrderProcessState {
 override def identifier: String = "add-to-wait-list-processing"
 }
 
 case object OrderCompleted extends OrderProcessState {
 override def identifier: String = "order-completed"
 }
 
 case object CancellationProcessing extends OrderProcessState {
 override def identifier: String = "cancellation-processing"
 }
 
 }
 
 
 }
  3. ࣮૷(2) object Commands {
 
 sealed trait OrderProcessCommand extends Command[OrderProcessId]


    
 case class PlaceOrder(id: OrderProcessId) extends OrderProcessCommand
 
 case class CancelOrderProcess(id: OrderProcessId) extends OrderProcessCommand
 
 }
 
 object Events {
 
 sealed trait OrderProcessEvent extends DomainEvent
 
 case class OrderStarted(orderId: OrderId) extends OrderProcessEvent
 
 case class OrderAdded(orderId: OrderId) extends OrderProcessEvent
 
 case class ReservationAdded(reservationId: ReservationId, seatId: SeatId) extends OrderProcessEvent
 
 case class WaitListAdded(waitListId: WaitListId) extends OrderProcessEvent
 
 case class PaymentAdded(paymentId: PaymentId) extends OrderProcessEvent
 
 case class OrderConfirmed(orderId: OrderId) extends OrderProcessEvent
 
 case class PaymentRemoved(paymentId: PaymentId) extends OrderProcessEvent
 
 case class ReservationRemoved(reservationId: ReservationId) extends OrderProcessEvent
 
 case class WaitListRemoved(waitListId: WaitListId) extends OrderProcessEvent
 
 case class OrderRemoved(orderId: OrderId) extends OrderProcessEvent
 
 }
 
 object Data {
 
 case class OrderProcessData(orderId: Option[OrderId],
 reservationId: Option[ReservationId],
 waitListId: Option[WaitListId],
 paymentId: Option[PaymentId]) {
 def isAllCanceled = orderId.isEmpty && reservationId.isEmpty && waitListId.isEmpty && paymentId.isEmpty
 }
 }

  4. ࣮૷(3) class OrderProcessManager(receiver: ActorRef, order: ActorRef, reservation: ActorRef, payment: ActorRef,

    waitList: ActorRef)
 extends PersistentFSM[OrderProcessState, OrderProcessData, OrderProcessEvent] {
 
 override def domainEventClassTag: ClassTag[OrderProcessEvent] = scala.reflect.classTag[OrderProcessEvent]
 
 override def persistenceId: String = self.path.parent.name + "-" + self.path.name
 
 override def applyEvent(domainEvent: OrderProcessEvent, currentData: OrderProcessData): OrderProcessData = {
 domainEvent match {
 case OrderStarted(orderId) => currentData.copy(orderId = Some(orderId))
 case OrderAdded(orderId) => currentData.copy(orderId = Some(orderId))
 case ReservationAdded(reservationId, seatId) => currentData.copy(reservationId = Some(reservationId))
 case WaitListAdded(waitListId) => currentData.copy(waitListId = Some(waitListId))
 case PaymentAdded(paymentId) => currentData.copy(paymentId = Some(paymentId))
 case PaymentRemoved(_) => currentData.copy(paymentId = None)
 case ReservationRemoved(_) => currentData.copy(reservationId = None)
 case WaitListRemoved(_) => currentData.copy(waitListId = None)
 case OrderRemoved(_) => currentData.copy(orderId = None)
 case OrderConfirmed(_) => currentData
 }
 }
  5. ࣮૷(4) startWith(Untouched, OrderProcessData(None, None, None, None))
 
 when(Untouched, 10 seconds)

    {
 case Event(PlaceOrder(id), _) =>
 val orderId = OrderId(UUID.randomUUID().toString)
 
 goto(OrderProcessing) applying OrderStarted(orderId) forMax (5 seconds) andThen {
 case OrderProcessData(Some(oid), _, _, _) =>
 order ! CreateOrder(orderId)
 }
 }
 
 when(OrderProcessing, 10 seconds) {
 case Event(OrderCreated(orderId), _) =>
 val reservationId = ReservationId(UUID.randomUUID().toString)
 
 goto(ReservationProcessing) applying OrderAdded(orderId) forMax (5 seconds) andThen {
 case OrderProcessData(Some(oid), _, _, _) =>
 reservation ! MakeReservation(reservationId)
 }
 case Event(StateTimeout, _) =>
 goto(CancellationProcessing) andThen {
 case d: OrderProcessData =>
 cancelAll(d)
 }
 }
  6. ࣮૷(5) when(ReservationProcessing, 10 seconds) {
 case Event(SeatReserved(reservationId, seatId), _) =>


    val paymentId = PaymentId(UUID.randomUUID().toString)
 
 goto(PaymentProcessing) applying ReservationAdded(reservationId, seatId) forMax (5 seconds) andThen {
 case OrderProcessData(Some(_), Some(rid), _, _) =>
 payment ! MakePayment(paymentId)
 }
 case Event(SeatNotReserved(reservationId, waitListId), _) =>
 goto(AddToWaitListProcessing) applying WaitListAdded(waitListId) forMax (5 seconds) andThen {
 case OrderProcessData(Some(_), None, Some(wid), _) =>
 waitList ! AddSeatToWaitList(waitListId)
 }
 case Event(StateTimeout, _) =>
 goto(CancellationProcessing) andThen {
 case d: OrderProcessData =>
 cancelAll(d)
 }
 }
 
 when(AddToWaitListProcessing, 10 seconds) {
 case Event(SeatAdded(waitListId), OrderProcessData(Some(oid), _, _, _)) =>
 receiver ! OrderConfirmed(oid)
 goto(OrderCompleted)
 }
  7. ࣮૷(6) when(PaymentProcessing, 10 seconds) {
 case Event(PaymentAccepted(paymentId), OrderProcessData(Some(oid), _, _,

    _)) =>
 receiver ! OrderConfirmed(oid)
 goto(OrderCompleted) applying PaymentAdded(paymentId)
 case Event(StateTimeout, _) =>
 goto(CancellationProcessing) andThen {
 case d: OrderProcessData =>
 cancelAll(d)
 }
 }
 
 when(OrderCompleted, 10 seconds) {
 case Event(CancelOrderProcess(id), OrderProcessData(Some(oid), Some(rid), wid, pid)) =>
 goto(CancellationProcessing) andThen {
 case d: OrderProcessData =>
 cancelAll(d)
 }
 }
  8. ࣮૷(7) when(CancellationProcessing, 10 seconds) {
 case Event(PaymentCanceled(paymentId), _) =>
 stay

    applying PaymentRemoved(paymentId) andThen {
 case d: OrderProcessData if d.isAllCanceled =>
 goto(Untouched)
 }
 case Event(WaitListCanceled(waitListId), _) =>
 stay applying WaitListRemoved(waitListId) andThen {
 case d: OrderProcessData if d.isAllCanceled =>
 goto(Untouched)
 }
 case Event(ReservationCanceled(reservationId), _) =>
 stay applying ReservationRemoved(reservationId) andThen {
 case d: OrderProcessData if d.isAllCanceled =>
 goto(Untouched)
 }
 case Event(OrderCanceled(orderId), _) =>
 stay applying OrderRemoved(orderId) andThen {
 case d: OrderProcessData if d.isAllCanceled =>
 goto(Untouched)
 }
 case Event(StateTimeout, _) =>
 stay andThen {
 case d: OrderProcessData =>
 cancelAll(d)
 }
 }
  9. ࣮૷(8) private def cancelAll(d: OrderProcessData) = {
 d.paymentId.foreach {
 id

    =>
 payment ! CancelPayment(id)
 }
 d.waitListId.foreach {
 id =>
 waitList ! CancelWaitList(id)
 }
 d.reservationId.foreach {
 id =>
 reservation ! CancelReservation(id)
 }
 d.orderId.foreach {
 id =>
 order ! CancelOrder(id)
 }
 }
  10. ·ͱΊͱߟ࡯ • PersistentFSMͰProcess ManagerΛ࣮૷ͨ͠ • become/unbecomeͱͷҧ͍ • ू໿ʹ࣮૷͢Δ͔ɺϓϩηεϚωʔδϟʔΛىಈ͢Δ͔ɺू໿͕ϓ ϩηεϚωʔδϟʔΛىಈ͢Δ͔ɻ •

    ֤ू໿ʹ͸AtLeastOnceDeliveryͰίϚϯυΛૹΔ͔ɺTimeoutͰ Կ౓΋ϦτϥΠ͢Δ͔ɻ • ֤ू໿͔ΒΠϕϯτ͸EventBus΍Distributed Pub-SubͰτϐοΫ Λߪಡ͢Δ͔ɻ