rippled
Loading...
Searching...
No Matches
NuDBFactory.cpp
1//------------------------------------------------------------------------------
2/*
3 This file is part of rippled: https://github.com/ripple/rippled
4 Copyright (c) 2012, 2013 Ripple Labs Inc.
5
6 Permission to use, copy, modify, and/or distribute this software for any
7 purpose with or without fee is hereby granted, provided that the above
8 copyright notice and this permission notice appear in all copies.
9
10 THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11 WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12 MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13 ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14 WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15 ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16 OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17*/
18//==============================================================================
19
20#include <xrpld/nodestore/Factory.h>
21#include <xrpld/nodestore/Manager.h>
22#include <xrpld/nodestore/detail/DecodedBlob.h>
23#include <xrpld/nodestore/detail/EncodedBlob.h>
24#include <xrpld/nodestore/detail/codec.h>
25#include <xrpl/basics/contract.h>
26#include <xrpl/beast/utility/instrumentation.h>
27#include <boost/filesystem.hpp>
28#include <chrono>
29#include <cstdint>
30#include <cstdio>
31#include <exception>
32#include <memory>
33#include <nudb/nudb.hpp>
34
35namespace ripple {
36namespace NodeStore {
37
38class NuDBBackend : public Backend
39{
40public:
41 // "appnum" is an application-defined constant stored in the header of a
42 // NuDB database. We used it to identify shard databases before that code
43 // was removed. For now, its only use is a sanity check that the database
44 // was created by xrpld.
45 static constexpr std::uint64_t appnum = 1;
46
48 size_t const keyBytes_;
51 nudb::store db_;
54
56 size_t keyBytes,
57 Section const& keyValues,
59 Scheduler& scheduler,
60 beast::Journal journal)
61 : j_(journal)
62 , keyBytes_(keyBytes)
64 , name_(get(keyValues, "path"))
65 , deletePath_(false)
66 , scheduler_(scheduler)
67 {
68 if (name_.empty())
69 Throw<std::runtime_error>(
70 "nodestore: Missing path in NuDB backend");
71 }
72
74 size_t keyBytes,
75 Section const& keyValues,
77 Scheduler& scheduler,
78 nudb::context& context,
79 beast::Journal journal)
80 : j_(journal)
81 , keyBytes_(keyBytes)
83 , name_(get(keyValues, "path"))
84 , db_(context)
85 , deletePath_(false)
86 , scheduler_(scheduler)
87 {
88 if (name_.empty())
89 Throw<std::runtime_error>(
90 "nodestore: Missing path in NuDB backend");
91 }
92
93 ~NuDBBackend() override
94 {
95 try
96 {
97 // close can throw and we don't want the destructor to throw.
98 close();
99 }
100 catch (nudb::system_error const&)
101 {
102 // Don't allow exceptions to propagate out of destructors.
103 // close() has already logged the error.
104 }
105 }
106
108 getName() override
109 {
110 return name_;
111 }
112
113 void
114 open(bool createIfMissing, uint64_t appType, uint64_t uid, uint64_t salt)
115 override
116 {
117 using namespace boost::filesystem;
118 if (db_.is_open())
119 {
120 UNREACHABLE(
121 "ripple::NodeStore::NuDBBackend::open : database is already "
122 "open");
123 JLOG(j_.error()) << "database is already open";
124 return;
125 }
126 auto const folder = path(name_);
127 auto const dp = (folder / "nudb.dat").string();
128 auto const kp = (folder / "nudb.key").string();
129 auto const lp = (folder / "nudb.log").string();
130 nudb::error_code ec;
131 if (createIfMissing)
132 {
133 create_directories(folder);
134 nudb::create<nudb::xxhasher>(
135 dp,
136 kp,
137 lp,
138 appType,
139 uid,
140 salt,
141 keyBytes_,
142 nudb::block_size(kp),
143 0.50,
144 ec);
145 if (ec == nudb::errc::file_exists)
146 ec = {};
147 if (ec)
148 Throw<nudb::system_error>(ec);
149 }
150 db_.open(dp, kp, lp, ec);
151 if (ec)
152 Throw<nudb::system_error>(ec);
153
154 if (db_.appnum() != appnum)
155 Throw<std::runtime_error>("nodestore: unknown appnum");
156 db_.set_burst(burstSize_);
157 }
158
159 bool
160 isOpen() override
161 {
162 return db_.is_open();
163 }
164
165 void
166 open(bool createIfMissing) override
167 {
168 open(createIfMissing, appnum, nudb::make_uid(), nudb::make_salt());
169 }
170
171 void
172 close() override
173 {
174 if (db_.is_open())
175 {
176 nudb::error_code ec;
177 db_.close(ec);
178 if (ec)
179 {
180 // Log to make sure the nature of the error gets to the user.
181 JLOG(j_.fatal()) << "NuBD close() failed: " << ec.message();
182 Throw<nudb::system_error>(ec);
183 }
184
185 if (deletePath_)
186 {
187 boost::filesystem::remove_all(name_, ec);
188 if (ec)
189 {
190 JLOG(j_.fatal()) << "Filesystem remove_all of " << name_
191 << " failed with: " << ec.message();
192 }
193 }
194 }
195 }
196
197 Status
198 fetch(void const* key, std::shared_ptr<NodeObject>* pno) override
199 {
200 Status status;
201 pno->reset();
202 nudb::error_code ec;
203 db_.fetch(
204 key,
205 [key, pno, &status](void const* data, std::size_t size) {
206 nudb::detail::buffer bf;
207 auto const result = nodeobject_decompress(data, size, bf);
208 DecodedBlob decoded(key, result.first, result.second);
209 if (!decoded.wasOk())
210 {
211 status = dataCorrupt;
212 return;
213 }
214 *pno = decoded.createObject();
215 status = ok;
216 },
217 ec);
218 if (ec == nudb::error::key_not_found)
219 return notFound;
220 if (ec)
221 Throw<nudb::system_error>(ec);
222 return status;
223 }
224
227 {
229 results.reserve(hashes.size());
230 for (auto const& h : hashes)
231 {
233 Status status = fetch(h->begin(), &nObj);
234 if (status != ok)
235 results.push_back({});
236 else
237 results.push_back(nObj);
238 }
239
240 return {results, ok};
241 }
242
243 void
245 {
246 EncodedBlob e(no);
247 nudb::error_code ec;
248 nudb::detail::buffer bf;
249 auto const result = nodeobject_compress(e.getData(), e.getSize(), bf);
250 db_.insert(e.getKey(), result.first, result.second, ec);
251 if (ec && ec != nudb::error::key_exists)
252 Throw<nudb::system_error>(ec);
253 }
254
255 void
257 {
258 BatchWriteReport report;
259 report.writeCount = 1;
260 auto const start = std::chrono::steady_clock::now();
261 do_insert(no);
262 report.elapsed = std::chrono::duration_cast<std::chrono::milliseconds>(
264 scheduler_.onBatchWrite(report);
265 }
266
267 void
268 storeBatch(Batch const& batch) override
269 {
270 BatchWriteReport report;
271 report.writeCount = batch.size();
272 auto const start = std::chrono::steady_clock::now();
273 for (auto const& e : batch)
274 do_insert(e);
275 report.elapsed = std::chrono::duration_cast<std::chrono::milliseconds>(
277 scheduler_.onBatchWrite(report);
278 }
279
280 void
281 sync() override
282 {
283 }
284
285 void
287 {
288 auto const dp = db_.dat_path();
289 auto const kp = db_.key_path();
290 auto const lp = db_.log_path();
291 // auto const appnum = db_.appnum();
292 nudb::error_code ec;
293 db_.close(ec);
294 if (ec)
295 Throw<nudb::system_error>(ec);
296 nudb::visit(
297 dp,
298 [&](void const* key,
299 std::size_t key_bytes,
300 void const* data,
301 std::size_t size,
302 nudb::error_code&) {
303 nudb::detail::buffer bf;
304 auto const result = nodeobject_decompress(data, size, bf);
305 DecodedBlob decoded(key, result.first, result.second);
306 if (!decoded.wasOk())
307 {
308 ec = make_error_code(nudb::error::missing_value);
309 return;
310 }
311 f(decoded.createObject());
312 },
313 nudb::no_progress{},
314 ec);
315 if (ec)
316 Throw<nudb::system_error>(ec);
317 db_.open(dp, kp, lp, ec);
318 if (ec)
319 Throw<nudb::system_error>(ec);
320 }
321
322 int
323 getWriteLoad() override
324 {
325 return 0;
326 }
327
328 void
329 setDeletePath() override
330 {
331 deletePath_ = true;
332 }
333
334 void
335 verify() override
336 {
337 auto const dp = db_.dat_path();
338 auto const kp = db_.key_path();
339 auto const lp = db_.log_path();
340 nudb::error_code ec;
341 db_.close(ec);
342 if (ec)
343 Throw<nudb::system_error>(ec);
344 nudb::verify_info vi;
345 nudb::verify<nudb::xxhasher>(vi, dp, kp, 0, nudb::no_progress{}, ec);
346 if (ec)
347 Throw<nudb::system_error>(ec);
348 db_.open(dp, kp, lp, ec);
349 if (ec)
350 Throw<nudb::system_error>(ec);
351 }
352
353 int
354 fdRequired() const override
355 {
356 return 3;
357 }
358};
359
360//------------------------------------------------------------------------------
361
362class NuDBFactory : public Factory
363{
364public:
366 {
367 Manager::instance().insert(*this);
368 }
369
370 ~NuDBFactory() override
371 {
372 Manager::instance().erase(*this);
373 }
374
376 getName() const override
377 {
378 return "NuDB";
379 }
380
383 size_t keyBytes,
384 Section const& keyValues,
386 Scheduler& scheduler,
387 beast::Journal journal) override
388 {
389 return std::make_unique<NuDBBackend>(
390 keyBytes, keyValues, burstSize, scheduler, journal);
391 }
392
395 size_t keyBytes,
396 Section const& keyValues,
398 Scheduler& scheduler,
399 nudb::context& context,
400 beast::Journal journal) override
401 {
402 return std::make_unique<NuDBBackend>(
403 keyBytes, keyValues, burstSize, scheduler, context, journal);
404 }
405};
406
408
409} // namespace NodeStore
410} // namespace ripple
A generic endpoint for log messages.
Definition: Journal.h:59
Stream fatal() const
Definition: Journal.h:341
Stream error() const
Definition: Journal.h:335
A backend used for the NodeStore.
Definition: Backend.h:40
Parsed key/value blob into NodeObject components.
Definition: DecodedBlob.h:39
std::shared_ptr< NodeObject > createObject()
Create a NodeObject from this data.
Definition: DecodedBlob.cpp:73
bool wasOk() const noexcept
Determine if the decoding was successful.
Definition: DecodedBlob.h:46
Convert a NodeObject from in-memory to database format.
Definition: EncodedBlob.h:55
void const * getKey() const noexcept
Definition: EncodedBlob.h:116
std::size_t getSize() const noexcept
Definition: EncodedBlob.h:122
void const * getData() const noexcept
Definition: EncodedBlob.h:128
Base class for backend factories.
Definition: Factory.h:34
static Manager & instance()
Returns the instance of the manager singleton.
Definition: ManagerImp.cpp:116
virtual void erase(Factory &factory)=0
Remove a factory.
virtual void insert(Factory &factory)=0
Add a factory.
void store(std::shared_ptr< NodeObject > const &no) override
Store a single object.
NuDBBackend(size_t keyBytes, Section const &keyValues, std::size_t burstSize, Scheduler &scheduler, beast::Journal journal)
Definition: NuDBFactory.cpp:55
Status fetch(void const *key, std::shared_ptr< NodeObject > *pno) override
Fetch a single object.
void open(bool createIfMissing) override
Open the backend.
std::size_t const burstSize_
Definition: NuDBFactory.cpp:49
std::pair< std::vector< std::shared_ptr< NodeObject > >, Status > fetchBatch(std::vector< uint256 const * > const &hashes) override
Fetch a batch synchronously.
static constexpr std::uint64_t appnum
Definition: NuDBFactory.cpp:45
void close() override
Close the backend.
NuDBBackend(size_t keyBytes, Section const &keyValues, std::size_t burstSize, Scheduler &scheduler, nudb::context &context, beast::Journal journal)
Definition: NuDBFactory.cpp:73
void storeBatch(Batch const &batch) override
Store a group of objects.
void do_insert(std::shared_ptr< NodeObject > const &no)
beast::Journal const j_
Definition: NuDBFactory.cpp:47
int fdRequired() const override
Returns the number of file descriptors the backend expects to need.
std::string getName() override
Get the human-readable name of this backend.
void open(bool createIfMissing, uint64_t appType, uint64_t uid, uint64_t salt) override
Open the backend.
void verify() override
Perform consistency checks on database.
void for_each(std::function< void(std::shared_ptr< NodeObject >)> f) override
Visit every object in the database This is usually called during import.
std::atomic< bool > deletePath_
Definition: NuDBFactory.cpp:52
bool isOpen() override
Returns true is the database is open.
int getWriteLoad() override
Estimate the number of write operations pending.
void setDeletePath() override
Remove contents on disk upon destruction.
std::unique_ptr< Backend > createInstance(size_t keyBytes, Section const &keyValues, std::size_t burstSize, Scheduler &scheduler, nudb::context &context, beast::Journal journal) override
Create an instance of this factory's backend.
std::string getName() const override
Retrieve the name of this factory.
std::unique_ptr< Backend > createInstance(size_t keyBytes, Section const &keyValues, std::size_t burstSize, Scheduler &scheduler, beast::Journal journal) override
Create an instance of this factory's backend.
Scheduling for asynchronous backend activity.
virtual void onBatchWrite(BatchWriteReport const &report)=0
Reports the completion of a batch write Allows the scheduler to monitor the node store's performance.
Holds a collection of configuration values.
Definition: BasicConfig.h:43
T empty(T... args)
static NuDBFactory nuDBFactory
std::pair< void const *, std::size_t > nodeobject_decompress(void const *in, std::size_t in_size, BufferFactory &&bf)
Definition: codec.h:108
std::pair< void const *, std::size_t > nodeobject_compress(void const *in, std::size_t in_size, BufferFactory &&bf)
Definition: codec.h:219
Status
Return codes from Backend operations.
Use hash_* containers for keys that do not need a cryptographically secure hashing algorithm.
Definition: algorithm.h:26
std::error_code make_error_code(ripple::TokenCodecErrc e)
Definition: token_errors.h:97
@ open
We haven't closed our ledger yet, but others might have.
@ no
Definition: Steps.h:42
T get(Section const &section, std::string const &name, T const &defaultValue=T{})
Retrieve a key/value pair from a section.
Definition: BasicConfig.h:353
T push_back(T... args)
T reserve(T... args)
T reset(T... args)
T size(T... args)
Contains information about a batch write operation.