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/nodestore/Database.h>
24 #include <ripple/protocol/HashPrefix.h>
25 
26 namespace ripple {
27 namespace NodeStore {
28 
30  std::string name,
31  Stoppable& parent,
32  Scheduler& scheduler,
33  int readThreads,
34  Section const& config,
35  beast::Journal journal)
36  : Stoppable(name, parent.getRoot())
37  , j_(journal)
38  , scheduler_(scheduler)
39  , earliestLedgerSeq_(
40  get<std::uint32_t>(config, "earliest_seq", XRP_LEDGER_EARLIEST_SEQ))
41 {
42  if (earliestLedgerSeq_ < 1)
43  Throw<std::runtime_error>("Invalid earliest_seq");
44 
45  while (readThreads-- > 0)
47 }
48 
50 {
51  // NOTE!
52  // Any derived class should call the stopThreads() method in its
53  // destructor. Otherwise, occasionally, the derived class may
54  // crash during shutdown when its members are accessed by one of
55  // these threads after the derived class is destroyed but before
56  // this base class is destroyed.
57  stopThreads();
58 }
59 
60 void
62 {
64  // Wake in two generations.
65  // Each generation is a full pass over the space.
66  // If we're in generation N and you issue a request,
67  // that request will only be done during generation N
68  // if it happens to land after where the pass currently is.
69  // But, if not, it will definitely be done during generation
70  // N+1 since the request was in the table before that pass
71  // even started. So when you reach generation N+2,
72  // you know the request is done.
73  std::uint64_t const wakeGen = readGen_ + 2;
74  while (!readShut_ && !read_.empty() && (readGen_ < wakeGen))
75  readGenCondVar_.wait(lock);
76 }
77 
78 void
80 {
81  // After stop time we can no longer use the JobQueue for background
82  // reads. Join the background read threads.
83  stopThreads();
84 }
85 
86 void
88 {
89  stopped();
90 }
91 
92 void
94 {
95  {
97  if (readShut_) // Only stop threads once.
98  return;
99 
100  readShut_ = true;
103  }
104 
105  for (auto& e : readThreads_)
106  e.join();
107 }
108 
109 void
111  uint256 const& hash,
112  std::uint32_t seq,
114  std::shared_ptr<KeyCache<uint256>> const& nCache)
115 {
116  // Post a read
118  if (read_.emplace(hash, std::make_tuple(seq, pCache, nCache)).second)
120 }
121 
124 {
126  Status status;
127  try
128  {
129  status = backend->fetch(hash.begin(), &nObj);
130  }
131  catch (std::exception const& e)
132  {
133  JLOG(j_.fatal()) << "Exception, " << e.what();
134  Rethrow();
135  }
136 
137  switch (status)
138  {
139  case ok:
140  ++fetchHitCount_;
141  if (nObj)
142  fetchSz_ += nObj->getData().size();
143  break;
144  case notFound:
145  break;
146  case dataCorrupt:
147  // VFALCO TODO Deal with encountering corrupt data!
148  JLOG(j_.fatal()) << "Corrupt NodeObject #" << hash;
149  break;
150  default:
151  JLOG(j_.warn()) << "Unknown status=" << status;
152  break;
153  }
154  return nObj;
155 }
156 
157 void
159 {
160  Batch b;
162  srcDB.for_each([&](std::shared_ptr<NodeObject> nObj) {
163  assert(nObj);
164  if (!nObj) // This should never happen
165  return;
166 
167  ++storeCount_;
168  storeSz_ += nObj->getData().size();
169 
170  b.push_back(nObj);
172  {
173  dstBackend.storeBatch(b);
174  b.clear();
176  }
177  });
178  if (!b.empty())
179  dstBackend.storeBatch(b);
180 }
181 
182 // Perform a fetch and report the time it took
185  uint256 const& hash,
186  std::uint32_t seq,
188  KeyCache<uint256>& nCache,
189  bool isAsync)
190 {
191  FetchReport report;
192  report.isAsync = isAsync;
193  report.wentToDisk = false;
194 
195  using namespace std::chrono;
196  auto const before = steady_clock::now();
197 
198  // See if the object already exists in the cache
199  auto nObj = pCache.fetch(hash);
200  if (!nObj && !nCache.touch_if_exists(hash))
201  {
202  // Try the database(s)
203  report.wentToDisk = true;
204  nObj = fetchFrom(hash, seq);
206  if (!nObj)
207  {
208  // Just in case a write occurred
209  nObj = pCache.fetch(hash);
210  if (!nObj)
211  // We give up
212  nCache.insert(hash);
213  }
214  else
215  {
216  // Ensure all threads get the same object
217  pCache.canonicalize_replace_client(hash, nObj);
218 
219  // Since this was a 'hard' fetch, we will log it.
220  JLOG(j_.trace()) << "HOS: " << hash << " fetch: in db";
221  }
222  }
223  report.wasFound = static_cast<bool>(nObj);
224  report.elapsed = duration_cast<milliseconds>(steady_clock::now() - before);
225  scheduler_.onFetch(report);
226  return nObj;
227 }
228 
229 bool
231  Ledger const& srcLedger,
232  std::shared_ptr<Backend> dstBackend,
236 {
237  assert(static_cast<bool>(dstPCache) == static_cast<bool>(dstNCache));
238  if (srcLedger.info().hash.isZero() || srcLedger.info().accountHash.isZero())
239  {
240  assert(false);
241  JLOG(j_.error()) << "source ledger seq " << srcLedger.info().seq
242  << " is invalid";
243  return false;
244  }
245  auto& srcDB = const_cast<Database&>(srcLedger.stateMap().family().db());
246  if (&srcDB == this)
247  {
248  assert(false);
249  JLOG(j_.error()) << "source and destination databases are the same";
250  return false;
251  }
252 
253  Batch batch;
255  auto storeBatch = [&]() {
256  if (dstPCache && dstNCache)
257  {
258  for (auto& nObj : batch)
259  {
260  dstPCache->canonicalize_replace_cache(nObj->getHash(), nObj);
261  dstNCache->erase(nObj->getHash());
262  storeStats(nObj->getData().size());
263  }
264  }
265  dstBackend->storeBatch(batch);
266  batch.clear();
268  };
269  bool error = false;
270  auto visit = [&](SHAMapAbstractNode& node) {
271  if (auto nObj = srcDB.fetch(
272  node.getNodeHash().as_uint256(), srcLedger.info().seq))
273  {
274  batch.emplace_back(std::move(nObj));
275  if (batch.size() < batchWritePreallocationSize)
276  return true;
277 
278  storeBatch();
279 
280  if (!isStopping())
281  return true;
282  }
283 
284  error = true;
285  return false;
286  };
287 
288  // Store ledger header
289  {
290  Serializer s(sizeof(std::uint32_t) + sizeof(LedgerInfo));
292  addRaw(srcLedger.info(), s);
293  auto nObj = NodeObject::createObject(
294  hotLEDGER, std::move(s.modData()), srcLedger.info().hash);
295  batch.emplace_back(std::move(nObj));
296  }
297 
298  // Store the state map
299  if (srcLedger.stateMap().getHash().isNonZero())
300  {
301  if (!srcLedger.stateMap().isValid())
302  {
303  JLOG(j_.error()) << "source ledger seq " << srcLedger.info().seq
304  << " state map invalid";
305  return false;
306  }
307  if (next && next->info().parentHash == srcLedger.info().hash)
308  {
309  auto have = next->stateMap().snapShot(false);
310  srcLedger.stateMap().snapShot(false)->visitDifferences(
311  &(*have), visit);
312  }
313  else
314  srcLedger.stateMap().snapShot(false)->visitNodes(visit);
315  if (error)
316  return false;
317  }
318 
319  // Store the transaction map
320  if (srcLedger.info().txHash.isNonZero())
321  {
322  if (!srcLedger.txMap().isValid())
323  {
324  JLOG(j_.error()) << "source ledger seq " << srcLedger.info().seq
325  << " transaction map invalid";
326  return false;
327  }
328  srcLedger.txMap().snapShot(false)->visitNodes(visit);
329  if (error)
330  return false;
331  }
332 
333  if (!batch.empty())
334  storeBatch();
335  return true;
336 }
337 
338 // Entry point for async read threads
339 void
341 {
342  beast::setCurrentThreadName("prefetch");
343  while (true)
344  {
345  uint256 lastHash;
346  std::uint32_t lastSeq;
349  {
351  while (!readShut_ && read_.empty())
352  {
353  // All work is done
355  readCondVar_.wait(lock);
356  }
357  if (readShut_)
358  break;
359 
360  // Read in key order to make the back end more efficient
361  auto it = read_.lower_bound(readLastHash_);
362  if (it == read_.end())
363  {
364  it = read_.begin();
365  // A generation has completed
366  ++readGen_;
368  }
369  lastHash = it->first;
370  lastSeq = std::get<0>(it->second);
371  lastPcache = std::get<1>(it->second).lock();
372  lastNcache = std::get<2>(it->second).lock();
373  read_.erase(it);
374  readLastHash_ = lastHash;
375  }
376 
377  // Perform the read
378  if (lastPcache && lastNcache)
379  doFetch(lastHash, lastSeq, *lastPcache, *lastNcache, true);
380  }
381 }
382 
383 } // namespace NodeStore
384 } // namespace ripple
beast::Journal::fatal
Stream fatal() const
Definition: Journal.h:339
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:310
ripple::SHAMap::isValid
bool isValid() const
Definition: SHAMap.h:523
std::make_tuple
T make_tuple(T... args)
ripple::NodeStore::Database::read_
std::map< uint256, std::tuple< std::uint32_t, std::weak_ptr< TaggedCache< uint256, NodeObject > >, std::weak_ptr< KeyCache< uint256 > > > > read_
Definition: Database.h:307
ripple::NodeStore::Database
Persistency layer for NodeObject.
Definition: Database.h:53
std::string
STL class.
ripple::NodeStore::Database::readShut_
bool readShut_
Definition: Database.h:313
std::shared_ptr
STL class.
ripple::TaggedCache
Map/cache combination.
Definition: TaggedCache.h:52
ripple::SHAMap::getHash
SHAMapHash getHash() const
Definition: SHAMap.cpp:789
ripple::NodeStore::Database::doFetch
std::shared_ptr< NodeObject > doFetch(uint256 const &hash, std::uint32_t seq, TaggedCache< uint256, NodeObject > &pCache, KeyCache< uint256 > &nCache, bool isAsync)
Definition: Database.cpp:184
std::exception
STL class.
ripple::base_uint::isNonZero
bool isNonZero() const
Definition: base_uint.h:480
ripple::Stoppable::stopped
void stopped()
Called by derived classes to indicate that the stoppable has stopped.
Definition: Stoppable.cpp:72
beast::Journal::trace
Stream trace() const
Severity stream access functions.
Definition: Journal.h:309
ripple::NodeStore::Database::fetchFrom
virtual std::shared_ptr< NodeObject > fetchFrom(uint256 const &hash, std::uint32_t seq)=0
ripple::NodeStore::ok
@ ok
Definition: nodestore/Types.h:45
ripple::SHAMap::family
Family const & family() const
Definition: SHAMap.h:111
ripple::Serializer::modData
Blob & modData()
Definition: Serializer.h:176
std::vector::reserve
T reserve(T... args)
ripple::LedgerInfo::hash
uint256 hash
Definition: ReadView.h:96
std::vector< std::shared_ptr< NodeObject > >
std::vector::size
T size(T... args)
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::TaggedCache::fetch
std::shared_ptr< T > fetch(const key_type &key)
Definition: TaggedCache.h:392
beast::Journal::warn
Stream warn() const
Definition: Journal.h:327
ripple::NodeStore::Database::fetchSz_
std::atomic< std::uint32_t > fetchSz_
Definition: Database.h:294
std::lock_guard
STL class.
ripple::NodeStore::FetchReport
Contains information about a fetch operation.
Definition: ripple/nodestore/Scheduler.h:30
ripple::addRaw
void addRaw(LedgerInfo const &info, Serializer &s)
Definition: View.cpp:43
ripple::LedgerInfo::seq
LedgerIndex seq
Definition: ReadView.h:88
ripple::SHAMapHash::isNonZero
bool isNonZero() const
Definition: SHAMapTreeNode.h:69
ripple::SHAMap::snapShot
std::shared_ptr< SHAMap > snapShot(bool isMutable) const
Definition: SHAMap.cpp:51
ripple::NodeStore::Database::fetchHitCount_
std::atomic< std::uint32_t > fetchHitCount_
Definition: Database.h:292
ripple::Family::db
virtual NodeStore::Database & db()=0
ripple::LedgerInfo::txHash
uint256 txHash
Definition: ReadView.h:97
std::vector::clear
T clear(T... args)
ripple::KeyCache::touch_if_exists
bool touch_if_exists(KeyComparable const &key)
Refresh the last access time on a key if present.
Definition: KeyCache.h:234
ripple::NodeStore::Database::readLock_
std::mutex readLock_
Definition: Database.h:296
std::vector::push_back
T push_back(T... args)
ripple::NodeStore::notFound
@ notFound
Definition: nodestore/Types.h:46
ripple::NodeStore::Database::onChildrenStopped
void onChildrenStopped() override
Override called when all children have stopped.
Definition: Database.cpp:87
ripple::base_uint< 256 >
ripple::Ledger::info
LedgerInfo const & info() const override
Returns information about the ledger.
Definition: Ledger.h:155
ripple::Stoppable
Provides an interface for starting and stopping.
Definition: Stoppable.h:200
ripple::NodeStore::Database::importInternal
void importInternal(Backend &dstBackend, Database &srcDB)
Definition: Database.cpp:158
ripple::base_uint::isZero
bool isZero() const
Definition: base_uint.h:475
ripple::KeyCache::insert
bool insert(Key const &key)
Insert the specified key.
Definition: KeyCache.h:213
ripple::Ledger
Holds a ledger.
Definition: Ledger.h:77
ripple::Rethrow
void Rethrow()
Rethrow the exception currently being handled.
Definition: contract.h:48
ripple::Ledger::stateMap
SHAMap const & stateMap() const
Definition: Ledger.h:289
std::unique_lock
STL class.
ripple::NodeStore::Database::readCondVar_
std::condition_variable readCondVar_
Definition: Database.h:297
ripple::NodeStore::Database::waitReads
void waitReads()
Wait for all currently pending async reads to complete.
Definition: Database.cpp:61
ripple::NodeStore::Database::storeStats
void storeStats(size_t sz)
Definition: Database.h:250
beast::Journal::error
Stream error() const
Definition: Journal.h:333
ripple::NodeStore::batchWritePreallocationSize
@ batchWritePreallocationSize
Definition: nodestore/Types.h:34
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
ripple::NodeStore::dataCorrupt
@ dataCorrupt
Definition: nodestore/Types.h:47
std::uint64_t
std::condition_variable::wait
T wait(T... args)
ripple::NodeStore::FetchReport::isAsync
bool isAsync
Definition: ripple/nodestore/Scheduler.h:35
ripple::NodeStore::Scheduler
Scheduling for asynchronous backend activity.
Definition: ripple/nodestore/Scheduler.h:57
ripple::NodeStore::Database::~Database
virtual ~Database()
Destroy the node store.
Definition: Database.cpp:49
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::fetchInternal
std::shared_ptr< NodeObject > fetchInternal(uint256 const &hash, std::shared_ptr< Backend > backend)
Definition: Database.cpp:123
std::condition_variable::notify_one
T notify_one(T... args)
ripple::NodeStore::Status
Status
Return codes from Backend operations.
Definition: nodestore/Types.h:44
ripple::NodeStore::Database::threadEntry
void threadEntry()
Definition: Database.cpp:340
ripple::Serializer
Definition: Serializer.h:39
ripple::Ledger::txMap
SHAMap const & txMap() const
Definition: Ledger.h:301
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:79
ripple::NodeStore::Database::storeLedger
virtual bool storeLedger(std::shared_ptr< Ledger const > const &srcLedger)=0
Copies a ledger stored in a different database to this one.
ripple::NodeStore::Database::readGenCondVar_
std::condition_variable readGenCondVar_
Definition: Database.h:298
ripple::NodeStore::Database::storeCount_
std::atomic< std::uint32_t > storeCount_
Definition: Database.h:290
ripple::base_uint::begin
iterator begin()
Definition: base_uint.h:114
ripple::NodeStore::Database::j_
const beast::Journal j_
Definition: Database.h:242
ripple::NodeStore::FetchReport::wasFound
bool wasFound
Definition: ripple/nodestore/Scheduler.h:37
ripple::NodeStore::Database::readThreads_
std::vector< std::thread > readThreads_
Definition: Database.h:312
ripple::NodeStore::Database::stopThreads
void stopThreads()
Definition: Database.cpp:93
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:60
ripple::NodeStore::Database::asyncFetch
virtual bool asyncFetch(uint256 const &hash, std::uint32_t seq, std::shared_ptr< NodeObject > &object)=0
Fetch an object without waiting.
ripple::NodeStore::Database::earliestLedgerSeq_
const std::uint32_t earliestLedgerSeq_
Definition: Database.h:320
ripple::NodeStore::Database::fetchTotalCount_
std::atomic< std::uint32_t > fetchTotalCount_
Definition: Database.h:291
ripple::SHAMapAbstractNode
Definition: SHAMapTreeNode.h:122
std::vector::empty
T empty(T... args)
ripple::NodeStore::Database::storeSz_
std::atomic< std::uint32_t > storeSz_
Definition: Database.h:293
ripple::NodeStore::Database::readGen_
uint64_t readGen_
Definition: Database.h:316
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:80
ripple::NodeStore::Database::scheduler_
Scheduler & scheduler_
Definition: Database.h:243
ripple::TaggedCache::canonicalize_replace_client
bool canonicalize_replace_client(const key_type &key, std::shared_ptr< T > &data)
Definition: TaggedCache.h:386
ripple::KeyCache< uint256 >
ripple::NodeStore::Backend::storeBatch
virtual void storeBatch(Batch const &batch)=0
Store a group of objects.
ripple::NodeStore::FetchReport::wentToDisk
bool wentToDisk
Definition: ripple/nodestore/Scheduler.h:36
ripple::NodeStore::FetchReport::elapsed
std::chrono::milliseconds elapsed
Definition: ripple/nodestore/Scheduler.h:34
std::condition_variable::notify_all
T notify_all(T... args)
ripple::LedgerInfo::accountHash
uint256 accountHash
Definition: ReadView.h:98
std::exception::what
T what(T... args)
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:37