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