Add Memory Isolating Cache Proxy

Created a specialized dogpile.cache proxy to isolate in-memory data in the
cache region from modificaiton during tests using liberal application of
copy.deepcopy for get/set operations.

partial-blueprint: caching-layer-for-driver-calls
Change-Id: Id6d27219b75ea7e6709d220542dd31c4a6c7aa04
This commit is contained in:
Morgan Fainberg 2013-08-28 22:04:18 -07:00
parent 8fdfbf04ba
commit 6dce45b1d2
4 changed files with 55 additions and 7 deletions

View File

@ -231,11 +231,14 @@ behavior is that subsystem caching is enabled, but the global toggle is set to d
* ``dogpile.cache.memory`` - in-memory cache
.. WARNING::
``dogpile.cache.memory`` is not suitable for use outside of testing or
small workloads as it does not cleanup it's internal cache on cache
expiration and does not share cache between processes. This means
that caching and cache invalidation will not be consistent or reliable
when using ``Keystone`` under HTTPD or similar configurations.
``dogpile.cache.memory`` is not suitable for use outside of unit testing
as it does not cleanup it's internal cache on cache expiration, does
not provide isolation to the cached data (values in the store can be
inadvertently changed without extra layers of data protection added),
and does not share cache between processes. This means that caching
and cache invalidation will not be consistent or reliable
when using ``Keystone`` and the ``dogpile.cache.memory`` backend under
any real workload.
* ``expiration_time`` - int, the default length of time to cache a specific value. A value of ``0``
indicates to not cache anything. It is recommended that the ``enabled`` option be used to disable

View File

@ -17,7 +17,6 @@
"""Keystone Caching Layer Implementation."""
import dogpile.cache
from dogpile.cache import proxy
from dogpile.cache import util

View File

@ -14,6 +14,8 @@
# License for the specific language governing permissions and limitations
# under the License.
import copy
from dogpile.cache import api
from dogpile.cache import proxy
@ -27,9 +29,52 @@ NO_VALUE = api.NO_VALUE
SHOULD_CACHE = cache.should_cache_fn('cache')
def _copy_value(value):
if value is not NO_VALUE:
value = copy.deepcopy(value)
return value
# NOTE(morganfainberg): WARNING - It is not recommended to use the Memory
# backend for dogpile.cache in a real deployment under any circumstances. The
# backend does no cleanup of expired values and therefore will leak memory. The
# backend is not implemented in a way to share data across processes (e.g.
# Keystone in HTTPD. This proxy is a hack to get around the lack of isolation
# of values in memory. Currently it blindly stores and retrieves the values
# from the cache, and modifications to dicts/lists/etc returned can result in
# changes to the cached values. In short, do not use the dogpile.cache.memory
# backend unless you are running tests or expecting odd/strange results.
class CacheIsolatingProxy(proxy.ProxyBackend):
"""Proxy that forces a memory copy of stored values.
The default in-memory cache-region does not perform a copy on values it
is meant to cache. Therefore if the value is modified after set or after
get, the cached value also is modified. This proxy does a copy as the last
thing before storing data.
"""
def get(self, key):
value = _copy_value(self.proxied.get(key))
return value
def get_multi(self, keys):
values = []
for value in self.proxied.get_multi(keys):
value = _copy_value(value)
values.append(value)
return values
def set(self, key, value):
value = _copy_value(value)
self.proxied.set(key, value)
def set_multi(self, mapping):
for key, value in mapping.items():
value = _copy_value(value)
self._cache[key] = value
class TestProxy(proxy.ProxyBackend):
def get(self, key):
value = self.proxied.get(key)
value = _copy_value(self.proxied.get(key))
if value != NO_VALUE:
value[0].cached = True
return value

View File

@ -18,6 +18,7 @@ driver = keystone.token.backends.kvs.Token
backend = dogpile.cache.memory
enabled = True
debug_cache_backend = True
proxies = keystone.tests.test_cache.CacheIsolatingProxy
[oauth1]
driver = keystone.contrib.oauth1.backends.kvs.OAuth1