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 | |
6 | |
5 from appengine_wrappers import memcache | 7 from appengine_wrappers import memcache |
8 from future import Future | |
6 | 9 |
7 MEMCACHE_FILE_SYSTEM_READ = 'MemcacheFileSystem.Get' | 10 MEMCACHE_FILE_SYSTEM_READ = 'MemcacheFileSystem.Get' |
8 MEMCACHE_FILE_SYSTEM_STAT = 'MemcacheFileSystem.Stat' | 11 MEMCACHE_FILE_SYSTEM_STAT = 'MemcacheFileSystem.Stat' |
9 MEMCACHE_GITHUB_STAT = 'MemcacheGithub.Stat' | 12 MEMCACHE_GITHUB_STAT = 'MemcacheGithub.Stat' |
10 MEMCACHE_BRANCH_UTILITY = 'BranchUtility' | 13 MEMCACHE_BRANCH_UTILITY = 'BranchUtility' |
14 MEMCACHE_FILE_SYSTEM_CACHE = 'MemcacheFileSystemCache' | |
15 MEMCACHE_FILE_SYSTEM_CACHE_LISTING = 'MemcacheFileSystemCache.Listing' | |
16 | |
17 class _CacheEntry(object): | |
18 def __init__(self, value, expire_time): | |
19 self.value = value | |
20 self._never_expires = (expire_time == 0) | |
21 self._expiry = time.time() + expire_time | |
22 | |
23 def HasExpired(self): | |
24 if self._never_expires: | |
25 return False | |
26 return time.time() > self._expiry | |
27 | |
28 class _AsyncGetFuture(object): | |
29 def __init__(self, cache, time, namespace, rpc=None, mapping=None): | |
not at google - send to devlin
2012/08/20 05:27:10
document these parameters?
I had to read the code
cduvall
2012/08/20 21:28:09
Done.
| |
30 self._cache = cache | |
31 self._time = time | |
32 self._namespace = namespace | |
33 self._rpc = rpc | |
34 self._mapping = mapping | |
35 | |
36 def Get(self): | |
37 if self._rpc is not None: | |
38 result = self._rpc.get_result() | |
39 self._cache[self._namespace].update( | |
40 dict((k, _CacheEntry(v, self._time)) for k, v in result.iteritems())) | |
41 self._mapping.update(result) | |
not at google - send to devlin
2012/08/20 05:27:10
this only works if initial_mapping is not None, an
cduvall
2012/08/20 21:28:09
Done.
| |
42 return self._mapping | |
11 | 43 |
12 class AppEngineMemcache(object): | 44 class AppEngineMemcache(object): |
not at google - send to devlin
2012/08/20 05:27:10
This all just got complicated.
You're gonna hate
cduvall
2012/08/20 21:28:09
Done.
| |
13 """A wrapper around the standard App Engine memcache that enforces namespace | 45 """A wrapper around the standard App Engine memcache that enforces namespace |
14 use. Uses a branch to make sure there are no key collisions if separate | 46 use. Uses a branch to make sure there are no key collisions if separate |
15 branches cache the same file. | 47 branches cache the same file. |
16 """ | 48 """ |
17 def __init__(self, branch): | 49 def __init__(self, branch): |
18 self._branch = branch | 50 self._branch = branch |
51 self._cache = {} | |
19 | 52 |
20 def Set(self, key, value, namespace, time=60): | 53 def _SetInMemory(self, key, value, namespace, time): |
21 return memcache.set(key, | 54 if namespace not in self._cache: |
22 value, | 55 self._cache[namespace] = {} |
23 namespace=self._branch + '.' + namespace, | 56 self._cache[namespace][key] = _CacheEntry(value, time) |
24 time=time) | |
25 | 57 |
26 def Get(self, key, namespace): | 58 def _TryGAE(self, key, namespace, time): |
27 return memcache.get(key, namespace=self._branch + '.' + namespace) | 59 result = memcache.get(key, namespace=namespace) |
60 if result is not None: | |
61 self._SetInMemory(key, result, namespace, time) | |
62 return result | |
63 | |
64 def _MakeNamespace(self, namespace): | |
65 return self._branch + '.' + namespace | |
66 | |
67 def Set(self, key, value, namespace, time=300): | |
not at google - send to devlin
2012/08/20 05:27:10
define 300 as a constant somewhere?
cduvall
2012/08/20 21:28:09
Done.
| |
68 namespace = self._MakeNamespace(namespace) | |
69 self._SetInMemory(key, value, namespace, time) | |
70 memcache.set(key, value, namespace=namespace, time=time) | |
not at google - send to devlin
2012/08/20 05:27:10
this can be an async set, right? We don't care abo
cduvall
2012/08/20 21:28:09
There isn't a set_async function in memcache, only
| |
71 | |
72 def SetMulti(self, mapping, namespace, time=300): | |
73 namespace = self._MakeNamespace(namespace) | |
74 for k, v in mapping.iteritems(): | |
75 self._SetInMemory(k, v, namespace, time) | |
76 # Use a batch set? App Engine kept throwing: | |
77 # ValueError: Values may not be more than 1000000 bytes in length | |
not at google - send to devlin
2012/08/20 05:27:10
Interesting.
Yeah make this a TODO. If it's an as
cduvall
2012/08/20 21:28:09
Done.
| |
78 # for the batch set. | |
79 self.Set(k, v, namespace, time=time) | |
80 | |
81 def Get(self, key, namespace, time=300): | |
82 namespace = self._MakeNamespace(namespace) | |
83 if namespace not in self._cache: | |
84 return self._TryGAE(key, namespace, time) | |
85 cache_entry = self._cache[namespace].get(key, None) | |
86 if not cache_entry: | |
87 return self._TryGAE(key, namespace, time) | |
88 if cache_entry.HasExpired(): | |
89 self._cache[namespace].pop(key) | |
90 return memcache.get(key, namespace=namespace) | |
91 return cache_entry.value | |
92 | |
93 def GetMulti(self, keys, namespace, time=300): | |
94 namespace = self._MakeNamespace(namespace) | |
95 keys = keys[:] | |
96 mapping = {} | |
97 if namespace not in self._cache: | |
98 self._cache[namespace] = {} | |
99 for key in keys: | |
100 cache_entry = self._cache[namespace].get(key, None) | |
101 if cache_entry is not None and not cache_entry.HasExpired(): | |
not at google - send to devlin
2012/08/20 05:27:10
nit: reverse the condition to make this more reada
cduvall
2012/08/20 21:28:09
Done.
| |
102 mapping[key] = cache_entry.value | |
103 keys.remove(key) | |
104 else: | |
105 mapping[key] = None | |
106 client = memcache.Client() | |
107 rpc = client.get_multi_async(keys, namespace=namespace) | |
108 return Future(delegate=_AsyncGetFuture(self._cache, | |
109 time, | |
110 namespace, | |
111 rpc=rpc, | |
112 mapping=mapping)) | |
28 | 113 |
29 def Delete(self, key, namespace): | 114 def Delete(self, key, namespace): |
30 return memcache.delete(key, namespace=self._branch + '.' + namespace) | 115 namespace = self._MakeNamespace(namespace) |
116 if namespace in self._cache: | |
117 self._cache[namespace].pop(key) | |
118 memcache.delete(key, namespace=namespace) | |
OLD | NEW |