/* This file is part of TON Blockchain Library. TON Blockchain Library is free software: you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation, either version 2 of the License, or (at your option) any later version. TON Blockchain Library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with TON Blockchain Library. If not, see . Copyright 2017-2020 Telegram Systems LLP */ #pragma once #include "td/actor/actor.h" #include "crypto/vm/db/DynamicBagOfCellsDb.h" #include "crypto/vm/db/CellStorage.h" #include "td/db/KeyValue.h" #include "ton/ton-types.h" #include "interfaces/block-handle.h" #include "auto/tl/ton_api.h" #include "validator.h" #include "db-utils.h" #include "td/db/RocksDb.h" #include #include namespace rocksdb { class Statistics; class DB; } // namespace rocksdb namespace ton { namespace validator { class RootDb; class CellDb; class CellDbAsyncExecutor; class CellDbBase : public td::actor::Actor { public: void start_up() override; protected: std::shared_ptr async_executor; private: void execute_sync(std::function f); friend CellDbAsyncExecutor; }; class CellDbIn : public CellDbBase { public: using KeyHash = td::Bits256; std::vector> prepare_stats(); void load_cell(RootHash hash, td::Promise> promise); void store_cell(BlockIdExt block_id, td::Ref cell, td::Promise> promise); void get_cell_db_reader(td::Promise> promise); void migrate_cell(td::Bits256 hash); void flush_db_stats(); CellDbIn(td::actor::ActorId root_db, td::actor::ActorId parent, std::string path, td::Ref opts); void start_up() override; void alarm() override; private: struct DbEntry { BlockIdExt block_id; KeyHash prev; KeyHash next; RootHash root_hash; DbEntry(tl_object_ptr entry); DbEntry() = default; DbEntry(BlockIdExt block_id, KeyHash prev, KeyHash next, RootHash root_hash) : block_id(block_id), prev(prev), next(next), root_hash(root_hash) { } td::BufferSlice release(); bool is_empty() const { return !block_id.is_valid(); } }; td::Result get_block(KeyHash key); void set_block(KeyHash key, DbEntry e); static std::string get_key(KeyHash key); static KeyHash get_key_hash(BlockIdExt block_id); static BlockIdExt get_empty_key(); KeyHash get_empty_key_hash(); void gc(BlockIdExt block_id); void gc_cont(BlockHandle handle); void gc_cont2(BlockHandle handle); void skip_gc(); void migrate_cells(); td::actor::ActorId root_db_; td::actor::ActorId parent_; std::string path_; td::Ref opts_; std::shared_ptr boc_; std::shared_ptr cell_db_; std::shared_ptr rocks_db_; std::function on_load_callback_; std::set cells_to_migrate_; td::Timestamp migrate_after_ = td::Timestamp::never(); bool migration_active_ = false; std::optional in_memory_load_time_; struct MigrationStats { td::Timer start_; td::Timestamp end_at_ = td::Timestamp::in(60.0); size_t batches_ = 0; size_t migrated_cells_ = 0; size_t checked_cells_ = 0; double total_time_ = 0.0; }; std::unique_ptr migration_stats_; struct CellDbStatistics { PercentileStats store_cell_time_; PercentileStats store_cell_prepare_time_; PercentileStats store_cell_write_time_; PercentileStats gc_cell_time_; td::Timestamp stats_start_time_ = td::Timestamp::now(); std::optional in_memory_load_time_; std::optional boc_stats_; std::vector> prepare_stats(); void clear() { *this = CellDbStatistics{}; } }; std::shared_ptr statistics_; std::shared_ptr snapshot_statistics_; CellDbStatistics cell_db_statistics_; td::Timestamp statistics_flush_at_ = td::Timestamp::never(); BlockSeqno last_deleted_mc_state_ = 0; bool db_busy_ = false; std::queue> action_queue_; void release_db() { db_busy_ = false; while (!db_busy_ && !action_queue_.empty()) { auto action = std::move(action_queue_.front()); action_queue_.pop(); action.set_value(td::Unit()); } } public: class MigrationProxy : public td::actor::Actor { public: explicit MigrationProxy(td::actor::ActorId cell_db) : cell_db_(cell_db) { } void migrate_cell(td::Bits256 hash) { td::actor::send_closure(cell_db_, &CellDbIn::migrate_cell, hash); } private: td::actor::ActorId cell_db_; }; }; class CellDb : public CellDbBase { public: void prepare_stats(td::Promise>> promise); void load_cell(RootHash hash, td::Promise> promise); void store_cell(BlockIdExt block_id, td::Ref cell, td::Promise> promise); void update_snapshot(std::unique_ptr snapshot) { CHECK(!opts_->get_celldb_in_memory()); if (!started_) { alarm(); } started_ = true; boc_->set_loader(std::make_unique(std::move(snapshot), on_load_callback_)).ensure(); } void set_in_memory_boc(std::shared_ptr in_memory_boc) { CHECK(opts_->get_celldb_in_memory()); if (!started_) { alarm(); } started_ = true; in_memory_boc_ = std::move(in_memory_boc); } void get_cell_db_reader(td::Promise> promise); CellDb(td::actor::ActorId root_db, std::string path, td::Ref opts) : root_db_(root_db), path_(path), opts_(opts) { } void start_up() override; private: td::actor::ActorId root_db_; std::string path_; td::Ref opts_; td::actor::ActorOwn cell_db_; std::unique_ptr boc_; std::shared_ptr in_memory_boc_; bool started_ = false; std::vector> prepared_stats_{{"started", "false"}}; std::function on_load_callback_; void update_stats(td::Result>> stats); void alarm() override; }; } // namespace validator } // namespace ton