20 #include <ripple/unity/rocksdb.h>
22 #if RIPPLE_ROCKSDB_AVAILABLE
24 #include <ripple/basics/ByteUtilities.h>
25 #include <ripple/basics/contract.h>
26 #include <ripple/beast/core/CurrentThreadName.h>
27 #include <ripple/core/Config.h>
28 #include <ripple/nodestore/Factory.h>
29 #include <ripple/nodestore/Manager.h>
30 #include <ripple/nodestore/impl/BatchWriter.h>
31 #include <ripple/nodestore/impl/DecodedBlob.h>
32 #include <ripple/nodestore/impl/EncodedBlob.h>
39 class RocksDBEnv :
public rocksdb::EnvWrapper
42 RocksDBEnv() : EnvWrapper(rocksdb::Env::Default())
48 ThreadParams(
void (*f_)(
void*),
void* a_) : f(f_), a(a_)
57 thread_entry(
void* ptr)
59 ThreadParams*
const p(
reinterpret_cast<ThreadParams*
>(ptr));
60 void (*f)(
void*) = p->f;
67 ss <<
"rocksdb #" << id;
74 StartThread(
void (*f)(
void*),
void* a)
override
76 ThreadParams*
const p(
new ThreadParams(f, a));
77 EnvWrapper::StartThread(&RocksDBEnv::thread_entry, p);
83 class RocksDBBackend :
public Backend,
public BatchWriter::Callback
90 size_t const m_keyBytes;
91 Scheduler& m_scheduler;
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_scheduler(scheduler)
108 , m_batch(*this, scheduler)
111 Throw<std::runtime_error>(
"Missing path in RocksDBFactory backend");
113 rocksdb::BlockBasedTableOptions table_options;
116 if (keyValues.exists(
"cache_mb"))
117 table_options.block_cache = rocksdb::NewLRUCache(
118 get<int>(keyValues,
"cache_mb") *
megabytes(1));
120 if (
auto const v = get<int>(keyValues,
"filter_bits"))
122 bool const filter_blocks = !keyValues.exists(
"filter_full") ||
123 (get<int>(keyValues,
"filter_full") == 0);
124 table_options.filter_policy.reset(
125 rocksdb::NewBloomFilterPolicy(v, filter_blocks));
128 if (
get_if_exists(keyValues,
"open_files", m_options.max_open_files))
129 fdRequired_ = m_options.max_open_files;
131 if (keyValues.exists(
"file_size_mb"))
133 m_options.target_file_size_base =
134 megabytes(1) * get<int>(keyValues,
"file_size_mb");
135 m_options.max_bytes_for_level_base =
136 5 * m_options.target_file_size_base;
137 m_options.write_buffer_size = 2 * m_options.target_file_size_base;
141 keyValues,
"file_size_mult", m_options.target_file_size_multiplier);
143 if (keyValues.exists(
"bg_threads"))
145 m_options.env->SetBackgroundThreads(
146 get<int>(keyValues,
"bg_threads"), rocksdb::Env::LOW);
149 if (keyValues.exists(
"high_threads"))
151 auto const highThreads = get<int>(keyValues,
"high_threads");
152 m_options.env->SetBackgroundThreads(
153 highThreads, rocksdb::Env::HIGH);
158 m_options.max_background_flushes = highThreads;
161 m_options.compression = rocksdb::kSnappyCompression;
163 get_if_exists(keyValues,
"block_size", table_options.block_size);
165 if (keyValues.exists(
"universal_compaction") &&
166 (get<int>(keyValues,
"universal_compaction") != 0))
168 m_options.compaction_style = rocksdb::kCompactionStyleUniversal;
169 m_options.min_write_buffer_number_to_merge = 2;
170 m_options.max_write_buffer_number = 6;
171 m_options.write_buffer_size = 6 * m_options.target_file_size_base;
174 if (keyValues.exists(
"bbt_options"))
176 auto const s = rocksdb::GetBlockBasedTableOptionsFromString(
178 get<std::string>(keyValues,
"bbt_options"),
181 Throw<std::runtime_error>(
182 std::string(
"Unable to set RocksDB bbt_options: ") +
186 m_options.table_factory.reset(NewBlockBasedTableFactory(table_options));
188 if (keyValues.exists(
"options"))
190 auto const s = rocksdb::GetOptionsFromString(
191 m_options, get<std::string>(keyValues,
"options"), &m_options);
193 Throw<std::runtime_error>(
199 rocksdb::GetStringFromDBOptions(&s1, m_options,
"; ");
200 rocksdb::GetStringFromColumnFamilyOptions(&s2, m_options,
"; ");
201 JLOG(m_journal.
debug()) <<
"RocksDB DBOptions: " << s1;
202 JLOG(m_journal.
debug()) <<
"RocksDB CFOptions: " << s2;
205 ~RocksDBBackend()
override
211 open(
bool createIfMissing)
override
216 JLOG(m_journal.
error()) <<
"database is already open";
219 rocksdb::DB* db =
nullptr;
220 m_options.create_if_missing = createIfMissing;
221 rocksdb::Status
status = rocksdb::DB::Open(m_options, m_name, &db);
223 Throw<std::runtime_error>(
232 return static_cast<bool>(m_db);
243 boost::filesystem::path dir = m_name;
244 boost::filesystem::remove_all(dir);
265 rocksdb::ReadOptions
const options;
266 rocksdb::Slice
const slice(
static_cast<char const*
>(key), m_keyBytes);
270 rocksdb::Status getStatus = m_db->Get(options, slice, &
string);
274 DecodedBlob decoded(key,
string.
data(),
string.
size());
278 *pObject = decoded.createObject();
289 if (getStatus.IsCorruption())
293 else if (getStatus.IsNotFound())
301 JLOG(m_journal.
error()) << getStatus.ToString();
309 canFetchBatch()
override
315 fetchBatch(
std::size_t n,
void const*
const* keys)
override
317 Throw<std::runtime_error>(
"pure virtual called");
324 m_batch.store(
object);
328 storeBatch(
Batch const& batch)
override
331 rocksdb::WriteBatch wb;
335 for (
auto const& e : batch)
341 reinterpret_cast<char const*
>(encoded.getKey()),
344 reinterpret_cast<char const*
>(encoded.getData()),
348 rocksdb::WriteOptions
const options;
350 auto ret = m_db->Write(options, &wb);
353 Throw<std::runtime_error>(
"storeBatch failed: " + ret.ToString());
360 rocksdb::ReadOptions
const options;
364 for (it->SeekToFirst(); it->Valid(); it->Next())
366 if (it->key().size() == m_keyBytes)
369 it->key().data(), it->value().data(), it->value().size());
373 f(decoded.createObject());
378 JLOG(m_journal.
fatal())
379 <<
"Corrupt NodeObject #" << it->key().ToString(
true);
386 JLOG(m_journal.
fatal())
387 <<
"Bad key size = " << it->key().size();
393 getWriteLoad()
override
395 return m_batch.getWriteLoad();
399 setDeletePath()
override
407 writeBatch(
Batch const& batch)
override
419 fdRequired()
const override
427 class RocksDBFactory :
public Factory
437 ~RocksDBFactory()
override
443 getName()
const override
451 Section
const& keyValues,
453 Scheduler& scheduler,
456 return std::make_unique<RocksDBBackend>(
457 keyBytes, keyValues, scheduler, journal, &m_env);
461 static RocksDBFactory rocksDBFactory;