1
0
Fork 0
mirror of https://github.com/ton-blockchain/ton synced 2025-03-09 15:40:10 +00:00

Add cache for some LS requests (#893)

* Cache runSmcMethod queries to LS

* Drop duplicate sendMessage

* Drop sendMessage cache once a minute

---------

Co-authored-by: SpyCheese <mikle98@yandex.ru>
This commit is contained in:
EmelyanenkoK 2024-02-07 15:45:51 +03:00 committed by GitHub
parent 12c1b1a2e6
commit 79c48ebbba
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
7 changed files with 185 additions and 23 deletions

View file

@ -43,6 +43,7 @@ set(VALIDATOR_HEADERS
fabric.h
interfaces/db.h
interfaces/external-message.h
interfaces/liteserver.h
interfaces/proof.h
interfaces/shard.h
interfaces/signature-set.h

View file

@ -32,6 +32,7 @@ set(TON_VALIDATOR_SOURCE
external-message.hpp
ihr-message.hpp
liteserver.hpp
liteserver-cache.hpp
message-queue.hpp
proof.hpp
shard.hpp

View file

@ -34,6 +34,7 @@
#include "ton/ton-io.hpp"
#include "liteserver.hpp"
#include "validator/fabric.h"
#include "liteserver-cache.hpp"
namespace ton {
@ -46,7 +47,7 @@ td::actor::ActorOwn<Db> create_db_actor(td::actor::ActorId<ValidatorManager> man
td::actor::ActorOwn<LiteServerCache> create_liteserver_cache_actor(td::actor::ActorId<ValidatorManager> manager,
std::string db_root) {
return td::actor::create_actor<LiteServerCache>("cache");
return td::actor::create_actor<LiteServerCacheImpl>("cache");
}
td::Result<td::Ref<BlockData>> create_block(BlockIdExt block_id, td::BufferSlice data) {
@ -244,7 +245,7 @@ void run_collate_hardfork(ShardIdFull shard, const BlockIdExt& min_masterchain_b
void run_liteserver_query(td::BufferSlice data, td::actor::ActorId<ValidatorManager> manager,
td::actor::ActorId<LiteServerCache> cache, td::Promise<td::BufferSlice> promise) {
LiteQuery::run_query(std::move(data), std::move(manager), std::move(promise));
LiteQuery::run_query(std::move(data), std::move(manager), std::move(cache), std::move(promise));
}
void run_fetch_account_state(WorkchainId wc, StdSmcAddress addr, td::actor::ActorId<ValidatorManager> manager,

View file

@ -0,0 +1,112 @@
/*
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/>.
*/
#pragma once
#include "interfaces/liteserver.h"
#include <map>
namespace ton::validator {
class LiteServerCacheImpl : public LiteServerCache {
public:
void start_up() override {
alarm();
}
void alarm() override {
alarm_timestamp() = td::Timestamp::in(60.0);
if (queries_cnt_ > 0 || !send_message_cache_.empty()) {
LOG(WARNING) << "LS Cache stats: " << queries_cnt_ << " queries, " << queries_hit_cnt_ << " hits; "
<< cache_.size() << " entries, size=" << total_size_ << "/" << MAX_CACHE_SIZE << "; "
<< send_message_cache_.size() << " different sendMessage queries, " << send_message_error_cnt_
<< " duplicates";
queries_cnt_ = 0;
queries_hit_cnt_ = 0;
send_message_cache_.clear();
send_message_error_cnt_ = 0;
}
}
void lookup(td::Bits256 key, td::Promise<td::BufferSlice> promise) override {
++queries_cnt_;
auto it = cache_.find(key);
if (it == cache_.end()) {
promise.set_error(td::Status::Error("not found"));
return;
}
++queries_hit_cnt_;
auto entry = it->second.get();
entry->remove();
lru_.put(entry);
promise.set_value(entry->value_.clone());
}
void update(td::Bits256 key, td::BufferSlice value) override {
std::unique_ptr<CacheEntry> &entry = cache_[key];
if (entry == nullptr) {
entry = std::make_unique<CacheEntry>(key, std::move(value));
} else {
total_size_ -= entry->size();
entry->value_ = std::move(value);
entry->remove();
}
lru_.put(entry.get());
total_size_ += entry->size();
while (total_size_ > MAX_CACHE_SIZE) {
auto to_remove = (CacheEntry *)lru_.get();
CHECK(to_remove);
total_size_ -= to_remove->size();
to_remove->remove();
cache_.erase(to_remove->key_);
}
}
void process_send_message(td::Bits256 key, td::Promise<td::Unit> promise) override {
if (send_message_cache_.insert(key).second) {
promise.set_result(td::Unit());
} else {
++send_message_error_cnt_;
promise.set_error(td::Status::Error("duplicate message"));
}
}
private:
struct CacheEntry : public td::ListNode {
explicit CacheEntry(td::Bits256 key, td::BufferSlice value) : key_(key), value_(std::move(value)) {
}
td::Bits256 key_;
td::BufferSlice value_;
size_t size() const {
return value_.size() + 32 * 2;
}
};
std::map<td::Bits256, std::unique_ptr<CacheEntry>> cache_;
td::ListNode lru_;
size_t total_size_ = 0;
size_t queries_cnt_ = 0, queries_hit_cnt_ = 0;
std::set<td::Bits256> send_message_cache_;
size_t send_message_error_cnt_ = 0;
static const size_t MAX_CACHE_SIZE = 64 << 20;
};
} // namespace ton::validator

View file

@ -54,8 +54,11 @@ td::int32 get_tl_tag(td::Slice slice) {
}
void LiteQuery::run_query(td::BufferSlice data, td::actor::ActorId<ValidatorManager> manager,
td::actor::ActorId<LiteServerCache> cache,
td::Promise<td::BufferSlice> promise) {
td::actor::create_actor<LiteQuery>("litequery", std::move(data), std::move(manager), std::move(promise)).release();
td::actor::create_actor<LiteQuery>("litequery", std::move(data), std::move(manager), std::move(cache),
std::move(promise))
.release();
}
void LiteQuery::fetch_account_state(WorkchainId wc, StdSmcAddress acc_addr, td::actor::ActorId<ton::validator::ValidatorManager> manager,
@ -64,8 +67,8 @@ void LiteQuery::fetch_account_state(WorkchainId wc, StdSmcAddress acc_addr, td:
}
LiteQuery::LiteQuery(td::BufferSlice data, td::actor::ActorId<ValidatorManager> manager,
td::Promise<td::BufferSlice> promise)
: query_(std::move(data)), manager_(std::move(manager)), promise_(std::move(promise)) {
td::actor::ActorId<LiteServerCache> cache, td::Promise<td::BufferSlice> promise)
: query_(std::move(data)), manager_(std::move(manager)), cache_(std::move(cache)), promise_(std::move(promise)) {
timeout_ = td::Timestamp::in(default_timeout_msec * 0.001);
}
@ -110,7 +113,10 @@ void LiteQuery::alarm() {
fatal_error(-503, "timeout");
}
bool LiteQuery::finish_query(td::BufferSlice result) {
bool LiteQuery::finish_query(td::BufferSlice result, bool skip_cache_update) {
if (use_cache_ && !skip_cache_update) {
td::actor::send_closure(cache_, &LiteServerCache::update, cache_key_, result.clone());
}
if (promise_) {
promise_.set_result(std::move(result));
stop();
@ -124,21 +130,53 @@ bool LiteQuery::finish_query(td::BufferSlice result) {
void LiteQuery::start_up() {
alarm_timestamp() = timeout_;
if(acc_state_promise_) {
td::actor::send_closure_later(actor_id(this),&LiteQuery::perform_fetchAccountState);
if (acc_state_promise_) {
td::actor::send_closure_later(actor_id(this), &LiteQuery::perform_fetchAccountState);
return;
}
auto F = fetch_tl_object<ton::lite_api::Function>(std::move(query_), true);
auto F = fetch_tl_object<ton::lite_api::Function>(query_, true);
if (F.is_error()) {
td::actor::send_closure(manager_, &ValidatorManager::add_lite_query_stats, 0); // unknown
abort_query(F.move_as_error());
return;
}
td::actor::send_closure(manager_, &ValidatorManager::add_lite_query_stats, F.ok()->get_id());
query_obj_ = F.move_as_ok();
if (!cache_.empty() && query_obj_->get_id() == lite_api::liteServer_sendMessage::ID) {
// Dropping duplicate "sendMessage"
cache_key_ = td::sha256_bits256(query_);
td::actor::send_closure(cache_, &LiteServerCache::process_send_message, cache_key_,
[SelfId = actor_id(this)](td::Result<td::Unit> R) {
if (R.is_ok()) {
td::actor::send_closure(SelfId, &LiteQuery::perform);
} else {
td::actor::send_closure(SelfId, &LiteQuery::abort_query,
R.move_as_error_prefix("cannot send external message : "));
}
});
return;
}
use_cache_ = !cache_.empty() && query_obj_->get_id() == lite_api::liteServer_runSmcMethod::ID;
if (use_cache_) {
cache_key_ = td::sha256_bits256(query_);
td::actor::send_closure(
cache_, &LiteServerCache::lookup, cache_key_, [SelfId = actor_id(this)](td::Result<td::BufferSlice> R) {
if (R.is_error()) {
td::actor::send_closure(SelfId, &LiteQuery::perform);
} else {
td::actor::send_closure(SelfId, &LiteQuery::finish_query, R.move_as_ok(), true);
}
});
} else {
perform();
}
}
void LiteQuery::perform() {
td::actor::send_closure(manager_, &ValidatorManager::add_lite_query_stats, query_obj_->get_id());
lite_api::downcast_call(
*F.move_as_ok().get(),
*query_obj_,
td::overloaded(
[&](lite_api::liteServer_getTime& q) { this->perform_getTime(); },
[&](lite_api::liteServer_getVersion& q) { this->perform_getVersion(); },
@ -501,7 +539,7 @@ void LiteQuery::perform_sendMessage(td::BufferSlice data) {
LOG(INFO) << "sending an external message to validator manager";
td::actor::send_closure_later(manager, &ValidatorManager::send_external_message, res.move_as_ok());
auto b = ton::create_serialize_tl_object<ton::lite_api::liteServer_sendMsgStatus>(1);
td::actor::send_closure(Self, &LiteQuery::finish_query, std::move(b));
td::actor::send_closure(Self, &LiteQuery::finish_query, std::move(b), false);
}
});
}

View file

@ -27,7 +27,7 @@
#include "shard.hpp"
#include "proof.hpp"
#include "block/block-auto.h"
#include "auto/tl/lite_api.h"
namespace ton {
@ -37,11 +37,16 @@ using td::Ref;
class LiteQuery : public td::actor::Actor {
td::BufferSlice query_;
td::actor::ActorId<ton::validator::ValidatorManager> manager_;
td::actor::ActorId<LiteServerCache> cache_;
td::Timestamp timeout_;
td::Promise<td::BufferSlice> promise_;
td::Promise<std::tuple<td::Ref<vm::CellSlice>,UnixTime,LogicalTime,std::unique_ptr<block::ConfigInfo>>> acc_state_promise_;
tl_object_ptr<ton::lite_api::Function> query_obj_;
bool use_cache_{false};
td::Bits256 cache_key_;
int pending_{0};
int mode_{0};
WorkchainId acc_workchain_;
@ -75,11 +80,11 @@ class LiteQuery : public td::actor::Actor {
ls_capabilities = 7
}; // version 1.1; +1 = build block proof chains, +2 = masterchainInfoExt, +4 = runSmcMethod
LiteQuery(td::BufferSlice data, td::actor::ActorId<ton::validator::ValidatorManager> manager,
td::Promise<td::BufferSlice> promise);
td::actor::ActorId<LiteServerCache> cache, td::Promise<td::BufferSlice> promise);
LiteQuery(WorkchainId wc, StdSmcAddress acc_addr, td::actor::ActorId<ton::validator::ValidatorManager> manager,
td::Promise<std::tuple<td::Ref<vm::CellSlice>,UnixTime,LogicalTime,std::unique_ptr<block::ConfigInfo>>> promise);
static void run_query(td::BufferSlice data, td::actor::ActorId<ton::validator::ValidatorManager> manager,
td::Promise<td::BufferSlice> promise);
td::actor::ActorId<LiteServerCache> cache, td::Promise<td::BufferSlice> promise);
static void fetch_account_state(WorkchainId wc, StdSmcAddress acc_addr, td::actor::ActorId<ton::validator::ValidatorManager> manager,
td::Promise<std::tuple<td::Ref<vm::CellSlice>,UnixTime,LogicalTime,std::unique_ptr<block::ConfigInfo>>> promise);
@ -90,9 +95,10 @@ class LiteQuery : public td::actor::Actor {
bool fatal_error(int err_code, std::string err_msg = "");
void abort_query(td::Status reason);
void abort_query_ext(td::Status reason, std::string err_msg);
bool finish_query(td::BufferSlice result);
bool finish_query(td::BufferSlice result, bool skip_cache_update = false);
void alarm() override;
void start_up() override;
void perform();
void perform_getTime();
void perform_getVersion();
void perform_getMasterchainInfo(int mode);

View file

@ -19,16 +19,19 @@
#pragma once
#include "td/actor/actor.h"
#include "td/utils/buffer.h"
#include "common/bitstring.h"
namespace ton {
namespace validator {
namespace ton::validator {
class LiteServerCache : public td::actor::Actor {
public:
virtual ~LiteServerCache() = default;
~LiteServerCache() override = default;
virtual void lookup(td::Bits256 key, td::Promise<td::BufferSlice> promise) = 0;
virtual void update(td::Bits256 key, td::BufferSlice value) = 0;
virtual void process_send_message(td::Bits256 key, td::Promise<td::Unit> promise) = 0;
};
} // namespace validator
} // namespace ton
} // namespace ton::validator