Introduce new default entity passivation strategy (#30968)

This commit is contained in:
Peter Vlugter 2021-12-10 01:04:27 +13:00 committed by GitHub
parent 35c128b7d8
commit 3a7201183d
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
11 changed files with 673 additions and 688 deletions

View file

@ -165,89 +165,90 @@ object ClusterShardingSettings {
@ApiMayChange
final class PassivationStrategySettings private (
val strategy: String,
val idleSettings: PassivationStrategySettings.IdleSettings,
val leastRecentlyUsedSettings: PassivationStrategySettings.LeastRecentlyUsedSettings,
val mostRecentlyUsedSettings: PassivationStrategySettings.MostRecentlyUsedSettings,
val leastFrequentlyUsedSettings: PassivationStrategySettings.LeastFrequentlyUsedSettings,
val idleEntitySettings: Option[PassivationStrategySettings.IdleSettings],
val activeEntityLimit: Option[Int],
val replacementPolicySettings: Option[PassivationStrategySettings.PolicySettings],
private[akka] val oldSettingUsed: Boolean) {
def this(
strategy: String,
idleSettings: PassivationStrategySettings.IdleSettings,
leastRecentlyUsedSettings: PassivationStrategySettings.LeastRecentlyUsedSettings,
mostRecentlyUsedSettings: PassivationStrategySettings.MostRecentlyUsedSettings,
leastFrequentlyUsedSettings: PassivationStrategySettings.LeastFrequentlyUsedSettings) =
this(
strategy,
idleSettings,
leastRecentlyUsedSettings,
mostRecentlyUsedSettings,
leastFrequentlyUsedSettings,
oldSettingUsed = false)
idleEntitySettings: Option[PassivationStrategySettings.IdleSettings],
activeEntityLimit: Option[Int],
replacementPolicySettings: Option[PassivationStrategySettings.PolicySettings]) =
this(idleEntitySettings, activeEntityLimit, replacementPolicySettings, oldSettingUsed = false)
import PassivationStrategySettings._
def withIdleStrategy(settings: IdleSettings): PassivationStrategySettings =
copy(strategy = "idle", idleSettings = settings, oldSettingUsed = false)
def withIdleEntityPassivation(settings: IdleSettings): PassivationStrategySettings =
copy(idleEntitySettings = Some(settings), oldSettingUsed = false)
def withLeastRecentlyUsedStrategy(settings: LeastRecentlyUsedSettings): PassivationStrategySettings =
copy(strategy = "least-recently-used", leastRecentlyUsedSettings = settings)
def withIdleEntityPassivation(timeout: FiniteDuration): PassivationStrategySettings =
withIdleEntityPassivation(IdleSettings.defaults.withTimeout(timeout))
def withMostRecentlyUsedStrategy(settings: MostRecentlyUsedSettings): PassivationStrategySettings =
copy(strategy = "most-recently-used", mostRecentlyUsedSettings = settings)
def withIdleEntityPassivation(timeout: FiniteDuration, interval: FiniteDuration): PassivationStrategySettings =
withIdleEntityPassivation(IdleSettings.defaults.withTimeout(timeout).withInterval(interval))
def withLeastFrequentlyUsedStrategy(settings: LeastFrequentlyUsedSettings): PassivationStrategySettings =
copy(strategy = "least-frequently-used", leastFrequentlyUsedSettings = settings)
def withIdleEntityPassivation(timeout: java.time.Duration): PassivationStrategySettings =
withIdleEntityPassivation(IdleSettings.defaults.withTimeout(timeout))
def withIdleEntityPassivation(
timeout: java.time.Duration,
interval: java.time.Duration): PassivationStrategySettings =
withIdleEntityPassivation(IdleSettings.defaults.withTimeout(timeout).withInterval(interval))
def withActiveEntityLimit(limit: Int): PassivationStrategySettings =
copy(activeEntityLimit = Some(limit))
def withReplacementPolicy(settings: PolicySettings): PassivationStrategySettings =
copy(replacementPolicySettings = Some(settings))
def withLeastRecentlyUsedReplacement(): PassivationStrategySettings =
withReplacementPolicy(LeastRecentlyUsedSettings.defaults)
def withMostRecentlyUsedReplacement(): PassivationStrategySettings =
withReplacementPolicy(MostRecentlyUsedSettings.defaults)
def withLeastFrequentlyUsedReplacement(): PassivationStrategySettings =
withReplacementPolicy(LeastFrequentlyUsedSettings.defaults)
private[akka] def withOldIdleStrategy(timeout: FiniteDuration): PassivationStrategySettings =
copy(strategy = "idle", idleSettings = idleSettings.withTimeout(timeout), oldSettingUsed = true)
copy(
idleEntitySettings = Some(new IdleSettings(timeout, None)),
activeEntityLimit = None,
replacementPolicySettings = None,
oldSettingUsed = true)
private def copy(
strategy: String,
idleSettings: IdleSettings = idleSettings,
leastRecentlyUsedSettings: LeastRecentlyUsedSettings = leastRecentlyUsedSettings,
mostRecentlyUsedSettings: MostRecentlyUsedSettings = mostRecentlyUsedSettings,
leastFrequentlyUsedSettings: LeastFrequentlyUsedSettings = leastFrequentlyUsedSettings,
idleEntitySettings: Option[IdleSettings] = idleEntitySettings,
activeEntityLimit: Option[Int] = activeEntityLimit,
replacementPolicySettings: Option[PolicySettings] = replacementPolicySettings,
oldSettingUsed: Boolean = oldSettingUsed): PassivationStrategySettings =
new PassivationStrategySettings(
strategy,
idleSettings,
leastRecentlyUsedSettings,
mostRecentlyUsedSettings,
leastFrequentlyUsedSettings,
oldSettingUsed)
new PassivationStrategySettings(idleEntitySettings, activeEntityLimit, replacementPolicySettings, oldSettingUsed)
}
object PassivationStrategySettings {
import ClassicShardingSettings.{ PassivationStrategySettings => ClassicPassivationStrategySettings }
val defaults = new PassivationStrategySettings(
strategy = "idle",
IdleSettings.defaults,
LeastRecentlyUsedSettings.defaults,
MostRecentlyUsedSettings.defaults,
LeastFrequentlyUsedSettings.defaults,
idleEntitySettings = None,
activeEntityLimit = None,
replacementPolicySettings = None,
oldSettingUsed = false)
val disabled: PassivationStrategySettings = defaults.copy(strategy = "none")
val disabled: PassivationStrategySettings = defaults
def apply(classic: ClassicShardingSettings.PassivationStrategySettings) =
new PassivationStrategySettings(
classic.strategy,
IdleSettings(classic.idleSettings),
LeastRecentlyUsedSettings(classic.leastRecentlyUsedSettings),
MostRecentlyUsedSettings(classic.mostRecentlyUsedSettings),
LeastFrequentlyUsedSettings(classic.leastFrequentlyUsedSettings),
classic.idleEntitySettings.map(IdleSettings.apply),
classic.activeEntityLimit,
classic.replacementPolicySettings.map(PolicySettings.apply),
classic.oldSettingUsed)
def toClassic(settings: PassivationStrategySettings): ClassicPassivationStrategySettings =
new ClassicPassivationStrategySettings(
strategy = settings.strategy,
IdleSettings.toClassic(settings.idleSettings),
LeastRecentlyUsedSettings.toClassic(settings.leastRecentlyUsedSettings),
MostRecentlyUsedSettings.toClassic(settings.mostRecentlyUsedSettings),
LeastFrequentlyUsedSettings.toClassic(settings.leastFrequentlyUsedSettings))
settings.idleEntitySettings.map(IdleSettings.toClassic),
settings.activeEntityLimit,
settings.replacementPolicySettings.map(PolicySettings.toClassic),
settings.oldSettingUsed)
object IdleSettings {
val defaults: IdleSettings = new IdleSettings(timeout = 2.minutes, interval = None)
@ -273,21 +274,34 @@ object ClusterShardingSettings {
new IdleSettings(timeout, interval)
}
object PolicySettings {
def apply(classic: ClassicPassivationStrategySettings.PolicySettings): PolicySettings = classic match {
case classic: ClassicPassivationStrategySettings.LeastRecentlyUsedSettings =>
LeastRecentlyUsedSettings(classic)
case classic: ClassicPassivationStrategySettings.MostRecentlyUsedSettings =>
MostRecentlyUsedSettings(classic)
case classic: ClassicPassivationStrategySettings.LeastFrequentlyUsedSettings =>
LeastFrequentlyUsedSettings(classic)
}
def toClassic(settings: PolicySettings): ClassicPassivationStrategySettings.PolicySettings = settings match {
case settings: LeastRecentlyUsedSettings => LeastRecentlyUsedSettings.toClassic(settings)
case settings: MostRecentlyUsedSettings => MostRecentlyUsedSettings.toClassic(settings)
case settings: LeastFrequentlyUsedSettings => LeastFrequentlyUsedSettings.toClassic(settings)
}
}
sealed trait PolicySettings
object LeastRecentlyUsedSettings {
val defaults: LeastRecentlyUsedSettings =
new LeastRecentlyUsedSettings(limit = 100000, segmentedSettings = None, idleSettings = None)
val defaults: LeastRecentlyUsedSettings = new LeastRecentlyUsedSettings(segmentedSettings = None)
def apply(classic: ClassicPassivationStrategySettings.LeastRecentlyUsedSettings): LeastRecentlyUsedSettings =
new LeastRecentlyUsedSettings(
classic.limit,
classic.segmentedSettings.map(SegmentedSettings.apply),
classic.idleSettings.map(IdleSettings.apply))
new LeastRecentlyUsedSettings(classic.segmentedSettings.map(SegmentedSettings.apply))
def toClassic(settings: LeastRecentlyUsedSettings): ClassicPassivationStrategySettings.LeastRecentlyUsedSettings =
new ClassicPassivationStrategySettings.LeastRecentlyUsedSettings(
settings.limit,
settings.segmentedSettings.map(SegmentedSettings.toClassic),
settings.idleSettings.map(IdleSettings.toClassic))
settings.segmentedSettings.map(SegmentedSettings.toClassic))
object SegmentedSettings {
def apply(classic: ClassicPassivationStrategySettings.LeastRecentlyUsedSettings.SegmentedSettings)
@ -315,122 +329,58 @@ object ClusterShardingSettings {
}
}
final class LeastRecentlyUsedSettings(
val limit: Int,
val segmentedSettings: Option[LeastRecentlyUsedSettings.SegmentedSettings],
val idleSettings: Option[IdleSettings]) {
final class LeastRecentlyUsedSettings(val segmentedSettings: Option[LeastRecentlyUsedSettings.SegmentedSettings])
extends PolicySettings {
import LeastRecentlyUsedSettings.SegmentedSettings
def withLimit(limit: Int): LeastRecentlyUsedSettings = copy(limit = limit)
def withSegmented(levels: Int): LeastRecentlyUsedSettings =
copy(segmentedSettings = Some(new SegmentedSettings(levels, Nil)))
def withSegmented(levels: Int): LeastRecentlyUsedSettings = withSegmented(levels, Nil)
def withSegmented(proportions: immutable.Seq[Double]): LeastRecentlyUsedSettings =
copy(segmentedSettings = Some(new SegmentedSettings(proportions.size, proportions)))
def withSegmented(levels: Int, proportions: immutable.Seq[Double]): LeastRecentlyUsedSettings =
copy(segmentedSettings = Some(new LeastRecentlyUsedSettings.SegmentedSettings(levels, proportions)))
def withSegmentedProportions(proportions: java.util.List[java.lang.Double]): LeastRecentlyUsedSettings =
withSegmented(immutableSeq(proportions).map(_.toDouble))
def withSegmentedProportions(
levels: Int,
proportions: java.util.List[java.lang.Double]): LeastRecentlyUsedSettings =
withSegmented(levels, immutableSeq(proportions).map(_.toDouble))
def withIdle(timeout: FiniteDuration): LeastRecentlyUsedSettings =
copy(idleSettings = Some(new IdleSettings(timeout, None)))
def withIdle(timeout: java.time.Duration): LeastRecentlyUsedSettings =
withIdle(timeout.asScala)
def withIdle(timeout: FiniteDuration, interval: FiniteDuration): LeastRecentlyUsedSettings =
copy(idleSettings = Some(new IdleSettings(timeout, Some(interval))))
def withIdle(timeout: java.time.Duration, interval: java.time.Duration): LeastRecentlyUsedSettings =
withIdle(timeout.asScala, interval.asScala)
private def copy(
limit: Int = limit,
segmentedSettings: Option[LeastRecentlyUsedSettings.SegmentedSettings] = segmentedSettings,
idleSettings: Option[IdleSettings] = idleSettings): LeastRecentlyUsedSettings =
new LeastRecentlyUsedSettings(limit, segmentedSettings, idleSettings)
private def copy(segmentedSettings: Option[SegmentedSettings]): LeastRecentlyUsedSettings =
new LeastRecentlyUsedSettings(segmentedSettings)
}
object MostRecentlyUsedSettings {
val defaults: MostRecentlyUsedSettings = new MostRecentlyUsedSettings(limit = 100000, idleSettings = None)
val defaults: MostRecentlyUsedSettings = new MostRecentlyUsedSettings
def apply(classic: ClassicPassivationStrategySettings.MostRecentlyUsedSettings): MostRecentlyUsedSettings =
new MostRecentlyUsedSettings(classic.limit, classic.idleSettings.map(IdleSettings.apply))
def apply(classic: ClassicPassivationStrategySettings.MostRecentlyUsedSettings): MostRecentlyUsedSettings = {
val _ = classic // currently not used
new MostRecentlyUsedSettings
}
def toClassic(settings: MostRecentlyUsedSettings): ClassicPassivationStrategySettings.MostRecentlyUsedSettings =
new ClassicPassivationStrategySettings.MostRecentlyUsedSettings(
settings.limit,
settings.idleSettings.map(IdleSettings.toClassic))
def toClassic(settings: MostRecentlyUsedSettings): ClassicPassivationStrategySettings.MostRecentlyUsedSettings = {
val _ = settings // currently not used
new ClassicPassivationStrategySettings.MostRecentlyUsedSettings
}
}
final class MostRecentlyUsedSettings(val limit: Int, val idleSettings: Option[IdleSettings]) {
def withLimit(limit: Int): MostRecentlyUsedSettings = copy(limit = limit)
def withIdle(timeout: FiniteDuration): MostRecentlyUsedSettings =
copy(idleSettings = Some(new IdleSettings(timeout, None)))
def withIdle(timeout: java.time.Duration): MostRecentlyUsedSettings =
withIdle(timeout.asScala)
def withIdle(timeout: FiniteDuration, interval: FiniteDuration): MostRecentlyUsedSettings =
copy(idleSettings = Some(new IdleSettings(timeout, Some(interval))))
def withIdle(timeout: java.time.Duration, interval: java.time.Duration): MostRecentlyUsedSettings =
withIdle(timeout.asScala, interval.asScala)
private def copy(
limit: Int = limit,
idleSettings: Option[IdleSettings] = idleSettings): MostRecentlyUsedSettings =
new MostRecentlyUsedSettings(limit, idleSettings)
}
final class MostRecentlyUsedSettings extends PolicySettings
object LeastFrequentlyUsedSettings {
val defaults: LeastFrequentlyUsedSettings =
new LeastFrequentlyUsedSettings(limit = 100000, dynamicAging = false, idleSettings = None)
val defaults: LeastFrequentlyUsedSettings = new LeastFrequentlyUsedSettings(dynamicAging = false)
def apply(classic: ClassicPassivationStrategySettings.LeastFrequentlyUsedSettings): LeastFrequentlyUsedSettings =
new LeastFrequentlyUsedSettings(
classic.limit,
classic.dynamicAging,
classic.idleSettings.map(IdleSettings.apply))
new LeastFrequentlyUsedSettings(classic.dynamicAging)
def toClassic(
settings: LeastFrequentlyUsedSettings): ClassicPassivationStrategySettings.LeastFrequentlyUsedSettings =
new ClassicPassivationStrategySettings.LeastFrequentlyUsedSettings(
settings.limit,
settings.dynamicAging,
settings.idleSettings.map(IdleSettings.toClassic))
new ClassicPassivationStrategySettings.LeastFrequentlyUsedSettings(settings.dynamicAging)
}
final class LeastFrequentlyUsedSettings(
val limit: Int,
val dynamicAging: Boolean,
val idleSettings: Option[IdleSettings]) {
def withLimit(limit: Int): LeastFrequentlyUsedSettings = copy(limit = limit)
final class LeastFrequentlyUsedSettings(val dynamicAging: Boolean) extends PolicySettings {
def withDynamicAging(): LeastFrequentlyUsedSettings = withDynamicAging(enabled = true)
def withDynamicAging(enabled: Boolean): LeastFrequentlyUsedSettings = copy(dynamicAging = enabled)
def withIdle(timeout: FiniteDuration): LeastFrequentlyUsedSettings =
copy(idleSettings = Some(new IdleSettings(timeout, None)))
def withIdle(timeout: java.time.Duration): LeastFrequentlyUsedSettings =
withIdle(timeout.asScala)
def withIdle(timeout: FiniteDuration, interval: FiniteDuration): LeastFrequentlyUsedSettings =
copy(idleSettings = Some(new IdleSettings(timeout, Some(interval))))
def withIdle(timeout: java.time.Duration, interval: java.time.Duration): LeastFrequentlyUsedSettings =
withIdle(timeout.asScala, interval.asScala)
private def copy(
limit: Int = limit,
dynamicAging: Boolean = dynamicAging,
idleSettings: Option[IdleSettings] = idleSettings): LeastFrequentlyUsedSettings =
new LeastFrequentlyUsedSettings(limit, dynamicAging, idleSettings)
private def copy(dynamicAging: Boolean): LeastFrequentlyUsedSettings =
new LeastFrequentlyUsedSettings(dynamicAging)
}
private[akka] def oldDefault(idleTimeout: FiniteDuration): PassivationStrategySettings =
@ -743,14 +693,15 @@ final class ClusterShardingSettings(
rememberEntitiesStoreMode: ClusterShardingSettings.RememberEntitiesStoreMode): ClusterShardingSettings =
copy(rememberEntitiesStoreMode = rememberEntitiesStoreMode)
@deprecated("See passivationStrategySettings.idleTimeout instead", since = "2.6.18")
def passivateIdleEntityAfter: FiniteDuration = passivationStrategySettings.idleSettings.timeout
@deprecated("See passivationStrategySettings.idleEntitySettings instead", since = "2.6.18")
def passivateIdleEntityAfter: FiniteDuration =
passivationStrategySettings.idleEntitySettings.fold(Duration.Zero)(_.timeout)
@deprecated("Use withIdlePassivationStrategy instead", since = "2.6.18")
@deprecated("Use withPassivationStrategy instead", since = "2.6.18")
def withPassivateIdleEntityAfter(duration: FiniteDuration): ClusterShardingSettings =
copy(passivationStrategySettings = passivationStrategySettings.withOldIdleStrategy(duration))
@deprecated("Use withIdlePassivationStrategy instead", since = "2.6.18")
@deprecated("Use withPassivationStrategy instead", since = "2.6.18")
def withPassivateIdleEntityAfter(duration: java.time.Duration): ClusterShardingSettings =
copy(passivationStrategySettings = passivationStrategySettings.withOldIdleStrategy(duration.asScala))
@ -760,24 +711,6 @@ final class ClusterShardingSettings(
def withNoPassivationStrategy(): ClusterShardingSettings =
copy(passivationStrategySettings = ClusterShardingSettings.PassivationStrategySettings.disabled)
def withIdlePassivationStrategy(
settings: ClusterShardingSettings.PassivationStrategySettings.IdleSettings): ClusterShardingSettings =
copy(passivationStrategySettings = passivationStrategySettings.withIdleStrategy(settings))
def withLeastRecentlyUsedPassivationStrategy(
settings: ClusterShardingSettings.PassivationStrategySettings.LeastRecentlyUsedSettings)
: ClusterShardingSettings =
copy(passivationStrategySettings = passivationStrategySettings.withLeastRecentlyUsedStrategy(settings))
def withMostRecentlyUsedPassivationStrategy(
settings: ClusterShardingSettings.PassivationStrategySettings.MostRecentlyUsedSettings): ClusterShardingSettings =
copy(passivationStrategySettings = passivationStrategySettings.withMostRecentlyUsedStrategy(settings))
def withLeastFrequentlyUsedPassivationStrategy(
settings: ClusterShardingSettings.PassivationStrategySettings.LeastFrequentlyUsedSettings)
: ClusterShardingSettings =
copy(passivationStrategySettings = passivationStrategySettings.withLeastFrequentlyUsedStrategy(settings))
def withShardRegionQueryTimeout(duration: FiniteDuration): ClusterShardingSettings =
copy(shardRegionQueryTimeout = duration)

View file

@ -28,7 +28,7 @@ akka.cluster.sharding {
# Default is ddata for backwards compatibility.
remember-entities-store = "ddata"
# Deprecated: use the `passivation.idle.timeout` for 'idle' `passivation.strategy` instead.
# Deprecated: use the `passivation.default-idle-strategy.idle-entity.timeout` setting instead.
# Set this to a time duration to have sharding passivate entities when they have not
# received any message in this length of time. Set to 'off' to disable.
# It is always disabled if `remember-entities` is enabled.
@ -36,87 +36,84 @@ akka.cluster.sharding {
# Automatic entity passivation settings.
passivation {
# Passivation strategy to use. Possible values are:
# - "idle"
# - "least-recently-used"
# - "most-recently-used"
# - "least-frequently-used"
# Automatic passivation strategy to use.
# Set to "none" or "off" to disable automatic passivation.
# Set to "default-strategy" to switch to the recommended default strategy with an active entity limit.
# See the strategy-defaults section for possible passivation strategy settings and default values.
# Passivation strategies are always disabled if `remember-entities` is enabled.
strategy = "idle"
strategy = "default-idle-strategy"
# Idle passivation strategy.
# Passivate entities when they have not received a message for a specified length of time.
idle {
# Passivate idle entities after the timeout.
timeout = 120s
# Check idle entities every interval. Set to "default" to use half the timeout by default.
interval = default
# Default passivation strategy without active entity limit; time out idle entities after 2 minutes.
default-idle-strategy {
idle-entity.timeout = 120s
}
# Least recently used passivation strategy.
# 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.
least-recently-used {
# Limit of active entities in a shard region.
limit = 100000
# Recommended default strategy for automatic passivation with an active entity limit.
# Configured with a segmented least recently used (SLRU) replacement policy.
default-strategy {
# Default limit of 100k active entities in a shard region (in a cluster node).
active-entity-limit = 100000
# Optionally use a "segmented" least recently used strategy.
# Disabled when segmented.levels are set to "none" or "off".
segmented {
# Number of segmented levels.
levels = none
# Fractional proportions for the segmented levels.
# If empty then segments are divided evenly by the number of levels.
proportions = []
# Segmented LRU replacement policy with an 80% "protected" level by default.
replacement {
policy = least-recently-used
least-recently-used {
segmented {
levels = 2
proportions = [0.2, 0.8]
}
}
}
}
# Optionally passivate entities when they have not received a message for a specified length of time.
idle {
# Passivate idle entities after the timeout. Set to "off" to disable.
timeout = off
strategy-defaults {
# Passivate entities when they have not received a message for a specified length of time.
idle-entity {
# Passivate idle entities after the timeout. Set to "none" or "off" to disable.
timeout = none
# Check idle entities every interval. Set to "default" to use half the timeout by default.
interval = default
}
}
# Most recently used passivation strategy.
# 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.
most-recently-used {
# Limit of active entities in a shard region.
limit = 100000
# Passivate entities when the number of active entities in a shard region reaches this limit.
# The per-region limit is divided evenly among the active shards in a region.
# Set to "none" or "off" to disable limit-based automatic passivation, to only use idle entity timeouts.
active-entity-limit = none
# Optionally passivate entities when they have not received a message for a specified length of time.
idle {
# Passivate idle entities after the timeout. Set to "off" to disable.
timeout = off
# Entity replacement settings, for when the active entity limit is reached.
replacement {
# Entity replacement policy to use when the active entity limit is reached. Possible values are:
# - "least-recently-used"
# - "most-recently-used"
# - "least-frequently-used"
# Set to "none" or "off" to disable the replacement policy and ignore the active entity limit.
policy = none
# Check idle entities every interval. Set to "default" to use half the timeout by default.
interval = default
}
}
# Least recently used entity replacement policy.
least-recently-used {
# Optionally use a "segmented" least recently used strategy.
# Disabled when segmented.levels are set to "none" or "off".
segmented {
# Number of segmented levels.
levels = none
# Least frequently used passivation strategy.
# 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.
least-frequently-used {
# Limit of active entities in a shard region.
limit = 100000
# Fractional proportions for the segmented levels.
# If empty then segments are divided evenly by the number of levels.
proportions = []
}
}
# New frequency counts will be "dynamically aged" when enabled.
dynamic-aging = off
# Most recently used entity replacement policy.
most-recently-used {}
# Optionally passivate entities when they have not received a message for a specified length of time.
idle {
# Passivate idle entities after the timeout. Set to "off" to disable.
timeout = off
# Check idle entities every interval. Set to "default" to use half the timeout by default.
interval = default
# Least frequently used entity replacement policy.
least-frequently-used {
# New frequency counts will be "dynamically aged" when enabled.
dynamic-aging = off
}
}
}
}

View file

@ -90,7 +90,7 @@ object ClusterShardingSettings {
val coordinatorSingletonSettings = ClusterSingletonManagerSettings(config.getConfig("coordinator-singleton"))
val passivationStrategySettings = PassivationStrategySettings(config)
val passivationStrategySettings = PassivationStrategySettings.fromSharding(config)
val lease = config.getString("use-lease") match {
case s if s.isEmpty => None
@ -130,71 +130,75 @@ object ClusterShardingSettings {
if (role == "") None else Option(role)
@ApiMayChange
final class PassivationStrategySettings private (
val strategy: String,
val idleSettings: PassivationStrategySettings.IdleSettings,
val leastRecentlyUsedSettings: PassivationStrategySettings.LeastRecentlyUsedSettings,
val mostRecentlyUsedSettings: PassivationStrategySettings.MostRecentlyUsedSettings,
val leastFrequentlyUsedSettings: PassivationStrategySettings.LeastFrequentlyUsedSettings,
final class PassivationStrategySettings private[akka] (
val idleEntitySettings: Option[PassivationStrategySettings.IdleSettings],
val activeEntityLimit: Option[Int],
val replacementPolicySettings: Option[PassivationStrategySettings.PolicySettings],
private[akka] val oldSettingUsed: Boolean) {
def this(
strategy: String,
idleSettings: PassivationStrategySettings.IdleSettings,
leastRecentlyUsedSettings: PassivationStrategySettings.LeastRecentlyUsedSettings,
mostRecentlyUsedSettings: PassivationStrategySettings.MostRecentlyUsedSettings,
leastFrequentlyUsedSettings: PassivationStrategySettings.LeastFrequentlyUsedSettings) =
this(
strategy,
idleSettings,
leastRecentlyUsedSettings,
mostRecentlyUsedSettings,
leastFrequentlyUsedSettings,
oldSettingUsed = false)
idleEntitySettings: Option[PassivationStrategySettings.IdleSettings],
activeEntityLimit: Option[Int],
replacementPolicySettings: Option[PassivationStrategySettings.PolicySettings]) =
this(idleEntitySettings, activeEntityLimit, replacementPolicySettings, oldSettingUsed = false)
import PassivationStrategySettings._
def withIdleStrategy(settings: IdleSettings): PassivationStrategySettings =
copy(strategy = "idle", idleSettings = settings, oldSettingUsed = false)
def withIdleEntityPassivation(settings: IdleSettings): PassivationStrategySettings =
copy(idleEntitySettings = Some(settings), oldSettingUsed = false)
def withLeastRecentlyUsedStrategy(settings: LeastRecentlyUsedSettings): PassivationStrategySettings =
copy(strategy = "least-recently-used", leastRecentlyUsedSettings = settings)
def withIdleEntityPassivation(timeout: FiniteDuration): PassivationStrategySettings =
withIdleEntityPassivation(IdleSettings.defaults.withTimeout(timeout))
def withMostRecentlyUsedStrategy(settings: MostRecentlyUsedSettings): PassivationStrategySettings =
copy(strategy = "most-recently-used", mostRecentlyUsedSettings = settings)
def withIdleEntityPassivation(timeout: FiniteDuration, interval: FiniteDuration): PassivationStrategySettings =
withIdleEntityPassivation(IdleSettings.defaults.withTimeout(timeout).withInterval(interval))
def withLeastFrequentlyUsedStrategy(settings: LeastFrequentlyUsedSettings): PassivationStrategySettings =
copy(strategy = "least-frequently-used", leastFrequentlyUsedSettings = settings)
def withIdleEntityPassivation(timeout: java.time.Duration): PassivationStrategySettings =
withIdleEntityPassivation(IdleSettings.defaults.withTimeout(timeout))
def withIdleEntityPassivation(
timeout: java.time.Duration,
interval: java.time.Duration): PassivationStrategySettings =
withIdleEntityPassivation(IdleSettings.defaults.withTimeout(timeout).withInterval(interval))
def withActiveEntityLimit(limit: Int): PassivationStrategySettings =
copy(activeEntityLimit = Some(limit))
def withReplacementPolicy(settings: PolicySettings): PassivationStrategySettings =
copy(replacementPolicySettings = Some(settings))
def withLeastRecentlyUsedReplacement(): PassivationStrategySettings =
withReplacementPolicy(LeastRecentlyUsedSettings.defaults)
def withMostRecentlyUsedReplacement(): PassivationStrategySettings =
withReplacementPolicy(MostRecentlyUsedSettings.defaults)
def withLeastFrequentlyUsedReplacement(): PassivationStrategySettings =
withReplacementPolicy(LeastFrequentlyUsedSettings.defaults)
private[akka] def withOldIdleStrategy(timeout: FiniteDuration): PassivationStrategySettings =
copy(strategy = "idle", idleSettings = idleSettings.withTimeout(timeout), oldSettingUsed = true)
copy(
idleEntitySettings = Some(new IdleSettings(timeout, None)),
activeEntityLimit = None,
replacementPolicySettings = None,
oldSettingUsed = true)
private def copy(
strategy: String,
idleSettings: IdleSettings = idleSettings,
leastRecentlyUsedSettings: LeastRecentlyUsedSettings = leastRecentlyUsedSettings,
mostRecentlyUsedSettings: MostRecentlyUsedSettings = mostRecentlyUsedSettings,
leastFrequentlyUsedSettings: LeastFrequentlyUsedSettings = leastFrequentlyUsedSettings,
idleEntitySettings: Option[IdleSettings] = idleEntitySettings,
activeEntityLimit: Option[Int] = activeEntityLimit,
replacementPolicySettings: Option[PolicySettings] = replacementPolicySettings,
oldSettingUsed: Boolean = oldSettingUsed): PassivationStrategySettings =
new PassivationStrategySettings(
strategy,
idleSettings,
leastRecentlyUsedSettings,
mostRecentlyUsedSettings,
leastFrequentlyUsedSettings,
oldSettingUsed)
new PassivationStrategySettings(idleEntitySettings, activeEntityLimit, replacementPolicySettings, oldSettingUsed)
}
object PassivationStrategySettings {
val defaults = new PassivationStrategySettings(
strategy = "idle",
IdleSettings.defaults,
LeastRecentlyUsedSettings.defaults,
MostRecentlyUsedSettings.defaults,
LeastFrequentlyUsedSettings.defaults,
idleEntitySettings = None,
activeEntityLimit = None,
replacementPolicySettings = None,
oldSettingUsed = false)
val disabled: PassivationStrategySettings = defaults.copy(strategy = "none")
val disabled: PassivationStrategySettings = defaults
object IdleSettings {
val defaults: IdleSettings = new IdleSettings(timeout = 2.minutes, interval = None)
@ -207,9 +211,11 @@ object ClusterShardingSettings {
new IdleSettings(timeout, interval)
}
def optional(config: Config): Option[IdleSettings] = {
if (toRootLowerCase(config.getString("timeout")) == "off") None else Some(IdleSettings(config))
}
def optional(config: Config): Option[IdleSettings] =
toRootLowerCase(config.getString("timeout")) match {
case "off" | "none" => None
case _ => Some(IdleSettings(config))
}
}
final class IdleSettings(val timeout: FiniteDuration, val interval: Option[FiniteDuration]) {
@ -226,15 +232,29 @@ object ClusterShardingSettings {
new IdleSettings(timeout, interval)
}
object PolicySettings {
def apply(config: Config): PolicySettings =
toRootLowerCase(config.getString("policy")) match {
case "least-recently-used" => LeastRecentlyUsedSettings(config.getConfig("least-recently-used"))
case "most-recently-used" => MostRecentlyUsedSettings(config.getConfig("most-recently-used"))
case "least-frequently-used" => LeastFrequentlyUsedSettings(config.getConfig("least-frequently-used"))
}
def optional(config: Config): Option[PolicySettings] =
toRootLowerCase(config.getString("policy")) match {
case "off" | "none" => None
case _ => Some(PolicySettings(config))
}
}
sealed trait PolicySettings
object LeastRecentlyUsedSettings {
val defaults: LeastRecentlyUsedSettings =
new LeastRecentlyUsedSettings(limit = 100000, segmentedSettings = None, idleSettings = None)
val defaults: LeastRecentlyUsedSettings = new LeastRecentlyUsedSettings(segmentedSettings = None)
def apply(config: Config): LeastRecentlyUsedSettings = {
val limit = config.getInt("limit")
val idleSettings = IdleSettings.optional(config.getConfig("idle"))
val segmentedSettings = SegmentedSettings.optional(config.getConfig("segmented"))
new LeastRecentlyUsedSettings(limit, segmentedSettings, idleSettings)
new LeastRecentlyUsedSettings(segmentedSettings)
}
object SegmentedSettings {
@ -266,133 +286,78 @@ object ClusterShardingSettings {
}
}
final class LeastRecentlyUsedSettings(
val limit: Int,
val segmentedSettings: Option[LeastRecentlyUsedSettings.SegmentedSettings],
val idleSettings: Option[IdleSettings]) {
final class LeastRecentlyUsedSettings(val segmentedSettings: Option[LeastRecentlyUsedSettings.SegmentedSettings])
extends PolicySettings {
import LeastRecentlyUsedSettings.SegmentedSettings
def withLimit(limit: Int): LeastRecentlyUsedSettings = copy(limit = limit)
def withSegmented(levels: Int): LeastRecentlyUsedSettings =
copy(segmentedSettings = Some(new SegmentedSettings(levels, Nil)))
def withSegmented(levels: Int): LeastRecentlyUsedSettings = withSegmented(levels, Nil)
def withSegmented(proportions: immutable.Seq[Double]): LeastRecentlyUsedSettings =
copy(segmentedSettings = Some(new SegmentedSettings(proportions.size, proportions)))
def withSegmented(levels: Int, proportions: immutable.Seq[Double]): LeastRecentlyUsedSettings =
copy(segmentedSettings = Some(new LeastRecentlyUsedSettings.SegmentedSettings(levels, proportions)))
def withSegmentedProportions(proportions: java.util.List[java.lang.Double]): LeastRecentlyUsedSettings =
withSegmented(immutableSeq(proportions).map(_.toDouble))
def withSegmentedProportions(
levels: Int,
proportions: java.util.List[java.lang.Double]): LeastRecentlyUsedSettings =
withSegmented(levels, immutableSeq(proportions).map(_.toDouble))
def withIdle(timeout: FiniteDuration): LeastRecentlyUsedSettings =
copy(idleSettings = Some(new IdleSettings(timeout, None)))
def withIdle(timeout: java.time.Duration): LeastRecentlyUsedSettings =
withIdle(timeout.asScala)
def withIdle(timeout: FiniteDuration, interval: FiniteDuration): LeastRecentlyUsedSettings =
copy(idleSettings = Some(new IdleSettings(timeout, Some(interval))))
def withIdle(timeout: java.time.Duration, interval: java.time.Duration): LeastRecentlyUsedSettings =
withIdle(timeout.asScala, interval.asScala)
private def copy(
limit: Int = limit,
segmentedSettings: Option[LeastRecentlyUsedSettings.SegmentedSettings] = segmentedSettings,
idleSettings: Option[IdleSettings] = idleSettings): LeastRecentlyUsedSettings =
new LeastRecentlyUsedSettings(limit, segmentedSettings, idleSettings)
private def copy(segmentedSettings: Option[SegmentedSettings]): LeastRecentlyUsedSettings =
new LeastRecentlyUsedSettings(segmentedSettings)
}
object MostRecentlyUsedSettings {
val defaults: MostRecentlyUsedSettings = new MostRecentlyUsedSettings(limit = 100000, idleSettings = None)
val defaults: MostRecentlyUsedSettings = new MostRecentlyUsedSettings
def apply(config: Config): MostRecentlyUsedSettings = {
val limit = config.getInt("limit")
val idleSettings = IdleSettings.optional(config.getConfig("idle"))
new MostRecentlyUsedSettings(limit, idleSettings)
val _ = config // not used
new MostRecentlyUsedSettings
}
}
final class MostRecentlyUsedSettings(val limit: Int, val idleSettings: Option[IdleSettings]) {
def withLimit(limit: Int): MostRecentlyUsedSettings = copy(limit = limit)
def withIdle(timeout: FiniteDuration): MostRecentlyUsedSettings =
copy(idleSettings = Some(new IdleSettings(timeout, None)))
def withIdle(timeout: java.time.Duration): MostRecentlyUsedSettings =
withIdle(timeout.asScala)
def withIdle(timeout: FiniteDuration, interval: FiniteDuration): MostRecentlyUsedSettings =
copy(idleSettings = Some(new IdleSettings(timeout, Some(interval))))
def withIdle(timeout: java.time.Duration, interval: java.time.Duration): MostRecentlyUsedSettings =
withIdle(timeout.asScala, interval.asScala)
private def copy(
limit: Int = limit,
idleSettings: Option[IdleSettings] = idleSettings): MostRecentlyUsedSettings =
new MostRecentlyUsedSettings(limit, idleSettings)
}
final class MostRecentlyUsedSettings extends PolicySettings
object LeastFrequentlyUsedSettings {
val defaults: LeastFrequentlyUsedSettings =
new LeastFrequentlyUsedSettings(limit = 100000, dynamicAging = false, idleSettings = None)
val defaults: LeastFrequentlyUsedSettings = new LeastFrequentlyUsedSettings(dynamicAging = false)
def apply(config: Config): LeastFrequentlyUsedSettings = {
val limit = config.getInt("limit")
val dynamicAging = config.getBoolean("dynamic-aging")
val idleSettings = IdleSettings.optional(config.getConfig("idle"))
new LeastFrequentlyUsedSettings(limit, dynamicAging, idleSettings)
new LeastFrequentlyUsedSettings(dynamicAging)
}
}
final class LeastFrequentlyUsedSettings(
val limit: Int,
val dynamicAging: Boolean,
val idleSettings: Option[IdleSettings]) {
def withLimit(limit: Int): LeastFrequentlyUsedSettings = copy(limit = limit)
final class LeastFrequentlyUsedSettings(val dynamicAging: Boolean) extends PolicySettings {
def withDynamicAging(): LeastFrequentlyUsedSettings = withDynamicAging(enabled = true)
def withDynamicAging(enabled: Boolean): LeastFrequentlyUsedSettings = copy(dynamicAging = enabled)
def withIdle(timeout: FiniteDuration): LeastFrequentlyUsedSettings =
copy(idleSettings = Some(new IdleSettings(timeout, None)))
def withIdle(timeout: java.time.Duration): LeastFrequentlyUsedSettings =
withIdle(timeout.asScala)
def withIdle(timeout: FiniteDuration, interval: FiniteDuration): LeastFrequentlyUsedSettings =
copy(idleSettings = Some(new IdleSettings(timeout, Some(interval))))
def withIdle(timeout: java.time.Duration, interval: java.time.Duration): LeastFrequentlyUsedSettings =
withIdle(timeout.asScala, interval.asScala)
private def copy(
limit: Int = limit,
dynamicAging: Boolean = dynamicAging,
idleSettings: Option[IdleSettings] = idleSettings): LeastFrequentlyUsedSettings =
new LeastFrequentlyUsedSettings(limit, dynamicAging, idleSettings)
private def copy(dynamicAging: Boolean): LeastFrequentlyUsedSettings =
new LeastFrequentlyUsedSettings(dynamicAging)
}
def apply(config: Config): PassivationStrategySettings = {
val settings =
new PassivationStrategySettings(
strategy = toRootLowerCase(config.getString("passivation.strategy")),
idleSettings = IdleSettings(config.getConfig("passivation.idle")),
leastRecentlyUsedSettings = LeastRecentlyUsedSettings(config.getConfig("passivation.least-recently-used")),
mostRecentlyUsedSettings = MostRecentlyUsedSettings(config.getConfig("passivation.most-recently-used")),
leastFrequentlyUsedSettings = LeastFrequentlyUsedSettings(
config.getConfig("passivation.least-frequently-used")))
toRootLowerCase(config.getString("strategy")) match {
case "off" | "none" => PassivationStrategySettings.disabled
case strategyName =>
val strategyDefaults = config.getConfig("strategy-defaults")
val strategyConfig = config.getConfig(strategyName).withFallback(strategyDefaults)
val idleEntitySettings = IdleSettings.optional(strategyConfig.getConfig("idle-entity"))
val activeEntityLimit = strategyConfig.getString("active-entity-limit") match {
case "off" | "none" => None
case _ => Some(strategyConfig.getInt("active-entity-limit"))
}
val replacementPolicySettings = PolicySettings.optional(strategyConfig.getConfig("replacement"))
new PassivationStrategySettings(idleEntitySettings, activeEntityLimit, replacementPolicySettings)
}
}
def fromSharding(shardingConfig: Config): PassivationStrategySettings = {
// default to old setting if it exists (defined in application.conf), overriding the new settings
if (config.hasPath("passivate-idle-entity-after")) {
if (shardingConfig.hasPath("passivate-idle-entity-after")) {
val timeout =
if (toRootLowerCase(config.getString("passivate-idle-entity-after")) == "off") Duration.Zero
else config.getDuration("passivate-idle-entity-after", MILLISECONDS).millis
settings.withOldIdleStrategy(timeout)
if (toRootLowerCase(shardingConfig.getString("passivate-idle-entity-after")) == "off") Duration.Zero
else shardingConfig.getDuration("passivate-idle-entity-after", MILLISECONDS).millis
oldDefault(timeout)
} else {
settings
PassivationStrategySettings(shardingConfig.getConfig("passivation"))
}
}
@ -417,9 +382,10 @@ object ClusterShardingSettings {
extends PassivationStrategy
private[akka] object LeastRecentlyUsedPassivationStrategy {
def apply(settings: PassivationStrategySettings.LeastRecentlyUsedSettings): LeastRecentlyUsedPassivationStrategy = {
val limit = settings.limit
val idle = settings.idleSettings.map(IdlePassivationStrategy.apply)
def apply(
settings: PassivationStrategySettings.LeastRecentlyUsedSettings,
limit: Int,
idle: Option[IdlePassivationStrategy]): LeastRecentlyUsedPassivationStrategy = {
settings.segmentedSettings match {
case Some(segmented) =>
val proportions =
@ -438,21 +404,15 @@ object ClusterShardingSettings {
idle: Option[IdlePassivationStrategy])
extends PassivationStrategy
private[akka] object MostRecentlyUsedPassivationStrategy {
def apply(settings: PassivationStrategySettings.MostRecentlyUsedSettings): MostRecentlyUsedPassivationStrategy =
MostRecentlyUsedPassivationStrategy(settings.limit, settings.idleSettings.map(IdlePassivationStrategy.apply))
}
private[akka] case class MostRecentlyUsedPassivationStrategy(limit: Int, idle: Option[IdlePassivationStrategy])
extends PassivationStrategy
private[akka] object LeastFrequentlyUsedPassivationStrategy {
def apply(
settings: PassivationStrategySettings.LeastFrequentlyUsedSettings): LeastFrequentlyUsedPassivationStrategy =
LeastFrequentlyUsedPassivationStrategy(
settings.limit,
settings.dynamicAging,
settings.idleSettings.map(IdlePassivationStrategy.apply))
settings: PassivationStrategySettings.LeastFrequentlyUsedSettings,
limit: Int,
idle: Option[IdlePassivationStrategy]): LeastFrequentlyUsedPassivationStrategy =
LeastFrequentlyUsedPassivationStrategy(limit, settings.dynamicAging, idle)
}
private[akka] case class LeastFrequentlyUsedPassivationStrategy(
@ -467,22 +427,28 @@ object ClusterShardingSettings {
*/
@InternalApi
private[akka] object PassivationStrategy {
def apply(settings: ClusterShardingSettings): PassivationStrategy = {
def apply(settings: ClusterShardingSettings): PassivationStrategy =
if (settings.rememberEntities) {
NoPassivationStrategy
} else
settings.passivationStrategySettings.strategy match {
case "idle" if settings.passivationStrategySettings.idleSettings.timeout > Duration.Zero =>
IdlePassivationStrategy(settings.passivationStrategySettings.idleSettings)
case "least-recently-used" =>
LeastRecentlyUsedPassivationStrategy(settings.passivationStrategySettings.leastRecentlyUsedSettings)
case "most-recently-used" =>
MostRecentlyUsedPassivationStrategy(settings.passivationStrategySettings.mostRecentlyUsedSettings)
case "least-frequently-used" =>
LeastFrequentlyUsedPassivationStrategy(settings.passivationStrategySettings.leastFrequentlyUsedSettings)
case _ => NoPassivationStrategy
} else {
val idle = settings.passivationStrategySettings.idleEntitySettings match {
case Some(idleSettings) if idleSettings.timeout > Duration.Zero => Some(IdlePassivationStrategy(idleSettings))
case _ => None
}
}
settings.passivationStrategySettings.activeEntityLimit match {
case Some(limit) =>
settings.passivationStrategySettings.replacementPolicySettings match {
case Some(settings: PassivationStrategySettings.LeastRecentlyUsedSettings) =>
LeastRecentlyUsedPassivationStrategy(settings, limit, idle)
case Some(_: PassivationStrategySettings.MostRecentlyUsedSettings) =>
MostRecentlyUsedPassivationStrategy(limit, idle)
case Some(settings: PassivationStrategySettings.LeastFrequentlyUsedSettings) =>
LeastFrequentlyUsedPassivationStrategy(settings, limit, idle)
case _ => idle.getOrElse(NoPassivationStrategy)
}
case _ => idle.getOrElse(NoPassivationStrategy)
}
}
}
class TuningParameters(
@ -872,14 +838,15 @@ final class ClusterShardingSettings(
def withStateStoreMode(stateStoreMode: String): ClusterShardingSettings =
copy(stateStoreMode = stateStoreMode)
@deprecated("See passivationStrategySettings.idleTimeout instead", since = "2.6.18")
def passivateIdleEntityAfter: FiniteDuration = passivationStrategySettings.idleSettings.timeout
@deprecated("See passivationStrategySettings.idleEntitySettings instead", since = "2.6.18")
def passivateIdleEntityAfter: FiniteDuration =
passivationStrategySettings.idleEntitySettings.fold(Duration.Zero)(_.timeout)
@deprecated("Use withIdlePassivationStrategy instead", since = "2.6.18")
@deprecated("Use withPassivationStrategy instead", since = "2.6.18")
def withPassivateIdleAfter(duration: FiniteDuration): ClusterShardingSettings =
copy(passivationStrategySettings = passivationStrategySettings.withOldIdleStrategy(duration))
@deprecated("Use withIdlePassivationStrategy instead", since = "2.6.18")
@deprecated("Use withPassivationStrategy instead", since = "2.6.18")
def withPassivateIdleAfter(duration: java.time.Duration): ClusterShardingSettings =
copy(passivationStrategySettings = passivationStrategySettings.withOldIdleStrategy(duration.asScala))
@ -889,24 +856,6 @@ final class ClusterShardingSettings(
def withNoPassivationStrategy(): ClusterShardingSettings =
copy(passivationStrategySettings = ClusterShardingSettings.PassivationStrategySettings.disabled)
def withIdlePassivationStrategy(
settings: ClusterShardingSettings.PassivationStrategySettings.IdleSettings): ClusterShardingSettings =
copy(passivationStrategySettings = passivationStrategySettings.withIdleStrategy(settings))
def withLeastRecentlyUsedPassivationStrategy(
settings: ClusterShardingSettings.PassivationStrategySettings.LeastRecentlyUsedSettings)
: ClusterShardingSettings =
copy(passivationStrategySettings = passivationStrategySettings.withLeastRecentlyUsedStrategy(settings))
def withMostRecentlyUsedPassivationStrategy(
settings: ClusterShardingSettings.PassivationStrategySettings.MostRecentlyUsedSettings): ClusterShardingSettings =
copy(passivationStrategySettings = passivationStrategySettings.withMostRecentlyUsedStrategy(settings))
def withLeastFrequentlyUsedPassivationStrategy(
settings: ClusterShardingSettings.PassivationStrategySettings.LeastFrequentlyUsedSettings)
: ClusterShardingSettings =
copy(passivationStrategySettings = passivationStrategySettings.withLeastFrequentlyUsedStrategy(settings))
def withShardRegionQueryTimeout(duration: FiniteDuration): ClusterShardingSettings =
copy(shardRegionQueryTimeout = duration)

View file

@ -674,7 +674,8 @@ private[akka] class ShardRegion(
if (settings.passivationStrategySettings.oldSettingUsed) {
log.warning(
"The `akka.cluster.sharding.passivate-idle-entity-after` setting and associated methods are deprecated. " +
"See automatic passivation strategies and use the `akka.cluster.sharding.passivation.idle.timeout` setting.")
"Use the `akka.cluster.sharding.passivation.default-idle-strategy.idle-entity.timeout` setting instead. " +
"See the documentation and reference config for more information on automatic passivation strategies.")
}
if (settings.rememberEntities) {
log.debug("{}: Entities will not be passivated automatically because 'rememberEntities' is enabled.", typeName)

View file

@ -35,10 +35,8 @@ class ClusterShardingSettingsSpec extends AnyWordSpec with Matchers {
"allow timeout for (default) idle passivation strategy to be configured (via config)" in {
settings("""
#passivation-idle-timeout
akka.cluster.sharding {
passivation {
idle.timeout = 3 minutes
}
akka.cluster.sharding.passivation {
default-idle-strategy.idle-entity.timeout = 3 minutes
}
#passivation-idle-timeout
""").passivationStrategy shouldBe ClusterShardingSettings.IdlePassivationStrategy(
@ -48,8 +46,8 @@ class ClusterShardingSettingsSpec extends AnyWordSpec with Matchers {
"allow timeout for (default) idle passivation strategy to be configured (via factory method)" in {
defaultSettings
.withIdlePassivationStrategy(
ClusterShardingSettings.PassivationStrategySettings.IdleSettings.defaults.withTimeout(42.seconds))
.withPassivationStrategy(
ClusterShardingSettings.PassivationStrategySettings.defaults.withIdleEntityPassivation(timeout = 42.seconds))
.passivationStrategy shouldBe ClusterShardingSettings.IdlePassivationStrategy(
timeout = 42.seconds,
interval = 21.seconds)
@ -57,9 +55,9 @@ class ClusterShardingSettingsSpec extends AnyWordSpec with Matchers {
"allow timeout and interval for (default) idle passivation strategy to be configured (via config)" in {
settings("""
akka.cluster.sharding {
passivation {
idle {
akka.cluster.sharding.passivation {
default-idle-strategy {
idle-entity {
timeout = 3 minutes
interval = 1 minute
}
@ -72,25 +70,71 @@ class ClusterShardingSettingsSpec extends AnyWordSpec with Matchers {
"allow timeout and interval for (default) idle passivation strategy to be configured (via factory method)" in {
defaultSettings
.withIdlePassivationStrategy(
ClusterShardingSettings.PassivationStrategySettings.IdleSettings.defaults
.withTimeout(42.seconds)
.withInterval(42.millis))
.withPassivationStrategy(ClusterShardingSettings.PassivationStrategySettings.defaults
.withIdleEntityPassivation(timeout = 42.seconds, interval = 42.millis))
.passivationStrategy shouldBe ClusterShardingSettings.IdlePassivationStrategy(
timeout = 42.seconds,
interval = 42.millis)
}
"allow least recently used passivation strategy to be configured (via config)" in {
"allow new default passivation strategy to be enabled (via config)" in {
settings("""
#passivation-least-recently-used
akka.cluster.sharding {
passivation {
strategy = least-recently-used
least-recently-used.limit = 1000000
#passivation-new-default-strategy
akka.cluster.sharding.passivation {
strategy = default-strategy
}
#passivation-new-default-strategy
""").passivationStrategy shouldBe ClusterShardingSettings.LeastRecentlyUsedPassivationStrategy(
limit = 100000,
segmented = List(0.2, 0.8),
idle = None)
}
"allow new default passivation strategy limit to be configured (via config)" in {
settings("""
#passivation-new-default-strategy-configured
akka.cluster.sharding.passivation {
strategy = default-strategy
default-strategy {
active-entity-limit = 1000000
}
}
#passivation-least-recently-used
#passivation-new-default-strategy-configured
""").passivationStrategy shouldBe ClusterShardingSettings.LeastRecentlyUsedPassivationStrategy(
limit = 1000000,
segmented = List(0.2, 0.8),
idle = None)
}
"allow new default passivation strategy with idle timeout to be configured (via config)" in {
settings("""
#passivation-new-default-strategy-with-idle
akka.cluster.sharding.passivation {
strategy = default-strategy
default-strategy {
idle-entity.timeout = 30.minutes
}
}
#passivation-new-default-strategy-with-idle
""").passivationStrategy shouldBe ClusterShardingSettings.LeastRecentlyUsedPassivationStrategy(
limit = 100000,
segmented = List(0.2, 0.8),
idle = Some(ClusterShardingSettings.IdlePassivationStrategy(timeout = 30.minutes, interval = 15.minutes)))
}
"allow least recently used passivation strategy to be configured (via config)" in {
settings("""
#custom-passivation-strategy
#lru-policy
akka.cluster.sharding.passivation {
strategy = custom-lru-strategy
custom-lru-strategy {
active-entity-limit = 1000000
replacement.policy = least-recently-used
}
}
#lru-policy
#custom-passivation-strategy
""").passivationStrategy shouldBe ClusterShardingSettings.LeastRecentlyUsedPassivationStrategy(
limit = 1000000,
segmented = Nil,
@ -99,8 +143,10 @@ class ClusterShardingSettingsSpec extends AnyWordSpec with Matchers {
"allow least recently used passivation strategy to be configured (via factory method)" in {
defaultSettings
.withLeastRecentlyUsedPassivationStrategy(
ClusterShardingSettings.PassivationStrategySettings.LeastRecentlyUsedSettings.defaults.withLimit(42000))
.withPassivationStrategy(
ClusterShardingSettings.PassivationStrategySettings.defaults
.withActiveEntityLimit(42000)
.withLeastRecentlyUsedReplacement())
.passivationStrategy shouldBe ClusterShardingSettings.LeastRecentlyUsedPassivationStrategy(
limit = 42000,
segmented = Nil,
@ -109,20 +155,23 @@ class ClusterShardingSettingsSpec extends AnyWordSpec with Matchers {
"allow segmented least recently used passivation strategy to be configured (via config)" in {
settings("""
#passivation-segmented-least-recently-used
akka.cluster.sharding {
passivation {
strategy = least-recently-used
least-recently-used {
limit = 1000000
segmented {
levels = 2
proportions = [0.2, 0.8]
#slru-policy
akka.cluster.sharding.passivation {
strategy = custom-slru-strategy
custom-slru-strategy {
active-entity-limit = 1000000
replacement {
policy = least-recently-used
least-recently-used {
segmented {
levels = 2
proportions = [0.2, 0.8]
}
}
}
}
}
#passivation-segmented-least-recently-used
#slru-policy
""").passivationStrategy shouldBe ClusterShardingSettings.LeastRecentlyUsedPassivationStrategy(
limit = 1000000,
segmented = List(0.2, 0.8),
@ -131,17 +180,20 @@ class ClusterShardingSettingsSpec extends AnyWordSpec with Matchers {
"allow 4-level segmented least recently used passivation strategy to be configured (via config)" in {
settings("""
#passivation-s4-least-recently-used
akka.cluster.sharding {
passivation {
strategy = least-recently-used
least-recently-used {
limit = 1000000
segmented.levels = 4
#s4lru-policy
akka.cluster.sharding.passivation {
strategy = custom-s4lru-strategy
custom-s4lru-strategy {
active-entity-limit = 1000000
replacement {
policy = least-recently-used
least-recently-used {
segmented.levels = 4
}
}
}
}
#passivation-s4-least-recently-used
#s4lru-policy
""").passivationStrategy shouldBe ClusterShardingSettings.LeastRecentlyUsedPassivationStrategy(
limit = 1000000,
segmented = List(0.25, 0.25, 0.25, 0.25),
@ -150,10 +202,10 @@ class ClusterShardingSettingsSpec extends AnyWordSpec with Matchers {
"allow segmented least recently used passivation strategy to be configured (via factory method)" in {
defaultSettings
.withLeastRecentlyUsedPassivationStrategy(
ClusterShardingSettings.PassivationStrategySettings.LeastRecentlyUsedSettings.defaults
.withLimit(42000)
.withSegmented(levels = 4, proportions = List(0.4, 0.3, 0.2, 0.1)))
.withPassivationStrategy(ClusterShardingSettings.PassivationStrategySettings.defaults
.withActiveEntityLimit(42000)
.withReplacementPolicy(ClusterShardingSettings.PassivationStrategySettings.LeastRecentlyUsedSettings.defaults
.withSegmented(proportions = List(0.4, 0.3, 0.2, 0.1))))
.passivationStrategy shouldBe ClusterShardingSettings.LeastRecentlyUsedPassivationStrategy(
limit = 42000,
segmented = List(0.4, 0.3, 0.2, 0.1),
@ -162,17 +214,14 @@ class ClusterShardingSettingsSpec extends AnyWordSpec with Matchers {
"allow least recently used passivation strategy with idle timeout to be configured (via config)" in {
settings("""
#passivation-least-recently-used-with-idle
akka.cluster.sharding {
passivation {
strategy = least-recently-used
least-recently-used {
limit = 1000000
idle.timeout = 30.minutes
}
akka.cluster.sharding.passivation {
strategy = custom-lru-with-idle
custom-lru-with-idle {
active-entity-limit = 1000000
replacement.policy = least-recently-used
idle-entity.timeout = 30.minutes
}
}
#passivation-least-recently-used-with-idle
""").passivationStrategy shouldBe ClusterShardingSettings.LeastRecentlyUsedPassivationStrategy(
limit = 1000000,
segmented = Nil,
@ -181,10 +230,11 @@ class ClusterShardingSettingsSpec extends AnyWordSpec with Matchers {
"allow least recently used passivation strategy with idle timeout to be configured (via factory method)" in {
defaultSettings
.withLeastRecentlyUsedPassivationStrategy(
ClusterShardingSettings.PassivationStrategySettings.LeastRecentlyUsedSettings.defaults
.withLimit(42000)
.withIdle(timeout = 42.minutes))
.withPassivationStrategy(
ClusterShardingSettings.PassivationStrategySettings.defaults
.withActiveEntityLimit(42000)
.withLeastRecentlyUsedReplacement()
.withIdleEntityPassivation(timeout = 42.minutes))
.passivationStrategy shouldBe ClusterShardingSettings.LeastRecentlyUsedPassivationStrategy(
limit = 42000,
segmented = Nil,
@ -193,14 +243,15 @@ class ClusterShardingSettingsSpec extends AnyWordSpec with Matchers {
"allow most recently used passivation strategy to be configured (via config)" in {
settings("""
#passivation-most-recently-used
akka.cluster.sharding {
passivation {
strategy = most-recently-used
most-recently-used.limit = 1000000
#mru-policy
akka.cluster.sharding.passivation {
strategy = custom-mru-strategy
custom-mru-strategy {
active-entity-limit = 1000000
replacement.policy = most-recently-used
}
}
#passivation-most-recently-used
#mru-policy
""").passivationStrategy shouldBe ClusterShardingSettings.MostRecentlyUsedPassivationStrategy(
limit = 1000000,
idle = None)
@ -208,8 +259,10 @@ class ClusterShardingSettingsSpec extends AnyWordSpec with Matchers {
"allow most recently used passivation strategy to be configured (via factory method)" in {
defaultSettings
.withMostRecentlyUsedPassivationStrategy(
ClusterShardingSettings.PassivationStrategySettings.MostRecentlyUsedSettings.defaults.withLimit(42000))
.withPassivationStrategy(
ClusterShardingSettings.PassivationStrategySettings.defaults
.withActiveEntityLimit(42000)
.withMostRecentlyUsedReplacement())
.passivationStrategy shouldBe ClusterShardingSettings.MostRecentlyUsedPassivationStrategy(
limit = 42000,
idle = None)
@ -217,17 +270,14 @@ class ClusterShardingSettingsSpec extends AnyWordSpec with Matchers {
"allow most recently used passivation strategy with idle timeout to be configured (via config)" in {
settings("""
#passivation-most-recently-used-with-idle
akka.cluster.sharding {
passivation {
strategy = most-recently-used
most-recently-used {
limit = 1000000
idle.timeout = 30.minutes
}
akka.cluster.sharding.passivation {
strategy = custom-mru-with-idle
custom-mru-with-idle {
active-entity-limit = 1000000
replacement.policy = most-recently-used
idle-entity.timeout = 30.minutes
}
}
#passivation-most-recently-used-with-idle
""").passivationStrategy shouldBe ClusterShardingSettings.MostRecentlyUsedPassivationStrategy(
limit = 1000000,
idle = Some(ClusterShardingSettings.IdlePassivationStrategy(timeout = 30.minutes, interval = 15.minutes)))
@ -235,10 +285,11 @@ class ClusterShardingSettingsSpec extends AnyWordSpec with Matchers {
"allow most recently used passivation strategy with idle timeout to be configured (via factory method)" in {
defaultSettings
.withMostRecentlyUsedPassivationStrategy(
ClusterShardingSettings.PassivationStrategySettings.MostRecentlyUsedSettings.defaults
.withLimit(42000)
.withIdle(timeout = 42.minutes))
.withPassivationStrategy(
ClusterShardingSettings.PassivationStrategySettings.defaults
.withActiveEntityLimit(42000)
.withMostRecentlyUsedReplacement()
.withIdleEntityPassivation(timeout = 42.minutes))
.passivationStrategy shouldBe ClusterShardingSettings.MostRecentlyUsedPassivationStrategy(
limit = 42000,
idle = Some(ClusterShardingSettings.IdlePassivationStrategy(timeout = 42.minutes, interval = 21.minutes)))
@ -246,14 +297,15 @@ class ClusterShardingSettingsSpec extends AnyWordSpec with Matchers {
"allow least frequently used passivation strategy to be configured (via config)" in {
settings("""
#passivation-least-frequently-used
akka.cluster.sharding {
passivation {
strategy = least-frequently-used
least-frequently-used.limit = 1000000
#lfu-policy
akka.cluster.sharding.passivation {
strategy = custom-lfu-strategy
custom-lfu-strategy {
active-entity-limit = 1000000
replacement.policy = least-frequently-used
}
}
#passivation-least-frequently-used
#lfu-policy
""").passivationStrategy shouldBe ClusterShardingSettings.LeastFrequentlyUsedPassivationStrategy(
limit = 1000000,
dynamicAging = false,
@ -262,8 +314,10 @@ class ClusterShardingSettingsSpec extends AnyWordSpec with Matchers {
"allow least frequently used passivation strategy to be configured (via factory method)" in {
defaultSettings
.withLeastFrequentlyUsedPassivationStrategy(
ClusterShardingSettings.PassivationStrategySettings.LeastFrequentlyUsedSettings.defaults.withLimit(42000))
.withPassivationStrategy(
ClusterShardingSettings.PassivationStrategySettings.defaults
.withActiveEntityLimit(42000)
.withLeastFrequentlyUsedReplacement())
.passivationStrategy shouldBe ClusterShardingSettings.LeastFrequentlyUsedPassivationStrategy(
limit = 42000,
dynamicAging = false,
@ -272,17 +326,14 @@ class ClusterShardingSettingsSpec extends AnyWordSpec with Matchers {
"allow least frequently used passivation strategy with idle timeout to be configured (via config)" in {
settings("""
#passivation-least-frequently-used-with-idle
akka.cluster.sharding {
passivation {
strategy = least-frequently-used
least-frequently-used {
limit = 1000000
idle.timeout = 30.minutes
}
akka.cluster.sharding.passivation {
strategy = custom-lfu-with-idle
custom-lfu-with-idle {
active-entity-limit = 1000000
replacement.policy = least-frequently-used
idle-entity.timeout = 30.minutes
}
}
#passivation-least-frequently-used-with-idle
""").passivationStrategy shouldBe ClusterShardingSettings.LeastFrequentlyUsedPassivationStrategy(
limit = 1000000,
dynamicAging = false,
@ -291,10 +342,11 @@ class ClusterShardingSettingsSpec extends AnyWordSpec with Matchers {
"allow least frequently used passivation strategy with idle timeout to be configured (via factory method)" in {
defaultSettings
.withLeastFrequentlyUsedPassivationStrategy(
ClusterShardingSettings.PassivationStrategySettings.LeastFrequentlyUsedSettings.defaults
.withLimit(42000)
.withIdle(timeout = 42.minutes))
.withPassivationStrategy(
ClusterShardingSettings.PassivationStrategySettings.defaults
.withActiveEntityLimit(42000)
.withLeastFrequentlyUsedReplacement()
.withIdleEntityPassivation(timeout = 42.minutes))
.passivationStrategy shouldBe ClusterShardingSettings.LeastFrequentlyUsedPassivationStrategy(
limit = 42000,
dynamicAging = false,
@ -303,17 +355,20 @@ class ClusterShardingSettingsSpec extends AnyWordSpec with Matchers {
"allow least frequently used passivation strategy with dynamic aging to be configured (via config)" in {
settings("""
#passivation-least-frequently-used-with-dynamic-aging
akka.cluster.sharding {
passivation {
strategy = least-frequently-used
least-frequently-used {
limit = 1000
dynamic-aging = on
#lfuda-policy
akka.cluster.sharding.passivation {
strategy = custom-lfu-with-dynamic-aging
custom-lfu-with-dynamic-aging {
active-entity-limit = 1000
replacement {
policy = least-frequently-used
least-frequently-used {
dynamic-aging = on
}
}
}
}
#passivation-least-frequently-used-with-dynamic-aging
#lfuda-policy
""").passivationStrategy shouldBe ClusterShardingSettings.LeastFrequentlyUsedPassivationStrategy(
limit = 1000,
dynamicAging = true,
@ -322,10 +377,12 @@ class ClusterShardingSettingsSpec extends AnyWordSpec with Matchers {
"allow least frequently used passivation strategy with dynamic aging to be configured (via factory method)" in {
defaultSettings
.withLeastFrequentlyUsedPassivationStrategy(
ClusterShardingSettings.PassivationStrategySettings.LeastFrequentlyUsedSettings.defaults
.withLimit(42000)
.withDynamicAging())
.withPassivationStrategy(
ClusterShardingSettings.PassivationStrategySettings.defaults
.withActiveEntityLimit(42000)
.withReplacementPolicy(
ClusterShardingSettings.PassivationStrategySettings.LeastFrequentlyUsedSettings.defaults
.withDynamicAging()))
.passivationStrategy shouldBe ClusterShardingSettings.LeastFrequentlyUsedPassivationStrategy(
limit = 42000,
dynamicAging = true,
@ -346,14 +403,14 @@ class ClusterShardingSettingsSpec extends AnyWordSpec with Matchers {
"disable automatic passivation if idle timeout is set to zero (via config)" in {
settings("""
akka.cluster.sharding.passivation.idle.timeout = 0
akka.cluster.sharding.passivation.default-idle-strategy.idle-entity.timeout = 0
""").passivationStrategy shouldBe ClusterShardingSettings.NoPassivationStrategy
}
"disable automatic passivation if idle timeout is set to zero (via factory method)" in {
defaultSettings
.withIdlePassivationStrategy(
ClusterShardingSettings.PassivationStrategySettings.IdleSettings.defaults.withTimeout(Duration.Zero))
.withPassivationStrategy(ClusterShardingSettings.PassivationStrategySettings.defaults.withIdleEntityPassivation(
timeout = Duration.Zero))
.passivationStrategy shouldBe ClusterShardingSettings.NoPassivationStrategy
}
@ -367,7 +424,7 @@ class ClusterShardingSettingsSpec extends AnyWordSpec with Matchers {
settings("""
akka.cluster.sharding {
passivate-idle-entity-after = 5 minutes
passivation-strategy = least-recently-used
passivation.strategy = default-strategy
}
""").passivationStrategy shouldBe ClusterShardingSettings.IdlePassivationStrategy(
timeout = 5.minutes,

View file

@ -36,7 +36,6 @@ object EntityPassivationSpec {
akka.cluster.sharding {
passivation {
strategy = none
idle.timeout = 1s
}
}
""").withFallback(config)
@ -84,8 +83,9 @@ abstract class AbstractEntityPassivationSpec(config: Config, expectedEntities: I
import EntityPassivationSpec._
val settings: ClusterShardingSettings = ClusterShardingSettings(system)
val configuredIdleTimeout: FiniteDuration = settings.passivationStrategySettings.idleSettings.timeout
val configuredLeastRecentlyUsedLimit: Int = settings.passivationStrategySettings.leastRecentlyUsedSettings.limit
val configuredIdleTimeout: FiniteDuration =
settings.passivationStrategySettings.idleEntitySettings.fold(Duration.Zero)(_.timeout)
val configuredActiveEntityLimit: Int = settings.passivationStrategySettings.activeEntityLimit.getOrElse(0)
val probes: Map[Int, TestProbe] = (1 to expectedEntities).map(id => id -> TestProbe()).toMap
val probeRefs: Map[String, ActorRef] = probes.map { case (id, probe) => id.toString -> probe.ref }
@ -137,7 +137,7 @@ class DisabledEntityPassivationSpec
val region = start()
region ! Envelope(shard = 1, id = 1, message = "A")
expectReceived(id = 1, message = "A")
expectNoMessage(id = 1, configuredIdleTimeout * 2)
expectNoMessage(id = 1, 1.second)
}
}
}

View file

@ -13,8 +13,7 @@ object IdleSpec {
val config: Config = ConfigFactory.parseString("""
akka.cluster.sharding {
passivation {
strategy = idle
idle.timeout = 1s
default-idle-strategy.idle-entity.timeout = 1s
}
}
""").withFallback(EntityPassivationSpec.config)

View file

@ -14,8 +14,11 @@ object LeastFrequentlyUsedSpec {
val config: Config = ConfigFactory.parseString("""
akka.cluster.sharding {
passivation {
strategy = least-frequently-used
least-frequently-used.limit = 10
strategy = lfu
lfu {
active-entity-limit = 10
replacement.policy = least-frequently-used
}
}
}
""").withFallback(EntityPassivationSpec.config)
@ -23,10 +26,15 @@ object LeastFrequentlyUsedSpec {
val dynamicAgingConfig: Config = ConfigFactory.parseString("""
akka.cluster.sharding {
passivation {
strategy = least-frequently-used
least-frequently-used {
limit = 10
dynamic-aging = on
strategy = lfuda
lfuda {
active-entity-limit = 10
replacement {
policy = least-frequently-used
least-frequently-used {
dynamic-aging = on
}
}
}
}
}
@ -35,17 +43,18 @@ object LeastFrequentlyUsedSpec {
val idleConfig: Config = ConfigFactory.parseString("""
akka.cluster.sharding {
passivation {
strategy = least-frequently-used
least-frequently-used {
limit = 3
idle.timeout = 1s
strategy = lfu-idle
lfu-idle {
active-entity-limit = 3
replacement.policy = least-frequently-used
idle-entity.timeout = 1s
}
}
}
""").withFallback(EntityPassivationSpec.config)
}
class LeastFrequentlyUsedEntityPassivationSpec
class LeastFrequentlyUsedSpec
extends AbstractEntityPassivationSpec(LeastFrequentlyUsedSpec.config, expectedEntities = 40) {
import EntityPassivationSpec.Entity.Envelope
@ -196,7 +205,7 @@ class LeastFrequentlyUsedEntityPassivationSpec
}
}
class LeastFrequentlyUsedWithDynamicAgingEntityPassivationSpec
class LeastFrequentlyUsedWithDynamicAgingSpec
extends AbstractEntityPassivationSpec(LeastFrequentlyUsedSpec.dynamicAgingConfig, expectedEntities = 21) {
import EntityPassivationSpec.Entity.Envelope
@ -273,7 +282,7 @@ class LeastFrequentlyUsedWithDynamicAgingEntityPassivationSpec
}
}
class LeastFrequentlyUsedWithIdleEntityPassivationSpec
class LeastFrequentlyUsedWithIdleSpec
extends AbstractEntityPassivationSpec(LeastFrequentlyUsedSpec.idleConfig, expectedEntities = 3) {
import EntityPassivationSpec.Entity.Envelope
@ -283,19 +292,17 @@ class LeastFrequentlyUsedWithIdleEntityPassivationSpec
"passivate entities when they haven't seen messages for the configured timeout" in {
val region = start()
val idleTimeout = settings.passivationStrategySettings.leastFrequentlyUsedSettings.idleSettings.get.timeout
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((idleTimeout / 2).toMillis)
Thread.sleep((configuredIdleTimeout / 2).toMillis)
region ! Envelope(shard = 1, id = 3, message = "D")
Thread.sleep((idleTimeout / 2).toMillis)
Thread.sleep((configuredIdleTimeout / 2).toMillis)
region ! Envelope(shard = 1, id = 3, message = "E")
Thread.sleep((idleTimeout / 2).toMillis)
Thread.sleep((configuredIdleTimeout / 2).toMillis)
val lastSendNanoTime2 = System.nanoTime()
region ! Envelope(shard = 1, id = 3, message = "F")
@ -307,13 +314,13 @@ class LeastFrequentlyUsedWithIdleEntityPassivationSpec
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 = idleTimeout * 2)
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 > idleTimeout
(passivate2.nanoTime - lastSendNanoTime1).nanos should be > idleTimeout
(passivate3.nanoTime - lastSendNanoTime2).nanos should be > idleTimeout
(passivate1.nanoTime - lastSendNanoTime1).nanos should be > configuredIdleTimeout
(passivate2.nanoTime - lastSendNanoTime1).nanos should be > configuredIdleTimeout
(passivate3.nanoTime - lastSendNanoTime2).nanos should be > configuredIdleTimeout
}
}
}

View file

@ -14,8 +14,11 @@ object LeastRecentlyUsedSpec {
val config: Config = ConfigFactory.parseString("""
akka.cluster.sharding {
passivation {
strategy = least-recently-used
least-recently-used.limit = 10
strategy = lru
lru {
active-entity-limit = 10
replacement.policy = least-recently-used
}
}
}
""").withFallback(EntityPassivationSpec.config)
@ -23,12 +26,17 @@ object LeastRecentlyUsedSpec {
val segmentedConfig: Config = ConfigFactory.parseString("""
akka.cluster.sharding {
passivation {
strategy = least-recently-used
least-recently-used {
limit = 10
segmented {
levels = 2
proportions = [0.2, 0.8]
strategy = slru
slru {
active-entity-limit = 10
replacement {
policy = least-recently-used
least-recently-used {
segmented {
levels = 2
proportions = [0.2, 0.8]
}
}
}
}
}
@ -38,10 +46,11 @@ object LeastRecentlyUsedSpec {
val idleConfig: Config = ConfigFactory.parseString("""
akka.cluster.sharding {
passivation {
strategy = least-recently-used
least-recently-used {
limit = 3
idle.timeout = 1s
strategy = lru-idle
lru-idle {
active-entity-limit = 3
replacement.policy = least-recently-used
idle-entity.timeout = 1s
}
}
}
@ -238,19 +247,17 @@ class LeastRecentlyUsedWithIdleSpec
"passivate entities when they haven't seen messages for the configured timeout" in {
val region = start()
val idleTimeout = settings.passivationStrategySettings.leastRecentlyUsedSettings.idleSettings.get.timeout
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((idleTimeout / 2).toMillis)
Thread.sleep((configuredIdleTimeout / 2).toMillis)
region ! Envelope(shard = 1, id = 3, message = "D")
Thread.sleep((idleTimeout / 2).toMillis)
Thread.sleep((configuredIdleTimeout / 2).toMillis)
region ! Envelope(shard = 1, id = 3, message = "E")
Thread.sleep((idleTimeout / 2).toMillis)
Thread.sleep((configuredIdleTimeout / 2).toMillis)
val lastSendNanoTime2 = System.nanoTime()
region ! Envelope(shard = 1, id = 3, message = "F")
@ -262,13 +269,13 @@ class LeastRecentlyUsedWithIdleSpec
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 = idleTimeout * 2)
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 > idleTimeout
(passivate2.nanoTime - lastSendNanoTime1).nanos should be > idleTimeout
(passivate3.nanoTime - lastSendNanoTime2).nanos should be > idleTimeout
(passivate1.nanoTime - lastSendNanoTime1).nanos should be > configuredIdleTimeout
(passivate2.nanoTime - lastSendNanoTime1).nanos should be > configuredIdleTimeout
(passivate3.nanoTime - lastSendNanoTime2).nanos should be > configuredIdleTimeout
}
}
}

View file

@ -14,8 +14,11 @@ object MostRecentlyUsedSpec {
val config: Config = ConfigFactory.parseString("""
akka.cluster.sharding {
passivation {
strategy = most-recently-used
most-recently-used.limit = 10
strategy = mru
mru {
active-entity-limit = 10
replacement.policy = most-recently-used
}
}
}
""").withFallback(EntityPassivationSpec.config)
@ -23,10 +26,11 @@ object MostRecentlyUsedSpec {
val idleConfig: Config = ConfigFactory.parseString("""
akka.cluster.sharding {
passivation {
strategy = most-recently-used
most-recently-used {
limit = 3
idle.timeout = 1s
strategy = mru-idle
mru-idle {
active-entity-limit = 3
replacement.policy = most-recently-used
idle-entity.timeout = 1s
}
}
}
@ -141,7 +145,7 @@ class MostRecentlyUsedSpec extends AbstractEntityPassivationSpec(MostRecentlyUse
}
}
class MostRecentlyUsedWithIdleEntityPassivationSpec
class MostRecentlyUsedWithIdleSpec
extends AbstractEntityPassivationSpec(MostRecentlyUsedSpec.idleConfig, expectedEntities = 3) {
import EntityPassivationSpec.Entity.Envelope
@ -151,19 +155,17 @@ class MostRecentlyUsedWithIdleEntityPassivationSpec
"passivate entities when they haven't seen messages for the configured timeout" in {
val region = start()
val idleTimeout = settings.passivationStrategySettings.mostRecentlyUsedSettings.idleSettings.get.timeout
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((idleTimeout / 2).toMillis)
Thread.sleep((configuredIdleTimeout / 2).toMillis)
region ! Envelope(shard = 1, id = 3, message = "D")
Thread.sleep((idleTimeout / 2).toMillis)
Thread.sleep((configuredIdleTimeout / 2).toMillis)
region ! Envelope(shard = 1, id = 3, message = "E")
Thread.sleep((idleTimeout / 2).toMillis)
Thread.sleep((configuredIdleTimeout / 2).toMillis)
val lastSendNanoTime2 = System.nanoTime()
region ! Envelope(shard = 1, id = 3, message = "F")
@ -175,13 +177,13 @@ class MostRecentlyUsedWithIdleEntityPassivationSpec
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 = idleTimeout * 2)
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 > idleTimeout
(passivate2.nanoTime - lastSendNanoTime1).nanos should be > idleTimeout
(passivate3.nanoTime - lastSendNanoTime2).nanos should be > idleTimeout
(passivate1.nanoTime - lastSendNanoTime1).nanos should be > configuredIdleTimeout
(passivate2.nanoTime - lastSendNanoTime1).nanos should be > configuredIdleTimeout
(passivate3.nanoTime - lastSendNanoTime2).nanos should be > configuredIdleTimeout
}
}
}

View file

@ -289,7 +289,10 @@ The stop message is only sent locally, from the shard to the entity so does not
## Automatic Passivation
Entities are automatically passivated based on a passivation strategy. The default passivation strategy is to
passivate idle entities when they haven't received a message within a specified interval.
[passivate idle entities](#idle-entity-passivation) when they haven't received a message within a specified interval,
and this is the current default strategy to maintain compatibility with earlier versions. It's recommended to switch to
a [passivation strategy with an active entity limit](#active-entity-limits) and a pre-configured default strategy is
provided. Active entity limits and idle entity timeouts can also be used together.
Automatic passivation can be disabled by setting `akka.cluster.sharding.passivation.strategy = none`. It is disabled
automatically if @ref:[Remembering Entities](#remembering-entities) is enabled.
@ -301,34 +304,78 @@ directly to the `ActorRef`, including messages that the actor sends to itself, a
@@@
Supported passivation strategies are:
### Idle entity passivation
### Idle passivation strategy
The **idle** passivation strategy passivates entities when they have not received a message for a specified length of
time. This is the default strategy and is enabled automatically with a timeout of 2 minutes. Specify a different idle
timeout with configuration:
Idle entities can be automatically passivated when they have not received a message for a specified length of time.
This is currently the default strategy, for compatibility, and is enabled automatically with a timeout of 2 minutes.
Specify a different idle timeout with configuration:
@@snip [passivation idle timeout](/akka-cluster-sharding/src/test/scala/akka/cluster/sharding/ClusterShardingSettingsSpec.scala) { #passivation-idle-timeout type=conf }
Or specify the idle timeout as a duration using the `withIdlePassivationStrategy` method on `ClusterShardingSettings`.
Or specify the idle timeout as a duration using the `withPassivationStrategy` method on `ClusterShardingSettings`.
### Least recently used passivation strategy
Idle entity timeouts can be enabled and configured for any passivation strategy.
The **least recently used** passivation strategy passivates those entities that have the least recent activity when the
number of active entities passes a specified limit. The configurable limit is for a whole shard region and is divided
evenly among the active shards in each region. Configure automatic passivation to use the least recently used
passivation strategy, and set the limit for active entities in a shard region:
### Active entity limits
@@snip [passivation least recently used](/akka-cluster-sharding/src/test/scala/akka/cluster/sharding/ClusterShardingSettingsSpec.scala) { #passivation-least-recently-used type=conf }
Automatic passivation strategies can limit the number of active entities. Limit-based passivation strategies use a
replacement policy to determine which active entities should be passivated when the active entity limit is exceeded.
The configurable limit is for a whole shard region and is divided evenly among the active shards in each region.
Or enable the least recently used passivation strategy and set the active entity limit using the
`withLeastRecentlyUsedPassivationStrategy` method on `ClusterShardingSettings`.
A recommended passivation strategy, which will become the new default passivation strategy in future versions of Akka
Cluster Sharding, can be enabled with configuration:
#### Segmented least recently used strategy
@@snip [passivation new default strategy](/akka-cluster-sharding/src/test/scala/akka/cluster/sharding/ClusterShardingSettingsSpec.scala) { #passivation-new-default-strategy type=conf }
A variation of the least recently used passivation strategy can be enabled that divides the active entity space into
multiple segments to introduce frequency information into the strategy. Higher-level segments contain entities that
This default strategy uses a [segmented least recently used policy](#segmented-least-recently-used-policy). The active
entity limit 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 }
Or using the `withActiveEntityLimit` method on `ClusterShardingSettings.PassivationStrategySettings`.
An [idle entity timeout](#idle-entity-passivation) can also be enabled and configured for this strategy:
@@snip [passivation new default strategy with idle](/akka-cluster-sharding/src/test/scala/akka/cluster/sharding/ClusterShardingSettingsSpec.scala) { #passivation-new-default-strategy-with-idle type=conf }
Or using the `withIdleEntityPassivation` method on `ClusterShardingSettings.PassivationStrategySettings`.
If the default strategy is not appropriate for particular workloads and access patterns, a [custom passivation
strategy](#custom-passivation-strategies) can be created with configurable replacement policies, active entity limits,
and idle entity timeouts.
### Custom passivation strategies
To configure a custom passivation strategy, create a configuration section for the strategy under
`akka.cluster.sharding.passivation` and select this strategy using the `strategy` setting. The strategy needs a
_replacement policy_ to be chosen, an _active entity limit_ to be set, and can optionally [passivate idle
entities](#idle-entity-passivation). For example, a custom strategy can be configured to use the [least recently used
policy](#least-recently-used-policy):
@@snip [custom passivation strategy](/akka-cluster-sharding/src/test/scala/akka/cluster/sharding/ClusterShardingSettingsSpec.scala) { #custom-passivation-strategy type=conf }
The active entity limit and replacement policy can also be configured using the `withPassivationStrategy` method on
`ClusterShardingSettings`, passing custom `ClusterShardingSettings.PassivationStrategySettings`.
### Least recently used policy
The **least recently used** policy passivates those entities that have the least recent activity when the number of
active entities passes the specified limit.
**When to use**: the least recently used policy should be used when access patterns are recency biased, where entities
that were recently accessed are likely to be accessed again. See the [segmented least recently used
policy](#segmented-least-recently-used-policy) for a variation that also distinguishes frequency of access.
Configure a passivation strategy to use the least recently used policy:
@@snip [LRU policy](/akka-cluster-sharding/src/test/scala/akka/cluster/sharding/ClusterShardingSettingsSpec.scala) { #lru-policy type=conf }
Or using the `withLeastRecentlyUsedReplacement` method on `ClusterShardingSettings.PassivationStrategySettings`.
#### Segmented least recently used policy
A variation of the least recently used policy can be enabled that divides the active entity space into multiple
segments to introduce frequency information into the passivation strategy. Higher-level segments contain entities that
have been accessed more often. The first segment is for entities that have only been accessed once, the second segment
for entities that have been accessed at least twice, and so on. When an entity is accessed again, it will be promoted
to the most recent position of the next-level or highest-level segment. The higher-level segments are limited, where
@ -338,80 +385,66 @@ to the level below. Only the least recently used entities in the lowest level wi
higher levels are considered "protected", where entities will have additional opportunities to be accessed before being
considered for passivation.
To configure a segmented least recently used (SLRU) strategy, with two levels and a protected segment limited to 80% of the total limit:
**When to use**: the segmented least recently used policy can be used for workloads where some entities are more
popular than others, to prioritize those entities that are accessed more frequently.
@@snip [passivation segmented least recently used](/akka-cluster-sharding/src/test/scala/akka/cluster/sharding/ClusterShardingSettingsSpec.scala) { #passivation-segmented-least-recently-used type=conf }
To configure a segmented least recently used (SLRU) policy, with two levels and a protected segment limited to 80% of
the total limit:
Or to configure a 4-level segmented least recently used (S4LRU) strategy, with 4 evenly divided levels:
@@snip [SLRU policy](/akka-cluster-sharding/src/test/scala/akka/cluster/sharding/ClusterShardingSettingsSpec.scala) { #slru-policy type=conf }
@@snip [passivation segmented least recently used](/akka-cluster-sharding/src/test/scala/akka/cluster/sharding/ClusterShardingSettingsSpec.scala) { #passivation-s4-least-recently-used type=conf }
Or to configure a 4-level segmented least recently used (S4LRU) policy, with 4 evenly divided levels:
Or using the `withLeastRecentlyUsedPassivationStrategy` method on `ClusterShardingSettings`.
@@snip [S4LRU policy](/akka-cluster-sharding/src/test/scala/akka/cluster/sharding/ClusterShardingSettingsSpec.scala) { #s4lru-policy type=conf }
#### Idle timeouts (with least recently used strategy)
Or using custom `ClusterShardingSettings.PassivationStrategySettings.LeastRecentlyUsedSettings`.
Passivating idle entities (when they have not received a message for a specified length of time) can also be enabled by configuring the least recently used passivation strategy with an idle timeout:
### Most recently used policy
@@snip [passivation least recently used with idle](/akka-cluster-sharding/src/test/scala/akka/cluster/sharding/ClusterShardingSettingsSpec.scala) { #passivation-least-recently-used-with-idle type=conf }
The **most recently used** policy passivates those entities that have the most recent activity when the number of
active entities passes the specified limit.
Or enable the least recently used passivation strategy with both an active entity limit and an idle timeout using the
`withLeastRecentlyUsedPassivationStrategy` method on `ClusterShardingSettings`.
**When to use**: the most recently used policy is most useful when the older an entity is, the more likely that entity
will be accessed again; as seen in cyclic access patterns.
### Most recently used passivation strategy
Configure a passivation strategy to use the most recently used policy:
The **most recently used** passivation strategy passivates those entities that have the most recent activity when the
number of active entities passes a specified limit. The configurable limit is for a whole shard region and is divided
evenly among the active shards in each region. This strategy is most useful when the older an entity is, the more
likely that entity will be accessed again; as seen in cyclic access patterns. Configure automatic passivation to use
the most recently used passivation strategy, and set the limit for active entities in a shard region:
@@snip [MRU policy](/akka-cluster-sharding/src/test/scala/akka/cluster/sharding/ClusterShardingSettingsSpec.scala) { #mru-policy type=conf }
@@snip [passivation most recently used](/akka-cluster-sharding/src/test/scala/akka/cluster/sharding/ClusterShardingSettingsSpec.scala) { #passivation-most-recently-used type=conf }
Or using the `withMostRecentlyUsedReplacement` method on `ClusterShardingSettings.PassivationStrategySettings`.
Or enable the most recently used passivation strategy and set the active entity limit using the
`withMostRecentlyUsedPassivationStrategy` method on `ClusterShardingSettings`.
### Least frequently used policy
#### Idle timeouts (with most recently used strategy)
The **least frequently used** policy passivates those entities that have the least frequent activity when the number of
active entities passes the specified limit.
Passivating idle entities (when they have not received a message for a specified length of time) can also be enabled by configuring the most recently used passivation strategy with an idle timeout:
**When to use**: the least frequently used policy should be used when access patterns are frequency biased, where some
entities are much more popular than others and should be prioritized. See the [least frequently used with dynamic aging
policy](#least-frequently-used-with-dynamic-aging-policy) for a variation that also handles shifts in popularity.
@@snip [passivation most recently used with idle](/akka-cluster-sharding/src/test/scala/akka/cluster/sharding/ClusterShardingSettingsSpec.scala) { #passivation-most-recently-used-with-idle type=conf }
Configure automatic passivation to use the least frequently used policy:
Or enable the most recently used passivation strategy with both an active entity limit and an idle timeout using the
`withMostRecentlyUsedPassivationStrategy` method on `ClusterShardingSettings`.
@@snip [LFU policy](/akka-cluster-sharding/src/test/scala/akka/cluster/sharding/ClusterShardingSettingsSpec.scala) { #lfu-policy type=conf }
### Least frequently used passivation strategy
Or using the `withLeastFrequentlyUsedReplacement` method on `ClusterShardingSettings.PassivationStrategySettings`.
The **least frequently used** passivation strategy passivates those entities that have the least frequent activity when
the number of active entities passes a specified limit. The configurable limit is for a whole shard region and is
divided evenly among the active shards in each region. Configure automatic passivation to use the least frequently used
passivation strategy, and set the limit for active entities in a shard region:
#### Least frequently used with dynamic aging policy
@@snip [passivation least frequently used](/akka-cluster-sharding/src/test/scala/akka/cluster/sharding/ClusterShardingSettingsSpec.scala) { #passivation-least-frequently-used type=conf }
A variation of the least frequently used policy can be enabled that uses "dynamic aging" to adapt to shifts in the set
of popular entities, which is useful for smaller active entity limits and when shifts in popularity are common. If
entities were frequently accessed in the past but then become unpopular, they can still remain active for a long time
given their high frequency counts. Dynamic aging effectively increases the frequencies for recently accessed entities
so they can more easily become higher priority over entities that are no longer accessed.
Or enable the least frequently used passivation strategy and set the active entity limit using the
`withLeastFrequentlyUsedPassivationStrategy` method on `ClusterShardingSettings`.
**When to use**: the least frequently used with dynamic aging policy can be used when workloads are frequency biased
(there are some entities that are much more popular), but which entities are most popular changes over time. Shifts in
popularity can have more impact on a least frequently used policy if the active entity limit is small.
#### Dynamic aging for least frequently used strategy
Configure dynamic aging with the least frequently used policy:
A variation of the least frequently used passivation strategy can be enabled that uses "dynamic aging" to adapt to
shifts in the set of popular entities, which is useful for smaller active entity limits and when shifts in popularity
are common. If entities were frequently accessed in the past but then become unpopular, they can still remain active
for a long time given their high frequency counts. Dynamic aging effectively increases the frequencies for recently
accessed entities so they can more easily become higher priority over entities that are no longer accessed. Configure
dynamic aging with the least frequently used passivation strategy:
@@snip [LFUDA policy](/akka-cluster-sharding/src/test/scala/akka/cluster/sharding/ClusterShardingSettingsSpec.scala) { #lfuda-policy type=conf }
@@snip [passivation least frequently used with dynamic aging](/akka-cluster-sharding/src/test/scala/akka/cluster/sharding/ClusterShardingSettingsSpec.scala) { #passivation-least-frequently-used-with-dynamic-aging type=conf }
Or when using the `withLeastFrequentlyUsedPassivationStrategy` method on `ClusterShardingSettings`.
#### Idle timeouts (with least frequently used strategy)
Passivating idle entities (when they have not received a message for a specified length of time) can also be enabled by configuring the least frequently used passivation strategy with an idle timeout:
@@snip [passivation least frequently used with idle](/akka-cluster-sharding/src/test/scala/akka/cluster/sharding/ClusterShardingSettingsSpec.scala) { #passivation-least-frequently-used-with-idle type=conf }
Or enable the least frequently used passivation strategy with both an active entity limit and an idle timeout using the
`withLeastFrequentlyUsedPassivationStrategy` method on `ClusterShardingSettings`.
Or using custom `ClusterShardingSettings.PassivationStrategySettings.LeastFrequentlyUsedSettings`.
## Sharding State