Skip to content

Commit

Permalink
feat: improving metrics related to database (#297)
Browse files Browse the repository at this point in the history
  • Loading branch information
dinhani-cw authored Mar 4, 2024
1 parent bac9018 commit 596533a
Show file tree
Hide file tree
Showing 10 changed files with 220 additions and 87 deletions.
5 changes: 4 additions & 1 deletion src/eth/evm/evm.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ use crate::eth::primitives::BlockNumber;
use crate::eth::primitives::Bytes;
use crate::eth::primitives::CallInput;
use crate::eth::primitives::Execution;
use crate::eth::primitives::ExecutionMetrics;
use crate::eth::primitives::ExternalBlock;
use crate::eth::primitives::ExternalReceipt;
use crate::eth::primitives::ExternalTransaction;
Expand All @@ -23,10 +24,12 @@ use crate::eth::primitives::Wei;
use crate::ext::OptionExt;
use crate::if_else;

pub type EvmExecutionResult = (Execution, ExecutionMetrics);

/// EVM operations.
pub trait Evm: Send + Sync + 'static {
/// Execute a transaction that deploys a contract or call a contract function.
fn execute(&mut self, input: EvmInput) -> anyhow::Result<Execution>;
fn execute(&mut self, input: EvmInput) -> anyhow::Result<EvmExecutionResult>;
}

/// EVM input data. Usually derived from a transaction or call.
Expand Down
1 change: 1 addition & 0 deletions src/eth/evm/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,4 +10,5 @@ mod evm;
pub mod revm;

pub use evm::Evm;
pub use evm::EvmExecutionResult;
pub use evm::EvmInput;
23 changes: 18 additions & 5 deletions src/eth/evm/revm.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ use revm::Database;
use revm::EVM;
use tokio::runtime::Handle;

use crate::eth::evm::evm::EvmExecutionResult;
use crate::eth::evm::Evm;
use crate::eth::evm::EvmInput;
use crate::eth::primitives::Account;
Expand All @@ -33,6 +34,7 @@ use crate::eth::primitives::Bytes;
use crate::eth::primitives::Execution;
use crate::eth::primitives::ExecutionAccountChanges;
use crate::eth::primitives::ExecutionChanges;
use crate::eth::primitives::ExecutionMetrics;
use crate::eth::primitives::ExecutionResult;
use crate::eth::primitives::ExecutionValueChange;
use crate::eth::primitives::Gas;
Expand Down Expand Up @@ -68,7 +70,7 @@ impl Revm {
}

