All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.comcast.xfinity.sirius.api.impl.SiriusSupervisor.scala Maven / Gradle / Ivy

The newest version!
/*
 *  Copyright 2012-2014 Comcast Cable Communications Management, LLC
 *
 *  Licensed under the Apache License, Version 2.0 (the "License");
 *  you may not use this file except in compliance with the License.
 *  You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 *  Unless required by applicable law or agreed to in writing, software
 *  distributed under the License is distributed on an "AS IS" BASIS,
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *  See the License for the specific language governing permissions and
 *  limitations under the License.
 */
package com.comcast.xfinity.sirius.api.impl

import bridge.PaxosStateBridge
import com.comcast.xfinity.sirius.api.impl.membership.{MembershipHelper, MembershipActor}
import paxos.PaxosMessages.PaxosMessage
import akka.actor._
import akka.agent.Agent
import com.comcast.xfinity.sirius.api.impl.paxos.Replica
import scala.concurrent.duration._
import paxos.PaxosSupervisor
import state.SiriusPersistenceActor.LogQuery
import state.StateSup
import com.comcast.xfinity.sirius.writeaheadlog.SiriusLog
import akka.event.Logging
import com.comcast.xfinity.sirius.api.{SiriusConfiguration, RequestHandler}
import status.StatusWorker
import com.comcast.xfinity.sirius.util.AkkaExternalAddressResolver
import status.StatusWorker.StatusQuery
import com.comcast.xfinity.sirius.uberstore.CompactionManager
import com.comcast.xfinity.sirius.uberstore.CompactionManager.CompactionMessage
import com.comcast.xfinity.sirius.api.impl.SiriusSupervisor.{ChildProvider, CheckPaxosMembership}
import com.comcast.xfinity.sirius.api.impl.membership.MembershipActor.MembershipMessage
import scala.language.postfixOps

object SiriusSupervisor {

  sealed trait SupervisorMessage
  case object IsInitializedRequest extends SupervisorMessage
  case object CheckPaxosMembership extends SupervisorMessage

  case class IsInitializedResponse(initialized: Boolean)

  /**
   * Factory for creating the children actors of SiriusSupervisor.
   *
   * @param requestHandler User implemented RequestHandler.
   * @param siriusLog Interface into the Sirius persistent log.
   * @param config the SiriusConfiguration for this node
   */
  protected[impl] class ChildProvider(requestHandler: RequestHandler, siriusLog: SiriusLog, config: SiriusConfiguration){

    val akkaExternalAddressResolver  = config.getProp[AkkaExternalAddressResolver](SiriusConfiguration.AKKA_EXTERNAL_ADDRESS_RESOLVER).
      getOrElse(throw new IllegalStateException("SiriusConfiguration.AKKA_EXTERNAL_ADDRESS_RESOLVER returned nothing"))

    def createStateAgent()(implicit context: ActorContext): Agent[SiriusState] =
      Agent(new SiriusState)(context.dispatcher)

    def createMembershipAgent()(implicit context: ActorContext) =
      Agent(Map[String, Option[ActorRef]]())(context.dispatcher)

    def createStateSupervisor(stateAgent: Agent[SiriusState])(implicit context: ActorContext) =
      context.actorOf(StateSup.props(requestHandler, siriusLog, stateAgent, config), "state")

    def createMembershipActor(membershipAgent: Agent[Map[String, Option[ActorRef]]])(implicit context: ActorContext) =
      context.actorOf(MembershipActor.props(membershipAgent, config), "membership")

    def createStateBridge(stateSupervisor: ActorRef, siriusSupervisor: ActorRef, membershipHelper: MembershipHelper)
                         (implicit context: ActorContext) = {
      context.actorOf(PaxosStateBridge
        .props(siriusLog.getNextSeq, stateSupervisor, siriusSupervisor, membershipHelper, config),
        "paxos-state-bridge")
    }

    def createPaxosSupervisor(membership: MembershipHelper, performFun: Replica.PerformFun)(implicit context: ActorContext) =
      context.actorOf(PaxosSupervisor.props(membership, siriusLog.getNextSeq, performFun, config), "paxos")

    def createStatusSubsystem(siriusSupervisor: ActorRef)(implicit context: ActorContext) = {
      val supervisorAddress = akkaExternalAddressResolver.externalAddressFor(siriusSupervisor)
      context.actorOf(StatusWorker.props(supervisorAddress, config), "status")
    }

    def createCompactionManager()(implicit context: ActorContext) =
      context.actorOf(CompactionManager.props(siriusLog)(config), "compactionManager")
  }

