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

sss.openstar.test.MultiTx.scala Maven / Gradle / Ivy

package sss.openstar.test

import akka.actor.Status.{Failure => FutureFailure}
import akka.actor.{Actor, ActorContext, ActorLogging, ActorRef, ActorSystem, Cancellable, Props, ReceiveTimeout}
import akka.pattern.pipe
import sss.db
import sss.openstar.StartupHooks.HookDone
import sss.openstar.chains.TxWriterActor._
import sss.openstar.counterledger.{CounterLedger, CounterLedgerTx, FailOnTheseNodes, IncrementFailure}
import sss.openstar.ledger.{LedgerId, LedgerItem, SignedTxEntry}
import sss.openstar.network.MessageEventBus
import sss.openstar.test.MultiIdentityTestTransactionSender.FireTxs
import sss.openstar.tools.SendTxSupport.SendTx

import scala.concurrent.duration._


object MultiTx {

  case object Printout
  def apply(counterLedger: CounterLedger,
            counterLedgerId: LedgerId,
            maxInProgress: Int)(implicit context: ActorContext,
                                            sendTx: SendTx,
                                            events: MessageEventBus,
                                            runContext: db.SyncRunContext): ActorRef = {
    context.actorOf(Props(new MultiTx(counterLedger, counterLedgerId, maxInProgress)), "MultiTx")
  }
}

class MultiTx(counterLedger: CounterLedger,
                         counterLedgerId: LedgerId,
                         maxInProgress: Int)(implicit actorContext: ActorContext,
                                                         sendTx: SendTx,
                                                         events: MessageEventBus,
                                                         runContext: db.SyncRunContext)
  extends Actor with ActorLogging {

  override def preStart(): Unit = {
    super.preStart()
    events.subscribe(HookDone.getClass)
  }

  private def createLedgerItem(cmd: CounterLedgerTx) =
    LedgerItem(counterLedgerId, cmd.txId, SignedTxEntry(cmd.toBytes).toBytes)

  private def createIncrementFailure = createLedgerItem(IncrementFailure())

  private def createFailOnNodes = {
    createLedgerItem(FailOnTheseNodes(Seq.empty))
  }

  import actorContext.dispatcher
  actorContext.setReceiveTimeout(120.seconds)
  val printoutTimeout = 60.seconds
  val as = actorContext.system

  private var inProgress = 0
  private var totalTxsProcessed: Int = 0
  private var rejectedTxs: Int = 0

  private var schedFire: Option[Cancellable] = None

  private def scheduleFireTxs(delayInSeconds: Int): Unit = schedFire = schedFire match {
    case None => Option(as.scheduler.scheduleOnce(delayInSeconds.seconds, self, FireTxs))
    case x if inProgress <= 0  =>
      x.map(_.cancel())
      Option(as.scheduler.scheduleOnce(delayInSeconds.seconds, self, FireTxs))
    case x => x
  }

  override def receive: Receive = {
    case HookDone =>
      log.info("Starting!")
      scheduleFireTxs(1)
      as.scheduler.scheduleOnce(printoutTimeout, self, Printout)

    case FutureFailure(e) =>
      log.debug(s"Failed, retry, $e")
      inProgress -= 1
      scheduleFireTxs(2)

    case r: InternalNack =>
      log.warning(s"Rejected tx $r")
      rejectedTxs += 1
      inProgress -= 1
      scheduleFireTxs(2)

    case r: InternalTempNack =>
      log.debug("TEMP NACK")
      inProgress -= 1
      scheduleFireTxs(2)

    case com: InternalCommit =>
      log.debug(s"Got commit: $com, going again")
      inProgress -= 1
      totalTxsProcessed += 1
      scheduleFireTxs(0)

    case ReceiveTimeout =>
      val counter = counterLedger.successCounter().runSyncAndGet
      val failures = counterLedger.failureCounter().runSyncAndGet
      log.info(s"totalProcessed = $totalTxsProcessed counter = $counter failures = $failures")
      context.setReceiveTimeout(Duration.Inf)

    case FireTxs if inProgress < maxInProgress =>
      log.debug("FireTxs!")
      (0 until (maxInProgress - inProgress)) foreach { _ =>
        inProgress += 1
        sendTx(createFailOnNodes).whenAvailableLocally.map(_.commitTxResult.internalCommit) pipeTo self
      }

    case FireTxs =>
      log.debug("FireTxs ")
      if (inProgress % 10 == 0) log.debug(s"$inProgress in progress, wait x seconds.... (total ${totalTxsProcessed})")
      scheduleFireTxs(10)

    case Printout =>
      log.info(s"Printout total=$totalTxsProcessed and rejected=$rejectedTxs")
      as.scheduler.scheduleOnce(printoutTimeout, self, Printout)
  }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy