Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(1952)

Unified Diff: appengine/config_service/projects.py

Issue 2931673003: config_service: fetch repos and metadata concurrently (Closed)
Patch Set: use ctx.memcache_[gs]et Created 3 years, 6 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
« no previous file with comments | « appengine/config_service/api_test.py ('k') | appengine/config_service/projects_test.py » ('j') | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: appengine/config_service/projects.py
diff --git a/appengine/config_service/projects.py b/appengine/config_service/projects.py
index b20155c7124112c505be55226cc9fb1262a5ac6c..7b402b8eeaf1c6dbb884d0f974e978c67309c526 100644
--- a/appengine/config_service/projects.py
+++ b/appengine/config_service/projects.py
@@ -77,36 +77,45 @@ def get_project(id):
return None
-def get_repos(project_ids):
+@ndb.tasklet
+def get_repos_async(project_ids):
"""Returns a mapping {project_id: (repo_type, repo_url)}.
All projects must exist.
"""
assert isinstance(project_ids, list)
- keys = [ndb.Key(ProjectImportInfo, pid) for pid in project_ids]
- return {
+ infos = yield ndb.get_multi_async(
+ ndb.Key(ProjectImportInfo, pid) for pid in project_ids)
+ raise ndb.Return({
pid: (info.repo_type, info.repo_url) if info else (None, None)
- for pid, info in zip(project_ids, ndb.get_multi(keys))
- }
+ for pid, info in zip(project_ids, infos)
+ })
-def get_metadata(project_ids):
+@ndb.tasklet
+def get_metadata_async(project_ids):
"""Returns a mapping {project_id: metadata}.
If a project does not exist, the metadata is None.
The project metadata stored in project.cfg files in each project.
"""
+ PROJECT_DOES_NOT_EXIST_SENTINEL = (0,)
cache_ns = 'projects.get_metadata'
- cache_map = memcache.get_multi(project_ids, namespace=cache_ns)
+ ctx = ndb.get_context()
+ # ctx.memcache_get is auto-batching. Internally it makes get_multi RPC.
+ cache_futs = {
+ pid: ctx.memcache_get(pid, namespace=cache_ns)
+ for pid in project_ids
+ }
+ yield cache_futs.values()
result = {}
missing = []
for pid in project_ids:
- if pid in cache_map:
+ binary = cache_futs[pid].get_result()
+ if binary is not None:
# cache hit
- binary = cache_map[pid]
- if binary is None:
- # project does not exist
+ if binary is PROJECT_DOES_NOT_EXIST_SENTINEL:
result[pid] = None
else:
cfg = project_config_pb2.ProjectCfg()
@@ -117,18 +126,22 @@ def get_metadata(project_ids):
missing.append(pid)
if missing:
- fetched = _get_project_configs(
+ fetched = yield _get_project_configs_async(
missing, common.PROJECT_METADATA_FILENAME,
project_config_pb2.ProjectCfg)
result.update(fetched) # at this point result must have all project ids
# Cache metadata for 10 min. In practice, it never changes.
- cache_map = {
- pid: cfg.SerializeToString() if cfg else None
+ # ctx.memcache_set is auto-batching. Internally it makes set_multi RPC.
+ yield [
+ ctx.memcache_set(
+ pid,
+ cfg.SerializeToString() if cfg else PROJECT_DOES_NOT_EXIST_SENTINEL,
+ namespace=cache_ns,
+ time=60 * 10)
for pid, cfg in fetched.iteritems()
- }
- memcache.set_multi(cache_map, namespace=cache_ns, time=60 * 10)
+ ]
- return result
+ raise ndb.Return(result)
def get_refs(project_ids):
@@ -138,15 +151,16 @@ def get_refs(project_ids):
The list of refs stored in refs.cfg of a project.
"""
- cfgs = _get_project_configs(
- project_ids, common.REFS_FILENAME, project_config_pb2.RefsCfg)
+ cfgs = _get_project_configs_async(
+ project_ids, common.REFS_FILENAME, project_config_pb2.RefsCfg
+ ).get_result()
return {
pid: None if cfg is None else cfg.refs or DEFAULT_REF_CFG.refs
for pid, cfg in cfgs.iteritems()
}
-def _get_project_configs(project_ids, path, message_factory):
+def _get_project_configs_async(project_ids, path, message_factory):
"""Returns a mapping {project_id: message}.
If a project does not exist, the message is None.
@@ -154,15 +168,20 @@ def _get_project_configs(project_ids, path, message_factory):
assert isinstance(project_ids, list)
if not project_ids:
return {}
- prefix = 'projects/'
- messages = storage.get_latest_messages_async(
- [prefix + pid for pid in _filter_existing(project_ids)],
- path, message_factory).get_result()
- return {
- # messages may not have a key because we filter project ids by existence
- pid: messages.get(prefix + pid)
- for pid in project_ids
- }
+
+ @ndb.tasklet
+ def get_async():
+ prefix = 'projects/'
+ messages = yield storage.get_latest_messages_async(
+ [prefix + pid for pid in _filter_existing(project_ids)],
+ path, message_factory)
+ raise ndb.Return({
+ # messages may not have a key because we filter project ids by existence
+ pid: messages.get(prefix + pid)
+ for pid in project_ids
+ })
+
+ return get_async()
def _filter_existing(project_ids):
« no previous file with comments | « appengine/config_service/api_test.py ('k') | appengine/config_service/projects_test.py » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698