diff --git a/Cargo.lock b/Cargo.lock index e4eab65f..eec70062 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -794,6 +794,29 @@ version = "1.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f2032f911046de80f0a198e0901378627c33f59ea0ac00e363d481118bd70a53" +[[package]] +name = "aws-lc-rs" +version = "1.17.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "00bdb5da18dac48ca2cc7cd4a98e533e8635a58e2361d13a1a4ee3888e0d72f1" +dependencies = [ + "aws-lc-sys", + "zeroize", +] + +[[package]] +name = "aws-lc-sys" +version = "0.43.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "43103168cc76fe62678a375e722fc9cb3a0146159ac5828bc4f0dfd755c2224c" +dependencies = [ + "cc", + "cmake", + "dunce", + "fs_extra", + "pkg-config", +] + [[package]] name = "axum" version = "0.7.9" @@ -1589,6 +1612,15 @@ dependencies = [ "lee_core", ] +[[package]] +name = "cmake" +version = "0.1.58" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c0f78a02292a74a88ac736019ab962ece0bc380e3f977bf72e376c5d78ff0678" +dependencies = [ + "cc", +] + [[package]] name = "cmov" version = "0.5.4" @@ -1655,6 +1687,7 @@ dependencies = [ "serde", "serde_with", "sha2 0.10.9", + "strum 0.28.0", "system_accounts", "thiserror 2.0.18", ] @@ -2554,6 +2587,12 @@ version = "1.0.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4c3cf4824e2d5f025c7b531afcb2325364084a16806f6d47fbc1f5fbd9960590" +[[package]] +name = "dunce" +version = "1.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "92773504d58c093f6de2459af4af33faa518c13451eb8f2b5698ed3d36e7c813" + [[package]] name = "dyn-clone" version = "1.0.20" @@ -2858,6 +2897,17 @@ dependencies = [ "pin-project-lite", ] +[[package]] +name = "evmap" +version = "11.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1b8874945f036109c72242964c1174cf99434e30cfa45bf45fedc983f50046f8" +dependencies = [ + "hashbag", + "left-right", + "smallvec", +] + [[package]] name = "example_program_deployment_methods" version = "0.1.0" @@ -3036,6 +3086,12 @@ version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d9c4f5dac5e15c24eb999c26181a6ca40b39fe946cbe4c263c7209467bc83af2" +[[package]] +name = "foldhash" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77ce24cb58228fbb8aa041425bb1050850ac19177686ea6e0f41a70416f56fdb" + [[package]] name = "foreign-types" version = "0.5.0" @@ -3072,6 +3128,12 @@ dependencies = [ "percent-encoding", ] +[[package]] +name = "fs_extra" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42703706b716c37f96a77aea830392ad231f44c9e9a67872fa5548707e11b11c" + [[package]] name = "funty" version = "2.0.0" @@ -3231,6 +3293,21 @@ dependencies = [ "num-traits", ] +[[package]] +name = "generator" +version = "0.8.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b3b854b0e584ead1a33f18b2fcad7cf7be18b3875c78816b753639aa501513ae" +dependencies = [ + "cc", + "cfg-if", + "libc", + "log", + "rustversion", + "windows-link", + "windows-result", +] + [[package]] name = "generic-array" version = "0.14.7" @@ -3436,6 +3513,12 @@ dependencies = [ "byteorder", ] +[[package]] +name = "hashbag" +version = "0.1.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7040a10f52cba493ddb09926e15d10a9d8a28043708a405931fe4c6f19fac064" + [[package]] name = "hashbrown" version = "0.12.3" @@ -3459,7 +3542,16 @@ checksum = "9229cfe53dfd69f0609a49f65461bd93001ea1ef889cd5529dd176593f5338a1" dependencies = [ "allocator-api2", "equivalent", - "foldhash", + "foldhash 0.1.5", +] + +[[package]] +name = "hashbrown" +version = "0.16.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "841d1cc9bed7f9236f321df977030373f4a4163ae1a7dbfe1a51a2c1a51d9100" +dependencies = [ + "foldhash 0.2.0", ] [[package]] @@ -3817,6 +3909,7 @@ dependencies = [ "hyper-util", "log", "rustls", + "rustls-native-certs", "tokio", "tokio-rustls", "tower-service", @@ -4890,6 +4983,17 @@ dependencies = [ "thiserror 2.0.18", ] +[[package]] +name = "left-right" +version = "0.11.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8bc015ded5d9b3054dbbdb63332cdd6ee42352ccef19e911e25117490e2f48ee" +dependencies = [ + "crossbeam-utils", + "loom", + "slab", +] + [[package]] name = "leptos" version = "0.8.19" @@ -5896,7 +6000,7 @@ dependencies = [ "multiaddr", "num-bigint 0.4.6", "serde", - "strum", + "strum 0.27.2", "thiserror 2.0.18", "time", "tracing", @@ -6435,6 +6539,19 @@ dependencies = [ "prost-types 0.13.5", ] +[[package]] +name = "loom" +version = "0.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "419e0dc8046cb947daa77eb95ae174acfbddb7673b4151f56d1eed8e93fbfaca" +dependencies = [ + "cfg-if", + "generator", + "scoped-tls", + "tracing", + "tracing-subscriber 0.3.23", +] + [[package]] name = "lru" version = "0.12.5" @@ -6613,6 +6730,7 @@ dependencies = [ name = "mempool" version = "0.1.0" dependencies = [ + "futures", "tokio", ] @@ -6643,6 +6761,56 @@ dependencies = [ "paste", ] +[[package]] +name = "metrics" +version = "0.24.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "89550ee9f79e88fef3119de263694973a8adb26c21d75322164fb8c493039fe2" +dependencies = [ + "portable-atomic", + "rapidhash", +] + +[[package]] +name = "metrics-exporter-prometheus" +version = "0.18.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1db0d8f1fc9e62caebd0319e11eaec5822b0186c171568f0480b46a0137f9108" +dependencies = [ + "base64 0.22.1", + "evmap", + "http-body-util", + "hyper", + "hyper-rustls", + "hyper-util", + "indexmap 2.14.0", + "ipnet", + "metrics", + "metrics-util", + "quanta", + "rustls", + "thiserror 2.0.18", + "tokio", + "tracing", +] + +[[package]] +name = "metrics-util" +version = "0.20.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "96f8722f8562635f92f8ed992f26df0532266eb03d5202607c20c0d7e9745e13" +dependencies = [ + "crossbeam-epoch", + "crossbeam-utils", + "hashbrown 0.16.1", + "metrics", + "quanta", + "rand 0.9.4", + "rand_xoshiro", + "rapidhash", + "sketches-ddsketch", +] + [[package]] name = "mime" version = "0.3.17" @@ -7965,6 +8133,21 @@ dependencies = [ "parking_lot", ] +[[package]] +name = "quanta" +version = "0.12.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f3ab5a9d756f0d97bdc89019bd2e4ea098cf9cde50ee7564dde6b81ccc8f06c7" +dependencies = [ + "crossbeam-utils", + "libc", + "once_cell", + "raw-cpuid", + "wasi", + "web-sys", + "winapi", +] + [[package]] name = "quick-protobuf" version = "0.8.1" @@ -8177,6 +8360,33 @@ dependencies = [ "rand_core 0.9.5", ] +[[package]] +name = "rand_xoshiro" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f703f4665700daf5512dcca5f43afa6af89f09db47fb56be587f80636bda2d41" +dependencies = [ + "rand_core 0.9.5", +] + +[[package]] +name = "rapidhash" +version = "4.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5da7e78a036ce858e8d55b7e7dc8ba3a88b78350fd2155d3591bbd966b58589e" +dependencies = [ + "rustversion", +] + +[[package]] +name = "raw-cpuid" +version = "11.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "498cd0dc59d73224351ee52a95fee0f1a617a2eae0e7d9d720cc622c73a54186" +dependencies = [ + "bitflags 2.12.1", +] + [[package]] name = "rawpointer" version = "0.2.1" @@ -8978,6 +9188,7 @@ version = "0.23.40" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ef86cd5876211988985292b91c96a8f2d298df24e75989a43a3c73f2d4d8168b" dependencies = [ + "aws-lc-rs", "log", "once_cell", "ring", @@ -9042,6 +9253,7 @@ version = "0.103.13" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "61c429a8649f110dddef65e2a5ad240f747e85f7758a6bccc7e5777bd33f756e" dependencies = [ + "aws-lc-rs", "ring", "rustls-pki-types", "untrusted", @@ -9091,7 +9303,7 @@ dependencies = [ "serde", "serde_with", "sha2 0.10.9", - "strum", + "strum 0.27.2", "tempfile", "thiserror 2.0.18", "toml 0.8.23", @@ -9162,6 +9374,12 @@ dependencies = [ "syn 2.0.117", ] +[[package]] +name = "scoped-tls" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e1cf6437eb19a8f4a6cc0f7dca544973b0b78843adbfeb3683d1a94a0024a294" + [[package]] name = "scopeguard" version = "1.2.0" @@ -9266,6 +9484,7 @@ dependencies = [ "logos-blockchain-key-management-system-service", "logos-blockchain-zone-sdk", "mempool", + "metrics", "num-bigint 0.4.6", "ping_core", "programs", @@ -9274,6 +9493,7 @@ dependencies = [ "serde", "serde_json", "storage", + "strum 0.28.0", "system_accounts", "tempfile", "test_programs", @@ -9301,6 +9521,8 @@ dependencies = [ "lee", "log", "mempool", + "metrics", + "metrics-exporter-prometheus", "programs", "sequencer_core", "sequencer_service_protocol", @@ -9724,6 +9946,12 @@ version = "0.3.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "703d5c7ef118737c72f1af64ad2f6f8c5e1921f818cdcb97b8fe6fc69bf66214" +[[package]] +name = "sketches-ddsketch" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c6f73aeb92d671e0cc4dca167e59b2deb6387c375391bc99ee743f326994a2b" + [[package]] name = "slab" version = "0.4.12" @@ -9923,7 +10151,16 @@ version = "0.27.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "af23d6f6c1a224baef9d3f61e287d2761385a5b88fdab4eb4c6f11aeb54c4bcf" dependencies = [ - "strum_macros", + "strum_macros 0.27.2", +] + +[[package]] +name = "strum" +version = "0.28.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9628de9b8791db39ceda2b119bbe13134770b56c138ec1d3af810d045c04f9bd" +dependencies = [ + "strum_macros 0.28.0", ] [[package]] @@ -9938,6 +10175,18 @@ dependencies = [ "syn 2.0.117", ] +[[package]] +name = "strum_macros" +version = "0.28.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ab85eea0270ee17587ed4156089e10b9e6880ee688791d45a905f5b1ca36f664" +dependencies = [ + "heck", + "proc-macro2", + "quote", + "syn 2.0.117", +] + [[package]] name = "subtle" version = "2.6.1" diff --git a/Cargo.toml b/Cargo.toml index 397eb965..534b4009 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -142,6 +142,8 @@ actix-rt = "*" lazy_static = "1.5.0" env_logger = "0.11" log = "0.4.28" +metrics = "0.24.6" +metrics-exporter-prometheus = "0.18.3" lru = "0.16.3" thiserror = "2.0" sha2 = "0.10.8" @@ -173,6 +175,7 @@ url = { version = "2.5.4", features = ["serde"] } tokio-retry = "0.3.0" schemars = "1.2" async-stream = "0.3.6" +strum = { version = "0.28.0", features = ["derive"] } logos-blockchain-common-http-client = { git = "https://github.com/logos-blockchain/logos-blockchain.git", rev = "0dc34e2c5a6e2ff772140378659489a602ee06bc" } logos-blockchain-key-management-system-service = { git = "https://github.com/logos-blockchain/logos-blockchain.git", rev = "0dc34e2c5a6e2ff772140378659489a602ee06bc" } diff --git a/Justfile b/Justfile index d436689d..ec22afb9 100644 --- a/Justfile +++ b/Justfile @@ -103,6 +103,10 @@ run-wallet +args: @echo "🔑 Running wallet" LEE_WALLET_HOME_DIR=$(pwd)/configs/debug cargo run --release -p wallet -- {{args}} +get-sequencer-metrics: + @echo "📊 Querying sequencer metrics" + curl http://localhost:9000/metrics + # Import test accounts supplied in sequencer configuration. wallet-import-test-accounts: @echo "⚙️ Initializing accounts" diff --git a/lez/common/Cargo.toml b/lez/common/Cargo.toml index 7582e885..d03dcbbf 100644 --- a/lez/common/Cargo.toml +++ b/lez/common/Cargo.toml @@ -25,6 +25,7 @@ log.workspace = true hex.workspace = true borsh.workspace = true logos-blockchain-common-http-client.workspace = true +strum.workspace = true [dev-dependencies] lee = { workspace = true, features = ["test-utils"] } diff --git a/lez/common/src/transaction.rs b/lez/common/src/transaction.rs index 13b2ada5..3c4df83c 100644 --- a/lez/common/src/transaction.rs +++ b/lez/common/src/transaction.rs @@ -35,6 +35,15 @@ impl LeeTransaction { }) } + #[must_use] + pub const fn kind(&self) -> TxKind { + match self { + Self::Public(_) => TxKind::Public, + Self::PrivacyPreserving(_) => TxKind::PrivacyPreserving, + Self::ProgramDeployment(_) => TxKind::ProgramDeployment, + } + } + #[must_use] pub fn affected_public_account_ids(&self) -> Vec { match self { @@ -200,7 +209,16 @@ impl From for LeeTransaction { } #[derive( - Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, BorshSerialize, BorshDeserialize, + Debug, + Clone, + Copy, + PartialEq, + Eq, + Serialize, + Deserialize, + BorshSerialize, + BorshDeserialize, + strum::IntoStaticStr, )] pub enum TxKind { Public, diff --git a/lez/mempool/Cargo.toml b/lez/mempool/Cargo.toml index a2f51bc0..47550fc9 100644 --- a/lez/mempool/Cargo.toml +++ b/lez/mempool/Cargo.toml @@ -12,3 +12,4 @@ tokio = { workspace = true, features = ["sync"] } [dev-dependencies] tokio = { workspace = true, features = ["rt-multi-thread", "macros"] } +futures.workspace = true diff --git a/lez/mempool/src/lib.rs b/lez/mempool/src/lib.rs index 0006f2c3..c081e9e7 100644 --- a/lez/mempool/src/lib.rs +++ b/lez/mempool/src/lib.rs @@ -18,6 +18,19 @@ impl MemPool { (mem_pool, sender) } + /// Returns the total number of items in the mempool, including both the front buffer and the + /// channel. + #[must_use] + pub fn len(&self) -> usize { + self.front_buffer.len().saturating_add(self.receiver.len()) + } + + /// Returns true if the mempool is empty, false otherwise. + #[must_use] + pub fn is_empty(&self) -> bool { + self.front_buffer.is_empty() && self.receiver.is_empty() + } + /// Pop an item from the mempool first checking the front buffer (LIFO) then the channel (FIFO). pub fn pop(&mut self) -> Option { use tokio::sync::mpsc::error::TryRecvError; @@ -74,6 +87,7 @@ impl MemPoolHandle { #[cfg(test)] mod tests { + use futures::FutureExt as _; use tokio::test; use super::*; @@ -82,6 +96,7 @@ mod tests { async fn mempool_new() { let (mut pool, _handle): (MemPool, _) = MemPool::new(10); assert_eq!(pool.pop(), None); + assert_eq!(pool.len(), 0); } #[test] @@ -89,10 +104,12 @@ mod tests { let (mut pool, handle) = MemPool::new(10); handle.push(1).await.unwrap(); + assert_eq!(pool.len(), 1); let item = pool.pop(); assert_eq!(item, Some(1)); assert_eq!(pool.pop(), None); + assert_eq!(pool.len(), 0); } #[test] @@ -103,29 +120,23 @@ mod tests { handle.push(2).await.unwrap(); handle.push(3).await.unwrap(); + assert_eq!(pool.len(), 3); assert_eq!(pool.pop(), Some(1)); assert_eq!(pool.pop(), Some(2)); assert_eq!(pool.pop(), Some(3)); assert_eq!(pool.pop(), None); - } - - #[test] - async fn pop_empty() { - let (mut pool, _handle): (MemPool, _) = MemPool::new(10); - assert_eq!(pool.pop(), None); + assert_eq!(pool.len(), 0); } #[test] async fn max_size() { - let (mut pool, handle) = MemPool::new(2); + let (_pool, handle) = MemPool::new(2); handle.push(1).await.unwrap(); handle.push(2).await.unwrap(); - // This should block if buffer is full, but we'll use try_send in a real scenario - // For now, just verify we can pop items - assert_eq!(pool.pop(), Some(1)); - assert_eq!(pool.pop(), Some(2)); + // This should block if buffer is full + assert_eq!(handle.push(3).now_or_never(), None); } #[test] diff --git a/lez/sequencer/core/Cargo.toml b/lez/sequencer/core/Cargo.toml index 64b154f4..41b70fca 100644 --- a/lez/sequencer/core/Cargo.toml +++ b/lez/sequencer/core/Cargo.toml @@ -34,6 +34,7 @@ humantime-serde.workspace = true tempfile.workspace = true chrono.workspace = true log.workspace = true +metrics.workspace = true tokio = { workspace = true, features = ["rt-multi-thread", "macros"] } tokio-util.workspace = true rand.workspace = true @@ -45,6 +46,7 @@ num-bigint.workspace = true risc0-zkvm.workspace = true futures.workspace = true itertools.workspace = true +strum.workspace = true [features] default = [] diff --git a/lez/sequencer/core/src/lib.rs b/lez/sequencer/core/src/lib.rs index 34ca24a5..1dd55c5b 100644 --- a/lez/sequencer/core/src/lib.rs +++ b/lez/sequencer/core/src/lib.rs @@ -50,6 +50,7 @@ pub mod block_publisher; pub mod block_store; pub mod config; pub mod cross_zone_watcher; +mod metrics; #[cfg(feature = "mock")] pub mod mock; @@ -72,7 +73,7 @@ const RETIRE_DISPATCH_AFTER_FAILURES: u32 = 3; const MAX_DISPATCHES_PER_BLOCK: usize = 16; /// The origin of a transaction. -#[derive(Clone, Copy)] +#[derive(Clone, Copy, strum::IntoStaticStr)] pub enum TransactionOrigin { /// Basic transactions submitted by users via RPC. User, @@ -299,6 +300,8 @@ impl SequencerCore { watchers, }; + crate::metrics::set_block_count(sequencer_core.chain_height()); + (sequencer_core, mempool_handle) } @@ -802,6 +805,7 @@ impl SequencerCore { let clock_tx = clock_invocation(new_block_timestamp); let clock_lee_tx = LeeTransaction::Public(clock_tx.clone()); + crate::metrics::record_mempool_size(self.mempool.len()); // Everything drained from the store first, then user work. `from_store` // is not the same as a `Sequencer` origin: it says the transaction has a // record behind it and so needs no requeue, where the origin only says @@ -872,16 +876,25 @@ impl SequencerCore { break; } - if Self::apply_mempool_transaction( + let before_tx_apply = Instant::now(); + let applied = Self::apply_mempool_transaction( &mut working_state, origin, &tx, new_block_height, new_block_timestamp, &mut withdrawals, - ) { + ); + crate::metrics::record_mempool_transaction_application_time( + origin, + tx.kind(), + before_tx_apply.elapsed(), + ); + + if applied { valid_transactions.push(tx); } else { + crate::metrics::increment_failed_transaction_count(); // A failed transaction is simply left out of the block, except a // dispatch: that one is re-fed from the store every turn, so one // that can never execute would fail on every block for ever. @@ -897,6 +910,7 @@ impl SequencerCore { .transition_from_public_transaction(&clock_tx, new_block_height, new_block_timestamp) .context("Clock transaction failed. Aborting block production.")?; valid_transactions.push(clock_lee_tx); + crate::metrics::set_block_transaction_count(valid_transactions.len()); let hashable_data = HashableBlockData { block_id: new_block_height, @@ -915,6 +929,9 @@ impl SequencerCore { now.elapsed().as_secs() ); + crate::metrics::record_block_creation_time(now.elapsed()); + crate::metrics::increment_block_count(); + Ok(BlockWithMeta { block, withdrawals }) } diff --git a/lez/sequencer/core/src/metrics.rs b/lez/sequencer/core/src/metrics.rs new file mode 100644 index 00000000..2486b3aa --- /dev/null +++ b/lez/sequencer/core/src/metrics.rs @@ -0,0 +1,90 @@ +//! This module provides all metrics exposed by the sequencer core crate. + +#![expect( + clippy::cast_precision_loss, + clippy::as_conversions, + reason = "It's okay for metrics" +)] + +use std::time::Duration; + +use common::transaction::TxKind; +use metrics::{Counter, Unit, counter, gauge, histogram}; + +use crate::TransactionOrigin; + +mod names { + pub const BLOCK_CREATION_TIME: &str = "block_creation_time"; + pub const BLOCK_COUNT: &str = "block_count"; + pub const MEMPOOL_SIZE: &str = "mempool_size"; + pub const MEMPOOL_TRANSACTION_APPLICATION_TIME: &str = "mempool_transaction_application_time"; + pub const BLOCK_TRANSACTION_COUNT: &str = "block_transaction_count"; + pub const FAILED_TRANSACTION_COUNT: &str = "failed_transaction_count"; +} + +pub fn record_block_creation_time(duration: Duration) { + histogram!( + description: "Time taken to create a block", + unit: Unit::Seconds, + names::BLOCK_CREATION_TIME + ) + .record(duration.as_secs_f64()); +} + +fn block_count_counter() -> Counter { + counter!( + description: "Number of blocks in chain", + unit: Unit::Count, + names::BLOCK_COUNT + ) +} + +pub fn set_block_count(value: u64) { + block_count_counter().absolute(value); +} + +pub fn increment_block_count() { + block_count_counter().increment(1); +} + +pub fn record_mempool_size(size: usize) { + gauge!( + description: "Size of the mempool", + unit: Unit::Count, + names::MEMPOOL_SIZE + ) + .set(u64::try_from(size).expect("Mempool size should fit into u64") as f64); +} + +pub fn record_mempool_transaction_application_time( + origin: TransactionOrigin, + kind: TxKind, + duration: Duration, +) { + histogram!( + description: "Time taken to apply a mempool transaction", + unit: Unit::Seconds, + names::MEMPOOL_TRANSACTION_APPLICATION_TIME, + "origin" => <&'static str>::from(origin), + "kind" => <&'static str>::from(kind), + ) + .record(duration.as_secs_f64()); +} + +pub fn set_block_transaction_count(count: usize) { + histogram!( + description: "Number of transactions included in block", + unit: Unit::Count, + names::BLOCK_TRANSACTION_COUNT + ) + .record(u64::try_from(count).expect("Block transaction count should fit into u64") as f64); +} + +pub fn increment_failed_transaction_count() { + counter!( + description: "Number of transactions that failed to be included in blocks", + unit: Unit::Count, + names::FAILED_TRANSACTION_COUNT + ) + .increment(1); +} diff --git a/lez/sequencer/service/Cargo.toml b/lez/sequencer/service/Cargo.toml index 338aac0d..03400935 100644 --- a/lez/sequencer/service/Cargo.toml +++ b/lez/sequencer/service/Cargo.toml @@ -22,6 +22,8 @@ anyhow.workspace = true env_logger.workspace = true hex.workspace = true log.workspace = true +metrics.workspace = true +metrics-exporter-prometheus.workspace = true tokio.workspace = true tokio-util.workspace = true jsonrpsee.workspace = true diff --git a/lez/sequencer/service/src/lib.rs b/lez/sequencer/service/src/lib.rs index ba5b68ec..5624cc91 100644 --- a/lez/sequencer/service/src/lib.rs +++ b/lez/sequencer/service/src/lib.rs @@ -21,6 +21,7 @@ use sequencer_service_rpc::RpcServer as _; use tokio::{sync::Mutex, task::JoinHandle}; use tokio_util::sync::CancellationToken; +mod metrics; pub mod service; const REQUEST_BODY_MAX_SIZE: ByteSize = ByteSize::mib(10); diff --git a/lez/sequencer/service/src/main.rs b/lez/sequencer/service/src/main.rs index 95d02e16..95facfff 100644 --- a/lez/sequencer/service/src/main.rs +++ b/lez/sequencer/service/src/main.rs @@ -3,7 +3,7 @@ use std::{ path::PathBuf, }; -use anyhow::Result; +use anyhow::{Context as _, Result}; use clap::Parser; use log::{error, info}; use tokio::signal::unix::{SignalKind, signal}; @@ -34,6 +34,11 @@ struct Args { async fn main() -> Result<()> { env_logger::init(); + metrics_exporter_prometheus::PrometheusBuilder::new() + .with_recommended_naming(true) + .install() + .context("Failed to install Prometheus recorder")?; + let Args { config_path, port, diff --git a/lez/sequencer/service/src/metrics.rs b/lez/sequencer/service/src/metrics.rs new file mode 100644 index 00000000..27cd47d1 --- /dev/null +++ b/lez/sequencer/service/src/metrics.rs @@ -0,0 +1,16 @@ +//! This module provides all metrics exposed by the sequencer service crate. + +use metrics::{Unit, counter}; + +mod names { + pub const SUBMITTED_TRANSACTION_COUNT: &str = "submitted_transaction_count"; +} + +pub fn increment_submitted_transaction_count() { + counter!( + description: "Number of transactions submitted", + unit: Unit::Count, + names::SUBMITTED_TRANSACTION_COUNT + ) + .increment(1); +} diff --git a/lez/sequencer/service/src/service.rs b/lez/sequencer/service/src/service.rs index 7ab9ed3c..d9eaf97f 100644 --- a/lez/sequencer/service/src/service.rs +++ b/lez/sequencer/service/src/service.rs @@ -93,6 +93,8 @@ impl sequencer_service_rpc::Rpc .await .expect("Mempool is closed, this is a bug"); + crate::metrics::increment_submitted_transaction_count(); + Ok(tx_hash) }