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

com.twitter.finagle.builder.Cluster.scala Maven / Gradle / Ivy

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

import collection.mutable
import com.twitter.concurrent.Spool
import com.twitter.concurrent.Spool.*::
import com.twitter.util.Future
import java.util.logging.Logger

/**
 * Cluster is a collection of servers. The intention of this interface
 * to express membership in a cluster of servers that provide a
 * specific service.
 *
 * Note that a Cluster can be elastic: members can join or leave at
 * any time.
 */
trait Cluster[T] { self =>
  /**
   * A Future object that is defined when the cluster is initialized.
   * Cluster users can subscribe to this Future object to check or get notified of the availability of the cluster.
   * @return the Future object
   */
  def ready: Future[Unit] = {
    def flatten(spool: Spool[Cluster.Change[T]]): Future[Unit] = spool match {
      case Cluster.Add(_) *:: tail => Future.Done
      case _ *:: tail => tail flatMap flatten
    }

    snap match {
      case (current, changes) if current.isEmpty => changes flatMap flatten
      case _ => Future.Done
    }
  }

  /**
   * Takes a snapshot of the collection; returns the current elements and a Spool of future updates
   */
  def snap: (Seq[T], Future[Spool[Cluster.Change[T]]])

  /**
   * Maps the elements as well as future updates to the given type.
   */
  def map[U](f: T => U): Cluster[U] = new Cluster[U] {
    // Translation cache to ensure that mapping is idempotent.
    private[this] val mapped = mutable.HashMap.empty[T, mutable.Queue[U]]

    override def ready = self.ready

    def snap: (Seq[U], Future[Spool[Cluster.Change[U]]]) = {
      val (seqT, changeT) = self.snap
      val seqU = mapped.synchronized {
        seqT map { t =>
          val q = mapped.getOrElseUpdate(t, mutable.Queue[U]())
          val u = f(t)
          q.enqueue(u)
          u
        }
      }

      val changeU = changeT map { spoolT =>
        spoolT map { elem =>
          mapped.synchronized {
            elem match {
              case Cluster.Add(t) =>
                val q = mapped.getOrElseUpdate(t, mutable.Queue[U]())
                val u = f(t)
                q.enqueue(u)
                Cluster.Add(u)

              case Cluster.Rem(t) =>
                 mapped.get(t) match {
                   case Some(q) =>
                     val u = q.dequeue()
                     if (q.isEmpty)
                       mapped.remove(t)
                     Cluster.Rem(u)

                   case None =>
                     Logger.getLogger("").warning(
                       "cluster does not have removed key, regenerating")
                     Cluster.Rem(f(t))
                 }
            }
          }
        }
      }
      (seqU, changeU)
    }
  }
}

object Cluster {
  sealed abstract trait Change[T] { def value: T }
  case class Add[T](value: T) extends Change[T]
  case class Rem[T](value: T) extends Change[T]
}

/**
 * A simple static cluster implementation.
 */
case class StaticCluster[T](underlying: Seq[T]) extends Cluster[T] {
  def snap: (Seq[T], Future[Spool[Cluster.Change[T]]]) = (underlying, Future.value(Spool.empty))
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy