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;
94 int fdRequired_ = 2048;
95 rocksdb::Options m_options;
99 Section
const& keyValues,
100 Scheduler& scheduler,
103 : m_deletePath(false)
105 , m_keyBytes(keyBytes)
106 , m_batch(*this, scheduler)
109 Throw<std::runtime_error>(
"Missing path in RocksDBFactory backend");
111 rocksdb::BlockBasedTableOptions table_options;
114 if (keyValues.exists(
"cache_mb"))
115 table_options.block_cache = rocksdb::NewLRUCache(
116 get<int>(keyValues,
"cache_mb") *
megabytes(1));
118 if (
auto const v = get<int>(keyValues,
"filter_bits"))
120 bool const filter_blocks = !keyValues.exists(
"filter_full") ||
121 (get<int>(keyValues,
"filter_full") == 0);
122 table_options.filter_policy.reset(
123 rocksdb::NewBloomFilterPolicy(v, filter_blocks));
126 if (
get_if_exists(keyValues,
"open_files", m_options.max_open_files))
127 fdRequired_ = m_options.max_open_files;
129 if (keyValues.exists(
"file_size_mb"))
131 m_options.target_file_size_base =
132 megabytes(1) * get<int>(keyValues,
"file_size_mb");
133 m_options.max_bytes_for_level_base =
134 5 * m_options.target_file_size_base;
135 m_options.write_buffer_size = 2 * m_options.target_file_size_base;
139 keyValues,
"file_size_mult", m_options.target_file_size_multiplier);
141 if (keyValues.exists(
"bg_threads"))
143 m_options.env->SetBackgroundThreads(
144 get<int>(keyValues,
"bg_threads"), rocksdb::Env::LOW);
147 if (keyValues.exists(
"high_threads"))
149 auto const highThreads = get<int>(keyValues,
"high_threads");
150 m_options.env->SetBackgroundThreads(
151 highThreads, rocksdb::Env::HIGH);
156 m_options.max_background_flushes = highThreads;
159 m_options.compression = rocksdb::kSnappyCompression;
161 get_if_exists(keyValues,
"block_size", table_options.block_size);
163 if (keyValues.exists(
"universal_compaction") &&
164 (get<int>(keyValues,
"universal_compaction") != 0))
166 m_options.compaction_style = rocksdb::kCompactionStyleUniversal;
167 m_options.min_write_buffer_number_to_merge = 2;
168 m_options.max_write_buffer_number = 6;
169 m_options.write_buffer_size = 6 * m_options.target_file_size_base;
172 if (keyValues.exists(
"bbt_options"))
174 auto const s = rocksdb::GetBlockBasedTableOptionsFromString(
176 get<std::string>(keyValues,
"bbt_options"),
179 Throw<std::runtime_error>(
180 std::string(
"Unable to set RocksDB bbt_options: ") +
184 m_options.table_factory.reset(NewBlockBasedTableFactory(table_options));
186 if (keyValues.exists(
"options"))
188 auto const s = rocksdb::GetOptionsFromString(
189 m_options, get<std::string>(keyValues,
"options"), &m_options);
191 Throw<std::runtime_error>(
197 rocksdb::GetStringFromDBOptions(&s1, m_options,
"; ");
198 rocksdb::GetStringFromColumnFamilyOptions(&s2, m_options,
"; ");
199 JLOG(m_journal.
debug()) <<
"RocksDB DBOptions: " << s1;
200 JLOG(m_journal.
debug()) <<
"RocksDB CFOptions: " << s2;
203 ~RocksDBBackend()
override
209 open(
bool createIfMissing)
override
214 JLOG(m_journal.
error()) <<
"database is already open";
217 rocksdb::DB* db =
nullptr;
218 m_options.create_if_missing = createIfMissing;
219 rocksdb::Status
status = rocksdb::DB::Open(m_options, m_name, &db);
221 Throw<std::runtime_error>(
230 return static_cast<bool>(m_db);
241 boost::filesystem::path dir = m_name;
242 boost::filesystem::remove_all(dir);
263 rocksdb::ReadOptions
const options;
264 rocksdb::Slice
const slice(
static_cast<char const*
>(key), m_keyBytes);
268 rocksdb::Status getStatus = m_db->Get(options, slice, &
string);
272 DecodedBlob decoded(key,
string.
data(),
string.
size());
276 *pObject = decoded.createObject();
287 if (getStatus.IsCorruption())
291 else if (getStatus.IsNotFound())
299 JLOG(m_journal.
error()) << getStatus.ToString();
311 for (
auto const& h : hashes)
321 return {results,
ok};
327 m_batch.store(
object);
331 storeBatch(
Batch const& batch)
override
334 rocksdb::WriteBatch wb;
338 for (
auto const& e : batch)
344 reinterpret_cast<char const*
>(encoded.getKey()),
347 reinterpret_cast<char const*
>(encoded.getData()),
351 rocksdb::WriteOptions
const options;
353 auto ret = m_db->Write(options, &wb);
356 Throw<std::runtime_error>(
"storeBatch failed: " + ret.ToString());
368 rocksdb::ReadOptions
const options;
372 for (it->SeekToFirst(); it->Valid(); it->Next())
374 if (it->key().size() == m_keyBytes)
377 it->key().data(), it->value().data(), it->value().size());
381 f(decoded.createObject());
386 JLOG(m_journal.
fatal())
387 <<
"Corrupt NodeObject #" << it->key().ToString(
true);
394 JLOG(m_journal.
fatal())
395 <<
"Bad key size = " << it->key().size();
401 getWriteLoad()
override
403 return m_batch.getWriteLoad();
407 setDeletePath()
override
415 writeBatch(
Batch const& batch)
override
422 fdRequired()
const override
430 class RocksDBFactory :
public Factory
440 ~RocksDBFactory()
override
446 getName()
const override
454 Section
const& keyValues,
456 Scheduler& scheduler,
459 return std::make_unique<RocksDBBackend>(
460 keyBytes, keyValues, scheduler, journal, &m_env);
464 static RocksDBFactory rocksDBFactory;