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