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/SociDB.h>
30 #include <boost/filesystem.hpp>
31 #include <memory>
32 #include <soci/sqlite3/soci-sqlite3.h>
33 
34 namespace ripple {
35 
36 static auto checkpointPageCount = 1000;
37 
38 namespace detail {
39 
42  std::string const& name,
43  std::string const& dir,
44  std::string const& ext)
45 {
46  if (name.empty())
47  {
48  Throw<std::runtime_error>(
49  "Sqlite databases must specify a dir and a name. Name: " + name +
50  " Dir: " + dir);
51  }
52  boost::filesystem::path file(dir);
53  if (is_directory(file))
54  file /= name + ext;
55  return std::make_pair(file.string(), std::ref(soci::sqlite3));
56 }
57 
59 getSociInit(BasicConfig const& config, std::string const& dbName)
60 {
61  auto const& section = config.section("sqdb");
62  auto const backendName = get(section, "backend", "sqlite");
63 
64  if (backendName != "sqlite")
65  Throw<std::runtime_error>("Unsupported soci backend: " + backendName);
66 
67  auto const path = config.legacy("database_path");
68  auto const ext =
69  dbName == "validators" || dbName == "peerfinder" ? ".sqlite" : ".db";
70  return detail::getSociSqliteInit(dbName, path, ext);
71 }
72 
73 } // namespace detail
74 
77  : connectionString_(std::move(init.first)), backendFactory_(init.second)
78 {
79 }
80 
81 SociConfig::SociConfig(BasicConfig const& config, std::string const& dbName)
82  : SociConfig(detail::getSociInit(config, dbName))
83 {
84 }
85 
88 {
89  return connectionString_;
90 }
91 
92 void
93 SociConfig::open(soci::session& s) const
94 {
96 }
97 
98 void
99 open(soci::session& s, BasicConfig const& config, std::string const& dbName)
100 {
101  SociConfig(config, dbName).open(s);
102 }
103 
104 void
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 
116 static sqlite_api::sqlite3*
117 getConnection(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 
130 size_t
131 getKBUsedAll(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 
139 size_t
140 getKBUsedDB(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 
154 void
155 convert(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 
163 void
164 convert(soci::blob& from, std::string& to)
165 {
167  convert(from, tmp);
168  to.assign(tmp.begin(), tmp.end());
169 }
170 
171 void
172 convert(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 
180 void
181 convert(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 
189 namespace {
190 
199 class WALCheckpointer : public Checkpointer
200 {
201 public:
202  WALCheckpointer(sqlite_api::sqlite3& conn, JobQueue& q, Logs& logs)
203  : conn_(conn), jobQueue_(q), j_(logs.journal("WALCheckpointer"))
204  {
205  sqlite_api::sqlite3_wal_hook(&conn_, &sqliteWALHook, this);
206  }
207 
208  ~WALCheckpointer() override = default;
209 
210 private:
211  sqlite_api::sqlite3& conn_;
212  std::mutex mutex_;
213  JobQueue& jobQueue_;
214 
215  bool running_ = false;
216  beast::Journal const j_;
217 
218  static int
219  sqliteWALHook(
220  void* cp,
221  sqlite_api::sqlite3*,
222  const char* dbName,
223  int walSize)
224  {
225  if (walSize >= checkpointPageCount)
226  {
227  if (auto checkpointer = reinterpret_cast<WALCheckpointer*>(cp))
228  checkpointer->scheduleCheckpoint();
229  else
230  Throw<std::logic_error>("Didn't get a WALCheckpointer");
231  }
232  return SQLITE_OK;
233  }
234 
235  void
236  scheduleCheckpoint()
237  {
238  {
239  std::lock_guard lock(mutex_);
240  if (running_)
241  return;
242  running_ = true;
243  }
244 
245  // If the Job is not added to the JobQueue then we're not running_.
246  if (!jobQueue_.addJob(jtWAL, "WAL", [this](Job&) { checkpoint(); }))
247  {
248  std::lock_guard lock(mutex_);
249  running_ = false;
250  }
251  }
252 
253  void
254  checkpoint()
255  {
256  int log = 0, ckpt = 0;
257  int ret = sqlite3_wal_checkpoint_v2(
258  &conn_, nullptr, SQLITE_CHECKPOINT_PASSIVE, &log, &ckpt);
259 
260  auto fname = sqlite3_db_filename(&conn_, "main");
261  if (ret != SQLITE_OK)
262  {
263  auto jm = (ret == SQLITE_LOCKED) ? j_.trace() : j_.warn();
264  JLOG(jm) << "WAL(" << fname << "): error " << ret;
265  }
266  else
267  {
268  JLOG(j_.trace()) << "WAL(" << fname << "): frames=" << log
269  << ", written=" << ckpt;
270  }
271 
272  std::lock_guard lock(mutex_);
273  running_ = false;
274  }
275 };
276 
277 } // namespace
278 
280 makeCheckpointer(soci::session& session, JobQueue& queue, Logs& logs)
281 {
282  if (auto conn = getConnection(session))
283  return std::make_unique<WALCheckpointer>(*conn, queue, logs);
284  return {};
285 }
286 
287 } // namespace ripple
288 
289 #if defined(__clang__)
290 #pragma clang diagnostic pop
291 #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.
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:140
ripple::getKBUsedAll
size_t getKBUsedAll(soci::session &s)
Definition: SociDB.cpp:131
std::pair
ripple::convert
void convert(soci::blob &from, std::vector< std::uint8_t > &to)
Definition: SociDB.cpp:155
std::vector
STL class.
std::vector::size
T size(T... args)
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::makeCheckpointer
std::unique_ptr< Checkpointer > makeCheckpointer(soci::session &session, JobQueue &queue, Logs &logs)
Returns a new checkpointer which makes checkpoints of a soci database every checkpointPageCount pages...
Definition: SociDB.cpp:280
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:41
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:59
beast::Journal
A generic endpoint for log messages.
Definition: Journal.h:58
ripple::checkpointPageCount
static auto checkpointPageCount
Definition: SociDB.cpp:36
memory
ripple::JobQueue
A pool of threads to perform work.
Definition: JobQueue.h:55
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:93
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:87
std::vector::end
T end(T... args)
std::unique_ptr
STL class.
ripple::SociConfig::SociConfig
SociConfig(std::pair< std::string, soci::backend_factory const & > init)
Definition: SociDB.cpp:75
std::string::data
T data(T... args)
ripple::BasicConfig
Holds unparsed configuration information.
Definition: BasicConfig.h:178
std::ref
T ref(T... args)
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:99
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:117