"""
Chain together multiple caches for tiered cacheing strategy
"""
import typing
from fictive.cache.abstract import AbstractCache
[docs]class MultilevelCache(AbstractCache):
"""
Manage multiple levels of a value cache hierarchy
Chain together one or more `AbstractCache` instances to cache a value at
multiple storage levels
"""
# pylint: disable=too-few-public-methods
[docs] class MultilevelCacheMissError(AbstractCache.CacheMissError):
"""
raised in a cache miss propogates through all cache levels
"""
[docs] def __init__(self, cache_levels: typing.Sequence, *args, **kwargs):
"""
:param cache_levels:
`AbstractCache` implementation(s); cache(s) will be accessed
hierarchically in the provided order
"""
super(MultilevelCache, self).__init__(**kwargs)
self.cache_levels = cache_levels
for cache_level in self.cache_levels:
cache_level.fetch_impl = self._fetch_next
def _fetch_next(self, cache, *args, **kwargs):
"""
Trampoline to chain a cache's `fetch_impl` to the next cache's `get_value`
"""
next_index = self.cache_levels.index(cache) + 1
if next_index < len(self.cache_levels):
return self.cache_levels[next_index].get_value(*args, **kwargs)
return self.fetch_impl(*args, **kwargs) # pylint: disable=not-callable
def _read_impl(self, *args, **kwargs):
"""
read the lowest level of the cache levels
"""
return self.cache_levels[0].get_value(*args, **kwargs)
def _dispatch_fetch(self, *args, **kwargs):
"""
fetching handled by cache levels
"""
raise self.MultilevelCacheMissError()
def _set_impl(self, value, *args, **kwargs):
"""
set the highest level of the cache and then clear the lower levels
"""
self.cache_levels[-1].set_value(value, *args, **kwargs)
for cache_level in reversed(self.cache_levels[:-1]):
cache_level.delete_value(cache_level.ANY_VALUE)
def _delete_impl(self, cache_value, *args, **kwargs):
"""
clear all levels of the cache
"""
any_deleted = False
for cache_level in self.cache_levels:
level_deleted = cache_level.delete_value(cache_value)
if level_deleted:
any_deleted = True
if level_deleted or cache_value == self.ANY_VALUE:
continue
return any_deleted
return any_deleted