mirror of
https://github.com/ton-blockchain/ton
synced 2025-03-09 15:40:10 +00:00
new db
new database fift/func bugfixes
This commit is contained in:
parent
950e292264
commit
e30d98eb30
110 changed files with 6102 additions and 2075 deletions
986
validator/db/archive-manager.cpp
Normal file
986
validator/db/archive-manager.cpp
Normal file
|
@ -0,0 +1,986 @@
|
|||
#include "archive-manager.hpp"
|
||||
#include "td/actor/MultiPromise.h"
|
||||
#include "td/utils/overloaded.h"
|
||||
#include "files-async.hpp"
|
||||
#include "td/db/RocksDb.h"
|
||||
#include "common/delay.h"
|
||||
|
||||
namespace ton {
|
||||
|
||||
namespace validator {
|
||||
|
||||
std::string PackageId::path() const {
|
||||
if (temp) {
|
||||
return "files/packages/";
|
||||
} else if (key) {
|
||||
char s[24];
|
||||
sprintf(s, "key%03d", id / 1000000);
|
||||
return PSTRING() << "archive/packages/" << s << "/";
|
||||
} else {
|
||||
char s[20];
|
||||
sprintf(s, "arch%04d", id / 100000);
|
||||
return PSTRING() << "archive/packages/" << s << "/";
|
||||
}
|
||||
}
|
||||
|
||||
std::string PackageId::name() const {
|
||||
if (temp) {
|
||||
return PSTRING() << "temp.archive." << id;
|
||||
} else if (key) {
|
||||
char s[20];
|
||||
sprintf(s, "%06d", id);
|
||||
return PSTRING() << "key.archive." << s;
|
||||
} else {
|
||||
char s[10];
|
||||
sprintf(s, "%05d", id);
|
||||
return PSTRING() << "archive." << s;
|
||||
}
|
||||
}
|
||||
|
||||
ArchiveManager::ArchiveManager(td::actor::ActorId<RootDb> root, std::string db_root) : db_root_(db_root) {
|
||||
}
|
||||
|
||||
void ArchiveManager::add_handle(BlockHandle handle, td::Promise<td::Unit> promise) {
|
||||
if (handle->handle_moved_to_archive()) {
|
||||
update_handle(std::move(handle), std::move(promise));
|
||||
return;
|
||||
}
|
||||
auto p = get_package_id_force(handle->masterchain_ref_block(), handle->id().shard_full(), handle->id().seqno(),
|
||||
handle->unix_time(), handle->logical_time(),
|
||||
handle->inited_is_key_block() && handle->is_key_block());
|
||||
auto f = get_file_desc(handle->id().shard_full(), p, handle->id().seqno(), handle->unix_time(),
|
||||
handle->logical_time(), true);
|
||||
td::actor::send_closure(f->file_actor_id(), &ArchiveSlice::add_handle, std::move(handle), std::move(promise));
|
||||
}
|
||||
|
||||
void ArchiveManager::update_handle(BlockHandle handle, td::Promise<td::Unit> promise) {
|
||||
FileDescription *f;
|
||||
if (handle->handle_moved_to_archive()) {
|
||||
CHECK(handle->inited_unix_time());
|
||||
f = get_file_desc(handle->id().shard_full(), get_package_id(handle->masterchain_ref_block()), handle->id().seqno(),
|
||||
handle->unix_time(), handle->logical_time(), true);
|
||||
} else {
|
||||
f = get_file_desc(handle->id().shard_full(), get_temp_package_id(), 0, 0, 0, true);
|
||||
}
|
||||
td::actor::send_closure(f->file_actor_id(), &ArchiveSlice::update_handle, std::move(handle), std::move(promise));
|
||||
}
|
||||
|
||||
void ArchiveManager::add_file(BlockHandle handle, FileReference ref_id, td::BufferSlice data,
|
||||
td::Promise<td::Unit> promise) {
|
||||
bool copy_to_key = false;
|
||||
if (handle->inited_is_key_block() && handle->is_key_block() && handle->inited_unix_time() &&
|
||||
handle->inited_logical_time() && handle->inited_masterchain_ref_block()) {
|
||||
copy_to_key = (ref_id.ref().get_offset() == ref_id.ref().offset<fileref::Proof>()) ||
|
||||
(ref_id.ref().get_offset() == ref_id.ref().offset<fileref::ProofLink>());
|
||||
}
|
||||
if (!handle->handle_moved_to_archive()) {
|
||||
td::MultiPromise mp;
|
||||
auto ig = mp.init_guard();
|
||||
ig.add_promise(std::move(promise));
|
||||
auto f1 = get_file_desc(handle->id().shard_full(), get_temp_package_id(), 0, 0, 0, true);
|
||||
td::actor::send_closure(f1->file_actor_id(), &ArchiveSlice::add_file, std::move(ref_id), data.clone(),
|
||||
ig.get_promise());
|
||||
if (copy_to_key) {
|
||||
auto f2 = get_file_desc(handle->id().shard_full(), get_key_package_id(handle->masterchain_ref_block()),
|
||||
handle->id().seqno(), handle->unix_time(), handle->logical_time(), true);
|
||||
td::actor::send_closure(f2->file_actor_id(), &ArchiveSlice::add_file, ref_id, std::move(data), ig.get_promise());
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
CHECK(handle->inited_is_key_block());
|
||||
|
||||
td::MultiPromise mp;
|
||||
auto ig = mp.init_guard();
|
||||
ig.add_promise(std::move(promise));
|
||||
auto f1 = get_file_desc(handle->id().shard_full(), get_package_id(handle->masterchain_ref_block()),
|
||||
handle->id().seqno(), handle->unix_time(), handle->logical_time(), true);
|
||||
td::actor::send_closure(f1->file_actor_id(), &ArchiveSlice::add_file, ref_id, data.clone(), ig.get_promise());
|
||||
if (copy_to_key) {
|
||||
auto f2 = get_file_desc(handle->id().shard_full(), get_key_package_id(handle->masterchain_ref_block()),
|
||||
handle->id().seqno(), handle->unix_time(), handle->logical_time(), true);
|
||||
td::actor::send_closure(f2->file_actor_id(), &ArchiveSlice::add_file, ref_id, std::move(data), ig.get_promise());
|
||||
}
|
||||
}
|
||||
|
||||
void ArchiveManager::add_key_block_proof(UnixTime ts, BlockSeqno seqno, LogicalTime lt, FileReference ref_id,
|
||||
td::BufferSlice data, td::Promise<td::Unit> promise) {
|
||||
auto f = get_file_desc(ShardIdFull{masterchainId}, get_key_package_id(seqno), seqno, ts, lt, true);
|
||||
td::actor::send_closure(f->file_actor_id(), &ArchiveSlice::add_file, std::move(ref_id), std::move(data),
|
||||
std::move(promise));
|
||||
}
|
||||
|
||||
void ArchiveManager::add_temp_file_short(FileReference ref_id, td::BufferSlice data, td::Promise<td::Unit> promise) {
|
||||
auto f = get_file_desc(ref_id.shard(), get_temp_package_id(), 0, 0, 0, true);
|
||||
td::actor::send_closure(f->file_actor_id(), &ArchiveSlice::add_file, std::move(ref_id), std::move(data),
|
||||
std::move(promise));
|
||||
}
|
||||
|
||||
void ArchiveManager::get_handle(BlockIdExt block_id, td::Promise<BlockHandle> promise) {
|
||||
auto f = get_file_desc_by_seqno(block_id.shard_full(), block_id.seqno(), false);
|
||||
if (f) {
|
||||
auto P = td::PromiseCreator::lambda([SelfId = actor_id(this), block_id, idx = get_max_temp_file_desc_idx(),
|
||||
promise = std::move(promise)](td::Result<BlockHandle> R) mutable {
|
||||
if (R.is_ok()) {
|
||||
promise.set_value(R.move_as_ok());
|
||||
} else {
|
||||
td::actor::send_closure(SelfId, &ArchiveManager::get_handle_cont, block_id, idx, std::move(promise));
|
||||
}
|
||||
});
|
||||
td::actor::send_closure(f->file_actor_id(), &ArchiveSlice::get_handle, block_id, std::move(P));
|
||||
} else {
|
||||
get_handle_cont(block_id, get_max_temp_file_desc_idx(), std::move(promise));
|
||||
}
|
||||
}
|
||||
|
||||
void ArchiveManager::get_handle_cont(BlockIdExt block_id, PackageId idx, td::Promise<BlockHandle> promise) {
|
||||
if (idx.is_empty()) {
|
||||
promise.set_error(td::Status::Error(ErrorCode::notready, "block handle not in db"));
|
||||
return;
|
||||
}
|
||||
auto f = get_temp_file_desc_by_idx(idx);
|
||||
if (!f) {
|
||||
promise.set_error(td::Status::Error(ErrorCode::notready, "block handle not in db"));
|
||||
return;
|
||||
}
|
||||
auto P = td::PromiseCreator::lambda([SelfId = actor_id(this), block_id, idx = get_prev_temp_file_desc_idx(idx),
|
||||
promise = std::move(promise)](td::Result<BlockHandle> R) mutable {
|
||||
if (R.is_ok()) {
|
||||
td::actor::send_closure(SelfId, &ArchiveManager::get_handle_finish, R.move_as_ok(), std::move(promise));
|
||||
} else {
|
||||
td::actor::send_closure(SelfId, &ArchiveManager::get_handle_cont, block_id, idx, std::move(promise));
|
||||
}
|
||||
});
|
||||
td::actor::send_closure(f->file_actor_id(), &ArchiveSlice::get_handle, block_id, std::move(P));
|
||||
}
|
||||
|
||||
void ArchiveManager::get_handle_finish(BlockHandle handle, td::Promise<BlockHandle> promise) {
|
||||
auto f = get_file_desc_by_seqno(handle->id().shard_full(), handle->id().seqno(), false);
|
||||
if (!f) {
|
||||
promise.set_value(std::move(handle));
|
||||
return;
|
||||
}
|
||||
auto P = td::PromiseCreator::lambda([handle, promise = std::move(promise)](td::Result<BlockHandle> R) mutable {
|
||||
if (R.is_ok()) {
|
||||
promise.set_value(R.move_as_ok());
|
||||
} else {
|
||||
promise.set_value(std::move(handle));
|
||||
}
|
||||
});
|
||||
td::actor::send_closure(f->file_actor_id(), &ArchiveSlice::get_handle, handle->id(), std::move(P));
|
||||
}
|
||||
|
||||
void ArchiveManager::get_file_short(FileReference ref_id, td::Promise<td::BufferSlice> promise) {
|
||||
bool search_in_key = false;
|
||||
BlockIdExt block_id;
|
||||
ref_id.ref().visit(td::overloaded(
|
||||
[&](const fileref::Proof &p) {
|
||||
search_in_key = p.block_id.is_masterchain();
|
||||
block_id = p.block_id;
|
||||
},
|
||||
[&](const fileref::ProofLink &p) {
|
||||
search_in_key = p.block_id.is_masterchain();
|
||||
block_id = p.block_id;
|
||||
},
|
||||
[&](const auto &p) {}));
|
||||
if (search_in_key) {
|
||||
auto f = get_file_desc_by_seqno(block_id.shard_full(), block_id.seqno(), true);
|
||||
if (f) {
|
||||
auto P = td::PromiseCreator::lambda([SelfId = actor_id(this), ref_id,
|
||||
promise = std::move(promise)](td::Result<td::BufferSlice> R) mutable {
|
||||
if (R.is_ok()) {
|
||||
promise.set_value(R.move_as_ok());
|
||||
} else {
|
||||
td::actor::send_closure(SelfId, &ArchiveManager::get_temp_file_short, std::move(ref_id), std::move(promise));
|
||||
}
|
||||
});
|
||||
td::actor::send_closure(f->file_actor_id(), &ArchiveSlice::get_file, ref_id, std::move(P));
|
||||
return;
|
||||
}
|
||||
}
|
||||
get_temp_file_short(std::move(ref_id), std::move(promise));
|
||||
}
|
||||
|
||||
void ArchiveManager::get_key_block_proof(FileReference ref_id, td::Promise<td::BufferSlice> promise) {
|
||||
bool search_in_key = false;
|
||||
BlockIdExt block_id;
|
||||
ref_id.ref().visit(td::overloaded(
|
||||
[&](const fileref::Proof &p) {
|
||||
search_in_key = p.block_id.is_masterchain();
|
||||
block_id = p.block_id;
|
||||
},
|
||||
[&](const fileref::ProofLink &p) {
|
||||
search_in_key = p.block_id.is_masterchain();
|
||||
block_id = p.block_id;
|
||||
},
|
||||
[&](const auto &p) {}));
|
||||
if (search_in_key) {
|
||||
auto f = get_file_desc_by_seqno(block_id.shard_full(), block_id.seqno(), true);
|
||||
if (f) {
|
||||
td::actor::send_closure(f->file_actor_id(), &ArchiveSlice::get_file, ref_id, std::move(promise));
|
||||
} else {
|
||||
promise.set_error(td::Status::Error(ErrorCode::notready, "key proof not in db"));
|
||||
}
|
||||
} else {
|
||||
promise.set_error(
|
||||
td::Status::Error(ErrorCode::protoviolation, "only proof/prooflink supported in get_key_block_proof"));
|
||||
}
|
||||
}
|
||||
|
||||
void ArchiveManager::get_temp_file_short(FileReference ref_id, td::Promise<td::BufferSlice> promise) {
|
||||
get_file_short_cont(std::move(ref_id), get_max_temp_file_desc_idx(), std::move(promise));
|
||||
}
|
||||
|
||||
void ArchiveManager::get_file_short_cont(FileReference ref_id, PackageId idx, td::Promise<td::BufferSlice> promise) {
|
||||
auto f = get_temp_file_desc_by_idx(idx);
|
||||
if (!f) {
|
||||
promise.set_error(td::Status::Error(ErrorCode::notready, "file not in db"));
|
||||
return;
|
||||
}
|
||||
auto P = td::PromiseCreator::lambda([SelfId = actor_id(this), ref_id, idx = get_prev_temp_file_desc_idx(idx),
|
||||
promise = std::move(promise)](td::Result<td::BufferSlice> R) mutable {
|
||||
if (R.is_ok()) {
|
||||
promise.set_value(R.move_as_ok());
|
||||
} else {
|
||||
td::actor::send_closure(SelfId, &ArchiveManager::get_file_short_cont, std::move(ref_id), idx, std::move(promise));
|
||||
}
|
||||
});
|
||||
td::actor::send_closure(f->file_actor_id(), &ArchiveSlice::get_file, std::move(ref_id), std::move(P));
|
||||
}
|
||||
|
||||
void ArchiveManager::get_file(BlockHandle handle, FileReference ref_id, td::Promise<td::BufferSlice> promise) {
|
||||
if (handle->moved_to_archive()) {
|
||||
auto f = get_file_desc(handle->id().shard_full(), PackageId{handle->masterchain_ref_block(), false, false}, 0, 0, 0,
|
||||
false);
|
||||
if (f) {
|
||||
td::actor::send_closure(f->file_actor_id(), &ArchiveSlice::get_file, std::move(ref_id), std::move(promise));
|
||||
return;
|
||||
}
|
||||
}
|
||||
get_file_short_cont(std::move(ref_id), get_max_temp_file_desc_idx(), std::move(promise));
|
||||
}
|
||||
|
||||
void ArchiveManager::written_perm_state(FileReferenceShort id) {
|
||||
perm_states_.emplace(id.hash(), id);
|
||||
}
|
||||
|
||||
void ArchiveManager::add_zero_state(BlockIdExt block_id, td::BufferSlice data, td::Promise<td::Unit> promise) {
|
||||
auto id = FileReference{fileref::ZeroState{block_id}};
|
||||
auto hash = id.hash();
|
||||
if (perm_states_.find(hash) != perm_states_.end()) {
|
||||
promise.set_value(td::Unit());
|
||||
return;
|
||||
}
|
||||
|
||||
auto path = db_root_ + "/archive/states/" + id.filename_short();
|
||||
auto P = td::PromiseCreator::lambda(
|
||||
[SelfId = actor_id(this), id = id.shortref(), promise = std::move(promise)](td::Result<std::string> R) mutable {
|
||||
if (R.is_error()) {
|
||||
promise.set_error(R.move_as_error());
|
||||
} else {
|
||||
td::actor::send_closure(SelfId, &ArchiveManager::written_perm_state, id);
|
||||
promise.set_value(td::Unit());
|
||||
}
|
||||
});
|
||||
td::actor::create_actor<db::WriteFile>("writefile", db_root_ + "/archive/tmp/", path, std::move(data), std::move(P))
|
||||
.release();
|
||||
}
|
||||
|
||||
void ArchiveManager::add_persistent_state(BlockIdExt block_id, BlockIdExt masterchain_block_id, td::BufferSlice data,
|
||||
td::Promise<td::Unit> promise) {
|
||||
auto id = FileReference{fileref::PersistentState{block_id, masterchain_block_id}};
|
||||
auto hash = id.hash();
|
||||
if (perm_states_.find(hash) != perm_states_.end()) {
|
||||
promise.set_value(td::Unit());
|
||||
return;
|
||||
}
|
||||
|
||||
auto path = db_root_ + "/archive/states/" + id.filename_short();
|
||||
auto P = td::PromiseCreator::lambda(
|
||||
[SelfId = actor_id(this), id = id.shortref(), promise = std::move(promise)](td::Result<std::string> R) mutable {
|
||||
if (R.is_error()) {
|
||||
promise.set_error(R.move_as_error());
|
||||
} else {
|
||||
td::actor::send_closure(SelfId, &ArchiveManager::written_perm_state, id);
|
||||
promise.set_value(td::Unit());
|
||||
}
|
||||
});
|
||||
td::actor::create_actor<db::WriteFile>("writefile", db_root_ + "/archive/tmp/", path, std::move(data), std::move(P))
|
||||
.release();
|
||||
}
|
||||
|
||||
void ArchiveManager::get_zero_state(BlockIdExt block_id, td::Promise<td::BufferSlice> promise) {
|
||||
auto id = FileReference{fileref::ZeroState{block_id}};
|
||||
auto hash = id.hash();
|
||||
if (perm_states_.find(hash) == perm_states_.end()) {
|
||||
promise.set_error(td::Status::Error(ErrorCode::notready, "zerostate not in db"));
|
||||
return;
|
||||
}
|
||||
|
||||
auto path = db_root_ + "/archive/states/" + id.filename_short();
|
||||
td::actor::create_actor<db::ReadFile>("readfile", path, 0, -1, 0, std::move(promise)).release();
|
||||
}
|
||||
|
||||
void ArchiveManager::check_zero_state(BlockIdExt block_id, td::Promise<bool> promise) {
|
||||
auto id = FileReference{fileref::ZeroState{block_id}};
|
||||
auto hash = id.hash();
|
||||
if (perm_states_.find(hash) == perm_states_.end()) {
|
||||
promise.set_result(false);
|
||||
return;
|
||||
}
|
||||
promise.set_result(true);
|
||||
}
|
||||
|
||||
void ArchiveManager::get_persistent_state(BlockIdExt block_id, BlockIdExt masterchain_block_id,
|
||||
td::Promise<td::BufferSlice> promise) {
|
||||
auto id = FileReference{fileref::PersistentState{block_id, masterchain_block_id}};
|
||||
auto hash = id.hash();
|
||||
if (perm_states_.find(hash) == perm_states_.end()) {
|
||||
promise.set_error(td::Status::Error(ErrorCode::notready, "state file not in db"));
|
||||
return;
|
||||
}
|
||||
|
||||
auto path = db_root_ + "/archive/states/" + id.filename_short();
|
||||
td::actor::create_actor<db::ReadFile>("readfile", path, 0, -1, 0, std::move(promise)).release();
|
||||
}
|
||||
|
||||
void ArchiveManager::get_persistent_state_slice(BlockIdExt block_id, BlockIdExt masterchain_block_id, td::int64 offset,
|
||||
td::int64 max_size, td::Promise<td::BufferSlice> promise) {
|
||||
auto id = FileReference{fileref::PersistentState{block_id, masterchain_block_id}};
|
||||
auto hash = id.hash();
|
||||
if (perm_states_.find(hash) == perm_states_.end()) {
|
||||
promise.set_error(td::Status::Error(ErrorCode::notready, "state file not in db"));
|
||||
return;
|
||||
}
|
||||
|
||||
auto path = db_root_ + "/archive/states/" + id.filename_short();
|
||||
td::actor::create_actor<db::ReadFile>("readfile", path, offset, max_size, 0, std::move(promise)).release();
|
||||
}
|
||||
|
||||
void ArchiveManager::check_persistent_state(BlockIdExt block_id, BlockIdExt masterchain_block_id,
|
||||
td::Promise<bool> promise) {
|
||||
auto id = FileReference{fileref::PersistentState{block_id, masterchain_block_id}};
|
||||
auto hash = id.hash();
|
||||
if (perm_states_.find(hash) == perm_states_.end()) {
|
||||
promise.set_result(false);
|
||||
return;
|
||||
}
|
||||
promise.set_result(true);
|
||||
}
|
||||
|
||||
void ArchiveManager::get_block_by_unix_time(AccountIdPrefixFull account_id, UnixTime ts,
|
||||
td::Promise<BlockHandle> promise) {
|
||||
auto f = get_file_desc_by_unix_time(account_id, ts, false);
|
||||
if (f) {
|
||||
td::actor::send_closure(f->file_actor_id(), &ArchiveSlice::get_block_by_unix_time, account_id, ts,
|
||||
std::move(promise));
|
||||
} else {
|
||||
promise.set_error(td::Status::Error(ErrorCode::notready, "ts not in db"));
|
||||
}
|
||||
}
|
||||
|
||||
void ArchiveManager::get_block_by_lt(AccountIdPrefixFull account_id, LogicalTime lt, td::Promise<BlockHandle> promise) {
|
||||
auto f = get_file_desc_by_lt(account_id, lt, false);
|
||||
if (f) {
|
||||
td::actor::send_closure(f->file_actor_id(), &ArchiveSlice::get_block_by_lt, account_id, lt, std::move(promise));
|
||||
} else {
|
||||
promise.set_error(td::Status::Error(ErrorCode::notready, "lt not in db"));
|
||||
}
|
||||
}
|
||||
|
||||
void ArchiveManager::get_block_by_seqno(AccountIdPrefixFull account_id, BlockSeqno seqno,
|
||||
td::Promise<BlockHandle> promise) {
|
||||
auto f = get_file_desc_by_seqno(account_id, seqno, false);
|
||||
if (f) {
|
||||
td::actor::send_closure(f->file_actor_id(), &ArchiveSlice::get_block_by_seqno, account_id, seqno,
|
||||
std::move(promise));
|
||||
} else {
|
||||
promise.set_error(td::Status::Error(ErrorCode::notready, "seqno not in db"));
|
||||
}
|
||||
}
|
||||
|
||||
void ArchiveManager::delete_package(PackageId id) {
|
||||
auto key = create_serialize_tl_object<ton_api::db_files_package_key>(id.id, id.key, id.temp);
|
||||
|
||||
std::string value;
|
||||
auto v = index_->get(key.as_slice(), value);
|
||||
v.ensure();
|
||||
CHECK(v.move_as_ok() == td::KeyValue::GetStatus::Ok);
|
||||
|
||||
auto R = fetch_tl_object<ton_api::db_files_package_value>(value, true);
|
||||
R.ensure();
|
||||
auto x = R.move_as_ok();
|
||||
|
||||
if (x->deleted_) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto &m = get_file_map(id);
|
||||
auto it = m.find(id);
|
||||
if (it == m.end() || it->second.deleted) {
|
||||
return;
|
||||
}
|
||||
|
||||
it->second.deleted = true;
|
||||
auto P = td::PromiseCreator::lambda([SelfId = actor_id(this), id](td::Result<td::Unit> R) {
|
||||
R.ensure();
|
||||
td::actor::send_closure(SelfId, &ArchiveManager::deleted_package, id);
|
||||
});
|
||||
td::actor::send_closure(it->second.file_actor_id(), &ArchiveSlice::destroy, std::move(P));
|
||||
}
|
||||
|
||||
void ArchiveManager::deleted_package(PackageId id) {
|
||||
auto key = create_serialize_tl_object<ton_api::db_files_package_key>(id.id, id.key, id.temp);
|
||||
|
||||
std::string value;
|
||||
auto v = index_->get(key.as_slice(), value);
|
||||
v.ensure();
|
||||
CHECK(v.move_as_ok() == td::KeyValue::GetStatus::Ok);
|
||||
|
||||
auto R = fetch_tl_object<ton_api::db_files_package_value>(value, true);
|
||||
R.ensure();
|
||||
auto x = R.move_as_ok();
|
||||
|
||||
if (x->deleted_) {
|
||||
return;
|
||||
}
|
||||
x->deleted_ = true;
|
||||
index_->begin_transaction().ensure();
|
||||
index_->set(key, serialize_tl_object(x, true)).ensure();
|
||||
index_->commit_transaction().ensure();
|
||||
|
||||
auto &m = get_file_map(id);
|
||||
auto it = m.find(id);
|
||||
CHECK(it != m.end());
|
||||
CHECK(it->second.deleted);
|
||||
it->second.clear_actor_id();
|
||||
}
|
||||
|
||||
void ArchiveManager::load_package(PackageId id) {
|
||||
auto key = create_serialize_tl_object<ton_api::db_files_package_key>(id.id, id.key, id.temp);
|
||||
|
||||
std::string value;
|
||||
auto v = index_->get(key.as_slice(), value);
|
||||
v.ensure();
|
||||
CHECK(v.move_as_ok() == td::KeyValue::GetStatus::Ok);
|
||||
|
||||
auto R = fetch_tl_object<ton_api::db_files_package_value>(value, true);
|
||||
R.ensure();
|
||||
auto x = R.move_as_ok();
|
||||
|
||||
if (x->deleted_) {
|
||||
return;
|
||||
}
|
||||
|
||||
FileDescription desc{id, false};
|
||||
if (!id.temp) {
|
||||
for (auto &e : x->firstblocks_) {
|
||||
desc.first_blocks[ShardIdFull{e->workchain_, static_cast<ShardId>(e->shard_)}] = FileDescription::Desc{
|
||||
static_cast<BlockSeqno>(e->seqno_), static_cast<UnixTime>(e->unixtime_), static_cast<LogicalTime>(e->lt_)};
|
||||
}
|
||||
}
|
||||
|
||||
std::string prefix = PSTRING() << db_root_ << id.path() << id.name();
|
||||
desc.file = td::actor::create_actor<ArchiveSlice>("slice", id.key, id.temp, prefix);
|
||||
|
||||
get_file_map(id).emplace(id, std::move(desc));
|
||||
}
|
||||
|
||||
ArchiveManager::FileDescription *ArchiveManager::get_file_desc(ShardIdFull shard, PackageId id, BlockSeqno seqno,
|
||||
UnixTime ts, LogicalTime lt, bool force) {
|
||||
auto &f = get_file_map(id);
|
||||
auto it = f.find(id);
|
||||
if (it != f.end()) {
|
||||
if (it->second.deleted) {
|
||||
CHECK(!force);
|
||||
return nullptr;
|
||||
}
|
||||
if (force && !id.temp) {
|
||||
update_desc(it->second, shard, seqno, ts, lt);
|
||||
}
|
||||
return &it->second;
|
||||
}
|
||||
if (!force) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return add_file_desc(shard, id, seqno, ts, lt);
|
||||
}
|
||||
|
||||
ArchiveManager::FileDescription *ArchiveManager::add_file_desc(ShardIdFull shard, PackageId id, BlockSeqno seqno,
|
||||
UnixTime ts, LogicalTime lt) {
|
||||
auto &f = get_file_map(id);
|
||||
CHECK(f.count(id) == 0);
|
||||
|
||||
FileDescription desc{id, false};
|
||||
td::mkdir(db_root_ + id.path()).ensure();
|
||||
std::string prefix = PSTRING() << db_root_ << id.path() << id.name();
|
||||
desc.file = td::actor::create_actor<ArchiveSlice>("slice", id.key, id.temp, prefix);
|
||||
if (!id.temp) {
|
||||
update_desc(desc, shard, seqno, ts, lt);
|
||||
}
|
||||
|
||||
std::vector<tl_object_ptr<ton_api::db_files_package_firstBlock>> vec;
|
||||
for (auto &e : desc.first_blocks) {
|
||||
vec.push_back(create_tl_object<ton_api::db_files_package_firstBlock>(e.first.workchain, e.first.shard,
|
||||
e.second.seqno, e.second.ts, e.second.lt));
|
||||
}
|
||||
|
||||
index_->begin_transaction().ensure();
|
||||
// add package info to list of packages
|
||||
{
|
||||
std::vector<td::int32> t;
|
||||
std::vector<td::int32> tk;
|
||||
std::vector<td::int32> tt;
|
||||
for (auto &e : files_) {
|
||||
t.push_back(e.first.id);
|
||||
}
|
||||
for (auto &e : key_files_) {
|
||||
tk.push_back(e.first.id);
|
||||
}
|
||||
for (auto &e : temp_files_) {
|
||||
tt.push_back(e.first.id);
|
||||
}
|
||||
(id.temp ? tt : (id.key ? tk : t)).push_back(id.id);
|
||||
index_
|
||||
->set(create_serialize_tl_object<ton_api::db_files_index_key>().as_slice(),
|
||||
create_serialize_tl_object<ton_api::db_files_index_value>(std::move(t), std::move(tk), std::move(tt))
|
||||
.as_slice())
|
||||
.ensure();
|
||||
}
|
||||
// add package info key
|
||||
{
|
||||
index_
|
||||
->set(create_serialize_tl_object<ton_api::db_files_package_key>(id.id, id.key, id.temp).as_slice(),
|
||||
create_serialize_tl_object<ton_api::db_files_package_value>(id.id, id.key, id.temp, std::move(vec), false)
|
||||
.as_slice())
|
||||
.ensure();
|
||||
}
|
||||
index_->commit_transaction().ensure();
|
||||
|
||||
return &f.emplace(id, std::move(desc)).first->second;
|
||||
}
|
||||
|
||||
void ArchiveManager::update_desc(FileDescription &desc, ShardIdFull shard, BlockSeqno seqno, UnixTime ts,
|
||||
LogicalTime lt) {
|
||||
auto it = desc.first_blocks.find(shard);
|
||||
if (it != desc.first_blocks.end() && it->second.seqno <= seqno) {
|
||||
return;
|
||||
}
|
||||
desc.first_blocks[shard] = FileDescription::Desc{seqno, ts, lt};
|
||||
std::vector<tl_object_ptr<ton_api::db_files_package_firstBlock>> vec;
|
||||
for (auto &e : desc.first_blocks) {
|
||||
vec.push_back(create_tl_object<ton_api::db_files_package_firstBlock>(e.first.workchain, e.first.shard,
|
||||
e.second.seqno, e.second.ts, e.second.lt));
|
||||
}
|
||||
index_->begin_transaction().ensure();
|
||||
index_
|
||||
->set(create_serialize_tl_object<ton_api::db_files_package_key>(desc.id.id, desc.id.key, desc.id.temp).as_slice(),
|
||||
create_serialize_tl_object<ton_api::db_files_package_value>(desc.id.id, desc.id.key, desc.id.temp,
|
||||
std::move(vec), false)
|
||||
.as_slice())
|
||||
.ensure();
|
||||
index_->commit_transaction().ensure();
|
||||
}
|
||||
|
||||
ArchiveManager::FileDescription *ArchiveManager::get_file_desc_by_seqno(ShardIdFull shard, BlockSeqno seqno,
|
||||
bool key_block) {
|
||||
auto &f = get_file_map(PackageId{0, key_block, false});
|
||||
for (auto it = f.rbegin(); it != f.rend(); it++) {
|
||||
auto i = it->second.first_blocks.find(shard);
|
||||
if (i != it->second.first_blocks.end() && i->second.seqno <= seqno) {
|
||||
return &it->second;
|
||||
}
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
ArchiveManager::FileDescription *ArchiveManager::get_file_desc_by_unix_time(ShardIdFull shard, UnixTime ts,
|
||||
bool key_block) {
|
||||
auto &f = get_file_map(PackageId{0, key_block, false});
|
||||
for (auto it = f.rbegin(); it != f.rend(); it++) {
|
||||
auto i = it->second.first_blocks.find(shard);
|
||||
if (i != it->second.first_blocks.end() && i->second.ts <= ts) {
|
||||
return &it->second;
|
||||
}
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
ArchiveManager::FileDescription *ArchiveManager::get_file_desc_by_lt(ShardIdFull shard, LogicalTime lt,
|
||||
bool key_block) {
|
||||
auto &f = get_file_map(PackageId{0, key_block, false});
|
||||
for (auto it = f.rbegin(); it != f.rend(); it++) {
|
||||
auto i = it->second.first_blocks.find(shard);
|
||||
if (i != it->second.first_blocks.end() && i->second.lt <= lt) {
|
||||
return &it->second;
|
||||
}
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
ArchiveManager::FileDescription *ArchiveManager::get_file_desc_by_seqno(AccountIdPrefixFull account, BlockSeqno seqno,
|
||||
bool key_block) {
|
||||
auto &f = get_file_map(PackageId{0, key_block, false});
|
||||
if (account.is_masterchain()) {
|
||||
return get_file_desc_by_seqno(ShardIdFull{masterchainId}, seqno, key_block);
|
||||
}
|
||||
for (auto it = f.rbegin(); it != f.rend(); it++) {
|
||||
bool found = false;
|
||||
for (int i = 0; i < 60; i++) {
|
||||
auto shard = shard_prefix(account, i);
|
||||
auto it2 = it->second.first_blocks.find(shard);
|
||||
if (it2 != it->second.first_blocks.end()) {
|
||||
if (it2->second.seqno <= seqno) {
|
||||
return &it->second;
|
||||
}
|
||||
found = true;
|
||||
} else if (found) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
ArchiveManager::FileDescription *ArchiveManager::get_file_desc_by_unix_time(AccountIdPrefixFull account, UnixTime ts,
|
||||
bool key_block) {
|
||||
auto &f = get_file_map(PackageId{0, key_block, false});
|
||||
if (account.is_masterchain()) {
|
||||
return get_file_desc_by_unix_time(ShardIdFull{masterchainId}, ts, key_block);
|
||||
}
|
||||
for (auto it = f.rbegin(); it != f.rend(); it++) {
|
||||
bool found = false;
|
||||
for (int i = 0; i < 60; i++) {
|
||||
auto shard = shard_prefix(account, i);
|
||||
auto it2 = it->second.first_blocks.find(shard);
|
||||
if (it2 != it->second.first_blocks.end()) {
|
||||
if (it2->second.ts <= ts) {
|
||||
return &it->second;
|
||||
}
|
||||
found = true;
|
||||
} else if (found) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
ArchiveManager::FileDescription *ArchiveManager::get_file_desc_by_lt(AccountIdPrefixFull account, LogicalTime lt,
|
||||
bool key_block) {
|
||||
auto &f = get_file_map(PackageId{0, key_block, false});
|
||||
if (account.is_masterchain()) {
|
||||
return get_file_desc_by_lt(ShardIdFull{masterchainId}, lt, key_block);
|
||||
}
|
||||
for (auto it = f.rbegin(); it != f.rend(); it++) {
|
||||
bool found = false;
|
||||
for (int i = 0; i < 60; i++) {
|
||||
auto shard = shard_prefix(account, i);
|
||||
auto it2 = it->second.first_blocks.find(shard);
|
||||
if (it2 != it->second.first_blocks.end()) {
|
||||
if (it2->second.lt <= lt) {
|
||||
return &it->second;
|
||||
}
|
||||
found = true;
|
||||
} else if (found) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
ArchiveManager::FileDescription *ArchiveManager::get_temp_file_desc_by_idx(PackageId idx) {
|
||||
auto it = temp_files_.find(idx);
|
||||
if (it != temp_files_.end()) {
|
||||
if (it->second.deleted) {
|
||||
return nullptr;
|
||||
} else {
|
||||
return &it->second;
|
||||
}
|
||||
} else {
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
PackageId ArchiveManager::get_max_temp_file_desc_idx() {
|
||||
if (temp_files_.size() == 0) {
|
||||
return PackageId::empty(false, true);
|
||||
} else {
|
||||
return temp_files_.rbegin()->first;
|
||||
}
|
||||
}
|
||||
|
||||
PackageId ArchiveManager::get_prev_temp_file_desc_idx(PackageId idx) {
|
||||
auto it = temp_files_.lower_bound(idx);
|
||||
if (it == temp_files_.end()) {
|
||||
return PackageId::empty(false, true);
|
||||
}
|
||||
if (it == temp_files_.begin()) {
|
||||
return PackageId::empty(false, true);
|
||||
}
|
||||
it--;
|
||||
return it->first;
|
||||
}
|
||||
|
||||
void ArchiveManager::start_up() {
|
||||
td::mkdir(db_root_).ensure();
|
||||
td::mkdir(db_root_ + "/archive/").ensure();
|
||||
td::mkdir(db_root_ + "/archive/tmp/").ensure();
|
||||
td::mkdir(db_root_ + "/archive/packages/").ensure();
|
||||
td::mkdir(db_root_ + "/archive/states/").ensure();
|
||||
td::mkdir(db_root_ + "/files/").ensure();
|
||||
td::mkdir(db_root_ + "/files/packages/").ensure();
|
||||
index_ = std::make_shared<td::RocksDb>(td::RocksDb::open(db_root_ + "/files/globalindex").move_as_ok());
|
||||
std::string value;
|
||||
auto v = index_->get(create_serialize_tl_object<ton_api::db_files_index_key>().as_slice(), value);
|
||||
v.ensure();
|
||||
if (v.move_as_ok() == td::KeyValue::GetStatus::Ok) {
|
||||
auto R = fetch_tl_object<ton_api::db_files_index_value>(value, true);
|
||||
R.ensure();
|
||||
auto x = R.move_as_ok();
|
||||
|
||||
for (auto &d : x->packages_) {
|
||||
load_package(PackageId{static_cast<td::uint32>(d), false, false});
|
||||
}
|
||||
for (auto &d : x->key_packages_) {
|
||||
load_package(PackageId{static_cast<td::uint32>(d), true, false});
|
||||
}
|
||||
for (auto &d : x->temp_packages_) {
|
||||
load_package(PackageId{static_cast<td::uint32>(d), false, true});
|
||||
}
|
||||
}
|
||||
|
||||
td::WalkPath::run(db_root_ + "/archive/states/", [&](td::CSlice fname, td::WalkPath::Type t) -> void {
|
||||
if (t == td::WalkPath::Type::NotDir) {
|
||||
LOG(ERROR) << "checking file " << fname;
|
||||
auto pos = fname.rfind('/');
|
||||
if (pos != td::Slice::npos) {
|
||||
fname.remove_prefix(pos + 1);
|
||||
}
|
||||
auto R = FileReferenceShort::create(fname.str());
|
||||
if (R.is_error()) {
|
||||
auto R2 = FileReference::create(fname.str());
|
||||
if (R2.is_error()) {
|
||||
LOG(ERROR) << "deleting bad state file '" << fname << "': " << R.move_as_error() << R2.move_as_error();
|
||||
td::unlink(db_root_ + "/archive/states/" + fname.str()).ignore();
|
||||
return;
|
||||
}
|
||||
auto d = R2.move_as_ok();
|
||||
auto newfname = d.filename_short();
|
||||
td::rename(db_root_ + "/archive/states/" + fname.str(), db_root_ + "/archive/states/" + newfname).ensure();
|
||||
R = FileReferenceShort::create(newfname);
|
||||
R.ensure();
|
||||
}
|
||||
auto f = R.move_as_ok();
|
||||
auto hash = f.hash();
|
||||
perm_states_[hash] = std::move(f);
|
||||
}
|
||||
}).ensure();
|
||||
|
||||
persistent_state_gc(FileHash::zero());
|
||||
}
|
||||
|
||||
void ArchiveManager::run_gc(UnixTime ts) {
|
||||
auto p = get_temp_package_id_by_unixtime(ts);
|
||||
std::vector<PackageId> vec;
|
||||
for (auto &x : temp_files_) {
|
||||
if (x.first < p) {
|
||||
vec.push_back(x.first);
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (vec.size() <= 1) {
|
||||
return;
|
||||
}
|
||||
vec.resize(vec.size() - 1, PackageId::empty(false, true));
|
||||
|
||||
for (auto &x : vec) {
|
||||
delete_package(x);
|
||||
}
|
||||
}
|
||||
|
||||
void ArchiveManager::persistent_state_gc(FileHash last) {
|
||||
if (perm_states_.size() == 0) {
|
||||
delay_action(
|
||||
[hash = FileHash::zero(), SelfId = actor_id(this)]() {
|
||||
td::actor::send_closure(SelfId, &ArchiveManager::persistent_state_gc, hash);
|
||||
},
|
||||
td::Timestamp::in(1.0));
|
||||
return;
|
||||
}
|
||||
auto it = perm_states_.lower_bound(last);
|
||||
if (it != perm_states_.end() && it->first == last) {
|
||||
it++;
|
||||
}
|
||||
if (it == perm_states_.end()) {
|
||||
it = perm_states_.begin();
|
||||
}
|
||||
|
||||
auto &F = it->second;
|
||||
auto hash = F.hash();
|
||||
|
||||
int res = 0;
|
||||
BlockSeqno seqno = 0;
|
||||
F.ref().visit(td::overloaded([&](const fileref::ZeroStateShort &x) { res = 1; },
|
||||
[&](const fileref::PersistentStateShort &x) {
|
||||
res = 0;
|
||||
seqno = x.masterchain_seqno;
|
||||
},
|
||||
[&](const auto &obj) { res = -1; }));
|
||||
|
||||
if (res == -1) {
|
||||
td::unlink(db_root_ + "/archive/states/" + F.filename_short()).ignore();
|
||||
perm_states_.erase(it);
|
||||
}
|
||||
if (res != 0) {
|
||||
delay_action([hash, SelfId = actor_id(
|
||||
this)]() { td::actor::send_closure(SelfId, &ArchiveManager::persistent_state_gc, hash); },
|
||||
td::Timestamp::in(1.0));
|
||||
return;
|
||||
}
|
||||
|
||||
auto P = td::PromiseCreator::lambda([SelfId = actor_id(this), hash](td::Result<BlockHandle> R) {
|
||||
if (R.is_error()) {
|
||||
td::actor::send_closure(SelfId, &ArchiveManager::got_gc_masterchain_handle, nullptr, hash);
|
||||
} else {
|
||||
td::actor::send_closure(SelfId, &ArchiveManager::got_gc_masterchain_handle, R.move_as_ok(), hash);
|
||||
}
|
||||
});
|
||||
|
||||
get_block_by_seqno(AccountIdPrefixFull{masterchainId, 0}, seqno, std::move(P));
|
||||
}
|
||||
|
||||
void ArchiveManager::got_gc_masterchain_handle(BlockHandle handle, FileHash hash) {
|
||||
bool to_del = false;
|
||||
if (!handle || !handle->inited_unix_time() || !handle->unix_time()) {
|
||||
to_del = true;
|
||||
} else {
|
||||
auto ttl = ValidatorManager::persistent_state_ttl(handle->unix_time());
|
||||
to_del = ttl < td::Clocks::system();
|
||||
}
|
||||
auto it = perm_states_.find(hash);
|
||||
CHECK(it != perm_states_.end());
|
||||
auto &F = it->second;
|
||||
if (to_del) {
|
||||
td::unlink(db_root_ + "/archive/states/" + F.filename_short()).ignore();
|
||||
perm_states_.erase(it);
|
||||
}
|
||||
delay_action([hash, SelfId = actor_id(
|
||||
this)]() { td::actor::send_closure(SelfId, &ArchiveManager::persistent_state_gc, hash); },
|
||||
td::Timestamp::in(1.0));
|
||||
}
|
||||
|
||||
PackageId ArchiveManager::get_temp_package_id() const {
|
||||
return get_temp_package_id_by_unixtime(static_cast<UnixTime>(td::Clocks::system()));
|
||||
}
|
||||
|
||||
PackageId ArchiveManager::get_temp_package_id_by_unixtime(UnixTime ts) const {
|
||||
return PackageId{ts - (ts % 3600), false, true};
|
||||
}
|
||||
|
||||
PackageId ArchiveManager::get_key_package_id(BlockSeqno seqno) const {
|
||||
return PackageId{seqno - seqno % 200000, true, false};
|
||||
}
|
||||
|
||||
PackageId ArchiveManager::get_package_id(BlockSeqno seqno) const {
|
||||
auto it = files_.upper_bound(PackageId{seqno, false, false});
|
||||
CHECK(it != files_.begin());
|
||||
it--;
|
||||
return it->first;
|
||||
}
|
||||
|
||||
PackageId ArchiveManager::get_package_id_force(BlockSeqno masterchain_seqno, ShardIdFull shard, BlockSeqno seqno,
|
||||
UnixTime ts, LogicalTime lt, bool is_key) {
|
||||
PackageId p = PackageId::empty(false, false);
|
||||
if (!is_key) {
|
||||
auto it = files_.upper_bound(PackageId{masterchain_seqno, false, false});
|
||||
p = PackageId{masterchain_seqno - (masterchain_seqno % 20000), false, false};
|
||||
if (it != files_.begin()) {
|
||||
it--;
|
||||
if (p < it->first) {
|
||||
p = it->first;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
p = PackageId{masterchain_seqno, false, false};
|
||||
}
|
||||
auto it = files_.find(p);
|
||||
if (it != files_.end()) {
|
||||
return it->first;
|
||||
}
|
||||
add_file_desc(shard, p, seqno, ts, lt);
|
||||
it = files_.find(p);
|
||||
CHECK(it != files_.end());
|
||||
return it->first;
|
||||
}
|
||||
|
||||
void ArchiveManager::get_archive_id(BlockSeqno masterchain_seqno, td::Promise<td::uint64> promise) {
|
||||
auto F = get_file_desc_by_seqno(ShardIdFull{masterchainId}, masterchain_seqno, false);
|
||||
if (!F) {
|
||||
promise.set_error(td::Status::Error(ErrorCode::notready, "archive not found"));
|
||||
return;
|
||||
}
|
||||
|
||||
promise.set_result(F->id.id);
|
||||
}
|
||||
|
||||
void ArchiveManager::get_archive_slice(td::uint64 archive_id, td::uint64 offset, td::uint32 limit,
|
||||
td::Promise<td::BufferSlice> promise) {
|
||||
if (archive_id != static_cast<td::uint32>(archive_id)) {
|
||||
promise.set_error(td::Status::Error(ErrorCode::notready, "archive not found"));
|
||||
return;
|
||||
}
|
||||
auto F = get_file_desc(ShardIdFull{masterchainId}, PackageId{static_cast<BlockSeqno>(archive_id), false, false}, 0, 0,
|
||||
0, false);
|
||||
if (!F) {
|
||||
promise.set_error(td::Status::Error(ErrorCode::notready, "archive not found"));
|
||||
return;
|
||||
}
|
||||
|
||||
td::actor::send_closure(F->file_actor_id(), &ArchiveSlice::get_slice, offset, limit, std::move(promise));
|
||||
}
|
||||
|
||||
void ArchiveManager::commit_transaction() {
|
||||
if (!async_mode_ || huge_transaction_size_++ >= 100) {
|
||||
index_->commit_transaction().ensure();
|
||||
if (async_mode_) {
|
||||
huge_transaction_size_ = 0;
|
||||
huge_transaction_started_ = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ArchiveManager::set_async_mode(bool mode, td::Promise<td::Unit> promise) {
|
||||
async_mode_ = mode;
|
||||
if (!async_mode_ && huge_transaction_started_) {
|
||||
index_->commit_transaction().ensure();
|
||||
huge_transaction_size_ = 0;
|
||||
huge_transaction_started_ = false;
|
||||
}
|
||||
|
||||
td::MultiPromise mp;
|
||||
auto ig = mp.init_guard();
|
||||
ig.add_promise(std::move(promise));
|
||||
|
||||
for (auto &x : key_files_) {
|
||||
if (!x.second.deleted) {
|
||||
td::actor::send_closure(x.second.file_actor_id(), &ArchiveSlice::set_async_mode, mode, ig.get_promise());
|
||||
}
|
||||
}
|
||||
for (auto &x : temp_files_) {
|
||||
if (!x.second.deleted) {
|
||||
td::actor::send_closure(x.second.file_actor_id(), &ArchiveSlice::set_async_mode, mode, ig.get_promise());
|
||||
}
|
||||
}
|
||||
for (auto &x : files_) {
|
||||
if (!x.second.deleted) {
|
||||
td::actor::send_closure(x.second.file_actor_id(), &ArchiveSlice::set_async_mode, mode, ig.get_promise());
|
||||
}
|
||||
}
|
||||
}
|
||||
} // namespace validator
|
||||
|
||||
} // namespace ton
|
155
validator/db/archive-manager.hpp
Normal file
155
validator/db/archive-manager.hpp
Normal file
|
@ -0,0 +1,155 @@
|
|||
#pragma once
|
||||
|
||||
#include "archive-slice.hpp"
|
||||
|
||||
namespace ton {
|
||||
|
||||
namespace validator {
|
||||
|
||||
struct PackageId {
|
||||
td::uint32 id;
|
||||
bool key;
|
||||
bool temp;
|
||||
|
||||
explicit PackageId(td::uint32 id, bool key, bool temp) : id(id), key(key), temp(temp) {
|
||||
}
|
||||
|
||||
bool operator<(const PackageId &with) const {
|
||||
return id < with.id;
|
||||
}
|
||||
bool operator==(const PackageId &with) const {
|
||||
return id == with.id;
|
||||
}
|
||||
|
||||
std::string path() const;
|
||||
std::string name() const;
|
||||
|
||||
bool is_empty() {
|
||||
return id == std::numeric_limits<td::uint32>::max();
|
||||
}
|
||||
static PackageId empty(bool key, bool temp) {
|
||||
return PackageId(std::numeric_limits<td::uint32>::max(), key, temp);
|
||||
}
|
||||
};
|
||||
|
||||
class RootDb;
|
||||
|
||||
class ArchiveManager : public td::actor::Actor {
|
||||
public:
|
||||
ArchiveManager(td::actor::ActorId<RootDb> root, std::string db_root);
|
||||
|
||||
void add_handle(BlockHandle handle, td::Promise<td::Unit> promise);
|
||||
void update_handle(BlockHandle handle, td::Promise<td::Unit> promise);
|
||||
void add_file(BlockHandle handle, FileReference ref_id, td::BufferSlice data, td::Promise<td::Unit> promise);
|
||||
void add_key_block_proof(BlockSeqno seqno, UnixTime ts, LogicalTime lt, FileReference ref_id, td::BufferSlice data,
|
||||
td::Promise<td::Unit> promise);
|
||||
void add_temp_file_short(FileReference ref_id, td::BufferSlice data, td::Promise<td::Unit> promise);
|
||||
void get_handle(BlockIdExt block_id, td::Promise<BlockHandle> promise);
|
||||
void get_key_block_proof(FileReference ref_id, td::Promise<td::BufferSlice> promise);
|
||||
void get_temp_file_short(FileReference ref_id, td::Promise<td::BufferSlice> promise);
|
||||
void get_file_short(FileReference ref_id, td::Promise<td::BufferSlice> promise);
|
||||
void get_file(BlockHandle handle, FileReference ref_id, td::Promise<td::BufferSlice> promise);
|
||||
|
||||
void add_zero_state(BlockIdExt block_id, td::BufferSlice data, td::Promise<td::Unit> promise);
|
||||
void add_persistent_state(BlockIdExt block_id, BlockIdExt masterchain_block_id, td::BufferSlice data,
|
||||
td::Promise<td::Unit> promise);
|
||||
void get_zero_state(BlockIdExt block_id, td::Promise<td::BufferSlice> promise);
|
||||
void get_persistent_state(BlockIdExt block_id, BlockIdExt masterchain_block_id, td::Promise<td::BufferSlice> promise);
|
||||
void get_persistent_state_slice(BlockIdExt block_id, BlockIdExt masterchain_block_id, td::int64 offset,
|
||||
td::int64 max_size, td::Promise<td::BufferSlice> promise);
|
||||
void check_persistent_state(BlockIdExt block_id, BlockIdExt masterchain_block_id, td::Promise<bool> promise);
|
||||
void check_zero_state(BlockIdExt block_id, td::Promise<bool> promise);
|
||||
|
||||
void run_gc(UnixTime ts);
|
||||
|
||||
/* from LTDB */
|
||||
void get_block_by_unix_time(AccountIdPrefixFull account_id, UnixTime ts, td::Promise<BlockHandle> promise);
|
||||
void get_block_by_lt(AccountIdPrefixFull account_id, LogicalTime lt, td::Promise<BlockHandle> promise);
|
||||
void get_block_by_seqno(AccountIdPrefixFull account_id, BlockSeqno seqno, td::Promise<BlockHandle> promise);
|
||||
|
||||
void get_archive_id(BlockSeqno masterchain_seqno, td::Promise<td::uint64> promise);
|
||||
void get_archive_slice(td::uint64 archive_id, td::uint64 offset, td::uint32 limit,
|
||||
td::Promise<td::BufferSlice> promise);
|
||||
|
||||
void start_up() override;
|
||||
|
||||
void begin_transaction();
|
||||
void commit_transaction();
|
||||
void set_async_mode(bool mode, td::Promise<td::Unit> promise);
|
||||
|
||||
private:
|
||||
struct FileDescription {
|
||||
struct Desc {
|
||||
BlockSeqno seqno;
|
||||
UnixTime ts;
|
||||
LogicalTime lt;
|
||||
};
|
||||
FileDescription(PackageId id, bool deleted) : id(id), deleted(deleted) {
|
||||
}
|
||||
auto file_actor_id() const {
|
||||
return file.get();
|
||||
}
|
||||
void clear_actor_id() {
|
||||
file.reset();
|
||||
}
|
||||
PackageId id;
|
||||
bool deleted;
|
||||
|
||||
std::map<ShardIdFull, Desc> first_blocks;
|
||||
td::actor::ActorOwn<ArchiveSlice> file;
|
||||
};
|
||||
|
||||
std::map<PackageId, FileDescription> files_;
|
||||
std::map<PackageId, FileDescription> key_files_;
|
||||
std::map<PackageId, FileDescription> temp_files_;
|
||||
bool async_mode_ = false;
|
||||
bool huge_transaction_started_ = false;
|
||||
td::uint32 huge_transaction_size_ = 0;
|
||||
|
||||
auto &get_file_map(const PackageId &p) {
|
||||
return p.key ? key_files_ : p.temp ? temp_files_ : files_;
|
||||
}
|
||||
|
||||
std::map<FileHash, FileReferenceShort> perm_states_;
|
||||
|
||||
void load_package(PackageId seqno);
|
||||
void delete_package(PackageId seqno);
|
||||
void deleted_package(PackageId seqno);
|
||||
void get_handle_cont(BlockIdExt block_id, PackageId id, td::Promise<BlockHandle> promise);
|
||||
void get_handle_finish(BlockHandle handle, td::Promise<BlockHandle> promise);
|
||||
void get_file_short_cont(FileReference ref_id, PackageId idx, td::Promise<td::BufferSlice> promise);
|
||||
|
||||
FileDescription *get_file_desc(ShardIdFull shard, PackageId id, BlockSeqno seqno, UnixTime ts, LogicalTime lt,
|
||||
bool force);
|
||||
FileDescription *add_file_desc(ShardIdFull shard, PackageId id, BlockSeqno seqno, UnixTime ts, LogicalTime lt);
|
||||
void update_desc(FileDescription &desc, ShardIdFull shard, BlockSeqno seqno, UnixTime ts, LogicalTime lt);
|
||||
FileDescription *get_file_desc_by_seqno(ShardIdFull shard, BlockSeqno seqno, bool key_block);
|
||||
FileDescription *get_file_desc_by_lt(ShardIdFull shard, LogicalTime lt, bool key_block);
|
||||
FileDescription *get_file_desc_by_unix_time(ShardIdFull shard, UnixTime ts, bool key_block);
|
||||
FileDescription *get_file_desc_by_seqno(AccountIdPrefixFull shard, BlockSeqno seqno, bool key_block);
|
||||
FileDescription *get_file_desc_by_lt(AccountIdPrefixFull shard, LogicalTime lt, bool key_block);
|
||||
FileDescription *get_file_desc_by_unix_time(AccountIdPrefixFull shard, UnixTime ts, bool key_block);
|
||||
FileDescription *get_temp_file_desc_by_idx(PackageId idx);
|
||||
PackageId get_max_temp_file_desc_idx();
|
||||
PackageId get_prev_temp_file_desc_idx(PackageId id);
|
||||
|
||||
void written_perm_state(FileReferenceShort id);
|
||||
|
||||
void persistent_state_gc(FileHash last);
|
||||
void got_gc_masterchain_handle(BlockHandle handle, FileHash hash);
|
||||
|
||||
std::string db_root_;
|
||||
|
||||
std::shared_ptr<td::KeyValue> index_;
|
||||
|
||||
PackageId get_package_id(BlockSeqno seqno) const;
|
||||
PackageId get_package_id_force(BlockSeqno masterchain_seqno, ShardIdFull shard, BlockSeqno seqno, UnixTime ts,
|
||||
LogicalTime lt, bool is_key);
|
||||
PackageId get_temp_package_id() const;
|
||||
PackageId get_key_package_id(BlockSeqno seqno) const;
|
||||
PackageId get_temp_package_id_by_unixtime(UnixTime ts) const;
|
||||
};
|
||||
|
||||
} // namespace validator
|
||||
|
||||
} // namespace ton
|
475
validator/db/archive-mover.cpp
Normal file
475
validator/db/archive-mover.cpp
Normal file
|
@ -0,0 +1,475 @@
|
|||
#include "archive-mover.hpp"
|
||||
#include "td/actor/MultiPromise.h"
|
||||
#include "validator/fabric.h"
|
||||
|
||||
namespace ton {
|
||||
|
||||
namespace validator {
|
||||
|
||||
void ArchiveFileMover::start_up() {
|
||||
auto P = td::PromiseCreator::lambda([SelfId = actor_id(this)](td::Result<BlockHandle> R) {
|
||||
td::actor::send_closure(SelfId, &ArchiveFileMover::got_block_handle1, std::move(R));
|
||||
});
|
||||
td::actor::send_closure(archive_manager_, &ArchiveManager::get_handle, block_id_, std::move(P));
|
||||
}
|
||||
|
||||
void ArchiveFileMover::got_block_handle0(td::Result<BlockHandle> R) {
|
||||
if (R.is_ok()) {
|
||||
handle_ = R.move_as_ok();
|
||||
CHECK(handle_->moved_to_archive());
|
||||
CHECK(handle_->handle_moved_to_archive());
|
||||
finish_query();
|
||||
return;
|
||||
}
|
||||
auto P = td::PromiseCreator::lambda([SelfId = actor_id(this)](td::Result<BlockHandle> R) {
|
||||
td::actor::send_closure(SelfId, &ArchiveFileMover::got_block_handle1, std::move(R));
|
||||
});
|
||||
td::actor::send_closure(old_archive_manager_, &OldArchiveManager::read_handle, block_id_, std::move(P));
|
||||
}
|
||||
|
||||
void ArchiveFileMover::got_block_handle1(td::Result<BlockHandle> R) {
|
||||
if (R.is_ok()) {
|
||||
handle_ = R.move_as_ok();
|
||||
got_block_handle();
|
||||
return;
|
||||
}
|
||||
|
||||
if (R.error().code() != ErrorCode::notready) {
|
||||
abort_query(R.move_as_error());
|
||||
return;
|
||||
}
|
||||
|
||||
auto P = td::PromiseCreator::lambda([SelfId = actor_id(this)](td::Result<BlockHandle> R) {
|
||||
td::actor::send_closure(SelfId, &ArchiveFileMover::got_block_handle1, std::move(R));
|
||||
});
|
||||
td::actor::send_closure(block_db_, &BlockDb::get_block_handle, std::move(P));
|
||||
}
|
||||
|
||||
void ArchiveFileMover::got_block_handle2(td::Result<BlockHandle> R) {
|
||||
if (R.is_ok()) {
|
||||
handle_ = R.move_as_ok();
|
||||
got_block_handle();
|
||||
return;
|
||||
}
|
||||
|
||||
if (R.error().code() != ErrorCode::notready) {
|
||||
abort_query(R.move_as_error());
|
||||
return;
|
||||
}
|
||||
|
||||
finish_query();
|
||||
}
|
||||
|
||||
void ArchiveFileMover::got_block_handle() {
|
||||
if (!handle_->is_applied()) {
|
||||
finish_query();
|
||||
return;
|
||||
}
|
||||
if (handle_->id().seqno() == 0) {
|
||||
processed_all_children();
|
||||
return;
|
||||
}
|
||||
|
||||
CHECK(handle_->inited_prev());
|
||||
|
||||
auto P = td::PromiseCreator::lambda([SelfId = actor_id(this)](td::Result<td::Unit> R) {
|
||||
if (R.is_error()) {
|
||||
td::actor::send_closure(SelfId, &ArchiveFileMover::abort_query, R.move_as_error());
|
||||
} else {
|
||||
td::actor::send_closure(SelfId, &ArchiveFileMover::processed_child);
|
||||
}
|
||||
});
|
||||
|
||||
td::actor::create_actor<ArchiveFileMover>("mover", handle_->one_prev(left_), mode_, block_db_, file_db_,
|
||||
old_archive_db_, old_archive_manager_, archive_manager_, std::move(P))
|
||||
.release();
|
||||
}
|
||||
|
||||
void ArchiveFileMover::processed_child() {
|
||||
if (!left_ || !handle_->merge_before()) {
|
||||
processed_all_children();
|
||||
return;
|
||||
}
|
||||
left_ = false;
|
||||
auto P = td::PromiseCreator::lambda([SelfId = actor_id(this)](td::Result<td::Unit> R) {
|
||||
if (R.is_error()) {
|
||||
td::actor::send_closure(SelfId, &ArchiveFileMover::abort_query, R.move_as_error());
|
||||
} else {
|
||||
td::actor::send_closure(SelfId, &ArchiveFileMover::processed_child);
|
||||
}
|
||||
});
|
||||
|
||||
td::actor::create_actor<ArchiveFileMover>("mover", handle_->one_prev(left_), mode_, block_db_, file_db_,
|
||||
old_archive_db_, old_archive_manager_, archive_manager_, std::move(P))
|
||||
.release();
|
||||
}
|
||||
|
||||
void ArchiveFileMover::processed_all_children() {
|
||||
if (!handle_->received()) {
|
||||
got_block_data(td::BufferSlice{});
|
||||
} else {
|
||||
auto P = td::PromiseCreator::lambda([SelfId = actor_id(this)](td::Result<td::BufferSlice> R) {
|
||||
td::actor::send_closure(SelfId, &ArchiveFileMover::got_block_data, std::move(R));
|
||||
});
|
||||
|
||||
if (handle_->moved_to_archive()) {
|
||||
CHECK(handle_->inited_unix_time());
|
||||
td::actor::send_closure(old_archive_manager_, &OldArchiveManager::read, handle_->unix_time(),
|
||||
handle_->is_key_block(), FileDb::RefId{fileref::Block{handle_->id()}}, std::move(P));
|
||||
} else {
|
||||
td::actor::send_closure(handle_->moved_to_storage() ? old_archive_db_ : file_db_, &FileDb::load_file,
|
||||
FileDb::RefId{fileref::Block{handle_->id()}}, std::move(P));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ArchiveFileMover::got_block_data(td::Result<td::BufferSlice> R) {
|
||||
if (R.is_error()) {
|
||||
if (R.error().code() != ErrorCode::notready) {
|
||||
abort_query(R.move_as_error());
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
data_ = R.move_as_ok();
|
||||
}
|
||||
if (!handle_->inited_proof()) {
|
||||
got_block_proof(td::BufferSlice{});
|
||||
} else {
|
||||
auto P = td::PromiseCreator::lambda([SelfId = actor_id(this)](td::Result<td::BufferSlice> R) {
|
||||
td::actor::send_closure(SelfId, &ArchiveFileMover::got_block_proof, std::move(R));
|
||||
});
|
||||
|
||||
if (handle_->moved_to_archive()) {
|
||||
CHECK(handle_->inited_unix_time());
|
||||
td::actor::send_closure(old_archive_manager_, &OldArchiveManager::read, handle_->unix_time(),
|
||||
handle_->is_key_block(), FileDb::RefId{fileref::Proof{handle_->id()}}, std::move(P));
|
||||
} else {
|
||||
td::actor::send_closure(handle_->moved_to_storage() ? old_archive_db_ : file_db_, &FileDb::load_file,
|
||||
FileDb::RefId{fileref::Proof{handle_->id()}}, std::move(P));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ArchiveFileMover::got_block_proof(td::Result<td::BufferSlice> R) {
|
||||
if (R.is_error()) {
|
||||
if (R.error().code() != ErrorCode::notready) {
|
||||
abort_query(R.move_as_error());
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
proof_ = R.move_as_ok();
|
||||
}
|
||||
if (!handle_->inited_proof_link()) {
|
||||
got_block_proof_link(td::BufferSlice{});
|
||||
} else {
|
||||
auto P = td::PromiseCreator::lambda([SelfId = actor_id(this)](td::Result<td::BufferSlice> R) {
|
||||
td::actor::send_closure(SelfId, &ArchiveFileMover::got_block_proof_link, std::move(R));
|
||||
});
|
||||
|
||||
if (handle_->moved_to_archive()) {
|
||||
CHECK(handle_->inited_unix_time());
|
||||
td::actor::send_closure(old_archive_manager_, &OldArchiveManager::read, handle_->unix_time(),
|
||||
handle_->is_key_block(), FileDb::RefId{fileref::ProofLink{handle_->id()}}, std::move(P));
|
||||
} else {
|
||||
td::actor::send_closure(handle_->moved_to_storage() ? old_archive_db_ : file_db_, &FileDb::load_file,
|
||||
FileDb::RefId{fileref::ProofLink{handle_->id()}}, std::move(P));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ArchiveFileMover::got_block_proof_link(td::Result<td::BufferSlice> R) {
|
||||
if (R.is_error()) {
|
||||
if (R.error().code() != ErrorCode::notready) {
|
||||
abort_query(R.move_as_error());
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
proof_link_ = R.move_as_ok();
|
||||
}
|
||||
|
||||
td::MultiPromise mp;
|
||||
auto ig = mp.init_guard();
|
||||
ig.add_promise([SelfId = actor_id(this)](td::Result<td::Unit> R) {
|
||||
R.ensure();
|
||||
td::actor::send_closure(SelfId, &ArchiveFileMover::written_data);
|
||||
});
|
||||
|
||||
if (data_.size() > 0) {
|
||||
td::actor::send_closure(archive_manager_, &ArchiveManager::add_file, handle_, fileref::Block{block_id_},
|
||||
std::move(data_), ig.get_promise());
|
||||
}
|
||||
if (proof_.size() > 0) {
|
||||
td::actor::send_closure(archive_manager_, &ArchiveManager::add_file, handle_, fileref::Proof{block_id_},
|
||||
std::move(proof_), ig.get_promise());
|
||||
}
|
||||
if (proof_link_.size() > 0) {
|
||||
td::actor::send_closure(archive_manager_, &ArchiveManager::add_file, handle_, fileref::ProofLink{block_id_},
|
||||
std::move(proof_link_), ig.get_promise());
|
||||
}
|
||||
}
|
||||
|
||||
void ArchiveFileMover::written_data() {
|
||||
handle_->set_moved_to_archive();
|
||||
auto P = td::PromiseCreator::lambda([SelfId = actor_id(this)](td::Result<td::Unit> R) {
|
||||
R.ensure();
|
||||
td::actor::send_closure(SelfId, &ArchiveFileMover::written_handle);
|
||||
});
|
||||
td::actor::send_closure(archive_manager_, &ArchiveManager::add_handle, handle_, std::move(P));
|
||||
}
|
||||
|
||||
void ArchiveFileMover::written_handle() {
|
||||
CHECK(handle_->handle_moved_to_archive());
|
||||
finish_query();
|
||||
}
|
||||
|
||||
void ArchiveFileMover::abort_query(td::Status error) {
|
||||
if (promise_) {
|
||||
promise_.set_error(std::move(error));
|
||||
}
|
||||
stop();
|
||||
}
|
||||
|
||||
void ArchiveFileMover::finish_query() {
|
||||
if (promise_) {
|
||||
promise_.set_value(td::Unit());
|
||||
}
|
||||
stop();
|
||||
}
|
||||
|
||||
void ArchiveKeyBlockMover::start_up() {
|
||||
auto P = td::PromiseCreator::lambda([SelfId = actor_id(this)](td::Result<td::BufferSlice> R) {
|
||||
if (R.is_ok()) {
|
||||
td::actor::send_closure(SelfId, &ArchiveKeyBlockMover::skip_block_proof, R.move_as_ok());
|
||||
} else {
|
||||
td::actor::send_closure(SelfId, &ArchiveKeyBlockMover::failed_to_get_proof0);
|
||||
}
|
||||
});
|
||||
if (proof_link_) {
|
||||
td::actor::send_closure(archive_manager_, &ArchiveManager::get_file_short, fileref::ProofLink{block_id_},
|
||||
std::move(P));
|
||||
} else {
|
||||
td::actor::send_closure(archive_manager_, &ArchiveManager::get_file_short, fileref::Proof{block_id_}, std::move(P));
|
||||
}
|
||||
}
|
||||
|
||||
void ArchiveKeyBlockMover::failed_to_get_proof0() {
|
||||
auto P = td::PromiseCreator::lambda([SelfId = actor_id(this)](td::Result<td::BufferSlice> R) {
|
||||
if (R.is_ok()) {
|
||||
td::actor::send_closure(SelfId, &ArchiveKeyBlockMover::got_block_proof, R.move_as_ok());
|
||||
} else {
|
||||
td::actor::send_closure(SelfId, &ArchiveKeyBlockMover::failed_to_get_proof1);
|
||||
}
|
||||
});
|
||||
if (proof_link_) {
|
||||
td::actor::send_closure(old_archive_manager_, &OldArchiveManager::read, fileref::ProofLink{block_id_},
|
||||
std::move(P));
|
||||
} else {
|
||||
td::actor::send_closure(old_archive_manager_, &OldArchiveManager::read, fileref::Proof{block_id_}, std::move(P));
|
||||
}
|
||||
}
|
||||
|
||||
void ArchiveKeyBlockMover::failed_to_get_proof1() {
|
||||
auto P = td::PromiseCreator::lambda([SelfId = actor_id(this)](td::Result<td::BufferSlice> R) {
|
||||
if (R.is_ok()) {
|
||||
td::actor::send_closure(SelfId, &ArchiveKeyBlockMover::got_block_proof, R.move_as_ok());
|
||||
} else {
|
||||
td::actor::send_closure(SelfId, &ArchiveKeyBlockMover::failed_to_get_proof2);
|
||||
}
|
||||
});
|
||||
if (proof_link_) {
|
||||
td::actor::send_closure(old_archive_db_, &FileDb::load_file, fileref::ProofLink{block_id_}, std::move(P));
|
||||
} else {
|
||||
td::actor::send_closure(old_archive_db_, &FileDb::load_file, fileref::Proof{block_id_}, std::move(P));
|
||||
}
|
||||
}
|
||||
|
||||
void ArchiveKeyBlockMover::failed_to_get_proof2() {
|
||||
auto P = td::PromiseCreator::lambda([SelfId = actor_id(this)](td::Result<td::BufferSlice> R) {
|
||||
if (R.is_ok()) {
|
||||
td::actor::send_closure(SelfId, &ArchiveKeyBlockMover::got_block_proof, R.move_as_ok());
|
||||
} else {
|
||||
td::actor::send_closure(SelfId, &ArchiveKeyBlockMover::failed_to_get_proof3);
|
||||
}
|
||||
});
|
||||
if (proof_link_) {
|
||||
td::actor::send_closure(file_db_, &FileDb::load_file, fileref::ProofLink{block_id_}, std::move(P));
|
||||
} else {
|
||||
td::actor::send_closure(file_db_, &FileDb::load_file, fileref::Proof{block_id_}, std::move(P));
|
||||
}
|
||||
}
|
||||
|
||||
void ArchiveKeyBlockMover::failed_to_get_proof3() {
|
||||
if (proof_link_) {
|
||||
written_data();
|
||||
} else {
|
||||
proof_link_ = true;
|
||||
start_up();
|
||||
}
|
||||
}
|
||||
|
||||
void ArchiveKeyBlockMover::got_block_proof(td::BufferSlice data) {
|
||||
data_ = std::move(data);
|
||||
|
||||
auto P = td::PromiseCreator::lambda([SelfId = actor_id(this)](td::Result<td::BufferSlice> R) {
|
||||
R.ensure();
|
||||
td::actor::send_closure(SelfId, &ArchiveKeyBlockMover::written_data);
|
||||
});
|
||||
|
||||
if (proof_link_) {
|
||||
auto p = create_proof_link(block_id_, data_.clone()).move_as_ok();
|
||||
auto h = p->get_basic_header_info().move_as_ok();
|
||||
td::actor::send_closure(archive_manager_, &ArchiveManager::add_key_block_proof, h.utime,
|
||||
fileref::ProofLink{block_id_}, std::move(data_), std::move(P));
|
||||
} else {
|
||||
auto p = create_proof(block_id_, data_.clone()).move_as_ok();
|
||||
auto h = p->get_basic_header_info().move_as_ok();
|
||||
td::actor::send_closure(archive_manager_, &ArchiveManager::add_key_block_proof, h.utime, fileref::Proof{block_id_},
|
||||
std::move(data_), std::move(P));
|
||||
}
|
||||
}
|
||||
|
||||
void ArchiveKeyBlockMover::skip_block_proof(td::BufferSlice data) {
|
||||
data_ = std::move(data);
|
||||
written_data();
|
||||
}
|
||||
|
||||
void ArchiveKeyBlockMover::written_data() {
|
||||
td::Ref<ProofLink> proof_link;
|
||||
if (proof_link_) {
|
||||
auto p = create_proof_link(block_id_, data_.clone()).move_as_ok();
|
||||
proof_link = std::move(p);
|
||||
} else {
|
||||
auto p = create_proof(block_id_, data_.clone()).move_as_ok();
|
||||
proof_link = std::move(p);
|
||||
}
|
||||
auto ts = proof_link->get_basic_header_info().move_as_ok().utime;
|
||||
auto te = ValidatorManager::persistent_state_ttl(ts);
|
||||
if (te < td::Clocks::system()) {
|
||||
finish_query();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
void ArchiveKeyBlockMover::abort_query(td::Status error) {
|
||||
if (promise_) {
|
||||
promise_.set_error(std::move(error));
|
||||
}
|
||||
stop();
|
||||
}
|
||||
|
||||
void ArchiveKeyBlockMover::finish_query() {
|
||||
if (promise_) {
|
||||
promise_.set_value(td::Unit());
|
||||
}
|
||||
stop();
|
||||
}
|
||||
|
||||
void ArchiveMover::start_up() {
|
||||
auto P = td::PromiseCreator::lambda([SelfId = actor_id(this)](td::Result<td::Unit> R) {
|
||||
if (R.is_error()) {
|
||||
td::actor::send_closure(SelfId, &ArchiveMover::abort_query, R.move_as_error());
|
||||
} else {
|
||||
td::actor::send_closure(SelfId, &ArchiveMover::moved_blocks);
|
||||
}
|
||||
});
|
||||
td::actor::create_actor<ArchiveFileMover>("fmover", masterchain_block_id_, block_db_.get(), file_db_.get(),
|
||||
old_archive_db_.get(), old_archive_manager_.get(), archive_manager_.get(),
|
||||
std::move(P))
|
||||
.release();
|
||||
}
|
||||
|
||||
void ArchiveMover::moved_blocks() {
|
||||
auto P = td::PromiseCreator::lambda([SelfId = actor_id(this)](td::Result<BlockHandle> R) {
|
||||
R.ensure();
|
||||
td::actor::send_closure(SelfId, &ArchiveMover::got_handle, R.move_as_ok());
|
||||
});
|
||||
td::actor::send_closure(archive_manager_, &ArchiveManager::get_handle, masterchain_block_id_, std::move(P));
|
||||
}
|
||||
|
||||
void ArchiveMover::got_handle(BlockHandle handle) {
|
||||
handle_ = std::move(handle);
|
||||
CHECK(handle_->is_applied());
|
||||
CHECK(handle_->inited_state_boc());
|
||||
CHECK(!handle_->deleted_state_boc());
|
||||
auto P = td::PromiseCreator::lambda(
|
||||
[handle = handle_, SelfId = actor_id(this)](td::Result<td::Ref<vm::DataCell>> R) mutable {
|
||||
R.ensure();
|
||||
auto S = create_shard_state(handle->id(), R.move_as_ok());
|
||||
S.ensure();
|
||||
td::actor::send_closure(SelfId, &ArchiveMover::got_state, td::Ref<MasterchainState>{S.move_as_ok()});
|
||||
});
|
||||
td::actor::send_closure(cell_db_, &CellDb::load_cell, handle_->state(), std::move(P));
|
||||
}
|
||||
|
||||
void ArchiveMover::got_state(td::Ref<MasterchainState> state) {
|
||||
state_ = std::move(state);
|
||||
|
||||
td::MultiPromise mp;
|
||||
auto ig = mp.init_guard();
|
||||
ig.add_promise([SelfId = actor_id(this)](td::Result<td::Unit> R) {
|
||||
if (R.is_error()) {
|
||||
td::actor::send_closure(SelfId, &ArchiveMover::abort_query, R.move_as_error());
|
||||
} else {
|
||||
td::actor::send_closure(SelfId, &ArchiveMover::moved_key_blocks);
|
||||
}
|
||||
});
|
||||
|
||||
auto k = state_->prev_key_block_id(std::numeric_limits<BlockSeqno>::max());
|
||||
while (k.is_valid() && k.seqno() > 0) {
|
||||
td::actor::create_actor<ArchiveKeyBlockMover>("keymover", k, block_db_.get(), file_db_.get(), old_archive_db_.get(),
|
||||
old_archive_manager_.get(), archive_manager_.get(), ig.get_promise())
|
||||
.release();
|
||||
k = state_->prev_key_block_id(k.seqno());
|
||||
}
|
||||
}
|
||||
|
||||
void ArchiveMover::moved_key_blocks() {
|
||||
td::MultiPromise mp;
|
||||
auto ig = mp.init_guard();
|
||||
ig.add_promise([SelfId = actor_id(this)](td::Result<td::Unit> R) {
|
||||
if (R.is_error()) {
|
||||
td::actor::send_closure(SelfId, &ArchiveMover::abort_query, R.move_as_error());
|
||||
} else {
|
||||
td::actor::send_closure(SelfId, &ArchiveMover::moved_key_blocks);
|
||||
}
|
||||
});
|
||||
|
||||
auto k = state_->prev_key_block_id(std::numeric_limits<BlockSeqno>::max());
|
||||
while (k.is_valid() && k.seqno() > 0) {
|
||||
td::actor::create_actor<ArchiveKeyBlockMover>("keymover", k, block_db_.get(), file_db_.get(), old_archive_db_.get(),
|
||||
old_archive_manager_.get(), archive_manager_.get(), ig.get_promise())
|
||||
.release();
|
||||
k = state_->prev_key_block_id(k.seqno());
|
||||
}
|
||||
}
|
||||
|
||||
void ArchiveMover::run() {
|
||||
if (to_move_.empty() && to_check_.empty()) {
|
||||
completed();
|
||||
return;
|
||||
}
|
||||
if (!to_check_.empty()) {
|
||||
auto B = to_check_.back();
|
||||
CHECK(to_check_set_.count(B) == 1);
|
||||
|
||||
auto P = td::PromiseCreator::lambda([SelfId = actor_id(this)](td::Result<BlockHandle> R) {
|
||||
td::actor::send_closure(SelfId, &ArchiveMover::got_to_check_handle, std::move(R));
|
||||
});
|
||||
td::actor::send_closure(block_db_, &BlockDb::get_block_handle, B, std::move(P));
|
||||
return;
|
||||
}
|
||||
CHECK(!to_move_.empty());
|
||||
}
|
||||
|
||||
void ArchiveMover::got_to_check_handle(td::Result<BlockHandle> R) {
|
||||
if (R.is_error()) {
|
||||
CHECK(R.error().code() == ErrorCode::notready);
|
||||
run();
|
||||
return;
|
||||
}
|
||||
auto handle = R.move_as_ok();
|
||||
}
|
||||
|
||||
} // namespace validator
|
||||
|
||||
} // namespace ton
|
148
validator/db/archive-mover.hpp
Normal file
148
validator/db/archive-mover.hpp
Normal file
|
@ -0,0 +1,148 @@
|
|||
#pragma once
|
||||
|
||||
#include "td/actor/actor.h"
|
||||
#include "filedb.hpp"
|
||||
#include "blockdb.hpp"
|
||||
#include "statedb.hpp"
|
||||
#include "celldb.hpp"
|
||||
#include "archive-db.hpp"
|
||||
#include "archive-manager.hpp"
|
||||
|
||||
#include <list>
|
||||
#include <set>
|
||||
|
||||
namespace ton {
|
||||
|
||||
namespace validator {
|
||||
|
||||
class ArchiveFileMover : public td::actor::Actor {
|
||||
public:
|
||||
ArchiveFileMover(BlockIdExt block_id, td::actor::ActorId<BlockDb> block_db, td::actor::ActorId<FileDb> file_db,
|
||||
td::actor::ActorId<FileDb> old_archive_db, td::actor::ActorId<OldArchiveManager> old_archive_manager,
|
||||
td::actor::ActorId<ArchiveManager> archive_manager, td::Promise<td::Unit> promise)
|
||||
: block_id_(block_id)
|
||||
, block_db_(block_db)
|
||||
, file_db_(file_db)
|
||||
, old_archive_db_(old_archive_db)
|
||||
, old_archive_manager_(old_archive_manager)
|
||||
, archive_manager_(archive_manager)
|
||||
, promise_(std::move(promise)) {
|
||||
}
|
||||
void start_up() override;
|
||||
void got_block_handle0(td::Result<BlockHandle> R);
|
||||
void got_block_handle1(td::Result<BlockHandle> R);
|
||||
void got_block_handle2(td::Result<BlockHandle> R);
|
||||
void got_block_handle();
|
||||
|
||||
void processed_child();
|
||||
void processed_all_children();
|
||||
|
||||
void got_block_data(td::Result<td::BufferSlice> R);
|
||||
void got_block_proof(td::Result<td::BufferSlice> R);
|
||||
void got_block_proof_link(td::Result<td::BufferSlice> R);
|
||||
|
||||
void written_data();
|
||||
void written_handle();
|
||||
|
||||
void abort_query(td::Status error);
|
||||
void finish_query();
|
||||
|
||||
private:
|
||||
BlockIdExt block_id_;
|
||||
BlockHandle handle_;
|
||||
td::BufferSlice data_;
|
||||
td::BufferSlice proof_;
|
||||
td::BufferSlice proof_link_;
|
||||
bool left_ = true;
|
||||
|
||||
td::actor::ActorId<BlockDb> block_db_;
|
||||
td::actor::ActorId<FileDb> file_db_;
|
||||
td::actor::ActorId<FileDb> old_archive_db_;
|
||||
td::actor::ActorId<OldArchiveManager> old_archive_manager_;
|
||||
td::actor::ActorId<ArchiveManager> archive_manager_;
|
||||
|
||||
td::Promise<td::Unit> promise_;
|
||||
};
|
||||
|
||||
class ArchiveKeyBlockMover : public td::actor::Actor {
|
||||
public:
|
||||
ArchiveKeyBlockMover(BlockIdExt block_id, td::actor::ActorId<BlockDb> block_db, td::actor::ActorId<FileDb> file_db,
|
||||
td::actor::ActorId<FileDb> old_archive_db,
|
||||
td::actor::ActorId<OldArchiveManager> old_archive_manager,
|
||||
td::actor::ActorId<ArchiveManager> archive_manager, td::Promise<td::Unit> promise)
|
||||
: block_id_(block_id)
|
||||
, block_db_(block_db)
|
||||
, file_db_(file_db)
|
||||
, old_archive_db_(old_archive_db)
|
||||
, old_archive_manager_(old_archive_manager)
|
||||
, archive_manager_(archive_manager)
|
||||
, promise_(std::move(promise)) {
|
||||
}
|
||||
|
||||
void start_up() override;
|
||||
void failed_to_get_proof0();
|
||||
void failed_to_get_proof1();
|
||||
void failed_to_get_proof2();
|
||||
void failed_to_get_proof3();
|
||||
void got_block_proof(td::BufferSlice data);
|
||||
void skip_block_proof(td::BufferSlice data);
|
||||
|
||||
void written_data();
|
||||
|
||||
void abort_query(td::Status error);
|
||||
void finish_query();
|
||||
|
||||
private:
|
||||
BlockIdExt block_id_;
|
||||
td::BufferSlice data_;
|
||||
bool proof_link_ = false;
|
||||
|
||||
td::actor::ActorId<BlockDb> block_db_;
|
||||
td::actor::ActorId<FileDb> file_db_;
|
||||
td::actor::ActorId<FileDb> old_archive_db_;
|
||||
td::actor::ActorId<OldArchiveManager> old_archive_manager_;
|
||||
td::actor::ActorId<ArchiveManager> archive_manager_;
|
||||
|
||||
td::Promise<td::Unit> promise_;
|
||||
};
|
||||
|
||||
class ArchiveMover : public td::actor::Actor {
|
||||
public:
|
||||
ArchiveMover(std::string db_root, BlockIdExt masterchain_block_id, BlockIdExt shard_block_id,
|
||||
BlockIdExt key_block_id);
|
||||
|
||||
void start_up() override;
|
||||
void moved_blocks();
|
||||
void got_handle(BlockHandle handle);
|
||||
void got_state(td::Ref<MasterchainState> state);
|
||||
void moved_key_blocks();
|
||||
void run();
|
||||
void completed();
|
||||
void add_to_move(BlockIdExt block_id);
|
||||
void add_to_check(BlockIdExt block_id);
|
||||
|
||||
void got_to_check_handle(td::Result<BlockHandle> R);
|
||||
|
||||
void abort_query(td::Status error);
|
||||
void finish_query();
|
||||
|
||||
private:
|
||||
std::string db_root_;
|
||||
BlockHandle handle_;
|
||||
td::Ref<MasterchainState> state_;
|
||||
|
||||
td::actor::ActorOwn<BlockDb> block_db_;
|
||||
td::actor::ActorOwn<FileDb> file_db_;
|
||||
td::actor::ActorOwn<FileDb> old_archive_db_;
|
||||
td::actor::ActorOwn<OldArchiveManager> old_archive_manager_;
|
||||
td::actor::ActorOwn<ArchiveManager> archive_manager_;
|
||||
td::actor::ActorOwn<CellDb> cell_db_;
|
||||
|
||||
BlockIdExt masterchain_block_id_;
|
||||
BlockIdExt shard_block_id_;
|
||||
BlockIdExt key_block_id_;
|
||||
};
|
||||
|
||||
} // namespace validator
|
||||
|
||||
} // namespace ton
|
470
validator/db/archive-slice.cpp
Normal file
470
validator/db/archive-slice.cpp
Normal file
|
@ -0,0 +1,470 @@
|
|||
#include "archive-slice.hpp"
|
||||
|
||||
#include "td/actor/MultiPromise.h"
|
||||
#include "validator/fabric.h"
|
||||
#include "td/db/RocksDb.h"
|
||||
#include "ton/ton-io.hpp"
|
||||
#include "td/utils/port/path.h"
|
||||
#include "common/delay.h"
|
||||
#include "files-async.hpp"
|
||||
|
||||
namespace ton {
|
||||
|
||||
namespace validator {
|
||||
|
||||
void PackageWriter::append(std::string filename, td::BufferSlice data,
|
||||
td::Promise<std::pair<td::uint64, td::uint64>> promise) {
|
||||
auto offset = package_->append(std::move(filename), std::move(data), !async_mode_);
|
||||
auto size = package_->size();
|
||||
|
||||
promise.set_value(std::pair<td::uint64, td::uint64>{offset, size});
|
||||
}
|
||||
|
||||
class PackageReader : public td::actor::Actor {
|
||||
public:
|
||||
PackageReader(std::shared_ptr<Package> package, td::uint64 offset,
|
||||
td::Promise<std::pair<std::string, td::BufferSlice>> promise)
|
||||
: package_(std::move(package)), offset_(offset), promise_(std::move(promise)) {
|
||||
}
|
||||
void start_up() {
|
||||
promise_.set_result(package_->read(offset_));
|
||||
}
|
||||
|
||||
private:
|
||||
std::shared_ptr<Package> package_;
|
||||
td::uint64 offset_;
|
||||
td::Promise<std::pair<std::string, td::BufferSlice>> promise_;
|
||||
};
|
||||
|
||||
void ArchiveSlice::add_handle(BlockHandle handle, td::Promise<td::Unit> promise) {
|
||||
if (destroyed_) {
|
||||
promise.set_error(td::Status::Error(ErrorCode::notready, "package already gc'd"));
|
||||
return;
|
||||
}
|
||||
if (handle->id().seqno() == 0) {
|
||||
update_handle(std::move(handle), std::move(promise));
|
||||
return;
|
||||
}
|
||||
CHECK(!key_blocks_only_);
|
||||
CHECK(!temp_);
|
||||
CHECK(handle->inited_unix_time());
|
||||
CHECK(handle->inited_logical_time());
|
||||
|
||||
auto key = get_db_key_lt_desc(handle->id().shard_full());
|
||||
|
||||
std::string value;
|
||||
auto R = kv_->get(key.as_slice(), value);
|
||||
R.ensure();
|
||||
tl_object_ptr<ton_api::db_lt_desc_value> v;
|
||||
bool add_shard = false;
|
||||
if (R.move_as_ok() == td::KeyValue::GetStatus::Ok) {
|
||||
auto F = fetch_tl_object<ton_api::db_lt_desc_value>(td::BufferSlice{value}, true);
|
||||
F.ensure();
|
||||
v = F.move_as_ok();
|
||||
} else {
|
||||
v = create_tl_object<ton_api::db_lt_desc_value>(1, 1, 0, 0, 0);
|
||||
add_shard = true;
|
||||
}
|
||||
if (handle->id().seqno() <= static_cast<td::uint32>(v->last_seqno_) ||
|
||||
handle->logical_time() <= static_cast<LogicalTime>(v->last_lt_) ||
|
||||
handle->unix_time() <= static_cast<UnixTime>(v->last_ts_)) {
|
||||
update_handle(std::move(handle), std::move(promise));
|
||||
return;
|
||||
}
|
||||
auto db_value = create_serialize_tl_object<ton_api::db_lt_el_value>(create_tl_block_id(handle->id()),
|
||||
handle->logical_time(), handle->unix_time());
|
||||
auto db_key = get_db_key_lt_el(handle->id().shard_full(), v->last_idx_++);
|
||||
auto status_key = create_serialize_tl_object<ton_api::db_lt_status_key>();
|
||||
v->last_seqno_ = handle->id().seqno();
|
||||
v->last_lt_ = handle->logical_time();
|
||||
v->last_ts_ = handle->unix_time();
|
||||
|
||||
td::uint32 idx = 0;
|
||||
if (add_shard) {
|
||||
auto G = kv_->get(status_key.as_slice(), value);
|
||||
G.ensure();
|
||||
if (G.move_as_ok() == td::KeyValue::GetStatus::NotFound) {
|
||||
idx = 0;
|
||||
} else {
|
||||
auto F = fetch_tl_object<ton_api::db_lt_status_value>(value, true);
|
||||
F.ensure();
|
||||
auto f = F.move_as_ok();
|
||||
idx = f->total_shards_;
|
||||
}
|
||||
}
|
||||
|
||||
auto version = handle->version();
|
||||
|
||||
begin_transaction();
|
||||
kv_->set(key, serialize_tl_object(v, true)).ensure();
|
||||
kv_->set(db_key, db_value.as_slice()).ensure();
|
||||
if (add_shard) {
|
||||
auto shard_key = create_serialize_tl_object<ton_api::db_lt_shard_key>(idx);
|
||||
auto shard_value =
|
||||
create_serialize_tl_object<ton_api::db_lt_shard_value>(handle->id().id.workchain, handle->id().id.shard);
|
||||
kv_->set(status_key.as_slice(), create_serialize_tl_object<ton_api::db_lt_status_value>(idx + 1)).ensure();
|
||||
kv_->set(shard_key.as_slice(), shard_value.as_slice()).ensure();
|
||||
}
|
||||
kv_->set(get_db_key_block_info(handle->id()), handle->serialize().as_slice()).ensure();
|
||||
commit_transaction();
|
||||
|
||||
handle->flushed_upto(version);
|
||||
handle->set_handle_moved_to_archive();
|
||||
|
||||
if (handle->need_flush()) {
|
||||
update_handle(std::move(handle), std::move(promise));
|
||||
} else {
|
||||
promise.set_value(td::Unit());
|
||||
}
|
||||
}
|
||||
|
||||
void ArchiveSlice::update_handle(BlockHandle handle, td::Promise<td::Unit> promise) {
|
||||
if (destroyed_) {
|
||||
promise.set_error(td::Status::Error(ErrorCode::notready, "package already gc'd"));
|
||||
return;
|
||||
}
|
||||
if (!handle->need_flush() && (temp_ || handle->handle_moved_to_archive())) {
|
||||
promise.set_value(td::Unit());
|
||||
return;
|
||||
}
|
||||
CHECK(!key_blocks_only_);
|
||||
|
||||
begin_transaction();
|
||||
do {
|
||||
auto version = handle->version();
|
||||
kv_->set(get_db_key_block_info(handle->id()), handle->serialize().as_slice()).ensure();
|
||||
handle->flushed_upto(version);
|
||||
} while (handle->need_flush());
|
||||
commit_transaction();
|
||||
if (!temp_) {
|
||||
handle->set_handle_moved_to_archive();
|
||||
}
|
||||
|
||||
promise.set_value(td::Unit());
|
||||
}
|
||||
|
||||
void ArchiveSlice::add_file(FileReference ref_id, td::BufferSlice data, td::Promise<td::Unit> promise) {
|
||||
if (destroyed_) {
|
||||
promise.set_error(td::Status::Error(ErrorCode::notready, "package already gc'd"));
|
||||
return;
|
||||
}
|
||||
std::string value;
|
||||
auto R = kv_->get(ref_id.hash().to_hex(), value);
|
||||
R.ensure();
|
||||
if (R.move_as_ok() == td::KeyValue::GetStatus::Ok) {
|
||||
promise.set_value(td::Unit());
|
||||
return;
|
||||
}
|
||||
auto P = td::PromiseCreator::lambda([SelfId = actor_id(this), ref_id, promise = std::move(promise)](
|
||||
td::Result<std::pair<td::uint64, td::uint64>> R) mutable {
|
||||
if (R.is_error()) {
|
||||
promise.set_error(R.move_as_error());
|
||||
return;
|
||||
}
|
||||
auto v = R.move_as_ok();
|
||||
td::actor::send_closure(SelfId, &ArchiveSlice::add_file_cont, std::move(ref_id), v.first, v.second,
|
||||
std::move(promise));
|
||||
});
|
||||
|
||||
td::actor::send_closure(writer_, &PackageWriter::append, ref_id.filename(), std::move(data), std::move(P));
|
||||
}
|
||||
|
||||
void ArchiveSlice::add_file_cont(FileReference ref_id, td::uint64 offset, td::uint64 size,
|
||||
td::Promise<td::Unit> promise) {
|
||||
if (destroyed_) {
|
||||
promise.set_error(td::Status::Error(ErrorCode::notready, "package already gc'd"));
|
||||
return;
|
||||
}
|
||||
begin_transaction();
|
||||
kv_->set("status", td::to_string(size)).ensure();
|
||||
kv_->set(ref_id.hash().to_hex(), td::to_string(offset)).ensure();
|
||||
commit_transaction();
|
||||
promise.set_value(td::Unit());
|
||||
}
|
||||
|
||||
void ArchiveSlice::get_handle(BlockIdExt block_id, td::Promise<BlockHandle> promise) {
|
||||
if (destroyed_) {
|
||||
promise.set_error(td::Status::Error(ErrorCode::notready, "package already gc'd"));
|
||||
return;
|
||||
}
|
||||
CHECK(!key_blocks_only_);
|
||||
std::string value;
|
||||
auto R = kv_->get(get_db_key_block_info(block_id), value);
|
||||
R.ensure();
|
||||
if (R.move_as_ok() == td::KeyValue::GetStatus::NotFound) {
|
||||
promise.set_error(td::Status::Error(ErrorCode::notready, "handle not in archive slice"));
|
||||
return;
|
||||
}
|
||||
auto E = create_block_handle(td::BufferSlice{value});
|
||||
E.ensure();
|
||||
auto handle = E.move_as_ok();
|
||||
if (!temp_) {
|
||||
handle->set_handle_moved_to_archive();
|
||||
}
|
||||
promise.set_value(std::move(handle));
|
||||
}
|
||||
|
||||
void ArchiveSlice::get_file(FileReference ref_id, td::Promise<td::BufferSlice> promise) {
|
||||
if (destroyed_) {
|
||||
promise.set_error(td::Status::Error(ErrorCode::notready, "package already gc'd"));
|
||||
return;
|
||||
}
|
||||
std::string value;
|
||||
auto R = kv_->get(ref_id.hash().to_hex(), value);
|
||||
R.ensure();
|
||||
if (R.move_as_ok() == td::KeyValue::GetStatus::NotFound) {
|
||||
promise.set_error(td::Status::Error(ErrorCode::notready, "file not in archive slice"));
|
||||
return;
|
||||
}
|
||||
auto offset = td::to_integer<td::uint64>(value);
|
||||
auto P = td::PromiseCreator::lambda(
|
||||
[promise = std::move(promise)](td::Result<std::pair<std::string, td::BufferSlice>> R) mutable {
|
||||
if (R.is_error()) {
|
||||
promise.set_error(R.move_as_error());
|
||||
} else {
|
||||
promise.set_value(std::move(R.move_as_ok().second));
|
||||
}
|
||||
});
|
||||
td::actor::create_actor<PackageReader>("reader", package_, offset, std::move(P)).release();
|
||||
}
|
||||
|
||||
void ArchiveSlice::get_block_common(AccountIdPrefixFull account_id,
|
||||
std::function<td::int32(ton_api::db_lt_desc_value &)> compare_desc,
|
||||
std::function<td::int32(ton_api::db_lt_el_value &)> compare, bool exact,
|
||||
td::Promise<BlockHandle> promise) {
|
||||
if (destroyed_) {
|
||||
promise.set_error(td::Status::Error(ErrorCode::notready, "package already gc'd"));
|
||||
return;
|
||||
}
|
||||
bool f = false;
|
||||
BlockIdExt block_id;
|
||||
td::uint32 ls = 0;
|
||||
for (td::uint32 len = 0; len <= 60; len++) {
|
||||
auto s = shard_prefix(account_id, len);
|
||||
auto key = get_db_key_lt_desc(s);
|
||||
std::string value;
|
||||
auto F = kv_->get(key, value);
|
||||
F.ensure();
|
||||
if (F.move_as_ok() == td::KeyValue::GetStatus::NotFound) {
|
||||
if (!f) {
|
||||
continue;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
f = true;
|
||||
auto G = fetch_tl_object<ton_api::db_lt_desc_value>(td::BufferSlice{value}, true);
|
||||
G.ensure();
|
||||
auto g = G.move_as_ok();
|
||||
if (compare_desc(*g.get()) > 0) {
|
||||
continue;
|
||||
}
|
||||
td::uint32 l = g->first_idx_ - 1;
|
||||
BlockIdExt lseq;
|
||||
td::uint32 r = g->last_idx_;
|
||||
BlockIdExt rseq;
|
||||
while (r - l > 1) {
|
||||
auto x = (r + l) / 2;
|
||||
auto db_key = get_db_key_lt_el(s, x);
|
||||
F = kv_->get(db_key, value);
|
||||
F.ensure();
|
||||
CHECK(F.move_as_ok() == td::KeyValue::GetStatus::Ok);
|
||||
auto E = fetch_tl_object<ton_api::db_lt_el_value>(td::BufferSlice{value}, true);
|
||||
E.ensure();
|
||||
auto e = E.move_as_ok();
|
||||
int cmp_val = compare(*e.get());
|
||||
|
||||
if (cmp_val < 0) {
|
||||
rseq = create_block_id(e->id_);
|
||||
r = x;
|
||||
} else if (cmp_val > 0) {
|
||||
lseq = create_block_id(e->id_);
|
||||
l = x;
|
||||
} else {
|
||||
get_handle(create_block_id(e->id_), std::move(promise));
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (rseq.is_valid()) {
|
||||
if (!block_id.is_valid()) {
|
||||
block_id = rseq;
|
||||
} else if (block_id.id.seqno > rseq.id.seqno) {
|
||||
block_id = rseq;
|
||||
}
|
||||
}
|
||||
if (lseq.is_valid()) {
|
||||
if (ls < lseq.id.seqno) {
|
||||
ls = lseq.id.seqno;
|
||||
}
|
||||
}
|
||||
if (block_id.is_valid() && ls + 1 == block_id.id.seqno) {
|
||||
if (!exact) {
|
||||
get_handle(block_id, std::move(promise));
|
||||
} else {
|
||||
promise.set_error(td::Status::Error(ErrorCode::notready, "ltdb: block not found"));
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (!exact && block_id.is_valid()) {
|
||||
get_handle(block_id, std::move(promise));
|
||||
} else {
|
||||
promise.set_error(td::Status::Error(ErrorCode::notready, "ltdb: block not found"));
|
||||
}
|
||||
}
|
||||
|
||||
void ArchiveSlice::get_block_by_lt(AccountIdPrefixFull account_id, LogicalTime lt, td::Promise<BlockHandle> promise) {
|
||||
return get_block_common(
|
||||
account_id,
|
||||
[lt](ton_api::db_lt_desc_value &w) {
|
||||
return lt > static_cast<LogicalTime>(w.last_lt_) ? 1 : lt == static_cast<LogicalTime>(w.last_lt_) ? 0 : -1;
|
||||
},
|
||||
[lt](ton_api::db_lt_el_value &w) {
|
||||
return lt > static_cast<LogicalTime>(w.lt_) ? 1 : lt == static_cast<LogicalTime>(w.lt_) ? 0 : -1;
|
||||
},
|
||||
false, std::move(promise));
|
||||
}
|
||||
|
||||
void ArchiveSlice::get_block_by_seqno(AccountIdPrefixFull account_id, BlockSeqno seqno,
|
||||
td::Promise<BlockHandle> promise) {
|
||||
return get_block_common(
|
||||
account_id,
|
||||
[seqno](ton_api::db_lt_desc_value &w) {
|
||||
return seqno > static_cast<BlockSeqno>(w.last_seqno_)
|
||||
? 1
|
||||
: seqno == static_cast<BlockSeqno>(w.last_seqno_) ? 0 : -1;
|
||||
},
|
||||
[seqno](ton_api::db_lt_el_value &w) {
|
||||
return seqno > static_cast<BlockSeqno>(w.id_->seqno_)
|
||||
? 1
|
||||
: seqno == static_cast<BlockSeqno>(w.id_->seqno_) ? 0 : -1;
|
||||
},
|
||||
true, std::move(promise));
|
||||
}
|
||||
|
||||
void ArchiveSlice::get_block_by_unix_time(AccountIdPrefixFull account_id, UnixTime ts,
|
||||
td::Promise<BlockHandle> promise) {
|
||||
return get_block_common(
|
||||
account_id,
|
||||
[ts](ton_api::db_lt_desc_value &w) {
|
||||
return ts > static_cast<UnixTime>(w.last_ts_) ? 1 : ts == static_cast<UnixTime>(w.last_ts_) ? 0 : -1;
|
||||
},
|
||||
[ts](ton_api::db_lt_el_value &w) {
|
||||
return ts > static_cast<UnixTime>(w.ts_) ? 1 : ts == static_cast<UnixTime>(w.ts_) ? 0 : -1;
|
||||
},
|
||||
false, std::move(promise));
|
||||
}
|
||||
|
||||
td::BufferSlice ArchiveSlice::get_db_key_lt_desc(ShardIdFull shard) {
|
||||
return create_serialize_tl_object<ton_api::db_lt_desc_key>(shard.workchain, shard.shard);
|
||||
}
|
||||
|
||||
td::BufferSlice ArchiveSlice::get_db_key_lt_el(ShardIdFull shard, td::uint32 idx) {
|
||||
return create_serialize_tl_object<ton_api::db_lt_el_key>(shard.workchain, shard.shard, idx);
|
||||
}
|
||||
|
||||
td::BufferSlice ArchiveSlice::get_db_key_block_info(BlockIdExt block_id) {
|
||||
return create_serialize_tl_object<ton_api::db_blockdb_key_value>(create_tl_block_id(block_id));
|
||||
}
|
||||
|
||||
void ArchiveSlice::get_slice(td::uint64 offset, td::uint32 limit, td::Promise<td::BufferSlice> promise) {
|
||||
td::actor::create_actor<db::ReadFile>("readfile", prefix_ + ".pack", offset, limit, 0, std::move(promise)).release();
|
||||
}
|
||||
|
||||
void ArchiveSlice::start_up() {
|
||||
auto R = Package::open(prefix_ + ".pack", false, true);
|
||||
if (R.is_error()) {
|
||||
LOG(FATAL) << "failed to open/create archive '" << prefix_ << ".pack"
|
||||
<< "': " << R.move_as_error();
|
||||
return;
|
||||
}
|
||||
package_ = std::make_shared<Package>(R.move_as_ok());
|
||||
kv_ = std::make_shared<td::RocksDb>(td::RocksDb::open(prefix_ + ".index").move_as_ok());
|
||||
|
||||
std::string value;
|
||||
auto R2 = kv_->get("status", value);
|
||||
R2.ensure();
|
||||
|
||||
if (R2.move_as_ok() == td::KeyValue::GetStatus::Ok) {
|
||||
auto len = td::to_integer<td::uint64>(value);
|
||||
package_->truncate(len);
|
||||
} else {
|
||||
package_->truncate(0);
|
||||
}
|
||||
|
||||
writer_ = td::actor::create_actor<PackageWriter>("writer", package_);
|
||||
}
|
||||
|
||||
void ArchiveSlice::begin_transaction() {
|
||||
if (!async_mode_ || !huge_transaction_started_) {
|
||||
kv_->begin_transaction().ensure();
|
||||
if (async_mode_) {
|
||||
huge_transaction_started_ = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ArchiveSlice::commit_transaction() {
|
||||
if (!async_mode_ || huge_transaction_size_++ >= 100) {
|
||||
kv_->commit_transaction().ensure();
|
||||
if (async_mode_) {
|
||||
huge_transaction_size_ = 0;
|
||||
huge_transaction_started_ = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ArchiveSlice::set_async_mode(bool mode, td::Promise<td::Unit> promise) {
|
||||
async_mode_ = mode;
|
||||
if (!async_mode_ && huge_transaction_started_) {
|
||||
kv_->commit_transaction().ensure();
|
||||
huge_transaction_size_ = 0;
|
||||
huge_transaction_started_ = false;
|
||||
}
|
||||
|
||||
td::actor::send_closure(writer_, &PackageWriter::set_async_mode, mode, std::move(promise));
|
||||
}
|
||||
|
||||
ArchiveSlice::ArchiveSlice(bool key_blocks_only, bool temp, std::string prefix)
|
||||
: key_blocks_only_(key_blocks_only), temp_(temp), prefix_(std::move(prefix)) {
|
||||
}
|
||||
|
||||
namespace {
|
||||
|
||||
void destroy_db(std::string name, td::uint32 attempt, td::Promise<td::Unit> promise) {
|
||||
auto S = td::RocksDb::destroy(name);
|
||||
if (S.is_ok()) {
|
||||
promise.set_value(td::Unit());
|
||||
return;
|
||||
}
|
||||
if (S.is_error() && attempt > 0 && attempt % 64 == 0) {
|
||||
LOG(ERROR) << "failed to destroy index " << name << ": " << S;
|
||||
} else {
|
||||
LOG(DEBUG) << "failed to destroy index " << name << ": " << S;
|
||||
}
|
||||
delay_action(
|
||||
[name, attempt, promise = std::move(promise)]() mutable { destroy_db(name, attempt, std::move(promise)); },
|
||||
td::Timestamp::in(1.0));
|
||||
}
|
||||
} // namespace
|
||||
|
||||
void ArchiveSlice::destroy(td::Promise<td::Unit> promise) {
|
||||
td::MultiPromise mp;
|
||||
auto ig = mp.init_guard();
|
||||
ig.add_promise(std::move(promise));
|
||||
destroyed_ = true;
|
||||
|
||||
writer_.reset();
|
||||
package_ = nullptr;
|
||||
kv_ = nullptr;
|
||||
|
||||
td::unlink(prefix_ + ".pack").ensure();
|
||||
|
||||
delay_action([name = prefix_ + ".index", attempt = 0,
|
||||
promise = ig.get_promise()]() mutable { destroy_db(name, attempt, std::move(promise)); },
|
||||
td::Timestamp::in(0.0));
|
||||
}
|
||||
|
||||
} // namespace validator
|
||||
|
||||
} // namespace ton
|
84
validator/db/archive-slice.hpp
Normal file
84
validator/db/archive-slice.hpp
Normal file
|
@ -0,0 +1,84 @@
|
|||
#pragma once
|
||||
|
||||
#include "validator/interfaces/db.h"
|
||||
#include "package.hpp"
|
||||
#include "fileref.hpp"
|
||||
|
||||
namespace ton {
|
||||
|
||||
namespace validator {
|
||||
|
||||
class PackageWriter : public td::actor::Actor {
|
||||
public:
|
||||
PackageWriter(std::shared_ptr<Package> package) : package_(std::move(package)) {
|
||||
}
|
||||
|
||||
void append(std::string filename, td::BufferSlice data, td::Promise<std::pair<td::uint64, td::uint64>> promise);
|
||||
void set_async_mode(bool mode, td::Promise<td::Unit> promise) {
|
||||
async_mode_ = mode;
|
||||
if (!async_mode_) {
|
||||
package_->sync();
|
||||
}
|
||||
promise.set_value(td::Unit());
|
||||
}
|
||||
|
||||
private:
|
||||
std::shared_ptr<Package> package_;
|
||||
bool async_mode_ = false;
|
||||
};
|
||||
|
||||
class ArchiveSlice : public td::actor::Actor {
|
||||
public:
|
||||
ArchiveSlice(bool key_blocks_only, bool temp, std::string prefix);
|
||||
|
||||
void add_handle(BlockHandle handle, td::Promise<td::Unit> promise);
|
||||
void update_handle(BlockHandle handle, td::Promise<td::Unit> promise);
|
||||
void add_file(FileReference ref_id, td::BufferSlice data, td::Promise<td::Unit> promise);
|
||||
void get_handle(BlockIdExt block_id, td::Promise<BlockHandle> promise);
|
||||
void get_file(FileReference ref_id, td::Promise<td::BufferSlice> promise);
|
||||
|
||||
/* from LTDB */
|
||||
void get_block_by_unix_time(AccountIdPrefixFull account_id, UnixTime ts, td::Promise<BlockHandle> promise);
|
||||
void get_block_by_lt(AccountIdPrefixFull account_id, LogicalTime lt, td::Promise<BlockHandle> promise);
|
||||
void get_block_by_seqno(AccountIdPrefixFull account_id, BlockSeqno seqno, td::Promise<BlockHandle> promise);
|
||||
void get_block_common(AccountIdPrefixFull account_id,
|
||||
std::function<td::int32(ton_api::db_lt_desc_value &)> compare_desc,
|
||||
std::function<td::int32(ton_api::db_lt_el_value &)> compare, bool exact,
|
||||
td::Promise<BlockHandle> promise);
|
||||
|
||||
void get_slice(td::uint64 offset, td::uint32 limit, td::Promise<td::BufferSlice> promise);
|
||||
|
||||
void start_up() override;
|
||||
void destroy(td::Promise<td::Unit> promise);
|
||||
|
||||
void begin_transaction();
|
||||
void commit_transaction();
|
||||
void set_async_mode(bool mode, td::Promise<td::Unit> promise);
|
||||
|
||||
private:
|
||||
void written_data(BlockHandle handle, td::Promise<td::Unit> promise);
|
||||
void add_file_cont(FileReference ref_id, td::uint64 offset, td::uint64 size, td::Promise<td::Unit> promise);
|
||||
|
||||
/* ltdb */
|
||||
td::BufferSlice get_db_key_lt_desc(ShardIdFull shard);
|
||||
td::BufferSlice get_db_key_lt_el(ShardIdFull shard, td::uint32 idx);
|
||||
td::BufferSlice get_db_key_block_info(BlockIdExt block_id);
|
||||
td::BufferSlice get_lt_from_db(ShardIdFull shard, td::uint32 idx);
|
||||
|
||||
bool key_blocks_only_;
|
||||
bool temp_;
|
||||
|
||||
bool destroyed_ = false;
|
||||
bool async_mode_ = false;
|
||||
bool huge_transaction_started_ = false;
|
||||
td::uint32 huge_transaction_size_ = 0;
|
||||
|
||||
std::string prefix_;
|
||||
std::shared_ptr<Package> package_;
|
||||
std::shared_ptr<td::KeyValue> kv_;
|
||||
td::actor::ActorOwn<PackageWriter> writer_;
|
||||
};
|
||||
|
||||
} // namespace validator
|
||||
|
||||
} // namespace ton
|
|
@ -24,41 +24,30 @@ namespace ton {
|
|||
|
||||
namespace validator {
|
||||
|
||||
BlockArchiver::BlockArchiver(BlockIdExt block_id, td::actor::ActorId<RootDb> root_db,
|
||||
td::actor::ActorId<FileDb> file_db, td::actor::ActorId<FileDb> archive_db,
|
||||
td::actor::ActorId<ArchiveManager> archive, td::Promise<td::Unit> promise)
|
||||
: block_id_(block_id)
|
||||
, root_db_(root_db)
|
||||
, file_db_(file_db)
|
||||
, archive_db_(archive_db)
|
||||
, archive_(archive)
|
||||
, promise_(std::move(promise)) {
|
||||
BlockArchiver::BlockArchiver(BlockHandle handle, td::actor::ActorId<ArchiveManager> archive_db,
|
||||
td::Promise<td::Unit> promise)
|
||||
: handle_(std::move(handle)), archive_(archive_db), promise_(std::move(promise)) {
|
||||
}
|
||||
|
||||
void BlockArchiver::start_up() {
|
||||
auto P = td::PromiseCreator::lambda([SelfId = actor_id(this)](td::Result<BlockHandle> R) {
|
||||
R.ensure();
|
||||
td::actor::send_closure(SelfId, &BlockArchiver::got_block_handle, R.move_as_ok());
|
||||
});
|
||||
td::actor::send_closure(root_db_, &RootDb::get_block_handle_external, block_id_, false, std::move(P));
|
||||
if (handle_->handle_moved_to_archive()) {
|
||||
moved_handle();
|
||||
} else {
|
||||
auto P = td::PromiseCreator::lambda([SelfId = actor_id(this)](td::Result<td::Unit> R) {
|
||||
R.ensure();
|
||||
td::actor::send_closure(SelfId, &BlockArchiver::moved_handle);
|
||||
});
|
||||
td::actor::send_closure(archive_, &ArchiveManager::add_handle, handle_, std::move(P));
|
||||
}
|
||||
}
|
||||
|
||||
void BlockArchiver::got_block_handle(BlockHandle handle) {
|
||||
handle_ = std::move(handle);
|
||||
void BlockArchiver::moved_handle() {
|
||||
CHECK(handle_->handle_moved_to_archive());
|
||||
if (handle_->moved_to_archive()) {
|
||||
finish_query();
|
||||
return;
|
||||
}
|
||||
|
||||
if (!handle_->is_applied() && !handle_->is_archived() &&
|
||||
(!handle_->inited_is_key_block() || !handle_->is_key_block())) {
|
||||
// no need for this block
|
||||
// probably this block not in final chain
|
||||
// this will eventually delete all associated data
|
||||
written_block_data();
|
||||
return;
|
||||
}
|
||||
|
||||
if (!handle_->inited_proof()) {
|
||||
written_proof();
|
||||
return;
|
||||
|
@ -69,11 +58,7 @@ void BlockArchiver::got_block_handle(BlockHandle handle) {
|
|||
td::actor::send_closure(SelfId, &BlockArchiver::got_proof, R.move_as_ok());
|
||||
});
|
||||
|
||||
if (handle_->moved_to_storage()) {
|
||||
td::actor::send_closure(archive_db_, &FileDb::load_file, FileDb::RefId{fileref::Proof{block_id_}}, std::move(P));
|
||||
} else {
|
||||
td::actor::send_closure(file_db_, &FileDb::load_file, FileDb::RefId{fileref::Proof{block_id_}}, std::move(P));
|
||||
}
|
||||
td::actor::send_closure(archive_, &ArchiveManager::get_temp_file_short, fileref::Proof{handle_->id()}, std::move(P));
|
||||
}
|
||||
|
||||
void BlockArchiver::got_proof(td::BufferSlice data) {
|
||||
|
@ -81,8 +66,8 @@ void BlockArchiver::got_proof(td::BufferSlice data) {
|
|||
R.ensure();
|
||||
td::actor::send_closure(SelfId, &BlockArchiver::written_proof);
|
||||
});
|
||||
td::actor::send_closure(archive_, &ArchiveManager::write, handle_->unix_time(), handle_->is_key_block(),
|
||||
FileDb::RefId{fileref::Proof{block_id_}}, std::move(data), std::move(P));
|
||||
td::actor::send_closure(archive_, &ArchiveManager::add_file, handle_, fileref::Proof{handle_->id()}, std::move(data),
|
||||
std::move(P));
|
||||
}
|
||||
|
||||
void BlockArchiver::written_proof() {
|
||||
|
@ -95,12 +80,9 @@ void BlockArchiver::written_proof() {
|
|||
R.ensure();
|
||||
td::actor::send_closure(SelfId, &BlockArchiver::got_proof_link, R.move_as_ok());
|
||||
});
|
||||
if (handle_->moved_to_storage()) {
|
||||
td::actor::send_closure(archive_db_, &FileDb::load_file, FileDb::RefId{fileref::ProofLink{block_id_}},
|
||||
std::move(P));
|
||||
} else {
|
||||
td::actor::send_closure(file_db_, &FileDb::load_file, FileDb::RefId{fileref::ProofLink{block_id_}}, std::move(P));
|
||||
}
|
||||
|
||||
td::actor::send_closure(archive_, &ArchiveManager::get_temp_file_short, fileref::ProofLink{handle_->id()},
|
||||
std::move(P));
|
||||
}
|
||||
|
||||
void BlockArchiver::got_proof_link(td::BufferSlice data) {
|
||||
|
@ -108,8 +90,8 @@ void BlockArchiver::got_proof_link(td::BufferSlice data) {
|
|||
R.ensure();
|
||||
td::actor::send_closure(SelfId, &BlockArchiver::written_proof_link);
|
||||
});
|
||||
td::actor::send_closure(archive_, &ArchiveManager::write, handle_->unix_time(), handle_->is_key_block(),
|
||||
FileDb::RefId{fileref::ProofLink{block_id_}}, std::move(data), std::move(P));
|
||||
td::actor::send_closure(archive_, &ArchiveManager::add_file, handle_, fileref::ProofLink{handle_->id()},
|
||||
std::move(data), std::move(P));
|
||||
}
|
||||
|
||||
void BlockArchiver::written_proof_link() {
|
||||
|
@ -121,11 +103,8 @@ void BlockArchiver::written_proof_link() {
|
|||
R.ensure();
|
||||
td::actor::send_closure(SelfId, &BlockArchiver::got_block_data, R.move_as_ok());
|
||||
});
|
||||
if (handle_->moved_to_storage()) {
|
||||
td::actor::send_closure(archive_db_, &FileDb::load_file, FileDb::RefId{fileref::Block{block_id_}}, std::move(P));
|
||||
} else {
|
||||
td::actor::send_closure(file_db_, &FileDb::load_file, FileDb::RefId{fileref::Block{block_id_}}, std::move(P));
|
||||
}
|
||||
|
||||
td::actor::send_closure(archive_, &ArchiveManager::get_temp_file_short, fileref::Block{handle_->id()}, std::move(P));
|
||||
}
|
||||
|
||||
void BlockArchiver::got_block_data(td::BufferSlice data) {
|
||||
|
@ -133,8 +112,8 @@ void BlockArchiver::got_block_data(td::BufferSlice data) {
|
|||
R.ensure();
|
||||
td::actor::send_closure(SelfId, &BlockArchiver::written_block_data);
|
||||
});
|
||||
td::actor::send_closure(archive_, &ArchiveManager::write, handle_->unix_time(), handle_->is_key_block(),
|
||||
FileDb::RefId{fileref::Block{block_id_}}, std::move(data), std::move(P));
|
||||
td::actor::send_closure(archive_, &ArchiveManager::add_file, handle_, fileref::Block{handle_->id()}, std::move(data),
|
||||
std::move(P));
|
||||
}
|
||||
|
||||
void BlockArchiver::written_block_data() {
|
||||
|
@ -144,7 +123,7 @@ void BlockArchiver::written_block_data() {
|
|||
R.ensure();
|
||||
td::actor::send_closure(SelfId, &BlockArchiver::finish_query);
|
||||
});
|
||||
td::actor::send_closure(root_db_, &RootDb::store_block_handle, handle_, std::move(P));
|
||||
td::actor::send_closure(archive_, &ArchiveManager::update_handle, handle_, std::move(P));
|
||||
}
|
||||
|
||||
void BlockArchiver::finish_query() {
|
||||
|
@ -156,7 +135,7 @@ void BlockArchiver::finish_query() {
|
|||
|
||||
void BlockArchiver::abort_query(td::Status reason) {
|
||||
if (promise_) {
|
||||
VLOG(VALIDATOR_WARNING) << "failed to archive block " << block_id_ << ": " << reason;
|
||||
VLOG(VALIDATOR_WARNING) << "failed to archive block " << handle_->id() << ": " << reason;
|
||||
promise_.set_error(std::move(reason));
|
||||
}
|
||||
stop();
|
||||
|
|
|
@ -22,7 +22,7 @@
|
|||
#include "td/actor/actor.h"
|
||||
#include "validator/interfaces/block-handle.h"
|
||||
#include "ton/ton-io.hpp"
|
||||
#include "archive-db.hpp"
|
||||
#include "archive-manager.hpp"
|
||||
|
||||
namespace ton {
|
||||
|
||||
|
@ -33,14 +33,12 @@ class FileDb;
|
|||
|
||||
class BlockArchiver : public td::actor::Actor {
|
||||
public:
|
||||
BlockArchiver(BlockIdExt block_id, td::actor::ActorId<RootDb> root_db, td::actor::ActorId<FileDb> file_db,
|
||||
td::actor::ActorId<FileDb> archive_db, td::actor::ActorId<ArchiveManager> archive,
|
||||
td::Promise<td::Unit> promise);
|
||||
BlockArchiver(BlockHandle handle, td::actor::ActorId<ArchiveManager> archive_db, td::Promise<td::Unit> promise);
|
||||
|
||||
void abort_query(td::Status error);
|
||||
|
||||
void start_up() override;
|
||||
void got_block_handle(BlockHandle handle);
|
||||
void moved_handle();
|
||||
void got_proof(td::BufferSlice data);
|
||||
void written_proof();
|
||||
void got_proof_link(td::BufferSlice data);
|
||||
|
@ -50,14 +48,9 @@ class BlockArchiver : public td::actor::Actor {
|
|||
void finish_query();
|
||||
|
||||
private:
|
||||
BlockIdExt block_id_;
|
||||
td::actor::ActorId<RootDb> root_db_;
|
||||
td::actor::ActorId<FileDb> file_db_;
|
||||
td::actor::ActorId<FileDb> archive_db_;
|
||||
BlockHandle handle_;
|
||||
td::actor::ActorId<ArchiveManager> archive_;
|
||||
td::Promise<td::Unit> promise_;
|
||||
|
||||
BlockHandle handle_;
|
||||
};
|
||||
|
||||
} // namespace validator
|
||||
|
|
|
@ -1,301 +0,0 @@
|
|||
/*
|
||||
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 <http://www.gnu.org/licenses/>.
|
||||
|
||||
Copyright 2017-2019 Telegram Systems LLP
|
||||
*/
|
||||
#include "blockdb.hpp"
|
||||
#include "rootdb.hpp"
|
||||
#include "validator/fabric.h"
|
||||
#include "ton/ton-tl.hpp"
|
||||
#include "td/utils/port/path.h"
|
||||
#include "files-async.hpp"
|
||||
#include "td/db/RocksDb.h"
|
||||
|
||||
namespace ton {
|
||||
|
||||
namespace validator {
|
||||
|
||||
void BlockDb::store_block_handle(BlockHandle handle, td::Promise<td::Unit> promise) {
|
||||
if (!handle->id().is_valid()) {
|
||||
promise.set_error(td::Status::Error(ErrorCode::protoviolation, "invalid block id"));
|
||||
return;
|
||||
}
|
||||
auto lru_key = get_block_lru_key(handle->id());
|
||||
auto value_key = get_block_value_key(handle->id());
|
||||
while (handle->need_flush()) {
|
||||
auto version = handle->version();
|
||||
auto R = get_block_value(value_key);
|
||||
if (R.is_ok()) {
|
||||
kv_->begin_transaction().ensure();
|
||||
set_block_value(value_key, handle->serialize());
|
||||
kv_->commit_transaction().ensure();
|
||||
} else {
|
||||
CHECK(get_block_lru(lru_key).is_error());
|
||||
auto empty = get_block_lru_empty_key_hash();
|
||||
auto ER = get_block_lru(empty);
|
||||
ER.ensure();
|
||||
auto E = ER.move_as_ok();
|
||||
|
||||
auto PR = get_block_lru(E.prev);
|
||||
PR.ensure();
|
||||
auto P = PR.move_as_ok();
|
||||
CHECK(P.next == empty);
|
||||
|
||||
DbEntry D{handle->id(), E.prev, empty};
|
||||
|
||||
E.prev = lru_key;
|
||||
P.next = lru_key;
|
||||
|
||||
if (P.is_empty()) {
|
||||
E.next = lru_key;
|
||||
P.prev = lru_key;
|
||||
}
|
||||
|
||||
kv_->begin_transaction().ensure();
|
||||
set_block_value(value_key, handle->serialize());
|
||||
set_block_lru(empty, std::move(E));
|
||||
set_block_lru(D.prev, std::move(P));
|
||||
set_block_lru(lru_key, std::move(D));
|
||||
kv_->commit_transaction().ensure();
|
||||
}
|
||||
handle->flushed_upto(version);
|
||||
}
|
||||
promise.set_value(td::Unit());
|
||||
}
|
||||
|
||||
void BlockDb::get_block_handle(BlockIdExt id, td::Promise<BlockHandle> promise) {
|
||||
if (!id.is_valid()) {
|
||||
promise.set_error(td::Status::Error(ErrorCode::protoviolation, "invalid block id"));
|
||||
return;
|
||||
}
|
||||
CHECK(id.is_valid());
|
||||
auto key_hash = get_block_value_key(id);
|
||||
auto B = get_block_value(key_hash);
|
||||
if (B.is_error()) {
|
||||
promise.set_error(B.move_as_error());
|
||||
return;
|
||||
}
|
||||
promise.set_result(create_block_handle(B.move_as_ok()));
|
||||
}
|
||||
|
||||
void BlockDb::start_up() {
|
||||
kv_ = std::make_shared<td::RocksDb>(td::RocksDb::open(db_path_).move_as_ok());
|
||||
alarm_timestamp() = td::Timestamp::in(0.1);
|
||||
|
||||
auto empty = get_block_lru_empty_key_hash();
|
||||
if (get_block_lru(empty).is_error()) {
|
||||
DbEntry e{BlockIdExt{}, empty, empty};
|
||||
kv_->begin_transaction().ensure();
|
||||
set_block_lru(empty, std::move(e));
|
||||
kv_->commit_transaction().ensure();
|
||||
}
|
||||
last_gc_ = empty;
|
||||
}
|
||||
|
||||
BlockDb::BlockDb(td::actor::ActorId<RootDb> root_db, std::string db_path) : root_db_(root_db), db_path_(db_path) {
|
||||
}
|
||||
|
||||
BlockDb::KeyHash BlockDb::get_block_lru_key(BlockIdExt id) {
|
||||
if (!id.is_valid()) {
|
||||
return KeyHash::zero();
|
||||
} else {
|
||||
auto obj = create_tl_object<ton_api::db_blockdb_key_lru>(create_tl_block_id(id));
|
||||
return get_tl_object_sha_bits256(obj);
|
||||
}
|
||||
}
|
||||
|
||||
BlockDb::KeyHash BlockDb::get_block_value_key(BlockIdExt id) {
|
||||
CHECK(id.is_valid());
|
||||
auto obj = create_tl_object<ton_api::db_blockdb_key_value>(create_tl_block_id(id));
|
||||
return get_tl_object_sha_bits256(obj);
|
||||
}
|
||||
|
||||
td::Result<BlockDb::DbEntry> BlockDb::get_block_lru(KeyHash key_hash) {
|
||||
std::string value;
|
||||
auto R = kv_->get(key_hash.as_slice(), value);
|
||||
R.ensure();
|
||||
if (R.move_as_ok() == td::KeyValue::GetStatus::NotFound) {
|
||||
return td::Status::Error(ErrorCode::notready, "not in db");
|
||||
}
|
||||
auto v = fetch_tl_object<ton_api::db_blockdb_lru>(td::BufferSlice{value}, true);
|
||||
v.ensure();
|
||||
return DbEntry{v.move_as_ok()};
|
||||
}
|
||||
|
||||
td::Result<td::BufferSlice> BlockDb::get_block_value(KeyHash key_hash) {
|
||||
std::string value;
|
||||
auto R = kv_->get(key_hash.as_slice(), value);
|
||||
R.ensure();
|
||||
if (R.move_as_ok() == td::KeyValue::GetStatus::NotFound) {
|
||||
return td::Status::Error(ErrorCode::notready, "not in db");
|
||||
}
|
||||
return td::BufferSlice{value};
|
||||
}
|
||||
|
||||
void BlockDb::set_block_lru(KeyHash key_hash, DbEntry e) {
|
||||
kv_->set(key_hash.as_slice(), serialize_tl_object(e.release(), true)).ensure();
|
||||
}
|
||||
|
||||
void BlockDb::set_block_value(KeyHash key_hash, td::BufferSlice value) {
|
||||
kv_->set(key_hash.as_slice(), std::move(value)).ensure();
|
||||
}
|
||||
|
||||
void BlockDb::alarm() {
|
||||
auto R = get_block_lru(last_gc_);
|
||||
R.ensure();
|
||||
|
||||
auto N = R.move_as_ok();
|
||||
if (N.is_empty()) {
|
||||
last_gc_ = N.next;
|
||||
alarm_timestamp() = td::Timestamp::in(0.01);
|
||||
return;
|
||||
}
|
||||
|
||||
auto P = td::PromiseCreator::lambda([SelfId = actor_id(this)](td::Result<bool> R) {
|
||||
if (R.is_error()) {
|
||||
td::actor::send_closure(SelfId, &BlockDb::skip_gc);
|
||||
} else {
|
||||
auto value = R.move_as_ok();
|
||||
if (!value) {
|
||||
td::actor::send_closure(SelfId, &BlockDb::skip_gc);
|
||||
} else {
|
||||
td::actor::send_closure(SelfId, &BlockDb::gc);
|
||||
}
|
||||
}
|
||||
});
|
||||
td::actor::send_closure(root_db_, &RootDb::allow_block_gc, N.block_id, std::move(P));
|
||||
}
|
||||
|
||||
void BlockDb::gc() {
|
||||
auto FR = get_block_lru(last_gc_);
|
||||
FR.ensure();
|
||||
auto F = FR.move_as_ok();
|
||||
|
||||
auto PR = get_block_lru(F.prev);
|
||||
PR.ensure();
|
||||
auto P = PR.move_as_ok();
|
||||
auto NR = get_block_lru(F.next);
|
||||
NR.ensure();
|
||||
auto N = NR.move_as_ok();
|
||||
|
||||
P.next = F.next;
|
||||
N.prev = F.prev;
|
||||
if (P.is_empty() && N.is_empty()) {
|
||||
P.prev = P.next;
|
||||
N.next = N.prev;
|
||||
}
|
||||
|
||||
auto value_key = get_block_value_key(F.block_id);
|
||||
|
||||
kv_->begin_transaction().ensure();
|
||||
kv_->erase(last_gc_.as_slice()).ensure();
|
||||
kv_->erase(value_key.as_slice()).ensure();
|
||||
set_block_lru(F.prev, std::move(P));
|
||||
set_block_lru(F.next, std::move(N));
|
||||
kv_->commit_transaction().ensure();
|
||||
alarm_timestamp() = td::Timestamp::now();
|
||||
|
||||
DCHECK(get_block_lru(last_gc_).is_error());
|
||||
last_gc_ = F.next;
|
||||
}
|
||||
|
||||
void BlockDb::skip_gc() {
|
||||
auto R = get_block_lru(last_gc_);
|
||||
R.ensure();
|
||||
|
||||
auto N = R.move_as_ok();
|
||||
last_gc_ = N.next;
|
||||
alarm_timestamp() = td::Timestamp::in(0.01);
|
||||
}
|
||||
|
||||
BlockDb::DbEntry::DbEntry(tl_object_ptr<ton_api::db_blockdb_lru> entry)
|
||||
: block_id(create_block_id(entry->id_)), prev(entry->prev_), next(entry->next_) {
|
||||
}
|
||||
|
||||
tl_object_ptr<ton_api::db_blockdb_lru> BlockDb::DbEntry::release() {
|
||||
return create_tl_object<ton_api::db_blockdb_lru>(create_tl_block_id(block_id), prev, next);
|
||||
}
|
||||
|
||||
void BlockDb::truncate(td::Ref<MasterchainState> state, td::Promise<td::Unit> promise) {
|
||||
std::map<ShardIdFull, BlockSeqno> max_seqno;
|
||||
max_seqno.emplace(ShardIdFull{masterchainId}, state->get_seqno() + 1);
|
||||
|
||||
auto shards = state->get_shards();
|
||||
auto it = KeyHash::zero();
|
||||
kv_->begin_transaction().ensure();
|
||||
while (true) {
|
||||
auto R = get_block_lru(it);
|
||||
R.ensure();
|
||||
auto v = R.move_as_ok();
|
||||
it = v.next;
|
||||
R = get_block_lru(it);
|
||||
R.ensure();
|
||||
v = R.move_as_ok();
|
||||
if (v.is_empty()) {
|
||||
break;
|
||||
}
|
||||
|
||||
auto s = v.block_id.shard_full();
|
||||
if (!max_seqno.count(s)) {
|
||||
bool found = false;
|
||||
for (auto &shard : shards) {
|
||||
if (shard_intersects(shard->shard(), s)) {
|
||||
found = true;
|
||||
max_seqno.emplace(s, shard->top_block_id().seqno() + 1);
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!found) {
|
||||
max_seqno.emplace(s, 0);
|
||||
}
|
||||
}
|
||||
|
||||
bool to_delete = v.block_id.seqno() >= max_seqno[s];
|
||||
if (to_delete) {
|
||||
auto key_hash = get_block_value_key(v.block_id);
|
||||
auto B = get_block_value(key_hash);
|
||||
B.ensure();
|
||||
auto handleR = create_block_handle(B.move_as_ok());
|
||||
handleR.ensure();
|
||||
auto handle = handleR.move_as_ok();
|
||||
|
||||
handle->unsafe_clear_applied();
|
||||
handle->unsafe_clear_next();
|
||||
|
||||
if (handle->need_flush()) {
|
||||
set_block_value(key_hash, handle->serialize());
|
||||
}
|
||||
} else if (v.block_id.seqno() + 1 == max_seqno[s]) {
|
||||
auto key_hash = get_block_value_key(v.block_id);
|
||||
auto B = get_block_value(key_hash);
|
||||
B.ensure();
|
||||
auto handleR = create_block_handle(B.move_as_ok());
|
||||
handleR.ensure();
|
||||
auto handle = handleR.move_as_ok();
|
||||
|
||||
handle->unsafe_clear_next();
|
||||
|
||||
if (handle->need_flush()) {
|
||||
set_block_value(key_hash, handle->serialize());
|
||||
}
|
||||
}
|
||||
}
|
||||
kv_->commit_transaction().ensure();
|
||||
}
|
||||
|
||||
} // namespace validator
|
||||
|
||||
} // namespace ton
|
|
@ -1,88 +0,0 @@
|
|||
/*
|
||||
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 <http://www.gnu.org/licenses/>.
|
||||
|
||||
Copyright 2017-2019 Telegram Systems LLP
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include "td/actor/actor.h"
|
||||
#include "ton/ton-types.h"
|
||||
#include "validator/interfaces/block-handle.h"
|
||||
#include "validator/interfaces/db.h"
|
||||
#include "td/db/KeyValueAsync.h"
|
||||
|
||||
namespace ton {
|
||||
|
||||
namespace validator {
|
||||
|
||||
class RootDb;
|
||||
|
||||
class BlockDb : public td::actor::Actor {
|
||||
public:
|
||||
void store_block_handle(BlockHandle handle, td::Promise<td::Unit> promise);
|
||||
void get_block_handle(BlockIdExt id, td::Promise<BlockHandle> promise);
|
||||
|
||||
void start_up() override;
|
||||
void alarm() override;
|
||||
|
||||
void gc();
|
||||
void skip_gc();
|
||||
|
||||
void truncate(td::Ref<MasterchainState> state, td::Promise<td::Unit> promise);
|
||||
|
||||
BlockDb(td::actor::ActorId<RootDb> root_db, std::string db_path);
|
||||
|
||||
private:
|
||||
using KeyHash = td::Bits256;
|
||||
struct DbEntry {
|
||||
BlockIdExt block_id;
|
||||
KeyHash prev;
|
||||
KeyHash next;
|
||||
|
||||
DbEntry(tl_object_ptr<ton_api::db_blockdb_lru> entry);
|
||||
DbEntry() {
|
||||
}
|
||||
DbEntry(BlockIdExt block_id, KeyHash prev, KeyHash next) : block_id(block_id), prev(prev), next(next) {
|
||||
}
|
||||
tl_object_ptr<ton_api::db_blockdb_lru> release();
|
||||
bool is_empty() const {
|
||||
return !block_id.is_valid();
|
||||
}
|
||||
};
|
||||
static KeyHash get_block_lru_key(BlockIdExt block_id);
|
||||
static KeyHash get_block_value_key(BlockIdExt block_id);
|
||||
static KeyHash get_block_lru_empty_key_hash() {
|
||||
return KeyHash::zero();
|
||||
}
|
||||
|
||||
td::Result<DbEntry> get_block_lru(KeyHash key);
|
||||
td::Result<td::BufferSlice> get_block_value(KeyHash key);
|
||||
|
||||
void set_block_lru(KeyHash key_hash, DbEntry e);
|
||||
void set_block_value(KeyHash key_hash, td::BufferSlice data);
|
||||
|
||||
std::shared_ptr<td::KeyValue> kv_;
|
||||
|
||||
td::actor::ActorId<RootDb> root_db_;
|
||||
|
||||
std::string db_path_;
|
||||
|
||||
KeyHash last_gc_ = KeyHash::zero();
|
||||
};
|
||||
|
||||
} // namespace validator
|
||||
|
||||
} // namespace ton
|
|
@ -1,400 +0,0 @@
|
|||
/*
|
||||
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 <http://www.gnu.org/licenses/>.
|
||||
|
||||
Copyright 2017-2019 Telegram Systems LLP
|
||||
*/
|
||||
#include "filedb.hpp"
|
||||
#include "rootdb.hpp"
|
||||
#include "td/utils/port/path.h"
|
||||
#include "files-async.hpp"
|
||||
#include "adnl/utils.hpp"
|
||||
#include "tl-utils/tl-utils.hpp"
|
||||
#include "td/utils/overloaded.h"
|
||||
|
||||
#include "td/db/RocksDb.h"
|
||||
|
||||
namespace ton {
|
||||
|
||||
namespace validator {
|
||||
|
||||
std::string FileDb::get_file_name(const RefId& ref_id, bool create_dirs) {
|
||||
auto ref_id_hash = get_ref_id_hash(ref_id);
|
||||
|
||||
auto s = ref_id_hash.to_hex();
|
||||
std::string path = root_path_ + "/files/";
|
||||
for (td::uint32 i = 0; i < depth_; i++) {
|
||||
path = path + s[2 * i] + s[2 * i + 1] + "/";
|
||||
if (create_dirs) {
|
||||
td::mkdir(path).ensure();
|
||||
}
|
||||
}
|
||||
return path + s;
|
||||
}
|
||||
|
||||
void FileDb::store_file(RefId ref_id, td::BufferSlice data, td::Promise<FileHash> promise) {
|
||||
auto ref_id_hash = get_ref_id_hash(ref_id);
|
||||
auto R = get_block(ref_id_hash);
|
||||
if (R.is_ok()) {
|
||||
auto val = R.move_as_ok();
|
||||
promise.set_result(val.file_hash);
|
||||
return;
|
||||
}
|
||||
|
||||
auto file_hash = sha256_bits256(data.as_slice());
|
||||
auto P = td::PromiseCreator::lambda([SelfId = actor_id(this), file_hash, ref_id = std::move(ref_id),
|
||||
promise = std::move(promise)](td::Result<std::string> R) mutable {
|
||||
if (R.is_error()) {
|
||||
promise.set_error(R.move_as_error());
|
||||
} else {
|
||||
td::actor::send_closure(SelfId, &FileDb::store_file_continue, std::move(ref_id), file_hash, R.move_as_ok(),
|
||||
std::move(promise));
|
||||
}
|
||||
});
|
||||
td::actor::create_actor<db::WriteFile>("writefile", root_path_ + "/tmp/", "", std::move(data), std::move(P))
|
||||
.release();
|
||||
}
|
||||
|
||||
void FileDb::store_file_continue(RefId ref_id, FileHash file_hash, std::string res_path,
|
||||
td::Promise<FileHash> promise) {
|
||||
auto ref_id_hash = get_ref_id_hash(ref_id);
|
||||
auto R = get_block(ref_id_hash);
|
||||
if (R.is_ok()) {
|
||||
td::unlink(res_path).ignore();
|
||||
auto val = R.move_as_ok();
|
||||
promise.set_result(val.file_hash);
|
||||
return;
|
||||
}
|
||||
|
||||
auto path = get_file_name(ref_id, true);
|
||||
td::rename(res_path, path).ensure();
|
||||
|
||||
auto empty = get_empty_ref_id_hash();
|
||||
auto ER = get_block(empty);
|
||||
ER.ensure();
|
||||
auto E = ER.move_as_ok();
|
||||
|
||||
auto PR = get_block(E.prev);
|
||||
PR.ensure();
|
||||
auto P = PR.move_as_ok();
|
||||
CHECK(P.next == empty);
|
||||
|
||||
DbEntry D;
|
||||
D.key = std::move(ref_id);
|
||||
D.prev = E.prev;
|
||||
D.next = empty;
|
||||
D.file_hash = file_hash;
|
||||
|
||||
E.prev = ref_id_hash;
|
||||
P.next = ref_id_hash;
|
||||
|
||||
if (P.is_empty()) {
|
||||
E.next = ref_id_hash;
|
||||
P.prev = ref_id_hash;
|
||||
}
|
||||
|
||||
kv_->begin_transaction().ensure();
|
||||
set_block(empty, std::move(E));
|
||||
set_block(D.prev, std::move(P));
|
||||
set_block(ref_id_hash, std::move(D));
|
||||
kv_->commit_transaction().ensure();
|
||||
|
||||
promise.set_value(std::move(file_hash));
|
||||
}
|
||||
|
||||
void FileDb::load_file(RefId ref_id, td::Promise<td::BufferSlice> promise) {
|
||||
auto ref_id_hash = get_ref_id_hash(ref_id);
|
||||
auto R = get_block(ref_id_hash);
|
||||
if (R.is_error()) {
|
||||
promise.set_error(R.move_as_error());
|
||||
return;
|
||||
}
|
||||
|
||||
auto v = R.move_as_ok();
|
||||
|
||||
auto P = td::PromiseCreator::lambda(
|
||||
[promise = std::move(promise), file_hash = v.file_hash](td::Result<td::BufferSlice> R) mutable {
|
||||
if (R.is_error()) {
|
||||
promise.set_error(R.move_as_error());
|
||||
} else {
|
||||
auto data = R.move_as_ok();
|
||||
if (file_hash != sha256_bits256(data.as_slice())) {
|
||||
promise.set_error(td::Status::Error(ErrorCode::protoviolation, PSTRING() << "db error: bad file hash"));
|
||||
} else {
|
||||
promise.set_value(std::move(data));
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
td::actor::create_actor<db::ReadFile>("readfile", get_file_name(ref_id, false), 0, -1, 0, std::move(P)).release();
|
||||
}
|
||||
|
||||
void FileDb::load_file_slice(RefId ref_id, td::int64 offset, td::int64 max_size, td::Promise<td::BufferSlice> promise) {
|
||||
auto ref_id_hash = get_ref_id_hash(ref_id);
|
||||
auto R = get_block(ref_id_hash);
|
||||
if (R.is_error()) {
|
||||
promise.set_error(R.move_as_error());
|
||||
return;
|
||||
}
|
||||
|
||||
auto v = R.move_as_ok();
|
||||
|
||||
auto P = td::PromiseCreator::lambda([promise = std::move(promise)](td::Result<td::BufferSlice> R) mutable {
|
||||
if (R.is_error()) {
|
||||
promise.set_error(R.move_as_error());
|
||||
} else {
|
||||
promise.set_value(R.move_as_ok());
|
||||
}
|
||||
});
|
||||
|
||||
td::actor::create_actor<db::ReadFile>("readfile", get_file_name(ref_id, false), offset, max_size, 0, std::move(P))
|
||||
.release();
|
||||
}
|
||||
|
||||
void FileDb::check_file(RefId ref_id, td::Promise<bool> promise) {
|
||||
auto ref_id_hash = get_ref_id_hash(ref_id);
|
||||
auto R = get_block(ref_id_hash);
|
||||
if (R.is_error()) {
|
||||
promise.set_result(false);
|
||||
return;
|
||||
}
|
||||
promise.set_result(true);
|
||||
}
|
||||
|
||||
td::Slice FileDb::get_key(const RefIdHash& ref) {
|
||||
return ref.as_slice();
|
||||
}
|
||||
|
||||
td::Result<FileDb::DbEntry> FileDb::get_block(const RefIdHash& ref) {
|
||||
std::string value;
|
||||
auto R = kv_->get(get_key(ref), value);
|
||||
R.ensure();
|
||||
if (R.move_as_ok() == td::KeyValue::GetStatus::NotFound) {
|
||||
return td::Status::Error(ErrorCode::notready, "not in db");
|
||||
}
|
||||
auto val = fetch_tl_object<ton_api::db_filedb_value>(td::BufferSlice{value}, true);
|
||||
val.ensure();
|
||||
return DbEntry{val.move_as_ok()};
|
||||
}
|
||||
|
||||
void FileDb::set_block(const RefIdHash& ref, DbEntry entry) {
|
||||
DCHECK(ref == get_ref_id_hash(entry.key));
|
||||
kv_->set(get_key(ref), entry.release()).ensure();
|
||||
}
|
||||
|
||||
FileDb::FileDb(td::actor::ActorId<RootDb> root_db, std::string root_path, td::uint32 depth, bool is_archive)
|
||||
: root_db_(root_db), root_path_(root_path), depth_(depth), is_archive_(is_archive) {
|
||||
}
|
||||
|
||||
void FileDb::start_up() {
|
||||
td::mkdir(root_path_).ensure();
|
||||
db_path_ = root_path_ + "/db/";
|
||||
|
||||
kv_ = std::make_shared<td::RocksDb>(td::RocksDb::open(db_path_).move_as_ok());
|
||||
td::mkdir(root_path_ + "/files/").ensure();
|
||||
td::mkdir(root_path_ + "/tmp/").ensure();
|
||||
|
||||
last_gc_ = get_empty_ref_id_hash();
|
||||
alarm_timestamp() = td::Timestamp::in(0.01);
|
||||
|
||||
auto R = get_block(last_gc_);
|
||||
if (R.is_error()) {
|
||||
DbEntry e{get_empty_ref_id(), last_gc_, last_gc_, FileHash::zero()};
|
||||
kv_->begin_transaction().ensure();
|
||||
set_block(last_gc_, std::move(e));
|
||||
kv_->commit_transaction().ensure();
|
||||
}
|
||||
}
|
||||
|
||||
void FileDb::alarm() {
|
||||
auto R = get_block(last_gc_);
|
||||
R.ensure();
|
||||
auto N = R.move_as_ok();
|
||||
if (N.is_empty()) {
|
||||
last_gc_ = N.next;
|
||||
alarm_timestamp() = td::Timestamp::in(0.01);
|
||||
return;
|
||||
}
|
||||
|
||||
auto P = td::PromiseCreator::lambda([SelfId = actor_id(this)](td::Result<bool> R) mutable {
|
||||
if (R.is_error()) {
|
||||
td::actor::send_closure(SelfId, &FileDb::skip_gc);
|
||||
} else {
|
||||
auto value = R.move_as_ok();
|
||||
if (!value) {
|
||||
td::actor::send_closure(SelfId, &FileDb::skip_gc);
|
||||
} else {
|
||||
td::actor::send_closure(SelfId, &FileDb::gc);
|
||||
}
|
||||
}
|
||||
});
|
||||
td::actor::send_closure(root_db_, &RootDb::allow_gc, std::move(N.key), is_archive_, std::move(P));
|
||||
}
|
||||
|
||||
void FileDb::gc() {
|
||||
auto FR = get_block(last_gc_);
|
||||
FR.ensure();
|
||||
auto F = FR.move_as_ok();
|
||||
|
||||
auto PR = get_block(F.prev);
|
||||
PR.ensure();
|
||||
auto P = PR.move_as_ok();
|
||||
auto NR = get_block(F.next);
|
||||
NR.ensure();
|
||||
auto N = NR.move_as_ok();
|
||||
|
||||
P.next = F.next;
|
||||
N.prev = F.prev;
|
||||
if (P.is_empty() && N.is_empty()) {
|
||||
P.prev = P.next;
|
||||
N.next = N.prev;
|
||||
}
|
||||
|
||||
auto name = get_file_name(F.key, false);
|
||||
auto S = td::unlink(name);
|
||||
if (S.is_error()) {
|
||||
LOG(WARNING) << "failed to delete " << name;
|
||||
}
|
||||
|
||||
kv_->begin_transaction().ensure();
|
||||
kv_->erase(last_gc_.as_slice()).ensure();
|
||||
set_block(F.prev, std::move(P));
|
||||
set_block(F.next, std::move(N));
|
||||
kv_->commit_transaction().ensure();
|
||||
alarm_timestamp() = td::Timestamp::now();
|
||||
|
||||
DCHECK(get_block(last_gc_).is_error());
|
||||
last_gc_ = F.next;
|
||||
}
|
||||
|
||||
void FileDb::skip_gc() {
|
||||
auto FR = get_block(last_gc_);
|
||||
FR.ensure();
|
||||
auto F = FR.move_as_ok();
|
||||
last_gc_ = F.next;
|
||||
alarm_timestamp() = td::Timestamp::in(0.01);
|
||||
}
|
||||
|
||||
td::BufferSlice FileDb::DbEntry::release() {
|
||||
return create_serialize_tl_object<ton_api::db_filedb_value>(get_ref_id_tl(key), prev, next, file_hash);
|
||||
}
|
||||
|
||||
bool FileDb::DbEntry::is_empty() const {
|
||||
return (key.get_offset() == key.offset<fileref::Empty>());
|
||||
}
|
||||
|
||||
FileDb::RefIdHash FileDb::get_ref_id_hash(const RefId& ref) {
|
||||
FileHash x;
|
||||
ref.visit([&](const auto& obj) { x = obj.hash(); });
|
||||
return x;
|
||||
}
|
||||
|
||||
tl_object_ptr<ton_api::db_filedb_Key> FileDb::get_ref_id_tl(const RefId& ref) {
|
||||
tl_object_ptr<ton_api::db_filedb_Key> x;
|
||||
ref.visit([&](const auto& obj) { x = obj.tl(); });
|
||||
return x;
|
||||
}
|
||||
|
||||
FileDb::RefId FileDb::get_ref_from_tl(const ton_api::db_filedb_Key& from) {
|
||||
RefId ref_id{fileref::Empty{}};
|
||||
ton_api::downcast_call(
|
||||
const_cast<ton_api::db_filedb_Key&>(from),
|
||||
td::overloaded(
|
||||
[&](const ton_api::db_filedb_key_empty& key) { ref_id = fileref::Empty{}; },
|
||||
[&](const ton_api::db_filedb_key_blockFile& key) { ref_id = fileref::Block{create_block_id(key.block_id_)}; },
|
||||
[&](const ton_api::db_filedb_key_zeroStateFile& key) {
|
||||
ref_id = fileref::ZeroState{create_block_id(key.block_id_)};
|
||||
},
|
||||
[&](const ton_api::db_filedb_key_persistentStateFile& key) {
|
||||
ref_id =
|
||||
fileref::PersistentState{create_block_id(key.block_id_), create_block_id(key.masterchain_block_id_)};
|
||||
},
|
||||
[&](const ton_api::db_filedb_key_proof& key) { ref_id = fileref::Proof{create_block_id(key.block_id_)}; },
|
||||
[&](const ton_api::db_filedb_key_proofLink& key) {
|
||||
ref_id = fileref::ProofLink{create_block_id(key.block_id_)};
|
||||
},
|
||||
[&](const ton_api::db_filedb_key_signatures& key) {
|
||||
ref_id = fileref::Signatures{create_block_id(key.block_id_)};
|
||||
},
|
||||
[&](const ton_api::db_filedb_key_candidate& key) {
|
||||
ref_id = fileref::Candidate{PublicKey{key.id_->source_}, create_block_id(key.id_->id_),
|
||||
key.id_->collated_data_file_hash_};
|
||||
},
|
||||
[&](const ton_api::db_filedb_key_blockInfo& key) {
|
||||
ref_id = fileref::BlockInfo{create_block_id(key.block_id_)};
|
||||
}));
|
||||
return ref_id;
|
||||
}
|
||||
|
||||
FileDb::RefId FileDb::get_empty_ref_id() {
|
||||
return fileref::Empty();
|
||||
}
|
||||
|
||||
FileDb::RefIdHash FileDb::get_empty_ref_id_hash() {
|
||||
if (empty_.is_zero()) {
|
||||
empty_ = get_ref_id_hash(get_empty_ref_id());
|
||||
}
|
||||
return empty_;
|
||||
}
|
||||
|
||||
FileDb::DbEntry::DbEntry(tl_object_ptr<ton_api::db_filedb_value> entry)
|
||||
: key(FileDb::get_ref_from_tl(*entry->key_.get()))
|
||||
, prev(entry->prev_)
|
||||
, next(entry->next_)
|
||||
, file_hash(entry->file_hash_) {
|
||||
}
|
||||
|
||||
void FileDb::prepare_stats(td::Promise<std::vector<std::pair<std::string, std::string>>> promise) {
|
||||
std::vector<std::pair<std::string, std::string>> rocksdb_stats;
|
||||
auto stats = kv_->stats();
|
||||
if (stats.size() == 0) {
|
||||
promise.set_value(std::move(rocksdb_stats));
|
||||
return;
|
||||
}
|
||||
size_t pos = 0;
|
||||
while (pos < stats.size()) {
|
||||
while (pos < stats.size() &&
|
||||
(stats[pos] == ' ' || stats[pos] == '\n' || stats[pos] == '\r' || stats[pos] == '\t')) {
|
||||
pos++;
|
||||
}
|
||||
auto p = pos;
|
||||
if (pos == stats.size()) {
|
||||
break;
|
||||
}
|
||||
while (stats[pos] != '\n' && stats[pos] != '\r' && stats[pos] != ' ' && stats[pos] != '\t' && pos < stats.size()) {
|
||||
pos++;
|
||||
}
|
||||
auto name = stats.substr(p, pos - p);
|
||||
if (stats[pos] == '\n' || pos == stats.size()) {
|
||||
rocksdb_stats.emplace_back(name, "");
|
||||
continue;
|
||||
}
|
||||
while (pos < stats.size() &&
|
||||
(stats[pos] == ' ' || stats[pos] == '\n' || stats[pos] == '\r' || stats[pos] == '\t')) {
|
||||
pos++;
|
||||
}
|
||||
p = pos;
|
||||
while (stats[pos] != '\n' && stats[pos] != '\r' && pos < stats.size()) {
|
||||
pos++;
|
||||
}
|
||||
auto value = stats.substr(p, pos - p);
|
||||
rocksdb_stats.emplace_back(name, value);
|
||||
}
|
||||
promise.set_value(std::move(rocksdb_stats));
|
||||
}
|
||||
|
||||
} // namespace validator
|
||||
|
||||
} // namespace ton
|
|
@ -1,218 +0,0 @@
|
|||
/*
|
||||
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 <http://www.gnu.org/licenses/>.
|
||||
|
||||
Copyright 2017-2019 Telegram Systems LLP
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include "ton/ton-types.h"
|
||||
#include "td/actor/actor.h"
|
||||
#include "validator/interfaces/shard.h"
|
||||
#include "td/db/KeyValue.h"
|
||||
#include "ton/ton-tl.hpp"
|
||||
|
||||
namespace ton {
|
||||
|
||||
namespace validator {
|
||||
|
||||
namespace fileref {
|
||||
|
||||
class Empty {
|
||||
public:
|
||||
tl_object_ptr<ton_api::db_filedb_Key> tl() const {
|
||||
return create_tl_object<ton_api::db_filedb_key_empty>();
|
||||
}
|
||||
FileHash hash() const {
|
||||
return create_hash_tl_object<ton_api::db_filedb_key_empty>();
|
||||
}
|
||||
};
|
||||
|
||||
class Block {
|
||||
public:
|
||||
tl_object_ptr<ton_api::db_filedb_Key> tl() const {
|
||||
return create_tl_object<ton_api::db_filedb_key_blockFile>(create_tl_block_id(block_id));
|
||||
}
|
||||
FileHash hash() const {
|
||||
return create_hash_tl_object<ton_api::db_filedb_key_blockFile>(create_tl_block_id(block_id));
|
||||
}
|
||||
|
||||
BlockIdExt block_id;
|
||||
};
|
||||
|
||||
class ZeroState {
|
||||
public:
|
||||
tl_object_ptr<ton_api::db_filedb_Key> tl() const {
|
||||
return create_tl_object<ton_api::db_filedb_key_zeroStateFile>(create_tl_block_id(block_id));
|
||||
}
|
||||
FileHash hash() const {
|
||||
return create_hash_tl_object<ton_api::db_filedb_key_zeroStateFile>(create_tl_block_id(block_id));
|
||||
}
|
||||
|
||||
BlockIdExt block_id;
|
||||
};
|
||||
|
||||
class PersistentState {
|
||||
public:
|
||||
tl_object_ptr<ton_api::db_filedb_Key> tl() const {
|
||||
return create_tl_object<ton_api::db_filedb_key_persistentStateFile>(create_tl_block_id(block_id),
|
||||
create_tl_block_id(masterchain_block_id));
|
||||
}
|
||||
FileHash hash() const {
|
||||
return create_hash_tl_object<ton_api::db_filedb_key_persistentStateFile>(create_tl_block_id(block_id),
|
||||
create_tl_block_id(masterchain_block_id));
|
||||
}
|
||||
|
||||
BlockIdExt block_id;
|
||||
BlockIdExt masterchain_block_id;
|
||||
};
|
||||
|
||||
class Proof {
|
||||
public:
|
||||
tl_object_ptr<ton_api::db_filedb_Key> tl() const {
|
||||
return create_tl_object<ton_api::db_filedb_key_proof>(create_tl_block_id(block_id));
|
||||
}
|
||||
FileHash hash() const {
|
||||
return create_hash_tl_object<ton_api::db_filedb_key_proof>(create_tl_block_id(block_id));
|
||||
}
|
||||
|
||||
BlockIdExt block_id;
|
||||
};
|
||||
|
||||
class ProofLink {
|
||||
public:
|
||||
tl_object_ptr<ton_api::db_filedb_Key> tl() const {
|
||||
return create_tl_object<ton_api::db_filedb_key_proofLink>(create_tl_block_id(block_id));
|
||||
}
|
||||
FileHash hash() const {
|
||||
return create_hash_tl_object<ton_api::db_filedb_key_proofLink>(create_tl_block_id(block_id));
|
||||
}
|
||||
|
||||
BlockIdExt block_id;
|
||||
};
|
||||
|
||||
class Signatures {
|
||||
public:
|
||||
tl_object_ptr<ton_api::db_filedb_Key> tl() const {
|
||||
return create_tl_object<ton_api::db_filedb_key_signatures>(create_tl_block_id(block_id));
|
||||
}
|
||||
FileHash hash() const {
|
||||
return create_hash_tl_object<ton_api::db_filedb_key_signatures>(create_tl_block_id(block_id));
|
||||
}
|
||||
|
||||
BlockIdExt block_id;
|
||||
};
|
||||
|
||||
class Candidate {
|
||||
public:
|
||||
tl_object_ptr<ton_api::db_filedb_Key> tl() const {
|
||||
return create_tl_object<ton_api::db_filedb_key_candidate>(
|
||||
create_tl_object<ton_api::db_candidate_id>(source.tl(), create_tl_block_id(block_id), collated_data_file_hash));
|
||||
}
|
||||
FileHash hash() const {
|
||||
return create_hash_tl_object<ton_api::db_filedb_key_candidate>(
|
||||
create_tl_object<ton_api::db_candidate_id>(source.tl(), create_tl_block_id(block_id), collated_data_file_hash));
|
||||
}
|
||||
|
||||
PublicKey source;
|
||||
BlockIdExt block_id;
|
||||
FileHash collated_data_file_hash;
|
||||
};
|
||||
|
||||
class BlockInfo {
|
||||
public:
|
||||
tl_object_ptr<ton_api::db_filedb_Key> tl() const {
|
||||
return create_tl_object<ton_api::db_filedb_key_blockInfo>(create_tl_block_id(block_id));
|
||||
}
|
||||
FileHash hash() const {
|
||||
return create_hash_tl_object<ton_api::db_filedb_key_blockInfo>(create_tl_block_id(block_id));
|
||||
}
|
||||
|
||||
BlockIdExt block_id;
|
||||
};
|
||||
}; // namespace fileref
|
||||
|
||||
class RootDb;
|
||||
|
||||
class FileDb : public td::actor::Actor {
|
||||
public:
|
||||
using RefId =
|
||||
td::Variant<fileref::Empty, fileref::Block, fileref::ZeroState, fileref::PersistentState, fileref::Proof,
|
||||
fileref::Proof, fileref::ProofLink, fileref::Signatures, fileref::Candidate, fileref::BlockInfo>;
|
||||
using RefIdHash = td::Bits256;
|
||||
|
||||
void store_file(RefId ref_id, td::BufferSlice data, td::Promise<FileHash> promise);
|
||||
void store_file_continue(RefId ref_id, FileHash file_hash, std::string path, td::Promise<FileHash> promise);
|
||||
void load_file(RefId ref_id, td::Promise<td::BufferSlice> promise);
|
||||
void load_file_slice(RefId ref_id, td::int64 offset, td::int64 max_size, td::Promise<td::BufferSlice> promise);
|
||||
void check_file(RefId ref_id, td::Promise<bool> promise);
|
||||
|
||||
void prepare_stats(td::Promise<std::vector<std::pair<std::string, std::string>>> promise);
|
||||
|
||||
void start_up() override;
|
||||
void alarm() override;
|
||||
|
||||
void gc();
|
||||
void skip_gc();
|
||||
|
||||
FileDb(td::actor::ActorId<RootDb> root_db, std::string root_path, td::uint32 depth, bool is_archive);
|
||||
|
||||
private:
|
||||
struct DbEntry {
|
||||
RefId key;
|
||||
RefIdHash prev;
|
||||
RefIdHash next;
|
||||
FileHash file_hash;
|
||||
|
||||
DbEntry(tl_object_ptr<ton_api::db_filedb_value> entry);
|
||||
DbEntry() {
|
||||
}
|
||||
DbEntry(RefId key, RefIdHash prev, RefIdHash next, FileHash file_hash)
|
||||
: key(std::move(key)), prev(prev), next(next), file_hash(file_hash) {
|
||||
}
|
||||
td::BufferSlice release();
|
||||
bool is_empty() const;
|
||||
};
|
||||
|
||||
static RefIdHash get_ref_id_hash(const RefId& ref);
|
||||
static tl_object_ptr<ton_api::db_filedb_Key> get_ref_id_tl(const RefId& ref);
|
||||
static RefId get_ref_from_tl(const ton_api::db_filedb_Key& from);
|
||||
static RefId get_empty_ref_id();
|
||||
RefIdHash get_empty_ref_id_hash();
|
||||
|
||||
std::string get_file_name(const RefId& ref, bool create_dirs);
|
||||
td::Slice get_key(const RefIdHash& ref);
|
||||
|
||||
td::Result<DbEntry> get_block(const RefIdHash& ref_id);
|
||||
void set_block(const RefIdHash& ref_id_hash, DbEntry entry);
|
||||
|
||||
td::actor::ActorId<RootDb> root_db_;
|
||||
|
||||
std::string root_path_;
|
||||
std::string db_path_;
|
||||
td::uint32 depth_;
|
||||
|
||||
bool is_archive_;
|
||||
|
||||
std::shared_ptr<td::KeyValue> kv_;
|
||||
|
||||
RefIdHash last_gc_;
|
||||
RefIdHash empty_ = RefIdHash::zero();
|
||||
};
|
||||
|
||||
} // namespace validator
|
||||
|
||||
} // namespace ton
|
||||
|
471
validator/db/fileref.cpp
Normal file
471
validator/db/fileref.cpp
Normal file
|
@ -0,0 +1,471 @@
|
|||
#include "fileref.hpp"
|
||||
#include "auto/tl/ton_api.hpp"
|
||||
#include "td/utils/overloaded.h"
|
||||
#include "td/utils/misc.h"
|
||||
|
||||
namespace ton {
|
||||
|
||||
namespace validator {
|
||||
|
||||
namespace {
|
||||
|
||||
td::Result<BlockId> get_block_id(std::stringstream& ss) {
|
||||
std::string token;
|
||||
BlockId block_id;
|
||||
std::getline(ss, token, '_');
|
||||
TRY_RESULT(w, td::to_integer_safe<WorkchainId>(token));
|
||||
std::getline(ss, token, '_');
|
||||
TRY_RESULT(shard, td::hex_to_integer_safe<ShardId>(token));
|
||||
std::getline(ss, token, '_');
|
||||
TRY_RESULT(s, td::to_integer_safe<BlockSeqno>(token));
|
||||
return BlockId{w, shard, s};
|
||||
}
|
||||
|
||||
td::Result<FileHash> get_token_hash(std::stringstream& ss) {
|
||||
std::string token;
|
||||
std::getline(ss, token, '_');
|
||||
if (token.size() != 64) {
|
||||
return td::Status::Error(ErrorCode::protoviolation, "hash must have exactly 64 hexdigits");
|
||||
}
|
||||
|
||||
TRY_RESULT(v, td::hex_decode(token));
|
||||
|
||||
FileHash r;
|
||||
r.as_slice().copy_from(v);
|
||||
return r;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
namespace fileref {
|
||||
|
||||
std::string Empty::filename() const {
|
||||
return "empty";
|
||||
}
|
||||
|
||||
Empty Empty::shortref() const {
|
||||
return *this;
|
||||
}
|
||||
|
||||
std::string Empty::filename_short() const {
|
||||
return "empty";
|
||||
}
|
||||
|
||||
BlockShort Block::shortref() const {
|
||||
return BlockShort{block_id.id, hash()};
|
||||
}
|
||||
|
||||
std::string Block::filename() const {
|
||||
return PSTRING() << "block_" << block_id.to_str();
|
||||
}
|
||||
|
||||
std::string Block::filename_short() const {
|
||||
char s[33];
|
||||
sprintf(s, "%llx", static_cast<long long>(block_id.id.shard));
|
||||
return PSTRING() << "block_" << block_id.id.workchain << "_" << s << "_" << block_id.id.seqno << "_"
|
||||
<< hash().to_hex();
|
||||
}
|
||||
|
||||
std::string BlockShort::filename_short() const {
|
||||
char s[33];
|
||||
sprintf(s, "%llx", static_cast<long long>(block_id.shard));
|
||||
return PSTRING() << "block_" << block_id.workchain << "_" << s << "_" << block_id.seqno << "_" << hash().to_hex();
|
||||
}
|
||||
|
||||
ZeroStateShort ZeroState::shortref() const {
|
||||
return ZeroStateShort{block_id.id.workchain, hash()};
|
||||
}
|
||||
|
||||
std::string ZeroState::filename() const {
|
||||
return PSTRING() << "zerostate_" << block_id.to_str();
|
||||
}
|
||||
|
||||
std::string ZeroState::filename_short() const {
|
||||
return PSTRING() << "zerostate_" << block_id.id.workchain << "_" << hash().to_hex();
|
||||
}
|
||||
|
||||
std::string ZeroStateShort::filename_short() const {
|
||||
return PSTRING() << "zerostate_" << workchain << "_" << hash().to_hex();
|
||||
}
|
||||
|
||||
PersistentStateShort PersistentState::shortref() const {
|
||||
return PersistentStateShort{block_id.shard_full(), masterchain_block_id.seqno(), hash()};
|
||||
}
|
||||
|
||||
std::string PersistentState::filename() const {
|
||||
return PSTRING() << "state_" << masterchain_block_id.to_str() << "_" << block_id.to_str();
|
||||
}
|
||||
|
||||
std::string PersistentState::filename_short() const {
|
||||
char s[33];
|
||||
sprintf(s, "%llx", static_cast<long long>(block_id.id.shard));
|
||||
return PSTRING() << "state_" << masterchain_block_id.seqno() << "_" << block_id.id.workchain << "_" << s << "_"
|
||||
<< hash().to_hex();
|
||||
}
|
||||
|
||||
std::string PersistentStateShort::filename_short() const {
|
||||
char s[33];
|
||||
sprintf(s, "%llx", static_cast<long long>(shard_id.shard));
|
||||
return PSTRING() << "state_" << masterchain_seqno << "_" << shard_id.workchain << "_" << s << "_" << hash().to_hex();
|
||||
}
|
||||
|
||||
ProofShort Proof::shortref() const {
|
||||
return ProofShort{block_id.id, hash()};
|
||||
}
|
||||
|
||||
std::string Proof::filename() const {
|
||||
return PSTRING() << "proof_" << block_id.to_str();
|
||||
}
|
||||
|
||||
std::string Proof::filename_short() const {
|
||||
char s[33];
|
||||
sprintf(s, "%llx", static_cast<long long>(block_id.id.shard));
|
||||
return PSTRING() << "proof_" << block_id.id.workchain << "_" << s << "_" << block_id.id.seqno << "_"
|
||||
<< hash().to_hex();
|
||||
}
|
||||
|
||||
std::string ProofShort::filename_short() const {
|
||||
char s[33];
|
||||
sprintf(s, "%llx", static_cast<long long>(block_id.shard));
|
||||
return PSTRING() << "proof_" << block_id.workchain << "_" << s << "_" << block_id.seqno << "_" << hash().to_hex();
|
||||
}
|
||||
|
||||
ProofLinkShort ProofLink::shortref() const {
|
||||
return ProofLinkShort{block_id.id, hash()};
|
||||
}
|
||||
|
||||
std::string ProofLink::filename() const {
|
||||
return PSTRING() << "prooflink_" << block_id.to_str();
|
||||
}
|
||||
|
||||
std::string ProofLink::filename_short() const {
|
||||
char s[33];
|
||||
sprintf(s, "%llx", static_cast<long long>(block_id.id.shard));
|
||||
return PSTRING() << "prooflink_" << block_id.id.workchain << "_" << s << "_" << block_id.id.seqno << "_"
|
||||
<< hash().to_hex();
|
||||
}
|
||||
|
||||
std::string ProofLinkShort::filename_short() const {
|
||||
char s[33];
|
||||
sprintf(s, "%llx", static_cast<long long>(block_id.shard));
|
||||
return PSTRING() << "prooflink_" << block_id.workchain << "_" << s << "_" << block_id.seqno << "_" << hash().to_hex();
|
||||
}
|
||||
|
||||
SignaturesShort Signatures::shortref() const {
|
||||
return SignaturesShort{block_id.id, hash()};
|
||||
}
|
||||
|
||||
std::string Signatures::filename() const {
|
||||
return PSTRING() << "signatures_" << block_id.to_str();
|
||||
}
|
||||
|
||||
std::string Signatures::filename_short() const {
|
||||
char s[33];
|
||||
sprintf(s, "%llx", static_cast<long long>(block_id.id.shard));
|
||||
return PSTRING() << "signatures_" << block_id.id.workchain << "_" << s << "_" << block_id.id.seqno << "_"
|
||||
<< hash().to_hex();
|
||||
}
|
||||
|
||||
std::string SignaturesShort::filename_short() const {
|
||||
char s[33];
|
||||
sprintf(s, "%llx", static_cast<long long>(block_id.shard));
|
||||
return PSTRING() << "signatures_" << block_id.workchain << "_" << s << "_" << block_id.seqno << "_"
|
||||
<< hash().to_hex();
|
||||
}
|
||||
|
||||
CandidateShort Candidate::shortref() const {
|
||||
return CandidateShort{block_id.id, hash()};
|
||||
}
|
||||
|
||||
std::string Candidate::filename() const {
|
||||
return PSTRING() << "candidate_" << block_id.to_str() << "_" << collated_data_file_hash.to_hex() << "_"
|
||||
<< td::base64url_encode(source.export_as_slice());
|
||||
}
|
||||
|
||||
std::string Candidate::filename_short() const {
|
||||
char s[33];
|
||||
sprintf(s, "%llx", static_cast<long long>(block_id.id.shard));
|
||||
return PSTRING() << "candidate_" << block_id.id.workchain << "_" << s << "_" << block_id.id.seqno << "_"
|
||||
<< hash().to_hex();
|
||||
}
|
||||
|
||||
std::string CandidateShort::filename_short() const {
|
||||
char s[33];
|
||||
sprintf(s, "%llx", static_cast<long long>(block_id.shard));
|
||||
return PSTRING() << "candidate_" << block_id.workchain << "_" << s << "_" << block_id.seqno << "_" << hash().to_hex();
|
||||
}
|
||||
|
||||
BlockInfoShort BlockInfo::shortref() const {
|
||||
return BlockInfoShort{block_id.id, hash()};
|
||||
}
|
||||
|
||||
std::string BlockInfo::filename() const {
|
||||
return PSTRING() << "info_" << block_id.to_str();
|
||||
}
|
||||
|
||||
std::string BlockInfo::filename_short() const {
|
||||
char s[33];
|
||||
sprintf(s, "%llx", static_cast<long long>(block_id.id.shard));
|
||||
return PSTRING() << "info_" << block_id.id.workchain << "_" << s << "_" << block_id.id.seqno << "_"
|
||||
<< hash().to_hex();
|
||||
}
|
||||
|
||||
std::string BlockInfoShort::filename_short() const {
|
||||
char s[33];
|
||||
sprintf(s, "%llx", static_cast<long long>(block_id.shard));
|
||||
return PSTRING() << "info_" << block_id.workchain << "_" << s << "_" << block_id.seqno << "_" << hash().to_hex();
|
||||
}
|
||||
|
||||
} // namespace fileref
|
||||
|
||||
FileReference::FileReference(tl_object_ptr<ton_api::db_filedb_Key> key) {
|
||||
ton_api::downcast_call(
|
||||
*key.get(),
|
||||
td::overloaded(
|
||||
[&](const ton_api::db_filedb_key_empty& key) { ref_ = fileref::Empty{}; },
|
||||
[&](const ton_api::db_filedb_key_blockFile& key) { ref_ = fileref::Block{create_block_id(key.block_id_)}; },
|
||||
[&](const ton_api::db_filedb_key_zeroStateFile& key) {
|
||||
ref_ = fileref::ZeroState{create_block_id(key.block_id_)};
|
||||
},
|
||||
[&](const ton_api::db_filedb_key_persistentStateFile& key) {
|
||||
ref_ = fileref::PersistentState{create_block_id(key.block_id_), create_block_id(key.masterchain_block_id_)};
|
||||
},
|
||||
[&](const ton_api::db_filedb_key_proof& key) { ref_ = fileref::Proof{create_block_id(key.block_id_)}; },
|
||||
[&](const ton_api::db_filedb_key_proofLink& key) {
|
||||
ref_ = fileref::ProofLink{create_block_id(key.block_id_)};
|
||||
},
|
||||
[&](const ton_api::db_filedb_key_signatures& key) {
|
||||
ref_ = fileref::Signatures{create_block_id(key.block_id_)};
|
||||
},
|
||||
[&](const ton_api::db_filedb_key_candidate& key) {
|
||||
ref_ = fileref::Candidate{PublicKey{key.id_->source_}, create_block_id(key.id_->id_),
|
||||
key.id_->collated_data_file_hash_};
|
||||
},
|
||||
[&](const ton_api::db_filedb_key_blockInfo& key) {
|
||||
ref_ = fileref::BlockInfo{create_block_id(key.block_id_)};
|
||||
}));
|
||||
}
|
||||
|
||||
FileReferenceShort FileReference::shortref() const {
|
||||
FileReferenceShort h;
|
||||
ref_.visit([&](const auto& obj) { h = obj.shortref(); });
|
||||
return h;
|
||||
}
|
||||
|
||||
td::Bits256 FileReference::hash() const {
|
||||
FileHash h;
|
||||
ref_.visit([&](const auto& obj) { h = obj.hash(); });
|
||||
return h;
|
||||
}
|
||||
|
||||
td::Bits256 FileReferenceShort::hash() const {
|
||||
FileHash h;
|
||||
ref_.visit([&](const auto& obj) { h = obj.hash(); });
|
||||
return h;
|
||||
}
|
||||
|
||||
ShardIdFull FileReference::shard() const {
|
||||
ShardIdFull h;
|
||||
ref_.visit([&](const auto& obj) { h = obj.shard(); });
|
||||
return h;
|
||||
}
|
||||
|
||||
ShardIdFull FileReferenceShort::shard() const {
|
||||
ShardIdFull h;
|
||||
ref_.visit([&](const auto& obj) { h = obj.shard(); });
|
||||
return h;
|
||||
}
|
||||
|
||||
std::string FileReference::filename() const {
|
||||
std::string h;
|
||||
ref_.visit([&](const auto& obj) { h = obj.filename(); });
|
||||
return h;
|
||||
}
|
||||
|
||||
std::string FileReference::filename_short() const {
|
||||
std::string h;
|
||||
ref_.visit([&](const auto& obj) { h = obj.filename_short(); });
|
||||
return h;
|
||||
}
|
||||
|
||||
std::string FileReferenceShort::filename_short() const {
|
||||
std::string h;
|
||||
ref_.visit([&](const auto& obj) { h = obj.filename_short(); });
|
||||
return h;
|
||||
}
|
||||
|
||||
td::Result<FileReference> FileReference::create(std::string filename) {
|
||||
std::stringstream ss{filename};
|
||||
|
||||
std::string token;
|
||||
std::getline(ss, token, '_');
|
||||
|
||||
if (token == "empty") {
|
||||
if (ss.eof()) {
|
||||
return fileref::Empty{};
|
||||
} else {
|
||||
return td::Status::Error(ErrorCode::protoviolation, "too big file name");
|
||||
}
|
||||
} else if (token == "block") {
|
||||
std::getline(ss, token, '_');
|
||||
TRY_RESULT(block_id, BlockIdExt::from_str(token));
|
||||
if (ss.eof()) {
|
||||
return fileref::Block{block_id};
|
||||
} else {
|
||||
return td::Status::Error(ErrorCode::protoviolation, "too big file name");
|
||||
}
|
||||
} else if (token == "zerostate") {
|
||||
std::getline(ss, token, '_');
|
||||
TRY_RESULT(block_id, BlockIdExt::from_str(token));
|
||||
if (ss.eof()) {
|
||||
return fileref::ZeroState{block_id};
|
||||
} else {
|
||||
return td::Status::Error(ErrorCode::protoviolation, "too big file name");
|
||||
}
|
||||
} else if (token == "state") {
|
||||
std::getline(ss, token, '_');
|
||||
TRY_RESULT(masterchain_block_id, BlockIdExt::from_str(token));
|
||||
std::getline(ss, token, '_');
|
||||
TRY_RESULT(block_id, BlockIdExt::from_str(token));
|
||||
if (ss.eof()) {
|
||||
return fileref::PersistentState{block_id, masterchain_block_id};
|
||||
} else {
|
||||
return td::Status::Error(ErrorCode::protoviolation, "too big file name");
|
||||
}
|
||||
} else if (token == "proof") {
|
||||
std::getline(ss, token, '_');
|
||||
TRY_RESULT(block_id, BlockIdExt::from_str(token));
|
||||
if (ss.eof()) {
|
||||
return fileref::Proof{block_id};
|
||||
} else {
|
||||
return td::Status::Error(ErrorCode::protoviolation, "too big file name");
|
||||
}
|
||||
} else if (token == "prooflink") {
|
||||
std::getline(ss, token, '_');
|
||||
TRY_RESULT(block_id, BlockIdExt::from_str(token));
|
||||
if (ss.eof()) {
|
||||
return fileref::ProofLink{block_id};
|
||||
} else {
|
||||
return td::Status::Error(ErrorCode::protoviolation, "too big file name");
|
||||
}
|
||||
} else if (token == "signatures") {
|
||||
std::getline(ss, token, '_');
|
||||
TRY_RESULT(block_id, BlockIdExt::from_str(token));
|
||||
if (ss.eof()) {
|
||||
return fileref::Signatures{block_id};
|
||||
} else {
|
||||
return td::Status::Error(ErrorCode::protoviolation, "too big file name");
|
||||
}
|
||||
} else if (token == "candidate") {
|
||||
std::getline(ss, token, '_');
|
||||
TRY_RESULT(block_id, BlockIdExt::from_str(token));
|
||||
TRY_RESULT(col_hash, get_token_hash(ss));
|
||||
|
||||
std::string rem = ss.str();
|
||||
|
||||
TRY_RESULT(source_s, td::base64url_decode(rem));
|
||||
TRY_RESULT(source, PublicKey::import(source_s));
|
||||
return fileref::Candidate{source, block_id, col_hash};
|
||||
} else if (token == "info") {
|
||||
std::getline(ss, token, '_');
|
||||
TRY_RESULT(block_id, BlockIdExt::from_str(token));
|
||||
if (ss.eof()) {
|
||||
return fileref::BlockInfo{block_id};
|
||||
} else {
|
||||
return td::Status::Error(ErrorCode::protoviolation, "too big file name");
|
||||
}
|
||||
} else {
|
||||
return td::Status::Error(ErrorCode::protoviolation, PSTRING() << "unknown prefix '" << token << "'");
|
||||
}
|
||||
}
|
||||
|
||||
td::Result<FileReferenceShort> FileReferenceShort::create(std::string filename) {
|
||||
std::stringstream ss{filename};
|
||||
|
||||
std::string token;
|
||||
std::getline(ss, token, '_');
|
||||
|
||||
if (token == "empty") {
|
||||
if (ss.eof()) {
|
||||
return fileref::Empty{};
|
||||
} else {
|
||||
return td::Status::Error(ErrorCode::protoviolation, "too big file name");
|
||||
}
|
||||
} else if (token == "block") {
|
||||
TRY_RESULT(block_id, get_block_id(ss));
|
||||
TRY_RESULT(vhash, get_token_hash(ss));
|
||||
if (ss.eof()) {
|
||||
return fileref::BlockShort{block_id, vhash};
|
||||
} else {
|
||||
return td::Status::Error(ErrorCode::protoviolation, "too big file name");
|
||||
}
|
||||
} else if (token == "zerostate") {
|
||||
std::getline(ss, token, '_');
|
||||
TRY_RESULT(workchain, td::to_integer_safe<WorkchainId>(token));
|
||||
TRY_RESULT(vhash, get_token_hash(ss));
|
||||
if (ss.eof()) {
|
||||
return fileref::ZeroStateShort{workchain, vhash};
|
||||
} else {
|
||||
return td::Status::Error(ErrorCode::protoviolation, "too big file name");
|
||||
}
|
||||
} else if (token == "state") {
|
||||
std::getline(ss, token, '_');
|
||||
TRY_RESULT(masterchain_seqno, td::to_integer_safe<BlockSeqno>(token));
|
||||
std::getline(ss, token, '_');
|
||||
TRY_RESULT(workchain, td::to_integer_safe<WorkchainId>(token));
|
||||
std::getline(ss, token, '_');
|
||||
TRY_RESULT(shard, td::hex_to_integer_safe<ShardId>(token));
|
||||
TRY_RESULT(vhash, get_token_hash(ss));
|
||||
if (ss.eof()) {
|
||||
return fileref::PersistentStateShort{ShardIdFull{workchain, shard}, masterchain_seqno, vhash};
|
||||
} else {
|
||||
return td::Status::Error(ErrorCode::protoviolation, "too big file name");
|
||||
}
|
||||
} else if (token == "proof") {
|
||||
TRY_RESULT(block_id, get_block_id(ss));
|
||||
TRY_RESULT(vhash, get_token_hash(ss));
|
||||
if (ss.eof()) {
|
||||
return fileref::ProofShort{block_id, vhash};
|
||||
} else {
|
||||
return td::Status::Error(ErrorCode::protoviolation, "too big file name");
|
||||
}
|
||||
} else if (token == "prooflink") {
|
||||
TRY_RESULT(block_id, get_block_id(ss));
|
||||
TRY_RESULT(vhash, get_token_hash(ss));
|
||||
if (ss.eof()) {
|
||||
return fileref::ProofLinkShort{block_id, vhash};
|
||||
} else {
|
||||
return td::Status::Error(ErrorCode::protoviolation, "too big file name");
|
||||
}
|
||||
} else if (token == "signatures") {
|
||||
TRY_RESULT(block_id, get_block_id(ss));
|
||||
TRY_RESULT(vhash, get_token_hash(ss));
|
||||
if (ss.eof()) {
|
||||
return fileref::SignaturesShort{block_id, vhash};
|
||||
} else {
|
||||
return td::Status::Error(ErrorCode::protoviolation, "too big file name");
|
||||
}
|
||||
} else if (token == "candidate") {
|
||||
TRY_RESULT(block_id, get_block_id(ss));
|
||||
TRY_RESULT(vhash, get_token_hash(ss));
|
||||
if (ss.eof()) {
|
||||
return fileref::CandidateShort{block_id, vhash};
|
||||
} else {
|
||||
return td::Status::Error(ErrorCode::protoviolation, "too big file name");
|
||||
}
|
||||
} else if (token == "info") {
|
||||
TRY_RESULT(block_id, get_block_id(ss));
|
||||
TRY_RESULT(vhash, get_token_hash(ss));
|
||||
if (ss.eof()) {
|
||||
return fileref::BlockInfoShort{block_id, vhash};
|
||||
} else {
|
||||
return td::Status::Error(ErrorCode::protoviolation, "too big file name");
|
||||
}
|
||||
} else {
|
||||
return td::Status::Error(ErrorCode::protoviolation, PSTRING() << "unknown prefix '" << token << "'");
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace validator
|
||||
|
||||
} // namespace ton
|
372
validator/db/fileref.hpp
Normal file
372
validator/db/fileref.hpp
Normal file
|
@ -0,0 +1,372 @@
|
|||
/*
|
||||
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 <http://www.gnu.org/licenses/>.
|
||||
|
||||
Copyright 2017-2019 Telegram Systems LLP
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include "ton/ton-types.h"
|
||||
#include "td/actor/actor.h"
|
||||
#include "validator/interfaces/shard.h"
|
||||
#include "td/db/KeyValue.h"
|
||||
#include "ton/ton-tl.hpp"
|
||||
|
||||
namespace ton {
|
||||
|
||||
namespace validator {
|
||||
|
||||
namespace fileref {
|
||||
|
||||
class Empty {
|
||||
public:
|
||||
tl_object_ptr<ton_api::db_filedb_Key> tl() const {
|
||||
return create_tl_object<ton_api::db_filedb_key_empty>();
|
||||
}
|
||||
FileHash hash() const {
|
||||
return create_hash_tl_object<ton_api::db_filedb_key_empty>();
|
||||
}
|
||||
ShardIdFull shard() const {
|
||||
return ShardIdFull{masterchainId};
|
||||
}
|
||||
std::string filename() const;
|
||||
std::string filename_short() const;
|
||||
Empty shortref() const;
|
||||
};
|
||||
|
||||
class BlockShort {
|
||||
public:
|
||||
FileHash hash() const {
|
||||
return hashv;
|
||||
}
|
||||
ShardIdFull shard() const {
|
||||
return block_id.shard_full();
|
||||
}
|
||||
std::string filename_short() const;
|
||||
|
||||
BlockId block_id;
|
||||
FileHash hashv;
|
||||
};
|
||||
|
||||
class Block {
|
||||
public:
|
||||
tl_object_ptr<ton_api::db_filedb_Key> tl() const {
|
||||
return create_tl_object<ton_api::db_filedb_key_blockFile>(create_tl_block_id(block_id));
|
||||
}
|
||||
FileHash hash() const {
|
||||
return create_hash_tl_object<ton_api::db_filedb_key_blockFile>(create_tl_block_id(block_id));
|
||||
}
|
||||
ShardIdFull shard() const {
|
||||
return block_id.shard_full();
|
||||
}
|
||||
BlockShort shortref() const;
|
||||
std::string filename() const;
|
||||
std::string filename_short() const;
|
||||
|
||||
BlockIdExt block_id;
|
||||
};
|
||||
|
||||
class ZeroStateShort {
|
||||
public:
|
||||
FileHash hash() const {
|
||||
return hashv;
|
||||
}
|
||||
ShardIdFull shard() const {
|
||||
return ShardIdFull{workchain, shardIdAll};
|
||||
}
|
||||
std::string filename_short() const;
|
||||
|
||||
WorkchainId workchain;
|
||||
FileHash hashv;
|
||||
};
|
||||
|
||||
class ZeroState {
|
||||
public:
|
||||
tl_object_ptr<ton_api::db_filedb_Key> tl() const {
|
||||
return create_tl_object<ton_api::db_filedb_key_zeroStateFile>(create_tl_block_id(block_id));
|
||||
}
|
||||
FileHash hash() const {
|
||||
return create_hash_tl_object<ton_api::db_filedb_key_zeroStateFile>(create_tl_block_id(block_id));
|
||||
}
|
||||
ShardIdFull shard() const {
|
||||
return block_id.shard_full();
|
||||
}
|
||||
ZeroStateShort shortref() const;
|
||||
std::string filename() const;
|
||||
std::string filename_short() const;
|
||||
|
||||
BlockIdExt block_id;
|
||||
};
|
||||
|
||||
class PersistentStateShort {
|
||||
public:
|
||||
FileHash hash() const {
|
||||
return hashv;
|
||||
}
|
||||
ShardIdFull shard() const {
|
||||
return shard_id;
|
||||
}
|
||||
std::string filename_short() const;
|
||||
|
||||
ShardIdFull shard_id;
|
||||
BlockSeqno masterchain_seqno;
|
||||
FileHash hashv;
|
||||
};
|
||||
|
||||
class PersistentState {
|
||||
public:
|
||||
tl_object_ptr<ton_api::db_filedb_Key> tl() const {
|
||||
return create_tl_object<ton_api::db_filedb_key_persistentStateFile>(create_tl_block_id(block_id),
|
||||
create_tl_block_id(masterchain_block_id));
|
||||
}
|
||||
FileHash hash() const {
|
||||
return create_hash_tl_object<ton_api::db_filedb_key_persistentStateFile>(create_tl_block_id(block_id),
|
||||
create_tl_block_id(masterchain_block_id));
|
||||
}
|
||||
ShardIdFull shard() const {
|
||||
return block_id.shard_full();
|
||||
}
|
||||
PersistentStateShort shortref() const;
|
||||
std::string filename() const;
|
||||
std::string filename_short() const;
|
||||
|
||||
BlockIdExt block_id;
|
||||
BlockIdExt masterchain_block_id;
|
||||
};
|
||||
|
||||
class ProofShort {
|
||||
public:
|
||||
FileHash hash() const {
|
||||
return hashv;
|
||||
}
|
||||
ShardIdFull shard() const {
|
||||
return block_id.shard_full();
|
||||
}
|
||||
std::string filename_short() const;
|
||||
|
||||
BlockId block_id;
|
||||
FileHash hashv;
|
||||
};
|
||||
|
||||
class Proof {
|
||||
public:
|
||||
tl_object_ptr<ton_api::db_filedb_Key> tl() const {
|
||||
return create_tl_object<ton_api::db_filedb_key_proof>(create_tl_block_id(block_id));
|
||||
}
|
||||
FileHash hash() const {
|
||||
return create_hash_tl_object<ton_api::db_filedb_key_proof>(create_tl_block_id(block_id));
|
||||
}
|
||||
ShardIdFull shard() const {
|
||||
return block_id.shard_full();
|
||||
}
|
||||
ProofShort shortref() const;
|
||||
std::string filename() const;
|
||||
std::string filename_short() const;
|
||||
|
||||
BlockIdExt block_id;
|
||||
};
|
||||
|
||||
class ProofLinkShort {
|
||||
public:
|
||||
FileHash hash() const {
|
||||
return hashv;
|
||||
}
|
||||
ShardIdFull shard() const {
|
||||
return block_id.shard_full();
|
||||
}
|
||||
std::string filename_short() const;
|
||||
|
||||
BlockId block_id;
|
||||
FileHash hashv;
|
||||
};
|
||||
|
||||
class ProofLink {
|
||||
public:
|
||||
tl_object_ptr<ton_api::db_filedb_Key> tl() const {
|
||||
return create_tl_object<ton_api::db_filedb_key_proofLink>(create_tl_block_id(block_id));
|
||||
}
|
||||
FileHash hash() const {
|
||||
return create_hash_tl_object<ton_api::db_filedb_key_proofLink>(create_tl_block_id(block_id));
|
||||
}
|
||||
ShardIdFull shard() const {
|
||||
return block_id.shard_full();
|
||||
}
|
||||
ProofLinkShort shortref() const;
|
||||
std::string filename() const;
|
||||
std::string filename_short() const;
|
||||
|
||||
BlockIdExt block_id;
|
||||
};
|
||||
|
||||
class SignaturesShort {
|
||||
public:
|
||||
FileHash hash() const {
|
||||
return hashv;
|
||||
}
|
||||
ShardIdFull shard() const {
|
||||
return block_id.shard_full();
|
||||
}
|
||||
std::string filename_short() const;
|
||||
|
||||
BlockId block_id;
|
||||
FileHash hashv;
|
||||
};
|
||||
|
||||
class Signatures {
|
||||
public:
|
||||
tl_object_ptr<ton_api::db_filedb_Key> tl() const {
|
||||
return create_tl_object<ton_api::db_filedb_key_signatures>(create_tl_block_id(block_id));
|
||||
}
|
||||
FileHash hash() const {
|
||||
return create_hash_tl_object<ton_api::db_filedb_key_signatures>(create_tl_block_id(block_id));
|
||||
}
|
||||
ShardIdFull shard() const {
|
||||
return block_id.shard_full();
|
||||
}
|
||||
SignaturesShort shortref() const;
|
||||
std::string filename() const;
|
||||
std::string filename_short() const;
|
||||
|
||||
BlockIdExt block_id;
|
||||
};
|
||||
|
||||
class CandidateShort {
|
||||
public:
|
||||
FileHash hash() const {
|
||||
return hashv;
|
||||
}
|
||||
ShardIdFull shard() const {
|
||||
return block_id.shard_full();
|
||||
}
|
||||
std::string filename_short() const;
|
||||
|
||||
BlockId block_id;
|
||||
FileHash hashv;
|
||||
};
|
||||
|
||||
class Candidate {
|
||||
public:
|
||||
tl_object_ptr<ton_api::db_filedb_Key> tl() const {
|
||||
return create_tl_object<ton_api::db_filedb_key_candidate>(
|
||||
create_tl_object<ton_api::db_candidate_id>(source.tl(), create_tl_block_id(block_id), collated_data_file_hash));
|
||||
}
|
||||
FileHash hash() const {
|
||||
return create_hash_tl_object<ton_api::db_filedb_key_candidate>(
|
||||
create_tl_object<ton_api::db_candidate_id>(source.tl(), create_tl_block_id(block_id), collated_data_file_hash));
|
||||
}
|
||||
ShardIdFull shard() const {
|
||||
return block_id.shard_full();
|
||||
}
|
||||
CandidateShort shortref() const;
|
||||
std::string filename() const;
|
||||
std::string filename_short() const;
|
||||
|
||||
PublicKey source;
|
||||
BlockIdExt block_id;
|
||||
FileHash collated_data_file_hash;
|
||||
};
|
||||
|
||||
class BlockInfoShort {
|
||||
public:
|
||||
FileHash hash() const {
|
||||
return hashv;
|
||||
}
|
||||
ShardIdFull shard() const {
|
||||
return block_id.shard_full();
|
||||
}
|
||||
std::string filename_short() const;
|
||||
|
||||
BlockId block_id;
|
||||
FileHash hashv;
|
||||
};
|
||||
|
||||
class BlockInfo {
|
||||
public:
|
||||
tl_object_ptr<ton_api::db_filedb_Key> tl() const {
|
||||
return create_tl_object<ton_api::db_filedb_key_blockInfo>(create_tl_block_id(block_id));
|
||||
}
|
||||
FileHash hash() const {
|
||||
return create_hash_tl_object<ton_api::db_filedb_key_blockInfo>(create_tl_block_id(block_id));
|
||||
}
|
||||
ShardIdFull shard() const {
|
||||
return block_id.shard_full();
|
||||
}
|
||||
BlockInfoShort shortref() const;
|
||||
std::string filename() const;
|
||||
std::string filename_short() const;
|
||||
|
||||
BlockIdExt block_id;
|
||||
};
|
||||
|
||||
}; // namespace fileref
|
||||
|
||||
class FileReferenceShort {
|
||||
private:
|
||||
td::Variant<fileref::Empty, fileref::BlockShort, fileref::ZeroStateShort, fileref::PersistentStateShort,
|
||||
fileref::ProofShort, fileref::ProofShort, fileref::ProofLinkShort, fileref::SignaturesShort,
|
||||
fileref::CandidateShort, fileref::BlockInfoShort>
|
||||
ref_;
|
||||
|
||||
public:
|
||||
template <typename T>
|
||||
FileReferenceShort(T x) : ref_(std::move(x)) {
|
||||
}
|
||||
FileReferenceShort() : ref_(fileref::Empty{}) {
|
||||
}
|
||||
|
||||
static td::Result<FileReferenceShort> create(std::string filename);
|
||||
|
||||
auto &ref() {
|
||||
return ref_;
|
||||
}
|
||||
|
||||
td::Bits256 hash() const;
|
||||
ShardIdFull shard() const;
|
||||
std::string filename_short() const;
|
||||
};
|
||||
|
||||
class FileReference {
|
||||
private:
|
||||
td::Variant<fileref::Empty, fileref::Block, fileref::ZeroState, fileref::PersistentState, fileref::Proof,
|
||||
fileref::Proof, fileref::ProofLink, fileref::Signatures, fileref::Candidate, fileref::BlockInfo>
|
||||
ref_;
|
||||
|
||||
public:
|
||||
template <typename T>
|
||||
FileReference(T x) : ref_(std::move(x)) {
|
||||
}
|
||||
FileReference() : ref_(fileref::Empty{}) {
|
||||
}
|
||||
FileReference(tl_object_ptr<ton_api::db_filedb_Key> key);
|
||||
|
||||
static td::Result<FileReference> create(std::string filename);
|
||||
|
||||
auto &ref() {
|
||||
return ref_;
|
||||
}
|
||||
|
||||
FileReferenceShort shortref() const;
|
||||
|
||||
tl_object_ptr<ton_api::db_filedb_Key> tl() const;
|
||||
td::Bits256 hash() const;
|
||||
ShardIdFull shard() const;
|
||||
std::string filename() const;
|
||||
std::string filename_short() const;
|
||||
};
|
||||
|
||||
} // namespace validator
|
||||
|
||||
} // namespace ton
|
||||
|
|
@ -1,310 +0,0 @@
|
|||
/*
|
||||
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 <http://www.gnu.org/licenses/>.
|
||||
|
||||
Copyright 2017-2019 Telegram Systems LLP
|
||||
*/
|
||||
#include "ltdb.hpp"
|
||||
#include "rootdb.hpp"
|
||||
#include "auto/tl/ton_api.h"
|
||||
#include "ton/ton-tl.hpp"
|
||||
#include "ton/ton-io.hpp"
|
||||
#include "td/db/RocksDb.h"
|
||||
|
||||
namespace ton {
|
||||
|
||||
namespace validator {
|
||||
|
||||
void LtDb::add_new_block(BlockIdExt block_id, LogicalTime lt, UnixTime ts, td::Promise<td::Unit> promise) {
|
||||
auto key = get_desc_key(block_id.shard_full());
|
||||
|
||||
std::string value;
|
||||
auto R = kv_->get(key.as_slice(), value);
|
||||
R.ensure();
|
||||
tl_object_ptr<ton_api::db_lt_desc_value> v;
|
||||
bool add_shard = false;
|
||||
if (R.move_as_ok() == td::KeyValue::GetStatus::Ok) {
|
||||
auto F = fetch_tl_object<ton_api::db_lt_desc_value>(td::BufferSlice{value}, true);
|
||||
F.ensure();
|
||||
v = F.move_as_ok();
|
||||
} else {
|
||||
v = create_tl_object<ton_api::db_lt_desc_value>(1, 1, 0, 0, 0);
|
||||
add_shard = true;
|
||||
}
|
||||
if (block_id.id.seqno <= static_cast<td::uint32>(v->last_seqno_) || lt <= static_cast<LogicalTime>(v->last_lt_) ||
|
||||
ts <= static_cast<UnixTime>(v->last_ts_)) {
|
||||
promise.set_value(td::Unit());
|
||||
return;
|
||||
}
|
||||
auto db_value = create_serialize_tl_object<ton_api::db_lt_el_value>(create_tl_block_id(block_id), lt, ts);
|
||||
auto db_key = get_el_key(block_id.shard_full(), v->last_idx_++);
|
||||
auto status_key = create_serialize_tl_object<ton_api::db_lt_status_key>();
|
||||
v->last_seqno_ = block_id.id.seqno;
|
||||
v->last_lt_ = lt;
|
||||
v->last_ts_ = ts;
|
||||
|
||||
td::uint32 idx = 0;
|
||||
if (add_shard) {
|
||||
auto G = kv_->get(status_key.as_slice(), value);
|
||||
G.ensure();
|
||||
if (G.move_as_ok() == td::KeyValue::GetStatus::NotFound) {
|
||||
idx = 0;
|
||||
} else {
|
||||
auto F = fetch_tl_object<ton_api::db_lt_status_value>(value, true);
|
||||
F.ensure();
|
||||
auto f = F.move_as_ok();
|
||||
idx = f->total_shards_;
|
||||
}
|
||||
}
|
||||
|
||||
kv_->begin_transaction().ensure();
|
||||
kv_->set(key, serialize_tl_object(v, true)).ensure();
|
||||
kv_->set(db_key, db_value.as_slice()).ensure();
|
||||
if (add_shard) {
|
||||
auto shard_key = create_serialize_tl_object<ton_api::db_lt_shard_key>(idx);
|
||||
auto shard_value = create_serialize_tl_object<ton_api::db_lt_shard_value>(block_id.id.workchain, block_id.id.shard);
|
||||
kv_->set(status_key.as_slice(), create_serialize_tl_object<ton_api::db_lt_status_value>(idx + 1)).ensure();
|
||||
kv_->set(shard_key.as_slice(), shard_value.as_slice()).ensure();
|
||||
}
|
||||
kv_->commit_transaction().ensure();
|
||||
|
||||
promise.set_value(td::Unit());
|
||||
}
|
||||
|
||||
void LtDb::get_block_common(AccountIdPrefixFull account_id,
|
||||
std::function<td::int32(ton_api::db_lt_desc_value &)> compare_desc,
|
||||
std::function<td::int32(ton_api::db_lt_el_value &)> compare, bool exact,
|
||||
td::Promise<BlockIdExt> promise) {
|
||||
bool f = false;
|
||||
BlockIdExt block_id;
|
||||
td::uint32 ls = 0;
|
||||
for (td::uint32 len = 0; len <= 60; len++) {
|
||||
auto s = shard_prefix(account_id, len);
|
||||
auto key = get_desc_key(s);
|
||||
std::string value;
|
||||
auto F = kv_->get(key, value);
|
||||
F.ensure();
|
||||
if (F.move_as_ok() == td::KeyValue::GetStatus::NotFound) {
|
||||
if (!f) {
|
||||
continue;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
f = true;
|
||||
auto G = fetch_tl_object<ton_api::db_lt_desc_value>(td::BufferSlice{value}, true);
|
||||
G.ensure();
|
||||
auto g = G.move_as_ok();
|
||||
if (compare_desc(*g.get()) > 0) {
|
||||
continue;
|
||||
}
|
||||
td::uint32 l = g->first_idx_ - 1;
|
||||
BlockIdExt lseq;
|
||||
td::uint32 r = g->last_idx_;
|
||||
BlockIdExt rseq;
|
||||
while (r - l > 1) {
|
||||
auto x = (r + l) / 2;
|
||||
auto db_key = get_el_key(s, x);
|
||||
F = kv_->get(db_key, value);
|
||||
F.ensure();
|
||||
CHECK(F.move_as_ok() == td::KeyValue::GetStatus::Ok);
|
||||
auto E = fetch_tl_object<ton_api::db_lt_el_value>(td::BufferSlice{value}, true);
|
||||
E.ensure();
|
||||
auto e = E.move_as_ok();
|
||||
int cmp_val = compare(*e.get());
|
||||
|
||||
if (cmp_val < 0) {
|
||||
rseq = create_block_id(e->id_);
|
||||
r = x;
|
||||
} else if (cmp_val > 0) {
|
||||
lseq = create_block_id(e->id_);
|
||||
l = x;
|
||||
} else {
|
||||
promise.set_value(create_block_id(e->id_));
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (rseq.is_valid()) {
|
||||
if (!block_id.is_valid()) {
|
||||
block_id = rseq;
|
||||
} else if (block_id.id.seqno > rseq.id.seqno) {
|
||||
block_id = rseq;
|
||||
}
|
||||
}
|
||||
if (lseq.is_valid()) {
|
||||
if (ls < lseq.id.seqno) {
|
||||
ls = lseq.id.seqno;
|
||||
}
|
||||
}
|
||||
if (block_id.is_valid() && ls + 1 == block_id.id.seqno) {
|
||||
if (!exact) {
|
||||
promise.set_value(std::move(block_id));
|
||||
} else {
|
||||
promise.set_error(td::Status::Error(ErrorCode::notready, "ltdb: block not found"));
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (!exact && block_id.is_valid()) {
|
||||
promise.set_value(std::move(block_id));
|
||||
} else {
|
||||
promise.set_error(td::Status::Error(ErrorCode::notready, "ltdb: block not found"));
|
||||
}
|
||||
}
|
||||
|
||||
void LtDb::get_block_by_lt(AccountIdPrefixFull account_id, LogicalTime lt, td::Promise<BlockIdExt> promise) {
|
||||
return get_block_common(
|
||||
account_id,
|
||||
[lt](ton_api::db_lt_desc_value &w) {
|
||||
return lt > static_cast<LogicalTime>(w.last_lt_) ? 1 : lt == static_cast<LogicalTime>(w.last_lt_) ? 0 : -1;
|
||||
},
|
||||
[lt](ton_api::db_lt_el_value &w) {
|
||||
return lt > static_cast<LogicalTime>(w.lt_) ? 1 : lt == static_cast<LogicalTime>(w.lt_) ? 0 : -1;
|
||||
},
|
||||
false, std::move(promise));
|
||||
}
|
||||
|
||||
void LtDb::get_block_by_seqno(AccountIdPrefixFull account_id, BlockSeqno seqno, td::Promise<BlockIdExt> promise) {
|
||||
return get_block_common(
|
||||
account_id,
|
||||
[seqno](ton_api::db_lt_desc_value &w) {
|
||||
return seqno > static_cast<BlockSeqno>(w.last_seqno_)
|
||||
? 1
|
||||
: seqno == static_cast<BlockSeqno>(w.last_seqno_) ? 0 : -1;
|
||||
},
|
||||
[seqno](ton_api::db_lt_el_value &w) {
|
||||
return seqno > static_cast<BlockSeqno>(w.id_->seqno_)
|
||||
? 1
|
||||
: seqno == static_cast<BlockSeqno>(w.id_->seqno_) ? 0 : -1;
|
||||
},
|
||||
true, std::move(promise));
|
||||
}
|
||||
|
||||
void LtDb::get_block_by_unix_time(AccountIdPrefixFull account_id, UnixTime ts, td::Promise<BlockIdExt> promise) {
|
||||
return get_block_common(
|
||||
account_id,
|
||||
[ts](ton_api::db_lt_desc_value &w) {
|
||||
return ts > static_cast<UnixTime>(w.last_ts_) ? 1 : ts == static_cast<UnixTime>(w.last_ts_) ? 0 : -1;
|
||||
},
|
||||
[ts](ton_api::db_lt_el_value &w) {
|
||||
return ts > static_cast<UnixTime>(w.ts_) ? 1 : ts == static_cast<UnixTime>(w.ts_) ? 0 : -1;
|
||||
},
|
||||
false, std::move(promise));
|
||||
}
|
||||
|
||||
td::BufferSlice LtDb::get_desc_key(ShardIdFull shard) {
|
||||
return create_serialize_tl_object<ton_api::db_lt_desc_key>(shard.workchain, shard.shard);
|
||||
}
|
||||
|
||||
td::BufferSlice LtDb::get_el_key(ShardIdFull shard, td::uint32 idx) {
|
||||
return create_serialize_tl_object<ton_api::db_lt_el_key>(shard.workchain, shard.shard, idx);
|
||||
}
|
||||
|
||||
void LtDb::start_up() {
|
||||
kv_ = std::make_shared<td::RocksDb>(td::RocksDb::open(db_path_).move_as_ok());
|
||||
}
|
||||
|
||||
void LtDb::truncate_workchain(ShardIdFull shard, td::Ref<MasterchainState> state) {
|
||||
auto key = get_desc_key(shard);
|
||||
std::string value;
|
||||
auto R = kv_->get(key, value);
|
||||
R.ensure();
|
||||
CHECK(R.move_as_ok() == td::KeyValue::GetStatus::Ok);
|
||||
auto F = fetch_tl_object<ton_api::db_lt_desc_value>(td::BufferSlice{value}, true);
|
||||
F.ensure();
|
||||
auto f = F.move_as_ok();
|
||||
|
||||
auto shards = state->get_shards();
|
||||
BlockSeqno seqno = 0;
|
||||
if (shard.is_masterchain()) {
|
||||
seqno = state->get_seqno();
|
||||
} else {
|
||||
for (auto s : shards) {
|
||||
if (shard_intersects(s->shard(), shard)) {
|
||||
seqno = s->top_block_id().seqno();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
while (f->last_idx_ > f->first_idx_) {
|
||||
auto db_key = get_el_key(shard, f->last_idx_ - 1);
|
||||
R = kv_->get(db_key, value);
|
||||
R.ensure();
|
||||
CHECK(R.move_as_ok() == td::KeyValue::GetStatus::Ok);
|
||||
auto E = fetch_tl_object<ton_api::db_lt_el_value>(td::BufferSlice{value}, true);
|
||||
E.ensure();
|
||||
auto e = E.move_as_ok();
|
||||
|
||||
bool to_delete = static_cast<td::uint32>(e->id_->seqno_) > seqno;
|
||||
|
||||
if (!to_delete) {
|
||||
break;
|
||||
} else {
|
||||
f->last_idx_--;
|
||||
kv_->erase(db_key).ensure();
|
||||
}
|
||||
}
|
||||
|
||||
if (f->first_idx_ == f->last_idx_) {
|
||||
f->last_ts_ = 0;
|
||||
f->last_lt_ = 0;
|
||||
f->last_seqno_ = 0;
|
||||
}
|
||||
|
||||
kv_->set(key, serialize_tl_object(f, true)).ensure();
|
||||
}
|
||||
|
||||
void LtDb::truncate(td::Ref<MasterchainState> state, td::Promise<td::Unit> promise) {
|
||||
auto status_key = create_serialize_tl_object<ton_api::db_lt_status_key>();
|
||||
td::Result<td::KeyValue::GetStatus> R;
|
||||
td::uint32 total_shards = 0;
|
||||
{
|
||||
std::string value;
|
||||
R = kv_->get(status_key.as_slice(), value);
|
||||
R.ensure();
|
||||
if (R.move_as_ok() == td::KeyValue::GetStatus::NotFound) {
|
||||
promise.set_value(td::Unit());
|
||||
return;
|
||||
}
|
||||
auto F = fetch_tl_object<ton_api::db_lt_status_value>(value, true);
|
||||
F.ensure();
|
||||
auto f = F.move_as_ok();
|
||||
total_shards = f->total_shards_;
|
||||
if (total_shards == 0) {
|
||||
promise.set_value(td::Unit());
|
||||
return;
|
||||
}
|
||||
}
|
||||
kv_->begin_transaction().ensure();
|
||||
for (td::uint32 idx = 0; idx < total_shards; idx++) {
|
||||
auto shard_key = create_serialize_tl_object<ton_api::db_lt_shard_key>(idx);
|
||||
std::string value;
|
||||
R = kv_->get(shard_key.as_slice(), value);
|
||||
R.ensure();
|
||||
CHECK(R.move_as_ok() == td::KeyValue::GetStatus::Ok);
|
||||
auto F = fetch_tl_object<ton_api::db_lt_shard_value>(value, true);
|
||||
F.ensure();
|
||||
auto f = F.move_as_ok();
|
||||
|
||||
truncate_workchain(ShardIdFull{f->workchain_, static_cast<td::uint64>(f->shard_)}, state);
|
||||
}
|
||||
kv_->commit_transaction().ensure();
|
||||
promise.set_value(td::Unit());
|
||||
}
|
||||
|
||||
} // namespace validator
|
||||
|
||||
} // namespace ton
|
|
@ -1,67 +0,0 @@
|
|||
/*
|
||||
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 <http://www.gnu.org/licenses/>.
|
||||
|
||||
Copyright 2017-2019 Telegram Systems LLP
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include "td/actor/actor.h"
|
||||
#include "td/db/KeyValueAsync.h"
|
||||
#include "validator/interfaces/db.h"
|
||||
|
||||
#include "ton/ton-types.h"
|
||||
|
||||
#include "auto/tl/ton_api.h"
|
||||
|
||||
namespace ton {
|
||||
|
||||
namespace validator {
|
||||
|
||||
class RootDb;
|
||||
|
||||
class LtDb : public td::actor::Actor {
|
||||
public:
|
||||
void add_new_block(BlockIdExt block_id, LogicalTime lt, UnixTime ts, td::Promise<td::Unit> promise);
|
||||
void get_block_common(AccountIdPrefixFull account_id,
|
||||
std::function<td::int32(ton_api::db_lt_desc_value &)> compare_desc,
|
||||
std::function<td::int32(ton_api::db_lt_el_value &)> compare, bool exact,
|
||||
td::Promise<BlockIdExt> promise);
|
||||
void get_block_by_lt(AccountIdPrefixFull account_id, LogicalTime lt, td::Promise<BlockIdExt> promise);
|
||||
void get_block_by_unix_time(AccountIdPrefixFull account_id, UnixTime ts, td::Promise<BlockIdExt> promise);
|
||||
void get_block_by_seqno(AccountIdPrefixFull account_id, BlockSeqno seqno, td::Promise<BlockIdExt> promise);
|
||||
|
||||
void truncate_workchain(ShardIdFull shard, td::Ref<MasterchainState> state);
|
||||
void truncate(td::Ref<MasterchainState> state, td::Promise<td::Unit> promise);
|
||||
|
||||
void start_up() override;
|
||||
|
||||
LtDb(td::actor::ActorId<RootDb> root_db, std::string db_path) : root_db_(root_db), db_path_(std::move(db_path)) {
|
||||
}
|
||||
|
||||
private:
|
||||
td::BufferSlice get_desc_key(ShardIdFull shard);
|
||||
td::BufferSlice get_el_key(ShardIdFull shard, td::uint32 idx);
|
||||
td::BufferSlice get_from_db(ShardIdFull shard, td::uint32 idx);
|
||||
|
||||
std::shared_ptr<td::KeyValue> kv_;
|
||||
|
||||
td::actor::ActorId<RootDb> root_db_;
|
||||
std::string db_path_;
|
||||
};
|
||||
|
||||
} // namespace validator
|
||||
|
||||
} // namespace ton
|
|
@ -34,7 +34,7 @@ td::Status Package::truncate(td::uint64 size) {
|
|||
return fd_.truncate_to_current_position(size + header_size());
|
||||
}
|
||||
|
||||
td::uint64 Package::append(std::string filename, td::Slice data) {
|
||||
td::uint64 Package::append(std::string filename, td::Slice data, bool sync) {
|
||||
CHECK(data.size() <= max_data_size());
|
||||
CHECK(filename.size() <= max_filename_size());
|
||||
auto size = fd_.get_size().move_as_ok();
|
||||
|
@ -48,10 +48,16 @@ td::uint64 Package::append(std::string filename, td::Slice data) {
|
|||
size += filename.size();
|
||||
CHECK(fd_.pwrite(data, size).move_as_ok() == data.size());
|
||||
size += data.size();
|
||||
fd_.sync().ensure();
|
||||
if (sync) {
|
||||
fd_.sync().ensure();
|
||||
}
|
||||
return orig_size - header_size();
|
||||
}
|
||||
|
||||
void Package::sync() {
|
||||
fd_.sync().ensure();
|
||||
}
|
||||
|
||||
td::uint64 Package::size() const {
|
||||
return fd_.get_size().move_as_ok() - header_size();
|
||||
}
|
||||
|
@ -140,4 +146,28 @@ td::Result<Package> Package::open(std::string path, bool read_only, bool create)
|
|||
return Package{std::move(fd)};
|
||||
}
|
||||
|
||||
void Package::iterate(std::function<bool(std::string, td::BufferSlice, td::uint64)> func) {
|
||||
td::uint64 p = 0;
|
||||
|
||||
td::uint64 size = fd_.get_size().move_as_ok();
|
||||
if (size < header_size()) {
|
||||
LOG(ERROR) << "too short archive";
|
||||
return;
|
||||
}
|
||||
size -= header_size();
|
||||
while (p != size) {
|
||||
auto R = read(p);
|
||||
if (R.is_error()) {
|
||||
LOG(ERROR) << "broken archive: " << R.move_as_error();
|
||||
return;
|
||||
}
|
||||
auto q = R.move_as_ok();
|
||||
if (!func(q.first, q.second.clone(), p)) {
|
||||
break;
|
||||
}
|
||||
|
||||
p = advance(p).move_as_ok();
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace ton
|
||||
|
|
|
@ -14,24 +14,17 @@ class Package {
|
|||
|
||||
td::Status truncate(td::uint64 size);
|
||||
|
||||
td::uint64 append(std::string filename, td::Slice data);
|
||||
td::uint64 append(std::string filename, td::Slice data, bool sync = true);
|
||||
void sync();
|
||||
td::uint64 size() const;
|
||||
td::Result<std::pair<std::string, td::BufferSlice>> read(td::uint64 offset) const;
|
||||
|
||||
td::Result<td::uint64> advance(td::uint64 offset);
|
||||
void iterate(std::function<bool(std::string, td::BufferSlice, td::uint64)> func);
|
||||
|
||||
struct Iterator {
|
||||
td::uint64 offset;
|
||||
Package &package;
|
||||
|
||||
Iterator operator++(int);
|
||||
const Iterator operator++(int) const;
|
||||
td::Result<std::pair<std::string, td::BufferSlice>> read() const;
|
||||
};
|
||||
|
||||
Iterator begin();
|
||||
const Iterator begin() const;
|
||||
Iterator end();
|
||||
const Iterator end() const;
|
||||
td::FileFd &fd() {
|
||||
return fd_;
|
||||
}
|
||||
|
||||
private:
|
||||
td::FileFd fd_;
|
||||
|
|
|
@ -32,22 +32,22 @@ namespace ton {
|
|||
namespace validator {
|
||||
|
||||
void RootDb::store_block_data(BlockHandle handle, td::Ref<BlockData> block, td::Promise<td::Unit> promise) {
|
||||
if (handle->moved_to_storage() || handle->moved_to_archive()) {
|
||||
if (handle->received()) {
|
||||
promise.set_value(td::Unit());
|
||||
return;
|
||||
}
|
||||
auto id = block_db_.get();
|
||||
|
||||
auto P = td::PromiseCreator::lambda([id, handle, promise = std::move(promise)](td::Result<FileHash> R) mutable {
|
||||
if (R.is_error()) {
|
||||
promise.set_error(R.move_as_error());
|
||||
} else {
|
||||
handle->set_received();
|
||||
td::actor::send_closure(id, &BlockDb::store_block_handle, std::move(handle), std::move(promise));
|
||||
}
|
||||
});
|
||||
auto P = td::PromiseCreator::lambda(
|
||||
[id = archive_db_.get(), handle, promise = std::move(promise)](td::Result<td::Unit> R) mutable {
|
||||
if (R.is_error()) {
|
||||
promise.set_error(R.move_as_error());
|
||||
} else {
|
||||
handle->set_received();
|
||||
td::actor::send_closure(id, &ArchiveManager::update_handle, std::move(handle), std::move(promise));
|
||||
}
|
||||
});
|
||||
|
||||
td::actor::send_closure(file_db_, &FileDb::store_file, FileDb::RefId{fileref::Block{handle->id()}}, block->data(),
|
||||
td::actor::send_closure(archive_db_, &ArchiveManager::add_file, handle, fileref::Block{handle->id()}, block->data(),
|
||||
std::move(P));
|
||||
}
|
||||
|
||||
|
@ -64,45 +64,34 @@ void RootDb::get_block_data(BlockHandle handle, td::Promise<td::Ref<BlockData>>
|
|||
}
|
||||
});
|
||||
|
||||
if (handle->moved_to_archive()) {
|
||||
td::actor::send_closure(new_archive_db_, &ArchiveManager::read, handle->unix_time(), handle->is_key_block(),
|
||||
FileDb::RefId{fileref::Block{handle->id()}}, std::move(P));
|
||||
} else {
|
||||
td::actor::send_closure(handle->moved_to_storage() ? old_archive_db_.get() : file_db_.get(), &FileDb::load_file,
|
||||
FileDb::RefId{fileref::Block{handle->id()}}, std::move(P));
|
||||
}
|
||||
td::actor::send_closure(archive_db_, &ArchiveManager::get_file, handle, fileref::Block{handle->id()}, std::move(P));
|
||||
}
|
||||
}
|
||||
|
||||
void RootDb::store_block_signatures(BlockHandle handle, td::Ref<BlockSignatureSet> data,
|
||||
td::Promise<td::Unit> promise) {
|
||||
if (handle->moved_to_storage() || handle->moved_to_archive()) {
|
||||
if (handle->inited_signatures() || handle->moved_to_archive()) {
|
||||
promise.set_value(td::Unit());
|
||||
return;
|
||||
}
|
||||
auto id = block_db_.get();
|
||||
|
||||
auto P = td::PromiseCreator::lambda([id, handle, promise = std::move(promise)](td::Result<FileHash> R) mutable {
|
||||
if (R.is_error()) {
|
||||
promise.set_error(R.move_as_error());
|
||||
} else {
|
||||
handle->set_signatures();
|
||||
td::actor::send_closure(id, &BlockDb::store_block_handle, std::move(handle), std::move(promise));
|
||||
}
|
||||
});
|
||||
|
||||
td::actor::send_closure(file_db_, &FileDb::store_file, FileDb::RefId{fileref::Signatures{handle->id()}},
|
||||
auto P = td::PromiseCreator::lambda(
|
||||
[id = archive_db_.get(), handle, promise = std::move(promise)](td::Result<td::Unit> R) mutable {
|
||||
if (R.is_error()) {
|
||||
promise.set_error(R.move_as_error());
|
||||
} else {
|
||||
handle->set_signatures();
|
||||
td::actor::send_closure(id, &ArchiveManager::update_handle, std::move(handle), std::move(promise));
|
||||
}
|
||||
});
|
||||
td::actor::send_closure(archive_db_, &ArchiveManager::add_temp_file_short, fileref::Signatures{handle->id()},
|
||||
data->serialize(), std::move(P));
|
||||
}
|
||||
|
||||
void RootDb::get_block_signatures(BlockHandle handle, td::Promise<td::Ref<BlockSignatureSet>> promise) {
|
||||
if (!handle->inited_signatures()) {
|
||||
if (!handle->inited_signatures() || handle->moved_to_archive()) {
|
||||
promise.set_error(td::Status::Error(ErrorCode::notready, "not in db"));
|
||||
} else {
|
||||
if (handle->moved_to_storage() || handle->moved_to_archive()) {
|
||||
promise.set_error(td::Status::Error(ErrorCode::error, "signatures already gc'd"));
|
||||
return;
|
||||
}
|
||||
auto P = td::PromiseCreator::lambda([promise = std::move(promise)](td::Result<td::BufferSlice> R) mutable {
|
||||
if (R.is_error()) {
|
||||
promise.set_error(R.move_as_error());
|
||||
|
@ -110,28 +99,28 @@ void RootDb::get_block_signatures(BlockHandle handle, td::Promise<td::Ref<BlockS
|
|||
promise.set_result(create_signature_set(R.move_as_ok()));
|
||||
}
|
||||
});
|
||||
td::actor::send_closure(file_db_, &FileDb::load_file, FileDb::RefId{fileref::Signatures{handle->id()}},
|
||||
td::actor::send_closure(archive_db_, &ArchiveManager::get_temp_file_short, fileref::Signatures{handle->id()},
|
||||
std::move(P));
|
||||
}
|
||||
}
|
||||
|
||||
void RootDb::store_block_proof(BlockHandle handle, td::Ref<Proof> proof, td::Promise<td::Unit> promise) {
|
||||
if (handle->moved_to_storage() || handle->moved_to_archive()) {
|
||||
if (handle->inited_proof()) {
|
||||
promise.set_value(td::Unit());
|
||||
return;
|
||||
}
|
||||
auto id = block_db_.get();
|
||||
|
||||
auto P = td::PromiseCreator::lambda([id, handle, promise = std::move(promise)](td::Result<FileHash> R) mutable {
|
||||
if (R.is_error()) {
|
||||
promise.set_error(R.move_as_error());
|
||||
} else {
|
||||
handle->set_proof();
|
||||
td::actor::send_closure(id, &BlockDb::store_block_handle, std::move(handle), std::move(promise));
|
||||
}
|
||||
});
|
||||
auto P = td::PromiseCreator::lambda(
|
||||
[id = archive_db_.get(), handle, promise = std::move(promise)](td::Result<td::Unit> R) mutable {
|
||||
if (R.is_error()) {
|
||||
promise.set_error(R.move_as_error());
|
||||
} else {
|
||||
handle->set_proof();
|
||||
td::actor::send_closure(id, &ArchiveManager::update_handle, std::move(handle), std::move(promise));
|
||||
}
|
||||
});
|
||||
|
||||
td::actor::send_closure(file_db_, &FileDb::store_file, FileDb::RefId{fileref::Proof{handle->id()}}, proof->data(),
|
||||
td::actor::send_closure(archive_db_, &ArchiveManager::add_file, handle, fileref::Proof{handle->id()}, proof->data(),
|
||||
std::move(P));
|
||||
}
|
||||
|
||||
|
@ -147,34 +136,27 @@ void RootDb::get_block_proof(BlockHandle handle, td::Promise<td::Ref<Proof>> pro
|
|||
promise.set_result(create_proof(id, R.move_as_ok()));
|
||||
}
|
||||
});
|
||||
if (handle->moved_to_archive()) {
|
||||
td::actor::send_closure(new_archive_db_, &ArchiveManager::read, handle->unix_time(), handle->is_key_block(),
|
||||
FileDb::RefId{fileref::Proof{handle->id()}}, std::move(P));
|
||||
} else {
|
||||
td::actor::send_closure(handle->moved_to_storage() ? old_archive_db_.get() : file_db_.get(), &FileDb::load_file,
|
||||
FileDb::RefId{fileref::Proof{handle->id()}}, std::move(P));
|
||||
}
|
||||
td::actor::send_closure(archive_db_, &ArchiveManager::get_file, handle, fileref::Proof{handle->id()}, std::move(P));
|
||||
}
|
||||
}
|
||||
|
||||
void RootDb::store_block_proof_link(BlockHandle handle, td::Ref<ProofLink> proof, td::Promise<td::Unit> promise) {
|
||||
if (handle->moved_to_storage() || handle->moved_to_archive()) {
|
||||
if (handle->inited_proof_link()) {
|
||||
promise.set_value(td::Unit());
|
||||
return;
|
||||
}
|
||||
auto id = block_db_.get();
|
||||
|
||||
auto P = td::PromiseCreator::lambda([id, handle, promise = std::move(promise)](td::Result<FileHash> R) mutable {
|
||||
if (R.is_error()) {
|
||||
promise.set_error(R.move_as_error());
|
||||
} else {
|
||||
handle->set_proof_link();
|
||||
td::actor::send_closure(id, &BlockDb::store_block_handle, std::move(handle), std::move(promise));
|
||||
}
|
||||
});
|
||||
|
||||
td::actor::send_closure(file_db_, &FileDb::store_file, FileDb::RefId{fileref::ProofLink{handle->id()}}, proof->data(),
|
||||
std::move(P));
|
||||
auto P = td::PromiseCreator::lambda(
|
||||
[id = archive_db_.get(), handle, promise = std::move(promise)](td::Result<td::Unit> R) mutable {
|
||||
if (R.is_error()) {
|
||||
promise.set_error(R.move_as_error());
|
||||
} else {
|
||||
handle->set_proof_link();
|
||||
td::actor::send_closure(id, &ArchiveManager::update_handle, std::move(handle), std::move(promise));
|
||||
}
|
||||
});
|
||||
td::actor::send_closure(archive_db_, &ArchiveManager::add_file, handle, fileref::ProofLink{handle->id()},
|
||||
proof->data(), std::move(P));
|
||||
}
|
||||
|
||||
void RootDb::get_block_proof_link(BlockHandle handle, td::Promise<td::Ref<ProofLink>> promise) {
|
||||
|
@ -189,13 +171,8 @@ void RootDb::get_block_proof_link(BlockHandle handle, td::Promise<td::Ref<ProofL
|
|||
promise.set_result(create_proof_link(id, R.move_as_ok()));
|
||||
}
|
||||
});
|
||||
if (handle->moved_to_archive()) {
|
||||
td::actor::send_closure(new_archive_db_, &ArchiveManager::read, handle->unix_time(), handle->is_key_block(),
|
||||
FileDb::RefId{fileref::ProofLink{handle->id()}}, std::move(P));
|
||||
} else {
|
||||
td::actor::send_closure(handle->moved_to_storage() ? old_archive_db_.get() : file_db_.get(), &FileDb::load_file,
|
||||
FileDb::RefId{fileref::ProofLink{handle->id()}}, std::move(P));
|
||||
}
|
||||
td::actor::send_closure(archive_db_, &ArchiveManager::get_file, handle, fileref::ProofLink{handle->id()},
|
||||
std::move(P));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -204,16 +181,16 @@ void RootDb::store_block_candidate(BlockCandidate candidate, td::Promise<td::Uni
|
|||
PublicKey{pubkeys::Ed25519{candidate.pubkey.as_bits256()}}.tl(), create_tl_block_id(candidate.id),
|
||||
std::move(candidate.data), std::move(candidate.collated_data));
|
||||
|
||||
auto P = td::PromiseCreator::lambda([promise = std::move(promise)](td::Result<FileHash> R) mutable {
|
||||
auto P = td::PromiseCreator::lambda([promise = std::move(promise)](td::Result<td::Unit> R) mutable {
|
||||
if (R.is_error()) {
|
||||
promise.set_error(R.move_as_error());
|
||||
} else {
|
||||
promise.set_value(td::Unit());
|
||||
}
|
||||
});
|
||||
td::actor::send_closure(file_db_, &FileDb::store_file,
|
||||
FileDb::RefId{fileref::Candidate{PublicKey{pubkeys::Ed25519{candidate.pubkey.as_bits256()}},
|
||||
candidate.id, candidate.collated_file_hash}},
|
||||
td::actor::send_closure(archive_db_, &ArchiveManager::add_temp_file_short,
|
||||
fileref::Candidate{PublicKey{pubkeys::Ed25519{candidate.pubkey.as_bits256()}}, candidate.id,
|
||||
candidate.collated_file_hash},
|
||||
std::move(obj), std::move(P));
|
||||
}
|
||||
|
||||
|
@ -234,18 +211,18 @@ void RootDb::get_block_candidate(PublicKey source, BlockIdExt id, FileHash colla
|
|||
std::move(val->collated_data_)});
|
||||
}
|
||||
});
|
||||
td::actor::send_closure(file_db_, &FileDb::load_file,
|
||||
FileDb::RefId{fileref::Candidate{source, id, collated_data_file_hash}}, std::move(P));
|
||||
td::actor::send_closure(archive_db_, &ArchiveManager::get_temp_file_short,
|
||||
fileref::Candidate{source, id, collated_data_file_hash}, std::move(P));
|
||||
}
|
||||
|
||||
void RootDb::store_block_state(BlockHandle handle, td::Ref<ShardState> state,
|
||||
td::Promise<td::Ref<ShardState>> promise) {
|
||||
if (handle->moved_to_storage() || handle->moved_to_archive()) {
|
||||
if (handle->moved_to_archive()) {
|
||||
promise.set_value(std::move(state));
|
||||
return;
|
||||
}
|
||||
if (!handle->inited_state_boc()) {
|
||||
auto P = td::PromiseCreator::lambda([b = block_db_.get(), root_hash = state->root_hash(), handle,
|
||||
auto P = td::PromiseCreator::lambda([b = archive_db_.get(), root_hash = state->root_hash(), handle,
|
||||
promise = std::move(promise)](td::Result<td::Ref<vm::DataCell>> R) mutable {
|
||||
if (R.is_error()) {
|
||||
promise.set_error(R.move_as_error());
|
||||
|
@ -262,7 +239,7 @@ void RootDb::store_block_state(BlockHandle handle, td::Ref<ShardState> state,
|
|||
promise.set_value(std::move(state));
|
||||
});
|
||||
|
||||
td::actor::send_closure(b, &BlockDb::store_block_handle, std::move(handle), std::move(P));
|
||||
td::actor::send_closure(b, &ArchiveManager::update_handle, std::move(handle), std::move(P));
|
||||
}
|
||||
});
|
||||
td::actor::send_closure(cell_db_, &CellDb::store_cell, handle->id(), state->root_cell(), std::move(P));
|
||||
|
@ -295,83 +272,46 @@ void RootDb::get_block_state(BlockHandle handle, td::Promise<td::Ref<ShardState>
|
|||
|
||||
void RootDb::store_persistent_state_file(BlockIdExt block_id, BlockIdExt masterchain_block_id, td::BufferSlice state,
|
||||
td::Promise<td::Unit> promise) {
|
||||
auto id = block_db_.get();
|
||||
|
||||
auto P = td::PromiseCreator::lambda([id, promise = std::move(promise)](td::Result<FileHash> R) mutable {
|
||||
if (R.is_error()) {
|
||||
promise.set_error(R.move_as_error());
|
||||
} else {
|
||||
promise.set_value(td::Unit());
|
||||
}
|
||||
});
|
||||
|
||||
td::actor::send_closure(old_archive_db_, &FileDb::store_file,
|
||||
FileDb::RefId{fileref::PersistentState{block_id, masterchain_block_id}}, std::move(state),
|
||||
std::move(P));
|
||||
td::actor::send_closure(archive_db_, &ArchiveManager::add_persistent_state, block_id, masterchain_block_id,
|
||||
std::move(state), std::move(promise));
|
||||
}
|
||||
|
||||
void RootDb::get_persistent_state_file(BlockIdExt block_id, BlockIdExt masterchain_block_id,
|
||||
td::Promise<td::BufferSlice> promise) {
|
||||
td::actor::send_closure(old_archive_db_, &FileDb::load_file,
|
||||
FileDb::RefId{fileref::PersistentState{block_id, masterchain_block_id}}, std::move(promise));
|
||||
td::actor::send_closure(archive_db_, &ArchiveManager::get_persistent_state, block_id, masterchain_block_id,
|
||||
std::move(promise));
|
||||
}
|
||||
|
||||
void RootDb::get_persistent_state_file_slice(BlockIdExt block_id, BlockIdExt masterchain_block_id, td::int64 offset,
|
||||
td::int64 max_size, td::Promise<td::BufferSlice> promise) {
|
||||
td::actor::send_closure(old_archive_db_, &FileDb::load_file_slice,
|
||||
FileDb::RefId{fileref::PersistentState{block_id, masterchain_block_id}}, offset, max_size,
|
||||
std::move(promise));
|
||||
td::actor::send_closure(archive_db_, &ArchiveManager::get_persistent_state_slice, block_id, masterchain_block_id,
|
||||
offset, max_size, std::move(promise));
|
||||
}
|
||||
|
||||
void RootDb::check_persistent_state_file_exists(BlockIdExt block_id, BlockIdExt masterchain_block_id,
|
||||
td::Promise<bool> promise) {
|
||||
td::actor::send_closure(old_archive_db_, &FileDb::check_file,
|
||||
FileDb::RefId{fileref::PersistentState{block_id, masterchain_block_id}}, std::move(promise));
|
||||
td::actor::send_closure(archive_db_, &ArchiveManager::check_persistent_state, block_id, masterchain_block_id,
|
||||
std::move(promise));
|
||||
}
|
||||
|
||||
void RootDb::store_zero_state_file(BlockIdExt block_id, td::BufferSlice state, td::Promise<td::Unit> promise) {
|
||||
auto id = block_db_.get();
|
||||
|
||||
auto P = td::PromiseCreator::lambda([id, promise = std::move(promise)](td::Result<FileHash> R) mutable {
|
||||
if (R.is_error()) {
|
||||
promise.set_error(R.move_as_error());
|
||||
} else {
|
||||
promise.set_value(td::Unit());
|
||||
}
|
||||
});
|
||||
|
||||
td::actor::send_closure(old_archive_db_, &FileDb::store_file, FileDb::RefId{fileref::ZeroState{block_id}},
|
||||
std::move(state), std::move(P));
|
||||
td::actor::send_closure(archive_db_, &ArchiveManager::add_zero_state, block_id, std::move(state), std::move(promise));
|
||||
}
|
||||
|
||||
void RootDb::get_zero_state_file(BlockIdExt block_id, td::Promise<td::BufferSlice> promise) {
|
||||
td::actor::send_closure(old_archive_db_, &FileDb::load_file, FileDb::RefId{fileref::ZeroState{block_id}},
|
||||
std::move(promise));
|
||||
td::actor::send_closure(archive_db_, &ArchiveManager::get_zero_state, block_id, std::move(promise));
|
||||
}
|
||||
|
||||
void RootDb::check_zero_state_file_exists(BlockIdExt block_id, td::Promise<bool> promise) {
|
||||
td::actor::send_closure(old_archive_db_, &FileDb::check_file, FileDb::RefId{fileref::ZeroState{block_id}},
|
||||
std::move(promise));
|
||||
td::actor::send_closure(archive_db_, &ArchiveManager::check_zero_state, block_id, std::move(promise));
|
||||
}
|
||||
|
||||
void RootDb::store_block_handle(BlockHandle handle, td::Promise<td::Unit> promise) {
|
||||
if (handle->moved_to_archive()) {
|
||||
td::actor::send_closure(new_archive_db_, &ArchiveManager::write_handle, std::move(handle), std::move(promise));
|
||||
} else {
|
||||
td::actor::send_closure(block_db_, &BlockDb::store_block_handle, std::move(handle), std::move(promise));
|
||||
}
|
||||
td::actor::send_closure(archive_db_, &ArchiveManager::update_handle, std::move(handle), std::move(promise));
|
||||
}
|
||||
|
||||
void RootDb::get_block_handle(BlockIdExt id, td::Promise<BlockHandle> promise) {
|
||||
auto P = td::PromiseCreator::lambda(
|
||||
[db = block_db_.get(), id, promise = std::move(promise)](td::Result<BlockHandle> R) mutable {
|
||||
if (R.is_error()) {
|
||||
td::actor::send_closure(db, &BlockDb::get_block_handle, id, std::move(promise));
|
||||
} else {
|
||||
promise.set_value(R.move_as_ok());
|
||||
}
|
||||
});
|
||||
td::actor::send_closure(new_archive_db_, &ArchiveManager::read_handle, id, std::move(P));
|
||||
td::actor::send_closure(archive_db_, &ArchiveManager::get_handle, id, std::move(promise));
|
||||
}
|
||||
|
||||
void RootDb::try_get_static_file(FileHash file_hash, td::Promise<td::BufferSlice> promise) {
|
||||
|
@ -379,24 +319,20 @@ void RootDb::try_get_static_file(FileHash file_hash, td::Promise<td::BufferSlice
|
|||
}
|
||||
|
||||
void RootDb::apply_block(BlockHandle handle, td::Promise<td::Unit> promise) {
|
||||
if (handle->id().id.seqno == 0) {
|
||||
promise.set_value(td::Unit());
|
||||
} else {
|
||||
td::actor::send_closure(lt_db_, &LtDb::add_new_block, handle->id(), handle->logical_time(), handle->unix_time(),
|
||||
std::move(promise));
|
||||
}
|
||||
td::actor::create_actor<BlockArchiver>("archiver", std::move(handle), archive_db_.get(), std::move(promise))
|
||||
.release();
|
||||
}
|
||||
|
||||
void RootDb::get_block_by_lt(AccountIdPrefixFull account, LogicalTime lt, td::Promise<BlockIdExt> promise) {
|
||||
td::actor::send_closure(lt_db_, &LtDb::get_block_by_lt, account, lt, std::move(promise));
|
||||
void RootDb::get_block_by_lt(AccountIdPrefixFull account, LogicalTime lt, td::Promise<BlockHandle> promise) {
|
||||
td::actor::send_closure(archive_db_, &ArchiveManager::get_block_by_lt, account, lt, std::move(promise));
|
||||
}
|
||||
|
||||
void RootDb::get_block_by_unix_time(AccountIdPrefixFull account, UnixTime ts, td::Promise<BlockIdExt> promise) {
|
||||
td::actor::send_closure(lt_db_, &LtDb::get_block_by_unix_time, account, ts, std::move(promise));
|
||||
void RootDb::get_block_by_unix_time(AccountIdPrefixFull account, UnixTime ts, td::Promise<BlockHandle> promise) {
|
||||
td::actor::send_closure(archive_db_, &ArchiveManager::get_block_by_unix_time, account, ts, std::move(promise));
|
||||
}
|
||||
|
||||
void RootDb::get_block_by_seqno(AccountIdPrefixFull account, BlockSeqno seqno, td::Promise<BlockIdExt> promise) {
|
||||
td::actor::send_closure(lt_db_, &LtDb::get_block_by_seqno, account, seqno, std::move(promise));
|
||||
void RootDb::get_block_by_seqno(AccountIdPrefixFull account, BlockSeqno seqno, td::Promise<BlockHandle> promise) {
|
||||
td::actor::send_closure(archive_db_, &ArchiveManager::get_block_by_seqno, account, seqno, std::move(promise));
|
||||
}
|
||||
|
||||
void RootDb::update_init_masterchain_block(BlockIdExt block, td::Promise<td::Unit> promise) {
|
||||
|
@ -451,19 +387,13 @@ void RootDb::get_hardforks(td::Promise<std::vector<BlockIdExt>> promise) {
|
|||
|
||||
void RootDb::start_up() {
|
||||
cell_db_ = td::actor::create_actor<CellDb>("celldb", actor_id(this), root_path_ + "/celldb/");
|
||||
block_db_ = td::actor::create_actor<BlockDb>("blockdb", actor_id(this), root_path_ + "/blockdb/");
|
||||
file_db_ = td::actor::create_actor<FileDb>("filedb", actor_id(this), root_path_ + "/files/", depth_, false);
|
||||
old_archive_db_ =
|
||||
td::actor::create_actor<FileDb>("filedbarchive", actor_id(this), root_path_ + "/archive/", depth_, true);
|
||||
lt_db_ = td::actor::create_actor<LtDb>("ltdb", actor_id(this), root_path_ + "/ltdb/");
|
||||
state_db_ = td::actor::create_actor<StateDb>("statedb", actor_id(this), root_path_ + "/state/");
|
||||
static_files_db_ = td::actor::create_actor<StaticFilesDb>("staticfilesdb", actor_id(this), root_path_ + "/static/");
|
||||
new_archive_db_ = td::actor::create_actor<ArchiveManager>("archivemanager", root_path_ + "/archive/");
|
||||
archive_db_ = td::actor::create_actor<ArchiveManager>("archive", actor_id(this), root_path_);
|
||||
}
|
||||
|
||||
void RootDb::archive(BlockIdExt block_id, td::Promise<td::Unit> promise) {
|
||||
td::actor::create_actor<BlockArchiver>("archiveblock", block_id, actor_id(this), file_db_.get(),
|
||||
old_archive_db_.get(), new_archive_db_.get(), std::move(promise))
|
||||
void RootDb::archive(BlockHandle handle, td::Promise<td::Unit> promise) {
|
||||
td::actor::create_actor<BlockArchiver>("archiveblock", std::move(handle), archive_db_.get(), std::move(promise))
|
||||
.release();
|
||||
}
|
||||
|
||||
|
@ -475,57 +405,86 @@ void RootDb::allow_block_gc(BlockIdExt block_id, td::Promise<bool> promise) {
|
|||
td::actor::send_closure(validator_manager_, &ValidatorManager::allow_block_info_gc, block_id, std::move(promise));
|
||||
}
|
||||
|
||||
void RootDb::allow_gc(FileDb::RefId ref_id, bool is_archive, td::Promise<bool> promise) {
|
||||
ref_id.visit(
|
||||
td::overloaded([&](const fileref::Empty &key) { UNREACHABLE(); },
|
||||
[&](const fileref::Block &key) {
|
||||
td::actor::send_closure(validator_manager_, &ValidatorManager::allow_block_data_gc, key.block_id,
|
||||
is_archive, std::move(promise));
|
||||
},
|
||||
[&](const fileref::ZeroState &key) {
|
||||
td::actor::send_closure(validator_manager_, &ValidatorManager::allow_zero_state_file_gc,
|
||||
key.block_id, std::move(promise));
|
||||
},
|
||||
[&](const fileref::PersistentState &key) {
|
||||
CHECK(is_archive);
|
||||
td::actor::send_closure(validator_manager_, &ValidatorManager::allow_persistent_state_file_gc,
|
||||
key.block_id, key.masterchain_block_id, std::move(promise));
|
||||
},
|
||||
[&](const fileref::Proof &key) {
|
||||
td::actor::send_closure(validator_manager_, &ValidatorManager::allow_block_proof_gc,
|
||||
key.block_id, is_archive, std::move(promise));
|
||||
},
|
||||
[&](const fileref::ProofLink &key) {
|
||||
td::actor::send_closure(validator_manager_, &ValidatorManager::allow_block_proof_link_gc,
|
||||
key.block_id, is_archive, std::move(promise));
|
||||
},
|
||||
[&](const fileref::Signatures &key) {
|
||||
CHECK(!is_archive);
|
||||
td::actor::send_closure(validator_manager_, &ValidatorManager::allow_block_signatures_gc,
|
||||
key.block_id, std::move(promise));
|
||||
},
|
||||
[&](const fileref::Candidate &key) {
|
||||
CHECK(!is_archive);
|
||||
td::actor::send_closure(validator_manager_, &ValidatorManager::allow_block_candidate_gc,
|
||||
key.block_id, std::move(promise));
|
||||
},
|
||||
[&](const fileref::BlockInfo &key) { UNREACHABLE(); }));
|
||||
}
|
||||
|
||||
void RootDb::prepare_stats(td::Promise<std::vector<std::pair<std::string, std::string>>> promise) {
|
||||
auto merger = StatsMerger::create(std::move(promise));
|
||||
|
||||
td::actor::send_closure(file_db_, &FileDb::prepare_stats, merger.make_promise("filedb."));
|
||||
td::actor::send_closure(old_archive_db_, &FileDb::prepare_stats, merger.make_promise("archivedb."));
|
||||
}
|
||||
|
||||
void RootDb::truncate(td::Ref<MasterchainState> state, td::Promise<td::Unit> promise) {
|
||||
td::MultiPromise mp;
|
||||
auto ig = mp.init_guard();
|
||||
ig.add_promise(std::move(promise));
|
||||
}
|
||||
|
||||
td::actor::send_closure(lt_db_, &LtDb::truncate, state, ig.get_promise());
|
||||
td::actor::send_closure(block_db_, &BlockDb::truncate, state, ig.get_promise());
|
||||
void RootDb::add_key_block_proof(td::Ref<Proof> proof, td::Promise<td::Unit> promise) {
|
||||
auto i = proof->get_basic_header_info().move_as_ok();
|
||||
td::actor::send_closure(archive_db_, &ArchiveManager::add_key_block_proof, i.utime, proof->block_id().seqno(),
|
||||
i.end_lt, fileref::Proof{proof->block_id()}, proof->data(), std::move(promise));
|
||||
}
|
||||
|
||||
void RootDb::add_key_block_proof_link(td::Ref<ProofLink> proof, td::Promise<td::Unit> promise) {
|
||||
auto i = proof->get_basic_header_info().move_as_ok();
|
||||
td::actor::send_closure(archive_db_, &ArchiveManager::add_key_block_proof, i.utime, proof->block_id().seqno(),
|
||||
i.end_lt, fileref::ProofLink{proof->block_id()}, proof->data(), std::move(promise));
|
||||
}
|
||||
void RootDb::get_key_block_proof(BlockIdExt block_id, td::Promise<td::Ref<Proof>> promise) {
|
||||
auto P = td::PromiseCreator::lambda([block_id, promise = std::move(promise)](td::Result<td::BufferSlice> R) mutable {
|
||||
if (R.is_error()) {
|
||||
promise.set_error(R.move_as_error());
|
||||
} else {
|
||||
promise.set_result(create_proof(block_id, R.move_as_ok()));
|
||||
}
|
||||
});
|
||||
td::actor::send_closure(archive_db_, &ArchiveManager::get_key_block_proof, fileref::Proof{block_id}, std::move(P));
|
||||
}
|
||||
void RootDb::get_key_block_proof_link(BlockIdExt block_id, td::Promise<td::Ref<ProofLink>> promise) {
|
||||
auto P = td::PromiseCreator::lambda([block_id, promise = std::move(promise)](td::Result<td::BufferSlice> R) mutable {
|
||||
if (R.is_error()) {
|
||||
promise.set_error(R.move_as_error());
|
||||
} else {
|
||||
promise.set_result(create_proof_link(block_id, R.move_as_ok()));
|
||||
}
|
||||
});
|
||||
td::actor::send_closure(archive_db_, &ArchiveManager::get_key_block_proof, fileref::Proof{block_id}, std::move(P));
|
||||
}
|
||||
|
||||
void RootDb::check_key_block_proof_exists(BlockIdExt block_id, td::Promise<bool> promise) {
|
||||
auto P = td::PromiseCreator::lambda([promise = std::move(promise)](td::Result<td::BufferSlice> R) mutable {
|
||||
if (R.is_error()) {
|
||||
promise.set_result(false);
|
||||
} else {
|
||||
promise.set_result(true);
|
||||
}
|
||||
});
|
||||
td::actor::send_closure(archive_db_, &ArchiveManager::get_key_block_proof, fileref::Proof{block_id}, std::move(P));
|
||||
}
|
||||
void RootDb::check_key_block_proof_link_exists(BlockIdExt block_id, td::Promise<bool> promise) {
|
||||
auto P = td::PromiseCreator::lambda([promise = std::move(promise)](td::Result<td::BufferSlice> R) mutable {
|
||||
if (R.is_error()) {
|
||||
promise.set_result(false);
|
||||
} else {
|
||||
promise.set_result(true);
|
||||
}
|
||||
});
|
||||
td::actor::send_closure(archive_db_, &ArchiveManager::get_key_block_proof, fileref::ProofLink{block_id},
|
||||
std::move(P));
|
||||
}
|
||||
|
||||
void RootDb::get_archive_id(BlockSeqno masterchain_seqno, td::Promise<td::uint64> promise) {
|
||||
td::actor::send_closure(archive_db_, &ArchiveManager::get_archive_id, masterchain_seqno, std::move(promise));
|
||||
}
|
||||
|
||||
void RootDb::get_archive_slice(td::uint64 archive_id, td::uint64 offset, td::uint32 limit,
|
||||
td::Promise<td::BufferSlice> promise) {
|
||||
td::actor::send_closure(archive_db_, &ArchiveManager::get_archive_slice, archive_id, offset, limit,
|
||||
std::move(promise));
|
||||
}
|
||||
|
||||
void RootDb::set_async_mode(bool mode, td::Promise<td::Unit> promise) {
|
||||
td::actor::send_closure(archive_db_, &ArchiveManager::set_async_mode, mode, std::move(promise));
|
||||
}
|
||||
|
||||
void RootDb::run_gc(UnixTime ts) {
|
||||
td::actor::send_closure(archive_db_, &ArchiveManager::run_gc, ts);
|
||||
}
|
||||
|
||||
} // namespace validator
|
||||
|
|
|
@ -22,13 +22,10 @@
|
|||
#include "td/db/KeyValueAsync.h"
|
||||
#include "ton/ton-types.h"
|
||||
|
||||
#include "blockdb.hpp"
|
||||
#include "celldb.hpp"
|
||||
#include "filedb.hpp"
|
||||
#include "ltdb.hpp"
|
||||
#include "statedb.hpp"
|
||||
#include "staticfilesdb.hpp"
|
||||
#include "archive-db.hpp"
|
||||
#include "archive-manager.hpp"
|
||||
|
||||
namespace ton {
|
||||
|
||||
|
@ -85,9 +82,9 @@ class RootDb : public Db {
|
|||
void try_get_static_file(FileHash file_hash, td::Promise<td::BufferSlice> promise) override;
|
||||
|
||||
void apply_block(BlockHandle handle, td::Promise<td::Unit> promise) override;
|
||||
void get_block_by_lt(AccountIdPrefixFull account, LogicalTime lt, td::Promise<BlockIdExt> promise) override;
|
||||
void get_block_by_unix_time(AccountIdPrefixFull account, UnixTime ts, td::Promise<BlockIdExt> promise) override;
|
||||
void get_block_by_seqno(AccountIdPrefixFull account, BlockSeqno seqno, td::Promise<BlockIdExt> promise) override;
|
||||
void get_block_by_lt(AccountIdPrefixFull account, LogicalTime lt, td::Promise<BlockHandle> promise) override;
|
||||
void get_block_by_unix_time(AccountIdPrefixFull account, UnixTime ts, td::Promise<BlockHandle> promise) override;
|
||||
void get_block_by_seqno(AccountIdPrefixFull account, BlockSeqno seqno, td::Promise<BlockHandle> promise) override;
|
||||
|
||||
void update_init_masterchain_block(BlockIdExt block, td::Promise<td::Unit> promise) override;
|
||||
void get_init_masterchain_block(td::Promise<BlockIdExt> promise) override;
|
||||
|
@ -108,16 +105,30 @@ class RootDb : public Db {
|
|||
void update_hardforks(std::vector<BlockIdExt> blocks, td::Promise<td::Unit> promise) override;
|
||||
void get_hardforks(td::Promise<std::vector<BlockIdExt>> promise) override;
|
||||
|
||||
void archive(BlockIdExt block_id, td::Promise<td::Unit> promise) override;
|
||||
void archive(BlockHandle handle, td::Promise<td::Unit> promise) override;
|
||||
|
||||
void allow_state_gc(BlockIdExt block_id, td::Promise<bool> promise);
|
||||
void allow_block_gc(BlockIdExt block_id, td::Promise<bool> promise);
|
||||
void allow_gc(FileDb::RefId ref_id, bool is_archive, td::Promise<bool> promise);
|
||||
//void allow_gc(FileDb::RefId ref_id, bool is_archive, td::Promise<bool> promise);
|
||||
|
||||
void prepare_stats(td::Promise<std::vector<std::pair<std::string, std::string>>> promise) override;
|
||||
|
||||
void truncate(td::Ref<MasterchainState> state, td::Promise<td::Unit> promise) override;
|
||||
|
||||
void add_key_block_proof(td::Ref<Proof> proof, td::Promise<td::Unit> promise) override;
|
||||
void add_key_block_proof_link(td::Ref<ProofLink> proof_link, td::Promise<td::Unit> promise) override;
|
||||
void get_key_block_proof(BlockIdExt block_id, td::Promise<td::Ref<Proof>> promise) override;
|
||||
void get_key_block_proof_link(BlockIdExt block_id, td::Promise<td::Ref<ProofLink>> promise) override;
|
||||
void check_key_block_proof_exists(BlockIdExt block_id, td::Promise<bool> promise) override;
|
||||
void check_key_block_proof_link_exists(BlockIdExt block_id, td::Promise<bool> promise) override;
|
||||
|
||||
void get_archive_id(BlockSeqno masterchain_seqno, td::Promise<td::uint64> promise) override;
|
||||
void get_archive_slice(td::uint64 archive_id, td::uint64 offset, td::uint32 limit,
|
||||
td::Promise<td::BufferSlice> promise) override;
|
||||
void set_async_mode(bool mode, td::Promise<td::Unit> promise) override;
|
||||
|
||||
void run_gc(UnixTime ts) override;
|
||||
|
||||
private:
|
||||
td::actor::ActorId<ValidatorManager> validator_manager_;
|
||||
|
||||
|
@ -125,13 +136,9 @@ class RootDb : public Db {
|
|||
td::uint32 depth_;
|
||||
|
||||
td::actor::ActorOwn<CellDb> cell_db_;
|
||||
td::actor::ActorOwn<BlockDb> block_db_;
|
||||
td::actor::ActorOwn<FileDb> file_db_;
|
||||
td::actor::ActorOwn<FileDb> old_archive_db_;
|
||||
td::actor::ActorOwn<LtDb> lt_db_;
|
||||
td::actor::ActorOwn<StateDb> state_db_;
|
||||
td::actor::ActorOwn<StaticFilesDb> static_files_db_;
|
||||
td::actor::ActorOwn<ArchiveManager> new_archive_db_;
|
||||
td::actor::ActorOwn<ArchiveManager> archive_db_;
|
||||
};
|
||||
|
||||
} // namespace validator
|
||||
|
|
|
@ -222,6 +222,22 @@ StateDb::StateDb(td::actor::ActorId<RootDb> root_db, std::string db_path) : root
|
|||
|
||||
void StateDb::start_up() {
|
||||
kv_ = std::make_shared<td::RocksDb>(td::RocksDb::open(db_path_).move_as_ok());
|
||||
|
||||
std::string value;
|
||||
auto R = kv_->get(create_serialize_tl_object<ton_api::db_state_key_dbVersion>(), value);
|
||||
R.ensure();
|
||||
if (R.move_as_ok() == td::KeyValue::GetStatus::Ok) {
|
||||
auto F = fetch_tl_object<ton_api::db_state_dbVersion>(value, true);
|
||||
F.ensure();
|
||||
auto f = F.move_as_ok();
|
||||
CHECK(f->version_ == 2);
|
||||
} else {
|
||||
kv_->begin_transaction().ensure();
|
||||
kv_->set(create_serialize_tl_object<ton_api::db_state_key_dbVersion>(),
|
||||
create_serialize_tl_object<ton_api::db_state_dbVersion>(2))
|
||||
.ensure();
|
||||
kv_->commit_transaction().ensure();
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace validator
|
||||
|
|
|
@ -50,6 +50,9 @@ class StateDb : public td::actor::Actor {
|
|||
void update_hardforks(std::vector<BlockIdExt> blocks, td::Promise<td::Unit> promise);
|
||||
void get_hardforks(td::Promise<std::vector<BlockIdExt>> promise);
|
||||
|
||||
void update_db_version(td::uint32 version, td::Promise<td::Unit> promise);
|
||||
void get_db_version(td::Promise<td::uint32> promise);
|
||||
|
||||
StateDb(td::actor::ActorId<RootDb> root_db, std::string path);
|
||||
|
||||
void start_up() override;
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue