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