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..3673096530a381373bbaaebd27b47deda8add90c 100644 |
--- a/chrome/common/extensions/docs/server2/caching_file_system.py |
+++ b/chrome/common/extensions/docs/server2/caching_file_system.py |
@@ -11,19 +11,49 @@ from path_util import AssertIsDirectory, IsDirectory, ToDirectory |
from third_party.json_schema_compiler.memoize import memoize |
+ |
+class CacheMissError(Exception): |
+ '''Raise when data is not found in a CachingFileSystem which is not allowed |
+ to reference its backing FileSystem.''' |
+ def __init__(self, message): |
+ Exception.__init__(self, message) |
+ |
+ |
+ |
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 CacheMissError rather than |
+ falling back onto the underlying FileSystem. |
+ |
+ If |empty_stat_cache| is True (default), its stat cache is initialized empty. |
+ This should be set to False when wrapping a FileSystem that makes a proper |
+ distinction between stable identity and unstable identity. |
+ |
+ The working assumption is that a file system's unstable identity changes any |
+ time any contents of the file system change, and therefore a stat cache keyed |
+ on unstable identity will never need to be refreshed. |
''' |
- def __init__(self, file_system, object_store_creator): |
+ def __init__(self, |
+ file_system, |
+ object_store_creator, |
+ fail_on_miss=False, |
+ empty_stat_cache=True): |
self._file_system = file_system |
- def create_object_store(category, **optargs): |
+ self._fail_on_miss = fail_on_miss |
+ def create_object_store(category, use_stable_identity=True, **optargs): |
+ if use_stable_identity: |
+ identity = file_system.GetStableIdentity() |
+ else: |
+ identity = file_system.GetUnstableIdentity() |
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') |
+ # The stable stat cache caches file stat info keyed by the file system's |
+ # stable identity (or unstable identity for persistent caches). |
+ self._stat_cache = create_object_store('stat', |
+ use_stable_identity=empty_stat_cache, |
+ start_empty=empty_stat_cache) |
# 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 +86,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.info('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. |
@@ -84,7 +121,7 @@ class CachingFileSystem(FileSystem): |
# (path, None, None). This is to prevent re-reads of files we know |
# do not exist. |
cached_read_values = self._read_cache.GetMulti(paths).Get() |
- cached_stat_values = self._stat_cache.GetMulti(paths).Get() |
+ cached_stat_info = self._stat_cache.GetMulti(paths).Get() |
# Populate a map of paths to Futures to their stat. They may have already |
# been cached in which case their Future will already have been constructed |
@@ -97,13 +134,13 @@ class CachingFileSystem(FileSystem): |
raise error |
for path in paths: |
- stat_value = cached_stat_values.get(path) |
- if stat_value is None: |
+ stat_info = cached_stat_info.get(path) |
+ if stat_info is None: |
stat_future = self.StatAsync(path) |
if skip_not_found: |
stat_future = stat_future.Then(lambda x: x, handle) |
else: |
- stat_future = Future(value=stat_value) |
+ stat_future = Future(value=stat_info) |
stat_futures[path] = stat_future |
# Filter only the cached data which is up to date by comparing to the latest |
@@ -169,12 +206,6 @@ 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() |