Add advanced passivation strategies (#31124)

This commit is contained in:
Peter Vlugter 2022-02-15 01:20:42 +13:00 committed by GitHub
parent 7a25618748
commit 61c27ae69c
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
27 changed files with 4611 additions and 665 deletions

View file

@ -100,6 +100,24 @@ private[akka] final class FrequencyList[A](dynamicAging: Boolean, clock: OptionV
def contains(value: A): Boolean = lookupNode.contains(value)
def leastFrequent: OptionVal[A] = frequency.getFirst match {
case OptionVal.Some(least) =>
least.nodes.getFirst match {
case OptionVal.Some(first) => OptionVal.Some(first.value)
case _ => OptionVal.none
}
case _ => OptionVal.none
}
def mostFrequent: OptionVal[A] = frequency.getLast match {
case OptionVal.Some(most) =>
most.nodes.getLast match {
case OptionVal.Some(last) => OptionVal.Some(last.value)
case _ => OptionVal.none
}
case _ => OptionVal.none
}
def leastToMostFrequent: Iterator[A] = forwardIterator.map(_.value)
def mostToLeastFrequent: Iterator[A] = backwardIterator.map(_.value)

View file

@ -76,6 +76,16 @@ private[akka] final class RecencyList[A](clock: RecencyList.Clock) {
def contains(value: A): Boolean = lookupNode.contains(value)
def leastRecent: OptionVal[A] = recency.getFirst match {
case OptionVal.Some(first) => OptionVal.Some(first.value)
case _ => OptionVal.none
}
def mostRecent: OptionVal[A] = recency.getLast match {
case OptionVal.Some(last) => OptionVal.Some(last.value)
case _ => OptionVal.none
}
def leastToMostRecent: Iterator[A] = recency.forwardIterator.map(_.value)
def mostToLeastRecent: Iterator[A] = recency.backwardIterator.map(_.value)

View file

@ -89,6 +89,11 @@ private[akka] final class SegmentedRecencyList[A](
def contains(value: A): Boolean = lookupNode.contains(value)
def leastRecent: OptionVal[A] = segments(lowest).getFirst match {
case OptionVal.Some(first) => OptionVal.Some(first.value)
case _ => OptionVal.none
}
def leastToMostRecentOf(level: Int): Iterator[A] = segments(level).forwardIterator.map(_.value)
def removeLeastRecentOverLimit(): immutable.Seq[A] = {

View file

@ -171,13 +171,33 @@ object ClusterShardingSettings {
val idleEntitySettings: Option[PassivationStrategySettings.IdleSettings],
val activeEntityLimit: Option[Int],
val replacementPolicySettings: Option[PassivationStrategySettings.PolicySettings],
val admissionSettings: Option[PassivationStrategySettings.AdmissionSettings],
private[akka] val oldSettingUsed: Boolean) {
private[akka] def this(
idleEntitySettings: Option[PassivationStrategySettings.IdleSettings],
activeEntityLimit: Option[Int],
replacementPolicySettings: Option[PassivationStrategySettings.PolicySettings],
oldSettingUsed: Boolean) =
this(idleEntitySettings, activeEntityLimit, replacementPolicySettings, admissionSettings = None, oldSettingUsed)
def this(
idleEntitySettings: Option[PassivationStrategySettings.IdleSettings],
activeEntityLimit: Option[Int],
replacementPolicySettings: Option[PassivationStrategySettings.PolicySettings],
admissionSettings: Option[PassivationStrategySettings.AdmissionSettings]) =
this(idleEntitySettings, activeEntityLimit, replacementPolicySettings, admissionSettings, oldSettingUsed = false)
def this(
idleEntitySettings: Option[PassivationStrategySettings.IdleSettings],
activeEntityLimit: Option[Int],
replacementPolicySettings: Option[PassivationStrategySettings.PolicySettings]) =
this(idleEntitySettings, activeEntityLimit, replacementPolicySettings, oldSettingUsed = false)
this(
idleEntitySettings,
activeEntityLimit,
replacementPolicySettings,
admissionSettings = None,
oldSettingUsed = false)
import PassivationStrategySettings._
@ -213,19 +233,29 @@ object ClusterShardingSettings {
def withLeastFrequentlyUsedReplacement(): PassivationStrategySettings =
withReplacementPolicy(LeastFrequentlyUsedSettings.defaults)
def withAdmission(settings: AdmissionSettings): PassivationStrategySettings =
copy(admissionSettings = Some(settings))
private[akka] def withOldIdleStrategy(timeout: FiniteDuration): PassivationStrategySettings =
copy(
idleEntitySettings = Some(new IdleSettings(timeout, None)),
activeEntityLimit = None,
replacementPolicySettings = None,
admissionSettings = None,
oldSettingUsed = true)
private def copy(
idleEntitySettings: Option[IdleSettings] = idleEntitySettings,
activeEntityLimit: Option[Int] = activeEntityLimit,
replacementPolicySettings: Option[PolicySettings] = replacementPolicySettings,
admissionSettings: Option[AdmissionSettings] = admissionSettings,
oldSettingUsed: Boolean = oldSettingUsed): PassivationStrategySettings =
new PassivationStrategySettings(idleEntitySettings, activeEntityLimit, replacementPolicySettings, oldSettingUsed)
new PassivationStrategySettings(
idleEntitySettings,
activeEntityLimit,
replacementPolicySettings,
admissionSettings,
oldSettingUsed)
}
/**
@ -239,6 +269,7 @@ object ClusterShardingSettings {
idleEntitySettings = None,
activeEntityLimit = None,
replacementPolicySettings = None,
admissionSettings = None,
oldSettingUsed = false)
val disabled: PassivationStrategySettings = defaults
@ -248,6 +279,7 @@ object ClusterShardingSettings {
classic.idleEntitySettings.map(IdleSettings.apply),
classic.activeEntityLimit,
classic.replacementPolicySettings.map(PolicySettings.apply),
classic.admissionSettings.map(AdmissionSettings.apply),
classic.oldSettingUsed)
def toClassic(settings: PassivationStrategySettings): ClassicPassivationStrategySettings =
@ -255,6 +287,7 @@ object ClusterShardingSettings {
settings.idleEntitySettings.map(IdleSettings.toClassic),
settings.activeEntityLimit,
settings.replacementPolicySettings.map(PolicySettings.toClassic),
settings.admissionSettings.map(AdmissionSettings.toClassic),
settings.oldSettingUsed)
object IdleSettings {
@ -390,6 +423,221 @@ object ClusterShardingSettings {
new LeastFrequentlyUsedSettings(dynamicAging)
}
object AdmissionSettings {
val defaults = new AdmissionSettings(filter = None, window = None)
object FilterSettings {
def apply(classic: ClassicPassivationStrategySettings.AdmissionSettings.FilterSettings): FilterSettings =
classic match {
case classic: ClassicPassivationStrategySettings.AdmissionSettings.FrequencySketchSettings =>
FrequencySketchSettings(classic)
}
def toClassic(settings: FilterSettings): ClassicPassivationStrategySettings.AdmissionSettings.FilterSettings =
settings match {
case settings: FrequencySketchSettings => FrequencySketchSettings.toClassic(settings)
}
}
sealed trait FilterSettings
object FrequencySketchSettings {
val defaults =
new FrequencySketchSettings(depth = 4, counterBits = 4, widthMultiplier = 4, resetMultiplier = 10.0)
def apply(classic: ClassicPassivationStrategySettings.AdmissionSettings.FrequencySketchSettings)
: FrequencySketchSettings =
new FrequencySketchSettings(
classic.depth,
classic.counterBits,
classic.widthMultiplier,
classic.resetMultiplier)
def toClassic(settings: FrequencySketchSettings)
: ClassicPassivationStrategySettings.AdmissionSettings.FrequencySketchSettings =
new ClassicPassivationStrategySettings.AdmissionSettings.FrequencySketchSettings(
settings.depth,
settings.counterBits,
settings.widthMultiplier,
settings.resetMultiplier)
}
final class FrequencySketchSettings(
val depth: Int,
val counterBits: Int,
val widthMultiplier: Int,
val resetMultiplier: Double)
extends FilterSettings {
def withDepth(depth: Int): FrequencySketchSettings =
copy(depth = depth)
def withCounterBits(bits: Int): FrequencySketchSettings =
copy(counterBits = bits)
def withWidthMultiplier(multiplier: Int): FrequencySketchSettings =
copy(widthMultiplier = multiplier)
def withResetMultiplier(multiplier: Double): FrequencySketchSettings =
copy(resetMultiplier = multiplier)
private def copy(
depth: Int = depth,
counterBits: Int = counterBits,
widthMultiplier: Int = widthMultiplier,
resetMultiplier: Double = resetMultiplier): FrequencySketchSettings =
new FrequencySketchSettings(depth, counterBits, widthMultiplier, resetMultiplier)
}
object WindowSettings {
val defaults: WindowSettings = new WindowSettings(
initialProportion = 0.01,
minimumProportion = 0.01,
maximumProportion = 1.0,
optimizer = None,
policy = None)
def apply(classic: ClassicPassivationStrategySettings.AdmissionSettings.WindowSettings): WindowSettings =
new WindowSettings(
classic.initialProportion,
classic.minimumProportion,
classic.maximumProportion,
classic.optimizer.map(OptimizerSettings.apply),
classic.policy.map(PolicySettings.apply))
def toClassic(settings: WindowSettings): ClassicPassivationStrategySettings.AdmissionSettings.WindowSettings =
new ClassicPassivationStrategySettings.AdmissionSettings.WindowSettings(
settings.initialProportion,
settings.minimumProportion,
settings.maximumProportion,
settings.optimizer.map(OptimizerSettings.toClassic),
settings.policy.map(PolicySettings.toClassic))
}
final class WindowSettings(
val initialProportion: Double,
val minimumProportion: Double,
val maximumProportion: Double,
val optimizer: Option[OptimizerSettings],
val policy: Option[PolicySettings]) {
def withInitialProportion(proportion: Double): WindowSettings =
copy(initialProportion = proportion)
def withMinimumProportion(proportion: Double): WindowSettings =
copy(minimumProportion = proportion)
def withMaximumProportion(proportion: Double): WindowSettings =
copy(maximumProportion = proportion)
def withOptimizer(settings: OptimizerSettings): WindowSettings =
copy(optimizer = Some(settings))
def withPolicy(settings: PolicySettings): WindowSettings =
copy(policy = Some(settings))
private def copy(
initialProportion: Double = initialProportion,
minimumProportion: Double = minimumProportion,
maximumProportion: Double = maximumProportion,
optimizer: Option[OptimizerSettings] = optimizer,
policy: Option[PolicySettings] = policy): WindowSettings =
new WindowSettings(initialProportion, minimumProportion, maximumProportion, optimizer, policy)
}
object OptimizerSettings {
def apply(classic: ClassicPassivationStrategySettings.AdmissionSettings.OptimizerSettings): OptimizerSettings =
classic match {
case classic: ClassicPassivationStrategySettings.AdmissionSettings.HillClimbingSettings =>
HillClimbingSettings(classic)
}
def toClassic(
settings: OptimizerSettings): ClassicPassivationStrategySettings.AdmissionSettings.OptimizerSettings =
settings match {
case settings: HillClimbingSettings => HillClimbingSettings.toClassic(settings)
}
}
sealed trait OptimizerSettings
object HillClimbingSettings {
val defaults: HillClimbingSettings = new HillClimbingSettings(
adjustMultiplier = 10.0,
initialStep = 0.0625,
restartThreshold = 0.05,
stepDecay = 0.98)
def apply(
classic: ClassicPassivationStrategySettings.AdmissionSettings.HillClimbingSettings): HillClimbingSettings =
new HillClimbingSettings(
classic.adjustMultiplier,
classic.initialStep,
classic.restartThreshold,
classic.stepDecay)
def toClassic(
settings: HillClimbingSettings): ClassicPassivationStrategySettings.AdmissionSettings.HillClimbingSettings =
new ClassicPassivationStrategySettings.AdmissionSettings.HillClimbingSettings(
settings.adjustMultiplier,
settings.initialStep,
settings.restartThreshold,
settings.stepDecay)
}
final class HillClimbingSettings(
val adjustMultiplier: Double,
val initialStep: Double,
val restartThreshold: Double,
val stepDecay: Double)
extends OptimizerSettings {
def withAdjustMultiplier(multiplier: Double): HillClimbingSettings =
copy(adjustMultiplier = multiplier)
def withInitialStep(step: Double): HillClimbingSettings =
copy(initialStep = step)
def withRestartThreshold(threshold: Double): HillClimbingSettings =
copy(restartThreshold = threshold)
def withStepDecay(decay: Double): HillClimbingSettings =
copy(stepDecay = decay)
private def copy(
adjustMultiplier: Double = adjustMultiplier,
initialStep: Double = initialStep,
restartThreshold: Double = restartThreshold,
stepDecay: Double = stepDecay): HillClimbingSettings =
new HillClimbingSettings(adjustMultiplier, initialStep, restartThreshold, stepDecay)
}
def apply(classic: ClassicPassivationStrategySettings.AdmissionSettings): AdmissionSettings =
new AdmissionSettings(classic.filter.map(FilterSettings.apply), classic.window.map(WindowSettings.apply))
def toClassic(settings: AdmissionSettings): ClassicPassivationStrategySettings.AdmissionSettings =
new ClassicPassivationStrategySettings.AdmissionSettings(
settings.filter.map(FilterSettings.toClassic),
settings.window.map(WindowSettings.toClassic))
}
final class AdmissionSettings(
val filter: Option[AdmissionSettings.FilterSettings],
val window: Option[AdmissionSettings.WindowSettings]) {
def withFilter(settings: AdmissionSettings.FilterSettings): AdmissionSettings =
copy(filter = Some(settings))
def withWindow(settings: AdmissionSettings.WindowSettings): AdmissionSettings =
copy(window = Some(settings))
private def copy(
filter: Option[AdmissionSettings.FilterSettings] = filter,
window: Option[AdmissionSettings.WindowSettings] = window): AdmissionSettings =
new AdmissionSettings(filter, window)
}
private[akka] def oldDefault(idleTimeout: FiniteDuration): PassivationStrategySettings =
disabled.withOldIdleStrategy(idleTimeout)
}

View file

@ -53,12 +53,22 @@ akka.cluster.sharding {
}
# Recommended default strategy for automatic passivation with an active entity limit.
# Configured with a segmented least recently used (SLRU) replacement policy.
# Configured with an adaptive recency-based admission window, a frequency-based admission filter, and
# a segmented least recently used (SLRU) replacement policy for the main active entity tracking.
default-strategy {
# Default limit of 100k active entities in a shard region (in a cluster node).
active-entity-limit = 100000
# Segmented LRU replacement policy with an 80% "protected" level by default.
# Admisson window with LRU policy and adaptive sizing, and a frequency sketch admission filter to the main area.
admission {
window {
policy = least-recently-used
optimizer = hill-climbing
}
filter = frequency-sketch
}
# Main area with segmented LRU replacement policy with an 80% "protected" level by default.
replacement {
policy = least-recently-used
least-recently-used {
@ -118,6 +128,84 @@ akka.cluster.sharding {
dynamic-aging = off
}
}
# An optional admission area, with a window for newly and recently activated entities, and an admission filter
# to determine whether a candidate should be admitted to the main area of the passivation strategy.
admission {
# An optional window area, where newly created entities will be admitted initially, and when evicted
# from the window area have an opportunity to move to the main area based on the admission filter.
window {
# The initial sizing for the window area (if enabled), as a fraction of the total active entity limit.
proportion = 0.01
# The minimum adaptive sizing for the window area, as a fraction of the total active entity limit.
# Only applies when an adaptive window optimizer is enabled.
minimum-proportion = 0.01
# The maximum adaptive sizing for the window area, as a fraction of the total active entity limit.
# Only applies when an adaptive window optimizer is enabled.
maximum-proportion = 1.0
# Adaptive optimizer to use for dynamically resizing the window area. Possible values are:
# - "hill-climbing"
# Set to "none" or "off" to disable adaptive sizing of the window area.
optimizer = off
# A window proportion optimizer using a simple hill-climbing algorithm.
hill-climbing {
# Multiplier of the active entity limit for how often (in accesses) to adjust the window proportion.
adjust-multiplier = 10.0
# The size of the initial step to take (also used when the climbing restarts).
initial-step = 0.0625
# A threshold for the change in active rate (hit rate) to restart climbing.
restart-threshold = 0.05
# The decay ratio applied on each climbing step.
step-decay = 0.98
}
# Replacement policy to use for the window area.
# Entities that are evicted from the window area may move to the main area, based on the admission filter.
# Possible values are the same as for the main replacement policy.
# Set to "none" or "off" to disable the window area.
policy = none
least-recently-used {
segmented {
levels = none
proportions = []
}
}
most-recently-used {}
least-frequently-used {
dynamic-aging = off
}
}
# The admission filter for the main area of the passivation strategy. Possible values are:
# - "frequency-sketch"
# Set to "none" or "off" to disable the admission filter and always admit to the main area.
filter = none
# An admission filter based on a frequency sketch (a variation of a count-min sketch).
frequency-sketch {
# The depth of the frequency sketch (the number of hash functions).
depth = 4
# The size of the frequency counters in bits: 2, 4, 8, 16, 32, or 64 bits.
counter-bits = 4
# Multiplier of the active entity limit for the width of the frequency sketch.
width-multiplier = 4
# Multiplier of the active entity limit for how often the reset operation of the frequency sketch is applied.
reset-multiplier = 10.0
}
}
}
}

View file

@ -137,13 +137,33 @@ object ClusterShardingSettings {
val idleEntitySettings: Option[PassivationStrategySettings.IdleSettings],
val activeEntityLimit: Option[Int],
val replacementPolicySettings: Option[PassivationStrategySettings.PolicySettings],
val admissionSettings: Option[PassivationStrategySettings.AdmissionSettings],
private[akka] val oldSettingUsed: Boolean) {
private[akka] def this(
idleEntitySettings: Option[PassivationStrategySettings.IdleSettings],
activeEntityLimit: Option[Int],
replacementPolicySettings: Option[PassivationStrategySettings.PolicySettings],
oldSettingUsed: Boolean) =
this(idleEntitySettings, activeEntityLimit, replacementPolicySettings, admissionSettings = None, oldSettingUsed)
def this(
idleEntitySettings: Option[PassivationStrategySettings.IdleSettings],
activeEntityLimit: Option[Int],
replacementPolicySettings: Option[PassivationStrategySettings.PolicySettings],
admissionSettings: Option[PassivationStrategySettings.AdmissionSettings]) =
this(idleEntitySettings, activeEntityLimit, replacementPolicySettings, admissionSettings, oldSettingUsed = false)
def this(
idleEntitySettings: Option[PassivationStrategySettings.IdleSettings],
activeEntityLimit: Option[Int],
replacementPolicySettings: Option[PassivationStrategySettings.PolicySettings]) =
this(idleEntitySettings, activeEntityLimit, replacementPolicySettings, oldSettingUsed = false)
this(
idleEntitySettings,
activeEntityLimit,
replacementPolicySettings,
admissionSettings = None,
oldSettingUsed = false)
import PassivationStrategySettings._
@ -179,19 +199,29 @@ object ClusterShardingSettings {
def withLeastFrequentlyUsedReplacement(): PassivationStrategySettings =
withReplacementPolicy(LeastFrequentlyUsedSettings.defaults)
def withAdmission(settings: AdmissionSettings): PassivationStrategySettings =
copy(admissionSettings = Some(settings))
private[akka] def withOldIdleStrategy(timeout: FiniteDuration): PassivationStrategySettings =
copy(
idleEntitySettings = Some(new IdleSettings(timeout, None)),
activeEntityLimit = None,
replacementPolicySettings = None,
admissionSettings = None,
oldSettingUsed = true)
private def copy(
idleEntitySettings: Option[IdleSettings] = idleEntitySettings,
activeEntityLimit: Option[Int] = activeEntityLimit,
replacementPolicySettings: Option[PolicySettings] = replacementPolicySettings,
admissionSettings: Option[AdmissionSettings] = admissionSettings,
oldSettingUsed: Boolean = oldSettingUsed): PassivationStrategySettings =
new PassivationStrategySettings(idleEntitySettings, activeEntityLimit, replacementPolicySettings, oldSettingUsed)
new PassivationStrategySettings(
idleEntitySettings,
activeEntityLimit,
replacementPolicySettings,
admissionSettings,
oldSettingUsed)
}
/**
@ -203,6 +233,7 @@ object ClusterShardingSettings {
idleEntitySettings = None,
activeEntityLimit = None,
replacementPolicySettings = None,
admissionSettings = None,
oldSettingUsed = false)
val disabled: PassivationStrategySettings = defaults
@ -340,6 +371,195 @@ object ClusterShardingSettings {
new LeastFrequentlyUsedSettings(dynamicAging)
}
object AdmissionSettings {
val defaults = new AdmissionSettings(filter = None, window = None)
object FilterSettings {
def optional(config: Config): Option[FilterSettings] =
toRootLowerCase(config.getString("filter")) match {
case "off" | "none" => None
case "frequency-sketch" => Some(FrequencySketchSettings(config.getConfig("frequency-sketch")))
case _ => None
}
}
sealed trait FilterSettings
object FrequencySketchSettings {
val defaults =
new FrequencySketchSettings(depth = 4, counterBits = 4, widthMultiplier = 4, resetMultiplier = 10.0)
def apply(config: Config): FrequencySketchSettings = {
val depth = config.getInt("depth")
val counterBits = config.getInt("counter-bits")
val widthMultiplier = config.getInt("width-multiplier")
val resetMultiplier = config.getDouble("reset-multiplier")
new FrequencySketchSettings(depth, counterBits, widthMultiplier, resetMultiplier)
}
}
final class FrequencySketchSettings(
val depth: Int,
val counterBits: Int,
val widthMultiplier: Int,
val resetMultiplier: Double)
extends FilterSettings {
def withDepth(depth: Int): FrequencySketchSettings =
copy(depth = depth)
def withCounterBits(bits: Int): FrequencySketchSettings =
copy(counterBits = bits)
def withWidthMultiplier(multiplier: Int): FrequencySketchSettings =
copy(widthMultiplier = multiplier)
def withResetMultiplier(multiplier: Double): FrequencySketchSettings =
copy(resetMultiplier = multiplier)
private def copy(
depth: Int = depth,
counterBits: Int = counterBits,
widthMultiplier: Int = widthMultiplier,
resetMultiplier: Double = resetMultiplier): FrequencySketchSettings =
new FrequencySketchSettings(depth, counterBits, widthMultiplier, resetMultiplier)
}
object WindowSettings {
val defaults: WindowSettings = new WindowSettings(
initialProportion = 0.01,
minimumProportion = 0.01,
maximumProportion = 1.0,
optimizer = None,
policy = None)
def apply(config: Config): WindowSettings = {
val initialProportion = config.getDouble("proportion")
val minimumProportion = config.getDouble("minimum-proportion")
val maximumProportion = config.getDouble("maximum-proportion")
val optimizer = OptimizerSettings.optional(config)
val policy = PolicySettings.optional(config)
new WindowSettings(initialProportion, minimumProportion, maximumProportion, optimizer, policy)
}
def optional(config: Config): Option[WindowSettings] =
toRootLowerCase(config.getString("policy")) match {
case "off" | "none" => None
case _ => Some(WindowSettings(config))
}
}
final class WindowSettings(
val initialProportion: Double,
val minimumProportion: Double,
val maximumProportion: Double,
val optimizer: Option[OptimizerSettings],
val policy: Option[PolicySettings]) {
def withInitialProportion(proportion: Double): WindowSettings =
copy(initialProportion = proportion)
def withMinimumProportion(proportion: Double): WindowSettings =
copy(minimumProportion = proportion)
def withMaximumProportion(proportion: Double): WindowSettings =
copy(maximumProportion = proportion)
def withOptimizer(settings: OptimizerSettings): WindowSettings =
copy(optimizer = Some(settings))
def withPolicy(settings: PolicySettings): WindowSettings =
copy(policy = Some(settings))
private def copy(
initialProportion: Double = initialProportion,
minimumProportion: Double = minimumProportion,
maximumProportion: Double = maximumProportion,
optimizer: Option[OptimizerSettings] = optimizer,
policy: Option[PolicySettings] = policy): WindowSettings =
new WindowSettings(initialProportion, minimumProportion, maximumProportion, optimizer, policy)
}
object OptimizerSettings {
def optional(config: Config): Option[OptimizerSettings] =
toRootLowerCase(config.getString("optimizer")) match {
case "off" | "none" => None
case "hill-climbing" => Some(HillClimbingSettings(config.getConfig("hill-climbing")))
case _ => None
}
}
sealed trait OptimizerSettings
object HillClimbingSettings {
val defaults: HillClimbingSettings = new HillClimbingSettings(
adjustMultiplier = 10.0,
initialStep = 0.0625,
restartThreshold = 0.05,
stepDecay = 0.98)
def apply(config: Config): HillClimbingSettings = {
val adjustMultiplier = config.getDouble("adjust-multiplier")
val initialStep = config.getDouble("initial-step")
val restartThreshold = config.getDouble("restart-threshold")
val stepDecay = config.getDouble("step-decay")
new HillClimbingSettings(adjustMultiplier, initialStep, restartThreshold, stepDecay)
}
}
final class HillClimbingSettings(
val adjustMultiplier: Double,
val initialStep: Double,
val restartThreshold: Double,
val stepDecay: Double)
extends OptimizerSettings {
def withAdjustMultiplier(multiplier: Double): HillClimbingSettings =
copy(adjustMultiplier = multiplier)
def withInitialStep(step: Double): HillClimbingSettings =
copy(initialStep = step)
def withRestartThreshold(threshold: Double): HillClimbingSettings =
copy(restartThreshold = threshold)
def withStepDecay(decay: Double): HillClimbingSettings =
copy(stepDecay = decay)
private def copy(
adjustMultiplier: Double = adjustMultiplier,
initialStep: Double = initialStep,
restartThreshold: Double = restartThreshold,
stepDecay: Double = stepDecay): HillClimbingSettings =
new HillClimbingSettings(adjustMultiplier, initialStep, restartThreshold, stepDecay)
}
def optional(config: Config): Option[AdmissionSettings] = {
val filter = FilterSettings.optional(config)
val window = WindowSettings.optional(config.getConfig("window"))
if (filter.isDefined || window.isDefined)
Some(new AdmissionSettings(filter, window))
else None
}
}
final class AdmissionSettings(
val filter: Option[AdmissionSettings.FilterSettings],
val window: Option[AdmissionSettings.WindowSettings]) {
def withFilter(settings: AdmissionSettings.FilterSettings): AdmissionSettings =
copy(filter = Some(settings))
def withWindow(settings: AdmissionSettings.WindowSettings): AdmissionSettings =
copy(window = Some(settings))
private def copy(
filter: Option[AdmissionSettings.FilterSettings] = filter,
window: Option[AdmissionSettings.WindowSettings] = window): AdmissionSettings =
new AdmissionSettings(filter, window)
}
/**
* API MAY CHANGE: Settings and configuration for passivation strategies may change after additional
* testing and feedback.
@ -357,7 +577,12 @@ object ClusterShardingSettings {
case _ => Some(strategyConfig.getInt("active-entity-limit"))
}
val replacementPolicySettings = PolicySettings.optional(strategyConfig.getConfig("replacement"))
new PassivationStrategySettings(idleEntitySettings, activeEntityLimit, replacementPolicySettings)
val admissionSettings = AdmissionSettings.optional(strategyConfig.getConfig("admission"))
new PassivationStrategySettings(
idleEntitySettings,
activeEntityLimit,
replacementPolicySettings,
admissionSettings)
}
}
@ -465,6 +690,101 @@ object ClusterShardingSettings {
idle: Option[IdlePassivationStrategy])
extends PassivationStrategy
/**
* INTERNAL API
*/
@InternalApi
private[akka] object CompositePassivationStrategy {
object AdmissionFilter {
def apply(filterSettings: Option[PassivationStrategySettings.AdmissionSettings.FilterSettings]): AdmissionFilter =
filterSettings match {
case Some(settings: PassivationStrategySettings.AdmissionSettings.FrequencySketchSettings) =>
FrequencySketchAdmissionFilter(
widthMultiplier = settings.widthMultiplier,
resetMultiplier = settings.resetMultiplier,
depth = settings.depth,
counterBits = settings.counterBits)
case _ => AlwaysAdmissionFilter
}
}
sealed trait AdmissionFilter
case object AlwaysAdmissionFilter extends AdmissionFilter
case class FrequencySketchAdmissionFilter(
widthMultiplier: Int,
resetMultiplier: Double,
depth: Int,
counterBits: Int)
extends AdmissionFilter
object AdmissionOptimizer {
def apply(optimizerSettings: Option[PassivationStrategySettings.AdmissionSettings.OptimizerSettings])
: AdmissionOptimizer =
optimizerSettings match {
case Some(settings: PassivationStrategySettings.AdmissionSettings.HillClimbingSettings) =>
HillClimbingAdmissionOptimizer(
adjustMultiplier = settings.adjustMultiplier,
initialStep = settings.initialStep,
restartThreshold = settings.restartThreshold,
stepDecay = settings.stepDecay)
case _ => NoAdmissionOptimizer
}
}
sealed trait AdmissionOptimizer
case object NoAdmissionOptimizer extends AdmissionOptimizer
case class HillClimbingAdmissionOptimizer(
adjustMultiplier: Double,
initialStep: Double,
restartThreshold: Double,
stepDecay: Double)
extends AdmissionOptimizer
def apply(
limit: Int,
mainSettings: Option[PassivationStrategySettings.PolicySettings],
admissionSettings: PassivationStrategySettings.AdmissionSettings,
idle: Option[IdlePassivationStrategy]): CompositePassivationStrategy = {
val mainStrategy = PassivationStrategy(mainSettings, limit = 0, idle = None)
val windowStrategy = PassivationStrategy(admissionSettings.window.flatMap(_.policy), limit = 0, idle = None)
val initialWindowProportion = admissionSettings.window.fold(0.0)(_.initialProportion)
val minimumWindowProportion = admissionSettings.window.fold(0.0)(_.minimumProportion)
val maximumWindowProportion = admissionSettings.window.fold(0.0)(_.maximumProportion)
val windowOptimizer = AdmissionOptimizer(admissionSettings.window.flatMap(_.optimizer))
val admissionFilter = AdmissionFilter(admissionSettings.filter)
CompositePassivationStrategy(
limit,
mainStrategy,
windowStrategy,
initialWindowProportion,
minimumWindowProportion,
maximumWindowProportion,
windowOptimizer,
admissionFilter,
idle)
}
}
/**
* INTERNAL API
*/
@InternalApi
private[akka] case class CompositePassivationStrategy(
limit: Int,
mainStrategy: PassivationStrategy,
windowStrategy: PassivationStrategy,
initialWindowProportion: Double,
minimumWindowProportion: Double,
maximumWindowProportion: Double,
windowOptimizer: CompositePassivationStrategy.AdmissionOptimizer,
admissionFilter: CompositePassivationStrategy.AdmissionFilter,
idle: Option[IdlePassivationStrategy])
extends PassivationStrategy
/**
* INTERNAL API
* Determine the passivation strategy to use from settings.
@ -481,7 +801,21 @@ object ClusterShardingSettings {
}
settings.passivationStrategySettings.activeEntityLimit match {
case Some(limit) =>
settings.passivationStrategySettings.replacementPolicySettings match {
settings.passivationStrategySettings.admissionSettings match {
case Some(admission) =>
val main = settings.passivationStrategySettings.replacementPolicySettings
CompositePassivationStrategy(limit, main, admission, idle)
case _ =>
PassivationStrategy(settings.passivationStrategySettings.replacementPolicySettings, limit, idle)
}
case _ => idle.getOrElse(NoPassivationStrategy)
}
}
def apply(
policySettings: Option[PassivationStrategySettings.PolicySettings],
limit: Int,
idle: Option[IdlePassivationStrategy]): PassivationStrategy = policySettings match {
case Some(settings: PassivationStrategySettings.LeastRecentlyUsedSettings) =>
LeastRecentlyUsedPassivationStrategy(settings, limit, idle)
case Some(_: PassivationStrategySettings.MostRecentlyUsedSettings) =>
@ -490,7 +824,78 @@ object ClusterShardingSettings {
LeastFrequentlyUsedPassivationStrategy(settings, limit, idle)
case _ => idle.getOrElse(NoPassivationStrategy)
}
case _ => idle.getOrElse(NoPassivationStrategy)
def describe(strategy: PassivationStrategy): String = {
import akka.util.PrettyDuration._
strategy match {
case NoPassivationStrategy =>
"disabled"
case IdlePassivationStrategy(timeout, interval) =>
s"idle entities after [${timeout.pretty}], checked every [${interval.pretty}]"
case LeastRecentlyUsedPassivationStrategy(limit, segmented, idle) =>
s"least recently used entities" +
(if (limit > 0) s" when over [$limit] entities" else "") +
(if (segmented.nonEmpty) {
val levels = segmented.size
val proportions = segmented.map(proportion => "%.2f".format(proportion)).mkString(", ")
s" (segmented with [$levels] levels with proportions of [$proportions])"
} else "") +
idle.fold("")(idle => " and " + describe(idle))
case MostRecentlyUsedPassivationStrategy(limit, idle) =>
s"most recently used entities" +
(if (limit > 0) s" when over [$limit] entities" else "") +
idle.fold("")(idle => " and " + describe(idle))
case LeastFrequentlyUsedPassivationStrategy(limit, dynamicAging, idle) =>
s"least frequently used entities" +
(if (limit > 0) s" when over [$limit] entities" else "") +
(if (dynamicAging) " (with dynamic aging)" else "") +
idle.fold("")(idle => " and " + describe(idle))
case CompositePassivationStrategy(
limit,
mainStrategy,
windowStrategy,
initialWindowProportion,
minimumWindowProportion,
maximumWindowProportion,
windowOptimizer,
admissionFilter,
idle) =>
val describeWindow = windowStrategy match {
case NoPassivationStrategy => "no admission window"
case _ =>
s"admission window (${describe(windowStrategy)})" +
(windowOptimizer match {
case CompositePassivationStrategy.NoAdmissionOptimizer =>
s" with proportion [$initialWindowProportion]"
case CompositePassivationStrategy.HillClimbingAdmissionOptimizer(
adjustMultiplier,
initialStep,
restartThreshold,
stepDecay) =>
s" with proportions [initial = $initialWindowProportion, min = $minimumWindowProportion, max = $maximumWindowProportion]" +
" adapting with hill-climbing optimizer [" +
s"adjust multiplier = $adjustMultiplier, " +
s"initial step = $initialStep, " +
s"restart threshold = $restartThreshold, " +
s"step decay = $stepDecay]"
})
}
val describeFilter = admissionFilter match {
case CompositePassivationStrategy.AlwaysAdmissionFilter => "always admit"
case CompositePassivationStrategy.FrequencySketchAdmissionFilter(
widthMultiplier,
resetMultiplier,
depth,
counterBits) =>
"admit using frequency sketch [" +
s"width multiplier = $widthMultiplier, " +
s"reset multiplier = $resetMultiplier, " +
s"depth = $depth, " +
s"counter bits = $counterBits]"
}
s"composite strategy with limit of [$limit] active entities, " +
s"$describeWindow, $describeFilter, main (${describe(mainStrategy)})" +
idle.fold("")(idle => " and " + describe(idle))
}
}
}

View file

@ -5,7 +5,6 @@
package akka.cluster.sharding
import java.net.URLEncoder
import scala.annotation.tailrec
import scala.collection.immutable
import scala.concurrent.{ Future, Promise }
@ -23,13 +22,13 @@ import akka.cluster.ClusterSettings
import akka.cluster.ClusterSettings.DataCenter
import akka.cluster.Member
import akka.cluster.MemberStatus
import akka.cluster.sharding.ClusterShardingSettings.PassivationStrategy
import akka.cluster.sharding.Shard.ShardStats
import akka.cluster.sharding.internal.RememberEntitiesProvider
import akka.event.Logging
import akka.pattern.ask
import akka.pattern.pipe
import akka.util.MessageBufferMap
import akka.util.PrettyDuration
import akka.util.Timeout
/**
@ -689,40 +688,11 @@ private[akka] class ShardRegion(
if (settings.rememberEntities) {
log.debug("{}: Entities will not be passivated automatically because 'rememberEntities' is enabled.", typeName)
} else {
logPassivation(settings.passivationStrategy)
}
}
private def logPassivation(strategy: ClusterShardingSettings.PassivationStrategy): Unit =
strategy match {
case ClusterShardingSettings.IdlePassivationStrategy(timeout, interval) =>
log.info(
"{}: Idle entities will be passivated after [{}], checked every [{}]",
"{}: Automatic entity passivation: {}",
typeName,
PrettyDuration.format(timeout),
PrettyDuration.format(interval))
case ClusterShardingSettings.LeastRecentlyUsedPassivationStrategy(limit, segmented, idle) =>
log.info("{}: Least recently used entities will be passivated when over [{}] entities", typeName, limit)
if (segmented.nonEmpty) {
log.info(
"{}: Least recently used strategy is segmented with [{}] levels with proportions of [{}]",
typeName,
segmented.size,
segmented.map(proportion => "%.2f".format(proportion)).mkString(", "))
PassivationStrategy.describe(settings.passivationStrategy))
}
idle.foreach(logPassivation)
case ClusterShardingSettings.MostRecentlyUsedPassivationStrategy(limit, idle) =>
log.info("{}: Most recently used entities will be passivated when over [{}] entities", typeName, limit)
idle.foreach(logPassivation)
case ClusterShardingSettings.LeastFrequentlyUsedPassivationStrategy(limit, dynamicAging, idle) =>
log.info(
"{}: Least frequently used entities will be passivated when over [{}] entities {}",
typeName,
limit,
if (dynamicAging) "with dynamic aging" else "")
idle.foreach(logPassivation)
case _ =>
log.debug("{}: Entities will not be passivated automatically", typeName)
}
// when using proxy the data center can be different from the own data center

View file

@ -7,6 +7,9 @@ package akka.cluster.sharding.internal
import akka.annotation.InternalApi
import akka.cluster.sharding.ClusterShardingSettings
import akka.cluster.sharding.ShardRegion.EntityId
import akka.util.FastFrequencySketch
import akka.util.FrequencySketch
import akka.util.OptionVal
import akka.util.{ FrequencyList, RecencyList, SegmentedRecencyList }
import scala.collection.immutable
@ -37,6 +40,31 @@ private[akka] object EntityPassivationStrategy {
case ClusterShardingSettings.LeastFrequentlyUsedPassivationStrategy(limit, dynamicAging, idle) =>
val idleCheck = idle.map(idle => new IdleCheck(idle.timeout, idle.interval))
new LeastFrequentlyUsedEntityPassivationStrategy(limit, dynamicAging, idleCheck)
case composite: ClusterShardingSettings.CompositePassivationStrategy =>
val main = ActiveEntities(composite.mainStrategy, composite.idle.isDefined)
if (main eq NoActiveEntities) DisabledEntityPassivationStrategy
else {
val initialLimit = composite.limit
val window = ActiveEntities(composite.windowStrategy, composite.idle.isDefined)
val initialWindowProportion = if (window eq NoActiveEntities) 0.0 else composite.initialWindowProportion
val minimumWindowProportion = if (window eq NoActiveEntities) 0.0 else composite.minimumWindowProportion
val maximumWindowProportion = if (window eq NoActiveEntities) 0.0 else composite.maximumWindowProportion
val windowOptimizer =
if (window eq NoActiveEntities) NoAdmissionOptimizer
else AdmissionOptimizer(composite.limit, composite.windowOptimizer)
val admissionFilter = AdmissionFilter(composite.limit, composite.admissionFilter)
val idleCheck = composite.idle.map(idle => new IdleCheck(idle.timeout, idle.interval))
new CompositeEntityPassivationStrategy(
initialLimit,
window,
initialWindowProportion,
minimumWindowProportion,
maximumWindowProportion,
windowOptimizer,
admissionFilter,
main,
idleCheck)
}
case _ => DisabledEntityPassivationStrategy
}
}
@ -165,37 +193,31 @@ private[akka] abstract class LimitBasedEntityPassivationStrategy(initialLimit: I
}
/**
* INTERNAL API: Passivate the least recently used entities when the number of active entities in a shard region
* INTERNAL API
*
* Passivate the least recently used entities when the number of active entities in a shard region
* reaches a limit. The per-region limit is divided evenly among the active shards in a region.
*
* @param initialLimit initial active entity capacity for a shard region
* @param idleCheck optionally passivate idle entities after the given timeout, checking every interval
*/
@InternalApi
private[akka] final class LeastRecentlyUsedEntityPassivationStrategy(initialLimit: Int, idleCheck: Option[IdleCheck])
extends LimitBasedEntityPassivationStrategy(initialLimit) {
import EntityPassivationStrategy.PassivateEntities
private val recencyList = RecencyList.empty[EntityId]
val active = new LeastRecentlyUsedReplacementPolicy(initialLimit)
override val scheduledInterval: Option[FiniteDuration] = idleCheck.map(_.interval)
override protected def passivateEntitiesOnLimitUpdate(): PassivateEntities = active.updateLimit(perShardLimit)
override def entityTouched(id: EntityId): PassivateEntities = {
recencyList.update(id)
passivateExcessEntities()
}
override def entityTouched(id: EntityId): PassivateEntities = active.update(id)
override def entityTerminated(id: EntityId): Unit = recencyList.remove(id)
override def entityTerminated(id: EntityId): Unit = active.remove(id)
override def scheduledInterval: Option[FiniteDuration] = idleCheck.map(_.interval)
override def intervalPassed(): PassivateEntities = idleCheck.fold(PassivateEntities.none) { idle =>
recencyList.removeLeastRecentOutside(idle.timeout)
}
override protected def passivateEntitiesOnLimitUpdate(): PassivateEntities = passivateExcessEntities()
private def passivateExcessEntities(): PassivateEntities = {
val excess = recencyList.size - perShardLimit
if (excess > 0) recencyList.removeLeastRecent(excess) else PassivateEntities.none
active.removeIdle(idle.timeout)
}
}
@ -207,6 +229,7 @@ private[akka] final class LeastRecentlyUsedEntityPassivationStrategy(initialLimi
* Active entities are tracked in multiple recency lists, where entities are promoted to higher-level
* segments on subsequent accesses, and demoted through levels when segments become full.
* The proportions of the segmented levels can be configured as fractions of the overall limit.
*
* @param initialLimit initial active entity capacity for a shard region
* @param proportions proportions of the segmented levels
* @param idleCheck optionally passivate idle entities after the given timeout, checking every interval
@ -220,31 +243,19 @@ private[akka] final class SegmentedLeastRecentlyUsedEntityPassivationStrategy(
import EntityPassivationStrategy.PassivateEntities
private def limits: immutable.Seq[Int] = proportions.map(p => (p * perShardLimit).toInt)
val active = new SegmentedLeastRecentlyUsedReplacementPolicy(initialLimit, proportions, idleCheck.isDefined)
private val segmentedRecencyList =
if (idleCheck.isDefined) SegmentedRecencyList.withOverallRecency.empty[EntityId](limits)
else SegmentedRecencyList.empty[EntityId](limits)
override protected def passivateEntitiesOnLimitUpdate(): PassivateEntities = active.updateLimit(perShardLimit)
override val scheduledInterval: Option[FiniteDuration] = idleCheck.map(_.interval)
override def entityTouched(id: EntityId): PassivateEntities = active.update(id)
override def entityTouched(id: EntityId): PassivateEntities = {
segmentedRecencyList.update(id)
passivateExcessEntities()
}
override def entityTerminated(id: EntityId): Unit = active.remove(id)
override def entityTerminated(id: EntityId): Unit = segmentedRecencyList.remove(id)
override def scheduledInterval: Option[FiniteDuration] = idleCheck.map(_.interval)
override def intervalPassed(): PassivateEntities = idleCheck.fold(PassivateEntities.none) { idle =>
segmentedRecencyList.removeOverallLeastRecentOutside(idle.timeout)
active.removeIdle(idle.timeout)
}
override protected def passivateEntitiesOnLimitUpdate(): PassivateEntities = {
segmentedRecencyList.updateLimits(limits)
passivateExcessEntities()
}
private def passivateExcessEntities(): PassivateEntities = segmentedRecencyList.removeLeastRecentOverLimit()
}
/**
@ -252,6 +263,7 @@ private[akka] final class SegmentedLeastRecentlyUsedEntityPassivationStrategy(
*
* Passivate the most recently used entities when the number of active entities in a shard region
* reaches a limit. The per-region limit is divided evenly among the active shards in a region.
*
* @param initialLimit initial active entity capacity for a shard region
* @param idleCheck optionally passivate idle entities after the given timeout, checking every interval
*/
@ -261,26 +273,18 @@ private[akka] final class MostRecentlyUsedEntityPassivationStrategy(initialLimit
import EntityPassivationStrategy.PassivateEntities
private val recencyList = RecencyList.empty[EntityId]
val active = new MostRecentlyUsedReplacementPolicy(initialLimit)
override val scheduledInterval: Option[FiniteDuration] = idleCheck.map(_.interval)
override protected def passivateEntitiesOnLimitUpdate(): PassivateEntities = active.updateLimit(perShardLimit)
override def entityTouched(id: EntityId): PassivateEntities = {
recencyList.update(id)
passivateExcessEntities(skip = 1) // remove most recent before adding this created entity
}
override def entityTouched(id: EntityId): PassivateEntities = active.update(id)
override def entityTerminated(id: EntityId): Unit = recencyList.remove(id)
override def entityTerminated(id: EntityId): Unit = active.remove(id)
override def scheduledInterval: Option[FiniteDuration] = idleCheck.map(_.interval)
override def intervalPassed(): PassivateEntities = idleCheck.fold(PassivateEntities.none) { idle =>
recencyList.removeLeastRecentOutside(idle.timeout)
}
override protected def passivateEntitiesOnLimitUpdate(): PassivateEntities = passivateExcessEntities()
private def passivateExcessEntities(skip: Int = 0): PassivateEntities = {
val excess = recencyList.size - perShardLimit
if (excess > 0) recencyList.removeMostRecent(excess, skip) else PassivateEntities.none
active.removeIdle(idle.timeout)
}
}
@ -289,6 +293,7 @@ private[akka] final class MostRecentlyUsedEntityPassivationStrategy(initialLimit
*
* Passivate the least frequently used entities when the number of active entities in a shard region
* reaches a limit. The per-region limit is divided evenly among the active shards in a region.
*
* @param initialLimit initial active entity capacity for a shard region
* @param dynamicAging whether to apply "dynamic aging" as entities are passivated
* @param idleCheck optionally passivate idle entities after the given timeout, checking every interval
@ -302,33 +307,670 @@ private[akka] final class LeastFrequentlyUsedEntityPassivationStrategy(
import EntityPassivationStrategy.PassivateEntities
val active = new LeastFrequentlyUsedReplacementPolicy(initialLimit, dynamicAging, idleCheck.isDefined)
override protected def passivateEntitiesOnLimitUpdate(): PassivateEntities = active.updateLimit(perShardLimit)
override def entityTouched(id: EntityId): PassivateEntities = active.update(id)
override def entityTerminated(id: EntityId): Unit = active.remove(id)
override def scheduledInterval: Option[FiniteDuration] = idleCheck.map(_.interval)
override def intervalPassed(): PassivateEntities = idleCheck.fold(PassivateEntities.none) { idle =>
active.removeIdle(idle.timeout)
}
}
/**
* INTERNAL API
*/
@InternalApi
private[akka] object ActiveEntities {
def apply(strategy: ClusterShardingSettings.PassivationStrategy, idleEnabled: Boolean): ActiveEntities =
strategy match {
case ClusterShardingSettings.LeastRecentlyUsedPassivationStrategy(_, segmented, _) =>
if (segmented.isEmpty) new LeastRecentlyUsedReplacementPolicy(initialLimit = 0)
else new SegmentedLeastRecentlyUsedReplacementPolicy(initialLimit = 0, segmented, idleEnabled)
case ClusterShardingSettings.MostRecentlyUsedPassivationStrategy(_, _) =>
new MostRecentlyUsedReplacementPolicy(initialLimit = 0)
case ClusterShardingSettings.LeastFrequentlyUsedPassivationStrategy(_, dynamicAging, _) =>
new LeastFrequentlyUsedReplacementPolicy(initialLimit = 0, dynamicAging, idleEnabled)
case _ => NoActiveEntities
}
}
/**
* INTERNAL API
*
* Active entity tracking for entity passivation strategies, implemented with a replacement policy.
*/
@InternalApi
private[akka] sealed abstract class ActiveEntities {
import EntityPassivationStrategy.PassivateEntities
/**
* The current number of active entities being tracked.
* @return size of active entities
*/
def size: Int
/**
* Check whether the entity id is currently tracked as active.
* @param id the entity id to check
* @return whether the entity is active
*/
def isActive(id: EntityId): Boolean
/**
* The per-shard active entity limit has been updated, which can trigger passivation.
* @param newLimit the new per-shard active entity limit
* @return entities to passivate in the associated shard
*/
def updateLimit(newLimit: Int): PassivateEntities
/**
* An entity instance has been touched. Recorded before message delivery.
* @param id entity id for the touched entity instance
* @return entities to passivate, when active capacity has been reached
*/
def update(id: EntityId): PassivateEntities
/**
* Select the entity that would be passivated by the replacement policy, when active capacity has been reached.
* @return entity that would be passivated
*/
def select: OptionVal[EntityId]
/**
* An entity instance should be removed from active tracking.
* @param id entity id for the removed entity instance
*/
def remove(id: EntityId): Unit
/**
* Remove entity instances that have not been active for the given timeout.
* @param timeout the idle timeout for entities
* @return entities to passivate, if deemed inactive
*/
def removeIdle(timeout: FiniteDuration): PassivateEntities
}
/**
* INTERNAL API
*
* Disabled ActiveEntities (for no window in composite passivation strategies).
*/
@InternalApi
private[akka] object NoActiveEntities extends ActiveEntities {
import EntityPassivationStrategy.PassivateEntities
override def size: Int = 0
override def isActive(id: EntityId): Boolean = false
override def updateLimit(newLimit: Int): PassivateEntities = PassivateEntities.none
override def update(id: EntityId): PassivateEntities = List(id)
override def select: OptionVal[EntityId] = OptionVal.None
override def remove(id: EntityId): Unit = ()
override def removeIdle(timeout: FiniteDuration): PassivateEntities = PassivateEntities.none
}
/**
* INTERNAL API
*
* Passivate the least recently used entities when the number of active entities in a shard region
* reaches a limit. The per-region limit is divided evenly among the active shards in a region.
*
* @param initialLimit initial active entity capacity for a shard
*/
@InternalApi
private[akka] final class LeastRecentlyUsedReplacementPolicy(initialLimit: Int) extends ActiveEntities {
import EntityPassivationStrategy.PassivateEntities
private var limit = initialLimit
private val recencyList = RecencyList.empty[EntityId]
override def size: Int = recencyList.size
override def isActive(id: EntityId): Boolean = recencyList.contains(id)
override def updateLimit(newLimit: Int): PassivateEntities = {
limit = newLimit
removeExcess()
}
override def update(id: EntityId): PassivateEntities = {
recencyList.update(id)
removeExcess()
}
override def select: OptionVal[EntityId] = recencyList.leastRecent
override def remove(id: EntityId): Unit = recencyList.remove(id)
override def removeIdle(timeout: FiniteDuration): PassivateEntities = recencyList.removeLeastRecentOutside(timeout)
private def removeExcess(): PassivateEntities = {
val excess = recencyList.size - limit
if (excess > 0) recencyList.removeLeastRecent(excess) else PassivateEntities.none
}
}
/**
* INTERNAL API
*
* Passivate the least recently used entities when the number of active entities in a shard region
* reaches a limit. The per-region limit is divided evenly among the active shards in a region.
* Active entities are tracked in multiple recency lists, where entities are promoted to higher-level
* segments on subsequent accesses, and demoted through levels when segments become full.
* The proportions of the segmented levels can be configured as fractions of the overall limit.
*
* @param initialLimit initial active entity capacity for a shard
* @param proportions proportions of the segmented levels
* @param idleEnabled whether idle entity passivation is enabled
*/
@InternalApi
private[akka] final class SegmentedLeastRecentlyUsedReplacementPolicy(
initialLimit: Int,
proportions: immutable.Seq[Double],
idleEnabled: Boolean)
extends ActiveEntities {
import EntityPassivationStrategy.PassivateEntities
private var limit = initialLimit
private def segmentLimits: immutable.Seq[Int] = {
// assign the first segment with the leftover, to have an accurate total limit
val higherLimits = proportions.drop(1).map(p => (p * limit).toInt)
(limit - higherLimits.sum) +: higherLimits
}
private val segmentedRecencyList =
if (idleEnabled) SegmentedRecencyList.withOverallRecency.empty[EntityId](segmentLimits)
else SegmentedRecencyList.empty[EntityId](segmentLimits)
override def size: Int = segmentedRecencyList.size
override def isActive(id: EntityId): Boolean = segmentedRecencyList.contains(id)
override def updateLimit(newLimit: Int): PassivateEntities = {
limit = newLimit
segmentedRecencyList.updateLimits(segmentLimits)
removeExcess()
}
override def update(id: EntityId): PassivateEntities = {
segmentedRecencyList.update(id)
removeExcess()
}
override def select: OptionVal[EntityId] = segmentedRecencyList.leastRecent
override def remove(id: EntityId): Unit = segmentedRecencyList.remove(id)
override def removeIdle(timeout: FiniteDuration): PassivateEntities =
segmentedRecencyList.removeOverallLeastRecentOutside(timeout)
private def removeExcess(): PassivateEntities = segmentedRecencyList.removeLeastRecentOverLimit()
}
/**
* INTERNAL API
*
* Passivate the most recently used entities when the number of active entities in a shard region
* reaches a limit. The per-region limit is divided evenly among the active shards in a region.
*
* @param initialLimit initial active entity capacity for a shard
*/
@InternalApi
private[akka] final class MostRecentlyUsedReplacementPolicy(initialLimit: Int) extends ActiveEntities {
import EntityPassivationStrategy.PassivateEntities
private var limit = initialLimit
private val recencyList = RecencyList.empty[EntityId]
override def size: Int = recencyList.size
override def isActive(id: EntityId): Boolean = recencyList.contains(id)
override def updateLimit(newLimit: Int): PassivateEntities = {
limit = newLimit
removeExcess()
}
override def update(id: EntityId): PassivateEntities = {
recencyList.update(id)
removeExcess(skip = 1) // remove the most recent entity before the just touched entity
}
override def select: OptionVal[EntityId] = recencyList.mostRecent
override def remove(id: EntityId): Unit = recencyList.remove(id)
override def removeIdle(timeout: FiniteDuration): PassivateEntities = recencyList.removeLeastRecentOutside(timeout)
private def removeExcess(skip: Int = 0): PassivateEntities = {
val excess = recencyList.size - limit
if (excess > 0) recencyList.removeMostRecent(excess, skip) else PassivateEntities.none
}
}
/**
* INTERNAL API
*
* Passivate the least frequently used entities when the number of active entities in a shard region
* reaches a limit. The per-region limit is divided evenly among the active shards in a region.
*
* @param initialLimit initial active entity capacity for a shard
* @param dynamicAging whether to apply "dynamic aging" as entities are passivated
* @param idleEnabled whether idle entity passivation is enabled
*/
@InternalApi
private[akka] final class LeastFrequentlyUsedReplacementPolicy(
initialLimit: Int,
dynamicAging: Boolean,
idleEnabled: Boolean)
extends ActiveEntities {
import EntityPassivationStrategy.PassivateEntities
private var limit = initialLimit
private val frequencyList =
if (idleCheck.isDefined) FrequencyList.withOverallRecency.empty[EntityId](dynamicAging)
if (idleEnabled) FrequencyList.withOverallRecency.empty[EntityId](dynamicAging)
else FrequencyList.empty[EntityId](dynamicAging)
override val scheduledInterval: Option[FiniteDuration] = idleCheck.map(_.interval)
override def size: Int = frequencyList.size
override def entityTouched(id: EntityId): PassivateEntities = {
override def isActive(id: EntityId): Boolean = frequencyList.contains(id)
override def updateLimit(newLimit: Int): PassivateEntities = {
limit = newLimit
removeExcess()
}
override def update(id: EntityId): PassivateEntities = {
// first remove excess entities so that dynamic aging is updated
// and the adjusted age is applied to any new entities on update
// adjust the expected size by 1 if this is a newly activated entity
val adjustment = if (frequencyList.contains(id)) 0 else 1
val passivated = passivateExcessEntities(adjustment)
val passivated = removeExcess(adjustment)
frequencyList.update(id)
passivated
}
override def entityTerminated(id: EntityId): Unit = frequencyList.remove(id)
override def select: OptionVal[EntityId] = frequencyList.leastFrequent
override def intervalPassed(): PassivateEntities = idleCheck.fold(PassivateEntities.none) { idle =>
frequencyList.removeOverallLeastRecentOutside(idle.timeout)
}
override def remove(id: EntityId): Unit = frequencyList.remove(id)
override protected def passivateEntitiesOnLimitUpdate(): PassivateEntities = passivateExcessEntities()
override def removeIdle(timeout: FiniteDuration): PassivateEntities =
frequencyList.removeOverallLeastRecentOutside(timeout)
private def passivateExcessEntities(adjustment: Int = 0): PassivateEntities = {
val excess = frequencyList.size - perShardLimit + adjustment
private def removeExcess(adjustment: Int = 0): PassivateEntities = {
val excess = frequencyList.size - limit + adjustment
if (excess > 0) frequencyList.removeLeastFrequent(excess) else PassivateEntities.none
}
}
/**
* INTERNAL API
*
* Passivate entities using a "composite" strategy, with an admission area before the main area, an admission
* filter for admitting entities to the main area, and an optimizer for the proportion of the admission window.
*
* References:
*
* "TinyLFU: A Highly Efficient Cache Admission Policy"
* Gil Einziger, Roy Friedman, Ben Manes
*
* "Adaptive Software Cache Management"
* Gil Einziger, Ohad Eytan, Roy Friedman, Ben Manes
*
* @param initialLimit initial active entity capacity for a shard region
* @param window the active entities tracking and replacement policy for the admission window
* @param initialWindowProportion the initial proportion for the admission window
* @param minimumWindowProportion the minimum proportion for the admission window (if being optimized)
* @param maximumWindowProportion the maximum proportion for the admission window (if being optimized)
* @param windowOptimizer the optimizer for the admission window proportion
* @param admissionFilter the admission filter to apply for the main area
* @param main the active entities tracking and replacement policy for the main area
* @param idleCheck optionally passivate idle entities after the given timeout, checking every interval
*/
@InternalApi
private[akka] final class CompositeEntityPassivationStrategy(
initialLimit: Int,
window: ActiveEntities,
initialWindowProportion: Double,
minimumWindowProportion: Double,
maximumWindowProportion: Double,
windowOptimizer: AdmissionOptimizer,
admissionFilter: AdmissionFilter,
main: ActiveEntities,
idleCheck: Option[IdleCheck])
extends LimitBasedEntityPassivationStrategy(initialLimit) {
import EntityPassivationStrategy.PassivateEntities
private var windowProportion = initialWindowProportion
private var windowLimit = 0
private var mainLimit = 0
private def calculateLimits(): Unit = {
windowLimit = (windowProportion * perShardLimit).toInt
mainLimit = perShardLimit - windowLimit
}
// set initial limits based on initial window proportion
calculateLimits()
window.updateLimit(windowLimit)
main.updateLimit(mainLimit)
override def entityTouched(id: EntityId): PassivateEntities = {
admissionFilter.update(id)
val passivated = if (window.isActive(id)) {
windowOptimizer.recordActive()
window.update(id)
} else if (main.isActive(id)) {
windowOptimizer.recordActive()
main.update(id)
} else {
windowOptimizer.recordPassive()
maybeAdmitToMain(window.update(id))
}
adaptWindow()
passivated
}
override def entityTerminated(id: EntityId): Unit = {
window.remove(id)
main.remove(id)
}
override protected def passivateEntitiesOnLimitUpdate(): PassivateEntities = {
calculateLimits()
windowOptimizer.updateLimit(perShardLimit)
admissionFilter.updateCapacity(perShardLimit)
maybeAdmitToMain(window.updateLimit(windowLimit)) ++ main.updateLimit(mainLimit)
}
override def scheduledInterval: Option[FiniteDuration] = idleCheck.map(_.interval)
override def intervalPassed(): PassivateEntities = idleCheck.fold(PassivateEntities.none) { idle =>
window.removeIdle(idle.timeout) ++ main.removeIdle(idle.timeout)
}
private def maybeAdmitToMain(candidates: PassivateEntities): PassivateEntities = {
if (candidates.nonEmpty) {
var passivated: PassivateEntities = PassivateEntities.none
candidates.foreach { candidate =>
if (main.size >= mainLimit) {
main.select match {
case OptionVal.Some(selected) =>
if (admissionFilter.admit(candidate, selected))
passivated ++= main.update(candidate)
else passivated :+= candidate
case _ => passivated ++= main.update(candidate)
}
} else passivated ++= main.update(candidate)
}
passivated
} else PassivateEntities.none
}
private def adaptWindow(): Unit = {
val adjustment = windowOptimizer.calculateAdjustment()
if (adjustment != 0.0) {
windowProportion =
math.max(minimumWindowProportion, math.min(maximumWindowProportion, windowProportion + adjustment))
calculateLimits()
// note: no passivations from adjustments, entities are transferred between window and main
if (adjustment > 0.0) { // increase window limit
window.updateLimit(windowLimit)
main.updateLimit(mainLimit).foreach(window.update)
} else { // decrease window limit
main.updateLimit(mainLimit)
window.updateLimit(windowLimit).foreach(main.update)
}
}
}
}
/**
* INTERNAL API
*/
@InternalApi
private[akka] object AdmissionOptimizer {
def apply(
initialLimit: Int,
optimizer: ClusterShardingSettings.CompositePassivationStrategy.AdmissionOptimizer): AdmissionOptimizer =
optimizer match {
case ClusterShardingSettings.CompositePassivationStrategy.HillClimbingAdmissionOptimizer(
adjustMultiplier,
initialStep,
restartThreshold,
stepDecay) =>
new HillClimbingAdmissionOptimizer(initialLimit, adjustMultiplier, initialStep, restartThreshold, stepDecay)
case _ => NoAdmissionOptimizer
}
}
/**
* INTERNAL API
*
* An optimizer for the size of the admission window for a composite passivation strategy.
*/
@InternalApi
private[akka] abstract class AdmissionOptimizer {
/**
* An entity was accessed that is already active.
*/
def recordActive(): Unit
/**
* An entity was accessed that was passive (needed to be activated).
*/
def recordPassive(): Unit
/**
* The per-shard limit has been updated.
* @param newLimit the new per-shard limit
*/
def updateLimit(newLimit: Int): Unit
/**
* Calculate an adjustment to the proportion of the admission window.
* Can be positive (to grow the window) or negative (to shrink the window).
* Returns 0.0 if no adjustment should be made.
* @return the adjustment to make to the admission window proportion
*/
def calculateAdjustment(): Double
}
/**
* INTERNAL API
*
* Disabled admission window proportion optimizer.
*/
@InternalApi
private[akka] object NoAdmissionOptimizer extends AdmissionOptimizer {
override def recordActive(): Unit = ()
override def recordPassive(): Unit = ()
override def updateLimit(newLimit: Int): Unit = ()
override def calculateAdjustment(): Double = 0.0
}
/**
* INTERNAL API
*
* Optimizer for the admission window using a simple hill-climbing algorithm.
*/
@InternalApi
private[akka] final class HillClimbingAdmissionOptimizer(
initialLimit: Int,
adjustMultiplier: Double,
initialStep: Double,
restartThreshold: Double,
stepDecay: Double)
extends AdmissionOptimizer {
private var adjustSize = adjustMultiplier * initialLimit
private var accesses = 0
private var activeAccesses = 0
private var previousActiveRate = 0.0
private var nextStep = -initialStep // start in decreasing direction
override def recordActive(): Unit = {
accesses += 1
activeAccesses += 1
}
override def recordPassive(): Unit = accesses += 1
override def updateLimit(newLimit: Int): Unit =
adjustSize = adjustMultiplier * newLimit
override def calculateAdjustment(): Double = {
if (accesses >= adjustSize) {
val activeRate = activeAccesses.toDouble / accesses
val delta = activeRate - previousActiveRate
val adjustment = if (delta >= 0) nextStep else -nextStep
val direction = if (adjustment >= 0) 1 else -1
val restart = math.abs(delta) >= restartThreshold
nextStep = if (restart) initialStep * direction else adjustment * stepDecay
previousActiveRate = activeRate
accesses = 0
activeAccesses = 0
adjustment
} else 0.0
}
}
/**
* INTERNAL API
*/
@InternalApi
private[akka] object AdmissionFilter {
def apply(
initialCapacity: Int,
filter: ClusterShardingSettings.CompositePassivationStrategy.AdmissionFilter): AdmissionFilter = filter match {
case ClusterShardingSettings.CompositePassivationStrategy
.FrequencySketchAdmissionFilter(widthMultiplier, resetMultiplier, depth, counterBits) =>
FrequencySketchAdmissionFilter(initialCapacity, widthMultiplier, resetMultiplier, depth, counterBits)
case _ => AlwaysAdmissionFilter
}
}
/**
* INTERNAL API
*
* An admission filter for the main area for a composite passivation strategy.
*/
@InternalApi
private[akka] abstract class AdmissionFilter {
/**
* Update the capacity, the per-shard entity limit.
* @param newCapacity the new capacity for the filter
*/
def updateCapacity(newCapacity: Int): Unit
/**
* Update the filter when an entity is accessed
* @param id the entity id that has been accessed
*/
def update(id: EntityId): Unit
/**
* Determine whether an entity should be admitted to the main area.
* The candidate has been removed from the admission window (according to its replacement policy)
* and can replace an entity in the main area (selected by its replacement policy).
* Whichever entity is not admitted or retained will be passivated.
* @param candidate the candidate from the window that may be admitted to the main area
* @param selected the entity selected from the main area to possibly be replaced by the candidate
* @return whether to admit the candidate to the main area
*/
def admit(candidate: EntityId, selected: EntityId): Boolean
}
/**
* INTERNAL API
*
* Disabled admission filter, always admit candidates to the main area.
*/
@InternalApi
private[akka] object AlwaysAdmissionFilter extends AdmissionFilter {
override def updateCapacity(newCapacity: Int): Unit = ()
override def update(id: EntityId): Unit = ()
override def admit(candidate: EntityId, selected: EntityId): Boolean = true
}
/**
* INTERNAL API
*
* Admission filter based on a frequency sketch.
*/
@InternalApi
private[akka] object FrequencySketchAdmissionFilter {
def apply(
initialCapacity: Int,
widthMultiplier: Int,
resetMultiplier: Double,
depth: Int,
counterBits: Int): AdmissionFilter = {
if (depth == 4 && counterBits == 4)
new FastFrequencySketchAdmissionFilter(initialCapacity, widthMultiplier, resetMultiplier)
else
new FrequencySketchAdmissionFilter(initialCapacity, widthMultiplier, resetMultiplier, depth, counterBits)
}
}
/**
* INTERNAL API
*
* Admission filter based on a frequency sketch.
*/
@InternalApi
private[akka] final class FrequencySketchAdmissionFilter(
initialCapacity: Int,
widthMultiplier: Int,
resetMultiplier: Double,
depth: Int,
counterBits: Int)
extends AdmissionFilter {
private def createSketch(capacity: Int): FrequencySketch[EntityId] =
FrequencySketch[EntityId](capacity, widthMultiplier, resetMultiplier, depth, counterBits)
private var frequencySketch = createSketch(initialCapacity)
override def updateCapacity(newCapacity: Int): Unit = frequencySketch = createSketch(newCapacity)
override def update(id: EntityId): Unit = frequencySketch.increment(id)
override def admit(candidate: EntityId, selected: EntityId): Boolean =
frequencySketch.frequency(candidate) > frequencySketch.frequency(selected)
}
/**
* INTERNAL API
*
* Admission filter based on a frequency sketch (fast version with depth of 4 and 4-bit counters).
*/
@InternalApi
private[akka] final class FastFrequencySketchAdmissionFilter(
initialCapacity: Int,
widthMultiplier: Int,
resetMultiplier: Double)
extends AdmissionFilter {
private def createSketch(capacity: Int): FastFrequencySketch[EntityId] =
FastFrequencySketch[EntityId](capacity, widthMultiplier, resetMultiplier)
private var frequencySketch = createSketch(initialCapacity)
override def updateCapacity(newCapacity: Int): Unit = frequencySketch = createSketch(newCapacity)
override def update(id: EntityId): Unit = frequencySketch.increment(id)
override def admit(candidate: EntityId, selected: EntityId): Boolean =
frequencySketch.frequency(candidate) > frequencySketch.frequency(selected)
}

View file

@ -0,0 +1,243 @@
#
# Run a joined trace for testing adaptivity, switching between a recency-biased R3 Corda trace and
# a frequency-biased LIRS loop trace.
#
# Similar to the adaptivity test for Caffeine: https://github.com/ben-manes/caffeine/wiki/Efficiency
#
# Download LIRS traces from: https://github.com/zhongch4g/LIRS2
#
# Download Corda traces from: https://github.com/ben-manes/caffeine
#
# > akka-cluster-sharding/Test/runMain akka.cluster.sharding.passivation.simulator.Simulator adaptivity-trace
#
# ╔════════════════════╤═════════╤═══════════╤═════════════╤══════════════╗
# ║ Run │ Active │ Accesses │ Activations │ Passivations ║
# ╠════════════════════╪═════════╪═══════════╪═════════════╪══════════════╣
# ║ OPT 500 │ 41.89 % │ 8,565,548 │ 4,977,489 │ 4,976,989 ║
# ╟────────────────────┼─────────┼───────────┼─────────────┼──────────────╢
# ║ LRU 500 │ 15.63 % │ 8,565,548 │ 7,226,870 │ 7,226,370 ║
# ╟────────────────────┼─────────┼───────────┼─────────────┼──────────────╢
# ║ SLRU 500 │ 7.31 % │ 8,565,548 │ 7,939,452 │ 7,938,952 ║
# ╟────────────────────┼─────────┼───────────┼─────────────┼──────────────╢
# ║ S4LRU 500 │ 15.63 % │ 8,565,548 │ 7,226,902 │ 7,226,402 ║
# ╟────────────────────┼─────────┼───────────┼─────────────┼──────────────╢
# ║ MRU 500 │ 0.03 % │ 8,565,548 │ 8,563,041 │ 8,562,541 ║
# ╟────────────────────┼─────────┼───────────┼─────────────┼──────────────╢
# ║ LFU 500 │ 0.02 % │ 8,565,548 │ 8,564,051 │ 8,563,551 ║
# ╟────────────────────┼─────────┼───────────┼─────────────┼──────────────╢
# ║ LFUDA 500 │ 15.63 % │ 8,565,548 │ 7,226,892 │ 7,226,392 ║
# ╟────────────────────┼─────────┼───────────┼─────────────┼──────────────╢
# ║ LRU/FS/SLRU 500 │ 26.14 % │ 8,565,548 │ 6,326,555 │ 6,326,055 ║
# ╟────────────────────┼─────────┼───────────┼─────────────┼──────────────╢
# ║ LRU/FS/SLRU/HC 500 │ 40.74 % │ 8,565,548 │ 5,076,280 │ 5,075,780 ║
# ╚════════════════════╧═════════╧═══════════╧═════════════╧══════════════╝
#
corda-traces="corda-traces"
corda-traces=${?CORDA_TRACES}
lirs-traces="lirs-traces"
lirs-traces=${?LIRS_TRACES}
akka.cluster.sharding {
passivation.simulator {
runs = [
{
name = "OPT 500"
shards = 1
regions = 1
pattern = changing
strategy = optimal-500
},
{
name = "LRU 500"
shards = 1
regions = 1
pattern = changing
strategy = lru-500
},
{
name = "SLRU 500"
shards = 1
regions = 1
pattern = changing
strategy = slru-500
},
{
name = "S4LRU 500"
shards = 1
regions = 1
pattern = changing
strategy = s4lru-500
},
{
name = "MRU 500"
shards = 1
regions = 1
pattern = changing
strategy = mru-500
},
{
name = "LFU 500"
shards = 1
regions = 1
pattern = changing
strategy = lfu-500
},
{
name = "LFUDA 500"
shards = 1
regions = 1
pattern = changing
strategy = lfuda-500
},
{
name = "LRU/FS/SLRU 500"
shards = 1
regions = 1
pattern = changing
strategy = lru-fs-slru-500
},
{
name = "LRU/FS/SLRU/HC 500"
shards = 1
regions = 1
pattern = changing
strategy = lru-fs-slru-hc-500
},
]
changing {
pattern = joined
joined = [
corda-vaultservice,
lirs-loop,
lirs-loop,
lirs-loop,
corda-vaultservice-large,
lirs-loop,
lirs-loop,
lirs-loop,
corda-vaultservice,
lirs-loop,
lirs-loop,
lirs-loop,
corda-vaultservice-large,
]
}
# recency-biased
corda-vaultservice {
pattern = trace
trace {
format = corda
path = ${corda-traces}"/vaultservice.trace"
}
}
# recency-biased
corda-vaultservice-large {
pattern = trace
trace {
format = corda
path = ${corda-traces}"/vaultservice-large.trace"
}
}
# frequency-biased
lirs-loop {
pattern = trace
trace {
format = lirs
path = ${lirs-traces}"/loop.trace"
}
}
optimal-500 {
strategy = optimal
optimal {
per-region-limit = 500
}
}
lru-500 {
strategy = least-recently-used
least-recently-used {
per-region-limit = 500
}
}
slru-500 {
strategy = least-recently-used
least-recently-used {
per-region-limit = 500
segmented {
levels = 2
proportions = [0.2, 0.8]
}
}
}
s4lru-500 {
strategy = least-recently-used
least-recently-used {
per-region-limit = 500
segmented.levels = 4
}
}
mru-500 {
strategy = most-recently-used
most-recently-used {
per-region-limit = 500
}
}
lfu-500 {
strategy = least-frequently-used
least-frequently-used {
per-region-limit = 500
}
}
lfuda-500 {
strategy = least-frequently-used
least-frequently-used {
per-region-limit = 500
dynamic-aging = on
}
}
lru-fs-slru {
strategy = composite
composite {
admission {
window.strategy = least-recently-used
filter = frequency-sketch
optimizer = none
}
main {
strategy = least-recently-used
least-recently-used {
segmented {
levels = 2
proportions = [0.2, 0.8]
}
}
}
}
}
lru-fs-slru-500 = ${akka.cluster.sharding.passivation.simulator.lru-fs-slru} {
composite.per-region-limit = 500
}
lru-fs-slru-hc = ${akka.cluster.sharding.passivation.simulator.lru-fs-slru} {
composite.admission.optimizer = hill-climbing
}
lru-fs-slru-hc-500 = ${akka.cluster.sharding.passivation.simulator.lru-fs-slru-hc} {
composite.per-region-limit = 500
}
}
}

View file

@ -8,65 +8,81 @@
#
# > akka-cluster-sharding/Test/runMain akka.cluster.sharding.passivation.simulator.Simulator arc-trace-database
#
# ╔══════════╤═════════╤════════════╤═════════════╤══════════════╗
# ╔═══════════════════╤═════════╤════════════╤═════════════╤══════════════╗
# ║ Run │ Active │ Accesses │ Activations │ Passivations ║
# ╠══════════╪═════════╪════════════╪═════════════╪══════════════╣
# ╠═══════════════════╪═════════╪════════════╪═════════════╪══════════════╣
# ║ OPT 1M │ 20.19 % │ 43,704,979 │ 34,879,633 │ 33,879,633 ║
# ╟──────────┼─────────┼────────────┼─────────────┼──────────────╢
# ╟───────────────────┼─────────┼────────────┼─────────────┼──────────────╢
# ║ OPT 2M │ 31.79 % │ 43,704,979 │ 29,810,905 │ 27,810,905 ║
# ╟──────────┼─────────┼────────────┼─────────────┼──────────────╢
# ╟───────────────────┼─────────┼────────────┼─────────────┼──────────────╢
# ║ OPT 4M │ 48.09 % │ 43,704,979 │ 22,685,636 │ 18,685,636 ║
# ╟──────────┼─────────┼────────────┼─────────────┼──────────────╢
# ╟───────────────────┼─────────┼────────────┼─────────────┼──────────────╢
# ║ OPT 8M │ 74.93 % │ 43,704,979 │ 10,957,661 │ 2,957,661 ║
# ╠══════════╪═════════╪════════════╪═════════════╪══════════════╣
# ╠═══════════════════╪═════════╪════════════╪═════════════╪══════════════╣
# ║ LRU 1M │ 3.09 % │ 43,704,979 │ 42,356,500 │ 41,356,500 ║
# ╟──────────┼─────────┼────────────┼─────────────┼──────────────╢
# ╟───────────────────┼─────────┼────────────┼─────────────┼──────────────╢
# ║ LRU 2M │ 10.75 % │ 43,704,979 │ 39,007,141 │ 37,007,141 ║
# ╟──────────┼─────────┼────────────┼─────────────┼──────────────╢
# ╟───────────────────┼─────────┼────────────┼─────────────┼──────────────╢
# ║ LRU 4M │ 20.24 % │ 43,704,979 │ 34,857,131 │ 30,857,131 ║
# ╟──────────┼─────────┼────────────┼─────────────┼──────────────╢
# ╟───────────────────┼─────────┼────────────┼─────────────┼──────────────╢
# ║ LRU 8M │ 43.03 % │ 43,704,979 │ 24,896,638 │ 16,896,638 ║
# ╠══════════╪═════════╪════════════╪═════════════╪══════════════╣
# ╠═══════════════════╪═════════╪════════════╪═════════════╪══════════════╣
# ║ SLRU 1M │ 6.13 % │ 43,704,979 │ 41,026,869 │ 40,026,869 ║
# ╟──────────┼─────────┼────────────┼─────────────┼──────────────╢
# ╟───────────────────┼─────────┼────────────┼─────────────┼──────────────╢
# ║ SLRU 2M │ 23.37 % │ 43,704,979 │ 33,489,560 │ 31,489,560 ║
# ╟──────────┼─────────┼────────────┼─────────────┼──────────────╢
# ╟───────────────────┼─────────┼────────────┼─────────────┼──────────────╢
# ║ SLRU 4M │ 39.06 % │ 43,704,979 │ 26,632,624 │ 22,632,624 ║
# ╟──────────┼─────────┼────────────┼─────────────┼──────────────╢
# ╟───────────────────┼─────────┼────────────┼─────────────┼──────────────╢
# ║ SLRU 8M │ 57.55 % │ 43,704,979 │ 18,552,089 │ 10,552,089 ║
# ╠══════════╪═════════╪════════════╪═════════════╪══════════════╣
# ╠═══════════════════╪═════════╪════════════╪═════════════╪══════════════╣
# ║ S4LRU 1M │ 6.13 % │ 43,704,979 │ 41,026,869 │ 40,026,869 ║
# ╟──────────┼─────────┼────────────┼─────────────┼──────────────╢
# ╟───────────────────┼─────────┼────────────┼─────────────┼──────────────╢
# ║ S4LRU 2M │ 16.74 % │ 43,704,979 │ 36,387,353 │ 34,387,353 ║
# ╟──────────┼─────────┼────────────┼─────────────┼──────────────╢
# ╟───────────────────┼─────────┼────────────┼─────────────┼──────────────╢
# ║ S4LRU 4M │ 30.27 % │ 43,704,979 │ 30,477,096 │ 26,477,096 ║
# ╟──────────┼─────────┼────────────┼─────────────┼──────────────╢
# ╟───────────────────┼─────────┼────────────┼─────────────┼──────────────╢
# ║ S4LRU 8M │ 57.86 % │ 43,704,979 │ 18,419,181 │ 10,419,181 ║
# ╠══════════╪═════════╪════════════╪═════════════╪══════════════╣
# ╠═══════════════════╪═════════╪════════════╪═════════════╪══════════════╣
# ║ MRU 1M │ 11.92 % │ 43,704,979 │ 38,493,369 │ 37,493,369 ║
# ╟──────────┼─────────┼────────────┼─────────────┼──────────────╢
# ╟───────────────────┼─────────┼────────────┼─────────────┼──────────────╢
# ║ MRU 2M │ 25.67 % │ 43,704,979 │ 32,486,285 │ 30,486,285 ║
# ╟──────────┼─────────┼────────────┼─────────────┼──────────────╢
# ╟───────────────────┼─────────┼────────────┼─────────────┼──────────────╢
# ║ MRU 4M │ 41.82 % │ 43,704,979 │ 25,429,608 │ 21,429,608 ║
# ╟──────────┼─────────┼────────────┼─────────────┼──────────────╢
# ╟───────────────────┼─────────┼────────────┼─────────────┼──────────────╢
# ║ MRU 8M │ 68.01 % │ 43,704,979 │ 13,980,246 │ 5,980,246 ║
# ╠══════════╪═════════╪════════════╪═════════════╪══════════════╣
# ╠═══════════════════╪═════════╪════════════╪═════════════╪══════════════╣
# ║ LFU 1M │ 6.13 % │ 43,704,979 │ 41,026,869 │ 40,026,869 ║
# ╟──────────┼─────────┼────────────┼─────────────┼──────────────╢
# ╟───────────────────┼─────────┼────────────┼─────────────┼──────────────╢
# ║ LFU 2M │ 23.39 % │ 43,704,979 │ 33,482,361 │ 31,482,361 ║
# ╟──────────┼─────────┼────────────┼─────────────┼──────────────╢
# ╟───────────────────┼─────────┼────────────┼─────────────┼──────────────╢
# ║ LFU 4M │ 39.06 % │ 43,704,979 │ 26,632,624 │ 22,632,624 ║
# ╟──────────┼─────────┼────────────┼─────────────┼──────────────╢
# ╟───────────────────┼─────────┼────────────┼─────────────┼──────────────╢
# ║ LFU 8M │ 57.55 % │ 43,704,979 │ 18,552,089 │ 10,552,089 ║
# ╠══════════╪═════════╪════════════╪═════════════╪══════════════╣
# ╠═══════════════════╪═════════╪════════════╪═════════════╪══════════════╣
# ║ LFUDA 1M │ 3.25 % │ 43,704,979 │ 42,285,825 │ 41,285,825 ║
# ╟──────────┼─────────┼────────────┼─────────────┼──────────────╢
# ╟───────────────────┼─────────┼────────────┼─────────────┼──────────────╢
# ║ LFUDA 2M │ 11.02 % │ 43,704,979 │ 38,890,280 │ 36,890,280 ║
# ╟──────────┼─────────┼────────────┼─────────────┼──────────────╢
# ╟───────────────────┼─────────┼────────────┼─────────────┼──────────────╢
# ║ LFUDA 4M │ 21.96 % │ 43,704,979 │ 34,107,344 │ 30,107,344 ║
# ╟──────────┼─────────┼────────────┼─────────────┼──────────────╢
# ╟───────────────────┼─────────┼────────────┼─────────────┼──────────────╢
# ║ LFUDA 8M │ 54.72 % │ 43,704,979 │ 19,790,371 │ 11,790,371 ║
# ╚══════════╧═════════╧════════════╧═════════════╧══════════════╝
# ╠═══════════════════╪═════════╪════════════╪═════════════╪══════════════╣
# ║ LRU/FS/SLRU 1M │ 14.36 % │ 43,704,979 │ 37,428,825 │ 36,428,825 ║
# ╟───────────────────┼─────────┼────────────┼─────────────┼──────────────╢
# ║ LRU/FS/SLRU 2M │ 28.61 % │ 43,704,979 │ 31,200,295 │ 29,200,295 ║
# ╟───────────────────┼─────────┼────────────┼─────────────┼──────────────╢
# ║ LRU/FS/SLRU 4M │ 45.36 % │ 43,704,979 │ 23,882,207 │ 19,882,207 ║
# ╟───────────────────┼─────────┼────────────┼─────────────┼──────────────╢
# ║ LRU/FS/SLRU 8M │ 70.73 % │ 43,704,979 │ 12,791,039 │ 4,791,039 ║
# ╠═══════════════════╪═════════╪════════════╪═════════════╪══════════════╣
# ║ LRU/FS/SLRU/HC 1M │ 14.28 % │ 43,704,979 │ 37,463,497 │ 36,463,497 ║
# ╟───────────────────┼─────────┼────────────┼─────────────┼──────────────╢
# ║ LRU/FS/SLRU/HC 2M │ 28.61 % │ 43,704,979 │ 31,200,295 │ 29,200,295 ║
# ╟───────────────────┼─────────┼────────────┼─────────────┼──────────────╢
# ║ LRU/FS/SLRU/HC 4M │ 45.36 % │ 43,704,979 │ 23,882,207 │ 19,882,207 ║
# ╟───────────────────┼─────────┼────────────┼─────────────┼──────────────╢
# ║ LRU/FS/SLRU/HC 8M │ 70.73 % │ 43,704,979 │ 12,791,039 │ 4,791,039 ║
# ╚═══════════════════╧═════════╧════════════╧═════════════╧══════════════╝
#
arc-traces="arc-traces"
@ -271,6 +287,62 @@ akka.cluster.sharding {
pattern = arc-database
strategy = lfuda-800k
},
{
name = "LRU/FS/SLRU 1M"
shards = 100
regions = 10
pattern = arc-database
strategy = lru-fs-slru-100k
},
{
name = "LRU/FS/SLRU 2M"
shards = 100
regions = 10
pattern = arc-database
strategy = lru-fs-slru-200k
},
{
name = "LRU/FS/SLRU 4M"
shards = 100
regions = 10
pattern = arc-database
strategy = lru-fs-slru-400k
},
{
name = "LRU/FS/SLRU 8M"
shards = 100
regions = 10
pattern = arc-database
strategy = lru-fs-slru-800k
},
{
name = "LRU/FS/SLRU/HC 1M"
shards = 100
regions = 10
pattern = arc-database
strategy = lru-fs-slru-hc-100k
},
{
name = "LRU/FS/SLRU/HC 2M"
shards = 100
regions = 10
pattern = arc-database
strategy = lru-fs-slru-hc-200k
},
{
name = "LRU/FS/SLRU/HC 4M"
shards = 100
regions = 10
pattern = arc-database
strategy = lru-fs-slru-hc-400k
},
{
name = "LRU/FS/SLRU/HC 8M"
shards = 100
regions = 10
pattern = arc-database
strategy = lru-fs-slru-hc-800k
},
]
print-detailed-stats = true
@ -502,5 +574,62 @@ akka.cluster.sharding {
dynamic-aging = on
}
}
lru-fs-slru {
strategy = composite
composite {
admission {
window.strategy = least-recently-used
filter = frequency-sketch
optimizer = none
}
main {
strategy = least-recently-used
least-recently-used {
segmented {
levels = 2
proportions = [0.2, 0.8]
}
}
}
}
}
lru-fs-slru-100k = ${akka.cluster.sharding.passivation.simulator.lru-fs-slru} {
composite.per-region-limit = 100000
}
lru-fs-slru-200k = ${akka.cluster.sharding.passivation.simulator.lru-fs-slru} {
composite.per-region-limit = 200000
}
lru-fs-slru-400k = ${akka.cluster.sharding.passivation.simulator.lru-fs-slru} {
composite.per-region-limit = 400000
}
lru-fs-slru-800k = ${akka.cluster.sharding.passivation.simulator.lru-fs-slru} {
composite.per-region-limit = 800000
}
lru-fs-slru-hc = ${akka.cluster.sharding.passivation.simulator.lru-fs-slru} {
composite.admission.optimizer = hill-climbing
}
lru-fs-slru-hc-100k = ${akka.cluster.sharding.passivation.simulator.lru-fs-slru-hc} {
composite.per-region-limit = 100000
}
lru-fs-slru-hc-200k = ${akka.cluster.sharding.passivation.simulator.lru-fs-slru-hc} {
composite.per-region-limit = 200000
}
lru-fs-slru-hc-400k = ${akka.cluster.sharding.passivation.simulator.lru-fs-slru-hc} {
composite.per-region-limit = 400000
}
lru-fs-slru-hc-800k = ${akka.cluster.sharding.passivation.simulator.lru-fs-slru-hc} {
composite.per-region-limit = 800000
}
}
}

View file

@ -8,51 +8,63 @@
#
# > akka-cluster-sharding/Test/runMain akka.cluster.sharding.passivation.simulator.Simulator arc-trace-search
#
# ╔════════════╤═════════╤════════════╤═════════════╤══════════════╗
# ╔═════════════════════╤═════════╤════════════╤═════════════╤══════════════╗
# ║ Run │ Active │ Accesses │ Activations │ Passivations ║
# ╠════════════╪═════════╪════════════╪═════════════╪══════════════╣
# ╠═════════════════════╪═════════╪════════════╪═════════════╪══════════════╣
# ║ OPT 250k │ 30.13 % │ 37,656,092 │ 26,309,294 │ 26,059,294 ║
# ╟────────────┼─────────┼────────────┼─────────────┼──────────────╢
# ╟─────────────────────┼─────────┼────────────┼─────────────┼──────────────╢
# ║ OPT 500k │ 45.95 % │ 37,656,092 │ 20,354,161 │ 19,854,161 ║
# ╟────────────┼─────────┼────────────┼─────────────┼──────────────╢
# ╟─────────────────────┼─────────┼────────────┼─────────────┼──────────────╢
# ║ OPT 1M │ 65.55 % │ 37,656,092 │ 12,970,868 │ 11,970,868 ║
# ╠════════════╪═════════╪════════════╪═════════════╪══════════════╣
# ╠═════════════════════╪═════════╪════════════╪═════════════╪══════════════╣
# ║ LRU 250k │ 3.07 % │ 37,656,092 │ 36,498,516 │ 36,248,516 ║
# ╟────────────┼─────────┼────────────┼─────────────┼──────────────╢
# ╟─────────────────────┼─────────┼────────────┼─────────────┼──────────────╢
# ║ LRU 500k │ 7.53 % │ 37,656,092 │ 34,822,122 │ 34,322,122 ║
# ╟────────────┼─────────┼────────────┼─────────────┼──────────────╢
# ╟─────────────────────┼─────────┼────────────┼─────────────┼──────────────╢
# ║ LRU 1M │ 25.37 % │ 37,656,092 │ 28,102,287 │ 27,102,287 ║
# ╠════════════╪═════════╪════════════╪═════════════╪══════════════╣
# ╠═════════════════════╪═════════╪════════════╪═════════════╪══════════════╣
# ║ SLRU 250k │ 9.59 % │ 37,656,092 │ 34,044,825 │ 33,794,825 ║
# ╟────────────┼─────────┼────────────┼─────────────┼──────────────╢
# ╟─────────────────────┼─────────┼────────────┼─────────────┼──────────────╢
# ║ SLRU 500k │ 16.76 % │ 37,656,092 │ 31,345,965 │ 30,845,965 ║
# ╟────────────┼─────────┼────────────┼─────────────┼──────────────╢
# ╟─────────────────────┼─────────┼────────────┼─────────────┼──────────────╢
# ║ SLRU 1M │ 27.49 % │ 37,656,092 │ 27,305,632 │ 26,305,632 ║
# ╠════════════╪═════════╪════════════╪═════════════╪══════════════╣
# ╠═════════════════════╪═════════╪════════════╪═════════════╪══════════════╣
# ║ S4LRU 250k │ 9.78 % │ 37,656,092 │ 33,973,161 │ 33,723,161 ║
# ╟────────────┼─────────┼────────────┼─────────────┼──────────────╢
# ╟─────────────────────┼─────────┼────────────┼─────────────┼──────────────╢
# ║ S4LRU 500k │ 17.04 % │ 37,656,092 │ 31,241,138 │ 30,741,138 ║
# ╟────────────┼─────────┼────────────┼─────────────┼──────────────╢
# ╟─────────────────────┼─────────┼────────────┼─────────────┼──────────────╢
# ║ S4LRU 1M │ 27.69 % │ 37,656,092 │ 27,229,802 │ 26,229,802 ║
# ╠════════════╪═════════╪════════════╪═════════════╪══════════════╣
# ╠═════════════════════╪═════════╪════════════╪═════════════╪══════════════╣
# ║ MRU 250k │ 5.25 % │ 37,656,092 │ 35,680,111 │ 35,430,111 ║
# ╟────────────┼─────────┼────────────┼─────────────┼──────────────╢
# ╟─────────────────────┼─────────┼────────────┼─────────────┼──────────────╢
# ║ MRU 500k │ 10.50 % │ 37,656,092 │ 33,702,975 │ 33,202,975 ║
# ╟────────────┼─────────┼────────────┼─────────────┼──────────────╢
# ╟─────────────────────┼─────────┼────────────┼─────────────┼──────────────╢
# ║ MRU 1M │ 20.79 % │ 37,656,092 │ 29,826,997 │ 28,826,997 ║
# ╠════════════╪═════════╪════════════╪═════════════╪══════════════╣
# ╠═════════════════════╪═════════╪════════════╪═════════════╪══════════════╣
# ║ LFU 250k │ 9.04 % │ 37,656,092 │ 34,253,102 │ 34,003,102 ║
# ╟────────────┼─────────┼────────────┼─────────────┼──────────────╢
# ╟─────────────────────┼─────────┼────────────┼─────────────┼──────────────╢
# ║ LFU 500k │ 16.42 % │ 37,656,092 │ 31,471,765 │ 30,971,765 ║
# ╟────────────┼─────────┼────────────┼─────────────┼──────────────╢
# ╟─────────────────────┼─────────┼────────────┼─────────────┼──────────────╢
# ║ LFU 1M │ 27.43 % │ 37,656,092 │ 27,328,351 │ 26,328,351 ║
# ╠════════════╪═════════╪════════════╪═════════════╪══════════════╣
# ╠═════════════════════╪═════════╪════════════╪═════════════╪══════════════╣
# ║ LFUDA 250k │ 3.18 % │ 37,656,092 │ 36,457,345 │ 36,207,345 ║
# ╟────────────┼─────────┼────────────┼─────────────┼──────────────╢
# ╟─────────────────────┼─────────┼────────────┼─────────────┼──────────────╢
# ║ LFUDA 500k │ 8.95 % │ 37,656,092 │ 34,285,981 │ 33,785,981 ║
# ╟────────────┼─────────┼────────────┼─────────────┼──────────────╢
# ╟─────────────────────┼─────────┼────────────┼─────────────┼──────────────╢
# ║ LFUDA 1M │ 28.10 % │ 37,656,092 │ 27,073,194 │ 26,073,194 ║
# ╚════════════╧═════════╧════════════╧═════════════╧══════════════╝
# ╠═════════════════════╪═════════╪════════════╪═════════════╪══════════════╣
# ║ LRU/FS/SLRU 250k │ 13.05 % │ 37,656,092 │ 32,741,656 │ 32,491,656 ║
# ╟─────────────────────┼─────────┼────────────┼─────────────┼──────────────╢
# ║ LRU/FS/SLRU 500k │ 24.89 % │ 37,656,092 │ 28,281,835 │ 27,781,835 ║
# ╟─────────────────────┼─────────┼────────────┼─────────────┼──────────────╢
# ║ LRU/FS/SLRU 1M │ 44.63 % │ 37,656,092 │ 20,850,711 │ 19,850,711 ║
# ╠═════════════════════╪═════════╪════════════╪═════════════╪══════════════╣
# ║ LRU/FS/SLRU/HC 250k │ 12.48 % │ 37,656,092 │ 32,955,813 │ 32,705,813 ║
# ╟─────────────────────┼─────────┼────────────┼─────────────┼──────────────╢
# ║ LRU/FS/SLRU/HC 500k │ 24.76 % │ 37,656,092 │ 28,332,311 │ 27,832,311 ║
# ╟─────────────────────┼─────────┼────────────┼─────────────┼──────────────╢
# ║ LRU/FS/SLRU/HC 1M │ 44.63 % │ 37,656,092 │ 20,850,711 │ 19,850,711 ║
# ╚═════════════════════╧═════════╧════════════╧═════════════╧══════════════╝
#
arc-traces="arc-traces"
@ -208,6 +220,48 @@ akka.cluster.sharding {
pattern = arc-search-merged
strategy = lfuda-100k
},
{
name = "LRU/FS/SLRU 250k"
shards = 100
regions = 10
pattern = arc-search-merged
strategy = lru-fs-slru-25k
},
{
name = "LRU/FS/SLRU 500k"
shards = 100
regions = 10
pattern = arc-search-merged
strategy = lru-fs-slru-50k
},
{
name = "LRU/FS/SLRU 1M"
shards = 100
regions = 10
pattern = arc-search-merged
strategy = lru-fs-slru-100k
},
{
name = "LRU/FS/SLRU/HC 250k"
shards = 100
regions = 10
pattern = arc-search-merged
strategy = lru-fs-slru-hc-25k
},
{
name = "LRU/FS/SLRU/HC 500k"
shards = 100
regions = 10
pattern = arc-search-merged
strategy = lru-fs-slru-hc-50k
},
{
name = "LRU/FS/SLRU/HC 1M"
shards = 100
regions = 10
pattern = arc-search-merged
strategy = lru-fs-slru-hc-100k
},
]
print-detailed-stats = true
@ -384,5 +438,54 @@ akka.cluster.sharding {
dynamic-aging = on
}
}
lru-fs-slru {
strategy = composite
composite {
admission {
window.strategy = least-recently-used
filter = frequency-sketch
optimizer = none
}
main {
strategy = least-recently-used
least-recently-used {
segmented {
levels = 2
proportions = [0.2, 0.8]
}
}
}
}
}
lru-fs-slru-25k = ${akka.cluster.sharding.passivation.simulator.lru-fs-slru} {
composite.per-region-limit = 25000
}
lru-fs-slru-50k = ${akka.cluster.sharding.passivation.simulator.lru-fs-slru} {
composite.per-region-limit = 50000
}
lru-fs-slru-100k = ${akka.cluster.sharding.passivation.simulator.lru-fs-slru} {
composite.per-region-limit = 100000
}
lru-fs-slru-hc = ${akka.cluster.sharding.passivation.simulator.lru-fs-slru} {
composite.admission.optimizer = hill-climbing
}
lru-fs-slru-hc-25k = ${akka.cluster.sharding.passivation.simulator.lru-fs-slru-hc} {
composite.per-region-limit = 25000
}
lru-fs-slru-hc-50k = ${akka.cluster.sharding.passivation.simulator.lru-fs-slru-hc} {
composite.per-region-limit = 50000
}
lru-fs-slru-hc-100k = ${akka.cluster.sharding.passivation.simulator.lru-fs-slru-hc} {
composite.per-region-limit = 100000
}
}
}

View file

@ -8,65 +8,81 @@
#
# > akka-cluster-sharding/Test/runMain akka.cluster.sharding.passivation.simulator.Simulator lirs-trace-glimpse
#
# ╔════════════╤═════════╤══════════╤═════════════╤══════════════╗
# ╔═════════════════════╤═════════╤══════════╤═════════════╤══════════════╗
# ║ Run │ Active │ Accesses │ Activations │ Passivations ║
# ╠════════════╪═════════╪══════════╪═════════════╪══════════════╣
# ╠═════════════════════╪═════════╪══════════╪═════════════╪══════════════╣
# ║ OPT 250 │ 17.70 % │ 6,016 │ 4,951 │ 4,701 ║
# ╟────────────┼─────────┼──────────┼─────────────┼──────────────╢
# ╟─────────────────────┼─────────┼──────────┼─────────────┼──────────────╢
# ║ OPT 500 │ 34.33 % │ 6,016 │ 3,951 │ 3,451 ║
# ╟────────────┼─────────┼──────────┼─────────────┼──────────────╢
# ╟─────────────────────┼─────────┼──────────┼─────────────┼──────────────╢
# ║ OPT 1000 │ 53.14 % │ 6,016 │ 2,819 │ 1,819 ║
# ╟────────────┼─────────┼──────────┼─────────────┼──────────────╢
# ╟─────────────────────┼─────────┼──────────┼─────────────┼──────────────╢
# ║ OPT 1500 │ 57.95 % │ 6,016 │ 2,530 │ 1,030 ║
# ╠════════════╪═════════╪══════════╪═════════════╪══════════════╣
# ╠═════════════════════╪═════════╪══════════╪═════════════╪══════════════╣
# ║ LRU 250 │ 0.91 % │ 6,016 │ 5,961 │ 5,711 ║
# ╟────────────┼─────────┼──────────┼─────────────┼──────────────╢
# ╟─────────────────────┼─────────┼──────────┼─────────────┼──────────────╢
# ║ LRU 500 │ 0.95 % │ 6,016 │ 5,959 │ 5,459 ║
# ╟────────────┼─────────┼──────────┼─────────────┼──────────────╢
# ╟─────────────────────┼─────────┼──────────┼─────────────┼──────────────╢
# ║ LRU 1000 │ 11.20 % │ 6,016 │ 5,342 │ 4,342 ║
# ╟────────────┼─────────┼──────────┼─────────────┼──────────────╢
# ╟─────────────────────┼─────────┼──────────┼─────────────┼──────────────╢
# ║ LRU 1500 │ 36.55 % │ 6,016 │ 3,817 │ 2,317 ║
# ╠════════════╪═════════╪══════════╪═════════════╪══════════════╣
# ╠═════════════════════╪═════════╪══════════╪═════════════╪══════════════╣
# ║ SLRU 250 │ 1.38 % │ 6,016 │ 5,933 │ 5,683 ║
# ╟────────────┼─────────┼──────────┼─────────────┼──────────────╢
# ╟─────────────────────┼─────────┼──────────┼─────────────┼──────────────╢
# ║ SLRU 500 │ 1.38 % │ 6,016 │ 5,933 │ 5,433 ║
# ╟────────────┼─────────┼──────────┼─────────────┼──────────────╢
# ╟─────────────────────┼─────────┼──────────┼─────────────┼──────────────╢
# ║ SLRU 1000 │ 31.33 % │ 6,016 │ 4,131 │ 3,131 ║
# ╟────────────┼─────────┼──────────┼─────────────┼──────────────╢
# ╟─────────────────────┼─────────┼──────────┼─────────────┼──────────────╢
# ║ SLRU 1500 │ 51.85 % │ 6,016 │ 2,897 │ 1,397 ║
# ╠════════════╪═════════╪══════════╪═════════════╪══════════════╣
# ╠═════════════════════╪═════════╪══════════╪═════════════╪══════════════╣
# ║ S4LRU 250 │ 1.38 % │ 6,016 │ 5,933 │ 5,693 ║
# ╟────────────┼─────────┼──────────┼─────────────┼──────────────╢
# ╟─────────────────────┼─────────┼──────────┼─────────────┼──────────────╢
# ║ S4LRU 500 │ 1.38 % │ 6,016 │ 5,933 │ 5,453 ║
# ╟────────────┼─────────┼──────────┼─────────────┼──────────────╢
# ╟─────────────────────┼─────────┼──────────┼─────────────┼──────────────╢
# ║ S4LRU 1000 │ 19.70 % │ 6,016 │ 4,831 │ 3,831 ║
# ╟────────────┼─────────┼──────────┼─────────────┼──────────────╢
# ╟─────────────────────┼─────────┼──────────┼─────────────┼──────────────╢
# ║ S4LRU 1500 │ 48.02 % │ 6,016 │ 3,127 │ 1,647 ║
# ╠════════════╪═════════╪══════════╪═════════════╪══════════════╣
# ╠═════════════════════╪═════════╪══════════╪═════════════╪══════════════╣
# ║ MRU 250 │ 14.78 % │ 6,016 │ 5,127 │ 4,877 ║
# ╟────────────┼─────────┼──────────┼─────────────┼──────────────╢
# ╟─────────────────────┼─────────┼──────────┼─────────────┼──────────────╢
# ║ MRU 500 │ 31.40 % │ 6,016 │ 4,127 │ 3,627 ║
# ╟────────────┼─────────┼──────────┼─────────────┼──────────────╢
# ╟─────────────────────┼─────────┼──────────┼─────────────┼──────────────╢
# ║ MRU 1000 │ 48.62 % │ 6,016 │ 3,091 │ 2,091 ║
# ╟────────────┼─────────┼──────────┼─────────────┼──────────────╢
# ╟─────────────────────┼─────────┼──────────┼─────────────┼──────────────╢
# ║ MRU 1500 │ 53.37 % │ 6,016 │ 2,805 │ 1,305 ║
# ╠════════════╪═════════╪══════════╪═════════════╪══════════════╣
# ╠═════════════════════╪═════════╪══════════╪═════════════╪══════════════╣
# ║ LFU 250 │ 1.38 % │ 6,016 │ 5,933 │ 5,683 ║
# ╟────────────┼─────────┼──────────┼─────────────┼──────────────╢
# ╟─────────────────────┼─────────┼──────────┼─────────────┼──────────────╢
# ║ LFU 500 │ 1.38 % │ 6,016 │ 5,933 │ 5,433 ║
# ╟────────────┼─────────┼──────────┼─────────────┼──────────────╢
# ╟─────────────────────┼─────────┼──────────┼─────────────┼──────────────╢
# ║ LFU 1000 │ 31.33 % │ 6,016 │ 4,131 │ 3,131 ║
# ╟────────────┼─────────┼──────────┼─────────────┼──────────────╢
# ╟─────────────────────┼─────────┼──────────┼─────────────┼──────────────╢
# ║ LFU 1500 │ 51.85 % │ 6,016 │ 2,897 │ 1,397 ║
# ╠════════════╪═════════╪══════════╪═════════════╪══════════════╣
# ╠═════════════════════╪═════════╪══════════╪═════════════╪══════════════╣
# ║ LFUDA 250 │ 1.11 % │ 6,016 │ 5,949 │ 5,699 ║
# ╟────────────┼─────────┼──────────┼─────────────┼──────────────╢
# ╟─────────────────────┼─────────┼──────────┼─────────────┼──────────────╢
# ║ LFUDA 500 │ 1.25 % │ 6,016 │ 5,941 │ 5,441 ║
# ╟────────────┼─────────┼──────────┼─────────────┼──────────────╢
# ╟─────────────────────┼─────────┼──────────┼─────────────┼──────────────╢
# ║ LFUDA 1000 │ 16.37 % │ 6,016 │ 5,031 │ 4,031 ║
# ╟────────────┼─────────┼──────────┼─────────────┼──────────────╢
# ╟─────────────────────┼─────────┼──────────┼─────────────┼──────────────╢
# ║ LFUDA 1500 │ 51.85 % │ 6,016 │ 2,897 │ 1,397 ║
# ╚════════════╧═════════╧══════════╧═════════════╧══════════════╝
# ╠═════════════════════╪═════════╪══════════╪═════════════╪══════════════╣
# ║ LRU/FS/SLRU 250 │ 13.43 % │ 6,016 │ 5,208 │ 4,958 ║
# ╟─────────────────────┼─────────┼──────────┼─────────────┼──────────────╢
# ║ LRU/FS/SLRU 500 │ 32.40 % │ 6,016 │ 4,067 │ 3,567 ║
# ╟─────────────────────┼─────────┼──────────┼─────────────┼──────────────╢
# ║ LRU/FS/SLRU 1000 │ 50.83 % │ 6,016 │ 2,958 │ 1,958 ║
# ╟─────────────────────┼─────────┼──────────┼─────────────┼──────────────╢
# ║ LRU/FS/SLRU 1500 │ 53.54 % │ 6,016 │ 2,795 │ 1,295 ║
# ╠═════════════════════╪═════════╪══════════╪═════════════╪══════════════╣
# ║ LRU/FS/SLRU/HC 250 │ 13.43 % │ 6,016 │ 5,208 │ 4,958 ║
# ╟─────────────────────┼─────────┼──────────┼─────────────┼──────────────╢
# ║ LRU/FS/SLRU/HC 500 │ 32.40 % │ 6,016 │ 4,067 │ 3,567 ║
# ╟─────────────────────┼─────────┼──────────┼─────────────┼──────────────╢
# ║ LRU/FS/SLRU/HC 1000 │ 50.83 % │ 6,016 │ 2,958 │ 1,958 ║
# ╟─────────────────────┼─────────┼──────────┼─────────────┼──────────────╢
# ║ LRU/FS/SLRU/HC 1500 │ 53.54 % │ 6,016 │ 2,795 │ 1,295 ║
# ╚═════════════════════╧═════════╧══════════╧═════════════╧══════════════╝
#
lirs-traces="lirs-traces"
@ -271,6 +287,62 @@ akka.cluster.sharding {
pattern = lirs-glimpse
strategy = lfuda-1500
},
{
name = "LRU/FS/SLRU 250"
shards = 10
regions = 1
pattern = lirs-glimpse
strategy = lru-fs-slru-250
},
{
name = "LRU/FS/SLRU 500"
shards = 10
regions = 1
pattern = lirs-glimpse
strategy = lru-fs-slru-500
},
{
name = "LRU/FS/SLRU 1000"
shards = 10
regions = 1
pattern = lirs-glimpse
strategy = lru-fs-slru-1000
},
{
name = "LRU/FS/SLRU 1500"
shards = 10
regions = 1
pattern = lirs-glimpse
strategy = lru-fs-slru-1500
},
{
name = "LRU/FS/SLRU/HC 250"
shards = 10
regions = 1
pattern = lirs-glimpse
strategy = lru-fs-slru-hc-250
},
{
name = "LRU/FS/SLRU/HC 500"
shards = 10
regions = 1
pattern = lirs-glimpse
strategy = lru-fs-slru-hc-500
},
{
name = "LRU/FS/SLRU/HC 1000"
shards = 10
regions = 1
pattern = lirs-glimpse
strategy = lru-fs-slru-hc-1000
},
{
name = "LRU/FS/SLRU/HC 1500"
shards = 10
regions = 1
pattern = lirs-glimpse
strategy = lru-fs-slru-hc-1500
},
]
print-detailed-stats = true
@ -502,5 +574,62 @@ akka.cluster.sharding {
dynamic-aging = on
}
}
lru-fs-slru {
strategy = composite
composite {
admission {
window.strategy = least-recently-used
filter = frequency-sketch
optimizer = none
}
main {
strategy = least-recently-used
least-recently-used {
segmented {
levels = 2
proportions = [0.2, 0.8]
}
}
}
}
}
lru-fs-slru-250 = ${akka.cluster.sharding.passivation.simulator.lru-fs-slru} {
composite.per-region-limit = 250
}
lru-fs-slru-500 = ${akka.cluster.sharding.passivation.simulator.lru-fs-slru} {
composite.per-region-limit = 500
}
lru-fs-slru-1000 = ${akka.cluster.sharding.passivation.simulator.lru-fs-slru} {
composite.per-region-limit = 1000
}
lru-fs-slru-1500 = ${akka.cluster.sharding.passivation.simulator.lru-fs-slru} {
composite.per-region-limit = 1500
}
lru-fs-slru-hc = ${akka.cluster.sharding.passivation.simulator.lru-fs-slru} {
composite.admission.optimizer = hill-climbing
}
lru-fs-slru-hc-250 = ${akka.cluster.sharding.passivation.simulator.lru-fs-slru-hc} {
composite.per-region-limit = 250
}
lru-fs-slru-hc-500 = ${akka.cluster.sharding.passivation.simulator.lru-fs-slru-hc} {
composite.per-region-limit = 500
}
lru-fs-slru-hc-1000 = ${akka.cluster.sharding.passivation.simulator.lru-fs-slru-hc} {
composite.per-region-limit = 1000
}
lru-fs-slru-hc-1500 = ${akka.cluster.sharding.passivation.simulator.lru-fs-slru-hc} {
composite.per-region-limit = 1500
}
}
}

