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?)