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
26#include <xrpl/basics/contract.h>
27#include <xrpl/beast/utility/instrumentation.h>
28
29#include <boost/filesystem.hpp>
30
31#include <nudb/nudb.hpp>
32
33#include <chrono>
34#include <cstdint>
35#include <cstdio>
36#include <exception>
37#include <memory>
38
39namespace ripple {
40namespace NodeStore {
41
42class NuDBBackend : public Backend
43{
44public:
45 // "appnum" is an application-defined constant stored in the header of a
46 // NuDB database. We used it to identify shard databases before that code
47 // was removed. For now, its only use is a sanity check that the database
48 // was created by xrpld.
49 static constexpr std::uint64_t appnum = 1;
50
52 size_t const keyBytes_;
55 nudb::store db_;
58
60 size_t keyBytes,
61 Section const& keyValues,
63 Scheduler& scheduler,
64 beast::Journal journal)
65 : j_(journal)
66 , keyBytes_(keyBytes)
68 , name_(get(keyValues, "path"))
69 , deletePath_(false)
70 , scheduler_(scheduler)
71 {
72 if (name_.empty())
73 Throw<std::runtime_error>(
74 "nodestore: Missing path in NuDB backend");
75 }
76
78 size_t keyBytes,
79 Section const& keyValues,
81 Scheduler& scheduler,
82 nudb::context& context,
83 beast::Journal journal)
84 : j_(journal)
85 , keyBytes_(keyBytes)
87 , name_(get(keyValues, "path"))
88 , db_(context)
89 , deletePath_(false)
90 , scheduler_(scheduler)
91 {
92 if (name_.empty())
93 Throw<std::runtime_error>(
94 "nodestore: Missing path in NuDB backend");
95 }
96
97 ~NuDBBackend() override
98 {
99 try
100 {
101 // close can throw and we don't want the destructor to throw.
102 close();
103 }
104 catch (nudb::system_error const&)
105 {
106 // Don't allow exceptions to propagate out of destructors.
107 // close() has already logged the error.
108 }
109 }
110
112 getName() override
113 {
114 return name_;
115 }
116
117 void
118 open(bool createIfMissing, uint64_t appType, uint64_t uid, uint64_t salt)
119 override
120 {
121 using namespace boost::filesystem;
122 if (db_.is_open())
123 {
124 UNREACHABLE(
125 "ripple::NodeStore::NuDBBackend::open : database is already "
126 "open");
127 JLOG(j_.error()) << "database is already open";
128 return;
129 }
130 auto const folder = path(name_);
131 auto const dp = (folder / "nudb.dat").string();
132 auto const kp = (folder / "nudb.key").string();
133 auto const lp = (folder / "nudb.log").string();
134 nudb::error_code ec;
135 if (createIfMissing)
136 {
137 create_directories(folder);
138 nudb::create<nudb::xxhasher>(
139 dp,
140 kp,
141 lp,
142 appType,
143 uid,
144 salt,
145 keyBytes_,
146 nudb::block_size(kp),
147 0.50,
148 ec);
149 if (ec == nudb::errc::file_exists)
150 ec = {};
151 if (ec)
152 Throw<nudb::system_error>(ec);
153 }
154 db_.open(dp, kp, lp, ec);
155 if (ec)
156 Throw<nudb::system_error>(ec);
157
158 if (db_.appnum() != appnum)
159 Throw<std::runtime_error>("nodestore: unknown appnum");
160 db_.set_burst(burstSize_);
161 }
162
163 bool
164 isOpen() override
165 {
166 return db_.is_open();
167 }
168
169 void
170 open(bool createIfMissing) override
171 {
172 open(createIfMissing, appnum, nudb::make_uid(), nudb::make_salt());
173 }
174
175 void
176 close() override
177 {
178 if (db_.is_open())
179 {
180 nudb::error_code ec;
181 db_.close(ec);
182 if (ec)
183 {
184 // Log to make sure the nature of the error gets to the user.
185 JLOG(j_.fatal()) << "NuBD close() failed: " << ec.message();
186 Throw<nudb::system_error>(ec);
187 }
188
189 if (deletePath_)
190 {
191 boost::filesystem::remove_all(name_, ec);
192 if (ec)
193 {
194 JLOG(j_.fatal()) << "Filesystem remove_all of " << name_
195 << " failed with: " << ec.message();
196 }
197 }
198 }
199 }
200
201 Status
202 fetch(void const* key, std::shared_ptr<NodeObject>* pno) override
203 {
204 Status status;
205 pno->reset();
206 nudb::error_code ec;
207 db_.fetch(
208 key,
209 [key, pno, &status](void const* data, std::size_t size) {
210 nudb::detail::buffer bf;
211 auto const result = nodeobject_decompress(data, size, bf);
212 DecodedBlob decoded(key, result.first, result.second);
213 if (!decoded.wasOk())
214 {
215 status = dataCorrupt;
216 return;
217 }
218 *pno = decoded.createObject();
219 status = ok;
220 },
221 ec);
222 if (ec == nudb::error::key_not_found)
223 return notFound;
224 if (ec)
225 Throw<nudb::system_error>(ec);
226 return status;
227 }
228
231 {
233 results.reserve(hashes.size());
234 for (auto const& h : hashes)
235 {
237 Status status = fetch(h->begin(), &nObj);
238 if (status != ok)
239 results.push_back({});
240 else
241 results.push_back(nObj);
242 }
243
244 return {results, ok};
245 }
246
247 void
249 {
250 EncodedBlob e(no);
251 nudb::error_code ec;
252 nudb::detail::buffer bf;
253 auto const result = nodeobject_compress(e.getData(), e.getSize(), bf);
254 db_.insert(e.getKey(), result.first, result.second, ec);
255 if (ec && ec != nudb::error::key_exists)
256 Throw<nudb::system_error>(ec);
257 }
258
259 void
261 {
262 BatchWriteReport report;
263 report.writeCount = 1;
264 auto const start = std::chrono::steady_clock::now();
265 do_insert(no);
266 report.elapsed = std::chrono::duration_cast<std::chrono::milliseconds>(
268 scheduler_.onBatchWrite(report);
269 }
270
271 void
272 storeBatch(Batch const& batch) override
273 {
274 BatchWriteReport report;
275 report.writeCount = batch.size();
276 auto const start = std::chrono::steady_clock::now();
277 for (auto const& e : batch)
278 do_insert(e);
279 report.elapsed = std::chrono::duration_cast<std::chrono::milliseconds>(
281 scheduler_.onBatchWrite(report);
282 }
283
284 void
285 sync() override
286 {
287 }
288
289 void
291 {
292 auto const dp = db_.dat_path();
293 auto const kp = db_.key_path();
294 auto const lp = db_.log_path();
295 // auto const appnum = db_.appnum();
296 nudb::error_code ec;
297 db_.close(ec);
298 if (ec)
299 Throw<nudb::system_error>(ec);
300 nudb::visit(
301 dp,
302 [&](void const* key,
303 std::size_t key_bytes,
304 void const* data,
305 std::size_t size,
306 nudb::error_code&) {
307 nudb::detail::buffer bf;
308 auto const result = nodeobject_decompress(data, size, bf);
309 DecodedBlob decoded(key, result.first, result.second);
310 if (!decoded.wasOk())
311 {
312 ec = make_error_code(nudb::error::missing_value);
313 return;
314 }
315 f(decoded.createObject());
316 },
317 nudb::no_progress{},
318 ec);
319 if (ec)
320 Throw<nudb::system_error>(ec);
321 db_.open(dp, kp, lp, ec);
322 if (ec)
323 Throw<nudb::system_error>(ec);
324 }
325
326 int
327 getWriteLoad() override
328 {
329 return 0;
330 }
331
332 void
333 setDeletePath() override
334 {
335 deletePath_ = true;
336 }
337
338 void
339 verify() override
340 {
341 auto const dp = db_.dat_path();
342 auto const kp = db_.key_path();
343 auto const lp = db_.log_path();
344 nudb::error_code ec;
345 db_.close(ec);
346 if (ec)
347 Throw<nudb::system_error>(ec);
348 nudb::verify_info vi;
349 nudb::verify<nudb::xxhasher>(vi, dp, kp, 0, nudb::no_progress{}, ec);
350 if (ec)
351 Throw<nudb::system_error>(ec);
352 db_.open(dp, kp, lp, ec);
353 if (ec)
354 Throw<nudb::system_error>(ec);
355 }
356
357 int
358 fdRequired() const override
359 {
360 return 3;
361 }
362};
363
364//------------------------------------------------------------------------------
365
366class NuDBFactory : public Factory
367{
368public:
370 {
371 Manager::instance().insert(*this);
372 }
373
374 ~NuDBFactory() override
375 {
376 Manager::instance().erase(*this);
377 }
378
380 getName() const override
381 {
382 return "NuDB";
383 }
384
387 size_t keyBytes,
388 Section const& keyValues,
390 Scheduler& scheduler,
391 beast::Journal journal) override
392 {
393 return std::make_unique<NuDBBackend>(
394 keyBytes, keyValues, burstSize, scheduler, journal);
395 }
396
399 size_t keyBytes,
400 Section const& keyValues,
402 Scheduler& scheduler,
403 nudb::context& context,
404 beast::Journal journal) override
405 {
406 return std::make_unique<NuDBBackend>(
407 keyBytes, keyValues, burstSize, scheduler, context, journal);
408 }
409};
410
412
413} // namespace NodeStore
414} // namespace ripple
A generic endpoint for log messages.
Definition: Journal.h:60
Stream fatal() const
Definition: Journal.h:352
Stream error() const
Definition: Journal.h:346
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:75
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:57
void const * getKey() const noexcept
Definition: EncodedBlob.h:118
std::size_t getSize() const noexcept
Definition: EncodedBlob.h:124
void const * getData() const noexcept
Definition: EncodedBlob.h:130
Base class for backend factories.
Definition: Factory.h:37
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:59
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:53
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:49
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:77
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:51
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:56
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:45
Add a path.
Definition: paths.h:58
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:111
std::pair< void const *, std::size_t > nodeobject_compress(void const *in, std::size_t in_size, BufferFactory &&bf)
Definition: codec.h:222
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:44
T get(Section const &section, std::string const &name, T const &defaultValue=T{})
Retrieve a key/value pair from a section.
Definition: BasicConfig.h:355
T push_back(T... args)
T reserve(T... args)
T reset(T... args)
T size(T... args)
Contains information about a batch write operation.