  /**
   * Create Props for a SiriusSupervisor actor.
   *
   * @param requestHandler the RequestHandler containing the callbacks for manipulating this instance's state
   * @param siriusLog the log to be used for persisting events
   * @param config SiriusConfiguration object full of all kinds of configuration goodies, see SiriusConfiguration for
   *               more information
   * @return  Props for creating this actor, which can then be further configured
   *         (e.g. calling `.withDispatcher()` on it)
   */
  def props(requestHandler: RequestHandler,
              siriusLog: SiriusLog,
              config: SiriusConfiguration): Props = {
    Props(classOf[SiriusSupervisor], new ChildProvider(requestHandler, siriusLog, config), config)
  }
}

/**
 * SiriusSupervisor is responsible for managing the top level of the actor hierarchy. It creates the actors
 * and manages their lifecycles, handles system initialization, and routes incoming messages. Messages from
 * remote systems come here first.
 *
 * @param childProvider factory for creating children actors
 * @param config SiriusConfiguration object containing node configuration
 */
private[impl] class SiriusSupervisor(childProvider: ChildProvider, config: SiriusConfiguration) extends Actor {

  implicit val executionContext = context.system.dispatcher

  val siriusStateAgent = childProvider.createStateAgent()
  val membershipAgent = childProvider.createMembershipAgent()
  val membershipHelper = MembershipHelper(membershipAgent, context.self)
  val stateSup = childProvider.createStateSupervisor(siriusStateAgent)
  val membershipActor = childProvider.createMembershipActor(membershipAgent)
  var orderingActor: Option[ActorRef] = None
  var compactionManager : Option[ActorRef] = None
  val statusSubsystem = childProvider.createStatusSubsystem(self)
  val stateBridge = childProvider.createStateBridge(stateSup, context.self, membershipHelper)

  private val logger = Logging(context.system, "Sirius")

  val initSchedule = context.system.scheduler
    .schedule(0 seconds, 50 milliseconds, self, SiriusSupervisor.IsInitializedRequest)

  val checkIntervalSecs = config.getProp(SiriusConfiguration.PAXOS_MEMBERSHIP_CHECK_INTERVAL, 2.0)
  val membershipCheckSchedule = context.system.scheduler.
    schedule(0 seconds, checkIntervalSecs seconds, self, CheckPaxosMembership)

  override def postStop() {
    membershipCheckSchedule.cancel()
  }

  def receive = {
    // TODO simplify this process a bit
    case SiriusSupervisor.IsInitializedRequest =>
      if (siriusStateAgent().areSubsystemsInitialized) {
        initSchedule.cancel()

        siriusStateAgent send (_.copy(supervisorInitialized = true))

        compactionManager = Some(childProvider.createCompactionManager())
        context.become(initialized)

        sender ! SiriusSupervisor.IsInitializedResponse(initialized = true)
      } else {
        sender ! SiriusSupervisor.IsInitializedResponse(initialized = false)
      }

    // Ignore other messages until Initialized.
    case _ =>
  }

  def initialized: Receive = {
    case get: Get => stateSup forward get
    case logQuery: LogQuery => stateSup forward logQuery
    case membershipMessage: MembershipMessage => membershipActor forward membershipMessage
    case SiriusSupervisor.IsInitializedRequest => sender ! new SiriusSupervisor.IsInitializedResponse(true)
    case statusQuery: StatusQuery => statusSubsystem forward statusQuery
    case compactionMessage: CompactionMessage => compactionManager match {
      case Some(actor) => actor forward compactionMessage
      case None =>
        logger.warning("Dropping {} cause CompactionMessage because CompactionManager is not up", compactionMessage.getClass.getName)
    }
    case CheckPaxosMembership =>
      if (membershipAgent.get().values.toSet.contains(Some(self))) {
        ensureOrderingActorRunning()
      } else {
        ensureOrderingActorStopped()
      }

    case paxosMessage: PaxosMessage => orderingActor match {
      case Some(actor) => actor forward paxosMessage
      case None =>
        logger.debug("Dropping {} PaxosMessage because Paxos is not up (yet?)", paxosMessage.getClass.getName)
    }
    case orderedReq: NonCommutativeSiriusRequest => orderingActor match {
      case Some(actor) => actor forward orderedReq
      case None =>
        logger.debug("Dropping {} because Paxos is not up (yet?)", orderedReq)
    }

    case Terminated(terminated) =>
      orderingActor match {
        case Some(actor) if actor == terminated => orderingActor = None
        case _ =>
      }

    case unknown: AnyRef => logger.warning("SiriusSupervisor Actor received unrecognized message {}", unknown)
  }

  def ensureOrderingActorRunning() {
    orderingActor match {
      case Some(actorRef) =>
        // do nothing, already alive and kicking
      case _ =>
        val actor = childProvider.createPaxosSupervisor(membershipHelper, stateBridge ! _)
        context.watch(actor)
        orderingActor = Some(actor)
    }
  }

  def ensureOrderingActorStopped() {
    orderingActor match {
      case Some(actorRef) =>
        context.stop(actorRef)
      case _ =>
        // do nothing, if there's an actor it's already been terminated
    }
    orderingActor = None
  }

}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy