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/chrono.h>
22 #include <ripple/beast/core/CurrentThreadName.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  std::string name,
34  Stoppable& parent,
35  Scheduler& scheduler,
36  int readThreads,
37  Section const& config,
38  beast::Journal journal)
39  : Stoppable(name, parent.getRoot())
40  , j_(journal)
41  , scheduler_(scheduler)
42  , earliestLedgerSeq_(
43  get<std::uint32_t>(config, "earliest_seq", XRP_LEDGER_EARLIEST_SEQ))
44 {
45  if (earliestLedgerSeq_ < 1)
46  Throw<std::runtime_error>("Invalid earliest_seq");
47 
48  while (readThreads-- > 0)
50 }
51 
53 {
54  // NOTE!
55  // Any derived class should call the stopReadThreads() method in its
56  // destructor. Otherwise, occasionally, the derived class may
57  // crash during shutdown when its members are accessed by one of
58  // these threads after the derived class is destroyed but before
59  // this base class is destroyed.
61 }
62 
63 void
65 {
66  // After stop time we can no longer use the JobQueue for background
67  // reads. Join the background read threads.
69 }
70 
71 void
73 {
74  stopped();
75 }
76 
77 void
79 {
80  {
82  if (readShut_) // Only stop threads once.
83  return;
84 
85  readShut_ = true;
87  }
88 
89  for (auto& e : readThreads_)
90  e.join();
91 }
92 
93 void
95  uint256 const& hash,
96  std::uint32_t ledgerSeq,
97  std::function<void(std::shared_ptr<NodeObject> const&)>&& cb)
98 {
99  // Post a read
101  read_[hash].emplace_back(ledgerSeq, std::move(cb));
103 }
104 
105 void
107 {
108  Batch batch;
110  auto storeBatch = [&]() {
111  try
112  {
113  dstBackend.storeBatch(batch);
114  }
115  catch (std::exception const& e)
116  {
117  JLOG(j_.error()) << "Exception caught in function " << __func__
118  << ". Error: " << e.what();
119  return;
120  }
121 
122  std::uint64_t sz{0};
123  for (auto const& nodeObject : batch)
124  sz += nodeObject->getData().size();
125  storeStats(batch.size(), sz);
126  batch.clear();
127  };
128 
129  srcDB.for_each([&](std::shared_ptr<NodeObject> nodeObject) {
130  assert(nodeObject);
131  if (!nodeObject) // This should never happen
132  return;
133 
134  batch.emplace_back(std::move(nodeObject));
135  if (batch.size() >= batchWritePreallocationSize)
136  storeBatch();
137  });
138 
139  if (!batch.empty())
140  storeBatch();
141 }
142 
143 // Perform a fetch and report the time it took
146  uint256 const& hash,
147  std::uint32_t ledgerSeq,
148  FetchType fetchType)
149 {
150  FetchReport fetchReport(fetchType);
151 
152  using namespace std::chrono;
153  auto const begin{steady_clock::now()};
154 
155  auto nodeObject{fetchNodeObject(hash, ledgerSeq, fetchReport)};
156  if (nodeObject)
157  {
158  ++fetchHitCount_;
159  fetchSz_ += nodeObject->getData().size();
160  }
162 
163  fetchReport.elapsed =
164  duration_cast<milliseconds>(steady_clock::now() - begin);
165  scheduler_.onFetch(fetchReport);
166  return nodeObject;
167 }
168 
169 bool
171  Ledger const& srcLedger,
172  std::shared_ptr<Backend> dstBackend)
173 {
174  auto fail = [&](std::string const& msg) {
175  JLOG(j_.error()) << "Source ledger sequence " << srcLedger.info().seq
176  << ". " << msg;
177  return false;
178  };
179 
180  if (srcLedger.info().hash.isZero())
181  return fail("Invalid hash");
182  if (srcLedger.info().accountHash.isZero())
183  return fail("Invalid account hash");
184 
185  auto& srcDB = const_cast<Database&>(srcLedger.stateMap().family().db());
186  if (&srcDB == this)
187  return fail("Source and destination databases are the same");
188 
189  Batch batch;
191  auto storeBatch = [&]() {
192  std::uint64_t sz{0};
193  for (auto const& nodeObject : batch)
194  sz += nodeObject->getData().size();
195 
196  try
197  {
198  dstBackend->storeBatch(batch);
199  }
200  catch (std::exception const& e)
201  {
202  fail(
203  std::string("Exception caught in function ") + __func__ +
204  ". Error: " + e.what());
205  return false;
206  }
207 
208  storeStats(batch.size(), sz);
209  batch.clear();
210  return true;
211  };
212 
213  // Store ledger header
214  {
215  Serializer s(sizeof(std::uint32_t) + sizeof(LedgerInfo));
217  addRaw(srcLedger.info(), s);
218  auto nObj = NodeObject::createObject(
219  hotLEDGER, std::move(s.modData()), srcLedger.info().hash);
220  batch.emplace_back(std::move(nObj));
221  }
222 
223  bool error = false;
224  auto visit = [&](SHAMapTreeNode& node) {
225  if (!isStopping())
226  {
227  if (auto nodeObject = srcDB.fetchNodeObject(
228  node.getHash().as_uint256(), srcLedger.info().seq))
229  {
230  batch.emplace_back(std::move(nodeObject));
231  if (batch.size() < batchWritePreallocationSize || storeBatch())
232  return true;
233  }
234  }
235 
236  error = true;
237  return false;
238  };
239 
240  // Store the state map
241  if (srcLedger.stateMap().getHash().isNonZero())
242  {
243  if (!srcLedger.stateMap().isValid())
244  return fail("Invalid state map");
245 
246  srcLedger.stateMap().snapShot(false)->visitNodes(visit);
247  if (error)
248  return fail("Failed to store state map");
249  }
250 
251  // Store the transaction map
252  if (srcLedger.info().txHash.isNonZero())
253  {
254  if (!srcLedger.txMap().isValid())
255  return fail("Invalid transaction map");
256 
257  srcLedger.txMap().snapShot(false)->visitNodes(visit);
258  if (error)
259  return fail("Failed to store transaction map");
260  }
261 
262  if (!batch.empty() && !storeBatch())
263  return fail("Failed to store");
264 
265  return true;
266 }
267 
268 // Entry point for async read threads
269 void
271 {
272  beast::setCurrentThreadName("prefetch");
273  while (true)
274  {
275  uint256 lastHash;
279  entry;
280 
281  {
283  while (!readShut_ && read_.empty())
284  {
285  // All work is done
286  readCondVar_.wait(lock);
287  }
288  if (readShut_)
289  break;
290 
291  // Read in key order to make the back end more efficient
292  auto it = read_.lower_bound(readLastHash_);
293  if (it == read_.end())
294  {
295  // start over from the beginning
296  it = read_.begin();
297  }
298  lastHash = it->first;
299  entry = std::move(it->second);
300  read_.erase(it);
301  readLastHash_ = lastHash;
302  }
303 
304  auto seq = entry[0].first;
305  auto obj = fetchNodeObject(lastHash, seq, FetchType::async);
306 
307  for (auto const& req : entry)
308  {
309  if ((seq == req.first) || isSameDB(req.first, seq))
310  req.second(obj);
311  else
312  req.second(
313  fetchNodeObject(lastHash, req.first, FetchType::async));
314  }
315  }
316 }
317 
318 void
320 {
321  assert(obj.isObject());
322  obj[jss::node_writes] = std::to_string(storeCount_);
323  obj[jss::node_reads_total] = std::to_string(fetchTotalCount_);
324  obj[jss::node_reads_hit] = std::to_string(fetchHitCount_);
325  obj[jss::node_written_bytes] = std::to_string(storeSz_);
326  obj[jss::node_read_bytes] = std::to_string(fetchSz_);
327  obj[jss::node_reads_duration_us] = std::to_string(fetchDurationUs_);
328  auto const& c = getBackend().counters();
329  obj[jss::node_read_errors] = std::to_string(c.readErrors);
330  obj[jss::node_read_retries] = std::to_string(c.readRetries);
331  obj[jss::node_write_retries] = std::to_string(c.writeRetries);
332  obj[jss::node_writes_delayed] = std::to_string(c.writesDelayed);
333  obj[jss::node_writes_duration_us] = std::to_string(c.writeDurationUs);
334 }
335 
336 } // namespace NodeStore
337 } // namespace ripple
ripple::Section
Holds a collection of configuration values.
Definition: BasicConfig.h:43
ripple::HashPrefix::ledgerMaster
@ ledgerMaster
ledger master data for signing
ripple::NodeStore::Database::readLastHash_
uint256 readLastHash_
Definition: Database.h:300
ripple::SHAMap::isValid
bool isValid() const
Definition: SHAMap.h:558
ripple::NodeStore::Database::fetchDurationUs_
std::atomic< std::uint64_t > fetchDurationUs_
Definition: Database.h:285
ripple::NodeStore::Database
Persistency layer for NodeObject.
Definition: Database.h:52
Json::Value::isObject
bool isObject() const
Definition: json_value.cpp:1027
std::string
STL class.
ripple::NodeStore::Database::readShut_
bool readShut_
Definition: Database.h:303
std::shared_ptr< NodeObject >
ripple::SHAMap::getHash
SHAMapHash getHash() const
Definition: SHAMap.cpp:783
std::exception
STL class.
ripple::base_uint::isNonZero
bool isNonZero() const
Definition: base_uint.h:444
ripple::Stoppable::stopped
void stopped()
Called by derived classes to indicate that the stoppable has stopped.
Definition: Stoppable.cpp:72
ripple::SHAMap::family
Family const & family() const
Definition: SHAMap.h:139
ripple::Serializer::modData
Blob & modData()
Definition: Serializer.h:176
std::pair
std::vector::reserve
T reserve(T... args)
ripple::LedgerInfo::hash
uint256 hash
Definition: ReadView.h:100
ripple::addRaw
void addRaw(LedgerInfo const &info, Serializer &s, bool includeHash)
Definition: View.cpp:43
std::vector< std::shared_ptr< NodeObject > >
std::vector::size
T size(T... args)
ripple::NodeStore::Backend::counters
virtual Counters const & counters() const =0
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:297
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::NodeStore::Database::getBackend
virtual Backend & getBackend()=0
ripple::NodeStore::Database::fetchTotalCount_
std::atomic< std::uint64_t > fetchTotalCount_
Definition: Database.h:284
ripple::NodeStore::Database::asyncFetch
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:94
ripple::NodeStore::Database::fetchSz_
std::atomic< std::uint32_t > fetchSz_
Definition: Database.h:248
std::lock_guard
STL class.
ripple::NodeStore::FetchReport
Contains information about a fetch operation.
Definition: ripple/nodestore/Scheduler.h:32
std::function
ripple::LedgerInfo::seq
LedgerIndex seq
Definition: ReadView.h:92
ripple::SHAMapHash::isNonZero
bool isNonZero() const
Definition: SHAMapTreeNode.h:73
ripple::SHAMap::snapShot
std::shared_ptr< SHAMap > snapShot(bool isMutable) const
Definition: SHAMap.cpp:70
ripple::NodeStore::Database::fetchHitCount_
std::atomic< std::uint32_t > fetchHitCount_
Definition: Database.h:247
ripple::Family::db
virtual NodeStore::Database & db()=0
ripple::LedgerInfo::txHash
uint256 txHash
Definition: ReadView.h:101
ripple::NodeStore::Database::storeCount_
std::atomic< std::uint64_t > storeCount_
Definition: Database.h:282
ripple::NodeStore::batchWritePreallocationSize
@ batchWritePreallocationSize
Definition: nodestore/Types.h:34
ripple::NodeStore::Database::readLock_
std::mutex readLock_
Definition: Database.h:288
ripple::NodeStore::Database::onChildrenStopped
void onChildrenStopped() override
Override called when all children have stopped.
Definition: Database.cpp:72
ripple::base_uint< 256 >
ripple::NodeStore::Database::storeSz_
std::atomic< std::uint64_t > storeSz_
Definition: Database.h:283
ripple::Ledger::info
LedgerInfo const & info() const override
Returns information about the ledger.
Definition: Ledger.h:149
ripple::Stoppable
Provides an interface for starting and stopping.
Definition: Stoppable.h:201
ripple::NodeStore::Database::importInternal
void importInternal(Backend &dstBackend, Database &srcDB)
Definition: Database.cpp:106
ripple::base_uint::isZero
bool isZero() const
Definition: base_uint.h:439
ripple::Ledger
Holds a ledger.
Definition: Ledger.h:77
chrono
ripple::Ledger::stateMap
SHAMap const & stateMap() const
Definition: Ledger.h:308
std::unique_lock
STL class.
ripple::NodeStore::Database::readCondVar_
std::condition_variable readCondVar_
Definition: Database.h:289
ripple::SHAMapTreeNode
Definition: SHAMapTreeNode.h:133
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::fetchNodeObject
std::shared_ptr< NodeObject > fetchNodeObject(uint256 const &hash, std::uint32_t ledgerSeq=0, FetchType fetchType=FetchType::synchronous)
Fetch a node object.
Definition: Database.cpp:145
ripple::NodeStore::Database::~Database
virtual ~Database()
Destroy the node store.
Definition: Database.cpp:52
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:254
ripple::NodeStore::FetchType
FetchType
Definition: ripple/nodestore/Scheduler.h:29
std::condition_variable::notify_one
T notify_one(T... args)
ripple::NodeStore::Database::threadEntry
void threadEntry()
Definition: Database.cpp:270
ripple::Serializer
Definition: Serializer.h:39
ripple::NodeStore::Database::stopReadThreads
void stopReadThreads()
Definition: Database.cpp:78
ripple::Ledger::txMap
SHAMap const & txMap() const
Definition: Ledger.h:320
ripple::NodeStore::FetchType::async
@ async
beast::setCurrentThreadName
void setCurrentThreadName(std::string_view name)
Changes the name of the caller thread.
Definition: CurrentThreadName.cpp:119
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::onStop
void onStop() override
Override called when the stop notification is issued.
Definition: Database.cpp:64
ripple::NodeStore::Database::storeLedger
virtual bool storeLedger(std::shared_ptr< Ledger const > const &srcLedger)=0
Store a ledger from a different database.
ripple::NodeStore::Database::j_
const beast::Journal j_
Definition: Database.h:243
ripple::NodeStore::Database::readThreads_
std::vector< std::thread > readThreads_
Definition: Database.h:302
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:61
ripple::NodeStore::Database::getCountsJson
void getCountsJson(Json::Value &obj)
Definition: Database.cpp:319
ripple::NodeStore::Database::earliestLedgerSeq_
const std::uint32_t earliestLedgerSeq_
Definition: Database.h:307
std::vector::empty
T empty(T... args)
ripple::hotLEDGER
@ hotLEDGER
Definition: NodeObject.h:34
ripple::Serializer::add32
int add32(std::uint32_t i)
Definition: Serializer.cpp:38
ripple::LedgerInfo
Information about the notional ledger backing the view.
Definition: ReadView.h:84
ripple::NodeStore::Database::scheduler_
Scheduler & scheduler_
Definition: Database.h:244
ripple::NodeStore::Database::isSameDB
virtual bool isSameDB(std::uint32_t s1, std::uint32_t s2)=0
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)
ripple::LedgerInfo::accountHash
uint256 accountHash
Definition: ReadView.h:102
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:116
ripple::NodeStore::Database::Database
Database()=delete
ripple::Stoppable::isStopping
bool isStopping() const
Returns true if the stoppable should stop.
Definition: Stoppable.cpp:54
std::chrono
ripple::NodeStore::Backend
A backend used for the NodeStore.
Definition: Backend.h:39