xrpld
Loading...
Searching...
No Matches
SociDB.cpp
1#include <xrpl/basics/Log.h>
2#include <xrpl/config/BasicConfig.h>
3#include <xrpl/config/Constants.h>
4#include <xrpl/core/Job.h>
5#include <xrpl/core/JobQueue.h>
6#include <xrpl/core/ServiceRegistry.h>
7
8#include <soci/blob.h>
9
10#include <cstddef>
11#include <cstdint>
12#include <filesystem>
13#include <mutex>
14#include <stdexcept>
15#include <string>
16#include <utility>
17#include <vector>
18#ifdef __clang__
19#pragma clang diagnostic push
20#pragma clang diagnostic ignored "-Wdeprecated"
21#endif
22
23#include <xrpl/basics/ByteUtilities.h>
24#include <xrpl/basics/contract.h>
25#include <xrpl/rdb/DatabaseCon.h>
26#include <xrpl/rdb/SociDB.h>
27
28#include <soci/sqlite3/soci-sqlite3.h> // IWYU pragma: keep
29
30#include <memory>
31
32namespace xrpl {
33
34static auto gCheckpointPageCount = 1000;
35
36namespace detail {
37
39getSociSqliteInit(std::string const& name, std::string const& dir, std::string const& ext)
40{
41 if (name.empty())
42 {
44 "Sqlite databases must specify a dir and a name. Name: " + name + " Dir: " + dir);
45 }
46 std::filesystem::path file(dir);
48 file /= name + ext;
49 return file.string();
50}
51
53getSociInit(BasicConfig const& config, std::string const& dbName)
54{
55 auto const& section = config.section(Sections::kSqdb);
56 auto const backendName = get(section, Keys::kBackend, "sqlite");
57
58 if (backendName != "sqlite")
59 Throw<std::runtime_error>("Unsupported soci backend: " + backendName);
60
61 auto const path = config.legacy(Sections::kDatabasePath);
62 auto const ext = dbName == "validators" || dbName == "peerfinder" ? ".sqlite" : ".db";
63 return detail::getSociSqliteInit(dbName, path, ext);
64}
65
66} // namespace detail
67
69{
70}
71
72DBConfig::DBConfig(BasicConfig const& config, std::string const& dbName)
73 : DBConfig(detail::getSociInit(config, dbName))
74{
75}
76
82
83void
84DBConfig::open(soci::session& s) const
85{
86 s.open(soci::sqlite3, connectionString());
87}
88
89void
90open(soci::session& s, BasicConfig const& config, std::string const& dbName)
91{
92 DBConfig(config, dbName).open(s);
93}
94
95void
96open(soci::session& s, std::string const& beName, std::string const& connectionString)
97{
98 if (beName == "sqlite")
99 {
100 s.open(soci::sqlite3, connectionString);
101 }
102 else
103 {
104 Throw<std::runtime_error>("Unsupported soci backend: " + beName);
105 }
106}
107
108static sqlite_api::sqlite3*
109getConnection(soci::session& s)
110{
111 sqlite_api::sqlite3* result = nullptr; // NOLINT(misc-const-correctness)
112 auto be = s.get_backend();
113 if (auto b = dynamic_cast<soci::sqlite3_session_backend*>(be))
114 result = b->conn_;
115
116 if (result == nullptr)
117 Throw<std::logic_error>("Didn't get a database connection.");
118
119 return result;
120}
121
123getKBUsedAll(soci::session& s)
124{
125 if (getConnection(s) == nullptr)
126 Throw<std::logic_error>("No connection found.");
127 return static_cast<size_t>(sqlite_api::sqlite3_memory_used() / kilobytes(1));
128}
129
131getKBUsedDB(soci::session& s)
132{
133 // This function will have to be customized when other backends are added
134 if (auto conn = getConnection(s))
135 {
136 int cur = 0, hiw = 0;
137 sqlite_api::sqlite3_db_status(conn, SQLITE_DBSTATUS_CACHE_USED, &cur, &hiw, 0);
138 return cur / kilobytes(1);
139 }
141 return 0; // Silence compiler warning.
142}
143
144void
145convert(soci::blob& from, std::vector<std::uint8_t>& to)
146{
147 to.resize(from.get_len());
148 if (to.empty())
149 return;
150 from.read(0, reinterpret_cast<char*>(&to[0]), from.get_len());
151}
152
153void
154convert(soci::blob& from, std::string& to)
155{
157 convert(from, tmp);
158 to.assign(tmp.begin(), tmp.end());
159}
160
161void
162convert(std::vector<std::uint8_t> const& from, soci::blob& to)
163{
164 if (!from.empty())
165 {
166 to.write(0, reinterpret_cast<char const*>(&from[0]), from.size());
167 }
168 else
169 {
170 to.trim(0);
171 }
172}
173
174void
175convert(std::string const& from, soci::blob& to)
176{
177 if (!from.empty())
178 {
179 to.write(0, from.data(), from.size());
180 }
181 else
182 {
183 to.trim(0);
184 }
185}
186
187namespace {
188
198
199class WALCheckpointer : public Checkpointer
200{
201public:
202 WALCheckpointer(
205 JobQueue& q,
206 ServiceRegistry& registry)
207 : id_(id)
208 , session_(std::move(session))
209 , jobQueue_(q)
210 , j_(registry.getJournal("WALCheckpointer"))
211 {
212 if (auto [conn, keepAlive] = getConnection(); conn)
213 {
214 (void)keepAlive;
215 // The checkpointer is identified to the C callback by an integer id
216 // (resolved via checkpointerFromId) rather than a raw `this`, so it
217 // cannot dangle if the checkpointer is destroyed. Passing the id
218 // through sqlite's void* user-data requires an integer-to-pointer
219 // cast.
220 // NOLINTNEXTLINE(performance-no-int-to-ptr)
221 sqlite_api::sqlite3_wal_hook(conn, &sqliteWALHook, reinterpret_cast<void*>(id_));
222 }
223 }
224
225 std::pair<sqlite_api::sqlite3*, std::shared_ptr<soci::session>>
226 getConnection() const
227 {
228 if (auto p = session_.lock())
229 {
230 return {xrpl::getConnection(*p), p};
231 }
232 return {nullptr, std::shared_ptr<soci::session>{}};
233 }
234
235 std::uintptr_t
236 id() const override
237 {
238 return id_;
239 }
240
241 ~WALCheckpointer() override = default;
242
243 void
244 schedule() override
245 {
246 {
247 std::scoped_lock const lock(mutex_);
248 if (running_)
249 return;
250 running_ = true;
251 }
252
253 // If the Job is not added to the JobQueue then we're not running_.
254 if (!jobQueue_.addJob(
255 JtWal,
256 "WAL",
257 // If the owning DatabaseCon is destroyed, no need to checkpoint
258 // or keep the checkpointer alive so use a weak_ptr to this.
259 // There is a separate check in `checkpoint` for a valid
260 // connection in the rare case when the DatabaseCon is destroyed
261 // after locking this weak_ptr
262 [wp = std::weak_ptr<Checkpointer>{shared_from_this()}]() {
263 if (auto self = wp.lock())
264 self->checkpoint();
265 }))
266 {
267 std::scoped_lock const lock(mutex_);
268 running_ = false;
269 }
270 }
271
272 void
273 checkpoint() override
274 {
275 auto [conn, keepAlive] = getConnection();
276 (void)keepAlive;
277 if (conn == nullptr)
278 return;
279
280 int log = 0, ckpt = 0;
281 int const ret = sqlite_api::sqlite3_wal_checkpoint_v2(
282 conn, nullptr, SQLITE_CHECKPOINT_PASSIVE, &log, &ckpt);
283
284 auto fname = sqlite_api::sqlite3_db_filename(conn, "main");
285 if (ret != SQLITE_OK)
286 {
287 auto jm = (ret == SQLITE_LOCKED) ? j_.trace() : j_.warn();
288 JLOG(jm) << "WAL(" << fname << "): error " << ret;
289 }
290 else
291 {
292 JLOG(j_.trace()) << "WAL(" << fname << "): frames=" << log << ", written=" << ckpt;
293 }
294
295 std::scoped_lock const lock(mutex_);
296 running_ = false;
297 }
298
299protected:
300 std::uintptr_t const id_;
301 // session is owned by the DatabaseCon parent that holds the checkpointer.
302 // It is possible (though rare) for the DatabaseCon class to be destroyed
303 // before the checkpointer.
304 std::weak_ptr<soci::session> session_;
305 std::mutex mutex_;
306 JobQueue& jobQueue_;
307
308 bool running_ = false;
309 beast::Journal const j_;
310
311 static int
312 sqliteWALHook(void* cpId, sqlite_api::sqlite3* conn, char const* dbName, int walSize)
313 {
314 if (walSize >= gCheckpointPageCount)
315 {
316 if (auto checkpointer = checkpointerFromId(reinterpret_cast<std::uintptr_t>(cpId)))
317 {
318 checkpointer->schedule();
319 }
320 else
321 {
322 sqlite_api::sqlite3_wal_hook(conn, nullptr, nullptr);
323 }
324 }
325 return SQLITE_OK;
326 }
327};
328
329} // namespace
330
331std::shared_ptr<Checkpointer>
335 JobQueue& queue,
336 ServiceRegistry& registry)
337{
338 return std::make_shared<WALCheckpointer>(id, std::move(session), queue, registry);
339}
340
341} // namespace xrpl
342
343#ifdef __clang__
344#pragma clang diagnostic pop
345#endif
T assign(T... args)
T begin(T... args)
Holds unparsed configuration information.
void legacy(std::string const &section, std::string value)
Set a value that is not a key/value pair.
Section & section(std::string const &name)
Returns the section with the given name.
DBConfig is used when a client wants to delay opening a soci::session after parsing the config parame...
Definition SociDB.h:44
void open(soci::session &s) const
Definition SociDB.cpp:84
std::string connectionString() const
Definition SociDB.cpp:78
DBConfig(std::string dbPath)
Definition SociDB.cpp:68
std::string connectionString_
Definition SociDB.h:45
A pool of threads to perform work.
Definition JobQueue.h:60
Service registry for dependency injection.
T data(T... args)
T empty(T... args)
T end(T... args)
T is_directory(T... args)
T lock(T... args)
T log(T... args)
T make_shared(T... args)
STL namespace.
std::string getSociSqliteInit(std::string const &name, std::string const &dir, std::string const &ext)
Definition SociDB.cpp:39
std::string getSociInit(BasicConfig const &config, std::string const &dbName)
Definition SociDB.cpp:53
Use hash_* containers for keys that do not need a cryptographically secure hashing algorithm.
Definition algorithm.h:5
T get(Section const &section, std::string const &name, T const &defaultValue=T{})
Retrieve a key/value pair from a section.
std::uint32_t getKBUsedDB(soci::session &s)
Definition SociDB.cpp:131
static auto gCheckpointPageCount
Definition SociDB.cpp:34
static sqlite_api::sqlite3 * getConnection(soci::session &s)
Definition SociDB.cpp:109
std::uint32_t getKBUsedAll(soci::session &s)
Definition SociDB.cpp:123
void open(soci::session &s, BasicConfig const &config, std::string const &dbName)
Open a soci session.
Definition SociDB.cpp:90
@ JtWal
Definition Job.h:56
constexpr auto kilobytes(T value) noexcept
std::shared_ptr< Checkpointer > makeCheckpointer(std::uintptr_t id, std::weak_ptr< soci::session >, JobQueue &, ServiceRegistry &)
Returns a new checkpointer which makes checkpoints of a soci database every checkpointPageCount pages...
Definition SociDB.cpp:332
void convert(soci::blob &from, std::vector< std::uint8_t > &to)
Definition SociDB.cpp:145
std::shared_ptr< Checkpointer > checkpointerFromId(std::uintptr_t id)
XRPL_NO_SANITIZE_ADDRESS void Throw(Args &&... args)
Definition contract.h:52
T resize(T... args)
T size(T... args)
static constexpr auto kBackend
Definition Constants.h:93
static constexpr auto kSqdb
Definition Constants.h:60
static constexpr auto kDatabasePath
Definition Constants.h:13