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<std::string>(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<std::string>(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  close();
96  }
97 
99  getName() override
100  {
101  return name_;
102  }
103 
104  void
105  open(bool createIfMissing, uint64_t appType, uint64_t uid, uint64_t salt)
106  override
107  {
108  using namespace boost::filesystem;
109  if (db_.is_open())
110  {
111  assert(false);
112  JLOG(j_.error()) << "database is already open";
113  return;
114  }
115  auto const folder = path(name_);
116  auto const dp = (folder / "nudb.dat").string();
117  auto const kp = (folder / "nudb.key").string();
118  auto const lp = (folder / "nudb.log").string();
119  nudb::error_code ec;
120  if (createIfMissing)
121  {
122  create_directories(folder);
123  nudb::create<nudb::xxhasher>(
124  dp,
125  kp,
126  lp,
127  appType,
128  uid,
129  salt,
130  keyBytes_,
131  nudb::block_size(kp),
132  0.50,
133  ec);
134  if (ec == nudb::errc::file_exists)
135  ec = {};
136  if (ec)
137  Throw<nudb::system_error>(ec);
138  }
139  db_.open(dp, kp, lp, ec);
140  if (ec)
141  Throw<nudb::system_error>(ec);
142 
151  if (db_.appnum() != currentType &&
152  (db_.appnum() & deterministicMask) != deterministicType)
153  Throw<std::runtime_error>("nodestore: unknown appnum");
154  db_.set_burst(burstSize_);
155  }
156 
157  bool
158  isOpen() override
159  {
160  return db_.is_open();
161  }
162 
163  void
164  open(bool createIfMissing) override
165  {
166  open(createIfMissing, currentType, nudb::make_uid(), nudb::make_salt());
167  }
168 
169  void
170  close() override
171  {
172  if (db_.is_open())
173  {
174  nudb::error_code ec;
175  db_.close(ec);
176  if (ec)
177  Throw<nudb::system_error>(ec);
178  if (deletePath_)
179  {
180  boost::filesystem::remove_all(name_);
181  }
182  }
183  }
184 
185  Status
186  fetch(void const* key, std::shared_ptr<NodeObject>* pno) override
187  {
188  Status status;
189  pno->reset();
190  nudb::error_code ec;
191  db_.fetch(
192  key,
193  [key, pno, &status](void const* data, std::size_t size) {
194  nudb::detail::buffer bf;
195  auto const result = nodeobject_decompress(data, size, bf);
196  DecodedBlob decoded(key, result.first, result.second);
197  if (!decoded.wasOk())
198  {
199  status = dataCorrupt;
200  return;
201  }
202  *pno = decoded.createObject();
203  status = ok;
204  },
205  ec);
206  if (ec == nudb::error::key_not_found)
207  return notFound;
208  if (ec)
209  Throw<nudb::system_error>(ec);
210  return status;
211  }
212 
213  bool
214  canFetchBatch() override
215  {
216  return true;
217  }
218 
220  fetchBatch(std::vector<uint256 const*> const& hashes) override
221  {
223  results.reserve(hashes.size());
224  for (auto const& h : hashes)
225  {
227  Status status = fetch(h->begin(), &nObj);
228  if (status != ok)
229  results.push_back({});
230  else
231  results.push_back(nObj);
232  }
233 
234  return {results, ok};
235  }
236 
237  void
239  {
240  EncodedBlob e;
241  e.prepare(no);
242  nudb::error_code ec;
243  nudb::detail::buffer bf;
244  auto const result = nodeobject_compress(e.getData(), e.getSize(), bf);
245  db_.insert(e.getKey(), result.first, result.second, ec);
246  if (ec && ec != nudb::error::key_exists)
247  Throw<nudb::system_error>(ec);
248  }
249 
250  void
251  store(std::shared_ptr<NodeObject> const& no) override
252  {
253  BatchWriteReport report;
254  report.writeCount = 1;
255  auto const start = std::chrono::steady_clock::now();
256  do_insert(no);
257  report.elapsed = std::chrono::duration_cast<std::chrono::milliseconds>(
259  scheduler_.onBatchWrite(report);
260  }
261 
262  void
263  storeBatch(Batch const& batch) override
264  {
265  BatchWriteReport report;
266  report.writeCount = batch.size();
267  auto const start = std::chrono::steady_clock::now();
268  for (auto const& e : batch)
269  do_insert(e);
270  report.elapsed = std::chrono::duration_cast<std::chrono::milliseconds>(
272  scheduler_.onBatchWrite(report);
273  }
274 
275  void
276  sync() override
277  {
278  }
279 
280  void
282  {
283  auto const dp = db_.dat_path();
284  auto const kp = db_.key_path();
285  auto const lp = db_.log_path();
286  // auto const appnum = db_.appnum();
287  nudb::error_code ec;
288  db_.close(ec);
289  if (ec)
290  Throw<nudb::system_error>(ec);
291  nudb::visit(
292  dp,
293  [&](void const* key,
294  std::size_t key_bytes,
295  void const* data,
296  std::size_t size,
297  nudb::error_code&) {
298  nudb::detail::buffer bf;
299  auto const result = nodeobject_decompress(data, size, bf);
300  DecodedBlob decoded(key, result.first, result.second);
301  if (!decoded.wasOk())
302  {
303  ec = make_error_code(nudb::error::missing_value);
304  return;
305  }
306  f(decoded.createObject());
307  },
308  nudb::no_progress{},
309  ec);
310  if (ec)
311  Throw<nudb::system_error>(ec);
312  db_.open(dp, kp, lp, ec);
313  if (ec)
314  Throw<nudb::system_error>(ec);
315  }
316 
317  int
318  getWriteLoad() override
319  {
320  return 0;
321  }
322 
323  void
324  setDeletePath() override
325  {
326  deletePath_ = true;
327  }
328 
329  void
330  verify() override
331  {
332  auto const dp = db_.dat_path();
333  auto const kp = db_.key_path();
334  auto const lp = db_.log_path();
335  nudb::error_code ec;
336  db_.close(ec);
337  if (ec)
338  Throw<nudb::system_error>(ec);
339  nudb::verify_info vi;
340  nudb::verify<nudb::xxhasher>(vi, dp, kp, 0, nudb::no_progress{}, ec);
341  if (ec)
342  Throw<nudb::system_error>(ec);
343  db_.open(dp, kp, lp, ec);
344  if (ec)
345  Throw<nudb::system_error>(ec);
346  }
347 
348  int
349  fdRequired() const override
350  {
351  return 3;
352  }
353 };
354 
355 //------------------------------------------------------------------------------
356 
357 class NuDBFactory : public Factory
358 {
359 public:
361  {
362  Manager::instance().insert(*this);
363  }
364 
365  ~NuDBFactory() override
366  {
367  Manager::instance().erase(*this);
368  }
369 
371  getName() const override
372  {
373  return "NuDB";
374  }
375 
378  size_t keyBytes,
379  Section const& keyValues,
380  std::size_t burstSize,
381  Scheduler& scheduler,
382  beast::Journal journal) override
383  {
384  return std::make_unique<NuDBBackend>(
385  keyBytes, keyValues, burstSize, scheduler, journal);
386  }
387 
390  size_t keyBytes,
391  Section const& keyValues,
392  std::size_t burstSize,
393  Scheduler& scheduler,
394  nudb::context& context,
395  beast::Journal journal) override
396  {
397  return std::make_unique<NuDBBackend>(
398  keyBytes, keyValues, burstSize, scheduler, context, journal);
399  }
400 };
401 
403 
404 } // namespace NodeStore
405 } // namespace ripple
ripple::Section
Holds a collection of configuration values.
Definition: BasicConfig.h:43
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:100
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:251
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:105
ripple::NodeStore::NuDBBackend::setDeletePath
void setDeletePath() override
Remove contents on disk upon destruction.
Definition: NuDBFactory.cpp:324
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:238
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:158
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:220
ripple::NodeStore::NuDBBackend::~NuDBBackend
~NuDBBackend() override
Definition: NuDBFactory.cpp:93
ripple::NodeStore::NuDBBackend::close
void close() override
Close the backend.
Definition: NuDBFactory.cpp:170
ripple::NodeStore::NuDBBackend::canFetchBatch
bool canFetchBatch() override
Return true if batch fetches are optimized.
Definition: NuDBFactory.cpp:214
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:349
ripple::NodeStore::nuDBFactory
static NuDBFactory nuDBFactory
Definition: NuDBFactory.cpp:402
ripple::NodeStore::NuDBBackend::keyBytes_
const size_t keyBytes_
Definition: NuDBFactory.cpp:48
ripple::NodeStore::NuDBBackend::sync
void sync() override
Definition: NuDBFactory.cpp:276
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:357
ripple::NodeStore::NuDBBackend::getWriteLoad
int getWriteLoad() override
Estimate the number of write operations pending.
Definition: NuDBFactory.cpp:318
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:164
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:365
ripple::NodeStore::NuDBBackend::verify
void verify() override
Perform consistency checks on database.
Definition: NuDBFactory.cpp:330
memory
ripple::NodeStore::NuDBBackend::getName
std::string getName() override
Get the human-readable name of this backend.
Definition: NuDBFactory.cpp:99
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:389
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:371
std
STL namespace.
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:377
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:263
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:211
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:281
ripple::NodeStore::Manager::instance
static Manager & instance()
Returns the instance of the manager singleton.
Definition: ManagerImp.cpp:128
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::NodeStore::NuDBBackend::fetch
Status fetch(void const *key, std::shared_ptr< NodeObject > *pno) override
Fetch a single object.
Definition: NuDBFactory.cpp:186
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:360
ripple::NodeStore::Backend
A backend used for the NodeStore.
Definition: Backend.h:39
std::chrono::steady_clock::now
T now(T... args)