Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,11 @@ option(LEVELDB_BUILD_TESTS "" OFF)
option(LEVELDB_AUTORUN_TESTS "" OFF)
option(DBSQL "Enable PostgeSQL server connection" OFF)

# The node binary always carries both DB backends; runtime selection is via
# config.ini `db_backend` (berkeleydb default, rocksdb opt-in).
set(CSDB_BACKEND "both" CACHE INTERNAL "csdb storage backend (locked to 'both')")
message(STATUS "csdb backend: both (locked)")

IF(DBSQL)
ADD_DEFINITIONS(-DDBSQL)
ENDIF(DBSQL)
Expand Down Expand Up @@ -88,6 +93,10 @@ endif()
# This large list is necessary to prevent thrift from generating unused stuff
add_definitions(-DUSE_STD_THREAD)

# Disable Boost's MSVC auto-link pragmas; we link Boost targets explicitly via
# CMake. Without this the linker hunts for libboost_*-vc142-mt-s-x64-*.lib.
add_definitions(-DBOOST_ALL_NO_LIB)

if (MSVC)
MARK_AS_ADVANCED(
CMAKE_CXX_FLAGS_RELMONITOR
Expand Down
5 changes: 5 additions & 0 deletions client/config/config.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -962,6 +962,11 @@ void Config::readStorageData(const boost::property_tree::ptree& config) {
storageData_.checkpointEvery = 1000;
}
checkAndSaveValue(data, block, PARAM_NAME_STORAGE_CHECKPOINT_EVERY_MINUTES, storageData_.checkpointEveryMinutes);
checkAndSaveValue(data, block, "async_write_queue_size", storageData_.asyncWriteQueueSize);
checkAndSaveValue(data, block, "write_batch_size", storageData_.writeBatchSize);
checkAndSaveValue(data, block, "db_backend", storageData_.dbBackend);
checkAndSaveValue(data, block, "rocksdb_block_cache_mb", storageData_.rocksdbBlockCacheMb);
checkAndSaveValue(data, block, "rocksdb_memtable_mb", storageData_.rocksdbMemtableMb);
}

