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