]>
Commit | Line | Data |
---|---|---|
7c673cae FG |
1 | // -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*- |
2 | // vim: ts=8 sw=2 smarttab | |
3 | /* | |
4 | * Ceph - scalable distributed file system | |
5 | * | |
6 | * Copyright (C) 2013 Inktank Storage, Inc. | |
7 | * | |
8 | * This is free software; you can redistribute it and/or | |
9 | * modify it under the terms of the GNU Lesser General Public | |
10 | * License version 2.1, as published by the Free Software | |
11 | * Foundation. See file COPYING. | |
12 | * | |
13 | */ | |
14 | ||
15 | #ifndef MAPCACHER_H | |
16 | #define MAPCACHER_H | |
17 | ||
7c673cae FG |
18 | #include "common/sharedptr_registry.hpp" |
19 | ||
20 | namespace MapCacher { | |
21 | /** | |
22 | * Abstraction for ordering key updates | |
23 | */ | |
24 | template<typename K, typename V> | |
25 | class Transaction { | |
26 | public: | |
27 | /// Set keys according to map | |
28 | virtual void set_keys( | |
29 | const std::map<K, V> &keys ///< [in] keys/values to set | |
30 | ) = 0; | |
31 | ||
32 | /// Remove keys | |
33 | virtual void remove_keys( | |
34 | const std::set<K> &to_remove ///< [in] keys to remove | |
35 | ) = 0; | |
36 | ||
37 | /// Add context to fire when data is readable | |
38 | virtual void add_callback( | |
39 | Context *c ///< [in] Context to fire on readable | |
40 | ) = 0; | |
41 | virtual ~Transaction() {} | |
42 | }; | |
43 | ||
44 | /** | |
45 | * Abstraction for fetching keys | |
46 | */ | |
47 | template<typename K, typename V> | |
48 | class StoreDriver { | |
49 | public: | |
50 | /// Returns requested key values | |
51 | virtual int get_keys( | |
52 | const std::set<K> &keys, ///< [in] keys requested | |
53 | std::map<K, V> *got ///< [out] values for keys obtained | |
54 | ) = 0; ///< @return error value | |
55 | ||
56 | /// Returns next key | |
57 | virtual int get_next( | |
58 | const K &key, ///< [in] key after which to get next | |
59 | pair<K, V> *next ///< [out] first key after key | |
60 | ) = 0; ///< @return 0 on success, -ENOENT if there is no next | |
61 | ||
62 | virtual ~StoreDriver() {} | |
63 | }; | |
64 | ||
65 | /** | |
66 | * Uses SharedPtrRegistry to cache objects of in progress writes | |
67 | * allowing the user to read/write a consistent view of the map | |
68 | * without flushing writes. | |
69 | */ | |
70 | template<typename K, typename V> | |
71 | class MapCacher { | |
72 | private: | |
73 | StoreDriver<K, V> *driver; | |
74 | ||
75 | SharedPtrRegistry<K, boost::optional<V> > in_progress; | |
76 | typedef typename SharedPtrRegistry<K, boost::optional<V> >::VPtr VPtr; | |
77 | typedef ContainerContext<set<VPtr> > TransHolder; | |
78 | ||
79 | public: | |
80 | MapCacher(StoreDriver<K, V> *driver) : driver(driver) {} | |
81 | ||
82 | /// Fetch first key/value pair after specified key | |
83 | int get_next( | |
84 | K key, ///< [in] key after which to get next | |
85 | pair<K, V> *next ///< [out] next key | |
86 | ) { | |
87 | while (true) { | |
88 | pair<K, boost::optional<V> > cached; | |
89 | pair<K, V> store; | |
90 | bool got_cached = in_progress.get_next(key, &cached); | |
91 | ||
92 | bool got_store = false; | |
93 | int r = driver->get_next(key, &store); | |
94 | if (r < 0 && r != -ENOENT) { | |
95 | return r; | |
96 | } else if (r == 0) { | |
97 | got_store = true; | |
98 | } | |
99 | ||
100 | if (!got_cached && !got_store) { | |
101 | return -ENOENT; | |
102 | } else if ( | |
103 | got_cached && | |
104 | (!got_store || store.first >= cached.first)) { | |
105 | if (cached.second) { | |
106 | if (next) | |
107 | *next = make_pair(cached.first, cached.second.get()); | |
108 | return 0; | |
109 | } else { | |
110 | key = cached.first; | |
111 | continue; // value was cached as removed, recurse | |
112 | } | |
113 | } else { | |
114 | if (next) | |
115 | *next = store; | |
116 | return 0; | |
117 | } | |
118 | } | |
119 | ceph_abort(); // not reachable | |
120 | return -EINVAL; | |
121 | } ///< @return error value, 0 on success, -ENOENT if no more entries | |
122 | ||
123 | /// Adds operation setting keys to Transaction | |
124 | void set_keys( | |
125 | const map<K, V> &keys, ///< [in] keys/values to set | |
126 | Transaction<K, V> *t ///< [out] transaction to use | |
127 | ) { | |
128 | std::set<VPtr> vptrs; | |
129 | for (typename map<K, V>::const_iterator i = keys.begin(); | |
130 | i != keys.end(); | |
131 | ++i) { | |
132 | VPtr ip = in_progress.lookup_or_create(i->first, i->second); | |
133 | *ip = i->second; | |
134 | vptrs.insert(ip); | |
135 | } | |
136 | t->set_keys(keys); | |
137 | t->add_callback(new TransHolder(vptrs)); | |
138 | } | |
139 | ||
140 | /// Adds operation removing keys to Transaction | |
141 | void remove_keys( | |
142 | const set<K> &keys, ///< [in] | |
143 | Transaction<K, V> *t ///< [out] transaction to use | |
144 | ) { | |
145 | std::set<VPtr> vptrs; | |
146 | for (typename set<K>::const_iterator i = keys.begin(); | |
147 | i != keys.end(); | |
148 | ++i) { | |
149 | boost::optional<V> empty; | |
150 | VPtr ip = in_progress.lookup_or_create(*i, empty); | |
151 | *ip = empty; | |
152 | vptrs.insert(ip); | |
153 | } | |
154 | t->remove_keys(keys); | |
155 | t->add_callback(new TransHolder(vptrs)); | |
156 | } | |
157 | ||
158 | /// Gets keys, uses cached values for unstable keys | |
159 | int get_keys( | |
160 | const set<K> &keys_to_get, ///< [in] set of keys to fetch | |
161 | map<K, V> *got ///< [out] keys gotten | |
162 | ) { | |
163 | set<K> to_get; | |
164 | map<K, V> _got; | |
165 | for (typename set<K>::const_iterator i = keys_to_get.begin(); | |
166 | i != keys_to_get.end(); | |
167 | ++i) { | |
168 | VPtr val = in_progress.lookup(*i); | |
169 | if (val) { | |
170 | if (*val) | |
171 | got->insert(make_pair(*i, val->get())); | |
172 | //else: value cached is empty, key doesn't exist | |
173 | } else { | |
174 | to_get.insert(*i); | |
175 | } | |
176 | } | |
177 | int r = driver->get_keys(to_get, &_got); | |
178 | if (r < 0) | |
179 | return r; | |
180 | for (typename map<K, V>::iterator i = _got.begin(); | |
181 | i != _got.end(); | |
182 | ++i) { | |
183 | got->insert(*i); | |
184 | } | |
185 | return 0; | |
186 | } ///< @return error value, 0 on success | |
187 | }; | |
188 | } // namespace | |
189 | ||
190 | #endif |