Kazoo Caches#
The kz_cache
interface exposes an LRU cache for storing Erlang terms in an ETS table.
This is heavily used to cache document lookups from CouchDB but is generally useful.
Features of the cache#
Expiring entries#
When stored with {'expires', timeout()}
where timeout()
is in seconds or 'infinity'
, the cache entry will stay in the cache until after timeout()
seconds have elapsed.
Note
The cache will check for expired entries, by default, based on the EXPIRE_PERIOD_MS
macro in the header. Caches can be started with alternate expiration timeouts. Lower timeouts mean more work being done to expire caches but entries are evicted closer to the expected timeout; higher timeouts mean less work but an expired cache entry may exist after expiration.
Self-flushing#
The cache has a gen_listener
process that can subscribe for document-change notices via AMQP (like when a document is successfully saved/deleted from CouchDB). This allows the cache to be programmatically flushed for entries that are no longer valid.
Callbacks#
Cache entries can have associated callbacks for various stages in the cache entry's lifecycle.
Include a callback function when storing a key/value pair:
kz_cache:store_local(CacheName, <<"key">>, <<"value">>, [{'callback', fun some_callback/3}]).
The callback function will receive three arguments:
some_callback(Key, Value, CallbackReason).
CallbackReason
can be one of 'expire'
, 'erase'
, 'flush'
, 'timeout'
, or 'store'
.
Timeout#
Monitor timer expires. Currently this isn't possible to be reached as the only monitor keys are for the wait_for_
functions.
Expire#
Cache entries that have expired
Erasure#
Cache entries are erased
Flush#
Cache entries are flushed
Store#
A cache entry is stored
Code layout#
kz_cache
#
API module
kz_cache_lru
#
Enforces the expiration of cache entries
kz_cache_ets
#
ETS table manipulations
kz_cache_callbacks
#
Callbacks executed on various events in the cache
kz_cache_listener
#
AMQP listener for document changes, if configured
kz_cache_nodes
#
kz_nodes
listener for new/expiring nodes
kz_cache_callbacks
#
Callbacks processor module
kz_cache_processes
#
If a cache entry is stored with {'monitor', 'true'}
or {'monitor', [pid()]}
, kz_cache_process
will monitor the PID(s) and remove cache entries if the PID(s) die.
ETS architecture#
Main ETS table#
Tracks the cache entry, expiration time, etc
Monitor ETS table#
Certain situations where processes can monitor a cache key (wait_for_*
functions).
Pointer ETS table#
Tracks the AMQP bindings associated with the key for automatic flushing.
Cache Strategies#
See what strategy a node is using:
sup kazoo_data_maintenance cache_strategy
strategy: none
stampede mitigation: false
async store: false
None#
This strategy does not use stampede mitigation and uses a blocking store operation.
sup kazoo_data_maintenance set_cache_strategy none
Async#
This strategy does not use stampede mitigation and uses a non-blocking store operation.
sup kazoo_data_maintenance set_cache_strategy async
Stampede#
This strategy uses stampede mitigation and uses a blocking store operation
sup kazoo_data_maintenance set_cache_strategy stampede
Stampede Async#
When the cache entry is missing, the database operation will proceed. However, when it comes time to cache the value, the key is locked until the store operation is successful. When a competing process tries to cache its value and sees the locked key, it will noop the store and continue on with life.
Essentially, the winning
process will lock-then-async-store the cache value. All other processes will noop-store and continue, thus avoiding messages to the cache process mailbox.
sup kazoo_data_maintenance set_cache_strategy stampede_async
Todo#
[ ] Move callback processing to alternative module
[ ] Monitor kz_cache
mailbox during load tests (maybe dbg?)