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::size_t currentType = 1;
42 
44  size_t const keyBytes_;
47  nudb::store db_;
50 
52  size_t keyBytes,
53  Section const& keyValues,
54  std::size_t burstSize,
55  Scheduler& scheduler,
56  beast::Journal journal)
57  : j_(journal)
58  , keyBytes_(keyBytes)
60  , name_(get<std::string>(keyValues, "path"))
61  , deletePath_(false)
62  , scheduler_(scheduler)
63  {
64  if (name_.empty())
65  Throw<std::runtime_error>(
66  "nodestore: Missing path in NuDB backend");
67  }
68 
70  size_t keyBytes,
71  Section const& keyValues,
72  std::size_t burstSize,
73  Scheduler& scheduler,
74  nudb::context& context,
75  beast::Journal journal)
76  : j_(journal)
77  , keyBytes_(keyBytes)
79  , name_(get<std::string>(keyValues, "path"))
80  , db_(context)
81  , deletePath_(false)
82  , scheduler_(scheduler)
83  {
84  if (name_.empty())
85  Throw<std::runtime_error>(
86  "nodestore: Missing path in NuDB backend");
87  }
88 
89  ~NuDBBackend() override
90  {
91  close();
92  }
93 
95  getName() override
96  {
97  return name_;
98  }
99 
100  void
101  open(bool createIfMissing) override
102  {
103  using namespace boost::filesystem;
104  if (db_.is_open())
105  {
106  assert(false);
107  JLOG(j_.error()) << "database is already open";
108  return;
109  }
110  auto const folder = path(name_);
111  auto const dp = (folder / "nudb.dat").string();
112  auto const kp = (folder / "nudb.key").string();
113  auto const lp = (folder / "nudb.log").string();
114  nudb::error_code ec;
115  if (createIfMissing)
116  {
117  create_directories(folder);
118  nudb::create<nudb::xxhasher>(
119  dp,
120  kp,
121  lp,
122  currentType,
123  nudb::make_salt(),
124  keyBytes_,
125  nudb::block_size(kp),
126  0.50,
127  ec);
128  if (ec == nudb::errc::file_exists)
129  ec = {};
130  if (ec)
131  Throw<nudb::system_error>(ec);
132  }
133  db_.open(dp, kp, lp, ec);
134  if (ec)
135  Throw<nudb::system_error>(ec);
136  if (db_.appnum() != currentType)
137  Throw<std::runtime_error>("nodestore: unknown appnum");
138  db_.set_burst(burstSize_);
139  }
140 
141  bool
142  isOpen() override
143  {
144  return db_.is_open();
145  }
146 
147  void
148  close() override
149  {
150  if (db_.is_open())
151  {
152  nudb::error_code ec;
153  db_.close(ec);
154  if (ec)
155  Throw<nudb::system_error>(ec);
156  if (deletePath_)
157  {
158  boost::filesystem::remove_all(name_);
159  }
160  }
161  }
162 
163  Status
164  fetch(void const* key, std::shared_ptr<NodeObject>* pno) override
165  {
166  Status status;
167  pno->reset();
168  nudb::error_code ec;
169  db_.fetch(
170  key,
171  [key, pno, &status](void const* data, std::size_t size) {
172  nudb::detail::buffer bf;
173  auto const result = nodeobject_decompress(data, size, bf);
174  DecodedBlob decoded(key, result.first, result.second);
175  if (!decoded.wasOk())
176  {
177  status = dataCorrupt;
178  return;
179  }
180  *pno = decoded.createObject();
181  status = ok;
182  },
183  ec);
184  if (ec == nudb::error::key_not_found)
185  return notFound;
186  if (ec)
187  Throw<nudb::system_error>(ec);
188  return status;
189  }
190 
191  bool
192  canFetchBatch() override
193  {
194  return false;
195  }
196 
198  fetchBatch(std::size_t n, void const* const* keys) override
199  {
200  Throw<std::runtime_error>("pure virtual called");
201  return {};
202  }
203 
204  void
206  {
207  EncodedBlob e;
208  e.prepare(no);
209  nudb::error_code ec;
210  nudb::detail::buffer bf;
211  auto const result = nodeobject_compress(e.getData(), e.getSize(), bf);
212  db_.insert(e.getKey(), result.first, result.second, ec);
213  if (ec && ec != nudb::error::key_exists)
214  Throw<nudb::system_error>(ec);
215  }
216 
217  void
218  store(std::shared_ptr<NodeObject> const& no) override
219  {
220  BatchWriteReport report;
221  report.writeCount = 1;
222  auto const start = std::chrono::steady_clock::now();
223  do_insert(no);
224  report.elapsed = std::chrono::duration_cast<std::chrono::milliseconds>(
226  scheduler_.onBatchWrite(report);
227  }
228 
229  void
230  storeBatch(Batch const& batch) override
231  {
232  BatchWriteReport report;
233  report.writeCount = batch.size();
234  auto const start = std::chrono::steady_clock::now();
235  for (auto const& e : batch)
236  do_insert(e);
237  report.elapsed = std::chrono::duration_cast<std::chrono::milliseconds>(
239  scheduler_.onBatchWrite(report);
240  }
241 
242  void
244  {
245  auto const dp = db_.dat_path();
246  auto const kp = db_.key_path();
247  auto const lp = db_.log_path();
248  // auto const appnum = db_.appnum();
249  nudb::error_code ec;
250  db_.close(ec);
251  if (ec)
252  Throw<nudb::system_error>(ec);
253  nudb::visit(
254  dp,
255  [&](void const* key,
256  std::size_t key_bytes,
257  void const* data,
258  std::size_t size,
259  nudb::error_code&) {
260  nudb::detail::buffer bf;
261  auto const result = nodeobject_decompress(data, size, bf);
262  DecodedBlob decoded(key, result.first, result.second);
263  if (!decoded.wasOk())
264  {
265  ec = make_error_code(nudb::error::missing_value);
266  return;
267  }
268  f(decoded.createObject());
269  },
270  nudb::no_progress{},
271  ec);
272  if (ec)
273  Throw<nudb::system_error>(ec);
274  db_.open(dp, kp, lp, ec);
275  if (ec)
276  Throw<nudb::system_error>(ec);
277  }
278 
279  int
280  getWriteLoad() override
281  {
282  return 0;
283  }
284 
285  void
286  setDeletePath() override
287  {
288  deletePath_ = true;
289  }
290 
291  void
292  verify() override
293  {
294  auto const dp = db_.dat_path();
295  auto const kp = db_.key_path();
296  auto const lp = db_.log_path();
297  nudb::error_code ec;
298  db_.close(ec);
299  if (ec)
300  Throw<nudb::system_error>(ec);
301  nudb::verify_info vi;
302  nudb::verify<nudb::xxhasher>(vi, dp, kp, 0, nudb::no_progress{}, ec);
303  if (ec)
304  Throw<nudb::system_error>(ec);
305  db_.open(dp, kp, lp, ec);
306  if (ec)
307  Throw<nudb::system_error>(ec);
308  }
309 
310  int
311  fdRequired() const override
312  {
313  return 3;
314  }
315 };
316 
317 //------------------------------------------------------------------------------
318 
319 class NuDBFactory : public Factory
320 {
321 public:
323  {
324  Manager::instance().insert(*this);
325  }
326 
327  ~NuDBFactory() override
328  {
329  Manager::instance().erase(*this);
330  }
331 
333  getName() const override
334  {
335  return "NuDB";
336  }
337 
340  size_t keyBytes,
341  Section const& keyValues,
342  std::size_t burstSize,
343  Scheduler& scheduler,
344  beast::Journal journal) override
345  {
346  return std::make_unique<NuDBBackend>(
347  keyBytes, keyValues, burstSize, scheduler, journal);
348  }
349 
352  size_t keyBytes,
353  Section const& keyValues,
354  std::size_t burstSize,
355  Scheduler& scheduler,
356  nudb::context& context,
357  beast::Journal journal) override
358  {
359  return std::make_unique<NuDBBackend>(
360  keyBytes, keyValues, burstSize, scheduler, context, journal);
361  }
362 };
363 
365 
366 } // namespace NodeStore
367 } // 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:32
ripple::NodeStore::NuDBBackend::deletePath_
std::atomic< bool > deletePath_
Definition: NuDBFactory.cpp:48
ripple::NodeStore::NuDBBackend::store
void store(std::shared_ptr< NodeObject > const &no) override
Store a single object.
Definition: NuDBFactory.cpp:218
ripple::NodeStore::EncodedBlob::getSize
std::size_t getSize() const noexcept
Definition: EncodedBlob.h:47
ripple::NodeStore::NuDBBackend::setDeletePath
void setDeletePath() override
Remove contents on disk upon destruction.
Definition: NuDBFactory.cpp:286
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.
ripple::NodeStore::NuDBBackend::do_insert
void do_insert(std::shared_ptr< NodeObject > const &no)
Definition: NuDBFactory.cpp:205
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:142
ripple::NodeStore::NuDBBackend::currentType
static constexpr std::size_t currentType
Definition: NuDBFactory.cpp:41
ripple::NodeStore::NuDBBackend::~NuDBBackend
~NuDBBackend() override
Definition: NuDBFactory.cpp:89
ripple::NodeStore::NuDBBackend::close
void close() override
Close the backend.
Definition: NuDBFactory.cpp:148
ripple::NodeStore::NuDBBackend::canFetchBatch
bool canFetchBatch() override
Return true if batch fetches are optimized.
Definition: NuDBFactory.cpp:192
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:311
ripple::NodeStore::nuDBFactory
static NuDBFactory nuDBFactory
Definition: NuDBFactory.cpp:364
ripple::NodeStore::NuDBBackend::keyBytes_
const size_t keyBytes_
Definition: NuDBFactory.cpp:44
std::shared_ptr::reset
T reset(T... args)
ripple::NodeStore::notFound
@ notFound
Definition: nodestore/Types.h:46
ripple::NodeStore::NuDBBackend::j_
const beast::Journal j_
Definition: NuDBFactory.cpp:43
ripple::NodeStore::Manager::insert
virtual void insert(Factory &factory)=0
Add a factory.
ripple::NodeStore::NuDBBackend::name_
const std::string name_
Definition: NuDBFactory.cpp:46
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:319
ripple::NodeStore::NuDBBackend::getWriteLoad
int getWriteLoad() override
Estimate the number of write operations pending.
Definition: NuDBFactory.cpp:280
beast::Journal::error
Stream error() const
Definition: Journal.h:333
cstdint
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
ripple::NodeStore::NuDBBackend::NuDBBackend
NuDBBackend(size_t keyBytes, Section const &keyValues, std::size_t burstSize, Scheduler &scheduler, beast::Journal journal)
Definition: NuDBFactory.cpp:51
std::atomic< bool >
ripple::NodeStore::Scheduler
Scheduling for asynchronous backend activity.
Definition: ripple/nodestore/Scheduler.h:61
ripple::NodeStore::NuDBBackend::open
void open(bool createIfMissing) override
Open the backend.
Definition: NuDBFactory.cpp:101
ripple::NodeStore::BatchWriteReport
Contains information about a batch write operation.
Definition: ripple/nodestore/Scheduler.h:45
ripple::NodeStore::NuDBFactory::~NuDBFactory
~NuDBFactory() override
Definition: NuDBFactory.cpp:327
ripple::NodeStore::NuDBBackend::verify
void verify() override
Perform consistency checks on database.
Definition: NuDBFactory.cpp:292
memory
ripple::NodeStore::NuDBBackend::getName
std::string getName() override
Get the human-readable name of this backend.
Definition: NuDBFactory.cpp:95
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:351
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:49
ripple::NodeStore::NuDBBackend::fetchBatch
std::vector< std::shared_ptr< NodeObject > > fetchBatch(std::size_t n, void const *const *keys) override
Fetch a batch synchronously.
Definition: NuDBFactory.cpp:198
ripple::NodeStore::NuDBFactory::getName
std::string getName() const override
Retrieve the name of this factory.
Definition: NuDBFactory.cpp:333
std
STL namespace.
cassert
ripple::NodeStore::NuDBBackend::burstSize_
const std::size_t burstSize_
Definition: NuDBFactory.cpp:45
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:339
std::string::empty
T empty(T... args)
ripple::NodeStore::NuDBBackend::db_
nudb::store db_
Definition: NuDBFactory.cpp:47
ripple::NodeStore::NuDBBackend::storeBatch
void storeBatch(Batch const &batch) override
Store a group of objects.
Definition: NuDBFactory.cpp:230
std::size_t
ripple::NodeStore::BatchWriteReport::writeCount
int writeCount
Definition: ripple/nodestore/Scheduler.h:50
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:243
ripple::NodeStore::Manager::instance
static Manager & instance()
Returns the instance of the manager singleton.
Definition: ManagerImp.cpp:119
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:49
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:69
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:164
ripple::get
T & get(EitherAmount &amt)
Definition: AmountSpec.h:116
ripple::NodeStore::EncodedBlob::prepare
void prepare(std::shared_ptr< NodeObject > const &object)
Definition: EncodedBlob.cpp:27
ripple::NodeStore::NuDBFactory::NuDBFactory
NuDBFactory()
Definition: NuDBFactory.cpp:322
ripple::NodeStore::Backend
A backend used for the NodeStore.
Definition: Backend.h:37
std::chrono::steady_clock::now
T now(T... args)