impl Evm for Revm {
fn execute(&mut self, input: EvmInput) -> anyhow::Result<Execution> {
fn execute(&mut self, input: EvmInput) -> anyhow::Result<EvmExecutionResult> {
let start = Instant::now();

// configure session
Expand Down Expand Up @@ -99,18 +101,22 @@ impl Evm for Revm {

// parse result and track metrics
let session = evm.take_db();
let metrics = session.metrics;
let point_in_time = session.input.point_in_time.clone();

let result = match evm_result {
let execution = match evm_result {
Ok(result) => Ok(parse_revm_execution(result, session.input, session.storage_changes)),
Err(e) => {
tracing::warn!(reason = ?e, "evm execution error");
Err(anyhow!("Error executing EVM transaction. Check logs for more information."))
}
};
metrics::inc_evm_execution(start.elapsed(), &point_in_time, result.is_ok());

result
metrics::inc_evm_execution(start.elapsed(), &point_in_time, execution.is_ok());
metrics::inc_evm_execution_account_reads(metrics.account_reads);
metrics::inc_evm_execution_slot_reads(metrics.slot_reads);

execution.map(|x| (x, metrics))
}
}

Expand All @@ -128,6 +134,9 @@ struct RevmDatabaseSession {

/// Changes made to the storage during the execution of the transaction.
storage_changes: ExecutionChanges,

/// Metrics collected during EVM execution.
metrics: ExecutionMetrics,
}

impl RevmDatabaseSession {
Expand All @@ -136,6 +145,7 @@ impl RevmDatabaseSession {
storage,
input,
storage_changes: Default::default(),
metrics: Default::default(),
}
}
}
Expand All @@ -144,6 +154,8 @@ impl Database for RevmDatabaseSession {
type Error = anyhow::Error;

fn basic(&mut self, revm_address: RevmAddress) -> anyhow::Result<Option<AccountInfo>> {
self.metrics.account_reads += 1;

// retrieve account
let address: Address = revm_address.into();
let account = Handle::current().block_on(self.storage.read_account(&address, &self.input.point_in_time))?;
Expand All @@ -169,10 +181,11 @@ impl Database for RevmDatabaseSession {
}

fn storage(&mut self, revm_address: RevmAddress, revm_index: U256) -> anyhow::Result<U256> {
self.metrics.slot_reads += 1;

// retrieve slot
let address: Address = revm_address.into();
let index: SlotIndex = revm_index.into();
//instructions?
let slot = Handle::current().block_on(self.storage.read_slot(&address, &index, &self.input.point_in_time))?;

// track original value, except if ignored address
Expand Down
28 changes: 20 additions & 8 deletions src/eth/executor.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,10 +20,12 @@ use tokio::sync::oneshot;
use tokio::sync::Mutex;

use crate::eth::evm::Evm;
use crate::eth::evm::EvmExecutionResult;
use crate::eth::evm::EvmInput;
use crate::eth::primitives::Block;
use crate::eth::primitives::CallInput;
use crate::eth::primitives::Execution;
use crate::eth::primitives::ExecutionMetrics;
use crate::eth::primitives::ExternalBlock;
use crate::eth::primitives::ExternalReceipt;
use crate::eth::primitives::ExternalTransaction;
Expand All @@ -40,7 +42,7 @@ use crate::infra::metrics;
/// Number of events in the backlog.
const NOTIFIER_CAPACITY: usize = u16::MAX as usize;

type EvmTask = (EvmInput, oneshot::Sender<anyhow::Result<Execution>>);
type EvmTask = (EvmInput, oneshot::Sender<anyhow::Result<EvmExecutionResult>>);

/// The EthExecutor struct is responsible for orchestrating the execution of Ethereum transactions.
/// It holds references to the EVM, block miner, and storage, managing the overall process of
Expand Down Expand Up @@ -77,12 +79,14 @@ impl EthExecutor {
/// Imports an external block using the offline flow.
pub async fn import_offline(&self, block: ExternalBlock, receipts: &HashMap<Hash, ExternalReceipt>) -> anyhow::Result<()> {
let start = Instant::now();
let mut block_metrics = ExecutionMetrics::default();
tracing::info!(number = %block.number(), "importing offline block");

// re-execute transactions
let mut executions: Vec<ExternalTransactionExecution> = Vec::with_capacity(block.transactions.len());
for tx in block.transactions.clone() {
let tx_start = Instant::now();

// find receipt
let Some(receipt) = receipts.get(&tx.hash()).cloned() else {
tracing::error!(hash = %tx.hash, "receipt is missing");
Expand All @@ -95,7 +99,7 @@ impl EthExecutor {

// handle execution result
match execution {
Ok(mut execution) => {
Ok((mut execution, execution_metrics)) => {
// apply execution costs that were not consided when re-executing the transaction
execution.apply_execution_costs(&receipt)?;

Expand All @@ -111,7 +115,11 @@ impl EthExecutor {
// temporarily save state to next transactions from the same block
self.storage.save_account_changes_to_temp(execution.clone()).await?;
executions.push((tx, receipt, execution));

// track metrics
metrics::inc_executor_import_offline_transaction(tx_start.elapsed());
block_metrics.account_reads += execution_metrics.account_reads;
block_metrics.slot_reads += execution_metrics.slot_reads;
}
Err(e) => {
let json_tx = serde_json::to_string(&tx).unwrap();
Expand All @@ -132,6 +140,9 @@ impl EthExecutor {
};

metrics::inc_executor_import_offline(start.elapsed());
metrics::inc_executor_import_offline_account_reads(block_metrics.account_reads);
metrics::inc_executor_import_offline_slot_reads(block_metrics.slot_reads);

Ok(())
}

Expand All @@ -152,7 +163,7 @@ impl EthExecutor {
};

let evm_input = EvmInput::from_external_transaction(&external_block, external_transaction.to_owned(), external_receipt);
let execution = self.execute_in_evm(evm_input).await?;
let execution = self.execute_in_evm(evm_input).await?.0;
execution.compare_with_receipt(external_receipt)?;
metrics::inc_executor_import_online_transaction(tx_start.elapsed());

Expand Down Expand Up @@ -219,7 +230,7 @@ impl EthExecutor {
let (execution, block) = loop {
// execute and check conflicts before mining block
let evm_input = EvmInput::from_eth_transaction(transaction.clone());
let execution = self.execute_in_evm(evm_input).await?;
let execution = self.execute_in_evm(evm_input).await?.0;

// mine and commit block
let mut miner_lock = self.miner.lock().await;
Expand Down Expand Up @@ -266,12 +277,12 @@ impl EthExecutor {
let evm_input = EvmInput::from_eth_call(input, point_in_time);
let result = self.execute_in_evm(evm_input).await;
metrics::inc_executor_call(start.elapsed(), result.is_ok());
result
result.map(|x| x.0)
}

/// Submits a transaction to the EVM and awaits for its execution.
async fn execute_in_evm(&self, evm_input: EvmInput) -> anyhow::Result<Execution> {
let (execution_tx, execution_rx) = oneshot::channel::<anyhow::Result<Execution>>();
async fn execute_in_evm(&self, evm_input: EvmInput) -> anyhow::Result<EvmExecutionResult> {
let (execution_tx, execution_rx) = oneshot::channel::<anyhow::Result<EvmExecutionResult>>();
self.evm_tx.send((evm_input, execution_tx))?;
execution_rx.await?
}
Expand Down Expand Up @@ -304,7 +315,8 @@ fn spawn_background_evms(evms: NonEmpty<Box<dyn Evm>>) -> crossbeam_channel::Sen

// keep executing transactions until the channel is closed
while let Ok((input, tx)) = evm_rx.recv() {
if let Err(e) = tx.send(evm.execute(input)) {
let result = evm.execute(input);
if let Err(e) = tx.send(result) {
tracing::error!(reason = ?e, "failed to send evm execution result");
};
}
Expand Down
30 changes: 30 additions & 0 deletions src/eth/primitives/block.rs
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,36 @@ impl Block {
})
}

/// Calculates block size label by the number of transactions.
pub fn label_size_by_transactions(&self) -> &'static str {
match self.transactions.len() {
0 => "0",
1..=5 => "1-5",
6..=10 => "6-10",
11..=15 => "11-15",
16..=20 => "16-20",
_ => "20+",
}
}

/// Calculates block size label by consumed gas.
pub fn label_size_by_gas(&self) -> &'static str {
match self.header.gas_used.as_u64() {
0 => "0",
1..=1_000_000 => "0-1M",
1_000_001..=2_000_000 => "1M-2M",
2_000_001..=3_000_000 => "2M-3M",
3_000_001..=4_000_000 => "3M-4M",
4_000_001..=5_000_000 => "4M-5M",
5_000_001..=6_000_000 => "5M-6M",
6_000_001..=7_000_000 => "6M-7M",
7_000_001..=8_000_000 => "7M-8M",
8_000_001..=9_000_000 => "8M-9M",
9_000_001..=10_000_000 => "9M-10M",
_ => "10M+",
}
}

/// Serializes itself to JSON-RPC block format with full transactions included.
pub fn to_json_rpc_with_full_transactions(self) -> JsonValue {
let json_rpc_format: EthersBlock<EthersTransaction> = self.into();
Expand Down
9 changes: 9 additions & 0 deletions src/eth/primitives/execution.rs
Original file line number Diff line number Diff line change
Expand Up @@ -151,3 +151,12 @@ impl Execution {
Ok(())
}
}

#[derive(Debug, Default)]
pub struct ExecutionMetrics {
/// Number of account reads during EVM execution.
pub account_reads: usize,

/// Number of slot reads during EVM execution.
pub slot_reads: usize,
}
6 changes: 5 additions & 1 deletion src/eth/primitives/gas.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,14 +23,18 @@ use sqlx::types::BigDecimal;

use crate::gen_newtype_from;

// XXX: should we use U256 or U64?
// XXX: we should use U64
#[derive(Debug, Clone, Default, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
#[serde(transparent)]
pub struct Gas(U256);

impl Gas {
pub const ZERO: Gas = Gas(U256::zero());
pub const MAX: Gas = Gas(U256::MAX);

pub fn as_u64(&self) -> u64 {
self.0.low_u64()
}
}

impl Display for Gas {
Expand Down
1 change: 1 addition & 0 deletions src/eth/primitives/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -132,6 +132,7 @@ pub use ecdsa_rs::EcdsaRs;
pub use ecdsa_v::EcdsaV;
pub use execution::Execution;
pub use execution::ExecutionChanges;
pub use execution::ExecutionMetrics;
pub use execution_account_changes::ExecutionAccountChanges;
pub use execution_conflict::ExecutionConflict;
pub use execution_conflict::ExecutionConflicts;
Expand Down
9 changes: 6 additions & 3 deletions src/eth/storage/stratus_storage.rs
Original file line number Diff line number Diff line change
Expand Up @@ -172,12 +172,15 @@ impl StratusStorage {
pub async fn commit_to_perm(&self, block: Block) -> anyhow::Result<(), StorageError> {
let start = Instant::now();

// save block to permanent storage and
// clears temporary storage
// compute labels
let label_size_by_tx = block.label_size_by_transactions();
let label_size_by_gas = block.label_size_by_gas();

// save block to permanent storage and clears temporary storage
let result = self.perm.save_block(block).await;
self.reset_temp().await?;

metrics::inc_storage_commit(start.elapsed(), result.is_ok());
metrics::inc_storage_commit(start.elapsed(), label_size_by_tx, label_size_by_gas, result.is_ok());
result
}

Expand Down
Loading

0 comments on commit 596533a

Please sign in to comment.