rippled
Loading...
Searching...
No Matches
SociDB.cpp
1#if defined(__clang__)
2#pragma clang diagnostic push
3#pragma clang diagnostic ignored "-Wdeprecated"
4#endif
5
6#include <xrpl/basics/ByteUtilities.h>
7#include <xrpl/basics/contract.h>
8#include <xrpl/rdb/DatabaseCon.h>
9#include <xrpl/rdb/SociDB.h>
10
11#include <boost/filesystem.hpp>
12
13#include <soci/sqlite3/soci-sqlite3.h>
14
15#include <memory>
16
17namespace xrpl {
18
19static auto checkpointPageCount = 1000;
20
21namespace detail {
22
24getSociSqliteInit(std::string const& name, std::string const& dir, std::string const& ext)
25{
26 if (name.empty())
27 {
28 Throw<std::runtime_error>(
29 "Sqlite databases must specify a dir and a name. Name: " + name + " Dir: " + dir);
30 }
31 boost::filesystem::path file(dir);
32 if (is_directory(file))
33 file /= name + ext;
34 return file.string();
35}
36
38getSociInit(BasicConfig const& config, std::string const& dbName)
39{
40 auto const& section = config.section("sqdb");
41 auto const backendName = get(section, "backend", "sqlite");
42
43 if (backendName != "sqlite")
44 Throw<std::runtime_error>("Unsupported soci backend: " + backendName);
45
46 auto const path = config.legacy("database_path");
47 auto const ext = dbName == "validators" || dbName == "peerfinder" ? ".sqlite" : ".db";
48 return detail::getSociSqliteInit(dbName, path, ext);
49}
50
51} // namespace detail
52
53DBConfig::DBConfig(std::string const& dbPath) : connectionString_(dbPath)
54{
55}
56
57DBConfig::DBConfig(BasicConfig const& config, std::string const& dbName)
58 : DBConfig(detail::getSociInit(config, dbName))
59{
60}
61
67
68void
69DBConfig::open(soci::session& s) const
70{
71 s.open(soci::sqlite3, connectionString());
72}
73
74void
75open(soci::session& s, BasicConfig const& config, std::string const& dbName)
76{
77 DBConfig(config, dbName).open(s);
78}
79
80void
81open(soci::session& s, std::string const& beName, std::string const& connectionString)
82{
83 if (beName == "sqlite")
84 {
85 s.open(soci::sqlite3, connectionString);
86 }
87 else
88 {
89 Throw<std::runtime_error>("Unsupported soci backend: " + beName);
90 }
91}
92
93static sqlite_api::sqlite3*
94getConnection(soci::session& s)
95{
96 sqlite_api::sqlite3* result = nullptr; // NOLINT(misc-const-correctness)
97 auto be = s.get_backend();
98 if (auto b = dynamic_cast<soci::sqlite3_session_backend*>(be))
99 result = b->conn_;
100
101 if (result == nullptr)
102 Throw<std::logic_error>("Didn't get a database connection.");
103
104 return result;
105}
106
108getKBUsedAll(soci::session& s)
109{
110 if (getConnection(s) == nullptr)
111 Throw<std::logic_error>("No connection found.");
112 return static_cast<size_t>(sqlite_api::sqlite3_memory_used() / kilobytes(1));
113}
114
116getKBUsedDB(soci::session& s)
117{
118 // This function will have to be customized when other backends are added
119 if (auto conn = getConnection(s))
120 {
121 int cur = 0, hiw = 0;
122 sqlite_api::sqlite3_db_status(conn, SQLITE_DBSTATUS_CACHE_USED, &cur, &hiw, 0);
123 return cur / kilobytes(1);
124 }
125 Throw<std::logic_error>("");
126 return 0; // Silence compiler warning.
127}
128
129void
130convert(soci::blob& from, std::vector<std::uint8_t>& to)
131{
132 to.resize(from.get_len());
133 if (to.empty())
134 return;
135 from.read(0, reinterpret_cast<char*>(&to[0]), from.get_len());
136}
137
138void
139convert(soci::blob& from, std::string& to)
140{
142 convert(from, tmp);
143 to.assign(tmp.begin(), tmp.end());
144}
145
146void
147convert(std::vector<std::uint8_t> const& from, soci::blob& to)
148{
149 if (!from.empty())
150 {
151 to.write(0, reinterpret_cast<char const*>(&from[0]), from.size());
152 }
153 else
154 {
155 to.trim(0);
156 }
157}
158
159void
160convert(std::string const& from, soci::blob& to)
161{
162 if (!from.empty())
163 {
164 to.write(0, from.data(), from.size());
165 }
166 else
167 {
168 to.trim(0);
169 }
170}
171
172namespace {
173
183class WALCheckpointer : public Checkpointer
184{
185public:
186 WALCheckpointer(
189 JobQueue& q,
190 ServiceRegistry& registry)
191 : id_(id)
192 , session_(std::move(session))
193 , jobQueue_(q)
194 , j_(registry.getJournal("WALCheckpointer"))
195 {
196 if (auto [conn, keepAlive] = getConnection(); conn)
197 {
198 (void)keepAlive;
199 sqlite_api::sqlite3_wal_hook(conn, &sqliteWALHook, reinterpret_cast<void*>(id_));
200 }
201 }
202
204 getConnection() const
205 {
206 if (auto p = session_.lock())
207 {
208 return {xrpl::getConnection(*p), p};
209 }
210 return {nullptr, std::shared_ptr<soci::session>{}};
211 }
212
214 id() const override
215 {
216 return id_;
217 }
218
219 ~WALCheckpointer() override = default;
220
221 void
222 schedule() override
223 {
224 {
225 std::lock_guard const lock(mutex_);
226 if (running_)
227 return;
228 running_ = true;
229 }
230
231 // If the Job is not added to the JobQueue then we're not running_.
232 if (!jobQueue_.addJob(
233 jtWAL,
234 "WAL",
235 // If the owning DatabaseCon is destroyed, no need to checkpoint
236 // or keep the checkpointer alive so use a weak_ptr to this.
237 // There is a separate check in `checkpoint` for a valid
238 // connection in the rare case when the DatabaseCon is destroyed
239 // after locking this weak_ptr
240 [wp = std::weak_ptr<Checkpointer>{shared_from_this()}]() {
241 if (auto self = wp.lock())
242 self->checkpoint();
243 }))
244 {
245 std::lock_guard const lock(mutex_);
246 running_ = false;
247 }
248 }
249
250 void
251 checkpoint() override
252 {
253 auto [conn, keepAlive] = getConnection();
254 (void)keepAlive;
255 if (conn == nullptr)
256 return;
257
258 int log = 0, ckpt = 0;
259 int const ret =
260 sqlite3_wal_checkpoint_v2(conn, nullptr, SQLITE_CHECKPOINT_PASSIVE, &log, &ckpt);
261
262 auto fname = sqlite3_db_filename(conn, "main");
263 if (ret != SQLITE_OK)
264 {
265 auto jm = (ret == SQLITE_LOCKED) ? j_.trace() : j_.warn();
266 JLOG(jm) << "WAL(" << fname << "): error " << ret;
267 }
268 else
269 {
270 JLOG(j_.trace()) << "WAL(" << fname << "): frames=" << log << ", written=" << ckpt;
271 }
272
273 std::lock_guard const lock(mutex_);
274 running_ = false;
275 }
276
277protected:
278 std::uintptr_t const id_;
279 // session is owned by the DatabaseCon parent that holds the checkpointer.
280 // It is possible (though rare) for the DatabaseCon class to be destroyed
281 // before the checkpointer.
283 std::mutex mutex_;
284 JobQueue& jobQueue_;
285
286 bool running_ = false;
287 beast::Journal const j_;
288
289 static int
290 sqliteWALHook(void* cpId, sqlite_api::sqlite3* conn, char const* dbName, int walSize)
291 {
292 if (walSize >= checkpointPageCount)
293 {
294 if (auto checkpointer = checkpointerFromId(reinterpret_cast<std::uintptr_t>(cpId)))
295 {
296 checkpointer->schedule();
297 }
298 else
299 {
300 sqlite_api::sqlite3_wal_hook(conn, nullptr, nullptr);
301 }
302 }
303 return SQLITE_OK;
304 }
305};
306
307} // namespace
308
313 JobQueue& queue,
314 ServiceRegistry& registry)
315{
316 return std::make_shared<WALCheckpointer>(id, std::move(session), queue, registry);
317}
318
319} // namespace xrpl
320
321#if defined(__clang__)
322#pragma clang diagnostic pop
323#endif
T assign(T... args)
T begin(T... args)
A generic endpoint for log messages.
Definition Journal.h:40
Stream trace() const
Severity stream access functions.
Definition Journal.h:295
Stream warn() const
Definition Journal.h:313
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:40
void open(soci::session &s) const
Definition SociDB.cpp:69
std::string connectionString() const
Definition SociDB.cpp:63
std::string connectionString_
Definition SociDB.h:41
DBConfig(std::string const &dbPath)
Definition SociDB.cpp:53
A pool of threads to perform work.
Definition JobQueue.h:38
Service registry for dependency injection.
T data(T... args)
T empty(T... args)
T end(T... args)
T is_same_v
T lock(T... args)
T log(T... args)
STL namespace.
std::string getSociSqliteInit(std::string const &name, std::string const &dir, std::string const &ext)
Definition SociDB.cpp:24
std::string getSociInit(BasicConfig const &config, std::string const &dbName)
Definition SociDB.cpp:38
Use hash_* containers for keys that do not need a cryptographically secure hashing algorithm.
Definition algorithm.h:5
static auto checkpointPageCount
Definition SociDB.cpp:19
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:116
static sqlite_api::sqlite3 * getConnection(soci::session &s)
Definition SociDB.cpp:94
std::uint32_t getKBUsedAll(soci::session &s)
Definition SociDB.cpp:108
@ jtWAL
Definition Job.h:49
@ open
We haven't closed our ledger yet, but others might have.
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:310
void convert(soci::blob &from, std::vector< std::uint8_t > &to)
Definition SociDB.cpp:130
std::shared_ptr< Checkpointer > checkpointerFromId(std::uintptr_t id)
T resize(T... args)
T size(T... args)