rippled
Loading...
Searching...
No Matches
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 <xrpld/nodestore/Database.h>
21
22#include <xrpl/basics/chrono.h>
23#include <xrpl/beast/core/CurrentThreadName.h>
24#include <xrpl/json/json_value.h>
25#include <xrpl/protocol/HashPrefix.h>
26#include <xrpl/protocol/jss.h>
27
28#include <chrono>
29
30namespace ripple {
31namespace NodeStore {
32
34 Scheduler& scheduler,
35 int readThreads,
36 Section const& config,
37 beast::Journal journal)
38 : j_(journal)
39 , scheduler_(scheduler)
40 , earliestLedgerSeq_(
41 get<std::uint32_t>(config, "earliest_seq", XRP_LEDGER_EARLIEST_SEQ))
42 , requestBundle_(get<int>(config, "rq_bundle", 4))
43 , readThreads_(std::max(1, readThreads))
44{
45 XRPL_ASSERT(
46 readThreads,
47 "ripple::NodeStore::Database::Database : nonzero threads input");
48
49 if (earliestLedgerSeq_ < 1)
50 Throw<std::runtime_error>("Invalid earliest_seq");
51
52 if (requestBundle_ < 1 || requestBundle_ > 64)
53 Throw<std::runtime_error>("Invalid rq_bundle");
54
55 for (int i = readThreads_.load(); i != 0; --i)
56 {
58 [this](int i) {
60
62 "db prefetch #" + std::to_string(i));
63
64 decltype(read_) read;
65
66 while (true)
67 {
68 {
70
71 if (isStopping())
72 break;
73
74 if (read_.empty())
75 {
77 readCondVar_.wait(lock);
79 }
80
81 if (isStopping())
82 break;
83
84 // extract multiple object at a time to minimize the
85 // overhead of acquiring the mutex.
86 for (int cnt = 0;
87 !read_.empty() && cnt != requestBundle_;
88 ++cnt)
89 read.insert(read_.extract(read_.begin()));
90 }
91
92 for (auto it = read.begin(); it != read.end(); ++it)
93 {
94 XRPL_ASSERT(
95 !it->second.empty(),
96 "ripple::NodeStore::Database::Database : non-empty "
97 "data");
98
99 auto const& hash = it->first;
100 auto const& data = it->second;
101 auto const seqn = data[0].first;
102
103 auto obj =
105
106 // This could be further optimized: if there are
107 // multiple requests for sequence numbers mapping to
108 // multiple databases by sorting requests such that all
109 // indices mapping to the same database are grouped
110 // together and serviced by a single read.
111 for (auto const& req : data)
112 {
113 req.second(
114 (seqn == req.first) || isSameDB(req.first, seqn)
115 ? obj
117 hash, req.first, FetchType::async));
118 }
119 }
120
121 read.clear();
122 }
123
125 --readThreads_;
126 },
127 i);
128 t.detach();
129 }
130}
131
133{
134 // NOTE!
135 // Any derived class should call the stop() method in its
136 // destructor. Otherwise, occasionally, the derived class may
137 // crash during shutdown when its members are accessed by one of
138 // these threads after the derived class is destroyed but before
139 // this base class is destroyed.
140 stop();
141}
142
143bool
145{
146 return readStopping_.load(std::memory_order_relaxed);
147}
148
149void
151{
152 {
154
155 if (!readStopping_.exchange(true, std::memory_order_relaxed))
156 {
157 JLOG(j_.debug()) << "Clearing read queue because of stop request";
158 read_.clear();
160 }
161 }
162
163 JLOG(j_.debug()) << "Waiting for stop request to complete...";
164
165 using namespace std::chrono;
166
167 auto const start = steady_clock::now();
168
169 while (readThreads_.load() != 0)
170 {
171 XRPL_ASSERT(
172 steady_clock::now() - start < 30s,
173 "ripple::NodeStore::Database::stop : maximum stop duration");
175 }
176
177 JLOG(j_.debug()) << "Stop request completed in "
178 << duration_cast<std::chrono::milliseconds>(
179 steady_clock::now() - start)
180 .count()
181 << " millseconds";
182}
183
184void
186 uint256 const& hash,
187 std::uint32_t ledgerSeq,
189{
191
192 if (!isStopping())
193 {
194 read_[hash].emplace_back(ledgerSeq, std::move(cb));
196 }
197}
198
199void
201{
202 Batch batch;
204 auto storeBatch = [&, fname = __func__]() {
205 try
206 {
207 dstBackend.storeBatch(batch);
208 }
209 catch (std::exception const& e)
210 {
211 JLOG(j_.error()) << "Exception caught in function " << fname
212 << ". Error: " << e.what();
213 return;
214 }
215
216 std::uint64_t sz{0};
217 for (auto const& nodeObject : batch)
218 sz += nodeObject->getData().size();
219 storeStats(batch.size(), sz);
220 batch.clear();
221 };
222
223 srcDB.for_each([&](std::shared_ptr<NodeObject> nodeObject) {
224 XRPL_ASSERT(
225 nodeObject,
226 "ripple::NodeStore::Database::importInternal : non-null node");
227 if (!nodeObject) // This should never happen
228 return;
229
230 batch.emplace_back(std::move(nodeObject));
231 if (batch.size() >= batchWritePreallocationSize)
232 storeBatch();
233 });
234
235 if (!batch.empty())
236 storeBatch();
237}
238
239// Perform a fetch and report the time it took
242 uint256 const& hash,
243 std::uint32_t ledgerSeq,
244 FetchType fetchType,
245 bool duplicate)
246{
247 FetchReport fetchReport(fetchType);
248
249 using namespace std::chrono;
250 auto const begin{steady_clock::now()};
251
252 auto nodeObject{fetchNodeObject(hash, ledgerSeq, fetchReport, duplicate)};
253 auto dur = steady_clock::now() - begin;
254 fetchDurationUs_ += duration_cast<microseconds>(dur).count();
255 if (nodeObject)
256 {
258 fetchSz_ += nodeObject->getData().size();
259 }
261
262 fetchReport.elapsed = duration_cast<milliseconds>(dur);
263 scheduler_.onFetch(fetchReport);
264 return nodeObject;
265}
266
267void
269{
270 XRPL_ASSERT(
271 obj.isObject(),
272 "ripple::NodeStore::Database::getCountsJson : valid input type");
273
274 {
276 obj["read_queue"] = static_cast<Json::UInt>(read_.size());
277 }
278
279 obj["read_threads_total"] = readThreads_.load();
280 obj["read_threads_running"] = runningThreads_.load();
281 obj["read_request_bundle"] = requestBundle_;
282
283 obj[jss::node_writes] = std::to_string(storeCount_);
284 obj[jss::node_reads_total] = std::to_string(fetchTotalCount_);
285 obj[jss::node_reads_hit] = std::to_string(fetchHitCount_);
286 obj[jss::node_written_bytes] = std::to_string(storeSz_);
287 obj[jss::node_read_bytes] = std::to_string(fetchSz_);
288 obj[jss::node_reads_duration_us] = std::to_string(fetchDurationUs_);
289}
290
291} // namespace NodeStore
292} // namespace ripple
Represents a JSON value.
Definition: json_value.h:148
bool isObject() const
A generic endpoint for log messages.
Definition: Journal.h:60
Stream error() const
Definition: Journal.h:346
Stream debug() const
Definition: Journal.h:328
A backend used for the NodeStore.
Definition: Backend.h:40
virtual void storeBatch(Batch const &batch)=0
Store a group of objects.
Persistency layer for NodeObject.
Definition: Database.h:51
void getCountsJson(Json::Value &obj)
Definition: Database.cpp:268
std::atomic< std::uint32_t > fetchSz_
Definition: Database.h:232
void storeStats(std::uint64_t count, std::uint64_t sz)
Definition: Database.h:248
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:185
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.
virtual ~Database()
Destroy the node store.
Definition: Database.cpp:132
std::condition_variable readCondVar_
Definition: Database.h:277
std::atomic< std::uint64_t > storeCount_
Definition: Database.h:270
std::uint32_t const earliestLedgerSeq_
Definition: Database.h:240
std::map< uint256, std::vector< std::pair< std::uint32_t, std::function< void(std::shared_ptr< NodeObject > const &)> > > > read_
Definition: Database.h:285
std::atomic< std::uint64_t > storeSz_
Definition: Database.h:271
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:241
std::atomic< bool > readStopping_
Definition: Database.h:287
std::atomic< std::uint32_t > fetchHitCount_
Definition: Database.h:231
beast::Journal const j_
Definition: Database.h:227
std::atomic< std::uint64_t > fetchDurationUs_
Definition: Database.h:273
std::atomic< int > runningThreads_
Definition: Database.h:289
virtual bool isSameDB(std::uint32_t s1, std::uint32_t s2)=0
std::atomic< std::uint64_t > fetchTotalCount_
Definition: Database.h:272
std::atomic< int > readThreads_
Definition: Database.h:288
void importInternal(Backend &dstBackend, Database &srcDB)
Definition: Database.cpp:200
Scheduling for asynchronous backend activity.
virtual void onFetch(FetchReport const &report)=0
Reports completion of a fetch Allows the scheduler to monitor the node store's performance.
Holds a collection of configuration values.
Definition: BasicConfig.h:45
T detach(T... args)
T emplace_back(T... args)
T empty(T... args)
T exchange(T... args)
T load(T... args)
unsigned int UInt
Definition: json_forwards.h:27
void setCurrentThreadName(std::string_view newThreadName)
Changes the name of the caller thread.
void read(nudb::detail::istream &is, std::size_t &u)
Definition: varint.h:121
Use hash_* containers for keys that do not need a cryptographically secure hashing algorithm.
Definition: algorithm.h:26
static constexpr std::uint32_t XRP_LEDGER_EARLIEST_SEQ
The XRP ledger network's earliest allowed sequence.
T get(Section const &section, std::string const &name, T const &defaultValue=T{})
Retrieve a key/value pair from a section.
Definition: BasicConfig.h:355
STL namespace.
T reserve(T... args)
T size(T... args)
Contains information about a fetch operation.
T to_string(T... args)
T what(T... args)
T yield(T... args)