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