Chromium Code Reviews| Index: chrome/common/extensions/docs/server2/appengine_memcache.py |
| diff --git a/chrome/common/extensions/docs/server2/appengine_memcache.py b/chrome/common/extensions/docs/server2/appengine_memcache.py |
| index ce01e8b52866880069f1f3a8d419f7e21fe25865..605ba71653d37181a5c801f5a3dbca66e7cd48ac 100644 |
| --- a/chrome/common/extensions/docs/server2/appengine_memcache.py |
| +++ b/chrome/common/extensions/docs/server2/appengine_memcache.py |
| @@ -2,12 +2,44 @@ |
| # Use of this source code is governed by a BSD-style license that can be |
| # found in the LICENSE file. |
| +import time |
| + |
| from appengine_wrappers import memcache |
| +from future import Future |
| MEMCACHE_FILE_SYSTEM_READ = 'MemcacheFileSystem.Get' |
| MEMCACHE_FILE_SYSTEM_STAT = 'MemcacheFileSystem.Stat' |
| MEMCACHE_GITHUB_STAT = 'MemcacheGithub.Stat' |
| MEMCACHE_BRANCH_UTILITY = 'BranchUtility' |
| +MEMCACHE_FILE_SYSTEM_CACHE = 'MemcacheFileSystemCache' |
| +MEMCACHE_FILE_SYSTEM_CACHE_LISTING = 'MemcacheFileSystemCache.Listing' |
| + |
| +class _CacheEntry(object): |
| + def __init__(self, value, expire_time): |
| + self.value = value |
| + self._never_expires = (expire_time == 0) |
| + self._expiry = time.time() + expire_time |
| + |
| + def HasExpired(self): |
| + if self._never_expires: |
| + return False |
| + return time.time() > self._expiry |
| + |
| +class _AsyncGetFuture(object): |
| + 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.
|
| + self._cache = cache |
| + self._time = time |
| + self._namespace = namespace |
| + self._rpc = rpc |
| + self._mapping = mapping |
| + |
| + def Get(self): |
| + if self._rpc is not None: |
| + result = self._rpc.get_result() |
| + self._cache[self._namespace].update( |
| + dict((k, _CacheEntry(v, self._time)) for k, v in result.iteritems())) |
| + 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.
|
| + return self._mapping |
| 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.
|
| """A wrapper around the standard App Engine memcache that enforces namespace |
| @@ -16,15 +48,71 @@ class AppEngineMemcache(object): |
| """ |
| def __init__(self, branch): |
| self._branch = branch |
| + self._cache = {} |
| + |
| + def _SetInMemory(self, key, value, namespace, time): |
| + if namespace not in self._cache: |
| + self._cache[namespace] = {} |
| + self._cache[namespace][key] = _CacheEntry(value, time) |
| + |
| + def _TryGAE(self, key, namespace, time): |
| + result = memcache.get(key, namespace=namespace) |
| + if result is not None: |
| + self._SetInMemory(key, result, namespace, time) |
| + return result |
| + |
| + def _MakeNamespace(self, namespace): |
| + return self._branch + '.' + namespace |
| + |
| + 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.
|
| + namespace = self._MakeNamespace(namespace) |
| + self._SetInMemory(key, value, namespace, time) |
| + 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
|
| + |
| + def SetMulti(self, mapping, namespace, time=300): |
| + namespace = self._MakeNamespace(namespace) |
| + for k, v in mapping.iteritems(): |
| + self._SetInMemory(k, v, namespace, time) |
| + # Use a batch set? App Engine kept throwing: |
| + # 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.
|
| + # for the batch set. |
| + self.Set(k, v, namespace, time=time) |
| - def Set(self, key, value, namespace, time=60): |
| - return memcache.set(key, |
| - value, |
| - namespace=self._branch + '.' + namespace, |
| - time=time) |
| + def Get(self, key, namespace, time=300): |
| + namespace = self._MakeNamespace(namespace) |
| + if namespace not in self._cache: |
| + return self._TryGAE(key, namespace, time) |
| + cache_entry = self._cache[namespace].get(key, None) |
| + if not cache_entry: |
| + return self._TryGAE(key, namespace, time) |
| + if cache_entry.HasExpired(): |
| + self._cache[namespace].pop(key) |
| + return memcache.get(key, namespace=namespace) |
| + return cache_entry.value |
| - def Get(self, key, namespace): |
| - return memcache.get(key, namespace=self._branch + '.' + namespace) |
| + def GetMulti(self, keys, namespace, time=300): |
| + namespace = self._MakeNamespace(namespace) |
| + keys = keys[:] |
| + mapping = {} |
| + if namespace not in self._cache: |
| + self._cache[namespace] = {} |
| + for key in keys: |
| + cache_entry = self._cache[namespace].get(key, None) |
| + 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.
|
| + mapping[key] = cache_entry.value |
| + keys.remove(key) |
| + else: |
| + mapping[key] = None |
| + client = memcache.Client() |
| + rpc = client.get_multi_async(keys, namespace=namespace) |
| + return Future(delegate=_AsyncGetFuture(self._cache, |
| + time, |
| + namespace, |
| + rpc=rpc, |
| + mapping=mapping)) |
| def Delete(self, key, namespace): |
| - return memcache.delete(key, namespace=self._branch + '.' + namespace) |
| + namespace = self._MakeNamespace(namespace) |
| + if namespace in self._cache: |
| + self._cache[namespace].pop(key) |
| + memcache.delete(key, namespace=namespace) |