OLD | NEW |
(Empty) | |
| 1 # Copyright 2013 The Chromium Authors. All rights reserved. |
| 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 |