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 from file_system import FileSystem, StatInfo, FileNotFoundError | 5 from file_system import FileSystem, StatInfo, FileNotFoundError |
6 from future import Future | 6 from future import Future |
| 7 from object_store_creator import ObjectStoreCreator |
7 | 8 |
8 class _AsyncUncachedFuture(object): | 9 class _AsyncUncachedFuture(object): |
9 def __init__(self, | 10 def __init__(self, |
10 uncached_read_futures, | 11 uncached_read_futures, |
11 stats_for_uncached, | 12 stats_for_uncached, |
12 current_results, | 13 current_results, |
13 file_system, | 14 file_system, |
14 object_store): | 15 object_store): |
15 self._uncached_read_futures = uncached_read_futures | 16 self._uncached_read_futures = uncached_read_futures |
16 self._stats_for_uncached = stats_for_uncached | 17 self._stats_for_uncached = stats_for_uncached |
17 self._current_results = current_results | 18 self._current_results = current_results |
18 self._file_system = file_system | 19 self._file_system = file_system |
19 self._object_store = object_store | 20 self._object_store = object_store |
20 | 21 |
21 def Get(self): | 22 def Get(self): |
22 new_results = self._uncached_read_futures.Get() | 23 new_results = self._uncached_read_futures.Get() |
23 # Update the cached data in the object store. This is a path -> (read, | 24 # Update the cached data in the object store. This is a path -> (read, |
24 # version) mapping. | 25 # version) mapping. |
25 self._object_store.SetMulti(dict( | 26 self._object_store.SetMulti(dict( |
26 (path, (new_result, self._stats_for_uncached[path].version)) | 27 (path, (new_result, self._stats_for_uncached[path].version)) |
27 for path, new_result in new_results.iteritems())) | 28 for path, new_result in new_results.iteritems())) |
28 new_results.update(self._current_results) | 29 new_results.update(self._current_results) |
29 return new_results | 30 return new_results |
30 | 31 |
31 class CachingFileSystem(FileSystem): | 32 class CachingFileSystem(FileSystem): |
32 '''FileSystem which implements a caching layer on top of |file_system|. It's | 33 '''FileSystem which implements a caching layer on top of |file_system|. It's |
33 smart, using Stat() to decided whether to skip Read()ing from |file_system|, | 34 smart, using Stat() to decided whether to skip Read()ing from |file_system|, |
34 and only Stat()ing directories never files. | 35 and only Stat()ing directories never files. |
35 | |
36 Specify |use_existing_values| to continue using whatever has been cached in | |
37 the object stores. By default, the data in the stores is assumed to be stale | |
38 (althought consistent). Using existing values is useful for live instances | |
39 that don't want to touch the file system; not using them is good for the | |
40 cron jobs, where we want to refresh the data. | |
41 ''' | 36 ''' |
42 def __init__(self, | 37 def __init__(self, file_system, object_store_creator): |
43 file_system, | |
44 object_store_creator_factory, | |
45 use_existing_values=False): | |
46 self._file_system = file_system | 38 self._file_system = file_system |
47 def create_object_store(category): | 39 def create_object_store(category, **optargs): |
48 return (object_store_creator_factory.Create(CachingFileSystem) | 40 return object_store_creator.Create( |
49 .Create(category='%s/%s' % (file_system.GetName(), category), | 41 CachingFileSystem, |
50 # By Stat()ing from scratch we'll end up not using the | 42 category='%s/%s' % (file_system.GetIdentity(), category), |
51 # existing values, but also not doing unnecessary Read()s if | 43 **optargs) |
52 # the files haven't changed from last time. | |
53 start_empty=(not use_existing_values and category == 'stat'))) | |
54 self._stat_object_store = create_object_store('stat') | 44 self._stat_object_store = create_object_store('stat') |
55 self._read_object_store = create_object_store('read') | 45 # Force the read stores to start populated, even if |object_store_creator| |
56 self._read_binary_object_store = create_object_store('read-binary') | 46 # is configured to start empty, because the result from Stat entirely |
| 47 # determines how we end up (re)Read-ing data. This is a core optimisation. |
| 48 self._read_object_store = create_object_store( |
| 49 'read', start_empty=False) |
| 50 self._read_binary_object_store = create_object_store( |
| 51 'read-binary', start_empty=False) |
57 | 52 |
58 def Stat(self, path): | 53 def Stat(self, path): |
59 '''Stats the directory given, or if a file is given, stats the file's parent | 54 '''Stats the directory given, or if a file is given, stats the file's parent |
60 directory to get info about the file. | 55 directory to get info about the file. |
61 ''' | 56 ''' |
62 # Always stat the parent directory, since it will have the stat of the child | 57 # Always stat the parent directory, since it will have the stat of the child |
63 # anyway, and this gives us an entire directory's stat info at once. | 58 # anyway, and this gives us an entire directory's stat info at once. |
64 if path.endswith('/'): | 59 if path.endswith('/'): |
65 dir_path = path | 60 dir_path = path |
66 else: | 61 else: |
(...skipping 44 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
111 | 106 |
112 if not uncached: | 107 if not uncached: |
113 return Future(value=results) | 108 return Future(value=results) |
114 | 109 |
115 return Future(delegate=_AsyncUncachedFuture( | 110 return Future(delegate=_AsyncUncachedFuture( |
116 self._file_system.Read(uncached.keys(), binary=binary), | 111 self._file_system.Read(uncached.keys(), binary=binary), |
117 uncached, | 112 uncached, |
118 results, | 113 results, |
119 self, | 114 self, |
120 read_object_store)) | 115 read_object_store)) |
OLD | NEW |