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

com.twitter.finagle.loadbalancer.P2C.scala Maven / Gradle / Ivy

There is a newer version: 7.0.0
Show newest version
package com.twitter.finagle.loadbalancer

import com.twitter.finagle.Status
import com.twitter.finagle.util.Rng

/**
 * An O(1), concurrent, weighted fair load balancer. This uses the
 * ideas behind "power of 2 choices" [1] combined with O(1) biased
 * coin flipping through the aliasing method, described in
 * [[com.twitter.finagle.util.Drv Drv]].
 *
 * [1] Michael Mitzenmacher. 2001. The Power of Two Choices in
 * Randomized Load Balancing. IEEE Trans. Parallel Distrib. Syst. 12,
 * 10 (October 2001), 1094-1104.
 */
private trait P2C[Req, Rep] { self: Balancer[Req, Rep] =>
  /**
   * Our sturdy coin flipper.
   */
  protected def rng: Rng

  protected class Distributor(val vector: Vector[Node])
    extends DistributorT[Node] {
    type This = Distributor

    // Indicates if we've seen any down nodes during pick which we expected to be available
    @volatile private[this] var sawDown = false

    private[this] val nodeUp: Node => Boolean = { node =>
      node.status == Status.Open
    }

    private[this] val (up, down) = vector.partition(nodeUp)

    def needsRebuild: Boolean =
      sawDown || (down.nonEmpty && down.exists(nodeUp))

    def rebuild(): This = new Distributor(vector)
    def rebuild(vec: Vector[Node]): This = new Distributor(vec)

    // TODO: consider consolidating some of this code with `Aperture.Distributor.pick`
    def pick(): Node = {
      if (vector.isEmpty)
        return failingNode(emptyException)

      // if all nodes are down, we might as well try to send requests somewhere
      // as our view of the world may be out of date.
      val vec = if (up.isEmpty) down else up
      val size = vec.size

      if (size == 1) vec.head else {
        val a = rng.nextInt(size)
        var b = rng.nextInt(size)

        // Try to pick b, b != a, up to 10 times.
        var i = 10
        while (a == b && i > 0) {
          b = rng.nextInt(size)
          i -= 1
        }

        val nodeA = vec(a)
        val nodeB = vec(b)

        if (nodeA.status != Status.Open || nodeB.status != Status.Open)
          sawDown = true

        if (nodeA.load < nodeB.load) nodeA else nodeB
      }
    }
  }

  protected def initDistributor() = new Distributor(Vector.empty)
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy