From 01c6cb0aafe17f75c9c4c9bc91ad2153c7738df6 Mon Sep 17 00:00:00 2001 From: Mitchell Turner Date: Tue, 28 Jan 2025 19:41:57 -0700 Subject: [PATCH] Add metrics to gas price service (#2635) --- CHANGELOG.md | 3 + Cargo.lock | 1 + bin/fuel-core/src/cli/run.rs | 3 + .../service/adapters/gas_price_adapters.rs | 1 + crates/fuel-core/src/service/config.rs | 3 + crates/metrics/src/config.rs | 1 + crates/metrics/src/gas_price_metrics.rs | 98 +++++++++++++++++++ crates/metrics/src/lib.rs | 1 + crates/services/gas_price_service/Cargo.toml | 1 + .../gas_price_service/src/v1/metadata.rs | 13 +++ .../gas_price_service/src/v1/service.rs | 78 ++++++++++++++- .../gas_price_service/src/v1/tests.rs | 6 ++ .../src/v1/uninitialized_task.rs | 3 + 13 files changed, 208 insertions(+), 4 deletions(-) create mode 100644 crates/metrics/src/gas_price_metrics.rs diff --git a/CHANGELOG.md b/CHANGELOG.md index 8269b825f2e..ef58b7360af 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,9 @@ and this project adheres to [Semantic Versioning](http://semver.org/). ## [Unreleased] +### Added +- [2635](https://github.com/FuelLabs/fuel-core/pull/2635): Add metrics to gas price service + ### Changed - [2387](https://github.com/FuelLabs/fuel-core/pull/2387): Update description `tx-max-depth` flag. - [2630](https://github.com/FuelLabs/fuel-core/pull/2630): Removed some noisy `tracing::info!` logs diff --git a/Cargo.lock b/Cargo.lock index 6d9799fb398..9647bf89221 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3661,6 +3661,7 @@ dependencies = [ "anyhow", "async-trait", "enum-iterator", + "fuel-core-metrics", "fuel-core-services", "fuel-core-storage", "fuel-core-types 0.41.4", diff --git a/bin/fuel-core/src/cli/run.rs b/bin/fuel-core/src/cli/run.rs index fdf9feaf918..1fadb8920ed 100644 --- a/bin/fuel-core/src/cli/run.rs +++ b/bin/fuel-core/src/cli/run.rs @@ -562,6 +562,8 @@ impl Command { disabled_metrics.is_enabled(Module::Importer), ); + let gas_price_metrics = disabled_metrics.is_enabled(Module::GasPrice); + let da_compression = match da_compression { Some(retention) => { DaCompressionConfig::Enabled(fuel_core_compression::Config { @@ -710,6 +712,7 @@ impl Command { max_da_gas_price_change_percent: gas_price_change_percent, da_gas_price_p_component, da_gas_price_d_component, + gas_price_metrics, activity_normal_range_size: 100, activity_capped_range_size: 0, activity_decrease_range_size: 0, diff --git a/crates/fuel-core/src/service/adapters/gas_price_adapters.rs b/crates/fuel-core/src/service/adapters/gas_price_adapters.rs index 0e72be48f75..b8c5d849e69 100644 --- a/crates/fuel-core/src/service/adapters/gas_price_adapters.rs +++ b/crates/fuel-core/src/service/adapters/gas_price_adapters.rs @@ -83,6 +83,7 @@ impl From for V1AlgorithmConfig { starting_recorded_height: value .starting_recorded_height .map(BlockHeight::from), + record_metrics: value.gas_price_metrics, } } } diff --git a/crates/fuel-core/src/service/config.rs b/crates/fuel-core/src/service/config.rs index 2623faae779..39ff4cc64c5 100644 --- a/crates/fuel-core/src/service/config.rs +++ b/crates/fuel-core/src/service/config.rs @@ -91,6 +91,7 @@ pub struct Config { pub max_da_gas_price_change_percent: u16, pub da_gas_price_p_component: i64, pub da_gas_price_d_component: i64, + pub gas_price_metrics: bool, pub activity_normal_range_size: u16, pub activity_capped_range_size: u16, pub activity_decrease_range_size: u16, @@ -148,6 +149,7 @@ impl Config { let gas_price_change_percent = 0; let min_gas_price = 0; let gas_price_threshold_percent = 50; + let gas_price_metrics = false; Self { graphql_config: GraphQLConfig { @@ -214,6 +216,7 @@ impl Config { max_da_gas_price_change_percent: 0, da_gas_price_p_component: 0, da_gas_price_d_component: 0, + gas_price_metrics, activity_normal_range_size: 0, activity_capped_range_size: 0, activity_decrease_range_size: 0, diff --git a/crates/metrics/src/config.rs b/crates/metrics/src/config.rs index 107c9fb91eb..7fac67a2d65 100644 --- a/crates/metrics/src/config.rs +++ b/crates/metrics/src/config.rs @@ -15,6 +15,7 @@ pub enum Module { Producer, TxPool, GraphQL, // TODO[RC]: Not used... yet. + GasPrice, } /// Configuration for disabling metrics. diff --git a/crates/metrics/src/gas_price_metrics.rs b/crates/metrics/src/gas_price_metrics.rs new file mode 100644 index 00000000000..bc32a4869c5 --- /dev/null +++ b/crates/metrics/src/gas_price_metrics.rs @@ -0,0 +1,98 @@ +use crate::global_registry; +use prometheus_client::metrics::gauge::Gauge; +use std::sync::OnceLock; + +#[derive(Debug)] +pub struct GasPriceMetrics { + pub real_gas_price: Gauge, + pub exec_gas_price: Gauge, + pub da_gas_price: Gauge, + pub total_reward: Gauge, + pub total_known_costs: Gauge, + pub predicted_profit: Gauge, + pub unrecorded_bytes: Gauge, + pub latest_cost_per_byte: Gauge, + pub recorded_height: Gauge, +} + +impl Default for GasPriceMetrics { + fn default() -> Self { + let real_gas_price = Gauge::default(); + let exec_gas_price = Gauge::default(); + let da_gas_price = Gauge::default(); + let total_reward = Gauge::default(); + let total_known_costs = Gauge::default(); + let predicted_profit = Gauge::default(); + let unrecorded_bytes = Gauge::default(); + let latest_cost_per_byte = Gauge::default(); + let recorded_height = Gauge::default(); + + let metrics = GasPriceMetrics { + real_gas_price, + exec_gas_price, + da_gas_price, + total_reward, + total_known_costs, + predicted_profit, + unrecorded_bytes, + latest_cost_per_byte, + recorded_height, + }; + + let mut registry = global_registry().registry.lock(); + registry.register( + "gas_price_service_real_gas_price", + "The real gas price used on the most recent block", + metrics.real_gas_price.clone(), + ); + registry.register( + "gas_price_service_exec_gas_price", + "The requested execution gas price for the next block", + metrics.exec_gas_price.clone(), + ); + registry.register( + "gas_price_service_da_gas_price", + "The requested data availability gas price for the next block", + metrics.da_gas_price.clone(), + ); + registry.register( + "gas_price_service_total_reward", + "The total reward received from DA gas price fees", + metrics.total_reward.clone(), + ); + registry.register( + "gas_price_service_total_known_costs", + "The total known costs for committing L2 blocks to DA", + metrics.total_known_costs.clone(), + ); + registry.register( + "gas_price_service_predicted_profit", + "The predicted profit based on the rewards, known costs, and predicted costs from price per byte", + metrics.predicted_profit.clone(), + ); + registry.register( + "gas_price_service_unrecorded_bytes", + "The total bytes of all L2 blocks waiting to be recorded on DA", + metrics.unrecorded_bytes.clone(), + ); + registry.register( + "gas_price_service_latest_cost_per_byte", + "The latest cost per byte to record L2 blocks on DA", + metrics.latest_cost_per_byte.clone(), + ); + + registry.register( + "gas_price_service_recorded_height", + "The height of the latest L2 block recorded on DA", + metrics.recorded_height.clone(), + ); + + metrics + } +} + +static GAS_PRICE_METRICS: OnceLock = OnceLock::new(); + +pub fn gas_price_metrics() -> &'static GasPriceMetrics { + GAS_PRICE_METRICS.get_or_init(GasPriceMetrics::default) +} diff --git a/crates/metrics/src/lib.rs b/crates/metrics/src/lib.rs index 399b34c51e5..0c3c4e1dfcc 100644 --- a/crates/metrics/src/lib.rs +++ b/crates/metrics/src/lib.rs @@ -23,6 +23,7 @@ mod buckets; pub mod config; pub mod core_metrics; pub mod futures; +pub mod gas_price_metrics; pub mod graphql_metrics; pub mod importer; pub mod p2p_metrics; diff --git a/crates/services/gas_price_service/Cargo.toml b/crates/services/gas_price_service/Cargo.toml index cda853ca4bd..30c77d61230 100644 --- a/crates/services/gas_price_service/Cargo.toml +++ b/crates/services/gas_price_service/Cargo.toml @@ -12,6 +12,7 @@ repository = { workspace = true } anyhow = { workspace = true } async-trait = { workspace = true } enum-iterator = { workspace = true } +fuel-core-metrics = { workspace = true } fuel-core-services = { workspace = true } fuel-core-storage = { workspace = true, features = ["std"] } fuel-core-types = { workspace = true, features = ["std"] } diff --git a/crates/services/gas_price_service/src/v1/metadata.rs b/crates/services/gas_price_service/src/v1/metadata.rs index 8c13c5e02fc..c583b694e87 100644 --- a/crates/services/gas_price_service/src/v1/metadata.rs +++ b/crates/services/gas_price_service/src/v1/metadata.rs @@ -59,6 +59,18 @@ impl V1Metadata { }; Ok(metadata) } + + pub fn new_exec_gas_price(&self) -> u64 { + self.new_scaled_exec_price + .checked_div(self.gas_price_factor.get()) + .unwrap_or(0) + } + + pub fn new_da_gas_price(&self) -> u64 { + self.new_scaled_da_gas_price + .checked_div(self.gas_price_factor.get()) + .unwrap_or(0) + } } #[derive(Debug, Clone, PartialEq)] @@ -82,6 +94,7 @@ pub struct V1AlgorithmConfig { /// The interval at which the `DaSourceService` polls for new data pub da_poll_interval: Option, pub starting_recorded_height: Option, + pub record_metrics: bool, } pub fn updater_from_config( diff --git a/crates/services/gas_price_service/src/v1/service.rs b/crates/services/gas_price_service/src/v1/service.rs index a0876d50f90..4353fa9a785 100644 --- a/crates/services/gas_price_service/src/v1/service.rs +++ b/crates/services/gas_price_service/src/v1/service.rs @@ -40,6 +40,7 @@ use crate::{ }; use anyhow::anyhow; use async_trait::async_trait; +use fuel_core_metrics::gas_price_metrics::gas_price_metrics; use fuel_core_services::{ RunnableService, RunnableTask, @@ -48,7 +49,10 @@ use fuel_core_services::{ StateWatcher, TaskNextAction, }; -use fuel_core_types::fuel_types::BlockHeight; +use fuel_core_types::{ + fuel_types::BlockHeight, + services::txpool::Metadata, +}; use fuel_gas_price_algorithm::{ v0::AlgorithmUpdaterV0, v1::{ @@ -127,6 +131,8 @@ where latest_l2_block: Arc, /// Initial Recorded Height initial_recorded_height: Option, + /// Metrics will be recorded if true + record_metrics: bool, } impl GasPriceServiceV1 @@ -204,6 +210,7 @@ where storage_tx_provider: AtomicStorage, latest_l2_block: Arc, initial_recorded_height: Option, + record_metrics: bool, ) -> Self { let da_source_channel = da_source_adapter_handle.shared.clone().subscribe(); Self { @@ -217,6 +224,7 @@ where storage_tx_provider, latest_l2_block, initial_recorded_height, + record_metrics, } } @@ -251,17 +259,19 @@ where block_gas_capacity: u64, block_bytes: u64, block_fees: u64, + gas_price: u64, ) -> anyhow::Result<()> { let capacity = Self::validate_block_gas_capacity(block_gas_capacity)?; let mut storage_tx = self.storage_tx_provider.begin_transaction()?; - let mut new_recorded_height = match storage_tx + let (old_recorded_height, mut new_recorded_height) = match storage_tx .get_recorded_height() .map_err(|err| anyhow!(err))? { - Some(_) => None, + Some(old) => (Some(old), None), None => { // Sets it on first run - self.initial_recorded_height.take() + let initial = self.initial_recorded_height.take(); + (None, initial) } }; @@ -304,11 +314,60 @@ where let new_algo = self.algorithm_updater.algorithm(); tracing::debug!("Updating gas price: {}", &new_algo.calculate()); self.shared_algo.update(new_algo); + let best_recorded_height = new_recorded_height.or(old_recorded_height); + Self::record_metrics(&metadata, gas_price, best_recorded_height); // Clear the buffer after committing changes self.da_block_costs_buffer.clear(); Ok(()) } + fn record_metrics( + metadata: &UpdaterMetadata, + gas_price: u64, + recorded_height: Option, + ) { + if let UpdaterMetadata::V1(v1_metadata) = metadata { + let metrics = gas_price_metrics(); + let real_gas_price_i64 = gas_price.try_into().unwrap_or(i64::MAX); + let exec_gas_price_i64 = v1_metadata + .new_exec_gas_price() + .try_into() + .unwrap_or(i64::MAX); + let da_gas_price_i64 = v1_metadata + .new_da_gas_price() + .try_into() + .unwrap_or(i64::MAX); + let total_reward_i64 = + v1_metadata.total_da_rewards.try_into().unwrap_or(i64::MAX); + let total_known_costs_i64 = v1_metadata + .latest_known_total_da_cost + .try_into() + .unwrap_or(i64::MAX); + let predicted_profit_i64 = + v1_metadata.last_profit.try_into().unwrap_or(i64::MAX); + let unrecorded_bytes_i64 = v1_metadata + .unrecorded_block_bytes + .try_into() + .unwrap_or(i64::MAX); + let latest_cost_per_byte_i64 = v1_metadata + .latest_da_cost_per_byte + .try_into() + .unwrap_or(i64::MAX); + metrics.real_gas_price.set(real_gas_price_i64); + metrics.exec_gas_price.set(exec_gas_price_i64); + metrics.da_gas_price.set(da_gas_price_i64); + metrics.total_reward.set(total_reward_i64); + metrics.total_known_costs.set(total_known_costs_i64); + metrics.predicted_profit.set(predicted_profit_i64); + metrics.unrecorded_bytes.set(unrecorded_bytes_i64); + metrics.latest_cost_per_byte.set(latest_cost_per_byte_i64); + if let Some(height) = recorded_height { + let inner: u32 = height.into(); + metrics.recorded_height.set(inner.into()); + } + } + } + async fn apply_block_info_to_gas_algorithm( &mut self, l2_block: BlockInfo, @@ -328,6 +387,7 @@ where block_gas_capacity, block_bytes, block_fees, + gas_price, .. } => { self.handle_normal_block( @@ -336,6 +396,7 @@ where block_gas_capacity, block_bytes, block_fees, + gas_price, ) .await?; } @@ -597,6 +658,7 @@ mod tests { block_activity_threshold: 20, da_poll_interval: None, starting_recorded_height: None, + record_metrics: false, }; let inner = database(); let (algo_updater, shared_algo) = initialize_algorithm( @@ -633,6 +695,7 @@ mod tests { inner, latest_l2_height, None, + false, ); let read_algo = service.next_block_algorithm(); let mut watcher = StateWatcher::default(); @@ -685,6 +748,7 @@ mod tests { block_activity_threshold: 20, da_poll_interval: None, starting_recorded_height: None, + record_metrics: false, }; let mut inner = database(); let mut tx = inner.write_transaction(); @@ -730,6 +794,7 @@ mod tests { inner, latest_l2_block, None, + false, ); let read_algo = service.next_block_algorithm(); let initial_price = read_algo.next_gas_price(); @@ -766,6 +831,7 @@ mod tests { block_activity_threshold: 20, da_poll_interval: None, starting_recorded_height: None, + record_metrics: false, } } @@ -835,6 +901,7 @@ mod tests { inner, latest_l2_height, None, + false, ); let read_algo = service.next_block_algorithm(); let initial_price = read_algo.next_gas_price(); @@ -928,6 +995,7 @@ mod tests { inner, latest_l2_height, None, + false, ); let read_algo = service.next_block_algorithm(); let initial_price = read_algo.next_gas_price(); @@ -971,6 +1039,7 @@ mod tests { block_activity_threshold: 20, da_poll_interval: None, starting_recorded_height: None, + record_metrics: false, } } @@ -1110,6 +1179,7 @@ mod tests { atomic_storage, latest_l2_height, Some(expected_recorded_height), + false, ); let read_algo = service.next_block_algorithm(); let initial_price = read_algo.next_gas_price(); diff --git a/crates/services/gas_price_service/src/v1/tests.rs b/crates/services/gas_price_service/src/v1/tests.rs index 81edb427ff4..d1a80b2c4ec 100644 --- a/crates/services/gas_price_service/src/v1/tests.rs +++ b/crates/services/gas_price_service/src/v1/tests.rs @@ -302,6 +302,7 @@ fn zero_threshold_arbitrary_config() -> V1AlgorithmConfig { block_activity_threshold: 0, da_poll_interval: None, starting_recorded_height: None, + record_metrics: false, } } @@ -338,6 +339,7 @@ fn different_arb_config() -> V1AlgorithmConfig { block_activity_threshold: 0, da_poll_interval: None, starting_recorded_height: None, + record_metrics: false, } } @@ -409,6 +411,7 @@ async fn next_gas_price__affected_by_new_l2_block() { inner, latest_l2_height, None, + false, ); let read_algo = service.next_block_algorithm(); @@ -468,6 +471,7 @@ async fn run__new_l2_block_saves_old_metadata() { inner, latest_l2_height, None, + false, ); let mut watcher = StateWatcher::started(); @@ -529,6 +533,7 @@ async fn run__new_l2_block_updates_latest_gas_price_arc() { inner, latest_l2_height, None, + false, ); let mut watcher = StateWatcher::started(); @@ -589,6 +594,7 @@ async fn run__updates_da_service_latest_l2_height() { inner, latest_l2_height, None, + false, ); let mut watcher = StateWatcher::started(); diff --git a/crates/services/gas_price_service/src/v1/uninitialized_task.rs b/crates/services/gas_price_service/src/v1/uninitialized_task.rs index 9ebc9c690ab..35a1efdba53 100644 --- a/crates/services/gas_price_service/src/v1/uninitialized_task.rs +++ b/crates/services/gas_price_service/src/v1/uninitialized_task.rs @@ -211,6 +211,7 @@ where ), }; + let record_metrics = self.config.record_metrics; let poll_duration = self.config.da_poll_interval; let latest_l2_height = Arc::new(AtomicU32::new(latest_block_height)); @@ -233,6 +234,7 @@ where self.gas_price_db, Arc::clone(&latest_l2_height), Some(starting_recorded_height), + record_metrics, ); Ok(service) } else { @@ -261,6 +263,7 @@ where self.gas_price_db, Arc::clone(&latest_l2_height), Some(starting_recorded_height), + record_metrics, ); Ok(service) }