Chromium Code Reviews| OLD | NEW |
|---|---|
| (Empty) | |
| 1 # Copyright 2013 The Chromium Authors. All rights reserved. | |
|
cduvall
2013/04/17 23:30:37
I like this class
not at google - send to devlin
2013/04/18 04:05:41
:)
| |
| 2 # Use of this source code is governed by a BSD-style license that can be | |
| 3 # found in the LICENSE file. | |
| 4 | |
| 5 from future import Future | |
| 6 from object_store import ObjectStore | |
| 7 | |
| 8 class _GetMultiFuture(object): | |
| 9 '''A Future for GetMulti. | |
| 10 | |
| 11 Params: | |
| 12 - |toplevel_cache| CacheChainObjectStore's cache. | |
| 13 - |object_store_futures| a list of (object store, future) pairs, where future | |
| 14 is the result of calling GetMulti on the missing keys for the object store. | |
| 15 - |cached_items| a mapping of cache items already in memory. | |
| 16 - |missing_keys| the keys that were missing from the GetMulti call | |
| 17 ''' | |
| 18 def __init__(self, | |
| 19 toplevel_cache, | |
| 20 object_store_futures, | |
| 21 cached_items, | |
| 22 missing_keys): | |
| 23 self._toplevel_cache = toplevel_cache | |
| 24 self._object_store_futures = object_store_futures | |
| 25 self._results_so_far = cached_items | |
| 26 self._missing_keys = missing_keys | |
| 27 | |
| 28 def Get(self): | |
| 29 # Approach: | |
| 30 # | |
| 31 # Try each object store in order, until there are no more missing keys. | |
| 32 # Don't realise the Future value of an object store that we don't need to; | |
| 33 # this is important e.g. to avoid querying data store constantly. | |
| 34 # | |
| 35 # When a value is found, cache it in all object stores further up the | |
| 36 # chain, including the object-based cache on CacheChainObjectStore. | |
| 37 object_store_updates = [] | |
| 38 for object_store, object_store_future in self._object_store_futures: | |
| 39 if len(self._missing_keys) == 0: | |
| 40 break | |
| 41 result = object_store_future.Get() | |
| 42 for k, v in result.items(): # use items(); changes during iteration | |
| 43 if v is None or k not in self._missing_keys: | |
| 44 del result[k] | |
| 45 continue | |
| 46 self._toplevel_cache[k] = v | |
| 47 self._results_so_far[k] = v | |
| 48 self._missing_keys.remove(k) | |
| 49 for _, updates in object_store_updates: | |
| 50 updates.update(result) | |
| 51 object_store_updates.append((object_store, {})) | |
| 52 # Update the caches of all object stores that need it. | |
| 53 for object_store, updates in object_store_updates: | |
| 54 if updates: | |
| 55 object_store.SetMulti(updates) | |
| 56 return self._results_so_far | |
| 57 | |
| 58 class CacheChainObjectStore(ObjectStore): | |
| 59 '''Maintains an in-memory cache along with a chain of other object stores to | |
| 60 try for the same keys. This is useful for implementing a multi-layered cache. | |
| 61 The in-memory cache is inbuilt since it's synchronous, but the object store | |
| 62 interface is asynchronous. | |
| 63 The rules for the object store chain are: | |
| 64 - When setting (or deleting) items, all object stores in the hierarcy will | |
| 65 have that item set. | |
| 66 - When getting items, each object store is tried in order. The first object | |
| 67 store to find the item will trickle back up, setting it on all object | |
| 68 stores higher in the hierarchy. | |
| 69 ''' | |
| 70 def __init__(self, object_stores): | |
| 71 self._object_stores = object_stores | |
| 72 self._cache = {} | |
| 73 | |
| 74 def SetMulti(self, mapping): | |
| 75 self._cache.update(mapping) | |
| 76 for object_store in self._object_stores: | |
| 77 object_store.SetMulti(mapping) | |
| 78 | |
| 79 def GetMulti(self, keys): | |
| 80 missing_keys = list(keys) | |
| 81 cached_items = {} | |
| 82 for key in keys: | |
| 83 if key in self._cache: | |
| 84 cached_items[key] = self._cache.get(key) | |
| 85 missing_keys.remove(key) | |
| 86 if len(missing_keys) == 0: | |
| 87 return Future(value=cached_items) | |
| 88 object_store_futures = [(object_store, object_store.GetMulti(missing_keys)) | |
| 89 for object_store in self._object_stores] | |
| 90 return Future(delegate=_GetMultiFuture( | |
| 91 self._cache, object_store_futures, cached_items, missing_keys)) | |
| 92 | |
| 93 def DelMulti(self, keys): | |
| 94 for k in keys: | |
| 95 self._cache.pop(k, None) | |
| 96 for object_store in self._object_stores: | |
| 97 object_store.DelMulti(keys) | |
| OLD | NEW |