Index: chrome/common/extensions/docs/server2/caching_file_system.py |
diff --git a/chrome/common/extensions/docs/server2/caching_file_system.py b/chrome/common/extensions/docs/server2/caching_file_system.py |
index b0573716f1fbc2fa56cc68e163bd5d627a9f2856..65b3dab49e80857ff136a8087d5cd9679c6b8eb9 100644 |
--- a/chrome/common/extensions/docs/server2/caching_file_system.py |
+++ b/chrome/common/extensions/docs/server2/caching_file_system.py |
@@ -2,6 +2,7 @@ |
# Use of this source code is governed by a BSD-style license that can be |
# found in the LICENSE file. |
+import logging |
import posixpath |
import sys |
@@ -12,18 +13,30 @@ from third_party.json_schema_compiler.memoize import memoize |
class CachingFileSystem(FileSystem): |
- '''FileSystem which implements a caching layer on top of |file_system|. It's |
- smart, using Stat() to decided whether to skip Read()ing from |file_system|, |
- and only Stat()ing directories never files. |
+ '''FileSystem which implements a caching layer on top of |file_system|. If |
+ |fail_on_miss| is True then cache misses throw a FileNotFoundError rather than |
+ falling back onto the underlying FileSystem. |
+ |
+ If the underlying FileSystem is versioned (i.e., it implements GetVersion to |
+ return something other than None), this will create a persistent stat cache |
+ (keyed on the FileSystem instance's version) as an additional optimization. |
''' |
- def __init__(self, file_system, object_store_creator): |
+ def __init__(self, file_system, object_store_creator, fail_on_miss=False): |
self._file_system = file_system |
- def create_object_store(category, **optargs): |
+ self._fail_on_miss = fail_on_miss |
+ def create_object_store(category, try_versioning=False, **optargs): |
+ version = file_system.GetVersion() |
+ versioned = try_versioning and version is not None |
+ if versioned: |
+ identity = '%s/%s' % (file_system.GetIdentity(), version) |
+ else: |
+ identity = file_system.GetIdentity() |
+ optargs['start_empty'] = optargs.get('start_empty', not versioned) |
return object_store_creator.Create( |
CachingFileSystem, |
- category='%s/%s' % (file_system.GetIdentity(), category), |
+ category='%s/%s' % (identity, category), |
**optargs) |
- self._stat_cache = create_object_store('stat') |
+ self._stat_cache = create_object_store('stat', try_versioning=True) |
# The read caches can start populated (start_empty=False) because file |
# updates are picked up by the stat, so it doesn't need the force-refresh |
# which starting empty is designed for. Without this optimisation, cron |
@@ -56,10 +69,17 @@ class CachingFileSystem(FileSystem): |
(path, dir_path, dir_stat.child_versions)) |
return StatInfo(file_version) |
+ def raise_cache_miss(path): |
+ raise FileNotFoundError('Got cache miss when trying to stat %s' % path) |
+ |
dir_stat = self._stat_cache.Get(dir_path).Get() |
if dir_stat is not None: |
return Future(callback=lambda: make_stat_info(dir_stat)) |
+ if self._fail_on_miss: |
+ logging.warning('Bailing on stat cache miss for %s' % dir_path) |
+ return Future(callback=lambda: raise_cache_miss(dir_path)) |
+ |
def next(dir_stat): |
assert dir_stat is not None # should have raised a FileNotFoundError |
# We only ever need to cache the dir stat. |
@@ -169,14 +189,11 @@ class CachingFileSystem(FileSystem): |
return dirs, files |
return self._file_system.Walk(root, depth=depth, file_lister=file_lister) |
- def GetCommitID(self): |
- return self._file_system.GetCommitID() |
- |
- def GetPreviousCommitID(self): |
- return self._file_system.GetPreviousCommitID() |
- |
def GetIdentity(self): |
return self._file_system.GetIdentity() |
+ def GetVersion(self): |
+ return self._file_system.GetVersion() |
+ |
def __repr__(self): |
return '%s of <%s>' % (type(self).__name__, repr(self._file_system)) |