rippled
Loading...
Searching...
No Matches
SociDB.cpp
1//------------------------------------------------------------------------------
2/*
3 This file is part of rippled: https://github.com/ripple/rippled
4 Copyright (c) 2012-2015 Ripple Labs Inc.
5
6 Permission to use, copy, modify, and/or distribute this software for any
7 purpose with or without fee is hereby granted, provided that the above
8 copyright notice and this permission notice appear in all copies.
9
10 THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11 WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12 MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13 ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14 WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15 ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16 OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17*/
18//==============================================================================
19
20#if defined(__clang__)
21#pragma clang diagnostic push
22#pragma clang diagnostic ignored "-Wdeprecated"
23#endif
24
25#include <xrpld/core/Config.h>
26#include <xrpld/core/DatabaseCon.h>
27#include <xrpld/core/SociDB.h>
28
29#include <xrpl/basics/ByteUtilities.h>
30#include <xrpl/basics/contract.h>
31
32#include <boost/filesystem.hpp>
33
34#include <soci/sqlite3/soci-sqlite3.h>
35
36#include <memory>
37
38namespace ripple {
39
40static auto checkpointPageCount = 1000;
41
42namespace detail {
43
46 std::string const& name,
47 std::string const& dir,
48 std::string const& ext)
49{
50 if (name.empty())
51 {
52 Throw<std::runtime_error>(
53 "Sqlite databases must specify a dir and a name. Name: " + name +
54 " Dir: " + dir);
55 }
56 boost::filesystem::path file(dir);
57 if (is_directory(file))
58 file /= name + ext;
59 return file.string();
60}
61
63getSociInit(BasicConfig const& config, std::string const& dbName)
64{
65 auto const& section = config.section("sqdb");
66 auto const backendName = get(section, "backend", "sqlite");
67
68 if (backendName != "sqlite")
69 Throw<std::runtime_error>("Unsupported soci backend: " + backendName);
70
71 auto const path = config.legacy("database_path");
72 auto const ext =
73 dbName == "validators" || dbName == "peerfinder" ? ".sqlite" : ".db";
74 return detail::getSociSqliteInit(dbName, path, ext);
75}
76
77} // namespace detail
78
79DBConfig::DBConfig(std::string const& dbPath) : connectionString_(dbPath)
80{
81}
82
83DBConfig::DBConfig(BasicConfig const& config, std::string const& dbName)
84 : DBConfig(detail::getSociInit(config, dbName))
85{
86}
87
93
94void
95DBConfig::open(soci::session& s) const
96{
97 s.open(soci::sqlite3, connectionString());
98}
99
100void
101open(soci::session& s, BasicConfig const& config, std::string const& dbName)
102{
103 DBConfig(config, dbName).open(s);
104}
105
106void
108 soci::session& s,
109 std::string const& beName,
110 std::string const& connectionString)
111{
112 if (beName == "sqlite")
113 s.open(soci::sqlite3, connectionString);
114 else
115 Throw<std::runtime_error>("Unsupported soci backend: " + beName);
116}
117
118static sqlite_api::sqlite3*
119getConnection(soci::session& s)
120{
121 sqlite_api::sqlite3* result = nullptr;
122 auto be = s.get_backend();
123 if (auto b = dynamic_cast<soci::sqlite3_session_backend*>(be))
124 result = b->conn_;
125
126 if (!result)
127 Throw<std::logic_error>("Didn't get a database connection.");
128
129 return result;
130}
131
133getKBUsedAll(soci::session& s)
134{
135 if (!getConnection(s))
136 Throw<std::logic_error>("No connection found.");
137 return static_cast<size_t>(
138 sqlite_api::sqlite3_memory_used() / kilobytes(1));
139}
140
142getKBUsedDB(soci::session& s)
143{
144 // This function will have to be customized when other backends are added
145 if (auto conn = getConnection(s))
146 {
147 int cur = 0, hiw = 0;
148 sqlite_api::sqlite3_db_status(
149 conn, SQLITE_DBSTATUS_CACHE_USED, &cur, &hiw, 0);
150 return cur / kilobytes(1);
151 }
152 Throw<std::logic_error>("");
153 return 0; // Silence compiler warning.
154}
155
156void
157convert(soci::blob& from, std::vector<std::uint8_t>& to)
158{
159 to.resize(from.get_len());
160 if (to.empty())
161 return;
162 from.read(0, reinterpret_cast<char*>(&to[0]), from.get_len());
163}
164
165void
166convert(soci::blob& from, std::string& to)
167{
169 convert(from, tmp);
170 to.assign(tmp.begin(), tmp.end());
171}
172
173void
174convert(std::vector<std::uint8_t> const& from, soci::blob& to)
175{
176 if (!from.empty())
177 to.write(0, reinterpret_cast<char const*>(&from[0]), from.size());
178 else
179 to.trim(0);
180}
181
182void
183convert(std::string const& from, soci::blob& to)
184{
185 if (!from.empty())
186 to.write(0, from.data(), from.size());
187 else
188 to.trim(0);
189}
190
191namespace {
192
202class WALCheckpointer : public Checkpointer
203{
204public:
205 WALCheckpointer(
208 JobQueue& q,
209 Logs& logs)
210 : id_(id)
211 , session_(std::move(session))
212 , jobQueue_(q)
213 , j_(logs.journal("WALCheckpointer"))
214 {
215 if (auto [conn, keepAlive] = getConnection(); conn)
216 {
217 (void)keepAlive;
218 sqlite_api::sqlite3_wal_hook(
219 conn, &sqliteWALHook, reinterpret_cast<void*>(id_));
220 }
221 }
222
224 getConnection() const
225 {
226 if (auto p = session_.lock())
227 {
228 return {ripple::getConnection(*p), p};
229 }
230 return {nullptr, std::shared_ptr<soci::session>{}};
231 }
232
234 id() const override
235 {
236 return id_;
237 }
238
239 ~WALCheckpointer() override = default;
240
241 void
242 schedule() override
243 {
244 {
245 std::lock_guard lock(mutex_);
246 if (running_)
247 return;
248 running_ = true;
249 }
250
251 // If the Job is not added to the JobQueue then we're not running_.
252 if (!jobQueue_.addJob(
253 jtWAL,
254 "WAL",
255 // If the owning DatabaseCon is destroyed, no need to checkpoint
256 // or keep the checkpointer alive so use a weak_ptr to this.
257 // There is a separate check in `checkpoint` for a valid
258 // connection in the rare case when the DatabaseCon is destroyed
259 // after locking this weak_ptr
260 [wp = std::weak_ptr<Checkpointer>{shared_from_this()}]() {
261 if (auto self = wp.lock())
262 self->checkpoint();
263 }))
264 {
265 std::lock_guard lock(mutex_);
266 running_ = false;
267 }
268 }
269
270 void
271 checkpoint() override
272 {
273 auto [conn, keepAlive] = getConnection();
274 (void)keepAlive;
275 if (!conn)
276 return;
277
278 int log = 0, ckpt = 0;
279 int ret = sqlite3_wal_checkpoint_v2(
280 conn, nullptr, SQLITE_CHECKPOINT_PASSIVE, &log, &ckpt);
281
282 auto fname = sqlite3_db_filename(conn, "main");
283 if (ret != SQLITE_OK)
284 {
285 auto jm = (ret == SQLITE_LOCKED) ? j_.trace() : j_.warn();
286 JLOG(jm) << "WAL(" << fname << "): error " << ret;
287 }
288 else
289 {
290 JLOG(j_.trace()) << "WAL(" << fname << "): frames=" << log
291 << ", written=" << ckpt;
292 }
293
294 std::lock_guard lock(mutex_);
295 running_ = false;
296 }
297
298protected:
299 std::uintptr_t const id_;
300 // session is owned by the DatabaseCon parent that holds the checkpointer.
301 // It is possible (tho rare) for the DatabaseCon class to be destoryed
302 // before the checkpointer.
304 std::mutex mutex_;
305 JobQueue& jobQueue_;
306
307 bool running_ = false;
308 beast::Journal const j_;
309
310 static int
311 sqliteWALHook(
312 void* cpId,
313 sqlite_api::sqlite3* conn,
314 char const* dbName,
315 int walSize)
316 {
317 if (walSize >= checkpointPageCount)
318 {
319 if (auto checkpointer =
320 checkpointerFromId(reinterpret_cast<std::uintptr_t>(cpId)))
321 {
322 checkpointer->schedule();
323 }
324 else
325 {
326 sqlite_api::sqlite3_wal_hook(conn, nullptr, nullptr);
327 }
328 }
329 return SQLITE_OK;
330 }
331};
332
333} // namespace
334
339 JobQueue& queue,
340 Logs& logs)
341{
343 id, std::move(session), queue, logs);
344}
345
346} // namespace ripple
347
348#if defined(__clang__)
349#pragma clang diagnostic pop
350#endif
T assign(T... args)
T begin(T... args)
A generic endpoint for log messages.
Definition Journal.h:60
Stream trace() const
Severity stream access functions.
Definition Journal.h:322
Stream warn() const
Definition Journal.h:340
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:61
std::string connectionString_
Definition SociDB.h:62
DBConfig(std::string const &dbPath)
Definition SociDB.cpp:79
void open(soci::session &s) const
Definition SociDB.cpp:95
std::string connectionString() const
Definition SociDB.cpp:89
A pool of threads to perform work.
Definition JobQueue.h:58
Manages partitions for logging.
Definition Log.h:52
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:63
std::string getSociSqliteInit(std::string const &name, std::string const &dir, std::string const &ext)
Definition SociDB.cpp:45
Use hash_* containers for keys that do not need a cryptographically secure hashing algorithm.
Definition algorithm.h:25
static auto checkpointPageCount
Definition SociDB.cpp:40
static sqlite_api::sqlite3 * getConnection(soci::session &s)
Definition SociDB.cpp:119
std::uint32_t getKBUsedAll(soci::session &s)
Definition SociDB.cpp:133
std::uint32_t getKBUsedDB(soci::session &s)
Definition SociDB.cpp:142
void convert(soci::blob &from, std::vector< std::uint8_t > &to)
Definition SociDB.cpp:157
@ 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:336
std::shared_ptr< Checkpointer > checkpointerFromId(std::uintptr_t id)
@ jtWAL
Definition Job.h:70
STL namespace.
T resize(T... args)
T size(T... args)