aboutsummaryrefslogtreecommitdiff
path: root/src/dbwrapper.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/dbwrapper.cpp')
-rw-r--r--src/dbwrapper.cpp233
1 files changed, 192 insertions, 41 deletions
diff --git a/src/dbwrapper.cpp b/src/dbwrapper.cpp
index 7cf6bef90f..c95937ba75 100644
--- a/src/dbwrapper.cpp
+++ b/src/dbwrapper.cpp
@@ -4,9 +4,12 @@
#include <dbwrapper.h>
+#include <clientversion.h>
#include <logging.h>
#include <random.h>
-#include <tinyformat.h>
+#include <serialize.h>
+#include <span.h>
+#include <streams.h>
#include <util/fs.h>
#include <util/fs_helpers.h>
#include <util/strencodings.h>
@@ -23,9 +26,31 @@
#include <leveldb/helpers/memenv/memenv.h>
#include <leveldb/iterator.h>
#include <leveldb/options.h>
+#include <leveldb/slice.h>
#include <leveldb/status.h>
+#include <leveldb/write_batch.h>
#include <memory>
#include <optional>
+#include <utility>
+
+static auto CharCast(const std::byte* data) { return reinterpret_cast<const char*>(data); }
+
+bool DestroyDB(const std::string& path_str)
+{
+ return leveldb::DestroyDB(path_str, {}).ok();
+}
+
+/** Handle database error by throwing dbwrapper_error exception.
+ */
+static void HandleError(const leveldb::Status& status)
+{
+ if (status.ok())
+ return;
+ const std::string errmsg = "Fatal LevelDB error: " + status.ToString();
+ LogPrintf("%s\n", errmsg);
+ LogPrintf("You can use -debug=leveldb to get more complete diagnostic messages\n");
+ throw dbwrapper_error(errmsg);
+}
class CBitcoinLevelDBLogger : public leveldb::Logger {
public:
@@ -127,24 +152,91 @@ static leveldb::Options GetOptions(size_t nCacheSize)
return options;
}
+struct CDBBatch::WriteBatchImpl {
+ leveldb::WriteBatch batch;
+};
+
+CDBBatch::CDBBatch(const CDBWrapper& _parent) : parent(_parent),
+ m_impl_batch{std::make_unique<CDBBatch::WriteBatchImpl>()},
+ ssValue(SER_DISK, CLIENT_VERSION){};
+
+CDBBatch::~CDBBatch() = default;
+
+void CDBBatch::Clear()
+{
+ m_impl_batch->batch.Clear();
+ size_estimate = 0;
+}
+
+void CDBBatch::WriteImpl(Span<const std::byte> key, CDataStream& ssValue)
+{
+ leveldb::Slice slKey(CharCast(key.data()), key.size());
+ ssValue.Xor(dbwrapper_private::GetObfuscateKey(parent));
+ leveldb::Slice slValue(CharCast(ssValue.data()), ssValue.size());
+ m_impl_batch->batch.Put(slKey, slValue);
+ // LevelDB serializes writes as:
+ // - byte: header
+ // - varint: key length (1 byte up to 127B, 2 bytes up to 16383B, ...)
+ // - byte[]: key
+ // - varint: value length
+ // - byte[]: value
+ // The formula below assumes the key and value are both less than 16k.
+ size_estimate += 3 + (slKey.size() > 127) + slKey.size() + (slValue.size() > 127) + slValue.size();
+}
+
+void CDBBatch::EraseImpl(Span<const std::byte> key)
+{
+ leveldb::Slice slKey(CharCast(key.data()), key.size());
+ m_impl_batch->batch.Delete(slKey);
+ // LevelDB serializes erases as:
+ // - byte: header
+ // - varint: key length
+ // - byte[]: key
+ // The formula below assumes the key is less than 16kB.
+ size_estimate += 2 + (slKey.size() > 127) + slKey.size();
+}
+
+struct LevelDBContext {
+ //! custom environment this database is using (may be nullptr in case of default environment)
+ leveldb::Env* penv;
+
+ //! database options used
+ leveldb::Options options;
+
+ //! options used when reading from the database
+ leveldb::ReadOptions readoptions;
+
+ //! options used when iterating over values of the database
+ leveldb::ReadOptions iteroptions;
+
+ //! options used when writing to the database
+ leveldb::WriteOptions writeoptions;
+
+ //! options used when sync writing to the database
+ leveldb::WriteOptions syncoptions;
+
+ //! the database itself
+ leveldb::DB* pdb;
+};
+
CDBWrapper::CDBWrapper(const DBParams& params)
- : m_name{fs::PathToString(params.path.stem())}, m_path{params.path}, m_is_memory{params.memory_only}
+ : m_db_context{std::make_unique<LevelDBContext>()}, m_name{fs::PathToString(params.path.stem())}, m_path{params.path}, m_is_memory{params.memory_only}
{
- penv = nullptr;
- readoptions.verify_checksums = true;
- iteroptions.verify_checksums = true;
- iteroptions.fill_cache = false;
- syncoptions.sync = true;
- options = GetOptions(params.cache_bytes);
- options.create_if_missing = true;
+ DBContext().penv = nullptr;
+ DBContext().readoptions.verify_checksums = true;
+ DBContext().iteroptions.verify_checksums = true;
+ DBContext().iteroptions.fill_cache = false;
+ DBContext().syncoptions.sync = true;
+ DBContext().options = GetOptions(params.cache_bytes);
+ DBContext().options.create_if_missing = true;
if (params.memory_only) {
- penv = leveldb::NewMemEnv(leveldb::Env::Default());
- options.env = penv;
+ DBContext().penv = leveldb::NewMemEnv(leveldb::Env::Default());
+ DBContext().options.env = DBContext().penv;
} else {
if (params.wipe_data) {
LogPrintf("Wiping LevelDB in %s\n", fs::PathToString(params.path));
- leveldb::Status result = leveldb::DestroyDB(fs::PathToString(params.path), options);
- dbwrapper_private::HandleError(result);
+ leveldb::Status result = leveldb::DestroyDB(fs::PathToString(params.path), DBContext().options);
+ HandleError(result);
}
TryCreateDirectories(params.path);
LogPrintf("Opening LevelDB in %s\n", fs::PathToString(params.path));
@@ -153,13 +245,13 @@ CDBWrapper::CDBWrapper(const DBParams& params)
// because on POSIX leveldb passes the byte string directly to ::open(), and
// on Windows it converts from UTF-8 to UTF-16 before calling ::CreateFileW
// (see env_posix.cc and env_windows.cc).
- leveldb::Status status = leveldb::DB::Open(options, fs::PathToString(params.path), &pdb);
- dbwrapper_private::HandleError(status);
+ leveldb::Status status = leveldb::DB::Open(DBContext().options, fs::PathToString(params.path), &DBContext().pdb);
+ HandleError(status);
LogPrintf("Opened LevelDB successfully\n");
if (params.options.force_compact) {
LogPrintf("Starting database compaction of %s\n", fs::PathToString(params.path));
- pdb->CompactRange(nullptr, nullptr);
+ DBContext().pdb->CompactRange(nullptr, nullptr);
LogPrintf("Finished database compaction of %s\n", fs::PathToString(params.path));
}
@@ -185,16 +277,16 @@ CDBWrapper::CDBWrapper(const DBParams& params)
CDBWrapper::~CDBWrapper()
{
- delete pdb;
- pdb = nullptr;
- delete options.filter_policy;
- options.filter_policy = nullptr;
- delete options.info_log;
- options.info_log = nullptr;
- delete options.block_cache;
- options.block_cache = nullptr;
- delete penv;
- options.env = nullptr;
+ delete DBContext().pdb;
+ DBContext().pdb = nullptr;
+ delete DBContext().options.filter_policy;
+ DBContext().options.filter_policy = nullptr;
+ delete DBContext().options.info_log;
+ DBContext().options.info_log = nullptr;
+ delete DBContext().options.block_cache;
+ DBContext().options.block_cache = nullptr;
+ delete DBContext().penv;
+ DBContext().options.env = nullptr;
}
bool CDBWrapper::WriteBatch(CDBBatch& batch, bool fSync)
@@ -204,8 +296,8 @@ bool CDBWrapper::WriteBatch(CDBBatch& batch, bool fSync)
if (log_memory) {
mem_before = DynamicMemoryUsage() / 1024.0 / 1024;
}
- leveldb::Status status = pdb->Write(fSync ? syncoptions : writeoptions, &batch.batch);
- dbwrapper_private::HandleError(status);
+ leveldb::Status status = DBContext().pdb->Write(fSync ? DBContext().syncoptions : DBContext().writeoptions, &batch.m_impl_batch->batch);
+ HandleError(status);
if (log_memory) {
double mem_after = DynamicMemoryUsage() / 1024.0 / 1024;
LogPrint(BCLog::LEVELDB, "WriteBatch memory usage: db=%s, before=%.1fMiB, after=%.1fMiB\n",
@@ -218,7 +310,7 @@ size_t CDBWrapper::DynamicMemoryUsage() const
{
std::string memory;
std::optional<size_t> parsed;
- if (!pdb->GetProperty("leveldb.approximate-memory-usage", &memory) || !(parsed = ToIntegral<size_t>(memory))) {
+ if (!DBContext().pdb->GetProperty("leveldb.approximate-memory-usage", &memory) || !(parsed = ToIntegral<size_t>(memory))) {
LogPrint(BCLog::LEVELDB, "Failed to get approximate-memory-usage property\n");
return 0;
}
@@ -244,6 +336,45 @@ std::vector<unsigned char> CDBWrapper::CreateObfuscateKey() const
return ret;
}
+std::optional<std::string> CDBWrapper::ReadImpl(Span<const std::byte> key) const
+{
+ leveldb::Slice slKey(CharCast(key.data()), key.size());
+ std::string strValue;
+ leveldb::Status status = DBContext().pdb->Get(DBContext().readoptions, slKey, &strValue);
+ if (!status.ok()) {
+ if (status.IsNotFound())
+ return std::nullopt;
+ LogPrintf("LevelDB read failure: %s\n", status.ToString());
+ HandleError(status);
+ }
+ return strValue;
+}
+
+bool CDBWrapper::ExistsImpl(Span<const std::byte> key) const
+{
+ leveldb::Slice slKey(CharCast(key.data()), key.size());
+
+ std::string strValue;
+ leveldb::Status status = DBContext().pdb->Get(DBContext().readoptions, slKey, &strValue);
+ if (!status.ok()) {
+ if (status.IsNotFound())
+ return false;
+ LogPrintf("LevelDB read failure: %s\n", status.ToString());
+ HandleError(status);
+ }
+ return true;
+}
+
+size_t CDBWrapper::EstimateSizeImpl(Span<const std::byte> key1, Span<const std::byte> key2) const
+{
+ leveldb::Slice slKey1(CharCast(key1.data()), key1.size());
+ leveldb::Slice slKey2(CharCast(key2.data()), key2.size());
+ uint64_t size = 0;
+ leveldb::Range range(slKey1, slKey2);
+ DBContext().pdb->GetApproximateSizes(&range, 1, &size);
+ return size;
+}
+
bool CDBWrapper::IsEmpty()
{
std::unique_ptr<CDBIterator> it(NewIterator());
@@ -251,23 +382,43 @@ bool CDBWrapper::IsEmpty()
return !(it->Valid());
}
-CDBIterator::~CDBIterator() { delete piter; }
-bool CDBIterator::Valid() const { return piter->Valid(); }
-void CDBIterator::SeekToFirst() { piter->SeekToFirst(); }
-void CDBIterator::Next() { piter->Next(); }
+struct CDBIterator::IteratorImpl {
+ const std::unique_ptr<leveldb::Iterator> iter;
-namespace dbwrapper_private {
+ explicit IteratorImpl(leveldb::Iterator* _iter) : iter{_iter} {}
+};
-void HandleError(const leveldb::Status& status)
+CDBIterator::CDBIterator(const CDBWrapper& _parent, std::unique_ptr<IteratorImpl> _piter) : parent(_parent),
+ m_impl_iter(std::move(_piter)) {}
+
+CDBIterator* CDBWrapper::NewIterator()
{
- if (status.ok())
- return;
- const std::string errmsg = "Fatal LevelDB error: " + status.ToString();
- LogPrintf("%s\n", errmsg);
- LogPrintf("You can use -debug=leveldb to get more complete diagnostic messages\n");
- throw dbwrapper_error(errmsg);
+ return new CDBIterator{*this, std::make_unique<CDBIterator::IteratorImpl>(DBContext().pdb->NewIterator(DBContext().iteroptions))};
}
+void CDBIterator::SeekImpl(Span<const std::byte> key)
+{
+ leveldb::Slice slKey(CharCast(key.data()), key.size());
+ m_impl_iter->iter->Seek(slKey);
+}
+
+Span<const std::byte> CDBIterator::GetKeyImpl() const
+{
+ return MakeByteSpan(m_impl_iter->iter->key());
+}
+
+Span<const std::byte> CDBIterator::GetValueImpl() const
+{
+ return MakeByteSpan(m_impl_iter->iter->value());
+}
+
+CDBIterator::~CDBIterator() = default;
+bool CDBIterator::Valid() const { return m_impl_iter->iter->Valid(); }
+void CDBIterator::SeekToFirst() { m_impl_iter->iter->SeekToFirst(); }
+void CDBIterator::Next() { m_impl_iter->iter->Next(); }
+
+namespace dbwrapper_private {
+
const std::vector<unsigned char>& GetObfuscateKey(const CDBWrapper &w)
{
return w.obfuscate_key;