20#include <xrpld/unity/rocksdb.h>
22#if RIPPLE_ROCKSDB_AVAILABLE
23#include <xrpld/core/Config.h>
24#include <xrpld/nodestore/Factory.h>
25#include <xrpld/nodestore/Manager.h>
26#include <xrpld/nodestore/detail/BatchWriter.h>
27#include <xrpld/nodestore/detail/DecodedBlob.h>
28#include <xrpld/nodestore/detail/EncodedBlob.h>
29#include <xrpl/basics/ByteUtilities.h>
30#include <xrpl/basics/contract.h>
31#include <xrpl/basics/safe_cast.h>
32#include <xrpl/beast/core/CurrentThreadName.h>
40class RocksDBEnv :
public rocksdb::EnvWrapper
43 RocksDBEnv() : EnvWrapper(rocksdb::Env::Default())
49 ThreadParams(
void (*f_)(
void*),
void* a_) : f(f_), a(a_)
58 thread_entry(
void* ptr)
60 ThreadParams*
const p(
reinterpret_cast<ThreadParams*
>(ptr));
61 void (*f)(
void*) = p->f;
68 ss <<
"rocksdb #" << id;
75 StartThread(
void (*f)(
void*),
void* a)
override
77 ThreadParams*
const p(
new ThreadParams(f, a));
78 EnvWrapper::StartThread(&RocksDBEnv::thread_entry, p);
84class RocksDBBackend :
public Backend,
public BatchWriter::Callback
91 size_t const m_keyBytes;
95 int fdRequired_ = 2048;
96 rocksdb::Options m_options;
100 Section
const& keyValues,
101 Scheduler& scheduler,
104 : m_deletePath(false)
106 , m_keyBytes(keyBytes)
107 , m_batch(*this, scheduler)
110 Throw<std::runtime_error>(
"Missing path in RocksDBFactory backend");
112 rocksdb::BlockBasedTableOptions table_options;
116 keyValues.exists(
"hard_set") && get<bool>(keyValues,
"hard_set");
118 if (keyValues.exists(
"cache_mb"))
120 auto size = get<int>(keyValues,
"cache_mb");
122 if (!hard_set && size == 256)
125 table_options.block_cache = rocksdb::NewLRUCache(
megabytes(size));
128 if (
auto const v = get<int>(keyValues,
"filter_bits"))
130 bool const filter_blocks = !keyValues.exists(
"filter_full") ||
131 (get<int>(keyValues,
"filter_full") == 0);
132 table_options.filter_policy.reset(
133 rocksdb::NewBloomFilterPolicy(v, filter_blocks));
136 if (
get_if_exists(keyValues,
"open_files", m_options.max_open_files))
138 if (!hard_set && m_options.max_open_files == 2000)
139 m_options.max_open_files = 8000;
141 fdRequired_ = m_options.max_open_files + 128;
144 if (keyValues.exists(
"file_size_mb"))
146 auto file_size_mb = get<int>(keyValues,
"file_size_mb");
148 if (!hard_set && file_size_mb == 8)
151 m_options.target_file_size_base =
megabytes(file_size_mb);
152 m_options.max_bytes_for_level_base =
153 5 * m_options.target_file_size_base;
154 m_options.write_buffer_size = 2 * m_options.target_file_size_base;
158 keyValues,
"file_size_mult", m_options.target_file_size_multiplier);
160 if (keyValues.exists(
"bg_threads"))
162 m_options.env->SetBackgroundThreads(
163 get<int>(keyValues,
"bg_threads"), rocksdb::Env::LOW);
166 if (keyValues.exists(
"high_threads"))
168 auto const highThreads = get<int>(keyValues,
"high_threads");
169 m_options.env->SetBackgroundThreads(
170 highThreads, rocksdb::Env::HIGH);
175 m_options.max_background_flushes = highThreads;
178 m_options.compression = rocksdb::kSnappyCompression;
180 get_if_exists(keyValues,
"block_size", table_options.block_size);
182 if (keyValues.exists(
"universal_compaction") &&
183 (get<int>(keyValues,
"universal_compaction") != 0))
185 m_options.compaction_style = rocksdb::kCompactionStyleUniversal;
186 m_options.min_write_buffer_number_to_merge = 2;
187 m_options.max_write_buffer_number = 6;
188 m_options.write_buffer_size = 6 * m_options.target_file_size_base;
191 if (keyValues.exists(
"bbt_options"))
193 auto const s = rocksdb::GetBlockBasedTableOptionsFromString(
194 table_options,
get(keyValues,
"bbt_options"), &table_options);
196 Throw<std::runtime_error>(
197 std::string(
"Unable to set RocksDB bbt_options: ") +
201 m_options.table_factory.reset(NewBlockBasedTableFactory(table_options));
203 if (keyValues.exists(
"options"))
205 auto const s = rocksdb::GetOptionsFromString(
206 m_options,
get(keyValues,
"options"), &m_options);
208 Throw<std::runtime_error>(
214 rocksdb::GetStringFromDBOptions(&s1, m_options,
"; ");
215 rocksdb::GetStringFromColumnFamilyOptions(&s2, m_options,
"; ");
216 JLOG(m_journal.
debug()) <<
"RocksDB DBOptions: " << s1;
217 JLOG(m_journal.
debug()) <<
"RocksDB CFOptions: " << s2;
220 ~RocksDBBackend()
override
226 open(
bool createIfMissing)
override
231 "ripple::NodeStore::RocksDBBackend::open : database is already "
233 JLOG(m_journal.
error()) <<
"database is already open";
236 rocksdb::DB* db =
nullptr;
237 m_options.create_if_missing = createIfMissing;
238 rocksdb::Status
status = rocksdb::DB::Open(m_options, m_name, &db);
240 Throw<std::runtime_error>(
249 return static_cast<bool>(m_db);
260 boost::filesystem::path dir = m_name;
261 boost::filesystem::remove_all(dir);
279 "ripple::NodeStore::RocksDBBackend::fetch : non-null database");
284 rocksdb::ReadOptions
const options;
285 rocksdb::Slice
const slice(
static_cast<char const*
>(key), m_keyBytes);
289 rocksdb::Status getStatus = m_db->Get(options, slice, &
string);
293 DecodedBlob decoded(key,
string.
data(),
string.
size());
297 *pObject = decoded.createObject();
308 if (getStatus.IsCorruption())
312 else if (getStatus.IsNotFound())
319 Status(customCode + unsafe_cast<int>(getStatus.code()));
321 JLOG(m_journal.
error()) << getStatus.ToString();
333 for (
auto const& h : hashes)
343 return {results,
ok};
349 m_batch.store(
object);
353 storeBatch(Batch
const& batch)
override
357 "ripple::NodeStore::RocksDBBackend::storeBatch : non-null "
359 rocksdb::WriteBatch wb;
361 for (
auto const& e : batch)
363 EncodedBlob encoded(e);
367 reinterpret_cast<char const*
>(encoded.getKey()),
370 reinterpret_cast<char const*
>(encoded.getData()),
374 rocksdb::WriteOptions
const options;
376 auto ret = m_db->Write(options, &wb);
379 Throw<std::runtime_error>(
"storeBatch failed: " + ret.ToString());
392 "ripple::NodeStore::RocksDBBackend::for_each : non-null database");
393 rocksdb::ReadOptions
const options;
397 for (it->SeekToFirst(); it->Valid(); it->Next())
399 if (it->key().size() == m_keyBytes)
402 it->key().data(), it->value().data(), it->value().size());
406 f(decoded.createObject());
411 JLOG(m_journal.
fatal())
412 <<
"Corrupt NodeObject #" << it->key().ToString(
true);
419 JLOG(m_journal.
fatal())
420 <<
"Bad key size = " << it->key().size();
426 getWriteLoad()
override
428 return m_batch.getWriteLoad();
432 setDeletePath()
override
440 writeBatch(Batch
const& batch)
override
447 fdRequired()
const override
455class RocksDBFactory :
public Factory
462 Manager::instance().insert(*
this);
465 ~RocksDBFactory()
override
467 Manager::instance().erase(*
this);
471 getName()
const override
479 Section
const& keyValues,
481 Scheduler& scheduler,
484 return std::make_unique<RocksDBBackend>(
485 keyBytes, keyValues, scheduler, journal, &m_env);
489static RocksDBFactory rocksDBFactory;
A generic endpoint for log messages.
void setCurrentThreadName(std::string_view newThreadName)
Changes the name of the caller thread.
Status
Return codes from Backend operations.
Use hash_* containers for keys that do not need a cryptographically secure hashing algorithm.
constexpr auto megabytes(T value) noexcept
bool get_if_exists(Section const §ion, std::string const &name, T &v)
void open(soci::session &s, BasicConfig const &config, std::string const &dbName)
Open a soci session.
T get(Section const §ion, std::string const &name, T const &defaultValue=T{})
Retrieve a key/value pair from a section.