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