1
package com.twitter.finagle.tunable
2

3
import com.twitter.util.tunable.{
4
  JsonTunableMapper,
5
  NullTunableMap,
6
  ServiceLoadedTunableMap,
7
  TunableMap
8
}
9
import com.twitter.finagle.server.ServerInfo
10

11
import java.util.concurrent.ConcurrentHashMap
12
import java.util.function.{BiFunction, Function => JFunction}
13
import scala.collection.JavaConverters._
14

15
/**
16
 * Object used for getting the [[TunableMap]] for a given `id`. This [[TunableMap]] is composed
17
 * from 3 sources, in order of priority:
18
 *
19
 *  i. A mutable, in-process [[TunableMap.Mutable]].
20
 *  i. The dynamically loaded [[TunableMap]], provided via [[ServiceLoadedTunableMap.apply]].
21
 *  i. The JSON file-based [[TunableMap]], provided via [[JsonTunableMapper.loadJsonTunables]].
22
 *
23
 *  The JSON file-based [[TunableMap]] is a composition of file-based per-instance and
24
 *  per-environment [[TunableMap]]s. [[TunableMap]]s are composed in the following priority order:
25
 *
26
 *  i.  Environment and instance-specific
27
 *  i.  Environment-specific for all instances
28
 *  i.  Instance-specific
29
 *  i.  All instances
30
 *
31
 *  For more information, see
32
 *  [[https://twitter.github.io/finagle/guide/Configuration.html#tunables]].
33
 */
34
object StandardTunableMap {
35

36 2
  private[this] val clientMaps = new ConcurrentHashMap[String, TunableMap]()
37

38
  private[this] val composeMap = (mutable: TunableMap, serverInfo: ServerInfo, id: String) => {
39 2
    val json = loadJsonConfig(id, serverInfo)
40 2
    TunableMap.of(
41
      mutable,
42 2
      ServiceLoadedTunableMap(id),
43
      json
44
    )
45
  }
46

47
  def apply(id: String): TunableMap =
48 2
    apply(id, ServerInfo(), TunableMap.newMutable(s"Mutable($id)"))
49

50
  // Exposed for testing
51
  private[tunable] def apply(id: String, serverInfo: ServerInfo, mutable: TunableMap): TunableMap =
52 2
    clientMaps.computeIfAbsent(
53
      id,
54 2
      new JFunction[String, TunableMap] {
55 2
        def apply(ID: String): TunableMap = composeMap(mutable, serverInfo, id)
56
      })
57

58
  /**
59
   * Re-compose [[TunableMap]]s after ServerInfo initialized, re-subscribe
60
   * [[com.twitter.util.tunable.ServiceLoadedTunableMap]]s to ConfigBus.
61
   *
62
   * @note this should be called after ServerInfo.initialized().
63
   */
64
  private[twitter] def reloadAll(): Unit = {
65 0
    ServiceLoadedTunableMap.reloadAll()
66 0
    clientMaps.keys().asScala.toSeq.foreach { id =>
67 0
      clientMaps.computeIfPresent(
68
        id,
69 0
        new BiFunction[String, TunableMap, TunableMap] {
70
          def apply(id: String, curr: TunableMap): TunableMap = {
71 0
            val mutable = collectFirstOrElse(
72 0
              TunableMap.components(curr),
73 0
              TunableMap.newMutable(s"Mutable($id)")
74
            )
75 0
            composeMap(mutable, ServerInfo(), id)
76
          }
77
        }
78
      )
79
    }
80
  }
81

82
  private[this] def collectFirstOrElse(
83
    elements: Seq[TunableMap],
84
    default: TunableMap.Mutable
85
  ): TunableMap.Mutable = {
86
    elements
87
      .collectFirst {
88
        case mutable: TunableMap.Mutable => mutable
89 0
      }.getOrElse(default)
90
  }
91

92
  /**
93
   * Returns all registered [[TunableMap TunableMaps]] that have been
94
   * created by [[apply]], keyed by `id`.
95
   */
96
  def registeredIds: Map[String, TunableMap] =
97 0
    clientMaps.asScala.toMap
98

99
  /**
100
   * Load `TunableMap`s from JSON configuration files and compose them in the path order from
101
   * `JsonTunableMapper.pathsByPriority`.
102
   */
103
  private[tunable] def loadJsonConfig(id: String, serverInfo: ServerInfo): TunableMap = {
104 2
    val environmentOpt = serverInfo.environment
105 2
    val instanceIdOpt = serverInfo.instanceId
106

107
    val paths =
108 2
      JsonTunableMapper.pathsByPriority(s"com/twitter/tunables/$id/", environmentOpt, instanceIdOpt)
109

110 2
    paths.foldLeft(NullTunableMap: TunableMap) {
111
      case (map, path) =>
112 2
        map.orElse(JsonTunableMapper().loadJsonTunables(id, path))
113
    }
114
  }
115
}

Read our documentation on viewing source code .

Loading