Skip to content

Commit

Permalink
Add limit to cached executives (FISCO-BCOS#4559)
Browse files Browse the repository at this point in the history
  • Loading branch information
morebtcg authored Jul 31, 2024
1 parent 9f9168d commit 6f62fad
Show file tree
Hide file tree
Showing 5 changed files with 64 additions and 52 deletions.
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
#pragma once

#include "../protocol/Block.h"
#include "../protocol/BlockHeader.h"
#include "../protocol/TransactionReceipt.h"
#include "bcos-task/Task.h"
#include "bcos-task/Trait.h"
#include "bcos-utilities/Ranges.h"

namespace bcos::transaction_scheduler
{
Expand Down
68 changes: 33 additions & 35 deletions bcos-rpc/bcos-rpc/web3jsonrpc/endpoints/EthEndpoint.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@
#include <bcos-rpc/web3jsonrpc/utils/Common.h>
#include <bcos-rpc/web3jsonrpc/utils/util.h>
#include <bcos-tars-protocol/protocol/TransactionImpl.h>
#include <variant>

using namespace bcos;
using namespace bcos::rpc;
Expand Down Expand Up @@ -475,71 +476,68 @@ task::Task<void> EthEndpoint::call(const Json::Value& request, Json::Value& resp
BOOST_THROW_EXCEPTION(
JsonRpcException(JsonRpcError::InternalError, "Scheduler not available!"));
}
auto [valid, call] = decodeCallRequest(request[0u]);
auto [valid, call] = decodeCallRequest(request[0U]);
if (!valid)
{
BOOST_THROW_EXCEPTION(JsonRpcException(InvalidParams, "Invalid call request!"));
}
auto const blockTag = toView(request[1u]);
auto const blockTag = toView(request[1U]);
auto [blockNumber, _] = co_await getBlockNumberByTag(blockTag);
if (c_fileLogLevel == TRACE)
{
WEB3_LOG(TRACE) << LOG_DESC("eth_call") << LOG_KV("call", call)
<< LOG_KV("blockTag", blockTag) << LOG_KV("blockNumber", blockNumber);
}
auto const tx = call.takeToTransaction(m_nodeService->blockFactory()->transactionFactory());
// TODO: ignore params blockNumber here, use it after historical data is available
std::variant<Error::Ptr, protocol::TransactionReceipt::Ptr> variantResult{};
// MOVE it into a new file
auto tx = call.takeToTransaction(m_nodeService->blockFactory()->transactionFactory());
struct Awaitable
{
bcos::scheduler::SchedulerInterface& m_scheduler;
bcos::protocol::Transaction::Ptr m_tx;
std::variant<Error::Ptr, protocol::TransactionReceipt::Ptr>& m_result;
bcos::protocol::Transaction::Ptr& m_tx;
Error::Ptr m_error;
Json::Value& m_response;

constexpr static bool await_ready() noexcept { return false; }
void await_suspend(std::coroutine_handle<> handle) noexcept
void await_suspend(std::coroutine_handle<> handle)
{
m_scheduler.call(m_tx, [this, handle](Error::Ptr&& error, auto&& result) {
if (error)
{
m_result.emplace<Error::Ptr>(std::move(error));
m_error = std::move(error);
}
else
{
m_result.emplace<protocol::TransactionReceipt::Ptr>(std::move(result));
auto output = toHexStringWithPrefix(result->output());
if (result->status() == static_cast<int32_t>(protocol::TransactionStatus::None))
{
m_response["jsonrpc"] = "2.0";
m_response["result"] = output;
}
else
{
// https://docs.infura.io/api/networks/ethereum/json-rpc-methods/eth_call#returns
Json::Value jsonResult = Json::objectValue;
jsonResult["code"] = result->status();
jsonResult["message"] = result->message();
jsonResult["data"] = output;
m_response["jsonrpc"] = "2.0";
m_response["error"] = std::move(jsonResult);
}
}

handle.resume();
});
}
protocol::TransactionReceipt::Ptr await_resume()
void await_resume()
{
if (std::holds_alternative<Error::Ptr>(m_result))
if (m_error)
{
BOOST_THROW_EXCEPTION(*std::get<Error::Ptr>(m_result));
BOOST_THROW_EXCEPTION(*m_error);
}
return std::get<protocol::TransactionReceipt::Ptr>(m_result);
}
};
auto const result =
co_await Awaitable{.m_scheduler = *scheduler, .m_tx = tx, .m_result = variantResult};

auto output = toHexStringWithPrefix(result->output());
if (result->status() == static_cast<int32_t>(protocol::TransactionStatus::None))
{
response["jsonrpc"] = "2.0";
response["result"] = std::move(output);
}
else
{
// https://docs.infura.io/api/networks/ethereum/json-rpc-methods/eth_call#returns
Json::Value jsonResult = Json::objectValue;
jsonResult["code"] = result->status();
jsonResult["message"] = result->message();
jsonResult["data"] = std::move(output);
response["jsonrpc"] = "2.0";
response["error"] = std::move(jsonResult);
}
co_return;
Awaitable awaitable{
.m_scheduler = *scheduler, .m_tx = tx, .m_error = {}, .m_response = response};
co_await awaitable;
}
task::Task<void> EthEndpoint::estimateGas(const Json::Value& request, Json::Value& response)
{
Expand Down
16 changes: 16 additions & 0 deletions transaction-executor/bcos-transaction-executor/vm/HostContext.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -58,3 +58,19 @@ std::variant<const evmc_message*, evmc_message> bcos::transaction_executor::getM
}
return message;
}

