| OLD | NEW |
| 1 # Copyright (c) 2012 The Chromium Authors. All rights reserved. | 1 # Copyright (c) 2012 The Chromium Authors. All rights reserved. |
| 2 # Use of this source code is governed by a BSD-style license that can be | 2 # Use of this source code is governed by a BSD-style license that can be |
| 3 # found in the LICENSE file. | 3 # found in the LICENSE file. |
| 4 | 4 |
| 5 import time | 5 import time |
| 6 | 6 |
| 7 from appengine_wrappers import CACHE_TIMEOUT | 7 from appengine_wrappers import CACHE_TIMEOUT |
| 8 from future import Future | 8 from future import Future |
| 9 from object_store import ObjectStore | 9 from object_store import ObjectStore |
| 10 from memcache_object_store import MemcacheObjectStore | |
| 11 | 10 |
| 12 class _CacheEntry(object): | 11 class _CacheEntry(object): |
| 13 def __init__(self, value, expire_time): | 12 def __init__(self, value, expire_time): |
| 14 self.value = value | 13 self.value = value |
| 15 self._never_expires = (expire_time == 0) | 14 self._never_expires = (expire_time == 0) |
| 16 self._expiry = time.time() + expire_time | 15 self._expiry = time.time() + expire_time |
| 17 | 16 |
| 18 def HasExpired(self): | 17 def HasExpired(self): |
| 19 if self._never_expires: | 18 if self._never_expires: |
| 20 return False | 19 return False |
| 21 return time.time() > self._expiry | 20 return time.time() > self._expiry |
| 22 | 21 |
| 23 class _AsyncGetFuture(object): | 22 class _AsyncGetFuture(object): |
| 24 """A future for memcache gets. | 23 """A future for memcache gets. |
| 25 | 24 |
| 26 Properties: | 25 Properties: |
| 27 - |cache| the in-memory cache used by InMemoryObjectStore | 26 - |cache| the in-memory cache used by InMemoryObjectStore |
| 28 - |time| the cache timeout | 27 - |time| the cache timeout |
| 29 - |namespace| the namespace of the cache items | |
| 30 - |future| the |Future| from the backing |ObjectStore| | 28 - |future| the |Future| from the backing |ObjectStore| |
| 31 - |initial_mapping| a mapping of cache items already in memory | 29 - |initial_mapping| a mapping of cache items already in memory |
| 32 """ | 30 """ |
| 33 def __init__(self, cache, time, namespace, future, initial_mapping): | 31 def __init__(self, cache, time, future, initial_mapping): |
| 34 self._cache = cache | 32 self._cache = cache |
| 35 self._time = time | 33 self._time = time |
| 36 self._namespace = namespace | |
| 37 self._future = future | 34 self._future = future |
| 38 self._mapping = initial_mapping | 35 self._mapping = initial_mapping |
| 39 | 36 |
| 40 def Get(self): | 37 def Get(self): |
| 41 if self._future is not None: | 38 if self._future is not None: |
| 42 result = self._future.Get() | 39 result = self._future.Get() |
| 43 self._cache[self._namespace].update( | 40 self._cache.update( |
| 44 dict((k, _CacheEntry(v, self._time)) for k, v in result.iteritems())) | 41 dict((k, _CacheEntry(v, self._time)) for k, v in result.iteritems())) |
| 45 self._mapping.update(result) | 42 self._mapping.update(result) |
| 46 return self._mapping | 43 return self._mapping |
| 47 | 44 |
| 48 class InMemoryObjectStore(ObjectStore): | 45 class InMemoryObjectStore(ObjectStore): |
| 49 def __init__(self, branch): | 46 def __init__(self, object_store): |
| 50 self._branch = branch | 47 self._object_store = object_store |
| 51 self._cache = {} | 48 self._cache = {} |
| 52 self._object_store = MemcacheObjectStore() | |
| 53 | 49 |
| 54 def _MakeNamespace(self, namespace): | 50 def SetMulti(self, mapping, time=CACHE_TIMEOUT): |
| 55 return 'ObjectStore.%s.%s' % (self._branch, namespace) | |
| 56 | |
| 57 def SetMulti(self, mapping, namespace, time=CACHE_TIMEOUT): | |
| 58 namespace = self._MakeNamespace(namespace) | |
| 59 for k, v in mapping.iteritems(): | 51 for k, v in mapping.iteritems(): |
| 60 if namespace not in self._cache: | 52 self._cache[k] = _CacheEntry(v, time) |
| 61 self._cache[namespace] = {} | |
| 62 self._cache[namespace][k] = _CacheEntry(v, time) | |
| 63 # TODO(cduvall): Use a batch set? App Engine kept throwing: | 53 # TODO(cduvall): Use a batch set? App Engine kept throwing: |
| 64 # ValueError: Values may not be more than 1000000 bytes in length | 54 # ValueError: Values may not be more than 1000000 bytes in length |
| 65 # for the batch set. | 55 # for the batch set. |
| 66 self._object_store.Set(k, v, namespace, time=time) | 56 self._object_store.Set(k, v, time=time) |
| 67 | 57 |
| 68 def GetMulti(self, keys, namespace, time=CACHE_TIMEOUT): | 58 def GetMulti(self, keys, time=CACHE_TIMEOUT): |
| 69 namespace = self._MakeNamespace(namespace) | |
| 70 keys = keys[:] | 59 keys = keys[:] |
| 71 mapping = {} | 60 mapping = {} |
| 72 if namespace not in self._cache: | |
| 73 self._cache[namespace] = {} | |
| 74 for key in keys: | 61 for key in keys: |
| 75 cache_entry = self._cache[namespace].get(key, None) | 62 cache_entry = self._cache.get(key, None) |
| 76 if cache_entry is None or cache_entry.HasExpired(): | 63 if cache_entry is None or cache_entry.HasExpired(): |
| 77 mapping[key] = None | 64 mapping[key] = None |
| 78 else: | 65 else: |
| 79 mapping[key] = cache_entry.value | 66 mapping[key] = cache_entry.value |
| 80 keys.remove(key) | 67 keys.remove(key) |
| 81 future = self._object_store.GetMulti(keys, namespace, time=time) | 68 future = self._object_store.GetMulti(keys, time=time) |
| 82 return Future(delegate=_AsyncGetFuture(self._cache, | 69 return Future(delegate=_AsyncGetFuture(self._cache, |
| 83 time, | 70 time, |
| 84 namespace, | |
| 85 future, | 71 future, |
| 86 mapping)) | 72 mapping)) |
| 87 | 73 |
| 88 def Delete(self, key, namespace): | 74 def Delete(self, key): |
| 89 namespace = self._MakeNamespace(namespace) | 75 if key in self._cache: |
| 90 if namespace in self._cache and key in self._cache[namespace]: | 76 self._cache.pop(key) |
| 91 self._cache[namespace].pop(key) | 77 self._object_store.Delete(key) |
| 92 self._object_store.Delete(key, namespace) | |
| OLD | NEW |