Skip to content

Commit

Permalink
GetConfigResponce struct caching
Browse files Browse the repository at this point in the history
  • Loading branch information
rnovikov committed Nov 29, 2024
1 parent 056c55e commit be9a1e8
Show file tree
Hide file tree
Showing 4 changed files with 127 additions and 77 deletions.
21 changes: 16 additions & 5 deletions evm_loader/lib/src/account_storage_tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,24 +4,26 @@ use crate::tracing::AccountOverride;
use evm_loader::types::vector::VectorVecExt;
use hex_literal::hex;
use solana_account_decoder::UiDataSliceConfig;

use std::collections::HashMap;
use std::str::FromStr;

const STORAGE_LENGTH: usize = 32 * STORAGE_ENTRIES_IN_CONTRACT_ACCOUNT;

mod mock_rpc_client {
use crate::types::programs_cache::get_programdata_slot_from_account;
use solana_sdk::bpf_loader_upgradeable::UpgradeableLoaderState;

use crate::commands::get_config::BuildConfigSimulator;
use crate::NeonResult;
use crate::{commands::get_config::ConfigSimulator, rpc::Rpc};
use async_trait::async_trait;
use solana_account_decoder::UiDataSliceConfig;
use solana_client::client_error::Result as ClientResult;
use solana_sdk::account::Account;
use solana_sdk::clock::{Slot, UnixTimestamp};
use solana_sdk::pubkey::Pubkey;
use std::collections::HashMap;

use solana_account_decoder::UiDataSliceConfig;

pub struct MockRpcClient {
accounts: HashMap<Pubkey, Account>,
}
Expand Down Expand Up @@ -91,8 +93,17 @@ mod mock_rpc_client {

#[async_trait(?Send)]
impl BuildConfigSimulator for MockRpcClient {
fn use_cache(&self) -> bool {
false
async fn get_last_deployed_slot(&self, program_id: &Pubkey) -> u64 {
let slice = UiDataSliceConfig {
offset: 0,
length: UpgradeableLoaderState::size_of_programdata_metadata(),
};
let result = self.get_account_slice(program_id, Some(slice)).await;
if let Ok(Some(acc)) = result {
get_programdata_slot_from_account(&acc).expect("NO slot value for acc")
} else {
panic!("get_account_slice return an Error ");
}
}
async fn build_config_simulator(&self, _program_id: Pubkey) -> NeonResult<ConfigSimulator> {
unimplemented!();
Expand Down
96 changes: 53 additions & 43 deletions evm_loader/lib/src/commands/get_config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,21 +2,24 @@

use std::collections::BTreeMap;

use crate::rpc::{CallDbClient, CloneRpcClient, Rpc};
use crate::solana_simulator::SolanaSimulator;
use crate::types::programs_cache::{get_programdata_slot_from_account, KeyAccountCache};
use crate::types::programs_cache::{program_config_cache_add, program_config_cache_get};
use async_trait::async_trait;
use base64::Engine;
use enum_dispatch::enum_dispatch;
use evm_loader::solana_program::bpf_loader_upgradeable::UpgradeableLoaderState;
use serde::{Deserialize, Serialize};
use serde_with::{serde_as, DisplayFromStr};
pub use solana_account_decoder::UiDataSliceConfig as SliceConfig;
use solana_client::rpc_config::RpcSimulateTransactionConfig;
use solana_sdk::signer::Signer;
use solana_sdk::{instruction::Instruction, pubkey::Pubkey, transaction::Transaction};
use tokio::sync::OnceCell;

use crate::rpc::{CallDbClient, CloneRpcClient};
use crate::solana_simulator::SolanaSimulator;
use crate::NeonResult;

#[derive(Debug, Serialize, Deserialize)]
#[derive(Debug, Clone, Serialize, Deserialize)]
pub enum Status {
Ok,
Emergency,
Expand All @@ -33,7 +36,7 @@ pub struct ChainInfo {
}

#[serde_as]
#[derive(Debug, Serialize, Deserialize)]
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct GetConfigResponse {
pub version: String,
pub revision: String,
Expand All @@ -58,16 +61,51 @@ pub enum ConfigSimulator<'r> {
#[async_trait(?Send)]
#[enum_dispatch]
pub trait BuildConfigSimulator {
fn use_cache(&self) -> bool;
async fn get_last_deployed_slot(&self, program_id: &Pubkey) -> u64;

async fn get_config_simulator(&self, program_id: Pubkey) -> NeonResult<GetConfigResponse> {
let key = KeyAccountCache {
addr: program_id,
slot: self.get_last_deployed_slot(&program_id).await,
};

let rz = program_config_cache_get(&key).await;
if rz.is_some() {
return Ok(rz.unwrap());
};
let mut simulator = self.build_config_simulator(program_id).await?;

let (version, revision) = simulator.get_version().await?;

let result = GetConfigResponse {
version,
revision,
status: simulator.get_status().await?,
environment: simulator.get_environment().await?,
chains: simulator.get_chains().await?,
config: simulator.get_properties().await?,
};
program_config_cache_add(key, result.clone()).await;
Ok(result)
}

async fn build_config_simulator(&self, program_id: Pubkey) -> NeonResult<ConfigSimulator>;
}

#[async_trait(?Send)]
impl BuildConfigSimulator for CloneRpcClient {
fn use_cache(&self) -> bool {
true
async fn get_last_deployed_slot(&self, program_id: &Pubkey) -> u64 {
let slice = SliceConfig {
offset: 0,
length: UpgradeableLoaderState::size_of_programdata_metadata(),
};
let result = self.get_account_slice(program_id, Some(slice)).await;
if let Ok(Some(acc)) = result {
get_programdata_slot_from_account(&acc).expect("NO slot value for acc")
} else {
panic!("get_account_slice return an Error ");
}
}

async fn build_config_simulator(&self, program_id: Pubkey) -> NeonResult<ConfigSimulator> {
Ok(ConfigSimulator::CloneRpcClient {
program_id,
Expand All @@ -78,13 +116,13 @@ impl BuildConfigSimulator for CloneRpcClient {

#[async_trait(?Send)]
impl BuildConfigSimulator for CallDbClient {
fn use_cache(&self) -> bool {
false
async fn get_last_deployed_slot(&self, _program_id: &Pubkey) -> u64 {
0
}

async fn build_config_simulator(&self, program_id: Pubkey) -> NeonResult<ConfigSimulator> {
let mut simulator = SolanaSimulator::new_without_sync(self).await?;
simulator.sync_accounts(self, &[program_id]).await?;

Ok(ConfigSimulator::ProgramTestContext {
program_id,
simulator,
Expand Down Expand Up @@ -264,44 +302,16 @@ pub async fn execute(
rpc: &impl BuildConfigSimulator,
program_id: Pubkey,
) -> NeonResult<GetConfigResponse> {
let mut simulator = rpc.build_config_simulator(program_id).await?;

let (version, revision) = simulator.get_version().await?;

Ok(GetConfigResponse {
version,
revision,
status: simulator.get_status().await?,
environment: simulator.get_environment().await?,
chains: simulator.get_chains().await?,
config: simulator.get_properties().await?,
})
rpc.get_config_simulator(program_id).await
}

static CHAINS_CACHE: OnceCell<Vec<ChainInfo>> = OnceCell::const_new();
// static CHAINS_CACHE: OnceCell<Vec<ChainInfo>> = OnceCell::const_new();

pub async fn read_chains(
rpc: &impl BuildConfigSimulator,
program_id: Pubkey,
) -> NeonResult<Vec<ChainInfo>> {
if rpc.use_cache() {
return CHAINS_CACHE
.get_or_try_init(|| get_chains(rpc, program_id))
.await
.cloned();
}

get_chains(rpc, program_id).await
}

async fn get_chains(
rpc: &(impl BuildConfigSimulator + Sized),
program_id: Pubkey,
) -> NeonResult<Vec<ChainInfo>> {
rpc.build_config_simulator(program_id)
.await?
.get_chains()
.await
Ok(rpc.get_config_simulator(program_id).await?.chains)
}

pub async fn read_legacy_chain_id(
Expand Down
6 changes: 3 additions & 3 deletions evm_loader/lib/src/rpc/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,11 @@ mod db_call_client;
mod emulator_client;
mod validator_client;

pub use db_call_client::CallDbClient;
pub use validator_client::CloneRpcClient;

use crate::commands::get_config::GetConfigResponse;
use crate::commands::get_config::{BuildConfigSimulator, ConfigSimulator};
use crate::{NeonError, NeonResult};
use async_trait::async_trait;
pub use db_call_client::CallDbClient;
use enum_dispatch::enum_dispatch;
pub use solana_account_decoder::UiDataSliceConfig as SliceConfig;
use solana_cli::cli::CliError;
Expand All @@ -19,6 +18,7 @@ use solana_sdk::{
native_token::lamports_to_sol,
pubkey::Pubkey,
};
pub use validator_client::CloneRpcClient;

#[async_trait(?Send)]
#[enum_dispatch]
Expand Down
81 changes: 55 additions & 26 deletions evm_loader/lib/src/types/programs_cache.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
use crate::rpc::Rpc;
use async_trait::async_trait;

use crate::commands::get_config::GetConfigResponse;
use bincode::deserialize;
use futures::future::join_all;
use solana_client::client_error::{ClientErrorKind, Result as ClientResult};
Expand All @@ -20,15 +21,18 @@ use tokio::sync::OnceCell;
use tracing::info;
#[derive(Debug, Eq, PartialEq, Hash)]
pub struct KeyAccountCache {
addr: Pubkey,
slot: u64,
pub addr: Pubkey,
pub slot: u64,
}

use crate::rpc::SliceConfig;
type ProgramDataCache = HashMap<KeyAccountCache, Account>;
type ThreadSaveProgramDataCache = RwLock<ProgramDataCache>;
type ProgramDataCache<Value> = HashMap<KeyAccountCache, Value>;
type ThreadSaveCache<Value> = RwLock<ProgramDataCache<Value>>;

type ThreadSaveProgramDataCache = ThreadSaveCache<Account>;
type ThreadSaveConfigCache<'a> = ThreadSaveCache<GetConfigResponse>;

static LOCAL_CONFIG: OnceCell<ThreadSaveProgramDataCache> = OnceCell::const_new();
static ACCOUNT_CACHE_TABLE: OnceCell<ThreadSaveProgramDataCache> = OnceCell::const_new();
static CONFIG_CACHE_TABLE: OnceCell<ThreadSaveConfigCache> = OnceCell::const_new();

pub async fn cut_programdata_from_acc(account: &mut Account, data_slice: SliceConfig) {
if data_slice.offset != 0 {
Expand All @@ -38,9 +42,25 @@ pub async fn cut_programdata_from_acc(account: &mut Account, data_slice: SliceCo
}
account.data.truncate(data_slice.length);
}

async fn programdata_hash_get_instance() -> &'static ThreadSaveProgramDataCache {
LOCAL_CONFIG
fn cache_get<Value: std::clone::Clone>(
key: &KeyAccountCache,
table: &ThreadSaveCache<Value>,
) -> Option<Value> {
table
.read()
.expect("acc_hash_get_instance poisoned")
.get(key)
.cloned()
}
fn cache_add<Value: std::clone::Clone>(
key: KeyAccountCache,
value: Value,
table: &ThreadSaveCache<Value>,
) {
table.write().expect("PANIC, no space ").insert(key, value);
}
async fn programdata_account_cache_get_instance() -> &'static ThreadSaveProgramDataCache {
ACCOUNT_CACHE_TABLE
.get_or_init(|| async {
let map = HashMap::new();

Expand All @@ -49,26 +69,17 @@ async fn programdata_hash_get_instance() -> &'static ThreadSaveProgramDataCache
.await
}

async fn programdata_hash_get(addr: Pubkey, slot: u64) -> Option<Account> {
async fn programdata_account_cache_get(addr: Pubkey, slot: u64) -> Option<Account> {
let val = KeyAccountCache { addr, slot };
programdata_hash_get_instance()
.await
.read()
.expect("acc_hash_get_instance poisoned")
.get(&val)
.cloned()
cache_get(&val, programdata_account_cache_get_instance().await)
}

async fn programdata_hash_add(addr: Pubkey, slot: u64, acc: Account) {
let val = KeyAccountCache { addr, slot };
programdata_hash_get_instance()
.await
.write()
.expect("PANIC, no nable")
.insert(val, acc);
async fn programdata_account_cache_add(addr: Pubkey, slot: u64, acc: Account) {
let key = KeyAccountCache { addr, slot };
cache_add(key, acc, programdata_account_cache_get_instance().await );
}

fn get_programdata_slot_from_account(acc: &Account) -> ClientResult<u64> {
pub fn get_programdata_slot_from_account(acc: &Account) -> ClientResult<u64> {
if !bpf_loader_upgradeable::check_id(&acc.owner) {
return Err(ClientErrorKind::Custom("Not upgradeable account".to_string()).into());
}
Expand Down Expand Up @@ -112,11 +123,11 @@ pub async fn programdata_cache_get_values_by_keys(
match result {
Ok(Some(account)) => {
let slot_val = get_programdata_slot_from_account(account)?;
if let Some(acc) = programdata_hash_get(*key, slot_val).await {
if let Some(acc) = programdata_account_cache_get(*key, slot_val).await {
answer.push(Some(acc));
} else if let Ok(Some(tmp_acc)) = rpc.get_account(key).await {
let current_slot = get_programdata_slot_from_account(&tmp_acc)?;
programdata_hash_add(*key, current_slot, tmp_acc.clone()).await;
programdata_account_cache_add(*key, current_slot, tmp_acc.clone()).await;

answer.push(Some(tmp_acc));
} else {
Expand Down Expand Up @@ -169,6 +180,24 @@ impl FakeRpc {
}
}

async fn program_config_cache_get_instance() -> &'static ThreadSaveConfigCache<'static> {
CONFIG_CACHE_TABLE
.get_or_init(|| async {
let map = HashMap::new();

RwLock::new(map)
})
.await
}

pub async fn program_config_cache_get(key: &KeyAccountCache) -> Option<GetConfigResponse> {
cache_get(key, program_config_cache_get_instance().await)
}

pub async fn program_config_cache_add(key: KeyAccountCache, val: GetConfigResponse) {
cache_add(key, val, program_config_cache_get_instance().await);
}

#[async_trait(?Send)]

impl Rpc for FakeRpc {
Expand Down

0 comments on commit be9a1e8

Please sign in to comment.