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

com.twitter.finagle.pool.BufferingPool.scala Maven / Gradle / Ivy

There is a newer version: 21.2.0
Show newest version
package com.twitter.finagle.pool

import com.twitter.finagle._
import com.twitter.finagle.util.ConcurrentRingBuffer
import com.twitter.util.{Future, Time}
import java.util.concurrent.atomic.AtomicBoolean
import scala.annotation.tailrec

/**
 * Buffers up to `size` connections and produces/closes new ones beyond that limit.
 *
 * @see The [[https://twitter.github.io/finagle/guide/Clients.html#buffering-pool user guide]]
 *      for more details.
 */
class BufferingPool[Req, Rep](underlying: ServiceFactory[Req, Rep], size: Int)
    extends ServiceFactoryProxy[Req, Rep](underlying) {
  @volatile private[this] var draining = false

  private[this] class Wrapped(self: Service[Req, Rep]) extends ServiceProxy[Req, Rep](self) {
    private[this] val wasReleased = new AtomicBoolean(false)
    def releaseSelf() = {
      if (wasReleased.compareAndSet(false, true))
        self.close()
      else
        Future.Done
    }

    override def close(deadline: Time) = {
      // The ordering here is peculiar but important, avoiding races
      // between draining and giving back to the pool.
      if (status == Status.Closed || !buffer.tryPut(this) || draining)
        releaseSelf()
      else
        Future.Done
    }
  }

  private[this] val buffer = new ConcurrentRingBuffer[Wrapped](size)

  @tailrec
  private[this] def get(): Future[Service[Req, Rep]] =
    buffer.tryGet() match {
      case None =>
        underlying() map (new Wrapped(_))
      case Some(service) if service.status != Status.Closed =>
        Future.value(service)
      case Some(service) =>
        service.releaseSelf()
        get()
    }

  private[this] def drain(): Unit = {
    draining = true
    while (true) {
      buffer.tryGet() match {
        case Some(service) => service.releaseSelf()
        case None => return
      }
    }
  }

  override def apply(conn: ClientConnection): Future[Service[Req, Rep]] =
    if (draining) underlying() else get()

  override def close(deadline: Time) = {
    drain()
    underlying.close()
  }

  override def toString = "BufferingPool(%d)".format(size)
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy