rippled
Loading...
Searching...
No Matches
DatabaseCon.cpp
1//------------------------------------------------------------------------------
2/*
3 This file is part of rippled: https://github.com/ripple/rippled
4 Copyright (c) 2012, 2013 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#include <xrpld/core/DatabaseCon.h>
21#include <xrpld/core/SociDB.h>
22
23#include <xrpl/basics/Log.h>
24#include <xrpl/basics/contract.h>
25
26#include <boost/algorithm/string.hpp>
27#include <boost/format.hpp>
28
29#include <memory>
30#include <unordered_map>
31
32namespace ripple {
33
35{
37 // Mutex protects the CheckpointersCollection
39 // Each checkpointer is given a unique id. All the checkpointers that are
40 // part of a DatabaseCon are part of this collection. When the DatabaseCon
41 // is destroyed, its checkpointer is removed from the collection
44
45public:
48 {
50 auto it = checkpointers_.find(id);
51 if (it != checkpointers_.end())
52 return it->second;
53 return {};
54 }
55
56 void
58 {
60 checkpointers_.erase(id);
61 }
62
65 std::shared_ptr<soci::session> const& session,
66 JobQueue& jobQueue,
67 Logs& logs)
68 {
70 auto const id = nextId_++;
71 auto const r = makeCheckpointer(id, session, jobQueue, logs);
72 checkpointers_[id] = r;
73 return r;
74 }
75};
76
78
84
86{
87 if (checkpointer_)
88 {
90
92 checkpointer_.reset();
93
94 // The references to our Checkpointer held by 'checkpointer_' and
95 // 'checkpointers' have been removed, so if the use count is nonzero, a
96 // checkpoint is currently in progress. Wait for it to end, otherwise
97 // creating a new DatabaseCon to the same database may fail due to the
98 // database being locked by our (now old) Checkpointer.
99 while (wk.use_count())
100 {
102 }
103 }
104}
105
108{
109 DatabaseCon::Setup setup;
110
111 setup.startUp = c.START_UP;
112 setup.standAlone = c.standalone();
113 setup.dataDir = c.legacy("database_path");
114 if (!setup.standAlone && setup.dataDir.empty())
115 {
116 Throw<std::runtime_error>("database_path must be set.");
117 }
118
119 if (!setup.globalPragma)
120 {
121 setup.globalPragma = [&c, &j]() {
122 auto const& sqlite = c.section("sqlite");
124 result->reserve(3);
125
126 // defaults
127 std::string safety_level;
128 std::string journal_mode = "wal";
129 std::string synchronous = "normal";
130 std::string temp_store = "file";
131 bool showRiskWarning = false;
132
133 if (set(safety_level, "safety_level", sqlite))
134 {
135 if (boost::iequals(safety_level, "low"))
136 {
137 // low safety defaults
138 journal_mode = "memory";
139 synchronous = "off";
140 temp_store = "memory";
141 showRiskWarning = true;
142 }
143 else if (!boost::iequals(safety_level, "high"))
144 {
145 Throw<std::runtime_error>(
146 "Invalid safety_level value: " + safety_level);
147 }
148 }
149
150 {
151 // #journal_mode Valid values : delete, truncate, persist,
152 // memory, wal, off
153 if (set(journal_mode, "journal_mode", sqlite) &&
154 !safety_level.empty())
155 {
156 Throw<std::runtime_error>(
157 "Configuration file may not define both "
158 "\"safety_level\" and \"journal_mode\"");
159 }
160 bool higherRisk = boost::iequals(journal_mode, "memory") ||
161 boost::iequals(journal_mode, "off");
162 showRiskWarning = showRiskWarning || higherRisk;
163 if (higherRisk || boost::iequals(journal_mode, "delete") ||
164 boost::iequals(journal_mode, "truncate") ||
165 boost::iequals(journal_mode, "persist") ||
166 boost::iequals(journal_mode, "wal"))
167 {
168 result->emplace_back(boost::str(
169 boost::format(CommonDBPragmaJournal) % journal_mode));
170 }
171 else
172 {
173 Throw<std::runtime_error>(
174 "Invalid journal_mode value: " + journal_mode);
175 }
176 }
177
178 {
179 // #synchronous Valid values : off, normal, full, extra
180 if (set(synchronous, "synchronous", sqlite) &&
181 !safety_level.empty())
182 {
183 Throw<std::runtime_error>(
184 "Configuration file may not define both "
185 "\"safety_level\" and \"synchronous\"");
186 }
187 bool higherRisk = boost::iequals(synchronous, "off");
188 showRiskWarning = showRiskWarning || higherRisk;
189 if (higherRisk || boost::iequals(synchronous, "normal") ||
190 boost::iequals(synchronous, "full") ||
191 boost::iequals(synchronous, "extra"))
192 {
193 result->emplace_back(boost::str(
194 boost::format(CommonDBPragmaSync) % synchronous));
195 }
196 else
197 {
198 Throw<std::runtime_error>(
199 "Invalid synchronous value: " + synchronous);
200 }
201 }
202
203 {
204 // #temp_store Valid values : default, file, memory
205 if (set(temp_store, "temp_store", sqlite) &&
206 !safety_level.empty())
207 {
208 Throw<std::runtime_error>(
209 "Configuration file may not define both "
210 "\"safety_level\" and \"temp_store\"");
211 }
212 bool higherRisk = boost::iequals(temp_store, "memory");
213 showRiskWarning = showRiskWarning || higherRisk;
214 if (higherRisk || boost::iequals(temp_store, "default") ||
215 boost::iequals(temp_store, "file"))
216 {
217 result->emplace_back(boost::str(
218 boost::format(CommonDBPragmaTemp) % temp_store));
219 }
220 else
221 {
222 Throw<std::runtime_error>(
223 "Invalid temp_store value: " + temp_store);
224 }
225 }
226
227 if (showRiskWarning && j && c.LEDGER_HISTORY > SQLITE_TUNING_CUTOFF)
228 {
229 JLOG(j->warn())
230 << "reducing the data integrity guarantees from the "
231 "default [sqlite] behavior is not recommended for "
232 "nodes storing large amounts of history, because of the "
233 "difficulty inherent in rebuilding corrupted data.";
234 }
235 XRPL_ASSERT(
236 result->size() == 3,
237 "ripple::setup_DatabaseCon::globalPragma : result size is 3");
238 return result;
239 }();
240 }
241 setup.useGlobalPragma = true;
242
243 auto setPragma =
244 [](std::string& pragma, std::string const& key, int64_t value) {
245 pragma = "PRAGMA " + key + "=" + std::to_string(value) + ";";
246 };
247
248 // Lgr Pragma
249 setPragma(setup.lgrPragma[0], "journal_size_limit", 1582080);
250
251 // TX Pragma
252 int64_t page_size = 4096;
253 int64_t journal_size_limit = 1582080;
254 if (c.exists("sqlite"))
255 {
256 auto& s = c.section("sqlite");
257 set(journal_size_limit, "journal_size_limit", s);
258 set(page_size, "page_size", s);
259 if (page_size < 512 || page_size > 65536)
260 Throw<std::runtime_error>(
261 "Invalid page_size. Must be between 512 and 65536.");
262
263 if (page_size & (page_size - 1))
264 Throw<std::runtime_error>(
265 "Invalid page_size. Must be a power of 2.");
266 }
267
268 setPragma(setup.txPragma[0], "page_size", page_size);
269 setPragma(setup.txPragma[1], "journal_size_limit", journal_size_limit);
270 setPragma(setup.txPragma[2], "max_page_count", 4294967294);
271 setPragma(setup.txPragma[3], "mmap_size", 17179869184);
272
273 return setup;
274}
275
278
279void
281{
282 if (!q)
283 Throw<std::logic_error>("No JobQueue");
285}
286
287} // namespace ripple
bool exists(std::string const &name) const
Returns true if a section with the given name exists.
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.
void erase(std::uintptr_t id)
std::shared_ptr< Checkpointer > create(std::shared_ptr< soci::session > const &session, JobQueue &jobQueue, Logs &logs)
std::shared_ptr< Checkpointer > fromId(std::uintptr_t id)
std::unordered_map< std::uintptr_t, std::shared_ptr< Checkpointer > > checkpointers_
std::uint32_t LEDGER_HISTORY
Definition Config.h:207
bool standalone() const
Definition Config.h:336
StartUpType START_UP
Definition Config.h:147
void setupCheckpointing(JobQueue *, Logs &)
std::shared_ptr< soci::session > const session_
std::shared_ptr< Checkpointer > checkpointer_
A pool of threads to perform work.
Definition JobQueue.h:58
Manages partitions for logging.
Definition Log.h:52
T empty(T... args)
T is_same_v
Use hash_* containers for keys that do not need a cryptographically secure hashing algorithm.
Definition algorithm.h:25
DatabaseCon::Setup setup_DatabaseCon(Config const &c, std::optional< beast::Journal > j=std::nullopt)
constexpr char const * CommonDBPragmaSync
Definition DBInit.h:33
CheckpointersCollection checkpointers
bool set(T &target, std::string const &name, Section const &section)
Set a value from a configuration Section If the named value is not found or doesn't parse as a T,...
constexpr char const * CommonDBPragmaTemp
Definition DBInit.h:34
constexpr std::uint32_t SQLITE_TUNING_CUTOFF
Definition DBInit.h:40
constexpr char const * CommonDBPragmaJournal
Definition DBInit.h:32
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:336
std::shared_ptr< Checkpointer > checkpointerFromId(std::uintptr_t id)
T sleep_for(T... args)
boost::filesystem::path dataDir
Definition DatabaseCon.h:94
static std::unique_ptr< std::vector< std::string > const > globalPragma
std::array< std::string, 1 > lgrPragma
Config::StartUpType startUp
Definition DatabaseCon.h:92
std::array< std::string, 4 > txPragma
T to_string(T... args)
T use_count(T... args)