Chromium Code Reviews| Index: chrome/common/extensions/docs/server2/in_memory_object_store.py |
| diff --git a/chrome/common/extensions/docs/server2/in_memory_object_store.py b/chrome/common/extensions/docs/server2/in_memory_object_store.py |
| new file mode 100644 |
| index 0000000000000000000000000000000000000000..f2499725a76226136caed1c4c71ff4b495e93169 |
| --- /dev/null |
| +++ b/chrome/common/extensions/docs/server2/in_memory_object_store.py |
| @@ -0,0 +1,117 @@ |
| +# Copyright (c) 2012 The Chromium Authors. All rights reserved. |
| +# Use of this source code is governed by a BSD-style license that can be |
| +# found in the LICENSE file. |
| + |
| +import time |
| + |
| +from future import Future |
| +from object_store import ObjectStore, CACHE_TIMEOUT |
| +from memcache_object_store import MemcacheObjectStore |
| + |
| +class _CacheEntry(object): |
| + def __init__(self, value, expire_time): |
| + self.value = value |
| + self._never_expires = (expire_time == 0) |
| + self._expiry = time.time() + expire_time |
| + |
| + def HasExpired(self): |
| + if self._never_expires: |
| + return False |
| + return time.time() > self._expiry |
| + |
| +class _AsyncGetFuture(object): |
| + """A future for memcache gets. |
| + |
| + Properties: |
| + - |cache| the in-memory cache used by InMemoryObjectStore |
| + - |time| the cache timeout |
| + - |namespace| the namespace of the cache items |
| + - |future| the |Future| from the backing |ObjectStore| |
| + - |initial_mapping| a mapping of cache items already in memory |
| + """ |
| + def __init__(self, cache, time, namespace, future, initial_mapping): |
| + self._cache = cache |
| + self._time = time |
| + self._namespace = namespace |
| + self._future = future |
| + self._mapping = initial_mapping |
| + |
| + def Get(self): |
| + if self._future is not None: |
| + result = self._future.Get() |
| + self._cache[self._namespace].update( |
| + dict((k, _CacheEntry(v, self._time)) for k, v in result.iteritems())) |
| + self._mapping.update(result) |
| + return self._mapping |
| + |
| +class InMemoryObjectStore(ObjectStore): |
| + def __init__(self, branch): |
| + self._branch = branch |
| + self._cache = {} |
| + self._object_store = MemcacheObjectStore() |
| + |
| + def _SetInMemory(self, key, value, namespace, time): |
| + if namespace not in self._cache: |
| + self._cache[namespace] = {} |
| + self._cache[namespace][key] = _CacheEntry(value, time) |
| + |
| + def _TryBackingObjectStore(self, key, namespace, time): |
| + result = self._object_store.Get(key, namespace, time=time) |
| + if result is not None: |
| + self._SetInMemory(key, result, namespace, time) |
| + return result |
| + |
| + def _MakeNamespace(self, namespace): |
| + return 'ObjectStore.' + self._branch + '.' + namespace |
|
not at google - send to devlin
2012/08/21 00:30:11
+'ing strings in python is inefficient, use '.'.jo
cduvall
2012/08/21 01:33:33
Done.
|
| + |
| + def Set(self, key, value, namespace, time=CACHE_TIMEOUT): |
| + namespace = self._MakeNamespace(namespace) |
| + self._SetInMemory(key, value, namespace, time) |
| + self._object_store.Set(key, value, namespace, time=time) |
| + |
| + def SetMulti(self, mapping, namespace, time=CACHE_TIMEOUT): |
| + namespace = self._MakeNamespace(namespace) |
| + for k, v in mapping.iteritems(): |
| + self._SetInMemory(k, v, namespace, time) |
| + # TODO(cduvall): Use a batch set? App Engine kept throwing: |
| + # ValueError: Values may not be more than 1000000 bytes in length |
| + # for the batch set. |
| + self._object_store.Set(k, v, namespace, time=time) |
| + |
| + def Get(self, key, namespace, time=CACHE_TIMEOUT): |
| + namespace = self._MakeNamespace(namespace) |
| + if namespace not in self._cache: |
| + return self._TryBackingObjectStore(key, namespace, time) |
| + cache_entry = self._cache[namespace].get(key, None) |
| + if not cache_entry: |
| + return self._TryBackingObjectStore(key, namespace, time) |
| + if cache_entry.HasExpired(): |
| + self._cache[namespace].pop(key) |
| + return self._object_store.Get(key, namespace, time=time) |
| + return cache_entry.value |
| + |
| + def GetMulti(self, keys, namespace, time=CACHE_TIMEOUT): |
| + namespace = self._MakeNamespace(namespace) |
| + keys = keys[:] |
| + mapping = {} |
| + if namespace not in self._cache: |
| + self._cache[namespace] = {} |
| + for key in keys: |
| + cache_entry = self._cache[namespace].get(key, None) |
| + if cache_entry is None or cache_entry.HasExpired(): |
| + mapping[key] = None |
| + else: |
| + mapping[key] = cache_entry.value |
| + keys.remove(key) |
| + future = self._object_store.GetMulti(keys, namespace, time=time) |
| + return Future(delegate=_AsyncGetFuture(self._cache, |
| + time, |
| + namespace, |
| + future, |
| + mapping)) |
| + |
|
not at google - send to devlin
2012/08/21 00:30:11
A lot of this code would be simplified by having G
cduvall
2012/08/21 01:33:33
Done.
|
| + def Delete(self, key, namespace): |
| + namespace = self._MakeNamespace(namespace) |
| + if namespace in self._cache: |
| + self._cache[namespace].pop(key) |
| + self._object_store.Delete(key, namespace) |