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

com.twitter.finagle.client.EndpointRegistry.scala Maven / Gradle / Ivy

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

import com.twitter.finagle.{Addr, Dtab}
import com.twitter.util.{Closable, Var, Witness}
import java.util.concurrent.atomic.AtomicReference
import scala.collection.mutable

/**
 * For all paths registered to a given client and Dtab, the [[EndpointRegistry]]
 * keeps a reference to observe changes to the current weight and endpoints. A path
 * and Var[Addr] are added for a given client and Dtab by calling
 * `addObservation`. A path is removed for a given client and Dtab by calling
 * `removeObservation`.
 */
private[twitter] object EndpointRegistry {

  private type Observation = (AtomicReference[Addr], Closable)

  private type EndpointMap = mutable.Map[String, Observation]
  private type DtabMap = mutable.Map[Dtab, EndpointMap]

  val registry = new EndpointRegistry()
}

private[twitter] class EndpointRegistry {
  import EndpointRegistry._

  // synchronized on `this`
  private[this] val registry = mutable.Map.empty[String, DtabMap]

  /**
   * Returns a map of Dtabs to a map of paths to addrs for a given client
   *
   * @param client Name of the client
   */
  def endpoints(client: String): Map[Dtab, Map[String, Addr]] = synchronized {
    registry.get(client) match {
      case Some(dtabMap) =>
        dtabMap.mapValues { paths =>
          paths.mapValues { case (observation, _) => observation.get() }.toMap
        }.toMap
      case None => Map.empty
    }
  }

  /**
   * Register a collection of endpoints for a given client, Dtab, and path.
   * If the path already exits for the given client and Dtab, it is replaced.
   *
   * @param client Name of the client
   * @param dtab Dtab for this path
   * @param path Path to observe endpoints for
   * @param endpoints Collection of endpoints for this serverset
   */
  def addObservation(
    client: String,
    dtab: Dtab,
    path: String,
    endpoints: Var[Addr]
  ): Unit = {
    val ar: AtomicReference[Addr] = new AtomicReference()
    val closable = endpoints.changes.register(Witness(ar))
    val observation = (ar, closable)
    synchronized {
      registry.get(client) match {
        case Some(dtabMap) =>
          dtabMap.get(dtab) match {
            case Some(dtabEntry) =>
              // If the path already exists, replace it and close the observation
              val prev = dtabEntry.put(path, observation)
              prev.foreach { case (_, closable) => closable.close() }
            case None =>
              dtabMap += ((dtab, mutable.Map(path -> observation)))
          }
        case None =>
          val endpointMap: EndpointMap = mutable.Map(path -> observation)
          val dtabMap: DtabMap = mutable.Map(dtab -> endpointMap)
          registry.put(client, dtabMap)
      }
    }
  }

  /**
   * Deregister a dtab and path to observe for a given client. If, after removal,
   * there are no paths for a dtab, remove the dtab from the client's registry
   * entry. If, after removal, there are no dtabs for the client, remove the
   * client from the registry.
   *
   * @param client Name of the client
   * @param dtab Dtab to remove the path for
   * @param path Path to remove observation for
   */
  def removeObservation(client: String, dtab: Dtab, path: String) = synchronized {
    registry.get(client).foreach { dtabEntries =>
      dtabEntries.get(dtab).foreach { entry =>
        entry.remove(path).foreach { case (_, closable) => closable.close() }
        if (entry.isEmpty) {
          dtabEntries.remove(dtab)
          if (dtabEntries.isEmpty) {
            registry.remove(client)
          }
        }
      }
    }
  }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy