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

View file

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

View file

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

View file

@ -674,7 +674,8 @@ private[akka] class ShardRegion(
if (settings.passivationStrategySettings.oldSettingUsed) { if (settings.passivationStrategySettings.oldSettingUsed) {
log.warning( log.warning(
"The `akka.cluster.sharding.passivate-idle-entity-after` setting and associated methods are deprecated. " + "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) { if (settings.rememberEntities) {
log.debug("{}: Entities will not be passivated automatically because 'rememberEntities' is enabled.", typeName) 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 { "allow timeout for (default) idle passivation strategy to be configured (via config)" in {
settings(""" settings("""
#passivation-idle-timeout #passivation-idle-timeout
akka.cluster.sharding { akka.cluster.sharding.passivation {
passivation { default-idle-strategy.idle-entity.timeout = 3 minutes
idle.timeout = 3 minutes
}
} }
#passivation-idle-timeout #passivation-idle-timeout
""").passivationStrategy shouldBe ClusterShardingSettings.IdlePassivationStrategy( """).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 { "allow timeout for (default) idle passivation strategy to be configured (via factory method)" in {
defaultSettings defaultSettings
.withIdlePassivationStrategy( .withPassivationStrategy(
ClusterShardingSettings.PassivationStrategySettings.IdleSettings.defaults.withTimeout(42.seconds)) ClusterShardingSettings.PassivationStrategySettings.defaults.withIdleEntityPassivation(timeout = 42.seconds))
.passivationStrategy shouldBe ClusterShardingSettings.IdlePassivationStrategy( .passivationStrategy shouldBe ClusterShardingSettings.IdlePassivationStrategy(
timeout = 42.seconds, timeout = 42.seconds,
interval = 21.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 { "allow timeout and interval for (default) idle passivation strategy to be configured (via config)" in {
settings(""" settings("""
akka.cluster.sharding { akka.cluster.sharding.passivation {
passivation { default-idle-strategy {
idle { idle-entity {
timeout = 3 minutes timeout = 3 minutes
interval = 1 minute 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 { "allow timeout and interval for (default) idle passivation strategy to be configured (via factory method)" in {
defaultSettings defaultSettings
.withIdlePassivationStrategy( .withPassivationStrategy(ClusterShardingSettings.PassivationStrategySettings.defaults
ClusterShardingSettings.PassivationStrategySettings.IdleSettings.defaults .withIdleEntityPassivation(timeout = 42.seconds, interval = 42.millis))
.withTimeout(42.seconds)
.withInterval(42.millis))
.passivationStrategy shouldBe ClusterShardingSettings.IdlePassivationStrategy( .passivationStrategy shouldBe ClusterShardingSettings.IdlePassivationStrategy(
timeout = 42.seconds, timeout = 42.seconds,
interval = 42.millis) 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(""" settings("""
#passivation-least-recently-used #passivation-new-default-strategy
akka.cluster.sharding { akka.cluster.sharding.passivation {
passivation { strategy = default-strategy
strategy = least-recently-used }
least-recently-used.limit = 1000000 #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( """).passivationStrategy shouldBe ClusterShardingSettings.LeastRecentlyUsedPassivationStrategy(
limit = 1000000, limit = 1000000,
segmented = Nil, 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 { "allow least recently used passivation strategy to be configured (via factory method)" in {
defaultSettings defaultSettings
.withLeastRecentlyUsedPassivationStrategy( .withPassivationStrategy(
ClusterShardingSettings.PassivationStrategySettings.LeastRecentlyUsedSettings.defaults.withLimit(42000)) ClusterShardingSettings.PassivationStrategySettings.defaults
.withActiveEntityLimit(42000)
.withLeastRecentlyUsedReplacement())
.passivationStrategy shouldBe ClusterShardingSettings.LeastRecentlyUsedPassivationStrategy( .passivationStrategy shouldBe ClusterShardingSettings.LeastRecentlyUsedPassivationStrategy(
limit = 42000, limit = 42000,
segmented = Nil, 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 { "allow segmented least recently used passivation strategy to be configured (via config)" in {
settings(""" settings("""
#passivation-segmented-least-recently-used #slru-policy
akka.cluster.sharding { akka.cluster.sharding.passivation {
passivation { strategy = custom-slru-strategy
strategy = least-recently-used custom-slru-strategy {
least-recently-used { active-entity-limit = 1000000
limit = 1000000 replacement {
segmented { policy = least-recently-used
levels = 2 least-recently-used {
proportions = [0.2, 0.8] segmented {
levels = 2
proportions = [0.2, 0.8]
}
} }
} }
} }
} }
#passivation-segmented-least-recently-used #slru-policy
""").passivationStrategy shouldBe ClusterShardingSettings.LeastRecentlyUsedPassivationStrategy( """).passivationStrategy shouldBe ClusterShardingSettings.LeastRecentlyUsedPassivationStrategy(
limit = 1000000, limit = 1000000,
segmented = List(0.2, 0.8), 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 { "allow 4-level segmented least recently used passivation strategy to be configured (via config)" in {
settings(""" settings("""
#passivation-s4-least-recently-used #s4lru-policy
akka.cluster.sharding { akka.cluster.sharding.passivation {
passivation { strategy = custom-s4lru-strategy
strategy = least-recently-used custom-s4lru-strategy {
least-recently-used { active-entity-limit = 1000000
limit = 1000000 replacement {
segmented.levels = 4 policy = least-recently-used
least-recently-used {
segmented.levels = 4
}
} }
} }
} }
#passivation-s4-least-recently-used #s4lru-policy
""").passivationStrategy shouldBe ClusterShardingSettings.LeastRecentlyUsedPassivationStrategy( """).passivationStrategy shouldBe ClusterShardingSettings.LeastRecentlyUsedPassivationStrategy(
limit = 1000000, limit = 1000000,
segmented = List(0.25, 0.25, 0.25, 0.25), 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 { "allow segmented least recently used passivation strategy to be configured (via factory method)" in {
defaultSettings defaultSettings
.withLeastRecentlyUsedPassivationStrategy( .withPassivationStrategy(ClusterShardingSettings.PassivationStrategySettings.defaults
ClusterShardingSettings.PassivationStrategySettings.LeastRecentlyUsedSettings.defaults .withActiveEntityLimit(42000)
.withLimit(42000) .withReplacementPolicy(ClusterShardingSettings.PassivationStrategySettings.LeastRecentlyUsedSettings.defaults
.withSegmented(levels = 4, proportions = List(0.4, 0.3, 0.2, 0.1))) .withSegmented(proportions = List(0.4, 0.3, 0.2, 0.1))))
.passivationStrategy shouldBe ClusterShardingSettings.LeastRecentlyUsedPassivationStrategy( .passivationStrategy shouldBe ClusterShardingSettings.LeastRecentlyUsedPassivationStrategy(
limit = 42000, limit = 42000,
segmented = List(0.4, 0.3, 0.2, 0.1), 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 { "allow least recently used passivation strategy with idle timeout to be configured (via config)" in {
settings(""" settings("""
#passivation-least-recently-used-with-idle akka.cluster.sharding.passivation {
akka.cluster.sharding { strategy = custom-lru-with-idle
passivation { custom-lru-with-idle {
strategy = least-recently-used active-entity-limit = 1000000
least-recently-used { replacement.policy = least-recently-used
limit = 1000000 idle-entity.timeout = 30.minutes
idle.timeout = 30.minutes
}
} }
} }
#passivation-least-recently-used-with-idle
""").passivationStrategy shouldBe ClusterShardingSettings.LeastRecentlyUsedPassivationStrategy( """).passivationStrategy shouldBe ClusterShardingSettings.LeastRecentlyUsedPassivationStrategy(
limit = 1000000, limit = 1000000,
segmented = Nil, 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 { "allow least recently used passivation strategy with idle timeout to be configured (via factory method)" in {
defaultSettings defaultSettings
.withLeastRecentlyUsedPassivationStrategy( .withPassivationStrategy(
ClusterShardingSettings.PassivationStrategySettings.LeastRecentlyUsedSettings.defaults ClusterShardingSettings.PassivationStrategySettings.defaults
.withLimit(42000) .withActiveEntityLimit(42000)
.withIdle(timeout = 42.minutes)) .withLeastRecentlyUsedReplacement()
.withIdleEntityPassivation(timeout = 42.minutes))
.passivationStrategy shouldBe ClusterShardingSettings.LeastRecentlyUsedPassivationStrategy( .passivationStrategy shouldBe ClusterShardingSettings.LeastRecentlyUsedPassivationStrategy(
limit = 42000, limit = 42000,
segmented = Nil, segmented = Nil,
@ -193,14 +243,15 @@ class ClusterShardingSettingsSpec extends AnyWordSpec with Matchers {
"allow most recently used passivation strategy to be configured (via config)" in { "allow most recently used passivation strategy to be configured (via config)" in {
settings(""" settings("""
#passivation-most-recently-used #mru-policy
akka.cluster.sharding { akka.cluster.sharding.passivation {
passivation { strategy = custom-mru-strategy
strategy = most-recently-used custom-mru-strategy {
most-recently-used.limit = 1000000 active-entity-limit = 1000000
replacement.policy = most-recently-used
} }
} }
#passivation-most-recently-used #mru-policy
""").passivationStrategy shouldBe ClusterShardingSettings.MostRecentlyUsedPassivationStrategy( """).passivationStrategy shouldBe ClusterShardingSettings.MostRecentlyUsedPassivationStrategy(
limit = 1000000, limit = 1000000,
idle = None) 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 { "allow most recently used passivation strategy to be configured (via factory method)" in {
defaultSettings defaultSettings
.withMostRecentlyUsedPassivationStrategy( .withPassivationStrategy(
ClusterShardingSettings.PassivationStrategySettings.MostRecentlyUsedSettings.defaults.withLimit(42000)) ClusterShardingSettings.PassivationStrategySettings.defaults
.withActiveEntityLimit(42000)
.withMostRecentlyUsedReplacement())
.passivationStrategy shouldBe ClusterShardingSettings.MostRecentlyUsedPassivationStrategy( .passivationStrategy shouldBe ClusterShardingSettings.MostRecentlyUsedPassivationStrategy(
limit = 42000, limit = 42000,
idle = None) 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 { "allow most recently used passivation strategy with idle timeout to be configured (via config)" in {
settings(""" settings("""
#passivation-most-recently-used-with-idle akka.cluster.sharding.passivation {
akka.cluster.sharding { strategy = custom-mru-with-idle
passivation { custom-mru-with-idle {
strategy = most-recently-used active-entity-limit = 1000000
most-recently-used { replacement.policy = most-recently-used
limit = 1000000 idle-entity.timeout = 30.minutes
idle.timeout = 30.minutes
}
} }
} }
#passivation-most-recently-used-with-idle
""").passivationStrategy shouldBe ClusterShardingSettings.MostRecentlyUsedPassivationStrategy( """).passivationStrategy shouldBe ClusterShardingSettings.MostRecentlyUsedPassivationStrategy(
limit = 1000000, limit = 1000000,
idle = Some(ClusterShardingSettings.IdlePassivationStrategy(timeout = 30.minutes, interval = 15.minutes))) 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 { "allow most recently used passivation strategy with idle timeout to be configured (via factory method)" in {
defaultSettings defaultSettings
.withMostRecentlyUsedPassivationStrategy( .withPassivationStrategy(
ClusterShardingSettings.PassivationStrategySettings.MostRecentlyUsedSettings.defaults ClusterShardingSettings.PassivationStrategySettings.defaults
.withLimit(42000) .withActiveEntityLimit(42000)
.withIdle(timeout = 42.minutes)) .withMostRecentlyUsedReplacement()
.withIdleEntityPassivation(timeout = 42.minutes))
.passivationStrategy shouldBe ClusterShardingSettings.MostRecentlyUsedPassivationStrategy( .passivationStrategy shouldBe ClusterShardingSettings.MostRecentlyUsedPassivationStrategy(
limit = 42000, limit = 42000,
idle = Some(ClusterShardingSettings.IdlePassivationStrategy(timeout = 42.minutes, interval = 21.minutes))) 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 { "allow least frequently used passivation strategy to be configured (via config)" in {
settings(""" settings("""
#passivation-least-frequently-used #lfu-policy
akka.cluster.sharding { akka.cluster.sharding.passivation {
passivation { strategy = custom-lfu-strategy
strategy = least-frequently-used custom-lfu-strategy {
least-frequently-used.limit = 1000000 active-entity-limit = 1000000
replacement.policy = least-frequently-used
} }
} }
#passivation-least-frequently-used #lfu-policy
""").passivationStrategy shouldBe ClusterShardingSettings.LeastFrequentlyUsedPassivationStrategy( """).passivationStrategy shouldBe ClusterShardingSettings.LeastFrequentlyUsedPassivationStrategy(
limit = 1000000, limit = 1000000,
dynamicAging = false, 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 { "allow least frequently used passivation strategy to be configured (via factory method)" in {
defaultSettings defaultSettings
.withLeastFrequentlyUsedPassivationStrategy( .withPassivationStrategy(
ClusterShardingSettings.PassivationStrategySettings.LeastFrequentlyUsedSettings.defaults.withLimit(42000)) ClusterShardingSettings.PassivationStrategySettings.defaults
.withActiveEntityLimit(42000)
.withLeastFrequentlyUsedReplacement())
.passivationStrategy shouldBe ClusterShardingSettings.LeastFrequentlyUsedPassivationStrategy( .passivationStrategy shouldBe ClusterShardingSettings.LeastFrequentlyUsedPassivationStrategy(
limit = 42000, limit = 42000,
dynamicAging = false, 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 { "allow least frequently used passivation strategy with idle timeout to be configured (via config)" in {
settings(""" settings("""
#passivation-least-frequently-used-with-idle akka.cluster.sharding.passivation {
akka.cluster.sharding { strategy = custom-lfu-with-idle
passivation { custom-lfu-with-idle {
strategy = least-frequently-used active-entity-limit = 1000000
least-frequently-used { replacement.policy = least-frequently-used
limit = 1000000 idle-entity.timeout = 30.minutes
idle.timeout = 30.minutes
}
} }
} }
#passivation-least-frequently-used-with-idle
""").passivationStrategy shouldBe ClusterShardingSettings.LeastFrequentlyUsedPassivationStrategy( """).passivationStrategy shouldBe ClusterShardingSettings.LeastFrequentlyUsedPassivationStrategy(
limit = 1000000, limit = 1000000,
dynamicAging = false, 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 { "allow least frequently used passivation strategy with idle timeout to be configured (via factory method)" in {
defaultSettings defaultSettings
.withLeastFrequentlyUsedPassivationStrategy( .withPassivationStrategy(
ClusterShardingSettings.PassivationStrategySettings.LeastFrequentlyUsedSettings.defaults ClusterShardingSettings.PassivationStrategySettings.defaults
.withLimit(42000) .withActiveEntityLimit(42000)
.withIdle(timeout = 42.minutes)) .withLeastFrequentlyUsedReplacement()
.withIdleEntityPassivation(timeout = 42.minutes))
.passivationStrategy shouldBe ClusterShardingSettings.LeastFrequentlyUsedPassivationStrategy( .passivationStrategy shouldBe ClusterShardingSettings.LeastFrequentlyUsedPassivationStrategy(
limit = 42000, limit = 42000,
dynamicAging = false, 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 { "allow least frequently used passivation strategy with dynamic aging to be configured (via config)" in {
settings(""" settings("""
#passivation-least-frequently-used-with-dynamic-aging #lfuda-policy
akka.cluster.sharding { akka.cluster.sharding.passivation {
passivation { strategy = custom-lfu-with-dynamic-aging
strategy = least-frequently-used custom-lfu-with-dynamic-aging {
least-frequently-used { active-entity-limit = 1000
limit = 1000 replacement {
dynamic-aging = on policy = least-frequently-used
least-frequently-used {
dynamic-aging = on
}
} }
} }
} }
#passivation-least-frequently-used-with-dynamic-aging #lfuda-policy
""").passivationStrategy shouldBe ClusterShardingSettings.LeastFrequentlyUsedPassivationStrategy( """).passivationStrategy shouldBe ClusterShardingSettings.LeastFrequentlyUsedPassivationStrategy(
limit = 1000, limit = 1000,
dynamicAging = true, 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 { "allow least frequently used passivation strategy with dynamic aging to be configured (via factory method)" in {
defaultSettings defaultSettings
.withLeastFrequentlyUsedPassivationStrategy( .withPassivationStrategy(
ClusterShardingSettings.PassivationStrategySettings.LeastFrequentlyUsedSettings.defaults ClusterShardingSettings.PassivationStrategySettings.defaults
.withLimit(42000) .withActiveEntityLimit(42000)
.withDynamicAging()) .withReplacementPolicy(
ClusterShardingSettings.PassivationStrategySettings.LeastFrequentlyUsedSettings.defaults
.withDynamicAging()))
.passivationStrategy shouldBe ClusterShardingSettings.LeastFrequentlyUsedPassivationStrategy( .passivationStrategy shouldBe ClusterShardingSettings.LeastFrequentlyUsedPassivationStrategy(
limit = 42000, limit = 42000,
dynamicAging = true, 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 { "disable automatic passivation if idle timeout is set to zero (via config)" in {
settings(""" settings("""
akka.cluster.sharding.passivation.idle.timeout = 0 akka.cluster.sharding.passivation.default-idle-strategy.idle-entity.timeout = 0
""").passivationStrategy shouldBe ClusterShardingSettings.NoPassivationStrategy """).passivationStrategy shouldBe ClusterShardingSettings.NoPassivationStrategy
} }
"disable automatic passivation if idle timeout is set to zero (via factory method)" in { "disable automatic passivation if idle timeout is set to zero (via factory method)" in {
defaultSettings defaultSettings
.withIdlePassivationStrategy( .withPassivationStrategy(ClusterShardingSettings.PassivationStrategySettings.defaults.withIdleEntityPassivation(
ClusterShardingSettings.PassivationStrategySettings.IdleSettings.defaults.withTimeout(Duration.Zero)) timeout = Duration.Zero))
.passivationStrategy shouldBe ClusterShardingSettings.NoPassivationStrategy .passivationStrategy shouldBe ClusterShardingSettings.NoPassivationStrategy
} }
@ -367,7 +424,7 @@ class ClusterShardingSettingsSpec extends AnyWordSpec with Matchers {
settings(""" settings("""
akka.cluster.sharding { akka.cluster.sharding {
passivate-idle-entity-after = 5 minutes passivate-idle-entity-after = 5 minutes
passivation-strategy = least-recently-used passivation.strategy = default-strategy
} }
""").passivationStrategy shouldBe ClusterShardingSettings.IdlePassivationStrategy( """).passivationStrategy shouldBe ClusterShardingSettings.IdlePassivationStrategy(
timeout = 5.minutes, timeout = 5.minutes,

View file

@ -36,7 +36,6 @@ object EntityPassivationSpec {
akka.cluster.sharding { akka.cluster.sharding {
passivation { passivation {
strategy = none strategy = none
idle.timeout = 1s
} }
} }
""").withFallback(config) """).withFallback(config)
@ -84,8 +83,9 @@ abstract class AbstractEntityPassivationSpec(config: Config, expectedEntities: I
import EntityPassivationSpec._ import EntityPassivationSpec._
val settings: ClusterShardingSettings = ClusterShardingSettings(system) val settings: ClusterShardingSettings = ClusterShardingSettings(system)
val configuredIdleTimeout: FiniteDuration = settings.passivationStrategySettings.idleSettings.timeout val configuredIdleTimeout: FiniteDuration =
val configuredLeastRecentlyUsedLimit: Int = settings.passivationStrategySettings.leastRecentlyUsedSettings.limit 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 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 } val probeRefs: Map[String, ActorRef] = probes.map { case (id, probe) => id.toString -> probe.ref }
@ -137,7 +137,7 @@ class DisabledEntityPassivationSpec
val region = start() val region = start()
region ! Envelope(shard = 1, id = 1, message = "A") region ! Envelope(shard = 1, id = 1, message = "A")
expectReceived(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(""" val config: Config = ConfigFactory.parseString("""
akka.cluster.sharding { akka.cluster.sharding {
passivation { passivation {
strategy = idle default-idle-strategy.idle-entity.timeout = 1s
idle.timeout = 1s
} }
} }
""").withFallback(EntityPassivationSpec.config) """).withFallback(EntityPassivationSpec.config)

View file

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

View file

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

View file

@ -14,8 +14,11 @@ object MostRecentlyUsedSpec {
val config: Config = ConfigFactory.parseString(""" val config: Config = ConfigFactory.parseString("""
akka.cluster.sharding { akka.cluster.sharding {
passivation { passivation {
strategy = most-recently-used strategy = mru
most-recently-used.limit = 10 mru {
active-entity-limit = 10
replacement.policy = most-recently-used
}
} }
} }
""").withFallback(EntityPassivationSpec.config) """).withFallback(EntityPassivationSpec.config)
@ -23,10 +26,11 @@ object MostRecentlyUsedSpec {
val idleConfig: Config = ConfigFactory.parseString(""" val idleConfig: Config = ConfigFactory.parseString("""
akka.cluster.sharding { akka.cluster.sharding {
passivation { passivation {
strategy = most-recently-used strategy = mru-idle
most-recently-used { mru-idle {
limit = 3 active-entity-limit = 3
idle.timeout = 1s 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) { extends AbstractEntityPassivationSpec(MostRecentlyUsedSpec.idleConfig, expectedEntities = 3) {
import EntityPassivationSpec.Entity.Envelope import EntityPassivationSpec.Entity.Envelope
@ -151,19 +155,17 @@ class MostRecentlyUsedWithIdleEntityPassivationSpec
"passivate entities when they haven't seen messages for the configured timeout" in { "passivate entities when they haven't seen messages for the configured timeout" in {
val region = start() val region = start()
val idleTimeout = settings.passivationStrategySettings.mostRecentlyUsedSettings.idleSettings.get.timeout
val lastSendNanoTime1 = System.nanoTime() val lastSendNanoTime1 = System.nanoTime()
region ! Envelope(shard = 1, id = 1, message = "A") region ! Envelope(shard = 1, id = 1, message = "A")
region ! Envelope(shard = 1, id = 2, message = "B") region ! Envelope(shard = 1, id = 2, message = "B")
// keep entity 3 active to prevent idle passivation // keep entity 3 active to prevent idle passivation
region ! Envelope(shard = 1, id = 3, message = "C") 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") 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") region ! Envelope(shard = 1, id = 3, message = "E")
Thread.sleep((idleTimeout / 2).toMillis) Thread.sleep((configuredIdleTimeout / 2).toMillis)
val lastSendNanoTime2 = System.nanoTime() val lastSendNanoTime2 = System.nanoTime()
region ! Envelope(shard = 1, id = 3, message = "F") region ! Envelope(shard = 1, id = 3, message = "F")
@ -175,13 +177,13 @@ class MostRecentlyUsedWithIdleEntityPassivationSpec
expectReceived(id = 3, message = "F") expectReceived(id = 3, message = "F")
val passivate1 = expectReceived(id = 1, message = Stop) val passivate1 = expectReceived(id = 1, message = Stop)
val passivate2 = expectReceived(id = 2, 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 // 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 // so look at the time from before sending the last message until receiving the passivate message
(passivate1.nanoTime - lastSendNanoTime1).nanos should be > idleTimeout (passivate1.nanoTime - lastSendNanoTime1).nanos should be > configuredIdleTimeout
(passivate2.nanoTime - lastSendNanoTime1).nanos should be > idleTimeout (passivate2.nanoTime - lastSendNanoTime1).nanos should be > configuredIdleTimeout
(passivate3.nanoTime - lastSendNanoTime2).nanos should be > idleTimeout (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 ## Automatic Passivation
Entities are automatically passivated based on a passivation strategy. The default passivation strategy is to 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 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. 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 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.
The **idle** passivation strategy passivates entities when they have not received a message for a specified length of Specify a different idle timeout with configuration:
time. This is the default strategy 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 } @@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 ### Active entity limits
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:
@@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 A recommended passivation strategy, which will become the new default passivation strategy in future versions of Akka
`withLeastRecentlyUsedPassivationStrategy` method on `ClusterShardingSettings`. 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 This default strategy uses a [segmented least recently used policy](#segmented-least-recently-used-policy). The active
multiple segments to introduce frequency information into the strategy. Higher-level segments contain entities that 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 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 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 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 higher levels are considered "protected", where entities will have additional opportunities to be accessed before being
considered for passivation. 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 **When to use**: the most recently used policy is most useful when the older an entity is, the more likely that entity
`withLeastRecentlyUsedPassivationStrategy` method on `ClusterShardingSettings`. 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 @@snip [MRU policy](/akka-cluster-sharding/src/test/scala/akka/cluster/sharding/ClusterShardingSettingsSpec.scala) { #mru-policy type=conf }
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 [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 ### Least frequently used policy
`withMostRecentlyUsedPassivationStrategy` method on `ClusterShardingSettings`.
#### 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 @@snip [LFU policy](/akka-cluster-sharding/src/test/scala/akka/cluster/sharding/ClusterShardingSettingsSpec.scala) { #lfu-policy type=conf }
`withMostRecentlyUsedPassivationStrategy` method on `ClusterShardingSettings`.
### 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 #### Least frequently used with dynamic aging policy
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:
@@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 **When to use**: the least frequently used with dynamic aging policy can be used when workloads are frequency biased
`withLeastFrequentlyUsedPassivationStrategy` method on `ClusterShardingSettings`. (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 @@snip [LFUDA policy](/akka-cluster-sharding/src/test/scala/akka/cluster/sharding/ClusterShardingSettingsSpec.scala) { #lfuda-policy type=conf }
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 [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 using custom `ClusterShardingSettings.PassivationStrategySettings.LeastFrequentlyUsedSettings`.
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`.
## Sharding State ## Sharding State