Skip to content

Commit d91e3df

Browse files
metalicjamesHalosGhost
authored andcommitted
Use snapshottable map for auditing in 2PC shard
Signed-off-by: James Lovejoy <[email protected]>
1 parent f030eb3 commit d91e3df

File tree

5 files changed

+196
-39
lines changed

5 files changed

+196
-39
lines changed

src/uhs/twophase/locking_shard/controller.cpp

+3
Original file line numberDiff line numberDiff line change
@@ -169,6 +169,7 @@ namespace cbdc::locking_shard {
169169
|| audit_epoch <= m_last_audit_epoch) {
170170
continue;
171171
}
172+
m_logger->info("Starting audit for", audit_epoch);
172173

173174
auto maybe_total = m_shard->audit(audit_epoch);
174175
if(!maybe_total.has_value()) {
@@ -186,6 +187,8 @@ namespace cbdc::locking_shard {
186187
m_last_audit_epoch = audit_epoch;
187188

188189
m_shard->prune(audit_epoch);
190+
191+
m_logger->info("Prune completed for", audit_epoch);
189192
}
190193
}
191194
}

src/uhs/twophase/locking_shard/locking_shard.cpp

+32-35
Original file line numberDiff line numberDiff line change
@@ -32,18 +32,13 @@ namespace cbdc::locking_shard {
3232
m_logger(std::move(logger)),
3333
m_completed_txs(completed_txs_cache_size),
3434
m_opts(std::move(opts)) {
35-
m_uhs.max_load_factor(std::numeric_limits<float>::max());
3635
m_applied_dtxs.max_load_factor(std::numeric_limits<float>::max());
3736
m_prepared_dtxs.max_load_factor(std::numeric_limits<float>::max());
38-
m_locked.max_load_factor(std::numeric_limits<float>::max());
3937

4038
static constexpr auto dtx_buckets = 100000;
4139
m_applied_dtxs.rehash(dtx_buckets);
4240
m_prepared_dtxs.rehash(dtx_buckets);
4341

44-
static constexpr auto locked_buckets = 10000000;
45-
m_locked.rehash(locked_buckets);
46-
4742
if(!preseed_file.empty()) {
4843
m_logger->info("Reading preseed file into memory");
4944
if(!read_preseed_file(preseed_file)) {
@@ -65,12 +60,9 @@ namespace cbdc::locking_shard {
6560
}
6661
in.seekg(0, std::ios::beg);
6762
auto deser = istream_serializer(in);
68-
m_uhs.clear();
69-
static constexpr auto uhs_size_factor = 2;
70-
auto bucket_count = static_cast<unsigned long>(sz / cbdc::hash_size
71-
* uhs_size_factor);
72-
m_uhs.rehash(bucket_count);
73-
deser >> m_uhs;
63+
auto uhs = std::map<hash_t, uhs_element>();
64+
deser >> uhs;
65+
m_uhs = std::move(uhs);
7466
return true;
7567
}
7668
return false;
@@ -215,22 +207,17 @@ namespace cbdc::locking_shard {
215207
}
216208
}
217209

218-
auto locking_shard::audit(uint64_t epoch) const
219-
-> std::optional<uint64_t> {
220-
auto uhs = decltype(m_uhs)();
221-
auto locked = decltype(m_locked)();
222-
auto spent = decltype(m_spent)();
210+
auto locking_shard::audit(uint64_t epoch) -> std::optional<uint64_t> {
223211
{
224-
std::shared_lock l(m_mut);
225-
uhs = m_uhs;
226-
locked = m_locked;
227-
spent = m_spent;
212+
std::unique_lock l(m_mut);
213+
m_uhs.snapshot();
214+
m_locked.snapshot();
215+
m_spent.snapshot();
228216
}
229217

230218
auto check_uhs
231-
= [epoch](
232-
const std::unordered_map<hash_t, uhs_element, hashing::null>&
233-
uhs_map) -> std::optional<uint64_t> {
219+
= [epoch](const snapshot_map<hash_t, uhs_element>& uhs_map)
220+
-> std::optional<uint64_t> {
234221
uint64_t tot{};
235222
for(const auto& [id, elem] : uhs_map) {
236223
if(elem.m_creation_epoch <= epoch
@@ -247,24 +234,34 @@ namespace cbdc::locking_shard {
247234
return tot;
248235
};
249236

237+
// TODO: use bignum because tot will probably overflow
250238
uint64_t tot{};
251-
auto uhs_tot = check_uhs(uhs);
252-
if(!uhs_tot.has_value()) {
253-
return std::nullopt;
239+
auto uhs_tot = check_uhs(m_uhs);
240+
if(uhs_tot.has_value()) {
241+
tot += uhs_tot.value();
254242
}
255-
tot += uhs_tot.value();
256243

257-
auto locked_tot = check_uhs(locked);
258-
if(!locked_tot.has_value()) {
259-
return std::nullopt;
244+
auto locked_tot = check_uhs(m_locked);
245+
if(locked_tot.has_value()) {
246+
tot += locked_tot.value();
247+
}
248+
249+
auto spent_tot = check_uhs(m_spent);
250+
if(spent_tot.has_value()) {
251+
tot += spent_tot.value();
252+
}
253+
254+
{
255+
std::unique_lock l(m_mut);
256+
m_uhs.release_snapshot();
257+
m_locked.release_snapshot();
258+
m_spent.release_snapshot();
260259
}
261-
tot += locked_tot.value();
262260

263-
auto spent_tot = check_uhs(spent);
264-
if(!spent_tot.has_value()) {
261+
if(!spent_tot.has_value() || !locked_tot.has_value()
262+
|| !uhs_tot.has_value()) {
265263
return std::nullopt;
266264
}
267-
tot += spent_tot.value();
268265

269266
return tot;
270267
}
@@ -277,7 +274,7 @@ namespace cbdc::locking_shard {
277274
void locking_shard::prune(uint64_t epoch) {
278275
std::unique_lock l(m_mut);
279276
for(auto it = m_spent.begin(); it != m_spent.end();) {
280-
auto& elem = it->second;
277+
const auto& elem = it->second;
281278
if(elem.m_deletion_epoch.value() < epoch) {
282279
it = m_spent.erase(it);
283280
} else {

src/uhs/twophase/locking_shard/locking_shard.hpp

+5-4
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
#include "util/common/hash.hpp"
1515
#include "util/common/hashmap.hpp"
1616
#include "util/common/logging.hpp"
17+
#include "util/common/snapshot_map.hpp"
1718

1819
#include <filesystem>
1920
#include <future>
@@ -143,7 +144,7 @@ namespace cbdc::locking_shard {
143144
/// \param epoch the epoch to audit the supply at.
144145
/// \return total value of coins in this shard's UHS, or std::nullopt
145146
/// if any of the UHS elements do not match their UHS ID.
146-
auto audit(uint64_t epoch) const -> std::optional<uint64_t>;
147+
auto audit(uint64_t epoch) -> std::optional<uint64_t>;
147148

148149
/// Prunes any spent UHS elements spent prior to the given epoch.
149150
/// \param epoch epoch to prune prior to.
@@ -167,9 +168,9 @@ namespace cbdc::locking_shard {
167168

168169
std::shared_ptr<logging::log> m_logger;
169170
mutable std::shared_mutex m_mut;
170-
std::unordered_map<hash_t, uhs_element, hashing::null> m_uhs;
171-
std::unordered_map<hash_t, uhs_element, hashing::null> m_locked;
172-
std::unordered_map<hash_t, uhs_element, hashing::null> m_spent;
171+
snapshot_map<hash_t, uhs_element> m_uhs;
172+
snapshot_map<hash_t, uhs_element> m_locked;
173+
snapshot_map<hash_t, uhs_element> m_spent;
173174
std::unordered_map<hash_t, prepared_dtx, hashing::null>
174175
m_prepared_dtxs;
175176
std::unordered_set<hash_t, hashing::null> m_applied_dtxs;

src/util/common/snapshot_map.hpp

+130
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,130 @@
1+
// Copyright (c) 2022 MIT Digital Currency Initiative,
2+
// Federal Reserve Bank of Boston
3+
// Distributed under the MIT software license, see the accompanying
4+
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
5+
6+
#ifndef CBDC_UNIVERSE0_SRC_COMMON_SNAPSHOT_MAP_H_
7+
#define CBDC_UNIVERSE0_SRC_COMMON_SNAPSHOT_MAP_H_
8+
9+
#include <cassert>
10+
#include <map>
11+
#include <set>
12+
13+
namespace cbdc {
14+
template<typename K, typename V, typename... Ts>
15+
class snapshot_map {
16+
public:
17+
using map_type = std::map<K, V, Ts...>;
18+
using set_type = std::set<K, Ts...>;
19+
20+
auto operator=(map_type&& m) -> snapshot_map<K, V, Ts...>& {
21+
m_map = std::move(m);
22+
return *this;
23+
}
24+
25+
void release_snapshot() {
26+
m_snapshot = false;
27+
}
28+
29+
void snapshot() {
30+
m_snapshot = true;
31+
}
32+
33+
[[nodiscard]] auto find(const K& key) const ->
34+
typename map_type::const_iterator {
35+
auto added_it = m_added.find(key);
36+
if(added_it != m_added.end()) {
37+
return added_it;
38+
}
39+
40+
auto removed_it = m_removed.find(key);
41+
if(removed_it != m_removed.end()) {
42+
return m_map.end();
43+
}
44+
45+
return m_map.find(key);
46+
}
47+
48+
[[nodiscard]] auto size() const -> size_t {
49+
return m_added.size() + m_removed.size() + m_map.size();
50+
}
51+
52+
[[nodiscard]] auto end() const -> typename map_type::const_iterator {
53+
return m_map.end();
54+
}
55+
56+
[[nodiscard]] auto begin() const -> typename map_type::const_iterator {
57+
return m_map.begin();
58+
}
59+
60+
template<class... Args>
61+
auto emplace(Args&&... args)
62+
-> std::pair<typename map_type::iterator, bool> {
63+
gc();
64+
auto ret = [&]() {
65+
if(m_snapshot) {
66+
return m_added.emplace(std::forward<Args>(args)...);
67+
}
68+
auto it = m_map.emplace(std::forward<Args>(args)...);
69+
m_added.erase(it.first->first);
70+
return it;
71+
}();
72+
m_removed.erase(ret.first->first);
73+
return ret;
74+
}
75+
76+
auto erase(typename map_type::const_iterator it) ->
77+
typename map_type::iterator {
78+
assert(!m_snapshot);
79+
m_added.erase(it->first);
80+
m_removed.erase(it->first);
81+
return m_map.erase(it);
82+
}
83+
84+
void erase(const K& key) {
85+
gc();
86+
m_added.erase(key);
87+
if(m_snapshot) {
88+
m_removed.emplace(key);
89+
} else {
90+
m_removed.erase(key);
91+
m_map.erase(key);
92+
}
93+
}
94+
95+
private:
96+
map_type m_map;
97+
map_type m_added;
98+
set_type m_removed;
99+
bool m_snapshot{false};
100+
101+
void gc() {
102+
if(m_snapshot) {
103+
return;
104+
}
105+
constexpr size_t factor = 1000000;
106+
constexpr size_t one = 1;
107+
auto added_elems = std::min(std::max(m_added.size() / factor, one),
108+
m_added.size());
109+
size_t count = 0;
110+
for(auto it = m_added.begin();
111+
it != m_added.end() && count < added_elems;
112+
count++) {
113+
auto n = m_added.extract(it++);
114+
m_map.insert(std::move(n));
115+
}
116+
count = 0;
117+
auto removed_elems
118+
= std::min(std::max(m_removed.size() / factor, one),
119+
m_removed.size());
120+
for(auto it = m_removed.begin();
121+
it != m_removed.end() && count < removed_elems;
122+
count++) {
123+
auto n = m_removed.extract(it++);
124+
m_map.erase(n.value());
125+
}
126+
}
127+
};
128+
}
129+
130+
#endif

src/util/serialization/format.hpp

+26
Original file line numberDiff line numberDiff line change
@@ -284,6 +284,32 @@ namespace cbdc {
284284
return deser;
285285
}
286286

287+
/// Deserializes a map of key-value pairs.
288+
template<typename K, typename V, typename... Ts>
289+
auto operator>>(serializer& deser, std::map<K, V, Ts...>& map)
290+
-> serializer& {
291+
auto len = uint64_t();
292+
if(!(deser >> len)) {
293+
return deser;
294+
}
295+
296+
for(uint64_t i = 0; i < len; i++) {
297+
auto key = K();
298+
if(!(deser >> key)) {
299+
return deser;
300+
}
301+
302+
auto val = V();
303+
if(!(deser >> val)) {
304+
return deser;
305+
}
306+
307+
map.emplace(std::move(key), std::move(val));
308+
}
309+
310+
return deser;
311+
}
312+
287313
/// Serializes the count of items, and then each item statically-casted.
288314
/// \see \ref cbdc::operator<<(serializer&, T)
289315
template<typename K, typename... Ts>

0 commit comments

Comments
 (0)