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 posixpath | 5 import posixpath |
| 6 import sys | 6 import sys |
| 7 | 7 |
| 8 from file_system import FileSystem, StatInfo, FileNotFoundError | 8 from file_system import FileSystem, StatInfo, FileNotFoundError |
| 9 from future import Future | 9 from future import Future |
| 10 from path_util import IsDirectory, ToDirectory | 10 from path_util import AssertIsDirectory, IsDirectory, ToDirectory |
| 11 from third_party.json_schema_compiler.memoize import memoize | 11 from third_party.json_schema_compiler.memoize import memoize |
| 12 | 12 |
| 13 | 13 |
| 14 class CachingFileSystem(FileSystem): | 14 class CachingFileSystem(FileSystem): |
| 15 '''FileSystem which implements a caching layer on top of |file_system|. It's | 15 '''FileSystem which implements a caching layer on top of |file_system|. It's |
| 16 smart, using Stat() to decided whether to skip Read()ing from |file_system|, | 16 smart, using Stat() to decided whether to skip Read()ing from |file_system|, |
| 17 and only Stat()ing directories never files. | 17 and only Stat()ing directories never files. |
| 18 ''' | 18 ''' |
| 19 def __init__(self, file_system, object_store_creator): | 19 def __init__(self, file_system, object_store_creator): |
| 20 self._file_system = file_system | 20 self._file_system = file_system |
| 21 def create_object_store(category, **optargs): | 21 def create_object_store(category, **optargs): |
| 22 return object_store_creator.Create( | 22 return object_store_creator.Create( |
| 23 CachingFileSystem, | 23 CachingFileSystem, |
| 24 category='%s/%s' % (file_system.GetIdentity(), category), | 24 category='%s/%s' % (file_system.GetIdentity(), category), |
| 25 **optargs) | 25 **optargs) |
| 26 self._stat_object_store = create_object_store('stat') | 26 self._stat_object_store = create_object_store('stat') |
| 27 # The read caches can start populated (start_empty=False) because file | 27 # The read caches can start populated (start_empty=False) because file |
| 28 # updates are picked up by the stat, so it doesn't need the force-refresh | 28 # updates are picked up by the stat, so it doesn't need the force-refresh |
| 29 # which starting empty is designed for. Without this optimisation, cron | 29 # which starting empty is designed for. Without this optimisation, cron |
| 30 # runs are extra slow. | 30 # runs are extra slow. |
| 31 self._read_object_store = create_object_store('read', start_empty=False) | 31 self._read_object_store = create_object_store('read', start_empty=False) |
| 32 self._walk_cache = create_object_store('walk') | |
|
not at google - send to devlin
2014/08/29 05:00:14
Should this be start_empty=False for the same reas
ahernandez
2014/08/29 17:20:00
Probably, I wasn't sure.
| |
| 32 | 33 |
| 33 def Refresh(self): | 34 def Refresh(self): |
| 34 return self._file_system.Refresh() | 35 return self._file_system.Refresh() |
| 35 | 36 |
| 36 def StatAsync(self, path): | 37 def StatAsync(self, path): |
| 37 '''Stats the directory given, or if a file is given, stats the file's parent | 38 '''Stats the directory given, or if a file is given, stats the file's parent |
| 38 directory to get info about the file. | 39 directory to get info about the file. |
| 39 ''' | 40 ''' |
| 40 # Always stat the parent directory, since it will have the stat of the child | 41 # Always stat the parent directory, since it will have the stat of the child |
| 41 # anyway, and this gives us an entire directory's stat info at once. | 42 # anyway, and this gives us an entire directory's stat info at once. |
| (...skipping 91 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 133 # constantly trying to read a file we now know doesn't exist. | 134 # constantly trying to read a file we now know doesn't exist. |
| 134 self._read_object_store.SetMulti( | 135 self._read_object_store.SetMulti( |
| 135 dict((path, (None, None)) for path in paths | 136 dict((path, (None, None)) for path in paths |
| 136 if stat_futures[path].Get() is None)) | 137 if stat_futures[path].Get() is None)) |
| 137 new_results.update(up_to_date_data) | 138 new_results.update(up_to_date_data) |
| 138 return new_results | 139 return new_results |
| 139 # Read in the values that were uncached or old. | 140 # Read in the values that were uncached or old. |
| 140 return self._file_system.Read(set(paths) - set(up_to_date_data.iterkeys()), | 141 return self._file_system.Read(set(paths) - set(up_to_date_data.iterkeys()), |
| 141 skip_not_found=skip_not_found).Then(next) | 142 skip_not_found=skip_not_found).Then(next) |
| 142 | 143 |
| 144 def Walk(self, root, depth=-1): | |
|
not at google - send to devlin
2014/08/29 05:00:13
I'm looking at this quite late at night, so, apolo
| |
| 145 '''Overrides FileSystem.Walk() to provide caching functionality. | |
| 146 ''' | |
| 147 AssertIsDirectory(root) | |
| 148 basepath = root | |
| 149 | |
| 150 def walk(root, depth): | |
| 151 if depth == 0: | |
| 152 return | |
| 153 AssertIsDirectory(root) | |
| 154 res = self._walk_cache.Get(root).Get() | |
| 155 cache_stat = self._stat_object_store.Get(root).Get() | |
|
ahernandez
2014/08/29 02:15:11
These don't need to be Then-ified, right?
| |
| 156 root_stat = self.Stat(root) | |
| 157 | |
| 158 if res and cache_stat.version == root_stat.version: | |
| 159 dirs, files = res | |
| 160 else: | |
| 161 dirs, files = [], [] | |
| 162 for f in self.ReadSingle(root).Get(): | |
| 163 if IsDirectory(f): | |
| 164 dirs.append(f) | |
| 165 else: | |
| 166 files.append(f) | |
| 167 self._walk_cache.Set(root, (dirs, files)) | |
| 168 | |
| 169 yield root[len(basepath):].rstrip('/'), dirs, files | |
| 170 | |
| 171 for d in dirs: | |
| 172 for walkinfo in walk(root + d, depth - 1): | |
| 173 yield walkinfo | |
| 174 | |
| 175 for walkinfo in walk(root, depth): | |
| 176 yield walkinfo | |
| 177 | |
| 143 def GetIdentity(self): | 178 def GetIdentity(self): |
| 144 return self._file_system.GetIdentity() | 179 return self._file_system.GetIdentity() |
| 145 | 180 |
| 146 def __repr__(self): | 181 def __repr__(self): |
| 147 return '%s of <%s>' % (type(self).__name__, repr(self._file_system)) | 182 return '%s of <%s>' % (type(self).__name__, repr(self._file_system)) |
| OLD | NEW |