bcos::transaction_executor::CacheExecutables& bcos::transaction_executor::getCacheExecutables()
{
struct CacheExecutables
{
bcos::transaction_executor::CacheExecutables m_cachedExecutables;

CacheExecutables()
{
constexpr static auto maxContracts = 100;
m_cachedExecutables.setMaxCapacity(sizeof(std::shared_ptr<Executable>) * maxContracts);
}
} static cachedExecutables;

return cachedExecutables.m_cachedExecutables;
}
26 changes: 12 additions & 14 deletions transaction-executor/bcos-transaction-executor/vm/HostContext.h
Original file line number Diff line number Diff line change
Expand Up @@ -75,45 +75,43 @@ std::variant<const evmc_message*, evmc_message> getMessage(const evmc_message& i

struct Executable
{
explicit Executable(storage::Entry code, evmc_revision revision)
Executable(storage::Entry code, evmc_revision revision)
: m_code(std::make_optional(std::move(code))),
m_revision(revision),
m_vmInstance(VMFactory::create(VMKind::evmone,
bytesConstRef(reinterpret_cast<const uint8_t*>(m_code->data()), m_code->size()),
m_revision))
revision))
{}
explicit Executable(bytesConstRef code, evmc_revision revision)
: m_vmInstance(VMFactory::create(VMKind::evmone, code, revision))
{}

std::optional<storage::Entry> m_code;
evmc_revision m_revision;
VMInstance m_vmInstance;
};

template <class Storage>
using Account = ledger::account::EVMAccount<Storage>;

inline task::Task<std::shared_ptr<Executable>> getExecutable(
auto& storage, const evmc_address& address, const evmc_revision& revision)
{
static storage2::memory_storage::MemoryStorage<evmc_address, std::shared_ptr<Executable>,
using CacheExecutables =
storage2::memory_storage::MemoryStorage<evmc_address, std::shared_ptr<Executable>,
storage2::memory_storage::Attribute(
storage2::memory_storage::LRU | storage2::memory_storage::CONCURRENT),
std::hash<evmc_address>>
cachedExecutables;
std::hash<evmc_address>>;
CacheExecutables& getCacheExecutables();

if (auto executable = co_await storage2::readOne(cachedExecutables, address))
inline task::Task<std::shared_ptr<Executable>> getExecutable(
auto& storage, const evmc_address& address, const evmc_revision& revision)
{
if (auto executable = co_await storage2::readOne(getCacheExecutables(), address))
{
co_return std::move(*executable);
}

Account<std::decay_t<decltype(storage)>> account(storage, address);
if (auto codeEntry = co_await ledger::account::code(account))
{
auto executable =
std::make_shared<Executable>(Executable(std::move(*codeEntry), std::move(revision)));
co_await storage2::writeOne(cachedExecutables, address, executable);
auto executable = std::make_shared<Executable>(Executable(std::move(*codeEntry), revision));
co_await storage2::writeOne(getCacheExecutables(), address, executable);
co_return executable;
}
co_return std::shared_ptr<Executable>{};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -661,7 +661,7 @@ class BaselineScheduler : public scheduler::SchedulerInterface
callback(nullptr);
}

void stop() override{};
void stop() override {};

void registerTransactionNotifier(std::function<void(bcos::protocol::BlockNumber,
bcos::protocol::TransactionSubmitResultsPtr, std::function<void(Error::Ptr)>)>
Expand Down

0 comments on commit 6f62fad

Please sign in to comment.