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