# SPDX-License-Identifier: Apache-2.0

################################################
# Pekko Cluster Sharding Reference Config File #
#######################3########################

# This is the reference config file that contains all the default settings.
# Make your edits/overrides in your application.conf.


# //#sharding-ext-config
# Settings for the ClusterShardingExtension
pekko.cluster.sharding {

  # The extension creates a top level actor with this name in top level system scope,
  # e.g. '/system/sharding'
  guardian-name = sharding

  # Specifies that entities run on cluster nodes with a specific role.
  # If the role is not specified (or empty) all nodes in the cluster are used.
  role = ""

  # When this is set to 'on' the active entity actors will automatically be restarted
  # upon Shard restart. i.e. if the Shard is started on a different ShardRegion
  # due to rebalance or crash.
  remember-entities = off

  # When 'remember-entities' is enabled and the state store mode is ddata this controls
  # how the remembered entities and shards are stored. Possible values are "eventsourced" and "ddata"
  # Default is ddata for backwards compatibility.
  remember-entities-store = "ddata"

  # Deprecated: use the `passivation.default-idle-strategy.idle-entity.timeout` setting instead.
  # Set this to a time duration to have sharding passivate entities when they have not
  # received any message in this length of time. Set to 'off' to disable.
  # It is always disabled if `remember-entities` is enabled.
  passivate-idle-entity-after = null

  # Automatic entity passivation settings.
  passivation {

    # Automatic passivation strategy to use.
    # Set to "none" or "off" to disable automatic passivation.
    # Set to "default-strategy" to switch to the recommended default strategy with an active entity limit.
    # See the strategy-defaults section for possible passivation strategy settings and default values.
    # Passivation strategies are always disabled if `remember-entities` is enabled.
    #
    # API MAY CHANGE: Configuration for passivation strategies, except default-idle-strategy,
    # may change after additional testing and feedback.
    strategy = "default-idle-strategy"

    # Default passivation strategy without active entity limit; time out idle entities after 2 minutes.
    default-idle-strategy {
      idle-entity.timeout = 120s
    }

    # Recommended default strategy for automatic passivation with an active entity limit.
    # Configured with an adaptive recency-based admission window, a frequency-based admission filter, and
    # a segmented least recently used (SLRU) replacement policy for the main active entity tracking.
    default-strategy {
      # Default limit of 100k active entities in a shard region (in a cluster node).
      active-entity-limit = 100000

      # Admission window with LRU policy and adaptive sizing, and a frequency sketch admission filter to the main area.
      admission {
        window {
          policy = least-recently-used
          optimizer = hill-climbing
        }
        filter = frequency-sketch
      }

      # Main area with segmented LRU replacement policy with an 80% "protected" level by default.
      replacement {
        policy = least-recently-used
        least-recently-used {
          segmented {
            levels = 2
            proportions = [0.2, 0.8]
          }
        }
      }
    }

    strategy-defaults {
      # Passivate entities when they have not received a message for a specified length of time.
      idle-entity {
        # Passivate idle entities after the timeout. Set to "none" or "off" to disable.
        timeout = none

        # Check idle entities every interval. Set to "default" to use half the timeout by default.
        interval = default
      }

      # Limit of active entities in a shard region.
      # 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

      # Entity replacement settings, for when the active entity limit is reached.
      replacement {
        # Entity replacement policy to use when the active entity limit is reached. Possible values are:
        #   - "least-recently-used"
        #   - "most-recently-used"
        #   - "least-frequently-used"
        # Set to "none" or "off" to disable the replacement policy and ignore the active entity limit.
        policy = none

        # Least recently used entity replacement policy.
        least-recently-used {
          # Optionally use a "segmented" least recently used strategy.
          # Disabled when segmented.levels are set to "none" or "off".
          segmented {
            # Number of segmented levels.
            levels = none

            # Fractional proportions for the segmented levels.
            # If empty then segments are divided evenly by the number of levels.
            proportions = []
          }
        }

        # Most recently used entity replacement policy.
        most-recently-used {}

        # Least frequently used entity replacement policy.
        least-frequently-used {
          # New frequency counts will be "dynamically aged" when enabled.
          dynamic-aging = off
        }
      }

      # An optional admission area, with a window for newly and recently activated entities, and an admission filter
      # to determine whether a candidate should be admitted to the main area of the passivation strategy.
      admission {
        # An optional window area, where newly created entities will be admitted initially, and when evicted
        # from the window area have an opportunity to move to the main area based on the admission filter.
        window {
          # The initial sizing for the window area (if enabled), as a fraction of the total active entity limit.
          proportion = 0.01

          # The minimum adaptive sizing for the window area, as a fraction of the total active entity limit.
          # Only applies when an adaptive window optimizer is enabled.
          minimum-proportion = 0.01

          # The maximum adaptive sizing for the window area, as a fraction of the total active entity limit.
          # Only applies when an adaptive window optimizer is enabled.
          maximum-proportion = 1.0

          # Adaptive optimizer to use for dynamically resizing the window area. Possible values are:
          #   - "hill-climbing"
          # Set to "none" or "off" to disable adaptive sizing of the window area.
          optimizer = off

          # A window proportion optimizer using a simple hill-climbing algorithm.
          hill-climbing {
            # Multiplier of the active entity limit for how often (in accesses) to adjust the window proportion.
            adjust-multiplier = 10.0

            # The size of the initial step to take (also used when the climbing restarts).
            initial-step = 0.0625

            # A threshold for the change in active rate (hit rate) to restart climbing.
            restart-threshold = 0.05

            # The decay ratio applied on each climbing step.
            step-decay = 0.98
          }

          # Replacement policy to use for the window area.
          # Entities that are evicted from the window area may move to the main area, based on the admission filter.
          # Possible values are the same as for the main replacement policy.
          # Set to "none" or "off" to disable the window area.
          policy = none

          least-recently-used {
            segmented {
              levels = none
              proportions = []
            }
          }

          most-recently-used {}

          least-frequently-used {
            dynamic-aging = off
          }
        }

        # The admission filter for the main area of the passivation strategy. Possible values are:
        #   - "frequency-sketch"
        # Set to "none" or "off" to disable the admission filter and always admit to the main area.
        filter = none

        # An admission filter based on a frequency sketch (a variation of a count-min sketch).
        frequency-sketch {
          # The depth of the frequency sketch (the number of hash functions).
          depth = 4

          # The size of the frequency counters in bits: 2, 4, 8, 16, 32, or 64 bits.
          counter-bits = 4

          # Multiplier of the active entity limit for the width of the frequency sketch.
          width-multiplier = 4

          # Multiplier of the active entity limit for how often the reset operation of the frequency sketch is applied.
          reset-multiplier = 10.0
        }
      }
    }
  }

  # If the coordinator can't store state changes it will be stopped
  # and started again after this duration, with an exponential back-off
  # of up to 5 times this duration.
  coordinator-failure-backoff = 5 s

  # The ShardRegion retries registration and shard location requests to the
  # ShardCoordinator with this interval if it does not reply.
  retry-interval = 2 s

  # Maximum number of messages that are buffered by a ShardRegion actor.
  buffer-size = 100000

  # Timeout of the shard rebalancing process.
  # Additionally, if an entity doesn't handle the stopMessage
  # after (handoff-timeout - 5.seconds).max(1.second) it will be stopped forcefully
  handoff-timeout = 60 s

  # Time given to a region to acknowledge it's hosting a shard.
  shard-start-timeout = 10 s

  # If the shard is remembering entities and can't store state changes, it
  # will be stopped and then started again after this duration. Any messages
  # sent to an affected entity may be lost in this process.
  shard-failure-backoff = 10 s

  # If the shard is remembering entities and an entity stops itself without
  # using passivate, the entity will be restarted after this duration or when
  # the next message for it is received, whichever occurs first.
  entity-restart-backoff = 10 s

  # Rebalance check is performed periodically with this interval.
  rebalance-interval = 10 s

  # Absolute path to the journal plugin configuration entity that is to be
  # used for the internal persistence of ClusterSharding. If not defined,
  # the default journal plugin is used. Note that this is not related to
  # persistence used by the entity actors.
  # Only used when state-store-mode=persistence
  journal-plugin-id = ""

  # Absolute path to the snapshot plugin configuration entity that is to be
  # used for the internal persistence of ClusterSharding. If not defined,
  # the default snapshot plugin is used. Note that this is not related to
  # persistence used by the entity actors.
  # Only used when state-store-mode=persistence
  snapshot-plugin-id = ""

  # Defines how the coordinator stores its state. Same is also used by the
  # shards for rememberEntities.
  # Valid values are "ddata" or "persistence".
  # "persistence" mode is deprecated
  state-store-mode = "ddata"

  # The shard saves persistent snapshots after this number of persistent
  # events. Snapshots are used to reduce recovery times. A snapshot trigger might be delayed if a batch of updates is processed.
  # Only used when state-store-mode=persistence
  snapshot-after = 1000

  # The shard deletes persistent events (messages and snapshots) after doing snapshot
  # keeping this number of old persistent batches.
  # Batch is of size `snapshot-after`.
  # When set to 0, after snapshot is successfully done, all events with equal or lower sequence number will be deleted.
  # Default value of 2 leaves last maximum 2*`snapshot-after` events and 3 snapshots (2 old ones + latest snapshot).
  # If larger than 0, one additional batch of journal messages is kept when state-store-mode=persistence to include messages from delayed snapshots.
  keep-nr-of-batches = 2

  # Settings for LeastShardAllocationStrategy.
  #
  # A new rebalance algorithm was included in Akka 2.6.10. It can reach optimal balance in
  # less rebalance rounds (typically 1 or 2 rounds). The amount of shards to rebalance in each
  # round can still be limited to make it progress slower. For backwards compatibility,
  # the new algorithm is not enabled by default. Enable the new algorithm by setting
  # `rebalance-absolute-limit` > 0, for example:
  # pekko.cluster.sharding.least-shard-allocation-strategy.rebalance-absolute-limit=20
  # The new algorithm is recommended and will become the default in future versions of Pekko.
  least-shard-allocation-strategy {
    # Maximum number of shards that will be rebalanced in one rebalance round.
    # The lower of this and `rebalance-relative-limit` will be used.
    rebalance-absolute-limit = 0

    # Maximum number of shards that will be rebalanced in one rebalance round.
    # Fraction of total number of (known) shards.
    # The lower of this and `rebalance-absolute-limit` will be used.
    rebalance-relative-limit = 0.1

    # Deprecated: Use rebalance-absolute-limit and rebalance-relative-limit instead. This property is not used
    # when rebalance-absolute-limit > 0.
    #
    # Threshold of how large the difference between most and least number of
    # allocated shards must be to begin the rebalancing.
    # The difference between number of shards in the region with most shards and
    # the region with least shards must be greater than (>) the `rebalanceThreshold`
    # for the rebalance to occur.
    # It is also the maximum number of shards that will start rebalancing per rebalance-interval
    # 1 gives the best distribution and therefore typically the best choice.
    # Increasing the threshold can result in quicker rebalance but has the
    # drawback of increased difference between number of shards (and therefore load)
    # on different nodes before rebalance will occur.
    rebalance-threshold = 1

    # Deprecated: Use rebalance-absolute-limit and rebalance-relative-limit instead. This property is not used
    # when rebalance-absolute-limit > 0.
    #
    # The number of ongoing rebalancing processes is limited to this number.
    max-simultaneous-rebalance = 3
  }

  external-shard-allocation-strategy {
    # How long to wait for the client to persist an allocation to ddata or get all shard locations
    client-timeout = 5s
  }

  # Timeout of waiting the initial distributed state for the shard coordinator (an initial state will be queried again if the timeout happened)
  # and for a shard to get its state when remembered entities is enabled
  # The read from ddata is a ReadMajority, for small clusters (< majority-min-cap) every node needs to respond
  # so is more likely to time out if there are nodes restarting e.g. when there is a rolling re-deploy happening
  waiting-for-state-timeout = 2 s

  # Timeout of waiting for update the distributed state (update will be retried if the timeout happened)
  # Also used as timeout for writes of remember entities when that is enabled
  updating-state-timeout = 5 s

  # Timeout to wait for querying all shards for a given `ShardRegion`.
  shard-region-query-timeout = 3 s

  # The shard uses this strategy to determines how to recover the underlying entity actors. The strategy is only used
  # by the persistent shard when rebalancing or restarting and is applied per remembered shard starting up (not for
  # entire shard region). The value can either be "all" or "constant". The "all"
  # strategy start all the underlying entity actors at the same time. The constant strategy will start the underlying
  # entity actors at a fix rate. The default strategy "all".
  entity-recovery-strategy = "all"

  # Default settings for the constant rate entity recovery strategy
  entity-recovery-constant-rate-strategy {
    # Sets the frequency at which a batch of entity actors is started.
    frequency = 100 ms
    # Sets the number of entity actors to be restart at a particular interval
    number-of-entities = 5
  }

  event-sourced-remember-entities-store {
    # When using remember entities and the event sourced remember entities store the batches
    # written to the store are limited by this number to avoid getting a too large event for
    # the journal to handle. If using long persistence ids you may have to increase this.
    max-updates-per-write = 100
  }

  # Settings for the coordinator singleton. Same layout as pekko.cluster.singleton.
  # The "role" of the singleton configuration is not used. The singleton role will
  # be the same as "pekko.cluster.sharding.role" if
  # "pekko.cluster.sharding.coordinator-singleton-role-override" is enabled. Disabling it will allow to
  # use separate nodes for the shard coordinator and the shards themselves.
  # A lease can be configured in these settings for the coordinator singleton
  coordinator-singleton = ${pekko.cluster.singleton}


  # Copies the role for the coordinator singleton from the shards role instead of using the one provided in the
  # "pekko.cluster.sharding.coordinator-singleton.role"
  coordinator-singleton-role-override = on

  coordinator-state {
    # State updates are required to be written to a majority of nodes plus this
    # number of additional nodes. Can also be set to "all" to require
    # writes to all nodes. The reason for write/read to more than majority
    # is to have more tolerance for membership changes between write and read.
    # The tradeoff of increasing this is that updates will be slower.
    # It is more important to increase the `read-majority-plus`.
    write-majority-plus = 3
    # State retrieval when ShardCoordinator is started is required to be read
    # from a majority of nodes plus this number of additional nodes. Can also
    # be set to "all" to require reads from all nodes. The reason for write/read
    # to more than majority is to have more tolerance for membership changes between
    # write and read.
    # The tradeoff of increasing this is that coordinator startup will be slower.
    read-majority-plus = 5
  }
  
  # Settings for the Distributed Data replicator. 
  # Same layout as pekko.cluster.distributed-data.
  # The "role" of the distributed-data configuration is not used. The distributed-data
  # role will be the same as "pekko.cluster.sharding.role".
  # Note that there is one Replicator per role and it's not possible
  # to have different distributed-data settings for different sharding entity types.
  # Only used when state-store-mode=ddata
  distributed-data = ${pekko.cluster.distributed-data}
  distributed-data {
    # minCap parameter to MajorityWrite and MajorityRead consistency level.
    majority-min-cap = 5
    durable.keys = ["shard-*"]
    
    # When using many entities with "remember entities" the Gossip message
    # can become too large if including too many in same message. Limit to
    # the same number as the number of ORSet per shard.
    max-delta-elements = 5

    # ShardCoordinator is singleton running on oldest
    prefer-oldest = on
  }

  # The id of the dispatcher to use for ClusterSharding actors.
  # If specified, you need to define the settings of the actual dispatcher.
  # This dispatcher for the entity actors is defined by the user provided
  # Props, i.e. this dispatcher is not used for the entity actors.
  use-dispatcher = "pekko.actor.internal-dispatcher"

  # Config path of the lease that each shard must acquire before starting entity actors
  # default is no lease
  # A lease can also be used for the singleton coordinator by settings it in the coordinator-singleton properties
  use-lease = ""

  # The interval between retries for acquiring the lease
  lease-retry-interval = 5s

  # Provide a higher level of details in the debug logs, often per routed message. Be careful about enabling
  # in production systems.
  verbose-debug-logging = off

  # Throw an exception if the internal state machine in the Shard actor does an invalid state transition.
  # Mostly for the Pekko test suite. If off, the invalid transition is logged as a warning instead of throwing and
  # crashing the shard.
  fail-on-invalid-entity-state-transition = off

  # Healthcheck that can be used with Pekko management health checks: https://pekko.apache.org/docs/pekko-management/current/healthchecks.html
  healthcheck {
    # sharding names to check have registered with the coordinator for the health check to pass
    # once initial registration has taken place the health check always returns true to prevent the coordinator
    # moving making the health check of all nodes fail
    # by default no sharding instances are monitored
    names = []

    # Timeout for the local shard region to respond. This should be lower than your monitoring system's
    # timeout for health checks
    timeout = 5s
  }
}
# //#sharding-ext-config

# Enable health check by default for when Pekko management is on the classpath
pekko.management.health-checks.readiness-checks {
  sharding = "org.apache.pekko.cluster.sharding.ClusterShardingHealthCheck"
}

pekko.cluster {
  configuration-compatibility-check {
    checkers {
      pekko-cluster-sharding = "org.apache.pekko.cluster.sharding.JoinConfigCompatCheckSharding"
    }
  }
}

# Protobuf serializer for Cluster Sharding messages
pekko.actor {
  serializers {
    pekko-sharding = "org.apache.pekko.cluster.sharding.protobuf.ClusterShardingMessageSerializer"
  }
  serialization-bindings {
    "org.apache.pekko.cluster.sharding.ClusterShardingSerializable" = pekko-sharding
  }
  serialization-identifiers {
    "org.apache.pekko.cluster.sharding.protobuf.ClusterShardingMessageSerializer" = 13
  }
}
