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