View file

@ -8,79 +8,99 @@
#
# > akka-cluster-sharding/Test/runMain akka.cluster.sharding.passivation.simulator.Simulator lirs-trace-multi
#
# ╔════════════╤═════════╤══════════╤═════════════╤══════════════╗
# ╔═════════════════════╤═════════╤══════════╤═════════════╤══════════════╗
# ║ Run │ Active │ Accesses │ Activations │ Passivations ║
# ╠════════════╪═════════╪══════════╪═════════════╪══════════════╣
# ╠═════════════════════╪═════════╪══════════╪═════════════╪══════════════╣
# ║ OPT 100 │ 31.20 % │ 30,241 │ 20,807 │ 20,707 ║
# ╟────────────┼─────────┼──────────┼─────────────┼──────────────╢
# ╟─────────────────────┼─────────┼──────────┼─────────────┼──────────────╢
# ║ OPT 200 │ 38.47 % │ 30,241 │ 18,607 │ 18,407 ║
# ╟────────────┼─────────┼──────────┼─────────────┼──────────────╢
# ╟─────────────────────┼─────────┼──────────┼─────────────┼──────────────╢
# ║ OPT 400 │ 47.14 % │ 30,241 │ 15,986 │ 15,586 ║
# ╟────────────┼─────────┼──────────┼─────────────┼──────────────╢
# ╟─────────────────────┼─────────┼──────────┼─────────────┼──────────────╢
# ║ OPT 800 │ 53.53 % │ 30,241 │ 14,054 │ 13,254 ║
# ╟────────────┼─────────┼──────────┼─────────────┼──────────────╢
# ╟─────────────────────┼─────────┼──────────┼─────────────┼──────────────╢
# ║ OPT 1600 │ 64.14 % │ 30,241 │ 10,844 │ 9,244 ║
# ╠════════════╪═════════╪══════════╪═════════════╪══════════════╣
# ╠═════════════════════╪═════════╪══════════╪═════════════╪══════════════╣
# ║ LRU 100 │ 6.53 % │ 30,241 │ 28,266 │ 28,166 ║
# ╟────────────┼─────────┼──────────┼─────────────┼──────────────╢
# ╟─────────────────────┼─────────┼──────────┼─────────────┼──────────────╢
# ║ LRU 200 │ 14.38 % │ 30,241 │ 25,891 │ 25,691 ║
# ╟────────────┼─────────┼──────────┼─────────────┼──────────────╢
# ╟─────────────────────┼─────────┼──────────┼─────────────┼──────────────╢
# ║ LRU 400 │ 27.55 % │ 30,241 │ 21,909 │ 21,509 ║
# ╟────────────┼─────────┼──────────┼─────────────┼──────────────╢
# ╟─────────────────────┼─────────┼──────────┼─────────────┼──────────────╢
# ║ LRU 800 │ 35.97 % │ 30,241 │ 19,364 │ 18,564 ║
# ╟────────────┼─────────┼──────────┼─────────────┼──────────────╢
# ╟─────────────────────┼─────────┼──────────┼─────────────┼──────────────╢
# ║ LRU 1600 │ 44.23 % │ 30,241 │ 16,865 │ 15,265 ║
# ╠════════════╪═════════╪══════════╪═════════════╪══════════════╣
# ╠═════════════════════╪═════════╪══════════╪═════════════╪══════════════╣
# ║ SLRU 100 │ 9.22 % │ 30,241 │ 27,454 │ 27,354 ║
# ╟────────────┼─────────┼──────────┼─────────────┼──────────────╢
# ╟─────────────────────┼─────────┼──────────┼─────────────┼──────────────╢
# ║ SLRU 200 │ 22.41 % │ 30,241 │ 23,463 │ 23,263 ║
# ╟────────────┼─────────┼──────────┼─────────────┼──────────────╢
# ╟─────────────────────┼─────────┼──────────┼─────────────┼──────────────╢
# ║ SLRU 400 │ 29.94 % │ 30,241 │ 21,186 │ 20,786 ║
# ╟────────────┼─────────┼──────────┼─────────────┼──────────────╢
# ╟─────────────────────┼─────────┼──────────┼─────────────┼──────────────╢
# ║ SLRU 800 │ 37.30 % │ 30,241 │ 18,961 │ 18,161 ║
# ╟────────────┼─────────┼──────────┼─────────────┼──────────────╢
# ╟─────────────────────┼─────────┼──────────┼─────────────┼──────────────╢
# ║ SLRU 1600 │ 46.51 % │ 30,241 │ 16,177 │ 14,577 ║
# ╠════════════╪═════════╪══════════╪═════════════╪══════════════╣
# ╠═════════════════════╪═════════╪══════════╪═════════════╪══════════════╣
# ║ S4LRU 100 │ 8.43 % │ 30,241 │ 27,691 │ 27,611 ║
# ╟────────────┼─────────┼──────────┼─────────────┼──────────────╢
# ╟─────────────────────┼─────────┼──────────┼─────────────┼──────────────╢
# ║ S4LRU 200 │ 22.62 % │ 30,241 │ 23,401 │ 23,201 ║
# ╟────────────┼─────────┼──────────┼─────────────┼──────────────╢
# ╟─────────────────────┼─────────┼──────────┼─────────────┼──────────────╢
# ║ S4LRU 400 │ 30.10 % │ 30,241 │ 21,139 │ 20,739 ║
# ╟────────────┼─────────┼──────────┼─────────────┼──────────────╢
# ╟─────────────────────┼─────────┼──────────┼─────────────┼──────────────╢
# ║ S4LRU 800 │ 37.30 % │ 30,241 │ 18,962 │ 18,162 ║
# ╟────────────┼─────────┼──────────┼─────────────┼──────────────╢
# ╟─────────────────────┼─────────┼──────────┼─────────────┼──────────────╢
# ║ S4LRU 1600 │ 46.51 % │ 30,241 │ 16,177 │ 14,577 ║
# ╠════════════╪═════════╪══════════╪═════════════╪══════════════╣
# ╠═════════════════════╪═════════╪══════════╪═════════════╪══════════════╣
# ║ MRU 100 │ 2.41 % │ 30,241 │ 29,512 │ 29,412 ║
# ╟────────────┼─────────┼──────────┼─────────────┼──────────────╢
# ╟─────────────────────┼─────────┼──────────┼─────────────┼──────────────╢
# ║ MRU 200 │ 4.20 % │ 30,241 │ 28,970 │ 28,770 ║
# ╟────────────┼─────────┼──────────┼─────────────┼──────────────╢
# ╟─────────────────────┼─────────┼──────────┼─────────────┼──────────────╢
# ║ MRU 400 │ 7.21 % │ 30,241 │ 28,062 │ 27,662 ║
# ╟────────────┼─────────┼──────────┼─────────────┼──────────────╢
# ╟─────────────────────┼─────────┼──────────┼─────────────┼──────────────╢
# ║ MRU 800 │ 14.11 % │ 30,241 │ 25,973 │ 25,173 ║
# ╟────────────┼─────────┼──────────┼─────────────┼──────────────╢
# ╟─────────────────────┼─────────┼──────────┼─────────────┼──────────────╢
# ║ MRU 1600 │ 26.31 % │ 30,241 │ 22,284 │ 20,684 ║
# ╠════════════╪═════════╪══════════╪═════════════╪══════════════╣
# ╠═════════════════════╪═════════╪══════════╪═════════════╪══════════════╣
# ║ LFU 100 │ 8.88 % │ 30,241 │ 27,557 │ 27,457 ║
# ╟────────────┼─────────┼──────────┼─────────────┼──────────────╢
# ╟─────────────────────┼─────────┼──────────┼─────────────┼──────────────╢
# ║ LFU 200 │ 22.14 % │ 30,241 │ 23,547 │ 23,347 ║
# ╟────────────┼─────────┼──────────┼─────────────┼──────────────╢
# ╟─────────────────────┼─────────┼──────────┼─────────────┼──────────────╢
# ║ LFU 400 │ 29.85 % │ 30,241 │ 21,213 │ 20,813 ║
# ╟────────────┼─────────┼──────────┼─────────────┼──────────────╢
# ╟─────────────────────┼─────────┼──────────┼─────────────┼──────────────╢
# ║ LFU 800 │ 37.30 % │ 30,241 │ 18,961 │ 18,161 ║
# ╟────────────┼─────────┼──────────┼─────────────┼──────────────╢
# ╟─────────────────────┼─────────┼──────────┼─────────────┼──────────────╢
# ║ LFU 1600 │ 46.51 % │ 30,241 │ 16,177 │ 14,577 ║
# ╠════════════╪═════════╪══════════╪═════════════╪══════════════╣
# ╠═════════════════════╪═════════╪══════════╪═════════════╪══════════════╣
# ║ LFUDA 100 │ 10.16 % │ 30,241 │ 27,168 │ 27,068 ║
# ╟────────────┼─────────┼──────────┼─────────────┼──────────────╢
# ╟─────────────────────┼─────────┼──────────┼─────────────┼──────────────╢
# ║ LFUDA 200 │ 22.99 % │ 30,241 │ 23,290 │ 23,090 ║
# ╟────────────┼─────────┼──────────┼─────────────┼──────────────╢
# ╟─────────────────────┼─────────┼──────────┼─────────────┼──────────────╢
# ║ LFUDA 400 │ 30.50 % │ 30,241 │ 21,019 │ 20,619 ║
# ╟────────────┼─────────┼──────────┼─────────────┼──────────────╢
# ╟─────────────────────┼─────────┼──────────┼─────────────┼──────────────╢
# ║ LFUDA 800 │ 37.25 % │ 30,241 │ 18,977 │ 18,177 ║
# ╟────────────┼─────────┼──────────┼─────────────┼──────────────╢
# ╟─────────────────────┼─────────┼──────────┼─────────────┼──────────────╢
# ║ LFUDA 1600 │ 46.21 % │ 30,241 │ 16,267 │ 14,667 ║
# ╚════════════╧═════════╧══════════╧═════════════╧══════════════╝
# ╠═════════════════════╪═════════╪══════════╪═════════════╪══════════════╣
# ║ LRU/FS/SLRU 100 │ 25.96 % │ 30,241 │ 22,389 │ 22,289 ║
# ╟─────────────────────┼─────────┼──────────┼─────────────┼──────────────╢
# ║ LRU/FS/SLRU 200 │ 32.56 % │ 30,241 │ 20,396 │ 20,196 ║
# ╟─────────────────────┼─────────┼──────────┼─────────────┼──────────────╢
# ║ LRU/FS/SLRU 400 │ 42.64 % │ 30,241 │ 17,347 │ 16,947 ║
# ╟─────────────────────┼─────────┼──────────┼─────────────┼──────────────╢
# ║ LRU/FS/SLRU 800 │ 46.94 % │ 30,241 │ 16,045 │ 15,245 ║
# ╟─────────────────────┼─────────┼──────────┼─────────────┼──────────────╢
# ║ LRU/FS/SLRU 1600 │ 57.65 % │ 30,241 │ 12,807 │ 11,207 ║
# ╠═════════════════════╪═════════╪══════════╪═════════════╪══════════════╣
# ║ LRU/FS/SLRU/HC 100 │ 24.74 % │ 30,241 │ 22,760 │ 22,660 ║
# ╟─────────────────────┼─────────┼──────────┼─────────────┼──────────────╢
# ║ LRU/FS/SLRU/HC 200 │ 32.38 % │ 30,241 │ 20,450 │ 20,250 ║
# ╟─────────────────────┼─────────┼──────────┼─────────────┼──────────────╢
# ║ LRU/FS/SLRU/HC 400 │ 42.63 % │ 30,241 │ 17,348 │ 16,948 ║
# ╟─────────────────────┼─────────┼──────────┼─────────────┼──────────────╢
# ║ LRU/FS/SLRU/HC 800 │ 47.11 % │ 30,241 │ 15,994 │ 15,194 ║
# ╟─────────────────────┼─────────┼──────────┼─────────────┼──────────────╢
# ║ LRU/FS/SLRU/HC 1600 │ 57.65 % │ 30,241 │ 12,807 │ 11,207 ║
# ╚═════════════════════╧═════════╧══════════╧═════════════╧══════════════╝
#
lirs-traces="lirs-traces"
@ -334,6 +354,76 @@ akka.cluster.sharding {
pattern = lirs-multi3
strategy = lfuda-1600
},
{
name = "LRU/FS/SLRU 100"
shards = 10
regions = 1
pattern = lirs-multi3
strategy = lru-fs-slru-100
},
{
name = "LRU/FS/SLRU 200"
shards = 10
regions = 1
pattern = lirs-multi3
strategy = lru-fs-slru-200
},
{
name = "LRU/FS/SLRU 400"
shards = 10
regions = 1
pattern = lirs-multi3
strategy = lru-fs-slru-400
},
{
name = "LRU/FS/SLRU 800"
shards = 10
regions = 1
pattern = lirs-multi3
strategy = lru-fs-slru-800
},
{
name = "LRU/FS/SLRU 1600"
shards = 10
regions = 1
pattern = lirs-multi3
strategy = lru-fs-slru-1600
},
{
name = "LRU/FS/SLRU/HC 100"
shards = 10
regions = 1
pattern = lirs-multi3
strategy = lru-fs-slru-hc-100
},
{
name = "LRU/FS/SLRU/HC 200"
shards = 10
regions = 1
pattern = lirs-multi3
strategy = lru-fs-slru-hc-200
},
{
name = "LRU/FS/SLRU/HC 400"
shards = 10
regions = 1
pattern = lirs-multi3
strategy = lru-fs-slru-hc-400
},
{
name = "LRU/FS/SLRU/HC 800"
shards = 10
regions = 1
pattern = lirs-multi3
strategy = lru-fs-slru-hc-800
},
{
name = "LRU/FS/SLRU/HC 1600"
shards = 10
regions = 1
pattern = lirs-multi3
strategy = lru-fs-slru-hc-1600
},
]
print-detailed-stats = true
@ -620,5 +710,70 @@ akka.cluster.sharding {
dynamic-aging = on
}
}
lru-fs-slru {
strategy = composite
composite {
admission {
window.strategy = least-recently-used
filter = frequency-sketch
optimizer = none
}
main {
strategy = least-recently-used
least-recently-used {
segmented {
levels = 2
proportions = [0.2, 0.8]
}
}
}
}
}
lru-fs-slru-100 = ${akka.cluster.sharding.passivation.simulator.lru-fs-slru} {
composite.per-region-limit = 100
}
lru-fs-slru-200 = ${akka.cluster.sharding.passivation.simulator.lru-fs-slru} {
composite.per-region-limit = 200
}
lru-fs-slru-400 = ${akka.cluster.sharding.passivation.simulator.lru-fs-slru} {
composite.per-region-limit = 400
}
lru-fs-slru-800 = ${akka.cluster.sharding.passivation.simulator.lru-fs-slru} {
composite.per-region-limit = 800
}
lru-fs-slru-1600 = ${akka.cluster.sharding.passivation.simulator.lru-fs-slru} {
composite.per-region-limit = 1600
}
lru-fs-slru-hc = ${akka.cluster.sharding.passivation.simulator.lru-fs-slru} {
composite.admission.optimizer = hill-climbing
}
lru-fs-slru-hc-100 = ${akka.cluster.sharding.passivation.simulator.lru-fs-slru-hc} {
composite.per-region-limit = 100
}
lru-fs-slru-hc-200 = ${akka.cluster.sharding.passivation.simulator.lru-fs-slru-hc} {
composite.per-region-limit = 200
}
lru-fs-slru-hc-400 = ${akka.cluster.sharding.passivation.simulator.lru-fs-slru-hc} {
composite.per-region-limit = 400
}
lru-fs-slru-hc-800 = ${akka.cluster.sharding.passivation.simulator.lru-fs-slru-hc} {
composite.per-region-limit = 800
}
lru-fs-slru-hc-1600 = ${akka.cluster.sharding.passivation.simulator.lru-fs-slru-hc} {
composite.per-region-limit = 1600
}
}
}

