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