OLD | NEW |
| (Empty) |
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 | |
3 # found in the LICENSE file. | |
4 | |
5 import time | |
6 | |
7 from appengine_wrappers import CACHE_TIMEOUT | |
8 from future import Future | |
9 from object_store import ObjectStore | |
10 | |
11 class _CacheEntry(object): | |
12 def __init__(self, value, expire_time): | |
13 self.value = value | |
14 self._never_expires = (expire_time == 0) | |
15 self._expiry = time.time() + expire_time | |
16 | |
17 def HasExpired(self): | |
18 if self._never_expires: | |
19 return False | |
20 return time.time() > self._expiry | |
21 | |
22 class _AsyncGetFuture(object): | |
23 """A future for memcache gets. | |
24 | |
25 Properties: | |
26 - |cache| the in-memory cache used by InMemoryObjectStore | |
27 - |time| the cache timeout | |
28 - |future| the |Future| from the backing |ObjectStore| | |
29 - |initial_mapping| a mapping of cache items already in memory | |
30 """ | |
31 def __init__(self, cache, time, future, initial_mapping): | |
32 self._cache = cache | |
33 self._time = time | |
34 self._future = future | |
35 self._mapping = initial_mapping | |
36 | |
37 def Get(self): | |
38 if self._future is not None: | |
39 result = self._future.Get() | |
40 self._cache.update( | |
41 dict((k, _CacheEntry(v, self._time)) for k, v in result.iteritems())) | |
42 self._mapping.update(result) | |
43 return self._mapping | |
44 | |
45 class InMemoryObjectStore(ObjectStore): | |
46 def __init__(self, object_store): | |
47 self._object_store = object_store | |
48 self._cache = {} | |
49 | |
50 def SetMulti(self, mapping, time=CACHE_TIMEOUT): | |
51 for k, v in mapping.iteritems(): | |
52 self._cache[k] = _CacheEntry(v, time) | |
53 # TODO(cduvall): Use a batch set? App Engine kept throwing: | |
54 # ValueError: Values may not be more than 1000000 bytes in length | |
55 # for the batch set. | |
56 self._object_store.Set(k, v, time=time) | |
57 | |
58 def GetMulti(self, keys, time=CACHE_TIMEOUT): | |
59 keys = keys[:] | |
60 mapping = {} | |
61 for key in keys: | |
62 cache_entry = self._cache.get(key, None) | |
63 if cache_entry is None or cache_entry.HasExpired(): | |
64 mapping[key] = None | |
65 else: | |
66 mapping[key] = cache_entry.value | |
67 keys.remove(key) | |
68 future = self._object_store.GetMulti(keys, time=time) | |
69 return Future(delegate=_AsyncGetFuture(self._cache, | |
70 time, | |
71 future, | |
72 mapping)) | |
73 | |
74 def Delete(self, key): | |
75 if key in self._cache: | |
76 self._cache.pop(key) | |
77 self._object_store.Delete(key) | |
OLD | NEW |