rippled
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 <ripple/basics/contract.h>
21 #include <ripple/nodestore/Factory.h>
22 #include <ripple/nodestore/Manager.h>
23 #include <ripple/nodestore/impl/DecodedBlob.h>
24 #include <ripple/nodestore/impl/EncodedBlob.h>
25 #include <ripple/nodestore/impl/codec.h>
26 #include <boost/filesystem.hpp>
27 #include <cassert>
28 #include <chrono>
29 #include <cstdint>
30 #include <cstdio>
31 #include <exception>
32 #include <memory>
33 #include <nudb/nudb.hpp>
34 
35 namespace ripple {
36 namespace NodeStore {
37 
38 class NuDBBackend : public Backend
39 {
40 public:
41  static constexpr std::uint64_t currentType = 1;
42  static constexpr std::uint64_t deterministicMask = 0xFFFFFFFF00000000ull;
43 
44  /* "SHRD" in ASCII */
45  static constexpr std::uint64_t deterministicType = 0x5348524400000000ull;
46 
48  size_t const keyBytes_;
51  nudb::store db_;
54 
56  size_t keyBytes,
57  Section const& keyValues,
58  std::size_t burstSize,
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,
76  std::size_t burstSize,
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  assert(false);
121  JLOG(j_.error()) << "database is already open";
122  return;
123  }
124  auto const folder = path(name_);
125  auto const dp = (folder / "nudb.dat").string();
126  auto const kp = (folder / "nudb.key").string();
127  auto const lp = (folder / "nudb.log").string();
128  nudb::error_code ec;
129  if (createIfMissing)
130  {
131  create_directories(folder);
132  nudb::create<nudb::xxhasher>(
133  dp,
134  kp,
135  lp,
136  appType,
137  uid,
138  salt,
139  keyBytes_,
140  nudb::block_size(kp),
141  0.50,
142  ec);
143  if (ec == nudb::errc::file_exists)
144  ec = {};
145  if (ec)
146  Throw<nudb::system_error>(ec);
147  }
148  db_.open(dp, kp, lp, ec);
149  if (ec)
150  Throw<nudb::system_error>(ec);
151 
160  if (db_.appnum() != currentType &&
161  (db_.appnum() & deterministicMask) != deterministicType)
162  Throw<std::runtime_error>("nodestore: unknown appnum");
163  db_.set_burst(burstSize_);
164  }
165 
166  bool
167  isOpen() override
168  {
169  return db_.is_open();
170  }
171 
172  void
173  open(bool createIfMissing) override
174  {
175  open(createIfMissing, currentType, nudb::make_uid(), nudb::make_salt());
176  }
177 
178  void
179  close() override
180  {
181  if (db_.is_open())
182  {
183  nudb::error_code ec;
184  db_.close(ec);
185  if (ec)
186  {
187  // Log to make sure the nature of the error gets to the user.
188  JLOG(j_.fatal()) << "NuBD close() failed: " << ec.message();
189  Throw<nudb::system_error>(ec);
190  }
191 
192  if (deletePath_)
193  {
194  boost::filesystem::remove_all(name_, ec);
195  if (ec)
196  {
197  JLOG(j_.fatal()) << "Filesystem remove_all of " << name_
198  << " failed with: " << ec.message();
199  }
200  }
201  }
202  }
203 
204  Status
205  fetch(void const* key, std::shared_ptr<NodeObject>* pno) override
206  {
207  Status status;
208  pno->reset();
209  nudb::error_code ec;
210  db_.fetch(
211  key,
212  [key, pno, &status](void const* data, std::size_t size) {
213  nudb::detail::buffer bf;
214  auto const result = nodeobject_decompress(data, size, bf);
215  DecodedBlob decoded(key, result.first, result.second);
216  if (!decoded.wasOk())
217  {
218  status = dataCorrupt;
219  return;
220  }
221  *pno = decoded.createObject();
222  status = ok;
223  },
224  ec);
225  if (ec == nudb::error::key_not_found)
226  return notFound;
227  if (ec)
228  Throw<nudb::system_error>(ec);
229  return status;
230  }
231 
233  fetchBatch(std::vector<uint256 const*> const& hashes) override
234  {
236  results.reserve(hashes.size());
237  for (auto const& h : hashes)
238  {
240  Status status = fetch(h->begin(), &nObj);
241  if (status != ok)
242  results.push_back({});
243  else
244  results.push_back(nObj);
245  }
246 
247  return {results, ok};
248  }
249 
250  void
252  {
253  EncodedBlob e;
254  e.prepare(no);
255  nudb::error_code ec;
256  nudb::detail::buffer bf;
257  auto const result = nodeobject_compress(e.getData(), e.getSize(), bf);
258  db_.insert(e.getKey(), result.first, result.second, ec);
259  if (ec && ec != nudb::error::key_exists)
260  Throw<nudb::system_error>(ec);
261  }
262 
263  void
264  store(std::shared_ptr<NodeObject> const& no) override
265  {
266  BatchWriteReport report;
267  report.writeCount = 1;
268  auto const start = std::chrono::steady_clock::now();
269  do_insert(no);
270  report.elapsed = std::chrono::duration_cast<std::chrono::milliseconds>(
272  scheduler_.onBatchWrite(report);
273  }
274 
275  void
276  storeBatch(Batch const& batch) override
277  {
278  BatchWriteReport report;
279  report.writeCount = batch.size();
280  auto const start = std::chrono::steady_clock::now();
281  for (auto const& e : batch)
282  do_insert(e);
283  report.elapsed = std::chrono::duration_cast<std::chrono::milliseconds>(
285  scheduler_.onBatchWrite(report);
286  }
287 
288  void
289  sync() override
290  {
291  }
292 
293  void
295  {
296  auto const dp = db_.dat_path();
297  auto const kp = db_.key_path();
298  auto const lp = db_.log_path();
299  // auto const appnum = db_.appnum();
300  nudb::error_code ec;
301  db_.close(ec);
302  if (ec)
303  Throw<nudb::system_error>(ec);
304  nudb::visit(
305  dp,
306  [&](void const* key,
307  std::size_t key_bytes,
308  void const* data,
309  std::size_t size,
310  nudb::error_code&) {
311  nudb::detail::buffer bf;
312  auto const result = nodeobject_decompress(data, size, bf);
313  DecodedBlob decoded(key, result.first, result.second);
314  if (!decoded.wasOk())
315  {
316  ec = make_error_code(nudb::error::missing_value);
317  return;
318  }
319  f(decoded.createObject());
320  },
321  nudb::no_progress{},
322  ec);
323  if (ec)
324  Throw<nudb::system_error>(ec);
325  db_.open(dp, kp, lp, ec);
326  if (ec)
327  Throw<nudb::system_error>(ec);
328  }
329 
330  int
331  getWriteLoad() override
332  {
333  return 0;
334  }
335 
336  void
337  setDeletePath() override
338  {
339  deletePath_ = true;
340  }
341 
342  void
343  verify() override
344  {
345  auto const dp = db_.dat_path();
346  auto const kp = db_.key_path();
347  auto const lp = db_.log_path();
348  nudb::error_code ec;
349  db_.close(ec);
350  if (ec)
351  Throw<nudb::system_error>(ec);
352  nudb::verify_info vi;
353  nudb::verify<nudb::xxhasher>(vi, dp, kp, 0, nudb::no_progress{}, ec);
354  if (ec)
355  Throw<nudb::system_error>(ec);
356  db_.open(dp, kp, lp, ec);
357  if (ec)
358  Throw<nudb::system_error>(ec);
359  }
360 
361  int
362  fdRequired() const override
363  {
364  return 3;
365  }
366 };
367 
368 //------------------------------------------------------------------------------
369 
370 class NuDBFactory : public Factory
371 {
372 public:
374  {
375  Manager::instance().insert(*this);
376  }
377 
378  ~NuDBFactory() override
379  {
380  Manager::instance().erase(*this);
381  }
382 
384  getName() const override
385  {
386  return "NuDB";
387  }
388 
391  size_t keyBytes,
392  Section const& keyValues,
393  std::size_t burstSize,
394  Scheduler& scheduler,
395  beast::Journal journal) override
396  {
397  return std::make_unique<NuDBBackend>(
398  keyBytes, keyValues, burstSize, scheduler, journal);
399  }
400 
403  size_t keyBytes,
404  Section const& keyValues,
405  std::size_t burstSize,
406  Scheduler& scheduler,
407  nudb::context& context,
408  beast::Journal journal) override
409  {
410  return std::make_unique<NuDBBackend>(
411  keyBytes, keyValues, burstSize, scheduler, context, journal);
412  }
413 };
414 
416 
417 } // namespace NodeStore
418 } // namespace ripple
beast::Journal::fatal
Stream fatal() const
Definition: Journal.h:339
ripple::Section
Holds a collection of configuration values.
Definition: BasicConfig.h:42
ripple::NodeStore::DecodedBlob::wasOk
bool wasOk() const noexcept
Determine if the decoding was successful.
Definition: DecodedBlob.h:46
ripple::NodeStore::nodeobject_decompress
std::pair< void const *, std::size_t > nodeobject_decompress(void const *in, std::size_t in_size, BufferFactory &&bf)
Definition: codec.h:108
ripple::NodeStore::Factory
Base class for backend factories.
Definition: Factory.h:33
ripple::NodeStore::NuDBBackend::deletePath_
std::atomic< bool > deletePath_
Definition: NuDBFactory.cpp:52
ripple::NodeStore::NuDBBackend::store
void store(std::shared_ptr< NodeObject > const &no) override
Store a single object.
Definition: NuDBFactory.cpp:264
ripple::NodeStore::EncodedBlob::getSize
std::size_t getSize() const noexcept
Definition: EncodedBlob.h:47
ripple::NodeStore::NuDBBackend::open
void open(bool createIfMissing, uint64_t appType, uint64_t uid, uint64_t salt) override
Open the backend.
Definition: NuDBFactory.cpp:114
ripple::NodeStore::NuDBBackend::setDeletePath
void setDeletePath() override
Remove contents on disk upon destruction.
Definition: NuDBFactory.cpp:337
ripple::NodeStore::DecodedBlob
Parsed key/value blob into NodeObject components.
Definition: DecodedBlob.h:38
std::string
STL class.
std::shared_ptr< NodeObject >
exception
ripple::NodeStore::ok
@ ok
Definition: nodestore/Types.h:45
ripple::NodeStore::Manager::erase
virtual void erase(Factory &factory)=0
Remove a factory.
std::pair
std::vector::reserve
T reserve(T... args)
ripple::NodeStore::NuDBBackend::do_insert
void do_insert(std::shared_ptr< NodeObject > const &no)
Definition: NuDBFactory.cpp:251
std::vector
STL class.
std::vector::size
T size(T... args)
ripple::NodeStore::NuDBBackend::isOpen
bool isOpen() override
Returns true is the database is open.
Definition: NuDBFactory.cpp:167
ripple::NodeStore::NuDBBackend::fetchBatch
std::pair< std::vector< std::shared_ptr< NodeObject > >, Status > fetchBatch(std::vector< uint256 const * > const &hashes) override
Fetch a batch synchronously.
Definition: NuDBFactory.cpp:233
ripple::NodeStore::NuDBBackend::~NuDBBackend
~NuDBBackend() override
Definition: NuDBFactory.cpp:93
ripple::NodeStore::NuDBBackend::close
void close() override
Close the backend.
Definition: NuDBFactory.cpp:179
ripple::NodeStore::EncodedBlob::getData
void const * getData() const noexcept
Definition: EncodedBlob.h:53
std::function
ripple::NodeStore::NuDBBackend::fdRequired
int fdRequired() const override
Returns the number of file descriptors the backend expects to need.
Definition: NuDBFactory.cpp:362
ripple::NodeStore::nuDBFactory
static NuDBFactory nuDBFactory
Definition: NuDBFactory.cpp:415
ripple::NodeStore::NuDBBackend::keyBytes_
const size_t keyBytes_
Definition: NuDBFactory.cpp:48
ripple::NodeStore::NuDBBackend::sync
void sync() override
Definition: NuDBFactory.cpp:289
std::shared_ptr::reset
T reset(T... args)
std::vector::push_back
T push_back(T... args)
ripple::NodeStore::notFound
@ notFound
Definition: nodestore/Types.h:46
ripple::NodeStore::NuDBBackend::currentType
static constexpr std::uint64_t currentType
Definition: NuDBFactory.cpp:41
ripple::NodeStore::NuDBBackend::j_
const beast::Journal j_
Definition: NuDBFactory.cpp:47
ripple::NodeStore::Manager::insert
virtual void insert(Factory &factory)=0
Add a factory.
ripple::NodeStore::NuDBBackend::name_
const std::string name_
Definition: NuDBFactory.cpp:50
ripple::NodeStore::DecodedBlob::createObject
std::shared_ptr< NodeObject > createObject()
Create a NodeObject from this data.
Definition: DecodedBlob.cpp:74
chrono
ripple::NodeStore::NuDBBackend
Definition: NuDBFactory.cpp:38
ripple::NodeStore::NuDBFactory
Definition: NuDBFactory.cpp:370
ripple::NodeStore::NuDBBackend::getWriteLoad
int getWriteLoad() override
Estimate the number of write operations pending.
Definition: NuDBFactory.cpp:331
beast::Journal::error
Stream error() const
Definition: Journal.h:333
cstdint
ripple::NodeStore::NuDBBackend::deterministicType
static constexpr std::uint64_t deterministicType
Definition: NuDBFactory.cpp:45
beast::Journal
A generic endpoint for log messages.
Definition: Journal.h:58
ripple::SizedItem::burstSize
@ burstSize
ripple::NodeStore::dataCorrupt
@ dataCorrupt
Definition: nodestore/Types.h:47
std::uint64_t
ripple::NodeStore::NuDBBackend::NuDBBackend
NuDBBackend(size_t keyBytes, Section const &keyValues, std::size_t burstSize, Scheduler &scheduler, beast::Journal journal)
Definition: NuDBFactory.cpp:55
std::atomic< bool >
ripple::NodeStore::Scheduler
Scheduling for asynchronous backend activity.
Definition: ripple/nodestore/Scheduler.h:60
ripple::NodeStore::NuDBBackend::open
void open(bool createIfMissing) override
Open the backend.
Definition: NuDBFactory.cpp:173
ripple::NodeStore::BatchWriteReport
Contains information about a batch write operation.
Definition: ripple/nodestore/Scheduler.h:44
ripple::NodeStore::NuDBFactory::~NuDBFactory
~NuDBFactory() override
Definition: NuDBFactory.cpp:378
ripple::NodeStore::NuDBBackend::verify
void verify() override
Perform consistency checks on database.
Definition: NuDBFactory.cpp:343
memory
ripple::NodeStore::NuDBBackend::getName
std::string getName() override
Get the human-readable name of this backend.
Definition: NuDBFactory.cpp:108
ripple::NodeStore::Status
Status
Return codes from Backend operations.
Definition: nodestore/Types.h:44
ripple::NodeStore::NuDBFactory::createInstance
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.
Definition: NuDBFactory.cpp:402
ripple::NodeStore::NuDBBackend::deterministicMask
static constexpr std::uint64_t deterministicMask
Definition: NuDBFactory.cpp:42
ripple
Use hash_* containers for keys that do not need a cryptographically secure hashing algorithm.
Definition: RCLCensorshipDetector.h:29
ripple::NodeStore::NuDBBackend::scheduler_
Scheduler & scheduler_
Definition: NuDBFactory.cpp:53
ripple::NodeStore::NuDBFactory::getName
std::string getName() const override
Retrieve the name of this factory.
Definition: NuDBFactory.cpp:384
cassert
ripple::NodeStore::NuDBBackend::burstSize_
const std::size_t burstSize_
Definition: NuDBFactory.cpp:49
ripple::NodeStore::NuDBFactory::createInstance
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.
Definition: NuDBFactory.cpp:390
std::string::empty
T empty(T... args)
ripple::NodeStore::NuDBBackend::db_
nudb::store db_
Definition: NuDBFactory.cpp:51
ripple::NodeStore::NuDBBackend::storeBatch
void storeBatch(Batch const &batch) override
Store a group of objects.
Definition: NuDBFactory.cpp:276
std::size_t
ripple::NodeStore::BatchWriteReport::writeCount
int writeCount
Definition: ripple/nodestore/Scheduler.h:49
ripple::NodeStore::nodeobject_compress
std::pair< void const *, std::size_t > nodeobject_compress(void const *in, std::size_t in_size, BufferFactory &&bf)
Definition: codec.h:219
ripple::NodeStore::EncodedBlob
Utility for producing flattened node objects.
Definition: EncodedBlob.h:34
ripple::NodeStore::NuDBBackend::for_each
void for_each(std::function< void(std::shared_ptr< NodeObject >)> f) override
Visit every object in the database This is usually called during import.
Definition: NuDBFactory.cpp:294
ripple::NodeStore::Manager::instance
static Manager & instance()
Returns the instance of the manager singleton.
Definition: ManagerImp.cpp:120
ripple::NodeStore::EncodedBlob::getKey
void const * getKey() const noexcept
Definition: EncodedBlob.h:41
ripple::NodeStore::BatchWriteReport::elapsed
std::chrono::milliseconds elapsed
Definition: ripple/nodestore/Scheduler.h:48
std::unique_ptr
STL class.
ripple::NodeStore::NuDBBackend::NuDBBackend
NuDBBackend(size_t keyBytes, Section const &keyValues, std::size_t burstSize, Scheduler &scheduler, nudb::context &context, beast::Journal journal)
Definition: NuDBFactory.cpp:73
cstdio
ripple::NodeStore::Scheduler::onBatchWrite
virtual void onBatchWrite(BatchWriteReport const &report)=0
Reports the completion of a batch write Allows the scheduler to monitor the node store's performance.
ripple::DefaultVote::no
@ no
ripple::NodeStore::NuDBBackend::fetch
Status fetch(void const *key, std::shared_ptr< NodeObject > *pno) override
Fetch a single object.
Definition: NuDBFactory.cpp:205
ripple::get
T & get(EitherAmount &amt)
Definition: AmountSpec.h:118
ripple::NodeStore::EncodedBlob::prepare
void prepare(std::shared_ptr< NodeObject > const &object)
Definition: EncodedBlob.cpp:27
ripple::NodeStore::NuDBFactory::NuDBFactory
NuDBFactory()
Definition: NuDBFactory.cpp:373
ripple::NodeStore::Backend
A backend used for the NodeStore.
Definition: Backend.h:39
std::chrono::steady_clock::now
T now(T... args)