mirror of
https://github.com/logos-blockchain/logos-execution-zone.git
synced 2026-07-09 15:29:34 +00:00
feat(wallet): metrics fetching and callibration
This commit is contained in:
parent
10c9d89ec8
commit
40fe9ff209
@ -192,7 +192,7 @@ pub struct AccountManager {
|
||||
|
||||
impl AccountManager {
|
||||
pub async fn new(
|
||||
wallet: &WalletCore,
|
||||
wallet: &mut WalletCore,
|
||||
accounts: Vec<AccountIdentity>,
|
||||
) -> Result<Self, ExecutionFailureKind> {
|
||||
let mut states = Vec::with_capacity(accounts.len());
|
||||
@ -475,7 +475,7 @@ struct AccountPreparedData {
|
||||
}
|
||||
|
||||
async fn prepare_public_state(
|
||||
wallet: &WalletCore,
|
||||
wallet: &mut WalletCore,
|
||||
account_id: AccountId,
|
||||
lookup_signing_key: bool,
|
||||
) -> Result<State, ExecutionFailureKind> {
|
||||
@ -493,7 +493,7 @@ async fn prepare_public_state(
|
||||
}
|
||||
|
||||
async fn prepare_public_keycard_state(
|
||||
wallet: &WalletCore,
|
||||
wallet: &mut WalletCore,
|
||||
account_id: AccountId,
|
||||
key_path: String,
|
||||
) -> Result<State, ExecutionFailureKind> {
|
||||
@ -506,7 +506,7 @@ async fn prepare_public_keycard_state(
|
||||
}
|
||||
|
||||
async fn private_key_tree_acc_preparation(
|
||||
wallet: &WalletCore,
|
||||
wallet: &mut WalletCore,
|
||||
account_id: AccountId,
|
||||
is_pda: bool,
|
||||
) -> Result<AccountPreparedData, ExecutionFailureKind> {
|
||||
@ -520,16 +520,16 @@ async fn private_key_tree_acc_preparation(
|
||||
let from_npk = from_keys.nullifier_public_key;
|
||||
let from_vpk = from_keys.viewing_public_key.clone();
|
||||
|
||||
// TODO: Technically we could allow unauthorized owned accounts, but currently we don't have
|
||||
// support from that in the wallet.
|
||||
let sender_pre = AccountWithMetadata::new(from_acc.account.clone(), true, account_id);
|
||||
|
||||
// TODO: Remove this unwrap, error types must be compatible
|
||||
let proof = wallet
|
||||
.check_private_account_initialized(account_id)
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
// TODO: Technically we could allow unauthorized owned accounts, but currently we don't have
|
||||
// support from that in the wallet.
|
||||
let sender_pre = AccountWithMetadata::new(from_acc.account.clone(), true, account_id);
|
||||
|
||||
let eph_holder = EphemeralKeyHolder::new(&from_vpk);
|
||||
let ssk = eph_holder.calculate_shared_secret_sender();
|
||||
let epk = eph_holder.ephemeral_public_key().clone();
|
||||
@ -548,7 +548,7 @@ async fn private_key_tree_acc_preparation(
|
||||
}
|
||||
|
||||
async fn private_shared_acc_preparation(
|
||||
wallet: &WalletCore,
|
||||
wallet: &mut WalletCore,
|
||||
account_id: AccountId,
|
||||
nsk: NullifierSecretKey,
|
||||
npk: NullifierPublicKey,
|
||||
|
||||
@ -186,7 +186,7 @@ impl AtaSubcommand {
|
||||
async fn handle_list(
|
||||
owner: AccountId,
|
||||
token_definition: Vec<AccountId>,
|
||||
wallet_core: &WalletCore,
|
||||
wallet_core: &mut WalletCore,
|
||||
) -> Result<SubcommandReturnValue> {
|
||||
let ata_program_id = programs::ata().id();
|
||||
|
||||
|
||||
@ -7,7 +7,7 @@
|
||||
reason = "Most of the shadows come from args parsing which is ok"
|
||||
)]
|
||||
|
||||
use std::path::PathBuf;
|
||||
use std::{collections::HashMap, path::PathBuf};
|
||||
|
||||
pub use account_manager::AccountIdentity;
|
||||
use anyhow::{Context as _, Result};
|
||||
@ -29,11 +29,15 @@ use log::info;
|
||||
use sequencer_service_rpc::{RpcClient as _, SequencerClient};
|
||||
use storage::Storage;
|
||||
use tokio::io::AsyncWriteExt as _;
|
||||
use url::Url;
|
||||
|
||||
use crate::{
|
||||
account::{AccountIdWithPrivacy, Label},
|
||||
config::WalletConfigOverrides,
|
||||
multi_client::{Metrics, MultiSequencerClient},
|
||||
multi_client::{
|
||||
Metrics, MetricsUpdate, MultiSequencerClient, extract_metrics_from_path,
|
||||
save_metrics_at_path_with_updates,
|
||||
},
|
||||
poller::TxPoller,
|
||||
storage::key_chain::SharedAccountEntry,
|
||||
};
|
||||
@ -94,22 +98,24 @@ pub struct WalletCore {
|
||||
storage: Storage,
|
||||
storage_path: PathBuf,
|
||||
|
||||
_metrics_path: PathBuf,
|
||||
metrics_path: PathBuf,
|
||||
metrics: HashMap<Url, Metrics>,
|
||||
metric_updates: Vec<MetricsUpdate>,
|
||||
|
||||
pub multi_sequencer_client: MultiSequencerClient,
|
||||
}
|
||||
|
||||
impl WalletCore {
|
||||
/// Construct wallet using [`HOME_DIR_ENV_VAR`] env var for paths or user home dir if not set.
|
||||
pub fn from_env() -> Result<Self> {
|
||||
pub async fn from_env() -> Result<Self> {
|
||||
let config_path = helperfunctions::fetch_config_path()?;
|
||||
let storage_path = helperfunctions::fetch_persistent_storage_path()?;
|
||||
let metrics_path = helperfunctions::fetch_metrics_path()?;
|
||||
|
||||
Self::new_update_chain(config_path, storage_path, metrics_path, None)
|
||||
Self::new_update_chain(config_path, storage_path, metrics_path, None).await
|
||||
}
|
||||
|
||||
pub fn new_update_chain(
|
||||
pub async fn new_update_chain(
|
||||
config_path: PathBuf,
|
||||
storage_path: PathBuf,
|
||||
metrics_path: PathBuf,
|
||||
@ -125,9 +131,10 @@ impl WalletCore {
|
||||
config_overrides,
|
||||
storage,
|
||||
)
|
||||
.await
|
||||
}
|
||||
|
||||
pub fn new_init_storage(
|
||||
pub async fn new_init_storage(
|
||||
config_path: PathBuf,
|
||||
storage_path: PathBuf,
|
||||
metrics_path: PathBuf,
|
||||
@ -141,12 +148,13 @@ impl WalletCore {
|
||||
metrics_path,
|
||||
config_overrides,
|
||||
storage,
|
||||
)?;
|
||||
)
|
||||
.await?;
|
||||
|
||||
Ok((wallet, mnemonic))
|
||||
}
|
||||
|
||||
fn new(
|
||||
async fn new(
|
||||
config_path: PathBuf,
|
||||
storage_path: PathBuf,
|
||||
metrics_path: PathBuf,
|
||||
@ -164,7 +172,10 @@ impl WalletCore {
|
||||
config.apply_overrides(config_overrides);
|
||||
}
|
||||
|
||||
let multi_sequencer_client = MultiSequencerClient::new(&config.sequencers_conn_data)?;
|
||||
let mut metrics = extract_metrics_from_path(&metrics_path)?;
|
||||
|
||||
let multi_sequencer_client =
|
||||
MultiSequencerClient::new(&config.sequencers_conn_data, &mut metrics).await?;
|
||||
|
||||
Ok(Self {
|
||||
config_path,
|
||||
@ -172,7 +183,9 @@ impl WalletCore {
|
||||
config,
|
||||
storage_path,
|
||||
storage,
|
||||
_metrics_path: metrics_path,
|
||||
metrics_path,
|
||||
metrics,
|
||||
metric_updates: vec![],
|
||||
multi_sequencer_client,
|
||||
})
|
||||
}
|
||||
@ -188,24 +201,7 @@ impl WalletCore {
|
||||
}
|
||||
|
||||
pub fn optimal_poller(&self) -> TxPoller {
|
||||
let metrics = self.get_metrics();
|
||||
|
||||
TxPoller::new(
|
||||
self.config(),
|
||||
self.multi_sequencer_client.optimal_client_clone(&metrics),
|
||||
)
|
||||
}
|
||||
|
||||
pub fn optimal_sequencer_client(&self) -> &SequencerClient {
|
||||
let metrics = self.get_metrics();
|
||||
|
||||
self.multi_sequencer_client.optimal_client_ref(&metrics)
|
||||
}
|
||||
|
||||
pub fn optimal_sequencer_client_owned(&self) -> SequencerClient {
|
||||
let metrics = self.get_metrics();
|
||||
|
||||
self.multi_sequencer_client.optimal_client_clone(&metrics)
|
||||
TxPoller::new(self.config(), self.multi_sequencer_client.leader_clone())
|
||||
}
|
||||
|
||||
/// Get storage.
|
||||
@ -244,6 +240,20 @@ impl WalletCore {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn store_metrics(&self) -> Result<()> {
|
||||
save_metrics_at_path_with_updates(
|
||||
self.metrics.clone(),
|
||||
&self.multi_sequencer_client.leader_url,
|
||||
&self.metric_updates,
|
||||
&self.storage_path,
|
||||
)
|
||||
.with_context(|| format!("Failed to store metrics at {}", self.storage_path.display()))?;
|
||||
|
||||
println!("Stored metrics at {}", self.metrics_path.display());
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Store persistent data at home.
|
||||
pub async fn store_config_changes(&self) -> Result<()> {
|
||||
let config = serde_json::to_vec_pretty(&self.config)?;
|
||||
@ -457,22 +467,28 @@ impl WalletCore {
|
||||
}
|
||||
|
||||
/// Get account balance.
|
||||
pub async fn get_account_balance(&self, acc: AccountId) -> Result<u128> {
|
||||
Ok(self
|
||||
.optimal_sequencer_client()
|
||||
.get_account_balance(acc)
|
||||
.await?)
|
||||
pub async fn get_account_balance(&mut self, acc: AccountId) -> Result<u128> {
|
||||
let call_f = async |client: &SequencerClient| client.get_account_balance(acc).await;
|
||||
|
||||
let (call_res, metrics_update) = self.multi_sequencer_client.metered_call(call_f).await;
|
||||
|
||||
self.metric_updates.push(metrics_update);
|
||||
|
||||
Ok(call_res?)
|
||||
}
|
||||
|
||||
/// Get accounts nonces.
|
||||
pub async fn get_accounts_nonces(&self, accs: Vec<AccountId>) -> Result<Vec<Nonce>> {
|
||||
Ok(self
|
||||
.optimal_sequencer_client()
|
||||
.get_accounts_nonces(accs)
|
||||
.await?)
|
||||
pub async fn get_accounts_nonces(&mut self, accs: Vec<AccountId>) -> Result<Vec<Nonce>> {
|
||||
let call_f = async |client: &SequencerClient| client.get_accounts_nonces(accs).await;
|
||||
|
||||
let (call_res, metrics_update) = self.multi_sequencer_client.metered_call(call_f).await;
|
||||
|
||||
self.metric_updates.push(metrics_update);
|
||||
|
||||
Ok(call_res?)
|
||||
}
|
||||
|
||||
pub async fn get_account(&self, account_id: AccountIdWithPrivacy) -> Result<Account> {
|
||||
pub async fn get_account(&mut self, account_id: AccountIdWithPrivacy) -> Result<Account> {
|
||||
match account_id {
|
||||
AccountIdWithPrivacy::Public(acc_id) => self.get_account_public(acc_id).await,
|
||||
AccountIdWithPrivacy::Private(acc_id) => {
|
||||
@ -486,11 +502,14 @@ impl WalletCore {
|
||||
}
|
||||
|
||||
/// Get public account.
|
||||
pub async fn get_account_public(&self, account_id: AccountId) -> Result<Account> {
|
||||
Ok(self
|
||||
.optimal_sequencer_client()
|
||||
.get_account(account_id)
|
||||
.await?)
|
||||
pub async fn get_account_public(&mut self, account_id: AccountId) -> Result<Account> {
|
||||
let call_f = async |client: &SequencerClient| client.get_account(account_id).await;
|
||||
|
||||
let (call_res, metrics_update) = self.multi_sequencer_client.metered_call(call_f).await;
|
||||
|
||||
self.metric_updates.push(metrics_update);
|
||||
|
||||
Ok(call_res?)
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
@ -531,25 +550,32 @@ impl WalletCore {
|
||||
}
|
||||
|
||||
pub async fn check_private_account_initialized(
|
||||
&self,
|
||||
&mut self,
|
||||
account_id: AccountId,
|
||||
) -> Result<Option<MembershipProof>> {
|
||||
if let Some(acc_comm) = self.get_private_account_commitment(account_id) {
|
||||
self.optimal_sequencer_client()
|
||||
.get_proof_for_commitment(acc_comm)
|
||||
.await
|
||||
.map_err(Into::into)
|
||||
let call_f =
|
||||
async |client: &SequencerClient| client.get_proof_for_commitment(acc_comm).await;
|
||||
|
||||
let (call_res, metrics_update) = self.multi_sequencer_client.metered_call(call_f).await;
|
||||
|
||||
self.metric_updates.push(metrics_update);
|
||||
|
||||
call_res.map_err(Into::into)
|
||||
} else {
|
||||
Ok(None)
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn get_commitment_root(&self) -> Result<Option<CommitmentSetDigest>> {
|
||||
let proof = self
|
||||
.optimal_sequencer_client()
|
||||
.get_proof_for_commitment(DUMMY_COMMITMENT)
|
||||
.await?;
|
||||
Ok(proof.map(|p| compute_digest_for_path(&DUMMY_COMMITMENT, &p)))
|
||||
pub async fn get_commitment_root(&mut self) -> Result<Option<CommitmentSetDigest>> {
|
||||
let call_f = async |client: &SequencerClient| {
|
||||
client.get_proof_for_commitment(DUMMY_COMMITMENT).await
|
||||
};
|
||||
|
||||
let (proof, metrics_update) = self.multi_sequencer_client.metered_call(call_f).await;
|
||||
|
||||
self.metric_updates.push(metrics_update);
|
||||
Ok(proof?.map(|p| compute_digest_for_path(&DUMMY_COMMITMENT, &p)))
|
||||
}
|
||||
|
||||
pub fn decode_insert_privacy_preserving_transaction_results(
|
||||
@ -615,7 +641,7 @@ impl WalletCore {
|
||||
}
|
||||
|
||||
pub async fn send_privacy_preserving_tx(
|
||||
&self,
|
||||
&mut self,
|
||||
accounts: Vec<AccountIdentity>,
|
||||
instruction_data: InstructionData,
|
||||
program: &ProgramWithDependencies,
|
||||
@ -627,7 +653,7 @@ impl WalletCore {
|
||||
}
|
||||
|
||||
pub async fn send_privacy_preserving_tx_with_pre_check(
|
||||
&self,
|
||||
&mut self,
|
||||
accounts: Vec<AccountIdentity>,
|
||||
instruction_data: InstructionData,
|
||||
program: &ProgramWithDependencies,
|
||||
@ -677,16 +703,21 @@ impl WalletCore {
|
||||
.map(|keys| keys.ssk)
|
||||
.collect();
|
||||
|
||||
Ok((
|
||||
self.optimal_sequencer_client()
|
||||
let call_f = async |client: &SequencerClient| {
|
||||
client
|
||||
.send_transaction(LeeTransaction::PrivacyPreserving(tx))
|
||||
.await?,
|
||||
shared_secrets,
|
||||
))
|
||||
.await
|
||||
};
|
||||
|
||||
let (call_res, metrics_update) = self.multi_sequencer_client.metered_call(call_f).await;
|
||||
|
||||
self.metric_updates.push(metrics_update);
|
||||
|
||||
Ok((call_res?, shared_secrets))
|
||||
}
|
||||
|
||||
pub async fn send_pub_tx(
|
||||
&self,
|
||||
&mut self,
|
||||
accounts: Vec<AccountIdentity>,
|
||||
instruction_data: InstructionData,
|
||||
program_id: ProgramId,
|
||||
@ -696,7 +727,7 @@ impl WalletCore {
|
||||
}
|
||||
|
||||
pub async fn send_pub_tx_with_pre_check(
|
||||
&self,
|
||||
&mut self,
|
||||
accounts: Vec<AccountIdentity>,
|
||||
instruction_data: InstructionData,
|
||||
program_id: ProgramId,
|
||||
@ -741,14 +772,25 @@ impl WalletCore {
|
||||
|
||||
let tx = lee::public_transaction::PublicTransaction::new(message, witness_set);
|
||||
|
||||
Ok(self
|
||||
.optimal_sequencer_client()
|
||||
.send_transaction(LeeTransaction::Public(tx))
|
||||
.await?)
|
||||
let call_f = async |client: &SequencerClient| {
|
||||
client.send_transaction(LeeTransaction::Public(tx)).await
|
||||
};
|
||||
|
||||
let (call_res, metrics_update) = self.multi_sequencer_client.metered_call(call_f).await;
|
||||
|
||||
self.metric_updates.push(metrics_update);
|
||||
|
||||
Ok(call_res?)
|
||||
}
|
||||
|
||||
pub async fn sync_to_latest_block(&mut self) -> Result<u64> {
|
||||
let latest_block_id = self.optimal_sequencer_client().get_last_block_id().await?;
|
||||
let call_f = async |client: &SequencerClient| client.get_last_block_id().await;
|
||||
|
||||
let (call_res, metrics_update) = self.multi_sequencer_client.metered_call(call_f).await;
|
||||
|
||||
self.metric_updates.push(metrics_update);
|
||||
|
||||
let latest_block_id = call_res?;
|
||||
println!("Latest block is {latest_block_id}");
|
||||
self.sync_to_block(latest_block_id).await?;
|
||||
Ok(latest_block_id)
|
||||
|
||||
@ -1,24 +1,57 @@
|
||||
use std::{collections::HashMap, io::Write, path::Path};
|
||||
|
||||
use anyhow::{Context, Result};
|
||||
use sequencer_service_rpc::{SequencerClient, SequencerClientBuilder};
|
||||
use sequencer_service_rpc::{RpcClient, SequencerClient, SequencerClientBuilder};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use url::Url;
|
||||
|
||||
use crate::config::SequencerConnectionData;
|
||||
|
||||
pub const CALLIBRATION_LIMIT: usize = 100;
|
||||
|
||||
pub fn extract_metrics_from_path(path: &Path) -> Result<HashMap<Url, Metrics>, anyhow::Error> {
|
||||
match std::fs::File::open(path) {
|
||||
Ok(file) => {
|
||||
let reader = std::io::BufReader::new(file);
|
||||
Ok(serde_json::from_reader(reader)?)
|
||||
}
|
||||
Err(err) if err.kind() == std::io::ErrorKind::NotFound => {
|
||||
println!("Metrics not found, choosing empty");
|
||||
Ok(HashMap::new())
|
||||
}
|
||||
Err(err) => Err(err).context("IO error"),
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub struct Metrics {
|
||||
pub latency_avg: u64,
|
||||
pub latency_var: u64,
|
||||
pub last_block_id: u64,
|
||||
pub latency_avg: f32,
|
||||
pub latency_var: f32,
|
||||
pub sample_size: usize,
|
||||
pub latest_block_id: u64,
|
||||
pub errors: u64,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct MetricsUpdate {
|
||||
pub latency: f32,
|
||||
pub new_latest_block_id: Option<u64>,
|
||||
pub is_failed: bool,
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct MultiSequencerClient {
|
||||
pub client_list: Vec<SequencerClient>,
|
||||
pub client_list: HashMap<Url, SequencerClient>,
|
||||
pub leader: SequencerClient,
|
||||
pub leader_url: Url,
|
||||
}
|
||||
|
||||
impl MultiSequencerClient {
|
||||
pub fn new(conn_data: &[SequencerConnectionData]) -> Result<Self> {
|
||||
let mut client_list = vec![];
|
||||
pub async fn new(
|
||||
conn_data: &[SequencerConnectionData],
|
||||
metrics: &mut HashMap<Url, Metrics>,
|
||||
) -> Result<Self> {
|
||||
let mut client_list = HashMap::new();
|
||||
|
||||
for SequencerConnectionData {
|
||||
sequencer_addr,
|
||||
@ -43,17 +76,173 @@ impl MultiSequencerClient {
|
||||
.context("Failed to create sequencer client")?
|
||||
};
|
||||
|
||||
client_list.push(sequencer_client);
|
||||
// If there is no metrics for client, callibrate it
|
||||
if !metrics.contains_key(sequencer_addr) {
|
||||
metrics.insert(
|
||||
sequencer_addr.clone(),
|
||||
callibration(sequencer_client.clone()).await,
|
||||
);
|
||||
}
|
||||
|
||||
client_list.insert(sequencer_addr.clone(), sequencer_client);
|
||||
}
|
||||
|
||||
Ok(Self { client_list })
|
||||
let (leader_url, leader) = choose_leader(&client_list, metrics);
|
||||
|
||||
Ok(Self {
|
||||
client_list,
|
||||
leader,
|
||||
leader_url,
|
||||
})
|
||||
}
|
||||
|
||||
pub fn optimal_client_ref(&self, _metrics: &[Metrics]) -> &SequencerClient {
|
||||
todo!();
|
||||
pub fn leader_ref(&self) -> &SequencerClient {
|
||||
&self.leader
|
||||
}
|
||||
|
||||
pub fn optimal_client_clone(&self, _metrics: &[Metrics]) -> SequencerClient {
|
||||
todo!();
|
||||
pub fn leader_clone(&self) -> SequencerClient {
|
||||
self.leader.clone()
|
||||
}
|
||||
|
||||
pub async fn metered_call<R, E, I: AsyncFnOnce(&SequencerClient) -> Result<R, E>>(
|
||||
&self,
|
||||
call: I,
|
||||
) -> (Result<R, E>, MetricsUpdate) {
|
||||
let call_last_block = self.leader_ref().get_last_block_id();
|
||||
|
||||
let now = tokio::time::Instant::now();
|
||||
|
||||
let (call_future_res, call_last_block_res) =
|
||||
tokio::join!(call(self.leader_ref()), call_last_block);
|
||||
|
||||
let latency = tokio::time::Instant::now().duration_since(now).as_millis() as f32;
|
||||
let is_failed = call_future_res.is_err() || call_last_block_res.is_err();
|
||||
let mut new_last_block = None;
|
||||
|
||||
if let Ok(last_block) = call_last_block_res {
|
||||
new_last_block = Some(last_block);
|
||||
}
|
||||
|
||||
let metrics_update = MetricsUpdate {
|
||||
latency,
|
||||
new_latest_block_id: new_last_block,
|
||||
is_failed,
|
||||
};
|
||||
|
||||
(call_future_res, metrics_update)
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn callibration(client: SequencerClient) -> Metrics {
|
||||
let mut latencies = vec![];
|
||||
let mut latest_block_id = 0;
|
||||
let mut errors = 0;
|
||||
|
||||
for _ in 0..CALLIBRATION_LIMIT {
|
||||
let now = tokio::time::Instant::now();
|
||||
|
||||
let block_id = client.get_last_block_id().await;
|
||||
|
||||
let latency = tokio::time::Instant::now().duration_since(now).as_millis();
|
||||
|
||||
let Ok(block_id) = block_id else {
|
||||
errors += 1;
|
||||
continue;
|
||||
};
|
||||
|
||||
latest_block_id = block_id;
|
||||
latencies.push(latency);
|
||||
}
|
||||
|
||||
// Precision loss if fine there
|
||||
let sample_size = latencies.len();
|
||||
let latency_avg = (latencies.iter().fold(0, |acc, x| acc + x) as f32) / (sample_size as f32);
|
||||
let latency_var = (latencies.iter().fold(0f32, |acc, x| {
|
||||
acc + ((*x as f32) - latency_avg) * ((*x as f32) - latency_avg)
|
||||
}) / (sample_size as f32))
|
||||
.sqrt();
|
||||
|
||||
Metrics {
|
||||
latency_avg,
|
||||
latency_var,
|
||||
sample_size,
|
||||
latest_block_id,
|
||||
errors,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn choose_leader(
|
||||
_client_list: &HashMap<Url, SequencerClient>,
|
||||
_metrics: &HashMap<Url, Metrics>,
|
||||
) -> (Url, SequencerClient) {
|
||||
todo!()
|
||||
}
|
||||
|
||||
pub fn save_metrics_at_path(
|
||||
metrics: &HashMap<Url, Metrics>,
|
||||
path: &Path,
|
||||
) -> Result<(), anyhow::Error> {
|
||||
let metrics_serialized = serde_json::to_vec_pretty(metrics)?;
|
||||
let mut file = std::fs::File::create(path).context("Failed to create file")?;
|
||||
file.write_all(&metrics_serialized)
|
||||
.context("Failed to write to file")?;
|
||||
file.sync_all().context("Failed to sync file")?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn save_metrics_at_path_with_updates(
|
||||
mut metrics: HashMap<Url, Metrics>,
|
||||
leader_url: &Url,
|
||||
metric_updates: &[MetricsUpdate],
|
||||
path: &Path,
|
||||
) -> Result<(), anyhow::Error> {
|
||||
let leader_metric = metrics
|
||||
.get_mut(leader_url)
|
||||
.ok_or(anyhow::anyhow!("Leader URL is in present in metrics"))?;
|
||||
|
||||
let (failure_count, latest_block_id, cumulative_latency) =
|
||||
metric_updates.iter().fold((0u64, None, 0f32), |acc, x| {
|
||||
let MetricsUpdate {
|
||||
latency,
|
||||
new_latest_block_id,
|
||||
is_failed,
|
||||
} = x;
|
||||
(
|
||||
if *is_failed { acc.0 + 1 } else { acc.0 },
|
||||
acc.1.map(|inner| {
|
||||
if let Some(new_latest_block_id) = new_latest_block_id {
|
||||
if inner < *new_latest_block_id {
|
||||
*new_latest_block_id
|
||||
} else {
|
||||
inner
|
||||
}
|
||||
} else {
|
||||
inner
|
||||
}
|
||||
}),
|
||||
acc.2 + latency,
|
||||
)
|
||||
});
|
||||
|
||||
leader_metric.errors += failure_count;
|
||||
if let Some(latest_block_id) = latest_block_id {
|
||||
leader_metric.latest_block_id = latest_block_id;
|
||||
}
|
||||
|
||||
let orig_size_f = leader_metric.sample_size as f32;
|
||||
let mod_size_f = (leader_metric.sample_size + metric_updates.len()) as f32;
|
||||
|
||||
let latency_avg_old = leader_metric.latency_avg;
|
||||
let latency_avg_new =
|
||||
(leader_metric.latency_avg * orig_size_f + cumulative_latency) / mod_size_f;
|
||||
|
||||
let latency_disp_new = (latency_avg_old - latency_avg_new)
|
||||
* ((3f32 * latency_avg_old + latency_avg_new) * orig_size_f / mod_size_f)
|
||||
+ (leader_metric.latency_var * leader_metric.latency_var) * orig_size_f / mod_size_f;
|
||||
|
||||
leader_metric.latency_avg = latency_avg_new;
|
||||
leader_metric.latency_var = latency_disp_new.sqrt();
|
||||
|
||||
save_metrics_at_path(&metrics, path)
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user