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 
214  fetchBatch(std::vector<uint256 const*> const& hashes) override
215  {
217  results.reserve(hashes.size());
218  for (auto const& h : hashes)
219  {
221  Status status = fetch(h->begin(), &nObj);
222  if (status != ok)
223  results.push_back({});
224  else
225  results.push_back(nObj);
226  }
227 
228  return {results, ok};
229  }
230 
231  void
233  {
234  EncodedBlob e;
235  e.prepare(no);
236  nudb::error_code ec;
237  nudb::detail::buffer bf;
238  auto const result = nodeobject_compress(e.getData(), e.getSize(), bf);
239  db_.insert(e.getKey(), result.first, result.second, ec);
240  if (ec && ec != nudb::error::key_exists)
241  Throw<nudb::system_error>(ec);
242  }
243 
244  void
245  store(std::shared_ptr<NodeObject> const& no) override
246  {
247  BatchWriteReport report;
248  report.writeCount = 1;
249  auto const start = std::chrono::steady_clock::now();
250  do_insert(no);
251  report.elapsed = std::chrono::duration_cast<std::chrono::milliseconds>(
253  scheduler_.onBatchWrite(report);
254  }
255 
256  void
257  storeBatch(Batch const& batch) override
258  {
259  BatchWriteReport report;
260  report.writeCount = batch.size();
261  auto const start = std::chrono::steady_clock::now();
262  for (auto const& e : batch)
263  do_insert(e);
264  report.elapsed = std::chrono::duration_cast<std::chrono::milliseconds>(
266  scheduler_.onBatchWrite(report);
267  }
268 
269  void
270  sync() override
271  {
272  }
273 
274  void
276  {
277  auto const dp = db_.dat_path();
278  auto const kp = db_.key_path();
279  auto const lp = db_.log_path();
280  // auto const appnum = db_.appnum();
281  nudb::error_code ec;
282  db_.close(ec);
283  if (ec)
284  Throw<nudb::system_error>(ec);
285  nudb::visit(
286  dp,
287  [&](void const* key,
288  std::size_t key_bytes,
289  void const* data,
290  std::size_t size,
291  nudb::error_code&) {
292  nudb::detail::buffer bf;
293  auto const result = nodeobject_decompress(data, size, bf);
294  DecodedBlob decoded(key, result.first, result.second);
295  if (!decoded.wasOk())
296  {
297  ec = make_error_code(nudb::error::missing_value);
298  return;
299  }
300  f(decoded.createObject());
301  },
302  nudb::no_progress{},
303  ec);
304  if (ec)
305  Throw<nudb::system_error>(ec);
306  db_.open(dp, kp, lp, ec);
307  if (ec)
308  Throw<nudb::system_error>(ec);
309  }
310 
311  int
312  getWriteLoad() override
313  {
314  return 0;
315  }
316 
317  void
318  setDeletePath() override
319  {
320  deletePath_ = true;
321  }
322 
323  void
324  verify() override
325  {
326  auto const dp = db_.dat_path();
327  auto const kp = db_.key_path();
328  auto const lp = db_.log_path();
329  nudb::error_code ec;
330  db_.close(ec);
331  if (ec)
332  Throw<nudb::system_error>(ec);
333  nudb::verify_info vi;
334  nudb::verify<nudb::xxhasher>(vi, dp, kp, 0, nudb::no_progress{}, ec);
335  if (ec)
336  Throw<nudb::system_error>(ec);
337  db_.open(dp, kp, lp, ec);
338  if (ec)
339  Throw<nudb::system_error>(ec);
340  }
341 
342  int
343  fdRequired() const override
344  {
345  return 3;
346  }
347 };
348 
349 //------------------------------------------------------------------------------
350 
351 class NuDBFactory : public Factory
352 {
353 public:
355  {
356  Manager::instance().insert(*this);
357  }
358 
359  ~NuDBFactory() override
360  {
361  Manager::instance().erase(*this);
362  }
363 
365  getName() const override
366  {
367  return "NuDB";
368  }
369 
372  size_t keyBytes,
373  Section const& keyValues,
374  std::size_t burstSize,
375  Scheduler& scheduler,
376  beast::Journal journal) override
377  {
378  return std::make_unique<NuDBBackend>(
379  keyBytes, keyValues, burstSize, scheduler, journal);
380  }
381 
384  size_t keyBytes,
385  Section const& keyValues,
386  std::size_t burstSize,
387  Scheduler& scheduler,
388  nudb::context& context,
389  beast::Journal journal) override
390  {
391  return std::make_unique<NuDBBackend>(
392  keyBytes, keyValues, burstSize, scheduler, context, journal);
393  }
394 };
395 
397 
398 } // namespace NodeStore
399 } // 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:245
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:318
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:232
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:214
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::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:343
ripple::NodeStore::nuDBFactory
static NuDBFactory nuDBFactory
Definition: NuDBFactory.cpp:396
ripple::NodeStore::NuDBBackend::keyBytes_
const size_t keyBytes_
Definition: NuDBFactory.cpp:48
ripple::NodeStore::NuDBBackend::sync
void sync() override
Definition: NuDBFactory.cpp:270
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:351
ripple::NodeStore::NuDBBackend::getWriteLoad
int getWriteLoad() override
Estimate the number of write operations pending.
Definition: NuDBFactory.cpp:312
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:359
ripple::NodeStore::NuDBBackend::verify
void verify() override
Perform consistency checks on database.
Definition: NuDBFactory.cpp:324
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:383
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:365
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:371
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:257
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:275
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::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:354
ripple::NodeStore::Backend
A backend used for the NodeStore.
Definition: Backend.h:39
std::chrono::steady_clock::now
T now(T... args)