rippled
Database.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/app/ledger/Ledger.h>
21 #include <ripple/basics/ThreadUtilities.h>
22 #include <ripple/basics/chrono.h>
23 #include <ripple/json/json_value.h>
24 #include <ripple/nodestore/Database.h>
25 #include <ripple/protocol/HashPrefix.h>
26 #include <ripple/protocol/jss.h>
27 #include <chrono>
28 
29 namespace ripple {
30 namespace NodeStore {
31 
33  Scheduler& scheduler,
34  int readThreads,
35  Section const& config,
36  beast::Journal journal)
37  : j_(journal)
38  , scheduler_(scheduler)
39  , ledgersPerShard_(get<std::uint32_t>(
40  config,
41  "ledgers_per_shard",
43  , earliestLedgerSeq_(
44  get<std::uint32_t>(config, "earliest_seq", XRP_LEDGER_EARLIEST_SEQ))
45  , earliestShardIndex_((earliestLedgerSeq_ - 1) / ledgersPerShard_)
46  , requestBundle_(get<int>(config, "rq_bundle", 4))
47  , readThreads_(std::max(1, readThreads))
48 {
49  assert(readThreads != 0);
50 
51  if (ledgersPerShard_ == 0 || ledgersPerShard_ % 256 != 0)
52  Throw<std::runtime_error>("Invalid ledgers_per_shard");
53 
54  if (earliestLedgerSeq_ < 1)
55  Throw<std::runtime_error>("Invalid earliest_seq");
56 
57  if (requestBundle_ < 1 || requestBundle_ > 64)
58  Throw<std::runtime_error>("Invalid rq_bundle");
59 
60  for (int i = readThreads_.load(); i != 0; --i)
61  {
62  std::thread t(
63  [this](int i) {
65 
66  this_thread::set_name("prefetch " + std::to_string(i));
67 
68  decltype(read_) read;
69 
70  while (true)
71  {
72  {
74 
75  if (isStopping())
76  break;
77 
78  if (read_.empty())
79  {
81  readCondVar_.wait(lock);
83  }
84 
85  if (isStopping())
86  break;
87 
88  // extract multiple object at a time to minimize the
89  // overhead of acquiring the mutex.
90  for (int cnt = 0;
91  !read_.empty() && cnt != requestBundle_;
92  ++cnt)
93  read.insert(read_.extract(read_.begin()));
94  }
95 
96  for (auto it = read.begin(); it != read.end(); ++it)
97  {
98  assert(!it->second.empty());
99 
100  auto const& hash = it->first;
101  auto const& data = it->second;
102  auto const seqn = data[0].first;
103 
104  auto obj =
105  fetchNodeObject(hash, seqn, FetchType::async);
106 
107  // This could be further optimized: if there are
108  // multiple requests for sequence numbers mapping to
109  // multiple databases by sorting requests such that all
110  // indices mapping to the same database are grouped
111  // together and serviced by a single read.
112  for (auto const& req : data)
113  {
114  req.second(
115  (seqn == req.first) || isSameDB(req.first, seqn)
116  ? obj
117  : fetchNodeObject(
118  hash, req.first, FetchType::async));
119  }
120  }
121 
122  read.clear();
123  }
124 
125  --runningThreads_;
126  --readThreads_;
127  },
128  i);
129  t.detach();
130  }
131 }
132 
134 {
135  // NOTE!
136  // Any derived class should call the stop() method in its
137  // destructor. Otherwise, occasionally, the derived class may
138  // crash during shutdown when its members are accessed by one of
139  // these threads after the derived class is destroyed but before
140  // this base class is destroyed.
141  stop();
142 }
143 
144 bool
146 {
147  return readStopping_.load(std::memory_order_relaxed);
148 }
149 
151 Database::maxLedgers(std::uint32_t shardIndex) const noexcept
152 {
153  if (shardIndex > earliestShardIndex_)
154  return ledgersPerShard_;
155 
156  if (shardIndex == earliestShardIndex_)
157  return lastLedgerSeq(shardIndex) - firstLedgerSeq(shardIndex) + 1;
158 
159  assert(!"Invalid shard index");
160  return 0;
161 }
162 
163 void
165 {
166  {
168 
169  if (!readStopping_.exchange(true, std::memory_order_relaxed))
170  {
171  JLOG(j_.debug()) << "Clearing read queue because of stop request";
172  read_.clear();
174  }
175  }
176 
177  JLOG(j_.debug()) << "Waiting for stop request to complete...";
178 
179  using namespace std::chrono;
180 
181  auto const start = steady_clock::now();
182 
183  while (readThreads_.load() != 0)
184  {
185  assert(steady_clock::now() - start < 30s);
187  }
188 
189  JLOG(j_.debug()) << "Stop request completed in "
190  << duration_cast<std::chrono::milliseconds>(
191  steady_clock::now() - start)
192  .count()
193  << " millseconds";
194 }
195 
196 void
198  uint256 const& hash,
199  std::uint32_t ledgerSeq,
200  std::function<void(std::shared_ptr<NodeObject> const&)>&& cb)
201 {
203 
204  if (!isStopping())
205  {
206  read_[hash].emplace_back(ledgerSeq, std::move(cb));
208  }
209 }
210 
211 void
213 {
214  Batch batch;
216  auto storeBatch = [&, fname = __func__]() {
217  try
218  {
219  dstBackend.storeBatch(batch);
220  }
221  catch (std::exception const& e)
222  {
223  JLOG(j_.error()) << "Exception caught in function " << fname
224  << ". Error: " << e.what();
225  return;
226  }
227 
228  std::uint64_t sz{0};
229  for (auto const& nodeObject : batch)
230  sz += nodeObject->getData().size();
231  storeStats(batch.size(), sz);
232  batch.clear();
233  };
234 
235  srcDB.for_each([&](std::shared_ptr<NodeObject> nodeObject) {
236  assert(nodeObject);
237  if (!nodeObject) // This should never happen
238  return;
239 
240  batch.emplace_back(std::move(nodeObject));
241  if (batch.size() >= batchWritePreallocationSize)
242  storeBatch();
243  });
244 
245  if (!batch.empty())
246  storeBatch();
247 }
248 
249 // Perform a fetch and report the time it took
252  uint256 const& hash,
253  std::uint32_t ledgerSeq,
254  FetchType fetchType,
255  bool duplicate)
256 {
257  FetchReport fetchReport(fetchType);
258 
259  using namespace std::chrono;
260  auto const begin{steady_clock::now()};
261 
262  auto nodeObject{fetchNodeObject(hash, ledgerSeq, fetchReport, duplicate)};
263  auto dur = steady_clock::now() - begin;
264  fetchDurationUs_ += duration_cast<microseconds>(dur).count();
265  if (nodeObject)
266  {
267  ++fetchHitCount_;
268  fetchSz_ += nodeObject->getData().size();
269  }
271 
272  fetchReport.elapsed = duration_cast<milliseconds>(dur);
273  scheduler_.onFetch(fetchReport);
274  return nodeObject;
275 }
276 
277 bool
279  Ledger const& srcLedger,
280  std::shared_ptr<Backend> dstBackend)
281 {
282  auto fail = [&](std::string const& msg) {
283  JLOG(j_.error()) << "Source ledger sequence " << srcLedger.info().seq
284  << ". " << msg;
285  return false;
286  };
287 
288  if (srcLedger.info().hash.isZero())
289  return fail("Invalid hash");
290  if (srcLedger.info().accountHash.isZero())
291  return fail("Invalid account hash");
292 
293  auto& srcDB = const_cast<Database&>(srcLedger.stateMap().family().db());
294  if (&srcDB == this)
295  return fail("Source and destination databases are the same");
296 
297  Batch batch;
299  auto storeBatch = [&, fname = __func__]() {
300  std::uint64_t sz{0};
301  for (auto const& nodeObject : batch)
302  sz += nodeObject->getData().size();
303 
304  try
305  {
306  dstBackend->storeBatch(batch);
307  }
308  catch (std::exception const& e)
309  {
310  fail(
311  std::string("Exception caught in function ") + fname +
312  ". Error: " + e.what());
313  return false;
314  }
315 
316  storeStats(batch.size(), sz);
317  batch.clear();
318  return true;
319  };
320 
321  // Store ledger header
322  {
323  Serializer s(sizeof(std::uint32_t) + sizeof(LedgerInfo));
325  addRaw(srcLedger.info(), s);
326  auto nObj = NodeObject::createObject(
327  hotLEDGER, std::move(s.modData()), srcLedger.info().hash);
328  batch.emplace_back(std::move(nObj));
329  }
330 
331  bool error = false;
332  auto visit = [&](SHAMapTreeNode& node) {
333  if (!isStopping())
334  {
335  if (auto nodeObject = srcDB.fetchNodeObject(
336  node.getHash().as_uint256(), srcLedger.info().seq))
337  {
338  batch.emplace_back(std::move(nodeObject));
339  if (batch.size() < batchWritePreallocationSize || storeBatch())
340  return true;
341  }
342  }
343 
344  error = true;
345  return false;
346  };
347 
348  // Store the state map
349  if (srcLedger.stateMap().getHash().isNonZero())
350  {
351  if (!srcLedger.stateMap().isValid())
352  return fail("Invalid state map");
353 
354  srcLedger.stateMap().snapShot(false)->visitNodes(visit);
355  if (error)
356  return fail("Failed to store state map");
357  }
358 
359  // Store the transaction map
360  if (srcLedger.info().txHash.isNonZero())
361  {
362  if (!srcLedger.txMap().isValid())
363  return fail("Invalid transaction map");
364 
365  srcLedger.txMap().snapShot(false)->visitNodes(visit);
366  if (error)
367  return fail("Failed to store transaction map");
368  }
369 
370  if (!batch.empty() && !storeBatch())
371  return fail("Failed to store");
372 
373  return true;
374 }
375 
376 void
378 {
379  assert(obj.isObject());
380 
381  {
383  obj["read_queue"] = static_cast<Json::UInt>(read_.size());
384  }
385 
386  obj["read_threads_total"] = readThreads_.load();
387  obj["read_threads_running"] = runningThreads_.load();
388  obj["read_request_bundle"] = requestBundle_;
389 
390  obj[jss::node_writes] = std::to_string(storeCount_);
391  obj[jss::node_reads_total] = std::to_string(fetchTotalCount_);
392  obj[jss::node_reads_hit] = std::to_string(fetchHitCount_);
393  obj[jss::node_written_bytes] = std::to_string(storeSz_);
394  obj[jss::node_read_bytes] = std::to_string(fetchSz_);
395  obj[jss::node_reads_duration_us] = std::to_string(fetchDurationUs_);
396 
397  if (auto c = getCounters())
398  {
399  obj[jss::node_read_errors] = std::to_string(c->readErrors);
400  obj[jss::node_read_retries] = std::to_string(c->readRetries);
401  obj[jss::node_write_retries] = std::to_string(c->writeRetries);
402  obj[jss::node_writes_delayed] = std::to_string(c->writesDelayed);
403  obj[jss::node_writes_duration_us] = std::to_string(c->writeDurationUs);
404  }
405 }
406 
407 } // namespace NodeStore
408 } // namespace ripple
ripple::Section
Holds a collection of configuration values.
Definition: BasicConfig.h:42
ripple::HashPrefix::ledgerMaster
@ ledgerMaster
ledger master data for signing
ripple::SHAMap::isValid
bool isValid() const
Definition: SHAMap.h:625
ripple::DEFAULT_LEDGERS_PER_SHARD
static constexpr std::uint32_t DEFAULT_LEDGERS_PER_SHARD
The number of ledgers in a shard.
Definition: SystemParameters.h:76
ripple::NodeStore::Database::fetchDurationUs_
std::atomic< std::uint64_t > fetchDurationUs_
Definition: Database.h:360
ripple::NodeStore::Database
Persistency layer for NodeObject.
Definition: Database.h:51
ripple::NodeStore::read
void read(nudb::detail::istream &is, std::size_t &u)
Definition: varint.h:120
Json::Value::isObject
bool isObject() const
Definition: json_value.cpp:1027
std::string
STL class.
std::shared_ptr< NodeObject >
ripple::SHAMap::getHash
SHAMapHash getHash() const
Definition: SHAMap.cpp:852
std::exception
STL class.
ripple::base_uint::isNonZero
bool isNonZero() const
Definition: base_uint.h:537
ripple::SHAMap::family
Family const & family() const
Definition: SHAMap.h:143
ripple::Serializer::modData
Blob & modData()
Definition: Serializer.h:179
ripple::NodeStore::Database::ledgersPerShard_
const std::uint32_t ledgersPerShard_
Definition: Database.h:314
std::vector::reserve
T reserve(T... args)
Json::UInt
unsigned int UInt
Definition: json_forwards.h:27
std::vector< std::shared_ptr< NodeObject > >
std::vector::size
T size(T... args)
ripple::NodeStore::Database::read_
std::map< uint256, std::vector< std::pair< std::uint32_t, std::function< void(std::shared_ptr< NodeObject > const &)> > > > read_
Definition: Database.h:372
ripple::NodeStore::Database::requestBundle_
const int requestBundle_
Definition: Database.h:330
ripple::NodeObject::createObject
static std::shared_ptr< NodeObject > createObject(NodeObjectType type, Blob &&data, uint256 const &hash)
Create an object from fields.
Definition: NodeObject.cpp:37
ripple::this_thread::set_name
void set_name(std::string s)
ripple::NodeStore::Database::fetchTotalCount_
std::atomic< std::uint64_t > fetchTotalCount_
Definition: Database.h:359
ripple::NodeStore::Database::asyncFetch
virtual void asyncFetch(uint256 const &hash, std::uint32_t ledgerSeq, std::function< void(std::shared_ptr< NodeObject > const &)> &&callback)
Fetch an object without waiting.
Definition: Database.cpp:197
ripple::LedgerHeader::seq
LedgerIndex seq
Definition: LedgerHeader.h:41
ripple::NodeStore::Database::fetchSz_
std::atomic< std::uint32_t > fetchSz_
Definition: Database.h:306
std::lock_guard
STL class.
ripple::NodeStore::Database::stop
virtual void stop()
Definition: Database.cpp:164
ripple::NodeStore::FetchReport
Contains information about a fetch operation.
Definition: ripple/nodestore/Scheduler.h:32
ripple::LedgerHeader::accountHash
uint256 accountHash
Definition: LedgerHeader.h:51
std::function
ripple::NodeStore::Database::readStopping_
std::atomic< bool > readStopping_
Definition: Database.h:374
ripple::NodeStore::Database::readThreads_
std::atomic< int > readThreads_
Definition: Database.h:375
ripple::SHAMapHash::isNonZero
bool isNonZero() const
Definition: SHAMapHash.h:58
ripple::SHAMap::snapShot
std::shared_ptr< SHAMap > snapShot(bool isMutable) const
Definition: SHAMap.cpp:88
ripple::NodeStore::Database::fetchHitCount_
std::atomic< std::uint32_t > fetchHitCount_
Definition: Database.h:305
ripple::LedgerHeader::txHash
uint256 txHash
Definition: LedgerHeader.h:50
ripple::Family::db
virtual NodeStore::Database & db()=0
std::thread::detach
T detach(T... args)
ripple::NodeStore::Database::storeCount_
std::atomic< std::uint64_t > storeCount_
Definition: Database.h:357
ripple::NodeStore::batchWritePreallocationSize
@ batchWritePreallocationSize
Definition: nodestore/Types.h:34
ripple::NodeStore::Database::readLock_
std::mutex readLock_
Definition: Database.h:363
ripple::LedgerHeader::hash
uint256 hash
Definition: LedgerHeader.h:49
ripple::base_uint< 256 >
ripple::NodeStore::Database::storeSz_
std::atomic< std::uint64_t > storeSz_
Definition: Database.h:358
ripple::Ledger::info
LedgerInfo const & info() const override
Returns information about the ledger.
Definition: Ledger.h:152
ripple::NodeStore::Database::importInternal
void importInternal(Backend &dstBackend, Database &srcDB)
Definition: Database.cpp:212
ripple::base_uint::isZero
bool isZero() const
Definition: base_uint.h:532
std::thread
STL class.
ripple::Ledger
Holds a ledger.
Definition: Ledger.h:76
std::atomic::load
T load(T... args)
chrono
ripple::Ledger::stateMap
SHAMap const & stateMap() const
Definition: Ledger.h:310
std::unique_lock
STL class.
ripple::NodeStore::Database::readCondVar_
std::condition_variable readCondVar_
Definition: Database.h:364
ripple::SHAMapTreeNode
Definition: SHAMapTreeNode.h:53
std::to_string
T to_string(T... args)
beast::Journal::error
Stream error() const
Definition: Journal.h:333
ripple::NodeStore::Scheduler::onFetch
virtual void onFetch(FetchReport const &report)=0
Reports completion of a fetch Allows the scheduler to monitor the node store's performance.
beast::Journal
A generic endpoint for log messages.
Definition: Journal.h:58
std::uint32_t
std::condition_variable::wait
T wait(T... args)
ripple::NodeStore::Scheduler
Scheduling for asynchronous backend activity.
Definition: ripple/nodestore/Scheduler.h:60
ripple::NodeStore::Database::~Database
virtual ~Database()
Destroy the node store.
Definition: Database.cpp:133
ripple::NodeStore::Database::for_each
virtual void for_each(std::function< void(std::shared_ptr< NodeObject >)> f)=0
Visit every object in the database This is usually called during import.
ripple::NodeStore::Database::storeStats
void storeStats(std::uint64_t count, std::uint64_t sz)
Definition: Database.h:333
ripple::LedgerHeader
Information about the notional ledger backing the view.
Definition: LedgerHeader.h:33
ripple::NodeStore::FetchType
FetchType
Definition: ripple/nodestore/Scheduler.h:29
ripple::NodeStore::Database::isStopping
bool isStopping() const
Definition: Database.cpp:145
std::condition_variable::notify_one
T notify_one(T... args)
ripple::Serializer
Definition: Serializer.h:40
ripple::Ledger::txMap
SHAMap const & txMap() const
Definition: Ledger.h:322
ripple::NodeStore::FetchType::async
@ async
ripple::NodeStore::Database::getCounters
virtual std::optional< Backend::Counters< std::uint64_t > > getCounters() const
Retrieve backend read and write stats.
Definition: Database.h:401
std::vector::emplace_back
T emplace_back(T... args)
ripple
Use hash_* containers for keys that do not need a cryptographically secure hashing algorithm.
Definition: RCLCensorshipDetector.h:29
ripple::NodeStore::Database::storeLedger
virtual bool storeLedger(std::shared_ptr< Ledger const > const &srcLedger)=0
Store a ledger from a different database.
std::atomic::exchange
T exchange(T... args)
ripple::addRaw
void addRaw(LedgerHeader const &info, Serializer &s, bool includeHash)
Definition: protocol/impl/LedgerHeader.cpp:25
ripple::NodeStore::Database::j_
const beast::Journal j_
Definition: Database.h:301
std
STL namespace.
ripple::XRP_LEDGER_EARLIEST_SEQ
static constexpr std::uint32_t XRP_LEDGER_EARLIEST_SEQ
The XRP ledger network's earliest allowed sequence.
Definition: SystemParameters.h:69
ripple::NodeStore::Database::getCountsJson
void getCountsJson(Json::Value &obj)
Definition: Database.cpp:377
ripple::NodeStore::Database::earliestLedgerSeq_
const std::uint32_t earliestLedgerSeq_
Definition: Database.h:322
std::vector::empty
T empty(T... args)
beast::Journal::debug
Stream debug() const
Definition: Journal.h:315
ripple::hotLEDGER
@ hotLEDGER
Definition: NodeObject.h:34
ripple::Serializer::add32
int add32(std::uint32_t i)
Definition: Serializer.cpp:38
ripple::NodeStore::Database::fetchNodeObject
std::shared_ptr< NodeObject > fetchNodeObject(uint256 const &hash, std::uint32_t ledgerSeq=0, FetchType fetchType=FetchType::synchronous, bool duplicate=false)
Fetch a node object.
Definition: Database.cpp:251
ripple::NodeStore::Database::scheduler_
Scheduler & scheduler_
Definition: Database.h:302
ripple::NodeStore::Database::runningThreads_
std::atomic< int > runningThreads_
Definition: Database.h:376
ripple::NodeStore::Database::isSameDB
virtual bool isSameDB(std::uint32_t s1, std::uint32_t s2)=0
ripple::NodeStore::Database::maxLedgers
std::uint32_t maxLedgers(std::uint32_t shardIndex) const noexcept
Calculates the maximum ledgers for a given shard index.
Definition: Database.cpp:151
ripple::NodeStore::Backend::storeBatch
virtual void storeBatch(Batch const &batch)=0
Store a group of objects.
ripple::NodeStore::FetchReport::elapsed
std::chrono::milliseconds elapsed
Definition: ripple/nodestore/Scheduler.h:38
std::condition_variable::notify_all
T notify_all(T... args)
std::this_thread::yield
T yield(T... args)
std::exception::what
T what(T... args)
Json::Value
Represents a JSON value.
Definition: json_value.h:145
ripple::get
T & get(EitherAmount &amt)
Definition: AmountSpec.h:118
ripple::NodeStore::Database::Database
Database()=delete
std::chrono
ripple::NodeStore::Backend
A backend used for the NodeStore.
Definition: Backend.h:39