the-algorithm/tweetypie/common/src/scala/com/twitter/tweetypie/decider/DeciderGates.scala

61 lines
1.9 KiB
Scala

package com.twitter.tweetypie
package decider
import com.google.common.hash.Hashing
import com.twitter.decider.Decider
import com.twitter.decider.Feature
import com.twitter.servo.gate.DeciderGate
import com.twitter.servo.util.Gate
import java.nio.charset.StandardCharsets
import scala.collection.mutable
trait DeciderGates {
def overrides: Map[String, Boolean] = Map.empty
def decider: Decider
def prefix: String
protected val seenFeatures: mutable.HashSet[String] = new mutable.HashSet[String]
private def deciderFeature(name: String): Feature = {
decider.feature(prefix + "_" + name)
}
def withOverride[T](name: String, mkGate: Feature => Gate[T]): Gate[T] = {
seenFeatures += name
overrides.get(name).map(Gate.const).getOrElse(mkGate(deciderFeature(name)))
}
protected def linear(name: String): Gate[Unit] = withOverride[Unit](name, DeciderGate.linear)
protected def byId(name: String): Gate[Long] = withOverride[Long](name, DeciderGate.byId)
/**
* It returns a Gate[String] that can be used to check availability of the feature.
* The string is hashed into a Long and used as an "id" and then used to call servo's
* DeciderGate.byId
*
* @param name decider name
* @return Gate[String]
*/
protected def byStringId(name: String): Gate[String] =
byId(name).contramap { s: String =>
Hashing.sipHash24().hashString(s, StandardCharsets.UTF_8).asLong()
}
def all: Traversable[String] = seenFeatures
def unusedOverrides: Set[String] = overrides.keySet.diff(all.toSet)
/**
* Generate a map of name -> availability, taking into account overrides.
* Overrides are either on or off so map to 10000 or 0, respectively.
*/
def availabilityMap: Map[String, Option[Int]] =
all.map { name =>
val availability: Option[Int] = overrides
.get(name)
.map(on => if (on) 10000 else 0)
.orElse(deciderFeature(name).availability)
name -> availability
}.toMap
}