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

com.twitter.finagle.service.ExpiringService.scala Maven / Gradle / Ivy

The newest version!
package com.twitter.finagle.service

import com.twitter.finagle.stats.{Counter, StatsReceiver}
import com.twitter.finagle.util.AsyncLatch
import com.twitter.finagle.{param, Service, ServiceFactory, ServiceProxy, Stack, Stackable}
import com.twitter.util.{Duration, Promise, Future, NullTimerTask, Timer, Time}
import java.util.concurrent.atomic.AtomicBoolean

private[finagle] object ExpiringService {
  val role = Stack.Role("Expiration")

  /**
   * A class eligible for configuring a [[com.twitter.finagle.Stackable]]
   * [[com.twitter.finagle.server.ExpiringService]].
   *
   * @param idleTime max duration for which a connection is allowed to be idle.
   * @param lifeTIme max lifetime of a connection.
   */
  case class Param(idleTime: Duration, lifeTime: Duration)
  implicit object Param extends Stack.Param[Param] {
    val default = Param(Duration.Top, Duration.Top)
  }

  /**
   * Creates a [[com.twitter.finagle.Stackable]] [[com.twitter.finagle.service.ExpiringService]].
   */
  def module[Req, Rep]: Stackable[ServiceFactory[Req, Rep]] =
    new Stack.Simple[ServiceFactory[Req, Rep]] {
      val role = ExpiringService.role
      val description = "Expire a service after a certain amount of idle time"
      def make(next: ServiceFactory[Req, Rep])(implicit params: Params) = {
        val param.Timer(timer) = get[param.Timer]
        val ExpiringService.Param(idleTime, lifeTime) = get[ExpiringService.Param]
        val param.Stats(statsReceiver) = get[param.Stats]

        val idle = if (idleTime.isFinite) Some(idleTime) else None
        val life = if (lifeTime.isFinite) Some(lifeTime) else None

        (idle, life) match {
          case (None, None) => next
          case _ => next map { service =>
            val closeOnRelease = new CloseOnReleaseService(service)
            new ExpiringService(closeOnRelease, idle, life, timer, statsReceiver) {
              def onExpire() { closeOnRelease.close() }
            }
          }
        }
      }
    }
}

/**
 * A service wrapper that expires the self service after a
 * certain amount of idle time. By default, expiring calls
 * ``.close()'' on the self channel, but this action is
 * customizable.
 */
abstract class ExpiringService[Req, Rep](
  self: Service[Req, Rep],
  maxIdleTime: Option[Duration],
  maxLifeTime: Option[Duration],
  timer: Timer,
  stats: StatsReceiver
) extends ServiceProxy[Req, Rep](self)
{
  private[this] var active = true
  private[this] val latch = new AsyncLatch

  private[this] val idleCounter = stats.counter("idle")
  private[this] val lifeCounter = stats.counter("lifetime")
  private[this] var idleTask = startTimer(maxIdleTime, idleCounter)
  private[this] var lifeTask = startTimer(maxLifeTime, lifeCounter)
  private[this] val expireFnCalled = new AtomicBoolean(false)
  private[this] val didExpire = new Promise[Unit]

  private[this] def startTimer(duration: Option[Duration], counter: Counter) =
    duration map { t: Duration =>
      timer.schedule(t.fromNow) { expire(counter) }
    } getOrElse { NullTimerTask }

  private[this] def expire(counter: Counter) {
    if (deactivate()) {
      latch.await {
        expired()
        counter.incr()
      }
    }
  }

  private[this] def deactivate(): Boolean = synchronized {
    if (!active) false else {
      active = false
      idleTask.cancel()
      lifeTask.cancel()
      idleTask = NullTimerTask
      lifeTask = NullTimerTask
      true
    }
  }

  private[this] def expired(): Unit = {
    if (expireFnCalled.compareAndSet(false, true)) {
      didExpire.setDone()
      onExpire()
    }
  }

  protected def onExpire()

  override def apply(req: Req): Future[Rep] = {
    val decrLatch = synchronized {
      if (!active) false else {
        if (latch.incr() == 1) {
          idleTask.cancel()
          idleTask = NullTimerTask
        }
        true
      }
    }

    super.apply(req) ensure {
      if (decrLatch) {
        val n = latch.decr()
        synchronized {
          if (n == 0 && active)
            idleTask = startTimer(maxIdleTime, idleCounter)
        }
      }
    }
  }

  override def close(deadline: Time): Future[Unit] = {
    deactivate()
    expired()
    didExpire
  }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy