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 
26 #include <ripple/basics/contract.h>
27 #include <ripple/basics/ByteUtilities.h>
28 #include <ripple/core/ConfigSections.h>
29 #include <ripple/core/SociDB.h>
30 #include <ripple/core/Config.h>
31 #include <memory>
32 #include <soci/sqlite3/soci-sqlite3.h>
33 #include <boost/filesystem.hpp>
34 
35 namespace ripple {
36 
37 static auto checkpointPageCount = 1000;
38 
39 namespace detail {
40 
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: " +
50  name + " 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,
60  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 = dbName == "validators" || dbName == "peerfinder"
70  ? ".sqlite" : ".db";
71  return detail::getSociSqliteInit(dbName, path, ext);
72 }
73 
74 } // detail
75 
78  : connectionString_ (std::move (init.first)),
79  backendFactory_ (init.second)
80 {
81 }
82 
83 SociConfig::SociConfig (BasicConfig const& config, std::string const& dbName)
84  : SociConfig (detail::getSociInit (config, dbName))
85 {
86 }
87 
89 {
90  return connectionString_;
91 }
92 
93 void SociConfig::open (soci::session& s) const
94 {
95  s.open (backendFactory_, connectionString ());
96 }
97 
98 void open (soci::session& s,
99  BasicConfig const& config,
100  std::string const& dbName)
101 {
102  SociConfig (config, dbName).open(s);
103 }
104 
105 void open (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 
115 static
116 sqlite_api::sqlite3* getConnection (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 
129 size_t getKBUsedAll (soci::session& s)
130 {
131  if (! getConnection (s))
132  Throw<std::logic_error> ("No connection found.");
133  return static_cast <size_t> (sqlite_api::sqlite3_memory_used () / kilobytes(1));
134 }
135 
136 size_t getKBUsedDB (soci::session& s)
137 {
138  // This function will have to be customized when other backends are added
139  if (auto conn = getConnection (s))
140  {
141  int cur = 0, hiw = 0;
142  sqlite_api::sqlite3_db_status (
143  conn, SQLITE_DBSTATUS_CACHE_USED, &cur, &hiw, 0);
144  return cur / kilobytes(1);
145  }
146  Throw<std::logic_error> ("");
147  return 0; // Silence compiler warning.
148 }
149 
150 void convert (soci::blob& from, std::vector<std::uint8_t>& to)
151 {
152  to.resize (from.get_len ());
153  if (to.empty ())
154  return;
155  from.read (0, reinterpret_cast<char*>(&to[0]), from.get_len ());
156 }
157 
158 void convert (soci::blob& from, std::string& to)
159 {
161  convert (from, tmp);
162  to.assign (tmp.begin (), tmp.end());
163 }
164 
165 void convert (std::vector<std::uint8_t> const& from, soci::blob& to)
166 {
167  if (!from.empty ())
168  to.write (0, reinterpret_cast<char const*>(&from[0]), from.size ());
169  else
170  to.trim (0);
171 }
172 
173 void convert (std::string const& from, soci::blob& to)
174 {
175  if (!from.empty ())
176  to.write (0, from.data (), from.size ());
177  else
178  to.trim (0);
179 }
180 
181 namespace {
182 
191 class WALCheckpointer : public Checkpointer
192 {
193 public:
194  WALCheckpointer (sqlite_api::sqlite3& conn, JobQueue& q, Logs& logs)
195  : conn_ (conn), jobQueue_ (q), j_ (logs.journal ("WALCheckpointer"))
196  {
197  sqlite_api::sqlite3_wal_hook (&conn_, &sqliteWALHook, this);
198  }
199 
200  ~WALCheckpointer () override = default;
201 
202 private:
203  sqlite_api::sqlite3& conn_;
204  std::mutex mutex_;
205  JobQueue& jobQueue_;
206 
207  bool running_ = false;
208  beast::Journal const j_;
209 
210  static
211  int sqliteWALHook (
212  void* cp, sqlite_api::sqlite3*, const char* dbName, int walSize)
213  {
214  if (walSize >= checkpointPageCount)
215  {
216  if (auto checkpointer = reinterpret_cast <WALCheckpointer*> (cp))
217  checkpointer->scheduleCheckpoint();
218  else
219  Throw<std::logic_error> ("Didn't get a WALCheckpointer");
220  }
221  return SQLITE_OK;
222  }
223 
224  void scheduleCheckpoint ()
225  {
226  {
227  std::lock_guard lock (mutex_);
228  if (running_)
229  return;
230  running_ = true;
231  }
232 
233  // If the Job is not added to the JobQueue then we're not running_.
234  if (! jobQueue_.addJob (
235  jtWAL, "WAL", [this] (Job&) { checkpoint(); }))
236  {
237  std::lock_guard lock (mutex_);
238  running_ = false;
239  }
240  }
241 
242  void checkpoint ()
243  {
244  int log = 0, ckpt = 0;
245  int ret = sqlite3_wal_checkpoint_v2 (
246  &conn_, nullptr, SQLITE_CHECKPOINT_PASSIVE, &log, &ckpt);
247 
248  auto fname = sqlite3_db_filename (&conn_, "main");
249  if (ret != SQLITE_OK)
250  {
251  auto jm = (ret == SQLITE_LOCKED) ? j_.trace() : j_.warn();
252  JLOG (jm)
253  << "WAL(" << fname << "): error " << ret;
254  }
255  else
256  {
257  JLOG (j_.trace())
258  << "WAL(" << fname << "): frames="
259  << log << ", written=" << ckpt;
260  }
261 
262  std::lock_guard lock (mutex_);
263  running_ = false;
264  }
265 };
266 
267 } // namespace
268 
270  soci::session& session, JobQueue& queue, Logs& logs)
271 {
272  if (auto conn = getConnection (session))
273  return std::make_unique <WALCheckpointer> (*conn, queue, logs);
274  return {};
275 }
276 
277 }
278 
279 #if defined(__clang__)
280 #pragma clang diagnostic pop
281 #endif
ripple::SociConfig
SociConfig is used when a client wants to delay opening a soci::session after parsing the config para...
Definition: SociDB.h:75
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:49
beast::Journal::trace
Stream trace() const
Severity stream access functions.
Definition: Journal.h:287
ripple::getKBUsedDB
size_t getKBUsedDB(soci::session &s)
Definition: SociDB.cpp:136
ripple::getKBUsedAll
size_t getKBUsedAll(soci::session &s)
Definition: SociDB.cpp:129
std::pair
ripple::convert
void convert(soci::blob &from, std::vector< std::uint8_t > &to)
Definition: SociDB.cpp:150
std::vector
STL class.
std::vector::size
T size(T... args)
beast::Journal::warn
Stream warn() const
Definition: Journal.h:302
std::lock_guard
STL class.
ripple::kilobytes
constexpr auto kilobytes(T value) noexcept
Definition: ByteUtilities.h:26
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:269
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:173
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:60
ripple::checkpointPageCount
static auto checkpointPageCount
Definition: SociDB.cpp:37
memory
ripple::JobQueue
A pool of threads to perform work.
Definition: JobQueue.h:56
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:57
ripple::SociConfig::open
void open(soci::session &s) const
Definition: SociDB.cpp:93
ripple::SociConfig::backendFactory_
soci::backend_factory const & backendFactory_
Definition: SociDB.h:78
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:77
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)
std::unique_ptr
STL class.
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:177
std::ref
T ref(T... args)
ripple::get
T & get(EitherAmount &amt)
Definition: AmountSpec.h:124
ripple::open
void open(soci::session &s, BasicConfig const &config, std::string const &dbName)
Open a soci session.
Definition: SociDB.cpp:98
ripple::BasicConfig::section
Section & section(std::string const &name)
Returns the section with the given name.
Definition: BasicConfig.cpp:140
ripple::getConnection
static sqlite_api::sqlite3 * getConnection(soci::session &s)
Definition: SociDB.cpp:116