Chromium Code Reviews| 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 |