View file

@ -8,65 +8,81 @@
#
# > akka-cluster-sharding/Test/runMain akka.cluster.sharding.passivation.simulator.Simulator lirs-trace-postgres
#
# ════════════╤═════════╤══════════╤═════════════╤══════════════╗
# ╔═════════════════════╤═════════╤══════════╤═════════════╤══════════════╗
# ║ Run │ Active │ Accesses │ Activations │ Passivations ║
# ════════════╪═════════╪══════════╪═════════════╪══════════════╣
# ╠═════════════════════╪═════════╪══════════╪═════════════╪══════════════╣
# ║ OPT 125 │ 35.16 % │ 10,448 │ 6,774 │ 6,654 ║
# ────────────┼─────────┼──────────┼─────────────┼──────────────╢
# ╟─────────────────────┼─────────┼──────────┼─────────────┼──────────────╢
# ║ OPT 250 │ 53.33 % │ 10,448 │ 4,876 │ 4,626 ║
# ────────────┼─────────┼──────────┼─────────────┼──────────────╢
# ╟─────────────────────┼─────────┼──────────┼─────────────┼──────────────╢
# ║ OPT 500 │ 58.12 % │ 10,448 │ 4,376 │ 3,876 ║
# ────────────┼─────────┼──────────┼─────────────┼──────────────╢
# ╟─────────────────────┼─────────┼──────────┼─────────────┼──────────────╢
# ║ OPT 1000 │ 67.69 % │ 10,448 │ 3,376 │ 2,376 ║
# ════════════╪═════════╪══════════╪═════════════╪══════════════╣
# ╠═════════════════════╪═════════╪══════════╪═════════════╪══════════════╣
# ║ LRU 125 │ 12.19 % │ 10,448 │ 9,174 │ 9,054 ║
# ────────────┼─────────┼──────────┼─────────────┼──────────────╢
# ╟─────────────────────┼─────────┼──────────┼─────────────┼──────────────╢
# ║ LRU 250 │ 13.95 % │ 10,448 │ 8,991 │ 8,741 ║
# ────────────┼─────────┼──────────┼─────────────┼──────────────╢
# ╟─────────────────────┼─────────┼──────────┼─────────────┼──────────────╢
# ║ LRU 500 │ 48.55 % │ 10,448 │ 5,376 │ 4,876 ║
# ────────────┼─────────┼──────────┼─────────────┼──────────────╢
# ╟─────────────────────┼─────────┼──────────┼─────────────┼──────────────╢
# ║ LRU 1000 │ 48.55 % │ 10,448 │ 5,376 │ 4,376 ║
# ════════════╪═════════╪══════════╪═════════════╪══════════════╣
# ╠═════════════════════╪═════════╪══════════╪═════════════╪══════════════╣
# ║ SLRU 125 │ 9.34 % │ 10,448 │ 9,472 │ 9,362 ║
# ────────────┼─────────┼──────────┼─────────────┼──────────────╢
# ╟─────────────────────┼─────────┼──────────┼─────────────┼──────────────╢
# ║ SLRU 250 │ 16.80 % │ 10,448 │ 8,693 │ 8,443 ║
# ────────────┼─────────┼──────────┼─────────────┼──────────────╢
# ╟─────────────────────┼─────────┼──────────┼─────────────┼──────────────╢
# ║ SLRU 500 │ 52.59 % │ 10,448 │ 4,953 │ 4,453 ║
# ────────────┼─────────┼──────────┼─────────────┼──────────────╢
# ╟─────────────────────┼─────────┼──────────┼─────────────┼──────────────╢
# ║ SLRU 1000 │ 52.59 % │ 10,448 │ 4,953 │ 3,953 ║
# ════════════╪═════════╪══════════╪═════════════╪══════════════╣
# ╠═════════════════════╪═════════╪══════════╪═════════════╪══════════════╣
# ║ S4LRU 125 │ 9.34 % │ 10,448 │ 9,472 │ 9,352 ║
# ────────────┼─────────┼──────────┼─────────────┼──────────────╢
# ╟─────────────────────┼─────────┼──────────┼─────────────┼──────────────╢
# ║ S4LRU 250 │ 16.80 % │ 10,448 │ 8,693 │ 8,453 ║
# ────────────┼─────────┼──────────┼─────────────┼──────────────╢
# ╟─────────────────────┼─────────┼──────────┼─────────────┼──────────────╢
# ║ S4LRU 500 │ 51.95 % │ 10,448 │ 5,020 │ 4,540 ║
# ────────────┼─────────┼──────────┼─────────────┼──────────────╢
# ╟─────────────────────┼─────────┼──────────┼─────────────┼──────────────╢
# ║ S4LRU 1000 │ 52.59 % │ 10,448 │ 4,953 │ 3,953 ║
# ════════════╪═════════╪══════════╪═════════════╪══════════════╣
# ╠═════════════════════╪═════════╪══════════╪═════════════╪══════════════╣
# ║ MRU 125 │ 14.23 % │ 10,448 │ 8,961 │ 8,841 ║
# ────────────┼─────────┼──────────┼─────────────┼──────────────╢
# ╟─────────────────────┼─────────┼──────────┼─────────────┼──────────────╢
# ║ MRU 250 │ 30.84 % │ 10,448 │ 7,226 │ 6,976 ║
# ────────────┼─────────┼──────────┼─────────────┼──────────────╢
# ╟─────────────────────┼─────────┼──────────┼─────────────┼──────────────╢
# ║ MRU 500 │ 45.67 % │ 10,448 │ 5,676 │ 5,176 ║
# ────────────┼─────────┼──────────┼─────────────┼──────────────╢
# ╟─────────────────────┼─────────┼──────────┼─────────────┼──────────────╢
# ║ MRU 1000 │ 64.41 % │ 10,448 │ 3,718 │ 2,718 ║
# ════════════╪═════════╪══════════╪═════════════╪══════════════╣
# ╠═════════════════════╪═════════╪══════════╪═════════════╪══════════════╣
# ║ LFU 125 │ 9.34 % │ 10,448 │ 9,472 │ 9,352 ║
# ────────────┼─────────┼──────────┼─────────────┼──────────────╢
# ╟─────────────────────┼─────────┼──────────┼─────────────┼──────────────╢
# ║ LFU 250 │ 16.80 % │ 10,448 │ 8,693 │ 8,443 ║
# ────────────┼─────────┼──────────┼─────────────┼──────────────╢
# ╟─────────────────────┼─────────┼──────────┼─────────────┼──────────────╢
# ║ LFU 500 │ 52.59 % │ 10,448 │ 4,953 │ 4,453 ║
# ────────────┼─────────┼──────────┼─────────────┼──────────────╢
# ╟─────────────────────┼─────────┼──────────┼─────────────┼──────────────╢
# ║ LFU 1000 │ 52.59 % │ 10,448 │ 4,953 │ 3,953 ║
# ════════════╪═════════╪══════════╪═════════════╪══════════════╣
# ╠═════════════════════╪═════════╪══════════╪═════════════╪══════════════╣
# ║ LFUDA 125 │ 12.89 % │ 10,448 │ 9,101 │ 8,981 ║
# ────────────┼─────────┼──────────┼─────────────┼──────────────╢
# ╟─────────────────────┼─────────┼──────────┼─────────────┼──────────────╢
# ║ LFUDA 250 │ 16.79 % │ 10,448 │ 8,694 │ 8,444 ║
# ────────────┼─────────┼──────────┼─────────────┼──────────────╢
# ╟─────────────────────┼─────────┼──────────┼─────────────┼──────────────╢
# ║ LFUDA 500 │ 52.58 % │ 10,448 │ 4,954 │ 4,454 ║
# ────────────┼─────────┼──────────┼─────────────┼──────────────╢
# ╟─────────────────────┼─────────┼──────────┼─────────────┼──────────────╢
# ║ LFUDA 1000 │ 52.58 % │ 10,448 │ 4,954 │ 3,954 ║
# ╚════════════╧═════════╧══════════╧═════════════╧══════════════╝
# ╠═════════════════════╪═════════╪══════════╪═════════════╪══════════════╣
# ║ LRU/FS/SLRU 125 │ 30.64 % │ 10,448 │ 7,247 │ 7,127 ║
# ╟─────────────────────┼─────────┼──────────┼─────────────┼──────────────╢
# ║ LRU/FS/SLRU 250 │ 52.41 % │ 10,448 │ 4,972 │ 4,722 ║
# ╟─────────────────────┼─────────┼──────────┼─────────────┼──────────────╢
# ║ LRU/FS/SLRU 500 │ 55.98 % │ 10,448 │ 4,599 │ 4,099 ║
# ╟─────────────────────┼─────────┼──────────┼─────────────┼──────────────╢
# ║ LRU/FS/SLRU 1000 │ 66.56 % │ 10,448 │ 3,494 │ 2,494 ║
# ╠═════════════════════╪═════════╪══════════╪═════════════╪══════════════╣
# ║ LRU/FS/SLRU/HC 125 │ 29.96 % │ 10,448 │ 7,318 │ 7,198 ║
# ╟─────────────────────┼─────────┼──────────┼─────────────┼──────────────╢
# ║ LRU/FS/SLRU/HC 250 │ 52.38 % │ 10,448 │ 4,975 │ 4,725 ║
# ╟─────────────────────┼─────────┼──────────┼─────────────┼──────────────╢
# ║ LRU/FS/SLRU/HC 500 │ 55.98 % │ 10,448 │ 4,599 │ 4,099 ║
# ╟─────────────────────┼─────────┼──────────┼─────────────┼──────────────╢
# ║ LRU/FS/SLRU/HC 1000 │ 66.56 % │ 10,448 │ 3,494 │ 2,494 ║
# ╚═════════════════════╧═════════╧══════════╧═════════════╧══════════════╝
#
lirs-traces="lirs-traces"
@ -271,6 +287,62 @@ akka.cluster.sharding {
pattern = lirs-postgres
strategy = lfuda-1000
},
{
name = "LRU/FS/SLRU 125"
shards = 10
regions = 1
pattern = lirs-postgres
strategy = lru-fs-slru-125
},
{
name = "LRU/FS/SLRU 250"
shards = 10
regions = 1
pattern = lirs-postgres
strategy = lru-fs-slru-250
},
{
name = "LRU/FS/SLRU 500"
shards = 10
regions = 1
pattern = lirs-postgres
strategy = lru-fs-slru-500
},
{
name = "LRU/FS/SLRU 1000"
shards = 10
regions = 1
pattern = lirs-postgres
strategy = lru-fs-slru-1000
},
{
name = "LRU/FS/SLRU/HC 125"
shards = 10
regions = 1
pattern = lirs-postgres
strategy = lru-fs-slru-hc-125
},
{
name = "LRU/FS/SLRU/HC 250"
shards = 10
regions = 1
pattern = lirs-postgres
strategy = lru-fs-slru-hc-250
},
{
name = "LRU/FS/SLRU/HC 500"
shards = 10
regions = 1
pattern = lirs-postgres
strategy = lru-fs-slru-hc-500
},
{
name = "LRU/FS/SLRU/HC 1000"
shards = 10
regions = 1
pattern = lirs-postgres
strategy = lru-fs-slru-hc-1000
},
]
print-detailed-stats = true
@ -502,5 +574,62 @@ akka.cluster.sharding {
dynamic-aging = on
}
}
lru-fs-slru {
strategy = composite
composite {
admission {
window.strategy = least-recently-used
filter = frequency-sketch
optimizer = none
}
main {
strategy = least-recently-used
least-recently-used {
segmented {
levels = 2
proportions = [0.2, 0.8]
}
}
}
}
}
lru-fs-slru-125 = ${akka.cluster.sharding.passivation.simulator.lru-fs-slru} {
composite.per-region-limit = 125
}
lru-fs-slru-250 = ${akka.cluster.sharding.passivation.simulator.lru-fs-slru} {
composite.per-region-limit = 250
}
lru-fs-slru-500 = ${akka.cluster.sharding.passivation.simulator.lru-fs-slru} {
composite.per-region-limit = 500
}
lru-fs-slru-1000 = ${akka.cluster.sharding.passivation.simulator.lru-fs-slru} {
composite.per-region-limit = 1000
}
lru-fs-slru-hc = ${akka.cluster.sharding.passivation.simulator.lru-fs-slru} {
composite.admission.optimizer = hill-climbing
}
lru-fs-slru-hc-125 = ${akka.cluster.sharding.passivation.simulator.lru-fs-slru-hc} {
composite.per-region-limit = 125
}
lru-fs-slru-hc-250 = ${akka.cluster.sharding.passivation.simulator.lru-fs-slru-hc} {
composite.per-region-limit = 250
}
lru-fs-slru-hc-500 = ${akka.cluster.sharding.passivation.simulator.lru-fs-slru-hc} {
composite.per-region-limit = 500
}
lru-fs-slru-hc-1000 = ${akka.cluster.sharding.passivation.simulator.lru-fs-slru-hc} {
composite.per-region-limit = 1000
}
}
}

