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.
5 from future
import Future
6 from object_store
import ObjectStore
8 class _GetMultiFuture(object):
9 '''A Future for GetMulti.
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
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
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.
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:
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
:
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
:
55 object_store
.SetMulti(updates
)
56 return self
._results
_so
_far
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
66 - When getting items, the behaviour depends on |start_empty|.
67 - If false, each object store is tried in order. The first object
68 store to find the item will trickle back up, setting it on all object
69 stores higher in the hierarchy.
70 - If true, only the first in-memory cache is checked, as though the store
71 had been initialized with no content as opposed to the union of its
74 def __init__(self
, object_stores
, start_empty
=False):
75 self
._object
_stores
= object_stores
76 self
._start
_empty
= start_empty
79 def SetMulti(self
, mapping
):
80 self
._cache
.update(mapping
)
81 for object_store
in self
._object
_stores
:
82 object_store
.SetMulti(mapping
)
84 def GetMulti(self
, keys
):
85 missing_keys
= list(keys
)
88 if key
in self
._cache
:
89 cached_items
[key
] = self
._cache
.get(key
)
90 missing_keys
.remove(key
)
91 if len(missing_keys
) == 0 or self
._start
_empty
:
92 return Future(value
=cached_items
)
93 object_store_futures
= [(object_store
, object_store
.GetMulti(missing_keys
))
94 for object_store
in self
._object
_stores
]
95 return Future(delegate
=_GetMultiFuture(
96 self
._cache
, object_store_futures
, cached_items
, missing_keys
))
98 def DelMulti(self
, keys
):
100 self
._cache
.pop(k
, None)
101 for object_store
in self
._object
_stores
:
102 object_store
.DelMulti(keys
)