diff --git a/tddb/td/db/KeyValue.h b/tddb/td/db/KeyValue.h index 4f30a272b..980e367c6 100644 --- a/tddb/td/db/KeyValue.h +++ b/tddb/td/db/KeyValue.h @@ -18,6 +18,7 @@ */ #pragma once #include "td/utils/Status.h" +#include "td/utils/Time.h" #include "td/utils/logging.h" #include namespace td { diff --git a/tddb/td/db/RocksDb.cpp b/tddb/td/db/RocksDb.cpp index f8688c006..a46d04b13 100644 --- a/tddb/td/db/RocksDb.cpp +++ b/tddb/td/db/RocksDb.cpp @@ -258,11 +258,17 @@ Status RocksDb::flush() { Status RocksDb::begin_snapshot() { snapshot_.reset(db_->GetSnapshot()); + if (options_.snapshot_statistics) { + options_.snapshot_statistics->begin_snapshot(snapshot_.get()); + } return td::Status::OK(); } Status RocksDb::end_snapshot() { if (snapshot_) { + if (options_.snapshot_statistics) { + options_.snapshot_statistics->end_snapshot(snapshot_.get()); + } db_->ReleaseSnapshot(snapshot_.release()); } return td::Status::OK(); @@ -271,4 +277,41 @@ Status RocksDb::end_snapshot() { RocksDb::RocksDb(std::shared_ptr db, RocksDbOptions options) : db_(std::move(db)), options_(options) { } + +void RocksDbSnapshotStatistics::begin_snapshot(const rocksdb::Snapshot *snapshot) { + auto lock = std::unique_lock(mutex_); + auto id = reinterpret_cast(snapshot); + auto ts = td::Timestamp::now().at(); + CHECK(id_to_ts_.emplace(id, ts).second); + CHECK(by_ts_.emplace(ts, id).second); +} + +void RocksDbSnapshotStatistics::end_snapshot(const rocksdb::Snapshot *snapshot) { + auto id = reinterpret_cast(snapshot); + auto it = id_to_ts_.find(id); + CHECK(it != id_to_ts_.end()); + auto ts = it->second; + CHECK(by_ts_.erase(std::make_pair(ts, id)) == 1u); + CHECK(id_to_ts_.erase(id) == 1u); +} + +td::Timestamp RocksDbSnapshotStatistics::oldest_snapshot_timestamp() const { + auto lock = std::unique_lock(mutex_); + if (by_ts_.empty()) { + return {}; + } + return td::Timestamp::at(by_ts_.begin()->first); +} + +std::string RocksDbSnapshotStatistics::to_string() const { + td::Timestamp oldest_snapshot = oldest_snapshot_timestamp(); + double value; + if (oldest_snapshot) { + value = td::Timestamp::now().at() - oldest_snapshot.at(); + } else { + value = -1; + } + return PSTRING() << "td.rocksdb.snapshot.oldest_snapshot_ago.seconds : " << value << "\n"; +} + } // namespace td diff --git a/tddb/td/db/RocksDb.h b/tddb/td/db/RocksDb.h index 5efcd0f48..32c53a529 100644 --- a/tddb/td/db/RocksDb.h +++ b/tddb/td/db/RocksDb.h @@ -26,6 +26,12 @@ #include "td/utils/Status.h" #include "td/utils/optional.h" +#include "td/utils/Time.h" + +#include +#include +#include + namespace rocksdb { class Cache; class OptimisticTransactionDB; @@ -36,10 +42,22 @@ class Statistics; } // namespace rocksdb namespace td { +struct RocksDbSnapshotStatistics { + void begin_snapshot(const rocksdb::Snapshot *snapshot); + void end_snapshot(const rocksdb::Snapshot *snapshot); + td::Timestamp oldest_snapshot_timestamp() const; + std::string to_string() const; + + private: + mutable std::mutex mutex_; + std::map id_to_ts_; + std::set> by_ts_; +}; struct RocksDbOptions { std::shared_ptr statistics = nullptr; std::shared_ptr block_cache; // Default - one 1G cache for all RocksDb + std::shared_ptr snapshot_statistics = nullptr; bool use_direct_reads = false; }; diff --git a/tddb/test/key_value.cpp b/tddb/test/key_value.cpp index e04e7ee90..74215e5bd 100644 --- a/tddb/test/key_value.cpp +++ b/tddb/test/key_value.cpp @@ -60,9 +60,19 @@ TEST(KeyValue, simple) { ensure_value(as_slice(x), as_slice(x)); kv.reset(); - kv = std::make_unique(td::RocksDb::open(db_name.str()).move_as_ok()); + td::RocksDbOptions options{.snapshot_statistics = std::make_shared()}; + kv = std::make_unique(td::RocksDb::open(db_name.str(), options).move_as_ok()); ensure_value("A", "HELLO"); ensure_value(as_slice(x), as_slice(x)); + + CHECK(!options.snapshot_statistics->oldest_snapshot_timestamp()); + auto snapshot = kv->snapshot(); + CHECK(options.snapshot_statistics->oldest_snapshot_timestamp()); + auto snapshot2 = kv->snapshot(); + snapshot.reset(); + CHECK(options.snapshot_statistics->oldest_snapshot_timestamp()); + snapshot2.reset(); + CHECK(!options.snapshot_statistics->oldest_snapshot_timestamp()); }; TEST(KeyValue, async_simple) { diff --git a/validator/db/celldb.cpp b/validator/db/celldb.cpp index dfbee0a1a..463e6e34a 100644 --- a/validator/db/celldb.cpp +++ b/validator/db/celldb.cpp @@ -84,11 +84,13 @@ void CellDbIn::start_up() { }; CellDbBase::start_up(); + td::RocksDbOptions db_options; if (!opts_->get_disable_rocksdb_stats()) { statistics_ = td::RocksDb::create_statistics(); statistics_flush_at_ = td::Timestamp::in(60.0); + snapshot_statistics_ = std::make_shared(); + db_options.snapshot_statistics = snapshot_statistics_; } - td::RocksDbOptions db_options; db_options.statistics = statistics_; if (opts_->get_celldb_cache_size()) { db_options.block_cache = td::RocksDb::create_cache(opts_->get_celldb_cache_size().value()); @@ -193,7 +195,11 @@ void CellDbIn::get_cell_db_reader(td::Promise> } void CellDbIn::flush_db_stats() { - auto stats = td::RocksDb::statistics_to_string(statistics_) + cell_db_statistics_.to_string(); + if (opts_->get_disable_rocksdb_stats()) { + return; + } + auto stats = td::RocksDb::statistics_to_string(statistics_) + snapshot_statistics_->to_string() + + cell_db_statistics_.to_string(); auto to_file_r = td::FileFd::open(path_ + "/db_stats.txt", td::FileFd::Truncate | td::FileFd::Create | td::FileFd::Write, 0644); if (to_file_r.is_error()) { diff --git a/validator/db/celldb.hpp b/validator/db/celldb.hpp index 7dc1fa781..b3857971c 100644 --- a/validator/db/celldb.hpp +++ b/validator/db/celldb.hpp @@ -27,6 +27,7 @@ #include "auto/tl/ton_api.h" #include "validator.h" #include "db-utils.h" +#include "td/db/RocksDb.h" namespace rocksdb { class Statistics; @@ -139,6 +140,7 @@ class CellDbIn : public CellDbBase { }; std::shared_ptr statistics_; + std::shared_ptr snapshot_statistics_; CellDbStatistics cell_db_statistics_; td::Timestamp statistics_flush_at_ = td::Timestamp::never();