View file

@ -11,65 +11,81 @@
#
# > akka-cluster-sharding/Test/runMain akka.cluster.sharding.passivation.simulator.Simulator lirs2-trace-w106
#
# ╔═══════════╤═════════╤═══════════╤═════════════╤══════════════╗
# ╔════════════════════╤═════════╤═══════════╤═════════════╤══════════════╗
# ║ Run │ Active │ Accesses │ Activations │ Passivations ║
# ╠═══════════╪═════════╪═══════════╪═════════════╪══════════════╣
# ╠════════════════════╪═════════╪═══════════╪═════════════╪══════════════╣
# ║ OPT 50 │ 57.92 % │ 5,292,456 │ 2,226,826 │ 2,226,776 ║
# ╟───────────┼─────────┼───────────┼─────────────┼──────────────╢
# ╟────────────────────┼─────────┼───────────┼─────────────┼──────────────╢
# ║ OPT 100 │ 66.17 % │ 5,292,456 │ 1,790,310 │ 1,790,210 ║
# ╟───────────┼─────────┼───────────┼─────────────┼──────────────╢
# ╟────────────────────┼─────────┼───────────┼─────────────┼──────────────╢
# ║ OPT 200 │ 71.69 % │ 5,292,456 │ 1,498,368 │ 1,498,168 ║
# ╟───────────┼─────────┼───────────┼─────────────┼──────────────╢
# ╟────────────────────┼─────────┼───────────┼─────────────┼──────────────╢
# ║ OPT 500 │ 77.20 % │ 5,292,456 │ 1,206,445 │ 1,205,945 ║
# ╠═══════════╪═════════╪═══════════╪═════════════╪══════════════╣
# ╠════════════════════╪═════════╪═══════════╪═════════════╪══════════════╣
# ║ LRU 50 │ 45.35 % │ 5,292,456 │ 2,892,538 │ 2,892,488 ║
# ╟───────────┼─────────┼───────────┼─────────────┼──────────────╢
# ╟────────────────────┼─────────┼───────────┼─────────────┼──────────────╢
# ║ LRU 100 │ 53.47 % │ 5,292,456 │ 2,462,659 │ 2,462,559 ║
# ╟───────────┼─────────┼───────────┼─────────────┼──────────────╢
# ╟────────────────────┼─────────┼───────────┼─────────────┼──────────────╢
# ║ LRU 200 │ 64.96 % │ 5,292,456 │ 1,854,353 │ 1,854,153 ║
# ╟───────────┼─────────┼───────────┼─────────────┼──────────────╢
# ╟────────────────────┼─────────┼───────────┼─────────────┼──────────────╢
# ║ LRU 500 │ 70.97 % │ 5,292,456 │ 1,536,203 │ 1,535,703 ║
# ╠═══════════╪═════════╪═══════════╪═════════════╪══════════════╣
# ╠════════════════════╪═════════╪═══════════╪═════════════╪══════════════╣
# ║ SLRU 50 │ 41.44 % │ 5,292,456 │ 3,099,112 │ 3,099,062 ║
# ╟───────────┼─────────┼───────────┼─────────────┼──────────────╢
# ╟────────────────────┼─────────┼───────────┼─────────────┼──────────────╢
# ║ SLRU 100 │ 52.52 % │ 5,292,456 │ 2,513,049 │ 2,512,949 ║
# ╟───────────┼─────────┼───────────┼─────────────┼──────────────╢
# ╟────────────────────┼─────────┼───────────┼─────────────┼──────────────╢
# ║ SLRU 200 │ 60.42 % │ 5,292,456 │ 2,094,936 │ 2,094,736 ║
# ╟───────────┼─────────┼───────────┼─────────────┼──────────────╢
# ╟────────────────────┼─────────┼───────────┼─────────────┼──────────────╢
# ║ SLRU 500 │ 70.02 % │ 5,292,456 │ 1,586,665 │ 1,586,165 ║
# ╠═══════════╪═════════╪═══════════╪═════════════╪══════════════╣
# ╠════════════════════╪═════════╪═══════════╪═════════════╪══════════════╣
# ║ S4LRU 50 │ 39.87 % │ 5,292,456 │ 3,182,347 │ 3,182,307 ║
# ╟───────────┼─────────┼───────────┼─────────────┼──────────────╢
# ╟────────────────────┼─────────┼───────────┼─────────────┼──────────────╢
# ║ S4LRU 100 │ 54.30 % │ 5,292,456 │ 2,418,459 │ 2,418,359 ║
# ╟───────────┼─────────┼───────────┼─────────────┼──────────────╢
# ╟────────────────────┼─────────┼───────────┼─────────────┼──────────────╢
# ║ S4LRU 200 │ 63.50 % │ 5,292,456 │ 1,931,759 │ 1,931,559 ║
# ╟───────────┼─────────┼───────────┼─────────────┼──────────────╢
# ╟────────────────────┼─────────┼───────────┼─────────────┼──────────────╢
# ║ S4LRU 500 │ 70.68 % │ 5,292,456 │ 1,551,818 │ 1,551,318 ║
# ╠═══════════╪═════════╪═══════════╪═════════════╪══════════════╣
# ╠════════════════════╪═════════╪═══════════╪═════════════╪══════════════╣
# ║ MRU 50 │ 22.23 % │ 5,292,456 │ 4,116,089 │ 4,116,039 ║
# ╟───────────┼─────────┼───────────┼─────────────┼──────────────╢
# ╟────────────────────┼─────────┼───────────┼─────────────┼──────────────╢
# ║ MRU 100 │ 22.36 % │ 5,292,456 │ 4,109,089 │ 4,108,989 ║
# ╟───────────┼─────────┼───────────┼─────────────┼──────────────╢
# ╟────────────────────┼─────────┼───────────┼─────────────┼──────────────╢
# ║ MRU 200 │ 22.68 % │ 5,292,456 │ 4,092,384 │ 4,092,184 ║
# ╟───────────┼─────────┼───────────┼─────────────┼──────────────╢
# ╟────────────────────┼─────────┼───────────┼─────────────┼──────────────╢
# ║ MRU 500 │ 23.53 % │ 5,292,456 │ 4,046,928 │ 4,046,428 ║
# ╠═══════════╪═════════╪═══════════╪═════════════╪══════════════╣
# ╠════════════════════╪═════════╪═══════════╪═════════════╪══════════════╣
# ║ LFU 50 │ 35.92 % │ 5,292,456 │ 3,391,594 │ 3,391,544 ║
# ╟───────────┼─────────┼───────────┼─────────────┼──────────────╢
# ╟────────────────────┼─────────┼───────────┼─────────────┼──────────────╢
# ║ LFU 100 │ 42.52 % │ 5,292,456 │ 3,041,915 │ 3,041,815 ║
# ╟───────────┼─────────┼───────────┼─────────────┼──────────────╢
# ╟────────────────────┼─────────┼───────────┼─────────────┼──────────────╢
# ║ LFU 200 │ 48.42 % │ 5,292,456 │ 2,729,711 │ 2,729,511 ║
# ╟───────────┼─────────┼───────────┼─────────────┼──────────────╢
# ╟────────────────────┼─────────┼───────────┼─────────────┼──────────────╢
# ║ LFU 500 │ 53.57 % │ 5,292,456 │ 2,457,445 │ 2,456,945 ║
# ╠═══════════╪═════════╪═══════════╪═════════════╪══════════════╣
# ╠════════════════════╪═════════╪═══════════╪═════════════╪══════════════╣
# ║ LFUDA 50 │ 46.11 % │ 5,292,456 │ 2,851,965 │ 2,851,915 ║
# ╟───────────┼─────────┼───────────┼─────────────┼──────────────╢
# ╟────────────────────┼─────────┼───────────┼─────────────┼──────────────╢
# ║ LFUDA 100 │ 55.62 % │ 5,292,456 │ 2,348,665 │ 2,348,565 ║
# ╟───────────┼─────────┼───────────┼─────────────┼──────────────╢
# ╟────────────────────┼─────────┼───────────┼─────────────┼──────────────╢
# ║ LFUDA 200 │ 64.83 % │ 5,292,456 │ 1,861,307 │ 1,861,107 ║
# ╟───────────┼─────────┼───────────┼─────────────┼──────────────╢
# ╟────────────────────┼─────────┼───────────┼─────────────┼──────────────╢
# ║ LFUDA 500 │ 71.38 % │ 5,292,456 │ 1,514,631 │ 1,514,131 ║
# ╚═══════════╧═════════╧═══════════╧═════════════╧══════════════╝
# ╠════════════════════╪═════════╪═══════════╪═════════════╪══════════════╣
# ║ LRU/FS/SLRU 50 │ 34.91 % │ 5,292,456 │ 3,444,873 │ 3,444,823 ║
# ╟────────────────────┼─────────┼───────────┼─────────────┼──────────────╢
# ║ LRU/FS/SLRU 100 │ 45.39 % │ 5,292,456 │ 2,890,305 │ 2,890,205 ║
# ╟────────────────────┼─────────┼───────────┼─────────────┼──────────────╢
# ║ LRU/FS/SLRU 200 │ 52.07 % │ 5,292,456 │ 2,536,639 │ 2,536,439 ║
# ╟────────────────────┼─────────┼───────────┼─────────────┼──────────────╢
# ║ LRU/FS/SLRU 500 │ 65.37 % │ 5,292,456 │ 1,832,718 │ 1,832,218 ║
# ╠════════════════════╪═════════╪═══════════╪═════════════╪══════════════╣
# ║ LRU/FS/SLRU/HC 50 │ 46.11 % │ 5,292,456 │ 2,852,272 │ 2,852,222 ║
# ╟────────────────────┼─────────┼───────────┼─────────────┼──────────────╢
# ║ LRU/FS/SLRU/HC 100 │ 55.86 % │ 5,292,456 │ 2,336,245 │ 2,336,145 ║
# ╟────────────────────┼─────────┼───────────┼─────────────┼──────────────╢
# ║ LRU/FS/SLRU/HC 200 │ 65.50 % │ 5,292,456 │ 1,826,070 │ 1,825,870 ║
# ╟────────────────────┼─────────┼───────────┼─────────────┼──────────────╢
# ║ LRU/FS/SLRU/HC 500 │ 71.77 % │ 5,292,456 │ 1,493,991 │ 1,493,491 ║
# ╚════════════════════╧═════════╧═══════════╧═════════════╧══════════════╝
#
lirs2-traces="lirs2-traces"
@ -274,6 +290,62 @@ akka.cluster.sharding {
pattern = lirs2-w106
strategy = lfuda-500
},
{
name = "LRU/FS/SLRU 50"
shards = 5
regions = 1
pattern = lirs2-w106
strategy = lru-fs-slru-50
},
{
name = "LRU/FS/SLRU 100"
shards = 5
regions = 1
pattern = lirs2-w106
strategy = lru-fs-slru-100
},
{
name = "LRU/FS/SLRU 200"
shards = 5
regions = 1
pattern = lirs2-w106
strategy = lru-fs-slru-200
},
{
name = "LRU/FS/SLRU 500"
shards = 5
regions = 1
pattern = lirs2-w106
strategy = lru-fs-slru-500
},
{
name = "LRU/FS/SLRU/HC 50"
shards = 5
regions = 1
pattern = lirs2-w106
strategy = lru-fs-slru-hc-50
},
{
name = "LRU/FS/SLRU/HC 100"
shards = 5
regions = 1
pattern = lirs2-w106
strategy = lru-fs-slru-hc-100
},
{
name = "LRU/FS/SLRU/HC 200"
shards = 5
regions = 1
pattern = lirs2-w106
strategy = lru-fs-slru-hc-200
},
{
name = "LRU/FS/SLRU/HC 500"
shards = 5
regions = 1
pattern = lirs2-w106
strategy = lru-fs-slru-hc-500
},
]
print-detailed-stats = true
@ -505,5 +577,62 @@ akka.cluster.sharding {
dynamic-aging = on
}
}
lru-fs-slru {
strategy = composite
composite {
admission {
window.strategy = least-recently-used
filter = frequency-sketch
optimizer = none
}
main {
strategy = least-recently-used
least-recently-used {
segmented {
levels = 2
proportions = [0.2, 0.8]
}
}
}
}
}
lru-fs-slru-50 = ${akka.cluster.sharding.passivation.simulator.lru-fs-slru} {
composite.per-region-limit = 50
}
lru-fs-slru-100 = ${akka.cluster.sharding.passivation.simulator.lru-fs-slru} {
composite.per-region-limit = 100
}
lru-fs-slru-200 = ${akka.cluster.sharding.passivation.simulator.lru-fs-slru} {
composite.per-region-limit = 200
}
lru-fs-slru-500 = ${akka.cluster.sharding.passivation.simulator.lru-fs-slru} {
composite.per-region-limit = 500
}
lru-fs-slru-hc = ${akka.cluster.sharding.passivation.simulator.lru-fs-slru} {
composite.admission.optimizer = hill-climbing
}
lru-fs-slru-hc-50 = ${akka.cluster.sharding.passivation.simulator.lru-fs-slru-hc} {
composite.per-region-limit = 50
}
lru-fs-slru-hc-100 = ${akka.cluster.sharding.passivation.simulator.lru-fs-slru-hc} {
composite.per-region-limit = 100
}
lru-fs-slru-hc-200 = ${akka.cluster.sharding.passivation.simulator.lru-fs-slru-hc} {
composite.per-region-limit = 200
}
lru-fs-slru-hc-500 = ${akka.cluster.sharding.passivation.simulator.lru-fs-slru-hc} {
composite.per-region-limit = 500
}
}
}

