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(no);
254  nudb::error_code ec;
255  nudb::detail::buffer bf;
256  auto const result = nodeobject_compress(e.getData(), e.getSize(), bf);
257  db_.insert(e.getKey(), result.first, result.second, ec);
258  if (ec && ec != nudb::error::key_exists)
259  Throw<nudb::system_error>(ec);
260  }
261 
262  void
263  store(std::shared_ptr<NodeObject> const& no) override
264  {
265  BatchWriteReport report;
266  report.writeCount = 1;
267  auto const start = std::chrono::steady_clock::now();
268  do_insert(no);
269  report.elapsed = std::chrono::duration_cast<std::chrono::milliseconds>(
271  scheduler_.onBatchWrite(report);
272  }
273 
274  void
275  storeBatch(Batch const& batch) override
276  {
277  BatchWriteReport report;
278  report.writeCount = batch.size();
279  auto const start = std::chrono::steady_clock::now();
280  for (auto const& e : batch)
281  do_insert(e);
282  report.elapsed = std::chrono::duration_cast<std::chrono::milliseconds>(
284  scheduler_.onBatchWrite(report);
285  }
286 
287  void
288  sync() override
289  {
290  }
291 
292  void
294  {
295  auto const dp = db_.dat_path();
296  auto const kp = db_.key_path();
297  auto const lp = db_.log_path();
298  // auto const appnum = db_.appnum();
299  nudb::error_code ec;
300  db_.close(ec);
301  if (ec)
302  Throw<nudb::system_error>(ec);
303  nudb::visit(
304  dp,
305  [&](void const* key,
306  std::size_t key_bytes,
307  void const* data,
308  std::size_t size,
309  nudb::error_code&) {
310  nudb::detail::buffer bf;
311  auto const result = nodeobject_decompress(data, size, bf);
312  DecodedBlob decoded(key, result.first, result.second);
313  if (!decoded.wasOk())
314  {
315  ec = make_error_code(nudb::error::missing_value);
316  return;
317  }
318  f(decoded.createObject());
319  },
320  nudb::no_progress{},
321  ec);
322  if (ec)
323  Throw<nudb::system_error>(ec);
324  db_.open(dp, kp, lp, ec);
325  if (ec)
326  Throw<nudb::system_error>(ec);
327  }
328 
329  int
330  getWriteLoad() override
331  {
332  return 0;
333  }
334 
335  void
336  setDeletePath() override
337  {
338  deletePath_ = true;
339  }
340 
341  void
342  verify() override
343  {
344  auto const dp = db_.dat_path();
345  auto const kp = db_.key_path();
346  auto const lp = db_.log_path();
347  nudb::error_code ec;
348  db_.close(ec);
349  if (ec)
350  Throw<nudb::system_error>(ec);
351  nudb::verify_info vi;
352  nudb::verify<nudb::xxhasher>(vi, dp, kp, 0, nudb::no_progress{}, ec);
353  if (ec)
354  Throw<nudb::system_error>(ec);
355  db_.open(dp, kp, lp, ec);
356  if (ec)
357  Throw<nudb::system_error>(ec);
358  }
359 
360  int
361  fdRequired() const override
362  {
363  return 3;
364  }
365 };
366 
367 //------------------------------------------------------------------------------
368 
369 class NuDBFactory : public Factory
370 {
371 public:
373  {
374  Manager::instance().insert(*this);
375  }
376 
377  ~NuDBFactory() override
378  {
379  Manager::instance().erase(*this);
380  }
381 
383  getName() const override
384  {
385  return "NuDB";
386  }
387 
390  size_t keyBytes,
391  Section const& keyValues,
392  std::size_t burstSize,
393  Scheduler& scheduler,
394  beast::Journal journal) override
395  {
396  return std::make_unique<NuDBBackend>(
397  keyBytes, keyValues, burstSize, scheduler, journal);
398  }
399 
402  size_t keyBytes,
403  Section const& keyValues,
404  std::size_t burstSize,
405  Scheduler& scheduler,
406  nudb::context& context,
407  beast::Journal journal) override
408  {
409  return std::make_unique<NuDBBackend>(
410  keyBytes, keyValues, burstSize, scheduler, context, journal);
411  }
412 };
413 
415 
416 } // namespace NodeStore
417 } // 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:263
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:336
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
std::function
ripple::NodeStore::NuDBBackend::fdRequired
int fdRequired() const override
Returns the number of file descriptors the backend expects to need.
Definition: NuDBFactory.cpp:361
ripple::NodeStore::nuDBFactory
static NuDBFactory nuDBFactory
Definition: NuDBFactory.cpp:414
ripple::NodeStore::NuDBBackend::keyBytes_
const size_t keyBytes_
Definition: NuDBFactory.cpp:48
ripple::NodeStore::NuDBBackend::sync
void sync() override
Definition: NuDBFactory.cpp:288
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:73
chrono
ripple::NodeStore::NuDBBackend
Definition: NuDBFactory.cpp:38
ripple::NodeStore::NuDBFactory
Definition: NuDBFactory.cpp:369
ripple::NodeStore::EncodedBlob::getData
void const * getData() const noexcept
Definition: EncodedBlob.h:124
ripple::NodeStore::NuDBBackend::getWriteLoad
int getWriteLoad() override
Estimate the number of write operations pending.
Definition: NuDBFactory.cpp:330
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:377
ripple::NodeStore::NuDBBackend::verify
void verify() override
Perform consistency checks on database.
Definition: NuDBFactory.cpp:342
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:401
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:383
ripple::NodeStore::EncodedBlob::getSize
std::size_t getSize() const noexcept
Definition: EncodedBlob.h:118
ripple::NodeStore::EncodedBlob
Convert a NodeObject from in-memory to database format.
Definition: EncodedBlob.h:54
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:389
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:275
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::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:293
ripple::NodeStore::Manager::instance
static Manager & instance()
Returns the instance of the manager singleton.
Definition: ManagerImp.cpp:120
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
ripple::NodeStore::EncodedBlob::getKey
void const * getKey() const noexcept
Definition: EncodedBlob.h:112
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::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::NuDBFactory::NuDBFactory
NuDBFactory()
Definition: NuDBFactory.cpp:372
ripple::NodeStore::Backend
A backend used for the NodeStore.
Definition: Backend.h:39
std::chrono::steady_clock::now
T now(T... args)