.. _program_listing_file_include_util_db_backends_RocksDbBackend.cpp: Program Listing for File RocksDbBackend.cpp =========================================== |exhale_lsh| :ref:`Return to documentation for file ` (``include\util\db\backends\RocksDbBackend.cpp``) .. |exhale_lsh| unicode:: U+021B0 .. UPWARDS ARROW WITH TIP LEFTWARDS .. code-block:: cpp #include "RocksDbBackend.hpp" #include #include #include #include #include #include #include namespace PDJE_UTIL::db::backends { namespace { rocksdb::Slice slice_of(std::string_view value) { return rocksdb::Slice(value.data(), value.size()); } } // namespace class RocksDbBackend::Impl { public: ~Impl() { delete db_; } common::Result open(const config_type &cfg) { if (db_ != nullptr) { return common::Result::failure( { common::StatusCode::invalid_argument, "RocksDB backend is already open." }); } if (cfg.path.empty()) { return common::Result::failure( { common::StatusCode::invalid_argument, "RocksDbConfig.path must not be empty." }); } config_ = cfg; if (config_.open_options.truncate_if_exists) { auto destroyed = RocksDbBackend::destroy(config_); if (!destroyed.ok()) { return destroyed; } } const bool exists = std::filesystem::exists(config_.path); if (!exists && !config_.open_options.create_if_missing && !config_.open_options.read_only) { return common::Result::failure( { common::StatusCode::not_found, "RocksDB directory does not exist." }); } auto options = db_options_; options.create_if_missing = config_.open_options.create_if_missing; options.error_if_exists = false; rocksdb::Status status; if (config_.open_options.read_only) { status = rocksdb::DB::OpenForReadOnly( options, config_.path.string(), &db_); } else { status = rocksdb::DB::Open(options, config_.path.string(), &db_); } if (!status.ok()) { db_ = nullptr; return common::Result::failure( { common::StatusCode::backend_error, status.ToString() }); } read_options_ = {}; write_options_ = {}; return common::Result::success(); } common::Result close() { delete db_; db_ = nullptr; config_ = {}; db_options_ = {}; read_options_ = {}; write_options_ = {}; return common::Result::success(); } common::Result contains(std::string_view key) const { if (auto status = require_open(); !status.ok()) { return common::Result::failure(status); } std::string value; auto get_status = db_->Get(read_options_, slice_of(key), &value); if (get_status.IsNotFound()) { return common::Result::success(false); } if (!get_status.ok()) { return common::Result::failure( { common::StatusCode::backend_error, get_status.ToString() }); } return common::Result::success(true); } common::Result get_text(std::string_view key) const { auto raw = get_raw(key); if (!raw.ok()) { return common::Result::failure(raw.status()); } if (raw.value().empty() || raw.value().front() != 'T') { return common::Result::failure( { common::StatusCode::type_mismatch, "RocksDB value is not stored as text." }); } return common::Result::success( std::string(raw.value().begin() + 1, raw.value().end())); } common::Result get_bytes(std::string_view key) const { auto raw = get_raw(key); if (!raw.ok()) { return common::Result::failure(raw.status()); } if (raw.value().empty() || raw.value().front() != 'B') { return common::Result::failure( { common::StatusCode::type_mismatch, "RocksDB value is not stored as bytes." }); } Bytes bytes(raw.value().size() - 1); if (!bytes.empty()) { std::memcpy(bytes.data(), raw.value().data() + 1, bytes.size()); } return common::Result::success(std::move(bytes)); } common::Result put_text(std::string_view key, std::string_view value) { if (auto status = require_writable(); !status.ok()) { return common::Result::failure(status); } std::string encoded; encoded.reserve(value.size() + 1); encoded.push_back('T'); encoded.append(value.data(), value.size()); auto put_status = db_->Put(write_options_, slice_of(key), rocksdb::Slice(encoded)); if (!put_status.ok()) { return common::Result::failure( { common::StatusCode::backend_error, put_status.ToString() }); } return common::Result::success(); } common::Result put_bytes(std::string_view key, std::span value) { if (auto status = require_writable(); !status.ok()) { return common::Result::failure(status); } std::string encoded; encoded.reserve(value.size_bytes() + 1); encoded.push_back('B'); encoded.append(reinterpret_cast(value.data()), value.size_bytes()); auto put_status = db_->Put(write_options_, slice_of(key), rocksdb::Slice(encoded)); if (!put_status.ok()) { return common::Result::failure( { common::StatusCode::backend_error, put_status.ToString() }); } return common::Result::success(); } common::Result erase(std::string_view key) { if (auto status = require_writable(); !status.ok()) { return common::Result::failure(status); } auto delete_status = db_->Delete(write_options_, slice_of(key)); if (!delete_status.ok()) { return common::Result::failure( { common::StatusCode::backend_error, delete_status.ToString() }); } return common::Result::success(); } common::Result> list_keys(std::string_view prefix) const { if (auto status = require_open(); !status.ok()) { return common::Result>::failure(status); } std::vector keys; std::unique_ptr iterator( db_->NewIterator(read_options_)); for (iterator->Seek(slice_of(prefix)); iterator->Valid(); iterator->Next()) { const auto current = iterator->key().ToStringView(); if (!prefix.empty() && !current.starts_with(prefix)) { break; } keys.emplace_back(current); } if (!iterator->status().ok()) { return common::Result>::failure( { common::StatusCode::backend_error, iterator->status().ToString() }); } return common::Result>::success(std::move(keys)); } common::Status require_open() const { if (db_ == nullptr) { return { common::StatusCode::closed, "RocksDB backend is not open." }; } return {}; } common::Status require_writable() const { if (auto status = require_open(); !status.ok()) { return status; } if (config_.open_options.read_only) { return { common::StatusCode::unsupported, "RocksDB backend is opened read-only." }; } return {}; } common::Result> get_raw(std::string_view key) const { if (auto status = require_open(); !status.ok()) { return common::Result>::failure(status); } std::string value; auto get_status = db_->Get(read_options_, slice_of(key), &value); if (get_status.IsNotFound()) { return common::Result>::failure( { common::StatusCode::not_found, "RocksDB key was not found." }); } if (!get_status.ok()) { return common::Result>::failure( { common::StatusCode::backend_error, get_status.ToString() }); } return common::Result>::success( std::vector(value.begin(), value.end())); } private: config_type config_{}; rocksdb::Options db_options_{}; rocksdb::ReadOptions read_options_{}; rocksdb::WriteOptions write_options_{}; rocksdb::DB *db_ = nullptr; }; RocksDbBackend::RocksDbBackend() : impl_(std::make_unique()) { } RocksDbBackend::~RocksDbBackend() = default; RocksDbBackend::RocksDbBackend(RocksDbBackend &&other) noexcept = default; RocksDbBackend & RocksDbBackend::operator=(RocksDbBackend &&other) noexcept = default; common::Result RocksDbBackend::create(const config_type &cfg) { if (cfg.path.empty()) { return common::Result::failure( { common::StatusCode::invalid_argument, "RocksDbConfig.path must not be empty." }); } rocksdb::Options options; options.create_if_missing = true; options.error_if_exists = false; rocksdb::DB *db = nullptr; auto open_status = rocksdb::DB::Open(options, cfg.path.string(), &db); if (!open_status.ok()) { return common::Result::failure( { common::StatusCode::backend_error, open_status.ToString() }); } delete db; return common::Result::success(); } common::Result RocksDbBackend::destroy(const config_type &cfg) { if (cfg.path.empty()) { return common::Result::failure( { common::StatusCode::invalid_argument, "RocksDbConfig.path must not be empty." }); } std::error_code ec; std::filesystem::remove_all(cfg.path, ec); if (ec) { return common::Result::failure( { common::StatusCode::io_error, ec.message() }); } return common::Result::success(); } common::Result RocksDbBackend::open(const config_type &cfg) { if (impl_ == nullptr) { impl_ = std::make_unique(); } return impl_->open(cfg); } common::Result RocksDbBackend::close() { if (impl_ == nullptr) { return common::Result::success(); } return impl_->close(); } common::Result RocksDbBackend::contains(std::string_view key) const { if (impl_ == nullptr) { return common::Result::failure( { common::StatusCode::closed, "RocksDB backend is not open." }); } return impl_->contains(key); } common::Result RocksDbBackend::get_text(std::string_view key) const { if (impl_ == nullptr) { return common::Result::failure( { common::StatusCode::closed, "RocksDB backend is not open." }); } return impl_->get_text(key); } common::Result RocksDbBackend::get_bytes(std::string_view key) const { if (impl_ == nullptr) { return common::Result::failure( { common::StatusCode::closed, "RocksDB backend is not open." }); } return impl_->get_bytes(key); } common::Result RocksDbBackend::put_text(std::string_view key, std::string_view value) { if (impl_ == nullptr) { return common::Result::failure( { common::StatusCode::closed, "RocksDB backend is not open." }); } return impl_->put_text(key, value); } common::Result RocksDbBackend::put_bytes(std::string_view key, std::span value) { if (impl_ == nullptr) { return common::Result::failure( { common::StatusCode::closed, "RocksDB backend is not open." }); } return impl_->put_bytes(key, value); } common::Result RocksDbBackend::erase(std::string_view key) { if (impl_ == nullptr) { return common::Result::failure( { common::StatusCode::closed, "RocksDB backend is not open." }); } return impl_->erase(key); } common::Result> RocksDbBackend::list_keys(std::string_view prefix) const { if (impl_ == nullptr) { return common::Result>::failure( { common::StatusCode::closed, "RocksDB backend is not open." }); } return impl_->list_keys(prefix); } } // namespace PDJE_UTIL::db::backends