View file

@ -19,8 +19,34 @@ akka.cluster.sharding {
}
}
least-frequently-used {
per-region-limit = 100000
dynamic-aging = off
}
composite {
main.strategy = least-recently-used
admission {
window {
proportion = 0.01
minimum-proportion = 0.01
maximum-proportion = 1.0
strategy = least-recently-used
}
filter = frequency-sketch
frequency-sketch {
depth = 4
counter-bits = 4
width-multiplier = 4
reset-multiplier = 10
}
optimizer = hill-climbing
hill-climbing {
adjust-multiplier = 10
initial-step = 0.0625
restart-threshold = 0.05
step-decay = 0.98
}
}
}
}
pattern-defaults {

View file

@ -3,13 +3,17 @@
#
# > akka-cluster-sharding/Test/runMain akka.cluster.sharding.passivation.simulator.Simulator synthetic-loop
#
# ╔══════════╤═════════╤════════════╤═════════════╤══════════════╗
# ╔═════════════════════╤═════════╤════════════╤═════════════╤══════════════╗
# ║ Run │ Active │ Accesses │ Activations │ Passivations ║
# ╠══════════╪═════════╪════════════╪═════════════╪══════════════╣
# ╠═════════════════════╪═════════╪════════════╪═════════════╪══════════════╣
# ║ LRU 500k │ 0.00 % │ 10,000,000 │ 10,000,000 │ 9,500,000 ║
# ╟──────────┼─────────┼────────────┼─────────────┼──────────────╢
# ╟─────────────────────┼─────────┼────────────┼─────────────┼──────────────╢
# ║ MRU 500k │ 45.00 % │ 10,000,000 │ 5,500,000 │ 5,000,000 ║
# ╚══════════╧═════════╧════════════╧═════════════╧══════════════╝
# ╟─────────────────────┼─────────┼────────────┼─────────────┼──────────────╢
# ║ LRU/FS/SLRU 500k │ 44.24 % │ 10,000,000 │ 5,576,284 │ 5,076,284 ║
# ╟─────────────────────┼─────────┼────────────┼─────────────┼──────────────╢
# ║ LRU/FS/SLRU/HC 500k │ 44.24 % │ 10,000,000 │ 5,576,284 │ 5,076,284 ║
# ╚═════════════════════╧═════════╧════════════╧═════════════╧══════════════╝
#
akka.cluster.sharding {
@ -28,7 +32,21 @@ akka.cluster.sharding {
regions = 10
pattern = loop-1M
strategy = mru-50k
}
},
{
name = "LRU/FS/SLRU 500k"
shards = 100
regions = 10
pattern = loop-1M
strategy = lru-fs-slru-50k
},
{
name = "LRU/FS/SLRU/HC 500k"
shards = 100
regions = 10
pattern = loop-1M
strategy = lru-fs-slru-hc-50k
},
]
print-detailed-stats = true
@ -64,5 +82,38 @@ akka.cluster.sharding {
per-region-limit = 50000
}
}
lru-fs-slru {
strategy = composite
composite {
admission {
window.strategy = least-recently-used
filter = frequency-sketch
optimizer = none
}
main {
strategy = least-recently-used
least-recently-used {
segmented {
levels = 2
proportions = [0.2, 0.8]
}
}
}
}
}
lru-fs-slru-50k = ${akka.cluster.sharding.passivation.simulator.lru-fs-slru} {
composite.per-region-limit = 50000
}
lru-fs-slru-hc = ${akka.cluster.sharding.passivation.simulator.lru-fs-slru} {
composite.admission.optimizer = hill-climbing
}
lru-fs-slru-hc-50k = ${akka.cluster.sharding.passivation.simulator.lru-fs-slru-hc} {
composite.per-region-limit = 50000
}
}
}

