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