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