View file

@ -3,110 +3,182 @@
#
# > akka-cluster-sharding/Test/runMain akka.cluster.sharding.passivation.simulator.Simulator synthetic-zipfian
#
# Zipfian:
#
# ╔════════════════════╤═════════╤════════════╤═════════════╤══════════════╗
# ║ Run │ Active │ Accesses │ Activations │ Passivations ║
# ╠════════════════════╪═════════╪════════════╪═════════════╪══════════════╣
# ║ OPT 10k │ 60.36 % │ 50,000,000 │ 19,818,336 │ 19,808,336 ║
# ╟────────────────────┼─────────┼────────────┼─────────────┼──────────────╢
# ║ LRU 10k │ 46.99 % │ 50,000,000 │ 26,504,477 │ 26,494,477 ║
# ╟────────────────────┼─────────┼────────────┼─────────────┼──────────────╢
# ║ SLRU 10k │ 55.23 % │ 50,000,000 │ 22,384,620 │ 22,374,620 ║
# ╟────────────────────┼─────────┼────────────┼─────────────┼──────────────╢
# ║ S4LRU 10k │ 54.94 % │ 50,000,000 │ 22,532,034 │ 22,522,034 ║
# ╟────────────────────┼─────────┼────────────┼─────────────┼──────────────╢
# ║ MRU 10k │ 15.89 % │ 50,000,000 │ 42,054,589 │ 42,044,589 ║
# ╟────────────────────┼─────────┼────────────┼─────────────┼──────────────╢
# ║ LFU 10k │ 55.41 % │ 50,000,000 │ 22,292,520 │ 22,282,520 ║
# ╟────────────────────┼─────────┼────────────┼─────────────┼──────────────╢
# ║ LFUDA 10k │ 52.15 % │ 50,000,000 │ 23,926,057 │ 23,916,057 ║
# ╟────────────────────┼─────────┼────────────┼─────────────┼──────────────╢
# ║ LRU/FS/SLRU 10k │ 55.67 % │ 50,000,000 │ 22,167,106 │ 22,157,106 ║
# ╟────────────────────┼─────────┼────────────┼─────────────┼──────────────╢
# ║ LRU/FS/SLRU/HC 10k │ 54.43 % │ 50,000,000 │ 22,785,290 │ 22,775,290 ║
# ╚════════════════════╧═════════╧════════════╧═════════════╧══════════════╝
#
# Scrambled Zipfian:
#
# ╔════════════╤═════════╤════════════╤═════════════╤══════════════╗
# ╔════════════════════╤═════════╤════════════╤═════════════╤══════════════╗
# ║ Run │ Active │ Accesses │ Activations │ Passivations ║
# ╠════════════╪═════════╪════════════╪═════════════╪══════════════╣
# ║ OPT 100k │ 53.75 % │ 50,000,000 │ 23,125,433 │ 23,025,433 ║
# ╟────────────┼─────────┼────────────┼─────────────┼──────────────╢
# ║ LRU 100k │ 40.47 % │ 50,000,000 │ 29,764,380 │ 29,664,380 ║
# ╟────────────┼─────────┼────────────┼─────────────┼──────────────╢
# ║ SLRU 100k │ 47.14 % │ 50,000,000 │ 26,428,026 │ 26,328,026 ║
# ╟────────────┼─────────┼────────────┼─────────────┼──────────────╢
# ║ S4LRU 100k │ 47.08 % │ 50,000,000 │ 26,458,377 │ 26,358,377 ║
# ╟────────────┼─────────┼────────────┼─────────────┼──────────────╢
# ║ MRU 100k │ 10.08 % │ 50,000,000 │ 44,960,042 │ 44,860,042 ║
# ╟────────────┼─────────┼────────────┼─────────────┼──────────────╢
# ║ LFU 100k │ 46.82 % │ 50,000,000 │ 26,589,475 │ 26,489,475 ║
# ╟────────────┼─────────┼────────────┼─────────────┼──────────────╢
# ║ LFUDA 100k │ 43.66 % │ 50,000,000 │ 28,169,941 │ 28,069,941 ║
# ╚════════════╧═════════╧════════════╧═════════════╧══════════════╝
# ╠════════════════════╪═════════╪════════════╪═════════════╪══════════════╣
# ║ OPT 10k │ 41.84 % │ 50,000,000 │ 29,079,442 │ 29,069,442 ║
# ╟────────────────────┼─────────┼────────────┼─────────────┼──────────────╢
# ║ LRU 10k │ 29.79 % │ 50,000,000 │ 35,105,304 │ 35,095,304 ║
# ╟────────────────────┼─────────┼────────────┼─────────────┼──────────────╢
# ║ SLRU 10k │ 37.81 % │ 50,000,000 │ 31,095,778 │ 31,085,778 ║
# ╟────────────────────┼─────────┼────────────┼─────────────┼──────────────╢
# ║ S4LRU 10k │ 37.58 % │ 50,000,000 │ 31,208,317 │ 31,198,317 ║
# ╟────────────────────┼─────────┼────────────┼─────────────┼──────────────╢
# ║ MRU 10k │ 9.04 % │ 50,000,000 │ 45,479,628 │ 45,469,628 ║
# ╟────────────────────┼─────────┼────────────┼─────────────┼──────────────╢
# ║ LFU 10k │ 37.82 % │ 50,000,000 │ 31,088,819 │ 31,078,819 ║
# ╟────────────────────┼─────────┼────────────┼─────────────┼──────────────╢
# ║ LFUDA 10k │ 33.80 % │ 50,000,000 │ 33,098,270 │ 33,088,270 ║
# ╟────────────────────┼─────────┼────────────┼─────────────┼──────────────╢
# ║ LRU/FS/SLRU 10k │ 38.00 % │ 50,000,000 │ 30,997,711 │ 30,987,711 ║
# ╟────────────────────┼─────────┼────────────┼─────────────┼──────────────╢
# ║ LRU/FS/SLRU/HC 10k │ 36.98 % │ 50,000,000 │ 31,510,604 │ 31,500,604 ║
# ╚════════════════════╧═════════╧════════════╧═════════════╧══════════════╝
#
# Shifting Scrambled Zipfian:
#
# ╔════════════╤═════════╤════════════╤═════════════╤══════════════╗
# ╔════════════════════╤═════════╤════════════╤═════════════╤══════════════╗
# ║ Run │ Active │ Accesses │ Activations │ Passivations ║
# ╠════════════╪═════════╪════════════╪═════════════╪══════════════╣
# ║ OPT 100k │ 32.31 % │ 50,000,000 │ 33,846,785 │ 33,746,785 ║
# ╟────────────┼─────────┼────────────┼─────────────┼──────────────╢
# ║ LRU 100k │ 22.50 % │ 50,000,000 │ 38,749,623 │ 38,649,623 ║
# ╟────────────┼─────────┼────────────┼─────────────┼──────────────╢
# ║ SLRU 100k │ 22.63 % │ 50,000,000 │ 38,682,890 │ 38,582,890 ║
# ╟────────────┼─────────┼────────────┼─────────────┼──────────────╢
# ║ S4LRU 100k │ 22.58 % │ 50,000,000 │ 38,711,319 │ 38,611,319 ║
# ╟────────────┼─────────┼────────────┼─────────────┼──────────────╢
# ║ MRU 100k │ 9.45 % │ 50,000,000 │ 45,276,420 │ 45,176,420 ║
# ╟────────────┼─────────┼────────────┼─────────────┼──────────────╢
# ║ LFU 100k │ 13.23 % │ 50,000,000 │ 43,385,876 │ 43,285,876 ║
# ╟────────────┼─────────┼────────────┼─────────────┼──────────────╢
# ║ LFUDA 100k │ 22.52 % │ 50,000,000 │ 38,739,017 │ 38,639,017 ║
# ╚════════════╧═════════╧════════════╧═════════════╧══════════════╝
# ╠════════════════════╪═════════╪════════════╪═════════════╪══════════════╣
# ║ OPT 10k │ 24.98 % │ 50,000,000 │ 37,510,100 │ 37,500,100 ║
# ╟────────────────────┼─────────┼────────────┼─────────────┼──────────────╢
# ║ LRU 10k │ 21.86 % │ 50,000,000 │ 39,072,007 │ 39,062,007 ║
# ╟────────────────────┼─────────┼────────────┼─────────────┼──────────────╢
# ║ SLRU 10k │ 21.18 % │ 50,000,000 │ 39,410,297 │ 39,400,297 ║
# ╟────────────────────┼─────────┼────────────┼─────────────┼──────────────╢
# ║ S4LRU 10k │ 21.47 % │ 50,000,000 │ 39,265,652 │ 39,255,652 ║
# ╟────────────────────┼─────────┼────────────┼─────────────┼──────────────╢
# ║ MRU 10k │ 8.60 % │ 50,000,000 │ 45,699,342 │ 45,689,342 ║
# ╟────────────────────┼─────────┼────────────┼─────────────┼──────────────╢
# ║ LFU 10k │ 9.19 % │ 50,000,000 │ 45,404,024 │ 45,394,024 ║
# ╟────────────────────┼─────────┼────────────┼─────────────┼──────────────╢
# ║ LFUDA 10k │ 21.84 % │ 50,000,000 │ 39,077,569 │ 39,067,569 ║
# ╟────────────────────┼─────────┼────────────┼─────────────┼──────────────╢
# ║ LRU/FS/SLRU 10k │ 16.99 % │ 50,000,000 │ 41,503,020 │ 41,493,020 ║
# ╟────────────────────┼─────────┼────────────┼─────────────┼──────────────╢
# ║ LRU/FS/SLRU/HC 10k │ 21.39 % │ 50,000,000 │ 39,306,616 │ 39,296,616 ║
# ╚════════════════════╧═════════╧════════════╧═════════════╧══════════════╝
#
akka.cluster.sharding {
passivation.simulator {
runs = [
{
name = "OPT 100k"
name = "OPT 10k"
shards = 100
regions = 10
# pattern = zipfian
pattern = scrambled-zipfian
# pattern = shifting-scrambled-zipfian
strategy = optimal-10k
strategy = optimal-1k
},
{
name = "LRU 100k"
name = "LRU 10k"
shards = 100
regions = 10
# pattern = zipfian
pattern = scrambled-zipfian
# pattern = shifting-scrambled-zipfian
strategy = lru-10k
strategy = lru-1k
},
{
name = "SLRU 100k"
name = "SLRU 10k"
shards = 100
regions = 10
# pattern = zipfian
pattern = scrambled-zipfian
# pattern = shifting-scrambled-zipfian
strategy = slru-10k
strategy = slru-1k
},
{
name = "S4LRU 100k"
name = "S4LRU 10k"
shards = 100
regions = 10
# pattern = zipfian
pattern = scrambled-zipfian
# pattern = shifting-scrambled-zipfian
strategy = s4lru-10k
strategy = s4lru-1k
},
{
name = "MRU 100k"
name = "MRU 10k"
shards = 100
regions = 10
# pattern = zipfian
pattern = scrambled-zipfian
# pattern = shifting-scrambled-zipfian
strategy = mru-10k
strategy = mru-1k
},
{
name = "LFU 100k"
name = "LFU 10k"
shards = 100
regions = 10
# pattern = zipfian
pattern = scrambled-zipfian
# pattern = shifting-scrambled-zipfian
strategy = lfu-10k
strategy = lfu-1k
},
{
name = "LFUDA 100k"
name = "LFUDA 10k"
shards = 100
regions = 10
# pattern = zipfian
pattern = scrambled-zipfian
# pattern = shifting-scrambled-zipfian
strategy = lfuda-10k
strategy = lfuda-1k
},
{
name = "LRU/FS/SLRU 10k"
shards = 100
regions = 10
# pattern = zipfian
pattern = scrambled-zipfian
# pattern = shifting-scrambled-zipfian
strategy = lru-fs-slru-1k
},
{
name = "LRU/FS/SLRU/HC 10k"
shards = 100
regions = 10
# pattern = zipfian
pattern = scrambled-zipfian
# pattern = shifting-scrambled-zipfian
strategy = lru-fs-slru-hc-1k
},
]
print-detailed-stats = true
# zipfian distribution
# generate 50M events over 10M ids
zipfian {
pattern = synthetic
synthetic {
events = 50000000
generator = zipfian
zipfian {
min = 1
max = 10000000
scrambled = off
}
}
}
# scrambled zipfian distribution
# generate 50M events over 10M ids
scrambled-zipfian {
@ -138,30 +210,30 @@ akka.cluster.sharding {
}
}
# Optimal (clairvoyant) strategy with 10k limit in each of 10 regions
# total limit across cluster of 100k (1% of id space)
optimal-10k {
# Optimal (clairvoyant) strategy with 1k limit in each of 10 regions
# total limit across cluster of 10k (0.1% of id space)
optimal-1k {
strategy = optimal
optimal {
per-region-limit = 10000
per-region-limit = 1000
}
}
# LRU strategy with 10k limit in each of 10 regions
# total limit across cluster of 100k (1% of id space)
lru-10k {
# LRU strategy with 1k limit in each of 10 regions
# total limit across cluster of 10k (0.1% of id space)
lru-1k {
strategy = least-recently-used
least-recently-used {
per-region-limit = 10000
per-region-limit = 1000
}
}
# SLRU strategy (segmented 80% protected) with 10k limit in each of 10 regions
# total limit across cluster of 100k (1% of id space)
slru-10k {
# SLRU strategy (segmented 80% protected) with 1k limit in each of 10 regions
# total limit across cluster of 10k (0.1% of id space)
slru-1k {
strategy = least-recently-used
least-recently-used {
per-region-limit = 10000
per-region-limit = 1000
segmented {
levels = 2
proportions = [0.2, 0.8]
@ -169,42 +241,78 @@ akka.cluster.sharding {
}
}
# S4LRU strategy (segmented 4 levels) with 10k limit in each of 10 regions
# total limit across cluster of 100k (1% of id space)
s4lru-10k {
# S4LRU strategy (segmented 4 levels) with 1k limit in each of 10 regions
# total limit across cluster of 10k (0.1% of id space)
s4lru-1k {
strategy = least-recently-used
least-recently-used {
per-region-limit = 10000
per-region-limit = 1000
segmented.levels = 4
}
}
# MRU strategy with 10k limit in each of 10 regions
# total limit across cluster of 100k (1% of id space)
mru-10k {
# MRU strategy with 1k limit in each of 10 regions
# total limit across cluster of 10k (0.1% of id space)
mru-1k {
strategy = most-recently-used
most-recently-used {
per-region-limit = 10000
per-region-limit = 1000
}
}
# LFU strategy with 10k limit in each of 10 regions
# total limit across cluster of 100k (1% of id space)
lfu-10k {
# LFU strategy with 1k limit in each of 10 regions
# total limit across cluster of 10k (0.1% of id space)
lfu-1k {
strategy = least-frequently-used
least-frequently-used {
per-region-limit = 10000
per-region-limit = 1000
}
}
# LFUDA strategy (dynamic aging) with 10k limit in each of 10 regions
# total limit across cluster of 100k (1% of id space)
lfuda-10k {
# LFUDA strategy (dynamic aging) with 1k limit in each of 10 regions
# total limit across cluster of 10k (0.1% of id space)
lfuda-1k {
strategy = least-frequently-used
least-frequently-used {
per-region-limit = 10000
per-region-limit = 1000
dynamic-aging = on
}
}
# Window-TinyLFU strategy with 1k limit in each of 10 regions
# total limit across cluster of 10k (0.1% of id space)
lru-fs-slru {
strategy = composite
composite {
admission {
window.strategy = least-recently-used
filter = frequency-sketch
optimizer = none
}
main {
strategy = least-recently-used
least-recently-used {
segmented {
levels = 2
proportions = [0.2, 0.8]
}
}
}
}
}
lru-fs-slru-1k = ${akka.cluster.sharding.passivation.simulator.lru-fs-slru} {
composite.per-region-limit = 1000
}
# Adaptive (hill climbing) Window-TinyLFU strategy
lru-fs-slru-hc = ${akka.cluster.sharding.passivation.simulator.lru-fs-slru} {
composite.admission.optimizer = hill-climbing
}
lru-fs-slru-hc-1k = ${akka.cluster.sharding.passivation.simulator.lru-fs-slru-hc} {
composite.per-region-limit = 1000
}
}
}

View file

@ -5,51 +5,63 @@
#
# > akka-cluster-sharding/Test/runMain akka.cluster.sharding.passivation.simulator.Simulator text-moby-dick
#
# ╔═══════════╤═════════╤══════════╤═════════════╤══════════════╗
# ╔════════════════════╤═════════╤══════════╤═════════════╤══════════════╗
# ║ Run │ Active │ Accesses │ Activations │ Passivations ║
# ╠═══════════╪═════════╪══════════╪═════════════╪══════════════╣
# ╠════════════════════╪═════════╪══════════╪═════════════╪══════════════╣
# ║ OPT 25 │ 45.52 % │ 216,904 │ 118,161 │ 118,136 ║
# ╟───────────┼─────────┼──────────┼─────────────┼──────────────╢
# ╟────────────────────┼─────────┼──────────┼─────────────┼──────────────╢
# ║ OPT 50 │ 54.52 % │ 216,904 │ 98,658 │ 98,608 ║
# ╟───────────┼─────────┼──────────┼─────────────┼──────────────╢
# ╟────────────────────┼─────────┼──────────┼─────────────┼──────────────╢
# ║ OPT 100 │ 62.62 % │ 216,904 │ 81,073 │ 80,973 ║
# ╠═══════════╪═════════╪══════════╪═════════════╪══════════════╣
# ╠════════════════════╪═════════╪══════════╪═════════════╪══════════════╣
# ║ LRU 25 │ 21.84 % │ 216,904 │ 169,542 │ 169,517 ║
# ╟───────────┼─────────┼──────────┼─────────────┼──────────────╢
# ╟────────────────────┼─────────┼──────────┼─────────────┼──────────────╢
# ║ LRU 50 │ 32.48 % │ 216,904 │ 146,444 │ 146,394 ║
# ╟───────────┼─────────┼──────────┼─────────────┼──────────────╢
# ╟────────────────────┼─────────┼──────────┼─────────────┼──────────────╢
# ║ LRU 100 │ 43.41 % │ 216,904 │ 122,750 │ 122,650 ║
# ╠═══════════╪═════════╪══════════╪═════════════╪══════════════╣
# ╠════════════════════╪═════════╪══════════╪═════════════╪══════════════╣
# ║ SLRU 25 │ 30.22 % │ 216,904 │ 151,349 │ 151,324 ║
# ╟───────────┼─────────┼──────────┼─────────────┼──────────────╢
# ╟────────────────────┼─────────┼──────────┼─────────────┼──────────────╢
# ║ SLRU 50 │ 40.14 % │ 216,904 │ 129,845 │ 129,795 ║
# ╟───────────┼─────────┼──────────┼─────────────┼──────────────╢
# ╟────────────────────┼─────────┼──────────┼─────────────┼──────────────╢
# ║ SLRU 100 │ 50.10 % │ 216,904 │ 108,241 │ 108,141 ║
# ╠═══════════╪═════════╪══════════╪═════════════╪══════════════╣
# ║ S4LRU 25 │ 30.19 % │ 216,904 │ 151,422 │ 151,398
# ╟───────────┼─────────┼──────────┼─────────────┼──────────────╢
# ║ S4LRU 50 │ 40.15 % │ 216,904 │ 129,816 │ 129,768
# ╟───────────┼─────────┼──────────┼─────────────┼──────────────╢
# ╠════════════════════╪═════════╪══════════╪═════════════╪══════════════╣
# ║ S4LRU 25 │ 30.64 % │ 216,904 │ 150,448 │ 150,423
# ╟────────────────────┼─────────┼──────────┼─────────────┼──────────────╢
# ║ S4LRU 50 │ 40.66 % │ 216,904 │ 128,707 │ 128,657
# ╟────────────────────┼─────────┼──────────┼─────────────┼──────────────╢
# ║ S4LRU 100 │ 50.53 % │ 216,904 │ 107,304 │ 107,204 ║
# ╠═══════════╪═════════╪══════════╪═════════════╪══════════════╣
# ╠════════════════════╪═════════╪══════════╪═════════════╪══════════════╣
# ║ MRU 25 │ 0.30 % │ 216,904 │ 216,250 │ 216,225 ║
# ╟───────────┼─────────┼──────────┼─────────────┼──────────────╢
# ╟────────────────────┼─────────┼──────────┼─────────────┼──────────────╢
# ║ MRU 50 │ 0.46 % │ 216,904 │ 215,899 │ 215,849 ║
# ╟───────────┼─────────┼──────────┼─────────────┼──────────────╢
# ╟────────────────────┼─────────┼──────────┼─────────────┼──────────────╢
# ║ MRU 100 │ 0.71 % │ 216,904 │ 215,359 │ 215,259 ║
# ╠═══════════╪═════════╪══════════╪═════════════╪══════════════╣
# ╠════════════════════╪═════════╪══════════╪═════════════╪══════════════╣
# ║ LFU 25 │ 24.55 % │ 216,904 │ 163,651 │ 163,626 ║
# ╟───────────┼─────────┼──────────┼─────────────┼──────────────╢
# ╟────────────────────┼─────────┼──────────┼─────────────┼──────────────╢
# ║ LFU 50 │ 33.91 % │ 216,904 │ 143,345 │ 143,295 ║
# ╟───────────┼─────────┼──────────┼─────────────┼──────────────╢
# ╟────────────────────┼─────────┼──────────┼─────────────┼──────────────╢
# ║ LFU 100 │ 43.58 % │ 216,904 │ 122,378 │ 122,278 ║
# ╠═══════════╪═════════╪══════════╪═════════════╪══════════════╣
# ╠════════════════════╪═════════╪══════════╪═════════════╪══════════════╣
# ║ LFUDA 25 │ 30.80 % │ 216,904 │ 150,106 │ 150,081 ║
# ╟───────────┼─────────┼──────────┼─────────────┼──────────────╢
# ╟────────────────────┼─────────┼──────────┼─────────────┼──────────────╢
# ║ LFUDA 50 │ 40.90 % │ 216,904 │ 128,180 │ 128,130 ║
# ╟───────────┼─────────┼──────────┼─────────────┼──────────────╢
# ╟────────────────────┼─────────┼──────────┼─────────────┼──────────────╢
# ║ LFUDA 100 │ 50.79 % │ 216,904 │ 106,731 │ 106,631 ║
# ╚═══════════╧═════════╧══════════╧═════════════╧══════════════╝
# ╠════════════════════╪═════════╪══════════╪═════════════╪══════════════╣
# ║ LRU/FS/SLRU 25 │ 32.92 % │ 216,904 │ 145,499 │ 145,474 ║
# ╟────────────────────┼─────────┼──────────┼─────────────┼──────────────╢
# ║ LRU/FS/SLRU 50 │ 42.10 % │ 216,904 │ 125,597 │ 125,547 ║
# ╟────────────────────┼─────────┼──────────┼─────────────┼──────────────╢
# ║ LRU/FS/SLRU 100 │ 51.24 % │ 216,904 │ 105,756 │ 105,656 ║
# ╠════════════════════╪═════════╪══════════╪═════════════╪══════════════╣
# ║ LRU/FS/SLRU/HC 25 │ 30.45 % │ 216,904 │ 150,867 │ 150,842 ║
# ╟────────────────────┼─────────┼──────────┼─────────────┼──────────────╢
# ║ LRU/FS/SLRU/HC 50 │ 41.40 % │ 216,904 │ 127,098 │ 127,048 ║
# ╟────────────────────┼─────────┼──────────┼─────────────┼──────────────╢
# ║ LRU/FS/SLRU/HC 100 │ 50.97 % │ 216,904 │ 106,345 │ 106,245 ║
# ╚════════════════════╧═════════╧══════════╧═════════════╧══════════════╝
#
text-traces="text-traces"
@ -205,6 +217,48 @@ akka.cluster.sharding {
pattern = moby-dick
strategy = lfuda-100
},
{
name = "LRU/FS/SLRU 25"
shards = 1
regions = 1
pattern = moby-dick
strategy = lru-fs-slru-25
},
{
name = "LRU/FS/SLRU 50"
shards = 1
regions = 1
pattern = moby-dick
strategy = lru-fs-slru-50
},
{
name = "LRU/FS/SLRU 100"
shards = 1
regions = 1
pattern = moby-dick
strategy = lru-fs-slru-100
},
{
name = "LRU/FS/SLRU/HC 25"
shards = 1
regions = 1
pattern = moby-dick
strategy = lru-fs-slru-hc-25
},
{
name = "LRU/FS/SLRU/HC 50"
shards = 1
regions = 1
pattern = moby-dick
strategy = lru-fs-slru-hc-50
},
{
name = "LRU/FS/SLRU/HC 100"
shards = 1
regions = 1
pattern = moby-dick
strategy = lru-fs-slru-hc-100
},
]
# Moby Dick text as a trace
@ -380,5 +434,54 @@ akka.cluster.sharding {
dynamic-aging = on
}
}
lru-fs-slru {
strategy = composite
composite {
admission {
window.strategy = least-recently-used
filter = frequency-sketch
optimizer = none
}
main {
strategy = least-recently-used
least-recently-used {
segmented {
levels = 2
proportions = [0.2, 0.8]
}
}
}
}
}
lru-fs-slru-25 = ${akka.cluster.sharding.passivation.simulator.lru-fs-slru} {
composite.per-region-limit = 25
}
lru-fs-slru-50 = ${akka.cluster.sharding.passivation.simulator.lru-fs-slru} {
composite.per-region-limit = 50
}
lru-fs-slru-100 = ${akka.cluster.sharding.passivation.simulator.lru-fs-slru} {
composite.per-region-limit = 100
}
lru-fs-slru-hc = ${akka.cluster.sharding.passivation.simulator.lru-fs-slru} {
composite.admission.optimizer = hill-climbing
}
lru-fs-slru-hc-25 = ${akka.cluster.sharding.passivation.simulator.lru-fs-slru-hc} {
composite.per-region-limit = 25
}
lru-fs-slru-hc-50 = ${akka.cluster.sharding.passivation.simulator.lru-fs-slru-hc} {
composite.per-region-limit = 50
}
lru-fs-slru-hc-100 = ${akka.cluster.sharding.passivation.simulator.lru-fs-slru-hc} {
composite.per-region-limit = 100
}
}
}

View file

@ -13,21 +13,25 @@
#
# > akka-cluster-sharding/Test/runMain akka.cluster.sharding.passivation.simulator.Simulator wikipedia-trace-2018
#
# ╔════════════╤═════════╤═══════════════╤═══════════════╤═══════════════╗
# ╔═════════════════════╤═════════╤═══════════════╤═══════════════╤═══════════════╗
# ║ Run │ Active │ Accesses │ Activations │ Passivations ║
# ╠════════════╪═════════╪═══════════════╪═══════════════╪═══════════════╣
# ╠═════════════════════╪═════════╪═══════════════╪═══════════════╪═══════════════╣
# ║ LRU 100k │ 53.48 % │ 2,800,000,000 │ 1,302,519,161 │ 1,302,419,161 ║
# ╟────────────┼─────────┼───────────────┼───────────────┼───────────────╢
# ╟─────────────────────┼─────────┼───────────────┼───────────────┼───────────────╢
# ║ SLRU 100k │ 60.89 % │ 2,800,000,000 │ 1,095,063,465 │ 1,094,963,465 ║
# ╟────────────┼─────────┼───────────────┼───────────────┼───────────────╢
# ╟─────────────────────┼─────────┼───────────────┼───────────────┼───────────────╢
# ║ S4LRU 100k │ 60.66 % │ 2,800,000,000 │ 1,101,617,318 │ 1,101,517,318 ║
# ╟────────────┼─────────┼───────────────┼───────────────┼───────────────╢
# ╟─────────────────────┼─────────┼───────────────┼───────────────┼───────────────╢
# ║ MRU 100k │ 5.70 % │ 2,800,000,000 │ 2,640,279,048 │ 2,640,179,048 ║
# ╟────────────┼─────────┼───────────────┼───────────────┼───────────────╢
# ╟─────────────────────┼─────────┼───────────────┼───────────────┼───────────────╢
# ║ LFU 100k │ 58.17 % │ 2,800,000,000 │ 1,171,104,161 │ 1,171,004,161 ║
# ╟────────────┼─────────┼───────────────┼───────────────┼───────────────╢
# ╟─────────────────────┼─────────┼───────────────┼───────────────┼───────────────╢
# ║ LFUDA 100k │ 60.01 % │ 2,800,000,000 │ 1,119,687,614 │ 1,119,587,614 ║
# ╚════════════╧═════════╧═══════════════╧═══════════════╧═══════════════╝
# ╟─────────────────────┼─────────┼───────────────┼───────────────┼───────────────╢
# ║ LRU/FS/SLRU 100k │ 61.81 % │ 2,800,000,000 │ 1,069,272,588 │ 1,069,172,588 ║
# ╟─────────────────────┼─────────┼───────────────┼───────────────┼───────────────╢
# ║ LRU/FS/SLRU/HC 100k │ 60.54 % │ 2,800,000,000 │ 1,104,800,099 │ 1,104,700,099 ║
# ╚═════════════════════╧═════════╧═══════════════╧═══════════════╧═══════════════╝
#
wiki-traces="wiki-traces"
@ -36,47 +40,61 @@ wiki-traces=${?WIKI_TRACES}
akka.cluster.sharding {
passivation.simulator {
runs = [
# {
# name = "LRU 100k"
# shards = 100
# regions = 10
# pattern = wiki-2018
# strategy = lru-10k
# },
# {
# name = "SLRU 100k"
# shards = 100
# regions = 10
# pattern = wiki-2018
# strategy = slru-10k
# },
# {
# name = "S4LRU 100k"
# shards = 100
# regions = 10
# pattern = wiki-2018
# strategy = s4lru-10k
# },
# {
# name = "MRU 100k"
# shards = 100
# regions = 10
# pattern = wiki-2018
# strategy = mru-10k
# },
# {
# name = "LFU 100k"
# shards = 100
# regions = 10
# pattern = wiki-2018
# strategy = lfu-10k
# },
# {
# name = "LFUDA 100k"
# shards = 100
# regions = 10
# pattern = wiki-2018
# strategy = lfuda-10k
# },
{
name = "LRU 100k"
name = "LRU/FS/SLRU 100k"
shards = 100
regions = 10
pattern = wiki-2018
strategy = lru-10k
strategy = lru-fs-slru-10k
},
{
name = "SLRU 100k"
name = "LRU/FS/SLRU/HC 100k"
shards = 100
regions = 10
pattern = wiki-2018
strategy = slru-10k
},
{
name = "S4LRU 100k"
shards = 100
regions = 10
pattern = wiki-2018
strategy = s4lru-10k
},
{
name = "MRU 100k"
shards = 100
regions = 10
pattern = wiki-2018
strategy = mru-10k
},
{
name = "LFU 100k"
shards = 100
regions = 10
pattern = wiki-2018
strategy = lfu-10k
},
{
name = "LFUDA 100k"
shards = 100
regions = 10
pattern = wiki-2018
strategy = lfuda-10k
strategy = lru-fs-slru-hc-10k
},
]
@ -137,5 +155,38 @@ akka.cluster.sharding {
dynamic-aging = on
}
}
lru-fs-slru {
strategy = composite
composite {
admission {
window.strategy = least-recently-used
filter = frequency-sketch
optimizer = none
}
main {
strategy = least-recently-used
least-recently-used {
segmented {
levels = 2
proportions = [0.2, 0.8]
}
}
}
}
}
lru-fs-slru-10k = ${akka.cluster.sharding.passivation.simulator.lru-fs-slru} {
composite.per-region-limit = 10000
}
lru-fs-slru-hc = ${akka.cluster.sharding.passivation.simulator.lru-fs-slru} {
composite.admission.optimizer = hill-climbing
}
lru-fs-slru-hc-10k = ${akka.cluster.sharding.passivation.simulator.lru-fs-slru-hc} {
composite.per-region-limit = 10000
}
}
}

View file

@ -84,9 +84,22 @@ class ClusterShardingSettingsSpec extends AnyWordSpec with Matchers {
strategy = default-strategy
}
#passivation-new-default-strategy
""").passivationStrategy shouldBe ClusterShardingSettings.LeastRecentlyUsedPassivationStrategy(
""").passivationStrategy shouldBe ClusterShardingSettings.CompositePassivationStrategy(
limit = 100000,
segmented = List(0.2, 0.8),
mainStrategy = ClusterShardingSettings
.LeastRecentlyUsedPassivationStrategy(limit = 0, segmented = List(0.2, 0.8), idle = None),
windowStrategy =
ClusterShardingSettings.LeastRecentlyUsedPassivationStrategy(limit = 0, segmented = Nil, idle = None),
initialWindowProportion = 0.01,
minimumWindowProportion = 0.01,
maximumWindowProportion = 1.0,
windowOptimizer = ClusterShardingSettings.CompositePassivationStrategy.HillClimbingAdmissionOptimizer(
adjustMultiplier = 10.0,
initialStep = 0.0625,
restartThreshold = 0.05,
stepDecay = 0.98),
admissionFilter = ClusterShardingSettings.CompositePassivationStrategy
.FrequencySketchAdmissionFilter(widthMultiplier = 4, resetMultiplier = 10.0, depth = 4, counterBits = 4),
idle = None)
}
@ -100,9 +113,22 @@ class ClusterShardingSettingsSpec extends AnyWordSpec with Matchers {
}
}
#passivation-new-default-strategy-configured
""").passivationStrategy shouldBe ClusterShardingSettings.LeastRecentlyUsedPassivationStrategy(
""").passivationStrategy shouldBe ClusterShardingSettings.CompositePassivationStrategy(
limit = 1000000,
segmented = List(0.2, 0.8),
mainStrategy = ClusterShardingSettings
.LeastRecentlyUsedPassivationStrategy(limit = 0, segmented = List(0.2, 0.8), idle = None),
windowStrategy =
ClusterShardingSettings.LeastRecentlyUsedPassivationStrategy(limit = 0, segmented = Nil, idle = None),
initialWindowProportion = 0.01,
minimumWindowProportion = 0.01,
maximumWindowProportion = 1.0,
windowOptimizer = ClusterShardingSettings.CompositePassivationStrategy.HillClimbingAdmissionOptimizer(
adjustMultiplier = 10.0,
initialStep = 0.0625,
restartThreshold = 0.05,
stepDecay = 0.98),
admissionFilter = ClusterShardingSettings.CompositePassivationStrategy
.FrequencySketchAdmissionFilter(widthMultiplier = 4, resetMultiplier = 10.0, depth = 4, counterBits = 4),
idle = None)
}
@ -116,9 +142,22 @@ class ClusterShardingSettingsSpec extends AnyWordSpec with Matchers {
}
}
#passivation-new-default-strategy-with-idle
""").passivationStrategy shouldBe ClusterShardingSettings.LeastRecentlyUsedPassivationStrategy(
""").passivationStrategy shouldBe ClusterShardingSettings.CompositePassivationStrategy(
limit = 100000,
segmented = List(0.2, 0.8),
mainStrategy = ClusterShardingSettings
.LeastRecentlyUsedPassivationStrategy(limit = 0, segmented = List(0.2, 0.8), idle = None),
windowStrategy =
ClusterShardingSettings.LeastRecentlyUsedPassivationStrategy(limit = 0, segmented = Nil, idle = None),
initialWindowProportion = 0.01,
minimumWindowProportion = 0.01,
maximumWindowProportion = 1.0,
windowOptimizer = ClusterShardingSettings.CompositePassivationStrategy.HillClimbingAdmissionOptimizer(
adjustMultiplier = 10.0,
initialStep = 0.0625,
restartThreshold = 0.05,
stepDecay = 0.98),
admissionFilter = ClusterShardingSettings.CompositePassivationStrategy
.FrequencySketchAdmissionFilter(widthMultiplier = 4, resetMultiplier = 10.0, depth = 4, counterBits = 4),
idle = Some(ClusterShardingSettings.IdlePassivationStrategy(timeout = 30.minutes, interval = 15.minutes)))
}
@ -389,6 +428,356 @@ class ClusterShardingSettingsSpec extends AnyWordSpec with Matchers {
idle = None)
}
"allow passivation strategy admission window policy to be configured (via config)" in {
settings("""
#admission-window-policy
akka.cluster.sharding.passivation {
strategy = custom-strategy-with-admission-window
custom-strategy-with-admission-window {
active-entity-limit = 1000000
admission.window.policy = least-recently-used
replacement.policy = least-frequently-used
}
}
#admission-window-policy
""").passivationStrategy shouldBe ClusterShardingSettings.CompositePassivationStrategy(
limit = 1000000,
mainStrategy =
ClusterShardingSettings.LeastFrequentlyUsedPassivationStrategy(limit = 0, dynamicAging = false, idle = None),
windowStrategy =
ClusterShardingSettings.LeastRecentlyUsedPassivationStrategy(limit = 0, segmented = Nil, idle = None),
initialWindowProportion = 0.01,
minimumWindowProportion = 0.01,
maximumWindowProportion = 1.0,
windowOptimizer = ClusterShardingSettings.CompositePassivationStrategy.NoAdmissionOptimizer,
admissionFilter = ClusterShardingSettings.CompositePassivationStrategy.AlwaysAdmissionFilter,
idle = None)
}
"allow passivation strategy admission window proportion to be configured (via config)" in {
settings("""
#admission-window-proportion
akka.cluster.sharding.passivation {
strategy = custom-strategy-with-admission-window
custom-strategy-with-admission-window {
active-entity-limit = 1000000
admission.window {
policy = least-recently-used
proportion = 0.1 # 10%
}
replacement.policy = least-frequently-used
}
}
#admission-window-proportion
""").passivationStrategy shouldBe ClusterShardingSettings.CompositePassivationStrategy(
limit = 1000000,
mainStrategy =
ClusterShardingSettings.LeastFrequentlyUsedPassivationStrategy(limit = 0, dynamicAging = false, idle = None),
windowStrategy =
ClusterShardingSettings.LeastRecentlyUsedPassivationStrategy(limit = 0, segmented = Nil, idle = None),
initialWindowProportion = 0.1,
minimumWindowProportion = 0.01,
maximumWindowProportion = 1.0,
windowOptimizer = ClusterShardingSettings.CompositePassivationStrategy.NoAdmissionOptimizer,
admissionFilter = ClusterShardingSettings.CompositePassivationStrategy.AlwaysAdmissionFilter,
idle = None)
}
"allow passivation strategy admission window optimizer to be configured (via config)" in {
settings("""
#admission-window-optimizer
akka.cluster.sharding.passivation {
strategy = custom-strategy-with-admission-window
custom-strategy-with-admission-window {
active-entity-limit = 1000000
admission.window {
policy = least-recently-used
optimizer = hill-climbing
}
replacement.policy = least-frequently-used
}
}
#admission-window-optimizer
""").passivationStrategy shouldBe ClusterShardingSettings.CompositePassivationStrategy(
limit = 1000000,
mainStrategy =
ClusterShardingSettings.LeastFrequentlyUsedPassivationStrategy(limit = 0, dynamicAging = false, idle = None),
windowStrategy =
ClusterShardingSettings.LeastRecentlyUsedPassivationStrategy(limit = 0, segmented = Nil, idle = None),
initialWindowProportion = 0.01,
minimumWindowProportion = 0.01,
maximumWindowProportion = 1.0,
windowOptimizer = ClusterShardingSettings.CompositePassivationStrategy.HillClimbingAdmissionOptimizer(
adjustMultiplier = 10.0,
initialStep = 0.0625,
restartThreshold = 0.05,
stepDecay = 0.98),
admissionFilter = ClusterShardingSettings.CompositePassivationStrategy.AlwaysAdmissionFilter,
idle = None)
}
"allow passivation strategy admission to be configured (via config)" in {
settings("""
#admission-policy
akka.cluster.sharding.passivation {
strategy = custom-strategy-with-admission
custom-strategy-with-admission {
active-entity-limit = 1000000
admission {
window {
policy = least-recently-used
optimizer = hill-climbing
}
filter = frequency-sketch
}
replacement {
policy = least-recently-used
least-recently-used {
segmented {
levels = 2
proportions = [0.2, 0.8]
}
}
}
}
}
#admission-policy
""").passivationStrategy shouldBe ClusterShardingSettings.CompositePassivationStrategy(
limit = 1000000,
mainStrategy = ClusterShardingSettings
.LeastRecentlyUsedPassivationStrategy(limit = 0, segmented = List(0.2, 0.8), idle = None),
windowStrategy =
ClusterShardingSettings.LeastRecentlyUsedPassivationStrategy(limit = 0, segmented = Nil, idle = None),
initialWindowProportion = 0.01,
minimumWindowProportion = 0.01,
maximumWindowProportion = 1.0,
windowOptimizer = ClusterShardingSettings.CompositePassivationStrategy.HillClimbingAdmissionOptimizer(
adjustMultiplier = 10.0,
initialStep = 0.0625,
restartThreshold = 0.05,
stepDecay = 0.98),
admissionFilter = ClusterShardingSettings.CompositePassivationStrategy
.FrequencySketchAdmissionFilter(widthMultiplier = 4, resetMultiplier = 10.0, depth = 4, counterBits = 4),
idle = None)
}
"allow passivation strategy admission parameters to be tuned (via config)" in {
settings("""
akka.cluster.sharding.passivation {
strategy = custom-strategy-with-admission
custom-strategy-with-admission {
active-entity-limit = 1000000
admission {
window {
policy = least-recently-used
proportion = 0.1
minimum-proportion = 0.05
maximum-proportion = 0.50
optimizer = hill-climbing
hill-climbing {
adjust-multiplier = 5
initial-step = 0.05
restart-threshold = 0.025
step-decay = 0.95
}
}
filter = frequency-sketch
frequency-sketch {
depth = 3
counter-bits = 8
width-multiplier = 2
reset-multiplier = 50.0
}
}
replacement {
policy = least-recently-used
least-recently-used {
segmented {
levels = 2
proportions = [0.2, 0.8]
}
}
}
}
}
""").passivationStrategy shouldBe ClusterShardingSettings.CompositePassivationStrategy(
limit = 1000000,
mainStrategy = ClusterShardingSettings
.LeastRecentlyUsedPassivationStrategy(limit = 0, segmented = List(0.2, 0.8), idle = None),
windowStrategy =
ClusterShardingSettings.LeastRecentlyUsedPassivationStrategy(limit = 0, segmented = Nil, idle = None),
initialWindowProportion = 0.1,
minimumWindowProportion = 0.05,
maximumWindowProportion = 0.5,
windowOptimizer = ClusterShardingSettings.CompositePassivationStrategy.HillClimbingAdmissionOptimizer(
adjustMultiplier = 5.0,
initialStep = 0.05,
restartThreshold = 0.025,
stepDecay = 0.95),
admissionFilter = ClusterShardingSettings.CompositePassivationStrategy
.FrequencySketchAdmissionFilter(widthMultiplier = 2, resetMultiplier = 50.0, depth = 3, counterBits = 8),
idle = None)
}
"allow passivation strategy admission to be configured (via factory method)" in {
defaultSettings
.withPassivationStrategy(
ClusterShardingSettings.PassivationStrategySettings.defaults
.withActiveEntityLimit(42000)
.withAdmission(ClusterShardingSettings.PassivationStrategySettings.AdmissionSettings.defaults
.withWindow(ClusterShardingSettings.PassivationStrategySettings.AdmissionSettings.WindowSettings.defaults
.withPolicy(ClusterShardingSettings.PassivationStrategySettings.LeastRecentlyUsedSettings.defaults)
.withOptimizer(
ClusterShardingSettings.PassivationStrategySettings.AdmissionSettings.HillClimbingSettings.defaults))
.withFilter(
ClusterShardingSettings.PassivationStrategySettings.AdmissionSettings.FrequencySketchSettings.defaults))
.withReplacementPolicy(
ClusterShardingSettings.PassivationStrategySettings.LeastRecentlyUsedSettings.defaults
.withSegmented(proportions = List(0.2, 0.8))))
.passivationStrategy shouldBe ClusterShardingSettings.CompositePassivationStrategy(
limit = 42000,
mainStrategy = ClusterShardingSettings
.LeastRecentlyUsedPassivationStrategy(limit = 0, segmented = List(0.2, 0.8), idle = None),
windowStrategy =
ClusterShardingSettings.LeastRecentlyUsedPassivationStrategy(limit = 0, segmented = Nil, idle = None),
initialWindowProportion = 0.01,
minimumWindowProportion = 0.01,
maximumWindowProportion = 1.0,
windowOptimizer = ClusterShardingSettings.CompositePassivationStrategy.HillClimbingAdmissionOptimizer(
adjustMultiplier = 10.0,
initialStep = 0.0625,
restartThreshold = 0.05,
stepDecay = 0.98),
admissionFilter = ClusterShardingSettings.CompositePassivationStrategy
.FrequencySketchAdmissionFilter(widthMultiplier = 4, resetMultiplier = 10.0, depth = 4, counterBits = 4),
idle = None)
}
"allow passivation strategy admission settings to be tuned (via factory method)" in {
defaultSettings
.withPassivationStrategy(
ClusterShardingSettings.PassivationStrategySettings.defaults
.withActiveEntityLimit(42000)
.withAdmission(ClusterShardingSettings.PassivationStrategySettings.AdmissionSettings.defaults
.withWindow(
ClusterShardingSettings.PassivationStrategySettings.AdmissionSettings.WindowSettings.defaults
.withPolicy(ClusterShardingSettings.PassivationStrategySettings.LeastRecentlyUsedSettings.defaults)
.withInitialProportion(0.1)
.withMinimumProportion(0.05)
.withMaximumProportion(0.5)
.withOptimizer(
ClusterShardingSettings.PassivationStrategySettings.AdmissionSettings.HillClimbingSettings.defaults
.withAdjustMultiplier(5)
.withInitialStep(0.05)
.withRestartThreshold(0.025)
.withStepDecay(0.95)))
.withFilter(
ClusterShardingSettings.PassivationStrategySettings.AdmissionSettings.FrequencySketchSettings.defaults
.withDepth(3)
.withCounterBits(8)
.withWidthMultiplier(2)
.withResetMultiplier(50)))
.withReplacementPolicy(
ClusterShardingSettings.PassivationStrategySettings.LeastRecentlyUsedSettings.defaults
.withSegmented(proportions = List(0.2, 0.8))))
.passivationStrategy shouldBe ClusterShardingSettings.CompositePassivationStrategy(
limit = 42000,
mainStrategy = ClusterShardingSettings
.LeastRecentlyUsedPassivationStrategy(limit = 0, segmented = List(0.2, 0.8), idle = None),
windowStrategy =
ClusterShardingSettings.LeastRecentlyUsedPassivationStrategy(limit = 0, segmented = Nil, idle = None),
initialWindowProportion = 0.1,
minimumWindowProportion = 0.05,
maximumWindowProportion = 0.5,
windowOptimizer = ClusterShardingSettings.CompositePassivationStrategy.HillClimbingAdmissionOptimizer(
adjustMultiplier = 5.0,
initialStep = 0.05,
restartThreshold = 0.025,
stepDecay = 0.95),
admissionFilter = ClusterShardingSettings.CompositePassivationStrategy
.FrequencySketchAdmissionFilter(widthMultiplier = 2, resetMultiplier = 50.0, depth = 3, counterBits = 8),
idle = None)
}
"allow passivation strategy with admission and idle timeout to be configured (via config)" in {
settings("""
akka.cluster.sharding.passivation {
strategy = custom-strategy-with-admission
custom-strategy-with-admission {
active-entity-limit = 1000000
admission {
window {
policy = least-recently-used
optimizer = hill-climbing
}
filter = frequency-sketch
}
replacement {
policy = least-recently-used
least-recently-used {
segmented {
levels = 2
proportions = [0.2, 0.8]
}
}
}
idle-entity.timeout = 30.minutes
}
}
""").passivationStrategy shouldBe ClusterShardingSettings.CompositePassivationStrategy(
limit = 1000000,
mainStrategy = ClusterShardingSettings
.LeastRecentlyUsedPassivationStrategy(limit = 0, segmented = List(0.2, 0.8), idle = None),
windowStrategy =
ClusterShardingSettings.LeastRecentlyUsedPassivationStrategy(limit = 0, segmented = Nil, idle = None),
initialWindowProportion = 0.01,
minimumWindowProportion = 0.01,
maximumWindowProportion = 1.0,
windowOptimizer = ClusterShardingSettings.CompositePassivationStrategy.HillClimbingAdmissionOptimizer(
adjustMultiplier = 10.0,
initialStep = 0.0625,
restartThreshold = 0.05,
stepDecay = 0.98),
admissionFilter = ClusterShardingSettings.CompositePassivationStrategy
.FrequencySketchAdmissionFilter(widthMultiplier = 4, resetMultiplier = 10.0, depth = 4, counterBits = 4),
idle = Some(ClusterShardingSettings.IdlePassivationStrategy(timeout = 30.minutes, interval = 15.minutes)))
}
"allow passivation strategy with admission and idle timeout to be configured (via factory method)" in {
defaultSettings
.withPassivationStrategy(
ClusterShardingSettings.PassivationStrategySettings.defaults
.withActiveEntityLimit(42000)
.withAdmission(ClusterShardingSettings.PassivationStrategySettings.AdmissionSettings.defaults
.withWindow(ClusterShardingSettings.PassivationStrategySettings.AdmissionSettings.WindowSettings.defaults
.withPolicy(ClusterShardingSettings.PassivationStrategySettings.LeastRecentlyUsedSettings.defaults)
.withOptimizer(
ClusterShardingSettings.PassivationStrategySettings.AdmissionSettings.HillClimbingSettings.defaults))
.withFilter(
ClusterShardingSettings.PassivationStrategySettings.AdmissionSettings.FrequencySketchSettings.defaults))
.withReplacementPolicy(
ClusterShardingSettings.PassivationStrategySettings.LeastRecentlyUsedSettings.defaults.withSegmented(
proportions = List(0.2, 0.8)))
.withIdleEntityPassivation(timeout = 42.minutes))
.passivationStrategy shouldBe ClusterShardingSettings.CompositePassivationStrategy(
limit = 42000,
mainStrategy = ClusterShardingSettings
.LeastRecentlyUsedPassivationStrategy(limit = 0, segmented = List(0.2, 0.8), idle = None),
windowStrategy =
ClusterShardingSettings.LeastRecentlyUsedPassivationStrategy(limit = 0, segmented = Nil, idle = None),
initialWindowProportion = 0.01,
minimumWindowProportion = 0.01,
maximumWindowProportion = 1.0,
windowOptimizer = ClusterShardingSettings.CompositePassivationStrategy.HillClimbingAdmissionOptimizer(
adjustMultiplier = 10.0,
initialStep = 0.0625,
restartThreshold = 0.05,
stepDecay = 0.98),
admissionFilter = ClusterShardingSettings.CompositePassivationStrategy
.FrequencySketchAdmissionFilter(widthMultiplier = 4, resetMultiplier = 10.0, depth = 4, counterBits = 4),
idle = Some(ClusterShardingSettings.IdlePassivationStrategy(timeout = 42.minutes, interval = 21.minutes)))
}
"disable automatic passivation if `remember-entities` is enabled (via config)" in {
settings("""
akka.cluster.sharding.remember-entities = on

View file

@ -0,0 +1,479 @@
/*
* Copyright (C) 2009-2022 Lightbend Inc. <https://www.lightbend.com>
*/
package akka.cluster.sharding.passivation
import akka.cluster.sharding.ShardRegion
import com.typesafe.config.Config
import com.typesafe.config.ConfigFactory
import scala.concurrent.duration._
object CompositeSpec {
val admissionWindowAndFilterConfig: Config = ConfigFactory.parseString("""
akka.cluster.sharding {
passivation {
strategy = lru-fs-slru
lru-fs-slru {
active-entity-limit = 20
admission {
window {
policy = least-recently-used
proportion = 0.2
optimizer = none
}
filter = frequency-sketch
}
replacement {
policy = least-recently-used
least-recently-used {
segmented {
levels = 2
proportions = [0.25, 0.75]
}
}
}
}
}
}
""").withFallback(EntityPassivationSpec.config)
val admissionFilterNoWindowConfig: Config = ConfigFactory.parseString("""
akka.cluster.sharding {
passivation {
strategy = fs-lru
fs-lru {
active-entity-limit = 10
admission {
window.policy = none
filter = frequency-sketch
}
replacement.policy = least-recently-used
}
}
}
""").withFallback(EntityPassivationSpec.config)
val adaptiveWindowConfig: Config = ConfigFactory.parseString("""
akka.cluster.sharding {
passivation {
strategy = lru-fs-lru-hc
lru-fs-lru-hc {
active-entity-limit = 10
admission {
window {
policy = least-recently-used
proportion = 0.1
minimum-proportion = 0.1
maximum-proportion = 1.0
optimizer = hill-climbing
hill-climbing {
adjust-multiplier = 2
initial-step = 0.4 # big step for testing
restart-threshold = 0.8 # high threshold for testing
step-decay = 0.5 # fast decay for testing
}
}
filter = frequency-sketch
frequency-sketch {
reset-multiplier = 100
}
}
replacement.policy = least-recently-used
}
}
}
""").withFallback(EntityPassivationSpec.config)
val idleConfig: Config = ConfigFactory.parseString("""
akka.cluster.sharding {
passivation {
strategy = default-strategy
default-strategy {
active-entity-limit = 3
idle-entity.timeout = 1s
}
}
}
""").withFallback(EntityPassivationSpec.config)
}
class AdmissionWindowAndFilterSpec
extends AbstractEntityPassivationSpec(CompositeSpec.admissionWindowAndFilterConfig, expectedEntities = 41) {
import EntityPassivationSpec.Entity.Envelope
import EntityPassivationSpec.Entity.Stop
"Passivation of entities with composite LRU/FS/SLRU strategy" must {
"passivate the least recently or frequently used entities when the per-shard entity limit is reached" in {
val region = start()
// only one active shard at first
// entities move through the LRU window before moving to main
// candidate entities from window passivated due to admission filter (and same frequency)
// entities are only accessed once (so all in lowest segment)
for (id <- 1 to 40) {
region ! Envelope(shard = 1, id = id, message = "A")
expectReceived(id = id, message = "A")
if (id > 20) expectReceived(id = id - 4, message = Stop)
}
// shard 1: window: 37-40, main level 0: 1-16, main level 1: empty
expectState(region)(1 -> ((1 to 16) ++ (37 to 40)))
// increase the frequency of the entities in the main area
// accessing entities a second time moves them to the higher "protected" segment in main area
// when the limit for higher segment is reached, entities are demoted to lower "probationary" segment
// any activated entities will passivate candidates from the window (which have lower frequency)
for (id <- 1 to 20) {
region ! Envelope(shard = 1, id = id, message = "B")
expectReceived(id = id, message = "B")
if (id > 16) expectReceived(id = id + 20, message = Stop)
}
// shard 1: window: 17-20, main level 0: 1-4, level 1: 5-16
expectState(region)(1 -> (1 to 20))
// cycle through the entities in the window to increase their counts in the frequency sketch
for (id <- 17 to 20; _ <- 1 to 5) {
region ! Envelope(shard = 1, id = id, message = "C")
expectReceived(id = id, message = "C")
}
// shard 1: window: 17-20, main level 0: 1-4, level 1: 5-16
expectState(region)(1 -> (1 to 20))
// activating new entities will promote candidates from the window to main (higher frequencies now)
for (id <- 21 to 24) {
region ! Envelope(shard = 1, id = id, message = "D")
expectReceived(id = id, message = "D")
expectReceived(id = id - 20, message = Stop)
}
// shard 1: window: 21-24, main level 0: 17-20, level 1: 5-16
expectState(region)(1 -> (5 to 24))
// activating more new entities will passivate candidates from the window (lower frequencies than main)
for (id <- 25 to 28) {
region ! Envelope(shard = 1, id = id, message = "E")
expectReceived(id = id, message = "E")
expectReceived(id = id - 4, message = Stop)
}
// shard 1: window: 25-28, main level 0: 17-20, level 1: 5-16
expectState(region)(1 -> ((5 to 20) ++ (25 to 28)))
// activating a second shard will divide the per-shard limit in two, passivating half of the first shard
region ! Envelope(shard = 2, id = 41, message = "F")
expectReceived(id = 41, message = "F")
for (id <- List(25, 26, 17, 18, 19, 20, 5, 6, 7, 8)) {
expectReceived(id = id, message = Stop)
}
// shard 1: window: 27-28, main level 0: 9-10, level 1: 11-16
// shard 2: window: 41, main level 0: empty, level 1: empty
expectState(region)(1 -> ((9 to 16) ++ (27 to 28)), 2 -> Set(41))
// ids 17 and 18 still have higher frequencies, will be promoted through to main from window
for (id <- 17 to 20) {
region ! Envelope(shard = 1, id = id, message = "G")
expectReceived(id = id, message = "G")
if (id == 17) expectReceived(id = 27, message = Stop)
if (id == 18) expectReceived(id = 28, message = Stop)
if (id == 19) expectReceived(id = 9, message = Stop)
if (id == 20) expectReceived(id = 10, message = Stop)
}
// shard 1: window: 19-20, main level 0: 17-18, level 1: 11-16
// shard 2: window: 41, main level 0: empty, level 1: empty
expectState(region)(1 -> (11 to 20), 2 -> Set(41))
}
}
}
class AdmissionFilterNoWindowSpec
extends AbstractEntityPassivationSpec(CompositeSpec.admissionFilterNoWindowConfig, expectedEntities = 21) {
import EntityPassivationSpec.Entity.Envelope
import EntityPassivationSpec.Entity.Stop
"Passivation of entities with composite (no window) FS/LRU strategy" must {
"passivate the least recently or frequently used entities when the per-shard entity limit is reached" in {
val region = start()
// only one active shard at first
// as entities have same frequency, when limit is reached the candidate entities (11-20) are not admitted to main
for (id <- 1 to 20) {
region ! Envelope(shard = 1, id = id, message = "A")
expectReceived(id = id, message = "A")
if (id > 10) expectReceived(id = id, message = Stop)
}
// shard 1: 1-10
expectState(region)(1 -> (1 to 10))
// entities 11-20 on second access, so admitted to main, replacing current entities
// cycle through multiple times to increase frequency counts
for (id <- 11 to 20; i <- 1 to 5) {
region ! Envelope(shard = 1, id = id, message = "B")
expectReceived(id = id, message = "B")
if (i == 1) expectReceived(id = id - 10, message = Stop)
}
// shard 1: 11-20
expectState(region)(1 -> (11 to 20))
// entities 1-10 on second and third access, but not admitted to main as lower frequency
for (id <- 1 to 10; _ <- 1 to 2) {
region ! Envelope(shard = 1, id = id, message = "C")
expectReceived(id = id, message = "C")
expectReceived(id = id, message = Stop)
}
// activating a second shard will divide the per-shard limit in two, passivating half of the first shard
region ! Envelope(shard = 2, id = 21, message = "D")
expectReceived(id = 21, message = "D")
for (id <- 11 to 15) {
expectReceived(id = id, message = Stop)
}
// shard 1: 16-20, shard 2: 21
expectState(region)(1 -> (16 to 20), 2 -> Set(21))
// frequency sketch is recreated on limit changes, so all frequencies are 0 again
// candidate entities will replace old entities, but then retain their position
for (id <- 1 to 10) {
region ! Envelope(shard = 1, id = id, message = "E")
expectReceived(id = id, message = "E")
if (id <= 5) expectReceived(id = id + 15, message = Stop)
}
// shard 1: 16-20, shard 2: 21
expectState(region)(1 -> (1 to 5), 2 -> Set(21))
}
}
}
class AdaptiveAdmissionWindowSpec
extends AbstractEntityPassivationSpec(CompositeSpec.adaptiveWindowConfig, expectedEntities = 70) {
import EntityPassivationSpec.Entity.Envelope
import EntityPassivationSpec.Entity.Stop
"Passivation of entities with composite LRU/FS/LRU/HC strategy" must {
"adjust the admission window size when optimizing with hill-climbing algorithm" in {
val region = start()
// fill admission window and main areas
// increase frequency counts for entities 1-10
for (id <- 1 to 10; i <- 1 to 10) {
region ! Envelope(shard = 1, id = id, message = s"A$i")
expectReceived(id = id, message = s"A$i")
}
expectState(region)(1 -> (1 to 10))
// window = [10], main = [1, 2, 3, 4, 5, 6, 7, 8, 9]
// window limit is currently 1 (window proportion of 0.1)
// these entities will be passivated as they have lower frequencies
// given drop in active rate the adjustment direction flips to increased (for next step)
// so that window proportion is increased by step of 0.4 (to window limit of 5) on next cycle
for (i <- 1 to 4; id <- 11 to 20) {
region ! Envelope(shard = 1, id = id, message = s"B$i")
expectReceived(id = id, message = s"B$i")
val passivated = if (id == 11 && i > 1) 20 else id - 1
expectReceived(id = passivated, message = Stop)
}
expectState(region)(1 -> ((1 to 9) ++ Set(20)))
// window = [20, 1, 2, 3, 4], main = [5, 6, 7, 8, 9]
// window limit is currently 5 (window proportion of 0.5), direction is increasing
// continue to maintain positive active rate delta to increase window by decayed step of 0.2
for (id <- 21 to 30; i <- 1 to 2) {
region ! Envelope(shard = 1, id = id, message = s"C$i")
expectReceived(id = id, message = s"C$i")
if (i == 1) {
val passivated = id match {
case 21 => 20 // in window from previous loop
case x if x <= 25 => x - 21 // demoted from main
case x => x - 5 // window size
}
expectReceived(id = passivated, message = Stop)
}
}
expectState(region)(1 -> ((5 to 9) ++ (26 to 30)))
// window = [26, 27, 28, 29, 30, 5, 6], main = [7, 8, 9]
// window proportion is currently 0.7, direction is increasing
// continue to maintain positive active rate delta to increase window by decayed step of 0.1
// increase to high active rate to trigger subsequent restart on delta over threshold
for (id <- 21 to 24; i <- 1 to 5) {
region ! Envelope(shard = 1, id = id, message = s"D$i")
expectReceived(id = id, message = s"D$i")
if (i == 1) expectReceived(id = id + 5, message = Stop)
}
expectState(region)(1 -> ((5 to 9) ++ (21 to 24) ++ Set(30)))
// window = [30, 5, 6, 21, 22, 23, 24, 7], main = [8, 9]
// window proportion is currently 0.8, direction is increasing
// drop the active rate to zero to trigger a direction change (decrease of 0.05 to window of 7)
// and hill-climbing restart triggered for next step (change is over restart threshold)
for (i <- 1 to 2; id <- 31 to 40) {
region ! Envelope(shard = 1, id = id, message = s"E$i")
expectReceived(id = id, message = s"E$i")
val previousWindow = IndexedSeq(30, 5, 6, 21, 22, 23, 24, 7)
val passivated = {
if (i == 1 && id <= 38) previousWindow(id - 31)
else if (id <= 38) id + 2
else id - 8 // window size
}
expectReceived(id = passivated, message = Stop)
}
expectState(region)(1 -> ((8 to 9) ++ (33 to 40)))
// window = [34, 35, 36, 37, 38, 39, 40], main = [8, 9, 33]
// window proportion is currently 0.7, direction is decreasing and restarted at initial step
// stable or increasing active rate will keep decreasing window now (step of -0.4)
for (id <- 41 to 50; i <- 1 to 2) {
region ! Envelope(shard = 1, id = id, message = s"F$i")
expectReceived(id = id, message = s"F$i")
if (i == 1) expectReceived(id = id - 7 /* window size */, message = Stop)
}
expectState(region)(1 -> ((8 to 9) ++ Set(33) ++ (44 to 50)))
// window = [48, 49, 50], main = [8, 9, 33, 44, 45, 46, 47]
// window proportion is currently 0.3, direction is decreasing, with next step decayed to -0.2
for (id <- 51 to 60; i <- 1 to 2) {
region ! Envelope(shard = 1, id = id, message = s"G$i")
expectReceived(id = id, message = s"G$i")
if (i == 1) expectReceived(id = id - 3 /* window size */, message = Stop)
}
expectState(region)(1 -> (Set(60) ++ Set(8, 9, 33, 44, 45, 46, 47, 58, 59)))
// window = [60], main = [8, 9, 33, 44, 45, 46, 47, 58, 59]
// window proportion is currently 0.1, direction is decreasing, with next step decayed to -0.1
for (id <- 61 to 70) {
region ! Envelope(shard = 1, id = id, message = s"H")
expectReceived(id = id, message = s"H")
expectReceived(id = id - 1 /* window size */, message = Stop)
}
expectState(region)(1 -> (Set(70) ++ Set(8, 9, 33, 44, 45, 46, 47, 58, 59)))
}
}
}
class CompositeWithIdleSpec extends AbstractEntityPassivationSpec(CompositeSpec.idleConfig, expectedEntities = 3) {
import EntityPassivationSpec.Entity.Envelope
import EntityPassivationSpec.Entity.Stop
"Passivation of idle entities with least recently used strategy" must {
"passivate entities when they haven't seen messages for the configured timeout" in {
val region = start()
val lastSendNanoTime1 = System.nanoTime()
region ! Envelope(shard = 1, id = 1, message = "A")
region ! Envelope(shard = 1, id = 2, message = "B")
// keep entity 3 active to prevent idle passivation
region ! Envelope(shard = 1, id = 3, message = "C")
Thread.sleep((configuredIdleTimeout / 2).toMillis)
region ! Envelope(shard = 1, id = 3, message = "D")
Thread.sleep((configuredIdleTimeout / 2).toMillis)
region ! Envelope(shard = 1, id = 3, message = "E")
Thread.sleep((configuredIdleTimeout / 2).toMillis)
val lastSendNanoTime2 = System.nanoTime()
region ! Envelope(shard = 1, id = 3, message = "F")
expectReceived(id = 1, message = "A")
expectReceived(id = 2, message = "B")
expectReceived(id = 3, message = "C")
expectReceived(id = 3, message = "D")
expectReceived(id = 3, message = "E")
expectReceived(id = 3, message = "F")
val passivate1 = expectReceived(id = 1, message = Stop)
val passivate2 = expectReceived(id = 2, message = Stop)
val passivate3 = expectReceived(id = 3, message = Stop, within = configuredIdleTimeout * 2)
// note: touched timestamps are when the shard receives the message, not the entity itself
// so look at the time from before sending the last message until receiving the passivate message
(passivate1.nanoTime - lastSendNanoTime1).nanos should be > configuredIdleTimeout
(passivate2.nanoTime - lastSendNanoTime1).nanos should be > configuredIdleTimeout
(passivate3.nanoTime - lastSendNanoTime2).nanos should be > configuredIdleTimeout
}
}
}
class CompositeLimitAdjustmentSpec
extends AbstractEntityPassivationSpec(CompositeSpec.admissionWindowAndFilterConfig, expectedEntities = 41) {
import EntityPassivationSpec.Entity.Envelope
import EntityPassivationSpec.Entity.Stop
"Passivation of least recently used entities" must {
"adjust per-shard entity limits when the per-region limit is dynamically adjusted" in {
val region = start()
// only one active shard at first, initial per-shard limit of 20
// entities move through the LRU window before moving to main
// candidate entities from window passivated due to admission filter (and same frequency)
for (id <- 1 to 40) {
region ! Envelope(shard = 1, id = id, message = "A")
expectReceived(id = id, message = "A")
if (id > 20) expectReceived(id = id - 4, message = Stop)
}
expectState(region)(1 -> ((1 to 16) ++ (37 to 40)))
// activating a second shard will divide the per-shard limit in two, passivating half of the first shard
region ! Envelope(shard = 2, id = 41, message = "B")
expectReceived(id = 41, message = "B")
for (id <- List(37, 38) ++ (1 to 8)) {
expectReceived(id = id, message = Stop)
}
expectState(region)(1 -> ((9 to 16) ++ (39 to 40)), 2 -> Set(41))
// reduce the per-region limit from 20 to 10, per-shard limit becomes 5
region ! ShardRegion.SetActiveEntityLimit(10)
for (id <- 39 +: (9 to 12)) { // passivate entities over new limit
expectReceived(id = id, message = Stop)
}
expectState(region)(1 -> ((13 to 16) ++ Set(40)), 2 -> Set(41))
// note: frequency sketch is recreated when limit changes, all frequency counts are 0 again
for (id <- 1 to 10) {
region ! Envelope(shard = 1, id = id, message = "C")
expectReceived(id = id, message = "C")
val passivated = if (id == 1) 40 else if (id <= 5) id + 11 else id - 1
expectReceived(id = passivated, message = Stop)
}
expectState(region)(1 -> ((1 to 4) ++ Set(10)), 2 -> Set(41))
// increase the per-region limit from 10 to 30, per-shard limit becomes 15
region ! ShardRegion.SetActiveEntityLimit(30)
// note: frequency sketch is recreated when limit changes, all frequency counts are 0 again
for (id <- 1 to 20) {
region ! Envelope(shard = 1, id = id, message = "D")
expectReceived(id = id, message = "D")
if (id > 15) { // start passivating window candidates at new higher limit of 15
expectReceived(id = id - 3 /* new window size */, message = Stop)
}
}
expectState(region)(1 -> ((1 to 12) ++ (18 to 20)), 2 -> Set(41))
}
}
}

View file

@ -0,0 +1,77 @@
/*
* Copyright (C) 2022 Lightbend Inc. <https://www.lightbend.com>
*/
package akka.cluster.sharding.passivation
import akka.cluster.sharding.internal.HillClimbingAdmissionOptimizer
import org.scalatest.matchers.should.Matchers
import org.scalatest.wordspec.AnyWordSpec
class HillClimbingAdmissionOptimizerSpec extends AnyWordSpec with Matchers {
def create(
initialLimit: Int = 5,
adjustMultiplier: Double = 2.0,
initialStep: Double = 0.01,
restartThreshold: Double = 0.05,
stepDecay: Double = 0.98): HillClimbingAdmissionOptimizer =
new HillClimbingAdmissionOptimizer(initialLimit, adjustMultiplier, initialStep, restartThreshold, stepDecay)
"HillClimbingAdmissionOptimizer" must {
"start in decreasing direction" in {
val optimizer = create(initialStep = 0.123)
for (_ <- 1 to 10) optimizer.recordPassive()
optimizer.calculateAdjustment() shouldBe -0.123
}
"only adjust every 'adjust size' accesses" in {
val optimizer = create()
for (i <- 1 to 100) {
optimizer.recordPassive()
if (i % 10 == 0) optimizer.calculateAdjustment() should not be 0.0
else optimizer.calculateAdjustment() shouldBe 0.0
}
}
"decay each step when active rate is under restart threshold" in {
val step = 0.1
val decay = 0.5
val optimizer = create(initialStep = step, stepDecay = decay)
for (i <- 1 to 100) {
optimizer.recordPassive()
if (i % 10 == 0) optimizer.calculateAdjustment() shouldBe math.pow(decay, i / 10 - 1) * -step
}
}
"restart when active rate is over restart threshold" in {
val step = 0.1
val decay = 0.5
val optimizer = create(initialStep = step, stepDecay = decay)
for (i <- 1 to 100) {
// increase (and maintain) active rate after 40 accesses to trigger restart at 60 then decay again
if (i > 40) optimizer.recordActive() else optimizer.recordPassive()
if (i % 10 == 0) {
val shift = if (i < 60) 1 else 6
optimizer.calculateAdjustment() shouldBe math.pow(decay, i / 10 - shift) * -step
}
}
}
"change direction when active rate drops" in {
val optimizer = create()
for (i <- 1 to 500) {
// decrease active rate every 50 accesses to switch direction
val activeRate = math.max(1, 10 - (i / 50))
if (i % 10 < activeRate) optimizer.recordActive() else optimizer.recordPassive()
if (i % 10 == 0) {
// at 10-50 should be negative direction, at 60-100 should be positive direction, and so on
val adjustment = optimizer.calculateAdjustment()
if (((i - 1) / 50) % 2 == 0) adjustment should be < 0.0 else adjustment should be > 0.0
}
}
}
}
}

View file

@ -144,6 +144,19 @@ object TraceFileReader {
}
}
/**
* Read binary traces from R3 Corda traces.
*/
final class Corda(path: String) extends AccessPattern {
override val isSynthetic = false
override def entityIds: Source[EntityId, NotUsed] =
FileIO // binary file of longs
.fromPath(Paths.get(path), chunkSize = 8)
.map(bytes => bytes.toByteBuffer.getLong.toString)
.mapMaterializedValue(_ => NotUsed)
}
/**
* Read traces provided with the "LIRS" (or "LIRS2") paper:
* LIRS: An Efficient Low Inter-reference Recency Set Replacement Policy to Improve Buffer Cache Performance
@ -179,3 +192,11 @@ object TraceFileReader {
}
}
}
class JoinedAccessPatterns(patterns: Seq[AccessPattern]) extends AccessPattern {
override def isSynthetic: Boolean =
patterns.exists(_.isSynthetic)
override def entityIds: Source[EntityId, NotUsed] =
patterns.map(_.entityIds).foldLeft(Source.empty[EntityId])(_.concat(_))
}

View file

@ -6,20 +6,36 @@ package akka.cluster.sharding.passivation.simulator
import akka.NotUsed
import akka.actor.ActorSystem
import akka.cluster.sharding.internal.{
EntityPassivationStrategy,
LeastFrequentlyUsedEntityPassivationStrategy,
LeastRecentlyUsedEntityPassivationStrategy,
MostRecentlyUsedEntityPassivationStrategy,
SegmentedLeastRecentlyUsedEntityPassivationStrategy
}
import akka.stream.scaladsl.{ Flow, Source }
import akka.cluster.sharding.internal.ActiveEntities
import akka.cluster.sharding.internal.AdmissionFilter
import akka.cluster.sharding.internal.AdmissionOptimizer
import akka.cluster.sharding.internal.AlwaysAdmissionFilter
import akka.cluster.sharding.internal.CompositeEntityPassivationStrategy
import akka.cluster.sharding.internal.DisabledEntityPassivationStrategy
import akka.cluster.sharding.internal.EntityPassivationStrategy
import akka.cluster.sharding.internal.FrequencySketchAdmissionFilter
import akka.cluster.sharding.internal.HillClimbingAdmissionOptimizer
import akka.cluster.sharding.internal.LeastFrequentlyUsedEntityPassivationStrategy
import akka.cluster.sharding.internal.LeastFrequentlyUsedReplacementPolicy
import akka.cluster.sharding.internal.LeastRecentlyUsedEntityPassivationStrategy
import akka.cluster.sharding.internal.LeastRecentlyUsedReplacementPolicy
import akka.cluster.sharding.internal.MostRecentlyUsedEntityPassivationStrategy
import akka.cluster.sharding.internal.MostRecentlyUsedReplacementPolicy
import akka.cluster.sharding.internal.NoActiveEntities
import akka.cluster.sharding.internal.NoAdmissionOptimizer
import akka.cluster.sharding.internal.SegmentedLeastRecentlyUsedEntityPassivationStrategy
import akka.cluster.sharding.internal.SegmentedLeastRecentlyUsedReplacementPolicy
import akka.stream.scaladsl.Flow
import akka.stream.scaladsl.Source
import akka.util.OptionVal
import com.typesafe.config.ConfigFactory
import scala.collection.{ immutable, mutable }
import scala.concurrent.{ ExecutionContext, Future }
import scala.util.{ Failure, Success }
import scala.collection.immutable
import scala.collection.mutable
import scala.concurrent.ExecutionContext
import scala.concurrent.Future
import scala.util.Failure
import scala.util.Success
/**
* Simulator for testing the efficiency of passivation strategies.
@ -75,10 +91,10 @@ object Simulator {
name = runSettings.name,
numberOfShards = runSettings.shards,
numberOfRegions = runSettings.regions,
accessPattern = accessPattern(runSettings),
accessPattern = accessPattern(runSettings.pattern),
strategyCreator = strategyCreator(runSettings))
def accessPattern(runSettings: SimulatorSettings.RunSettings): AccessPattern = runSettings.pattern match {
def accessPattern(patternSettings: SimulatorSettings.PatternSettings): AccessPattern = patternSettings match {
case SimulatorSettings.PatternSettings.Synthetic(generator, events) =>
generator match {
case SimulatorSettings.PatternSettings.Synthetic.Sequence(start) =>
@ -99,6 +115,7 @@ object Simulator {
case SimulatorSettings.PatternSettings.Trace(path, format) =>
format match {
case "arc" => new TraceFileReader.Arc(path)
case "corda" => new TraceFileReader.Corda(path)
case "lirs" => new TraceFileReader.Lirs(path)
case "lirs2" => new TraceFileReader.Lirs2(path)
case "simple" => new TraceFileReader.Simple(path)
@ -106,6 +123,8 @@ object Simulator {
case "wikipedia" => new TraceFileReader.Wikipedia(path)
case _ => sys.error(s"Unknown trace file format [$format]")
}
case SimulatorSettings.PatternSettings.Joined(patterns) =>
new JoinedAccessPatterns(patterns.map(accessPattern))
}
def strategyCreator(runSettings: SimulatorSettings.RunSettings): StrategyCreator =
@ -118,6 +137,10 @@ object Simulator {
new MostRecentlyUsedStrategyCreator(perRegionLimit)
case SimulatorSettings.StrategySettings.LeastFrequentlyUsed(perRegionLimit, dynamicAging) =>
new LeastFrequentlyUsedStrategyCreator(perRegionLimit, dynamicAging)
case settings: SimulatorSettings.StrategySettings.Composite =>
new CompositeStrategyCreator(settings)
case SimulatorSettings.StrategySettings.NoStrategy =>
DisabledStrategyCreator
}
}
@ -296,6 +319,70 @@ object Simulator {
new LeastFrequentlyUsedEntityPassivationStrategy(perRegionLimit, dynamicAging, idleCheck = None))
}
final class CompositeStrategyCreator(settings: SimulatorSettings.StrategySettings.Composite)
extends PassivationStrategyCreator {
override def create(shardId: ShardId): SimulatedStrategy = {
val main = activeEntities(settings.main)
val window = activeEntities(settings.window)
val initialWindowProportion = if (window eq NoActiveEntities) 0.0 else settings.initialWindowProportion
val minimumWindowProportion = if (window eq NoActiveEntities) 0.0 else settings.minimumWindowProportion
val maximumWindowProportion = if (window eq NoActiveEntities) 0.0 else settings.maximumWindowProportion
val windowOptimizer =
if (window eq NoActiveEntities) NoAdmissionOptimizer
else admissionOptimizer(settings.perRegionLimit, settings.optimizer)
val admission = admissionFilter(settings.perRegionLimit, settings.filter)
new PassivationStrategy(
new CompositeEntityPassivationStrategy(
settings.perRegionLimit,
window,
initialWindowProportion,
minimumWindowProportion,
maximumWindowProportion,
windowOptimizer,
admission,
main,
idleCheck = None))
}
private def activeEntities(strategySettings: SimulatorSettings.StrategySettings): ActiveEntities =
strategySettings match {
case SimulatorSettings.StrategySettings.LeastRecentlyUsed(perRegionLimit, segmented) if segmented.isEmpty =>
new LeastRecentlyUsedReplacementPolicy(perRegionLimit)
case SimulatorSettings.StrategySettings.LeastRecentlyUsed(perRegionLimit, segmented) =>
new SegmentedLeastRecentlyUsedReplacementPolicy(perRegionLimit, segmented, idleEnabled = false)
case SimulatorSettings.StrategySettings.MostRecentlyUsed(perRegionLimit) =>
new MostRecentlyUsedReplacementPolicy(perRegionLimit)
case SimulatorSettings.StrategySettings.LeastFrequentlyUsed(perRegionLimit, dynamicAging) =>
new LeastFrequentlyUsedReplacementPolicy(perRegionLimit, dynamicAging, idleEnabled = false)
case _ => NoActiveEntities
}
private def admissionOptimizer(
capacity: Int,
optimizerSettings: SimulatorSettings.StrategySettings.AdmissionOptimizerSettings): AdmissionOptimizer =
optimizerSettings match {
case SimulatorSettings.StrategySettings.AdmissionOptimizerSettings.NoOptimizer => NoAdmissionOptimizer
case SimulatorSettings.StrategySettings.AdmissionOptimizerSettings
.HillClimbingOptimizer(adjustMultiplier, initialStep, restartThreshold, stepDecay) =>
new HillClimbingAdmissionOptimizer(capacity, adjustMultiplier, initialStep, restartThreshold, stepDecay)
}
private def admissionFilter(
capacity: Int,
filterSettings: SimulatorSettings.StrategySettings.AdmissionFilterSettings): AdmissionFilter =
filterSettings match {
case SimulatorSettings.StrategySettings.AdmissionFilterSettings.NoFilter => AlwaysAdmissionFilter
case SimulatorSettings.StrategySettings.AdmissionFilterSettings
.FrequencySketchFilter(widthMultiplier, resetMultiplier, depth, counterBits) =>
FrequencySketchAdmissionFilter(capacity, widthMultiplier, resetMultiplier, depth, counterBits)
}
}
object DisabledStrategyCreator extends PassivationStrategyCreator {
override def create(shardId: ShardId): SimulatedStrategy =
new PassivationStrategy(DisabledEntityPassivationStrategy)
}
// Clairvoyant passivation strategy using Bélády's algorithm.
// Record virtual access times per entity id on a first pass through the access pattern,
// to passivate entities that will not be accessed again for the furthest time in the future.

View file

@ -49,9 +49,40 @@ object SimulatorSettings {
final case class LeastRecentlyUsed(perRegionLimit: Int, segmented: immutable.Seq[Double]) extends StrategySettings
final case class MostRecentlyUsed(perRegionLimit: Int) extends StrategySettings
final case class LeastFrequentlyUsed(perRegionLimit: Int, dynamicAging: Boolean) extends StrategySettings
case object NoStrategy extends StrategySettings
final case class Composite(
perRegionLimit: Int,
main: StrategySettings,
window: StrategySettings,
initialWindowProportion: Double,
minimumWindowProportion: Double,
maximumWindowProportion: Double,
filter: AdmissionFilterSettings,
optimizer: AdmissionOptimizerSettings)
extends StrategySettings
def apply(simulatorConfig: Config, strategy: String): StrategySettings = {
val config = simulatorConfig.getConfig(strategy).withFallback(simulatorConfig.getConfig("strategy-defaults"))
val config = simulatorConfig.getConfig(strategy)
val fallbackConfig = simulatorConfig.getConfig("strategy-defaults")
lowerCase(config.getString("strategy")) match {
case "composite" =>
val compositeConfig = config.getConfig("composite").withFallback(fallbackConfig.getConfig("composite"))
Composite(
compositeConfig.getInt("per-region-limit"),
settings(compositeConfig.getConfig("main"), fallbackConfig),
settings(compositeConfig.getConfig("admission.window"), fallbackConfig),
compositeConfig.getDouble("admission.window.proportion"),
compositeConfig.getDouble("admission.window.minimum-proportion"),
compositeConfig.getDouble("admission.window.maximum-proportion"),
AdmissionFilterSettings(compositeConfig),
AdmissionOptimizerSettings(compositeConfig))
case _ => settings(config, fallbackConfig)
}
}
private def settings(strategyConfig: Config, fallbackConfig: Config): StrategySettings = {
val config = strategyConfig.withFallback(fallbackConfig)
lowerCase(config.getString("strategy")) match {
case "optimal" => Optimal(config.getInt("optimal.per-region-limit"))
case "least-recently-used" =>
@ -70,7 +101,55 @@ object SimulatorSettings {
LeastFrequentlyUsed(
config.getInt("least-frequently-used.per-region-limit"),
config.getBoolean("least-frequently-used.dynamic-aging"))
case _ => sys.error(s"Unknown strategy for [$strategy]")
case _ => NoStrategy
}
}
sealed trait AdmissionFilterSettings
object AdmissionFilterSettings {
object NoFilter extends AdmissionFilterSettings
final case class FrequencySketchFilter(
widthMultiplier: Int,
resetMultiplier: Double,
depth: Int,
counterBits: Int)
extends AdmissionFilterSettings
def apply(config: Config): AdmissionFilterSettings = {
lowerCase(config.getString("admission.filter")) match {
case "frequency-sketch" =>
FrequencySketchFilter(
config.getInt("admission.frequency-sketch.width-multiplier"),
config.getDouble("admission.frequency-sketch.reset-multiplier"),
config.getInt("admission.frequency-sketch.depth"),
config.getInt("admission.frequency-sketch.counter-bits"))
case _ => NoFilter
}
}
}
sealed trait AdmissionOptimizerSettings
object AdmissionOptimizerSettings {
object NoOptimizer extends AdmissionOptimizerSettings
final case class HillClimbingOptimizer(
adjustMultiplier: Double,
initialStep: Double,
restartThreshold: Double,
stepDecay: Double)
extends AdmissionOptimizerSettings
def apply(config: Config): AdmissionOptimizerSettings = {
lowerCase(config.getString("admission.optimizer")) match {
case "hill-climbing" =>
HillClimbingOptimizer(
config.getDouble("admission.hill-climbing.adjust-multiplier"),
config.getDouble("admission.hill-climbing.initial-step"),
config.getDouble("admission.hill-climbing.restart-threshold"),
config.getDouble("admission.hill-climbing.step-decay"))
case _ => NoOptimizer
}
}
}
}
@ -141,11 +220,21 @@ object SimulatorSettings {
}
}
final case class Joined(patterns: Seq[PatternSettings]) extends PatternSettings
object Joined {
def apply(simulatorConfig: Config, patternConfig: Config): Joined = {
val patterns = patternConfig.getStringList("joined").asScala.toSeq
Joined(patterns.map(pattern => PatternSettings(simulatorConfig, pattern)))
}
}
def apply(simulatorConfig: Config, pattern: String): PatternSettings = {
val config = simulatorConfig.getConfig(pattern).withFallback(simulatorConfig.getConfig("pattern-defaults"))
lowerCase(config.getString("pattern")) match {
case "synthetic" => Synthetic(config)
case "trace" => Trace(config)
case "joined" => Joined(simulatorConfig, config)
case _ => sys.error(s"Unknown pattern for [$pattern]")
}
}

View file

@ -337,8 +337,13 @@ Cluster Sharding, can be enabled with configuration:
@@snip [passivation new default strategy](/akka-cluster-sharding/src/test/scala/akka/cluster/sharding/ClusterShardingSettingsSpec.scala) { #passivation-new-default-strategy type=conf }
This default strategy uses a [segmented least recently used policy](#segmented-least-recently-used-policy). The active
entity limit can be configured:
This default strategy uses a [composite passivation strategy](#composite-passivation-strategies) which combines
recency-based and frequency-based tracking: the main area is configured with a [segmented least recently used
policy](#segmented-least-recently-used-policy) with a frequency-biased [admission filter](#admission-filter), fronted
by a recency-biased [admission window](#admission-window-policy) with [adaptive sizing](#admission-window-optimizer)
enabled.
The active entity limit for the default strategy can be configured:
@@snip [passivation new default strategy configured](/akka-cluster-sharding/src/test/scala/akka/cluster/sharding/ClusterShardingSettingsSpec.scala) { #passivation-new-default-strategy-configured type=conf }
@ -456,6 +461,63 @@ Configure dynamic aging with the least frequently used policy:
Or using custom `ClusterShardingSettings.PassivationStrategySettings.LeastFrequentlyUsedSettings`.
### Composite passivation strategies
Passivation strategies can be combined using an admission window and admission filter. The admission window tracks
newly activated entities. Entities are replaced in the admission window using one of the replacement policies, such as
the least recently used replacement policy. When an entity is replaced in the window area it has an opportunity to
enter the main entity tracking area, based on the admission filter. The admission filter determines whether an entity
that has left the window area should be admitted into the main area, or otherwise be passivated. A frequency sketch is
the default admission filter and estimates the access frequency of entities over the lifespan of the cluster sharding
node, selecting the entity that is estimated to be accessed more frequently. Composite passivation strategies with an
admission window and admission filter are implementing the _Window-TinyLFU_ caching algorithm.
#### Admission window policy
The admission window tracks newly activated entities. When an entity is replaced in the window area, it has an
opportunity to enter the main entity tracking area, based on the [admission filter](#admission-filter). The admission
window can be enabled by selecting a policy (while the regular replacement policy is for the main area):
@@snip [admission window policy](/akka-cluster-sharding/src/test/scala/akka/cluster/sharding/ClusterShardingSettingsSpec.scala) { #admission-window-policy type=conf }
The proportion of the active entity limit used for the admission window can be configured (the default is 1%):
@@snip [admission window proportion](/akka-cluster-sharding/src/test/scala/akka/cluster/sharding/ClusterShardingSettingsSpec.scala) { #admission-window-proportion type=conf }
The proportion for the admission window can also be adapted and optimized dynamically, by enabling an [admission window
optimizer](#admission-window-optimizer).
#### Admission window optimizer
The proportion of the active entity limit used for the admission window can be adapted dynamically using an optimizer.
The window area will usually retain entities that are accessed again in a short time (recency-biased), while the main
area can track entities that are accessed more frequently over longer times (frequency-biased). If access patterns for
entities are changeable, then the adaptive sizing of the window allows the passivation strategy to adapt between
recency-biased and frequency-biased workloads.
The optimizer currently available uses a simple hill-climbing algorithm, which searches for a window proportion that
provides an optimal active rate (where entities are already active when accessed, the _cache hit rate_). Enable
adaptive window sizing by configuring the `hill-climbing` window optimizer:
@@snip [admission window optimizer](/akka-cluster-sharding/src/test/scala/akka/cluster/sharding/ClusterShardingSettingsSpec.scala) { #admission-window-optimizer type=conf }
See the `reference.conf` for parameters that can be tuned for the hill climbing admission window optimizer.
#### Admission filter
An admission filter can be enabled, which determines whether an entity that has left the window area (or a newly
activated entity if there is no admission window) should be admitted into the main entity tracking area, or otherwise
be passivated. If no admission filter is configured, then entities will always be admitted into the main area.
A frequency sketch is the default admission filter and estimates the access frequency of entities over the lifespan of
the cluster sharding node, selecting the entity that is estimated to be accessed more frequently. The frequency sketch
automatically ages entries, using the approach from the _TinyLFU_ cache admission algorithm. Enable an admission filter
by configuring the `frequency-sketch` admission filter:
@@snip [admission policy](/akka-cluster-sharding/src/test/scala/akka/cluster/sharding/ClusterShardingSettingsSpec.scala) { #admission-policy type=conf }
See the `reference.conf` for parameters that can be tuned for the frequency sketch admission filter.
## Sharding State