]>
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: | |
f67539c2 | 28 | /// Std::set keys according to map |
7c673cae | 29 | virtual void set_keys( |
f67539c2 | 30 | const std::map<K, V> &keys ///< [in] keys/values to std::set |
7c673cae FG |
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 | |
f67539c2 | 60 | std::pair<K, V> *next ///< [out] first key after key |
7c673cae FG |
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; | |
f67539c2 | 78 | typedef ContainerContext<std::set<VPtr> > TransHolder; |
7c673cae FG |
79 | |
80 | public: | |
81 | MapCacher(StoreDriver<K, V> *driver) : driver(driver) {} | |
82 | ||
f67539c2 | 83 | /// Fetch first key/value std::pair after specified key |
7c673cae FG |
84 | int get_next( |
85 | K key, ///< [in] key after which to get next | |
f67539c2 | 86 | std::pair<K, V> *next ///< [out] next key |
7c673cae FG |
87 | ) { |
88 | while (true) { | |
f67539c2 TL |
89 | std::pair<K, boost::optional<V> > cached; |
90 | std::pair<K, V> store; | |
7c673cae FG |
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( | |
f67539c2 | 126 | const std::map<K, V> &keys, ///< [in] keys/values to std::set |
7c673cae FG |
127 | Transaction<K, V> *t ///< [out] transaction to use |
128 | ) { | |
129 | std::set<VPtr> vptrs; | |
f67539c2 | 130 | for (auto i = keys.begin(); i != keys.end(); ++i) { |
7c673cae FG |
131 | VPtr ip = in_progress.lookup_or_create(i->first, i->second); |
132 | *ip = i->second; | |
133 | vptrs.insert(ip); | |
134 | } | |
135 | t->set_keys(keys); | |
136 | t->add_callback(new TransHolder(vptrs)); | |
137 | } | |
138 | ||
139 | /// Adds operation removing keys to Transaction | |
140 | void remove_keys( | |
f67539c2 | 141 | const std::set<K> &keys, ///< [in] |
7c673cae FG |
142 | Transaction<K, V> *t ///< [out] transaction to use |
143 | ) { | |
144 | std::set<VPtr> vptrs; | |
f67539c2 | 145 | for (auto i = keys.begin(); i != keys.end(); ++i) { |
7c673cae FG |
146 | boost::optional<V> empty; |
147 | VPtr ip = in_progress.lookup_or_create(*i, empty); | |
148 | *ip = empty; | |
149 | vptrs.insert(ip); | |
150 | } | |
151 | t->remove_keys(keys); | |
152 | t->add_callback(new TransHolder(vptrs)); | |
153 | } | |
154 | ||
155 | /// Gets keys, uses cached values for unstable keys | |
156 | int get_keys( | |
f67539c2 TL |
157 | const std::set<K> &keys_to_get, ///< [in] std::set of keys to fetch |
158 | std::map<K, V> *got ///< [out] keys gotten | |
7c673cae | 159 | ) { |
f67539c2 TL |
160 | std::set<K> to_get; |
161 | std::map<K, V> _got; | |
162 | for (auto i = keys_to_get.begin(); | |
7c673cae FG |
163 | i != keys_to_get.end(); |
164 | ++i) { | |
165 | VPtr val = in_progress.lookup(*i); | |
166 | if (val) { | |
167 | if (*val) | |
168 | got->insert(make_pair(*i, val->get())); | |
169 | //else: value cached is empty, key doesn't exist | |
170 | } else { | |
171 | to_get.insert(*i); | |
172 | } | |
173 | } | |
174 | int r = driver->get_keys(to_get, &_got); | |
175 | if (r < 0) | |
176 | return r; | |
f67539c2 | 177 | for (auto i = _got.begin(); i != _got.end(); ++i) { |
7c673cae FG |
178 | got->insert(*i); |
179 | } | |
180 | return 0; | |
181 | } ///< @return error value, 0 on success | |
182 | }; | |
183 | } // namespace | |
184 | ||
185 | #endif |