#  -*- text -*-
#
#
#  $Id: 1d5a8b64cd9503e687bcac0a7ed05bbe61e9d788 $

#######################################################################
#
#  = Cache Module
#
#  The `cache` module is used to cache attributes. The idea is that you can look
#  up information in a database, and then cache it. Repeated requests for the same
#  information will then have the cached values added to the request.
#
#  The module can cache a fixed set of attributes per key.
#  It can be listed in any `recv` or `send` section.
#
#  NOTE: If you want to have different things cached for different
#  sections, you will need to define multiple instances of the module,
#  via `cache <nameN> { ... }`.
#
#  [options="header,autowidth"]
#  |===
#  | Option    | Description
#  | `ok`      | if it found or created a cache entry.
#  | `updated` | if it merged a cached entry.
#  | `noop`    | if it did nothing.
#  | `fail`    | on error.
#  |===
#

#
#  ## Configuration Settings
#
cache {
	#
	#  driver::
	#
	#  The current backend datastore used to store the cache entries are:
	#
	#  [options="header,autowidth"]
	#  |===
	#  | Driver                | Description
	#  | `rbtree`              | An in memory, non persistent rbtree based datastore.
	#                            Useful for caching data locally.
	#  | `memcached`           | A non persistent "webscale" distributed datastore.
	#                            Useful if the cached data need to be shared between
	#                            a cluster of RADIUS servers.
	#  | `redis`               | A persistent "webscale" clustered, sharded, data store.
	#                            Extremely fast, and a good candidate for sharing
	#                            data such as EAP session blobs, between a cluster of
	#                            servers.
	#  |===
	#
#	driver = "rbtree"

	#
	#  NOTE: Some drivers accept specific options, to set them a config section with the
	#  the name as the driver should be added to the cache instance.
	#
	#  Driver specific options are:
	#

#
#  ### Memcached cache driver
#
#	memcached {
		#
		#  options:: Memcached configuration options.
		#
		#  The memcached options are documented at
		#  http://docs.libmemcached.org/libmemcached_configuration.html#memcached
		#
#		options = "--SERVER=localhost"

		#
		#  pool:: Connection pool.
		#
#		pool {
#			start = 0
#			min = 0
#			max =
#			spare = 1
#			uses = 0
#			lifetime = 0
#			idle_timeout = 60
#		}
#	}

#
#  ### Redis cache driver
#
#	redis {
		#
		#  server:: Redis Server name.
		#
		#  If using Redis cluster, multiple 'bootstrap' servers may be
		#  listed here (as separate config items). These will be contacted
		#  in turn until one provides us with a valid map for the cluster.
		#  Server strings may contain unique ports.
		#
		#  e.g.:
		#
		#    server = '127.0.0.1:30001'
		#    server = '[::1]:30002'
		#
		#  Instantiation failure behaviour is controlled by `pool.start` as
		#  with every other module, but with clustering, the pool section
		#  determines limits for each node in the cluster, not the cluster
		#  as a whole.
		#
#		server = 127.0.0.1

		#
		#  port:: Port to use for Redis server.
		#
		#  The default port is 6379.
		#
#		port = 6379

		#
		#  password:: For authenticating ourselves to the Redis server.
		#
#		password = 'supersecret'

		#
		#  database:: The database number to use.
		#
#		database = 0

		#
		#  pool:: Connection pool.
		#
#		pool {
			start = 0
			min = 0
#			max =
#			spare = 1
#			uses = 0
#			lifetime = 0
#			idle_timeout = 60
#		}
#	}

	#
	#  key:: The `key` used to index the cache. It is dynamically expanded at run time.
	#
	key = &User-Name

	#
	#  ttl::
	#
	#  The TTL of cache entries, in seconds. Entries older than this
	#  will be expired.
	#
	#  This value should be between `10` and `86400`.
	#
	ttl = 10

	#
	#  NOTE: You can flush the cache via
	#  `radmin -e "set module config cache epoch 123456789"`
	#  Where last value is a 32-bit Unix timestamp. Cache entries older
	#  than this are expired, as new entries added.
	#  You should never set the "epoch" configuration item in this file.

	#
	#  add_stats::
	#
	#  If `yes` the following attributes will be added to the request:
	#  * `&request.Cache-Entry-Hits` - The number of times this entry
	#  has been retrieved.
	#
	#  NOTE: Not supported by the `rlm_cache_memcached` module.
	#
	add_stats = no

	#
	#  max_entries:: Maximum entries allowed.
	#
#	max_entries = 0

	#
	#  update { ... }:: The attributes to cache for a particular key.
	#
	#  Each key gets the same set of cached attributes.
	#
	#  The operation of the `update` section is a little different
	#  from normal `update` sections.  This is because we need to
	#  both reference the attributes which we want to store in the
	#  cache, and also to describe where those attributes are
	#  written to when the cache entry is read.
	#
	#  The solution (albeit an imperfect one) is that the cache
	#  does not store attributes, it stores `update` sections.
	#  The `update` section given below is used as a template
	#  for the cache entry.
	#
	#  When the cache entry is created, the right-hand side of
	#  each attribute assignment line is expanded.  The left-hand
	#  side of the attribute assignment is left alone.
	#
	#  Once all of the right-hand side values are expanded, the
	#  result is an `update` section with left-hand side
	#  assignments, and right-hand side values.  That `update`
	#  section is then cached, indexed by the `key`
	#
	#  When the cache entry is read, it is looked up by the `key`,
	#  and the cached `update` section is found.  This cache entry
	#  now has left-hand side assignments, and right-hand side
	#  values.  It is then applied to the current request.
	#
	#  For example, if the `cache` module is configured with the
	#  block below:
	#
	#	update {
	#	       &reply.Reply-Message := "Hello %{User-Name}"
	#	}
	#
	#  When the cache entry is created, the module will expand the
	#  right side of the entry, using the attributes from the
	#  packet.  In this case, the string could expand to `"Hello bob"`.
	#
	#  Once all of the right-hand values are expanded, the
	#  resulting cache entry will look like this:
	#
	#	update {
	#	       &reply.Reply-Message := "Hello bob"
	#	}
	#
	#  When the cache module is read, this `update` section is
	#  applied just as if it had been specified in a configuration
	#  file.
	#
	#  NOTE: Only `request`, `reply`, `control` and
	#  `session-state` lists are available for the left side of
	#  cache entries. Attempting to reference other lists *will
	#  raise an error* during config validation.
	#
	update {
		#
		#  <list>.<attribute> <op> <value>::
		#

		#  Cache all instances of `Reply-Message` in the reply list.
		&reply.Reply-Message := &reply.Reply-Message

		#  Add our own to show when the cache was last updated.
		&reply.Reply-Message += "Cache last updated at %t"

		#  Add your own value for `Class`.
		&reply.Class := "%randstr(ssssssssssssssssssssssssssssssss)"
	}

	#
	#  ## How to use
	#
	#  ### Configuration
	#
	#  This module supports a number of runtime configuration parameters
	#  represented by attributes in the `&control.` list.
	#
	#  &control.Cache-TTL:: Sets the TTL of an entry to be created, or
	#   modifies the TTL of an existing entry.
	#
	#  [options="header,autowidth"]
	#  |===
	#  | Condition            | Description
	#  | `Cache-TTL` of > `0` | Set the TTL of the entry to the new value
	#                           (and reset the expiry timer).
	#  | `Cache-TTL` of < `0` | Expire the existing entry and create a new
	#                           one with TTL set to `Cache-TTL` * `-1`.
	#  | `Cache-TTL` of `0`   | Expire the existing entry and create a new one.
	#  |===
	#
	#  &control.Cache-Status-Only:: If present and set to `yes` will
	#  prevent a new entry from being created, and existing entries from
	#  being merged. It will also alter the module's return codes.
	#
	#   * The module will return `ok` if a cache entry was found.
	#   * The module will return `notfound` if no cache entry was found.
	#
	#  NOTE: If this is set to `yes`, no other cache control attributes will
	#  be honoured, but they will still be cleared.
	#
	#  &control.Cache-Allow-Insert:: If present and set to `no` will
	#  prevent a new entry from being created. If not present or set
	#  to `yes`, and no entry exists, a new one will be created.
	#  This is evaluated after `Cache-TTL`, so expired entries may be
	#  recreated.
	#
	#  &control.Cache-Allow-Merge:: If present and set to `no` will
	#  prevent existing entries from being merged. If not present or
	#  set to `yes`, and an entry exists (and is valid), it will be
	#  merged with the current request.
	#  This is evaluated before `Cache-TTL`, so entries being expired
	#  may first be merged.
	#
	#  &control.Cache-Merge-New:: If present and set to `yes` will merge new
	#  cache entries into the current request. Useful if results of execs or
	#  expansions are stored directly in the cache.
	#
	#
	#  NOTE: All runtime configuration attributes will be removed from the
	#  `&control.` list after the cache module is called.
	#
	#  ### Methods
	#
	#  The cache module also allows handling the cache using the methods.
	#
	#  cache.status:: Verify if an entry already exists without load the entries.
	#
	#  [options="header,autowidth"]
	#  |===
	#  | Return     | Description
	#  | `ok`       | if a cache entry was found.
	#  | `notfound` | if no cache entry was found.
	#  | `fail`     | if the cache was unavailable.
	#  |===
	#
	#  cache.load:: Load an existing cache entry and merge it into the request.
	#
	#  [options="header,autowidth"]
	#  |===
	#  | Return     | Description
	#  | `updated`  | if a cache entry was found and loaded.
	#  | `notfound` | if no cache entry was found.
	#  | `fail`     | if the cache was unavailable.
	#  |===
	#
	#  cache.update:: Perform an upsert against the data store, updating the entry TTL
	#
	#  [options="header,autowidth"]
	#  |===
	#  | Return     | Description
	#  | `updated`  | if we added cache entry.
	#  | `fail`     | if the cache was unavailable.
	#  |===
	#
	#  cache.store:: Inserts data into the cache if, and only if, it is not already present
	#  Will not update the entry TTL.
	#
	#  [options="header,autowidth"]
	#  |===
	#  | Return     | Description
	#  | `updated`  | we created or updated a cache entry.
	#  | `noop`     | if a cache entry aready existed.
	#  | `fail`     | if the cache was unavailable.
	#  |===
	#
	#  cache.clear:: Delete cache entry from the data store without checking if the entry
	#  already exists.
	#
	#  [options="header,autowidth"]
	#  |===
	#  | Return     | Description
	#  | `ok`       | if we found and removed a entry.
	#  | `notfound` | if no cache entry was found.
	#  | `fail`     | if the cache was unavailable.
	#  |===
	#
	#  cache.ttl:: Change the TTL on an existing entry.
	#
	#  [options="header,autowidth"]
	#  |===
	#  | Return     | Description
	#  | `updated`  | if we found entry and updated the ttl.
	#  | `notfound` | if no cache entry was found.
	#  | `fail`     | if the cache was unavailable.
	#  |===
	#
	#  ### Examples
	#
	#  ```
	#  # Add a cache entry
	#  &control.Cache-TTL := 1h
	#  cache.store
	#  if (updated) {
	#    ..keys stored
	#  }
	#
	#  # Get the cache status
	#  cache.status
	#  if (ok) {
	#    ..Exist a cache entry
	#  }
	#
	#  # Load the cache entry
	#  cache.load
	#  if (updated) {
	#    ..loaded
	#  }
	#
	#  # Change the entries TTL
	#  &control.Cache-TTL := 30m
	#  cache.ttl
	#  if (updated) {
	#    ..ttl changed
	#  }
	#
	#  # Clear the cache
	#  cache.clear
	#  if (ok) {
	#    ..cache is empty
	#  }
	#  ```
	#
	#  [NOTE]
	#  ====
	#  * This is evaluated before `Cache-TTL`, so entries being expired
	#  may first be merged.
	#  * All runtime configuration attributes will be removed from the
	#  `&control:` list after any cache method is called.
	#  ====
	#
}