void Config::readApiData(const boost::property_tree::ptree& config) {
Expand Down
5 changes: 5 additions & 0 deletions client/config/config.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,11 @@ struct StorageData {
size_t checkpointKeep = 5; // retained periodic checkpoints (qs/0 always kept on top)
size_t checkpointEvery = 500'000; // blocks between periodic checkpoints (rolling history depth = checkpointEvery * checkpointKeep)
size_t checkpointEveryMinutes = 0; // wall-clock fallback: also save if this many minutes elapsed since last save (0 = disabled; opt-in for slow networks)
size_t asyncWriteQueueSize = 5000; // bounded queue for the async DB writer
size_t writeBatchSize = 100; // pools coalesced into one DB write
std::string dbBackend = "berkeleydb"; // runtime DB backend (CSDB_BACKEND=both builds); set to "rocksdb" to opt in
size_t rocksdbBlockCacheMb = 1024; // RocksDB shared block cache (MiB); 0 = built-in default
size_t rocksdbMemtableMb = 256; // RocksDB write_buffer_size (MiB); 0 = built-in default
};

struct ApiData {
Expand Down
26 changes: 23 additions & 3 deletions csdb/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,16 @@ option(CSDB_BUILD_BENCHMARK "Bulid benchmark" OFF)
include (TestBigEndian)
TEST_BIG_ENDIAN(CSDB_PLATFORM_IS_BIG_ENDIAN)

if(CSDB_BACKEND STREQUAL "both")
set(CSDB_BACKEND_SOURCES
src/database_berkeleydb.cpp include/csdb/database_berkeleydb.hpp
src/database_rocksdb.cpp include/csdb/database_rocksdb.hpp)
elseif(CSDB_BACKEND STREQUAL "rocksdb")
set(CSDB_BACKEND_SOURCES src/database_rocksdb.cpp include/csdb/database_rocksdb.hpp)
else()
set(CSDB_BACKEND_SOURCES src/database_berkeleydb.cpp include/csdb/database_berkeleydb.hpp)
endif()

add_library(${PROJECT_NAME} STATIC
src/csdb.cpp
src/amount.cpp
Expand All @@ -29,7 +39,7 @@ add_library(${PROJECT_NAME} STATIC
src/priv_crypto.cpp
src/priv_crypto.hpp
src/database.cpp
src/database_berkeleydb.cpp
${CSDB_BACKEND_SOURCES}
src/user_field.cpp
include/csdb/internal/shared_data.hpp
include/csdb/internal/shared_data_ptr_implementation.hpp
Expand All @@ -48,7 +58,6 @@ add_library(${PROJECT_NAME} STATIC
include/csdb/wallet.hpp
include/csdb/storage.hpp
include/csdb/database.hpp
include/csdb/database_berkeleydb.hpp
include/csdb/user_field.hpp
)

Expand All @@ -72,13 +81,24 @@ target_include_directories(
)

include_directories(${Boost_INCLUDE_DIR})
set(CSDB_BACKEND_LIBS "")
if(CSDB_BACKEND STREQUAL "both")
list(APPEND CSDB_BACKEND_LIBS BerkeleyDB rocksdb)
target_compile_definitions(${PROJECT_NAME} PRIVATE CSDB_USE_ROCKSDB CSDB_USE_BERKELEYDB)
elseif(CSDB_BACKEND STREQUAL "rocksdb")
list(APPEND CSDB_BACKEND_LIBS rocksdb)
target_compile_definitions(${PROJECT_NAME} PRIVATE CSDB_USE_ROCKSDB)
else()
list(APPEND CSDB_BACKEND_LIBS BerkeleyDB)
endif()

target_link_libraries(
${PROJECT_NAME}
cscrypto
Boost::system
Boost::filesystem
Boost::disable_autolinking
BerkeleyDB
${CSDB_BACKEND_LIBS}
lz4
lib
)
Expand Down
11 changes: 11 additions & 0 deletions csdb/include/csdb/database.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -41,13 +41,24 @@ class Database {
virtual bool remove(const cs::Bytes& key) = 0;
virtual bool seq_no(const cs::Bytes& key, uint32_t* value) = 0; // sequence from block hash

// Vectorized put. Backends may override to coalesce writes (e.g. RocksDB WriteBatch).
struct PendingWrite {
cs::Bytes hash_key;
uint32_t seq_no;
cs::Bytes payload;
};
virtual bool put_batch(const std::vector<PendingWrite>& items);

using Item = std::pair<cs::Bytes, cs::Bytes>;
using ItemList = std::vector<Item>;
virtual bool write_batch(const ItemList& items) = 0;

virtual bool updateContractData(const cs::Bytes& key, const cs::Bytes& data) = 0;
virtual bool getContractData(const cs::Bytes& key, cs::Bytes& data) = 0;

// anchor buffered writes to disk at checkpoint boundaries; default no-op (BDB has its own loop)
virtual bool flush() { return true; }

class Iterator {
protected:
Iterator();
Expand Down
71 changes: 71 additions & 0 deletions csdb/include/csdb/database_rocksdb.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
// RocksDB-backed csdb::Database. CFs: blocks, seq_no, contracts.
// blocks keyed BE(seq+1). Selected via -DCSDB_BACKEND=rocksdb.

#ifndef _CREDITS_CSDB_DATABASE_ROCKSDB_H_INCLUDED_
#define _CREDITS_CSDB_DATABASE_ROCKSDB_H_INCLUDED_

#include <memory>
#include <string>

#include <csdb/database.hpp>

namespace rocksdb {
class DB;
class ColumnFamilyHandle;
class Status;
} // namespace rocksdb

namespace csdb {

class DatabaseRocksDB : public Database {
public:
DatabaseRocksDB();
~DatabaseRocksDB() override;

public:
// Bulk-load mode (before open): no auto-compaction, disableWAL; caller must compact_full() before close.
void set_bulk_load(bool yes);
// Per-write fsync toggle (default off; durability anchored at checkpoint via flush()).
void set_sync_writes(bool v);

// Flush + full-range compaction across all CFs; call once after a bulk-load run.
bool compact_full();

// Override RocksDB cache/memtable budget (before open); 0 keeps defaults (1 GiB / 256 MiB).
void set_tuning(uint64_t block_cache_bytes, uint64_t memtable_bytes);

bool open(const std::string& path);

private:
bool is_open() const final;
bool put(const cs::Bytes& key, uint32_t seq_no, const cs::Bytes& value) final;
bool put_batch(const std::vector<PendingWrite>& items) final;
bool get(const cs::Bytes& key, cs::Bytes* value) final;
bool get(const uint32_t seq_no, cs::Bytes* value) final;
bool remove(const cs::Bytes&) final;
bool seq_no(const cs::Bytes& key, uint32_t* value) final;
bool write_batch(const ItemList&) final;
IteratorPtr new_iterator() final;

bool updateContractData(const cs::Bytes& key, const cs::Bytes& data) override;
bool getContractData(const cs::Bytes& key, cs::Bytes& data) override;
bool flush() override;

private:
class Iterator;
void set_last_error_from_status(const rocksdb::Status& s);

private:
std::unique_ptr<rocksdb::DB> db_;
rocksdb::ColumnFamilyHandle* cf_blocks_ = nullptr; // default CF
rocksdb::ColumnFamilyHandle* cf_seq_no_ = nullptr;
rocksdb::ColumnFamilyHandle* cf_contracts_ = nullptr;
bool bulk_load_ = false;
bool sync_writes_ = false; // matches BDB's DB_TXN_NOSYNC; durability anchored at checkpoint via flush()
uint64_t block_cache_bytes_ = 1ULL << 30; // 1 GiB
uint64_t memtable_bytes_ = 256ULL << 20; // 256 MiB
};

} // namespace csdb

#endif // _CREDITS_CSDB_DATABASE_ROCKSDB_H_INCLUDED_
13 changes: 12 additions & 1 deletion csdb/include/csdb/storage.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,9 @@ class Storage final {
::std::shared_ptr<Database> db;
::cs::Sequence newBlockchainTop = ::cs::kWrongSequence;
::cs::Sequence startSequence = 0;
// 0 = leave Storage defaults (5000 / 100) untouched.
size_t asyncWriteQueueMax = 0;
size_t writeBatchSize = 0;
};

struct OpenProgress {
Expand Down Expand Up @@ -140,7 +143,12 @@ class Storage final {
bool open(const ::std::string& path_to_base = ::std::string{},
OpenCallback callback = nullptr,
cs::Sequence newBlockchainTop = cs::kWrongSequence,
cs::Sequence startReadFrom = 0);
cs::Sequence startReadFrom = 0,
size_t asyncWriteQueueMax = 5000,
size_t writeBatchSize = 100,
uint64_t rocksDbBlockCacheBytes = 0,
uint64_t rocksDbMemtableBytes = 0,
const std::string& dbBackend = std::string{});

/**
* @brief Creating the storage using the parameters set.
Expand Down Expand Up @@ -170,6 +178,9 @@ class Storage final {
*/
void close();

// Anchor buffered writes to disk (RocksDB SyncWAL; BerkeleyDB no-op). Call at checkpoint boundaries.
bool flush();

/**
* @brief Last block hash
* @return Last block hash
Expand Down
9 changes: 9 additions & 0 deletions csdb/src/database.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,15 @@ Database::Iterator::Iterator() = default;

Database::Iterator::~Iterator() = default;

bool Database::put_batch(const std::vector<PendingWrite>& items) {
for (const auto& item : items) {
if (!put(item.hash_key, item.seq_no, item.payload)) {
return false;
}
}
return true;
}

Database::Error Database::last_error() const {
return last_error_map(this).last_error_;
}
Expand Down
Loading