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 |