#  -*- text -*-
#
#
#  $Id: 59e4db24a43c1a7581328930a39ee0e20df317eb $

#######################################################################
#
#  = 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.
	#  | `htrie`               | An in memory, non persistent datastore which can use
	#                            a hash table, rbtree or patricia trie store depending
	#                            on the data type of the key.
	#  | `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:
	#

#
#  === Htrie cache driver
#
	htrie {
		#
		#  type:: Htrie backend type
		#
		#  [options="header,autowidth"]
		#  |===
		#  | Option    | Description
		#  | `auto`    | The backend is automatically determined based on data type of the key
		#  | `hash`    | Use a hash table
		#  | `rb`      | Use an rbtree
		#  | `trie`    | Use a patricia trie
		#  |===
		type = "auto"
	}

#
#  === 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 := "%str.rand(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
	#  |                      | which uses the module default TTL.
	#  |===

	#
	#  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.
	#  ====
	#
}
