Barretenberg
The ZK-SNARK library at the core of Aztec
Loading...
Searching...
No Matches
lmdb_store_wrapper.cpp
Go to the documentation of this file.
5#include "napi.h"
6#include <chrono>
7#include <cstdint>
8#include <memory>
9#include <optional>
10#include <ratio>
11#include <stdexcept>
12#include <utility>
13
14using namespace bb::nodejs;
15using namespace bb::nodejs::lmdb_store;
16
17const uint64_t DEFAULT_MAP_SIZE = 1024UL * 1024;
18const uint64_t DEFAULT_MAX_READERS = 16;
19const uint64_t DEFAULT_CURSOR_PAGE_SIZE = 10;
20
21LMDBStoreWrapper::LMDBStoreWrapper(const Napi::CallbackInfo& info)
22 : ObjectWrap(info)
23{
24 Napi::Env env = info.Env();
25
26 size_t data_dir_index = 0;
27 std::string data_dir;
28 if (info.Length() > data_dir_index && info[data_dir_index].IsString()) {
29 data_dir = info[data_dir_index].As<Napi::String>();
30 } else {
31 throw Napi::TypeError::New(env, "Directory needs to be a string");
32 }
33
34 size_t map_size_index = 1;
35 uint64_t map_size = DEFAULT_MAP_SIZE;
36 if (info.Length() > map_size_index) {
38 // Int64Value is the widest integer accessor in N-API (no Uint64Value exists)
39 int64_t val = info[map_size_index].As<Napi::Number>().Int64Value();
40 if (val <= 0) {
41 throw Napi::TypeError::New(env, "Map size must be a positive number");
42 }
43 map_size = static_cast<uint64_t>(val);
44 } else {
45 throw Napi::TypeError::New(env, "Map size must be a number or an object");
46 }
47 }
48
49 size_t max_readers_index = 2;
51 if (info.Length() > max_readers_index) {
53 max_readers = info[max_readers_index].As<Napi::Number>().Uint32Value();
54 } else if (!info[max_readers_index].IsUndefined()) {
55 throw Napi::TypeError::New(env, "The number of readers must be a number");
56 }
57 }
58
60
62
65
71
73
75
76 // The close operation requires exclusive execution, no other operations can be run concurrently with it
78
80}
81
82Napi::Value LMDBStoreWrapper::call(const Napi::CallbackInfo& info)
83{
85}
86
87Napi::Function LMDBStoreWrapper::get_class(Napi::Env env)
88{
89 return DefineClass(env,
90 "Store",
91 {
93 });
94}
95
96// Simply verify that the store is still valid and that close has not been called
98{
99 if (_store) {
100 return;
101 }
102 throw std::runtime_error(format("LMDB store unavailable, was close already called?"));
103}
104
106{
107 verify_store();
108 _store->open_database(req.db, !req.uniqueKeys.value_or(true));
109 return { true };
110}
111
113{
114 verify_store();
116 lmdblib::KeysVector keys = req.keys;
117 _store->get(keys, vals, req.db);
118 return { vals };
119}
120
122{
123 verify_store();
124 std::vector<bool> exists;
125 _store->has(req.entries, exists, req.db);
126 return { exists };
127}
128
130{
131 verify_store();
132 bool reverse = req.reverse.value_or(false);
134 bool one_page = req.onePage.value_or(false);
135 lmdblib::Key key = req.key;
136
137 auto tx = _store->create_shared_read_transaction();
138 lmdblib::LMDBCursor::SharedPtr cursor = _store->create_cursor(tx, req.db);
139 bool start_ok = cursor->set_at_key(key);
140
141 if (!start_ok) {
142 // we couldn't find exactly the requested key. Find the next biggest one.
143 start_ok = cursor->set_at_key_gte(key);
144 // if we found a key that's greater _and_ we want to go in reverse order
145 // then we're actually outside the requested bounds, we need to go back one position
146 if (start_ok && reverse) {
148 // read_prev returns `true` if there's nothing more to read
149 // turn this into a "not ok" because there's nothing in the db for this cursor to read
150 start_ok = !cursor->read_prev(1, entries);
151 } else if (!start_ok && reverse) {
152 // we couldn't find a key greater than our starting point _and_ we want to go in reverse..
153 // then we start at the end of the database (the client requested to start at a key greater than anything in
154 // the DB)
155 start_ok = cursor->set_at_end();
156 }
157
158 // in case we're iterating in ascending order and we can't find the exact key or one that's greater than it
159 // then that means theren's nothing in the DB for the cursor to read
160 }
161
162 // we couldn't find a starting position
163 if (!start_ok) {
164 return { std::nullopt, {} };
165 }
166
167 auto [done, first_page] = _advance_cursor(*cursor, reverse, page_size);
168 // cursor finished after reading a single page or client only wanted the first page
169 if (done || one_page) {
170 return { std::nullopt, first_page };
171 }
172
173 auto cursor_id = cursor->id();
174 {
176 _cursors[cursor_id] = { cursor, reverse };
177 }
178
179 return { cursor_id, first_page };
180}
181
183{
184 {
186 _cursors.erase(req.cursor);
187 }
188 return { true };
189}
190
192{
194
195 {
197 data = _cursors.at(req.cursor);
198 }
199
201 auto [done, entries] = _advance_cursor(*data.cursor, data.reverse, page_size);
202 return { entries, done };
203}
204
206{
208
209 {
211 data = _cursors.at(req.cursor);
212 }
213
214 auto [done, count] = _advance_cursor_count(*data.cursor, data.reverse, req.endKey);
215 return { count, done };
216}
217
219{
220 verify_store();
222 batches.reserve(req.batches.size());
223
224 for (const auto& data : req.batches) {
225 lmdblib::LMDBStore::PutData batch{ data.second.addEntries, data.second.removeEntries, data.first };
226 batches.push_back(batch);
227 }
228
229 auto start = std::chrono::high_resolution_clock::now();
230 _store->put(batches);
231 auto end = std::chrono::high_resolution_clock::now();
232 std::chrono::duration<uint64_t, std::nano> duration_ns = end - start;
233
234 return { duration_ns.count() };
235}
236
238{
239 verify_store();
241 auto [map_size, physical_file_size] = _store->get_stats(stats);
242 return { stats, map_size, physical_file_size };
243}
244
246{
247 // prevent this store from receiving further messages
249
250 {
251 // close all of the open read cursors
252 std::lock_guard cursors(_cursor_mutex);
253 _cursors.clear();
254 }
255
256 // and finally close the database handle
257 _store.reset(nullptr);
258
259 return { true };
260}
261
263{
264 verify_store();
265 _store->copy_store(req.dstPath, req.compact.value_or(false));
266
267 return { true };
268}
269
271 bool reverse,
272 uint64_t page_size)
273{
275 bool done = reverse ? cursor.read_prev(page_size, entries) : cursor.read_next(page_size, entries);
276 return std::make_pair(done, entries);
277}
278
280 bool reverse,
281 const lmdblib::Key& end_key)
282{
283 uint64_t count = 0;
284 bool done = reverse ? cursor.count_until_prev(end_key, count) : cursor.count_until_next(end_key, count);
285 return std::make_pair(done, count);
286}
bool count_until_next(const Key &key, uint64_t &count) const
bool read_next(uint64_t numKeysToRead, KeyDupValuesVector &keyValuePairs) const
bool read_prev(uint64_t numKeysToRead, KeyDupValuesVector &keyValuePairs) const
std::shared_ptr< LMDBCursor > SharedPtr
bool count_until_prev(const Key &key, uint64_t &count) const
void register_handler(uint32_t msgType, T *self, R(T::*handler)() const, bool unique=false)
Napi::Promise process_message(const Napi::CallbackInfo &info)
StartCursorResponse start_cursor(const StartCursorRequest &req)
GetResponse get(const GetRequest &req)
static Napi::Function get_class(Napi::Env env)
BoolResponse close_cursor(const CloseCursorRequest &req)
BoolResponse open_database(const OpenDatabaseRequest &req)
bb::nodejs::AsyncMessageProcessor _msg_processor
HasResponse has(const HasRequest &req)
BatchResponse batch(const BatchRequest &req)
BoolResponse copy_store(const CopyStoreRequest &req)
std::unordered_map< uint64_t, CursorData > _cursors
static std::pair< bool, uint64_t > _advance_cursor_count(const lmdblib::LMDBCursor &cursor, bool reverse, const lmdblib::Key &end_key)
AdvanceCursorResponse advance_cursor(const AdvanceCursorRequest &req)
AdvanceCursorCountResponse advance_cursor_count(const AdvanceCursorCountRequest &req)
Napi::Value call(const Napi::CallbackInfo &)
The only instance method exposed to JavaScript. Takes a msgpack Message and returns a Promise.
static std::pair< bool, lmdblib::KeyDupValuesVector > _advance_cursor(const lmdblib::LMDBCursor &cursor, bool reverse, uint64_t page_size)
std::unique_ptr< lmdblib::LMDBStore > _store
std::string format(Args... args)
Definition log.hpp:23
#define info(...)
Definition log.hpp:93
const std::vector< MemoryValue > data
const uint64_t DEFAULT_MAP_SIZE
const uint64_t DEFAULT_MAX_READERS
const uint64_t DEFAULT_CURSOR_PAGE_SIZE
std::vector< Key > KeysVector
Definition types.hpp:13
std::vector< uint8_t > Key
Definition types.hpp:11
std::vector< KeyValuesPair > KeyDupValuesVector
Definition types.hpp:18
std::vector< OptionalValues > OptionalValuesVector
Definition types.hpp:17
constexpr decltype(auto) get(::tuplet::tuple< T... > &&t) noexcept
Definition tuple.hpp:13