Skip to content

Commit 9788924

Browse files
authored
Enhance metrics tracking and add dependencies (#34)
* Enhance metrics tracking and add dependencies - Introduced `prometheus` and `lazy_static` dependencies to facilitate metrics collection. - Added metrics recording for transaction durations in various components, including EIP-7702 and external bundler. - Updated the `metrics` module to include functions for tracking transaction states and processing times. - Enhanced the server to initialize and expose metrics via a dedicated endpoint. These changes improve observability and performance monitoring across the execution framework. * Implement TWMQ duration calculation for metrics tracking - Added a new helper function `calculate_duration_seconds_from_twmq` to compute duration in seconds using timestamps in TWMQ format. - Updated various components (EIP-7702 executor, external bundler, and EOA worker) to utilize the new TWMQ duration calculation for recording transaction metrics. - Enhanced test coverage for the new duration calculation function. These changes improve the accuracy of metrics tracking related to transaction durations. * correct EOA worker time metrics
1 parent cabfbb9 commit 9788924

File tree

22 files changed

+924
-8
lines changed

22 files changed

+924
-8
lines changed

Cargo.lock

Lines changed: 25 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

docker-compose.monitoring.yml

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
version: "3.8"
2+
3+
services:
4+
prometheus:
5+
image: prom/prometheus:latest
6+
container_name: tw-engine-prometheus
7+
ports:
8+
- "9090:9090"
9+
volumes:
10+
- ./monitoring/prometheus.yml:/etc/prometheus/prometheus.yml
11+
- prometheus-data:/prometheus
12+
command:
13+
- "--config.file=/etc/prometheus/prometheus.yml"
14+
- "--storage.tsdb.path=/prometheus"
15+
- "--web.console.libraries=/etc/prometheus/console_libraries"
16+
- "--web.console.templates=/etc/prometheus/consoles"
17+
- "--storage.tsdb.retention.time=200h"
18+
- "--web.enable-lifecycle"
19+
restart: unless-stopped
20+
21+
grafana:
22+
image: grafana/grafana:latest
23+
container_name: tw-engine-grafana
24+
ports:
25+
- "3000:3000"
26+
volumes:
27+
- grafana-data:/var/lib/grafana
28+
- ./monitoring/grafana/dashboards:/var/lib/grafana/dashboards
29+
- ./monitoring/grafana/provisioning:/etc/grafana/provisioning
30+
environment:
31+
- GF_SECURITY_ADMIN_USER=admin
32+
- GF_SECURITY_ADMIN_PASSWORD=admin
33+
- GF_USERS_ALLOW_SIGN_UP=false
34+
restart: unless-stopped
35+
36+
volumes:
37+
prometheus-data:
38+
grafana-data:

executors/Cargo.toml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,3 +25,5 @@ chrono = "0.4.41"
2525
tokio = { version = "1.45.0", features = ["full"] }
2626
futures = "0.3.31"
2727
moka = { version = "0.12.10", features = ["future"] }
28+
prometheus = "0.13.4"
29+
lazy_static = "1.5.0"

executors/src/eip7702_executor/confirm.rs

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ use twmq::{
1818

1919
use crate::eip7702_executor::send::Eip7702Sender;
2020
use crate::{
21+
metrics::{record_transaction_queued_to_confirmed, current_timestamp_ms, calculate_duration_seconds_from_twmq},
2122
transaction_registry::TransactionRegistry,
2223
webhook::{
2324
WebhookJobHandler,
@@ -37,6 +38,9 @@ pub struct Eip7702ConfirmationJobData {
3738
pub rpc_credentials: RpcCredentials,
3839
#[serde(default)]
3940
pub webhook_options: Vec<WebhookOptions>,
41+
/// Original timestamp when the transaction was first queued (unix timestamp in milliseconds)
42+
#[serde(default)]
43+
pub original_queued_timestamp: Option<u64>,
4044
}
4145

4246
impl HasWebhookOptions for Eip7702ConfirmationJobData {
@@ -262,6 +266,13 @@ where
262266
"Transaction confirmed successfully"
263267
);
264268

269+
// Record metrics if original timestamp is available
270+
if let Some(original_timestamp) = job_data.original_queued_timestamp {
271+
let confirmed_timestamp = current_timestamp_ms();
272+
let queued_to_confirmed_duration = calculate_duration_seconds_from_twmq(original_timestamp, confirmed_timestamp);
273+
record_transaction_queued_to_confirmed("eip7702", job_data.chain_id, queued_to_confirmed_duration);
274+
}
275+
265276
Ok(Eip7702ConfirmationResult {
266277
transaction_id: job_data.transaction_id.clone(),
267278
transaction_hash,

executors/src/eip7702_executor/send.rs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ use twmq::{
2222
};
2323

2424
use crate::{
25+
metrics::{record_transaction_queued_to_sent, current_timestamp_ms, calculate_duration_seconds_from_twmq},
2526
transaction_registry::TransactionRegistry,
2627
webhook::{
2728
WebhookJobHandler,
@@ -280,6 +281,11 @@ where
280281

281282
tracing::debug!(transaction_id = ?transaction_id, "EIP-7702 transaction sent to bundler");
282283

284+
// Record metrics: transaction queued to sent
285+
let sent_timestamp = current_timestamp_ms();
286+
let queued_to_sent_duration = calculate_duration_seconds_from_twmq(job.job.created_at, sent_timestamp);
287+
record_transaction_queued_to_sent("eip7702", job_data.chain_id, queued_to_sent_duration);
288+
283289
let sender_details = match session_key_target_address {
284290
Some(target_address) => Eip7702Sender::SessionKey {
285291
session_key_address: owner_address,
@@ -327,6 +333,7 @@ where
327333
sender_details: success_data.result.sender_details.clone(),
328334
rpc_credentials: job.job.data.rpc_credentials.clone(),
329335
webhook_options: job.job.data.webhook_options.clone(),
336+
original_queued_timestamp: Some(job.job.created_at),
330337
})
331338
.with_id(job.job.transaction_id());
332339

executors/src/eoa/store/borrowed.rs

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ use crate::eoa::{
1212
},
1313
worker::error::EoaExecutorWorkerError,
1414
};
15+
use crate::metrics::{record_transaction_queued_to_sent, current_timestamp_ms, calculate_duration_seconds};
1516
use crate::webhook::{WebhookJobHandler, queue_webhook_envelopes};
1617

1718
#[derive(Debug, Clone)]
@@ -117,6 +118,14 @@ impl SafeRedisTransaction for ProcessBorrowedTransactions<'_> {
117118

118119
match &result.result {
119120
SubmissionResultType::Success => {
121+
// Record metrics: transaction queued to sent
122+
let sent_timestamp = current_timestamp_ms();
123+
let queued_to_sent_duration = calculate_duration_seconds(
124+
result.transaction.queued_at,
125+
sent_timestamp
126+
);
127+
record_transaction_queued_to_sent("eoa", self.keys.chain_id, queued_to_sent_duration);
128+
120129
// Add to submitted zset
121130
let (submitted_tx_redis_string, nonce) =
122131
result.transaction.to_redis_string_with_nonce();

executors/src/eoa/store/submitted.rs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ use crate::{
1616
TransactionStoreError, atomic::SafeRedisTransaction,
1717
},
1818
},
19+
metrics::{record_transaction_queued_to_confirmed, current_timestamp_ms, calculate_duration_seconds},
1920
webhook::{WebhookJobHandler, queue_webhook_envelopes},
2021
};
2122

@@ -329,6 +330,13 @@ impl SafeRedisTransaction for CleanSubmittedTransactions<'_> {
329330
);
330331

331332
if let SubmittedTransactionHydrated::Real(tx) = tx {
333+
// Record metrics: transaction queued to mined for confirmed transactions
334+
let confirmed_timestamp = current_timestamp_ms();
335+
let queued_to_mined_duration = calculate_duration_seconds(
336+
tx.queued_at,
337+
confirmed_timestamp
338+
);
339+
record_transaction_queued_to_confirmed("eoa", self.keys.chain_id, queued_to_mined_duration);
332340
if !tx.user_request.webhook_options.is_empty() {
333341
let event = EoaExecutorEvent {
334342
transaction_id: tx.transaction_id.clone(),

executors/src/eoa/worker/confirm.rs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
use alloy::{primitives::B256, providers::Provider};
22
use engine_core::{chain::Chain, error::AlloyRpcErrorToEngineError};
33
use serde::{Deserialize, Serialize};
4-
use twmq::redis::AsyncCommands;
54

65
use crate::eoa::{
76
EoaExecutorStore,

executors/src/eoa/worker/mod.rs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ use crate::eoa::authorization_cache::EoaAuthorizationCache;
2323
use crate::eoa::store::{
2424
AtomicEoaExecutorStore, EoaExecutorStore, EoaExecutorStoreKeys, EoaHealth, SubmissionResult,
2525
};
26+
use crate::metrics::{calculate_duration_seconds, calculate_duration_seconds_from_twmq, current_timestamp_ms, record_eoa_job_processing_time};
2627
use crate::webhook::WebhookJobHandler;
2728

2829
pub mod confirm;
@@ -188,11 +189,17 @@ where
188189
signer: self.eoa_signer.clone(),
189190
};
190191

192+
let job_start_time = current_timestamp_ms();
191193
let result = worker.execute_main_workflow().await?;
192194
if let Err(e) = worker.release_eoa_lock().await {
193195
tracing::error!(error = ?e, "Error releasing EOA lock");
194196
}
195197

198+
// Record EOA job processing metrics
199+
let job_end_time = current_timestamp_ms();
200+
let job_duration = calculate_duration_seconds(job_start_time, job_end_time);
201+
record_eoa_job_processing_time(data.chain_id, job_duration);
202+
196203
if result.is_work_remaining() {
197204
Err(EoaExecutorWorkerError::WorkRemaining { result })
198205
.map_err_nack(Some(Duration::from_secs(2)), RequeuePosition::Last)

executors/src/external_bundler/confirm.rs

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ use twmq::{
1515
};
1616

1717
use crate::{
18+
metrics::{record_transaction_queued_to_confirmed, current_timestamp_ms, calculate_duration_seconds_from_twmq},
1819
transaction_registry::TransactionRegistry,
1920
webhook::{
2021
WebhookJobHandler,
@@ -36,6 +37,9 @@ pub struct UserOpConfirmationJobData {
3637
pub deployment_lock_acquired: bool,
3738
pub webhook_options: Vec<WebhookOptions>,
3839
pub rpc_credentials: RpcCredentials,
40+
/// Original timestamp when the transaction was first queued (unix timestamp in milliseconds)
41+
#[serde(default)]
42+
pub original_queued_timestamp: Option<u64>,
3943
}
4044

4145
// --- Success Result ---
@@ -211,7 +215,14 @@ where
211215
"User operation confirmed on-chain"
212216
);
213217

214-
// 4. Success! Lock cleanup will happen atomically in on_success hook
218+
// 4. Record metrics if original timestamp is available
219+
if let Some(original_timestamp) = job_data.original_queued_timestamp {
220+
let confirmed_timestamp = current_timestamp_ms();
221+
let queued_to_confirmed_duration = calculate_duration_seconds_from_twmq(original_timestamp, confirmed_timestamp);
222+
record_transaction_queued_to_confirmed("erc4337-external", job_data.chain_id, queued_to_confirmed_duration);
223+
}
224+
225+
// 5. Success! Lock cleanup will happen atomically in on_success hook
215226
Ok(UserOpConfirmationResult {
216227
user_op_hash: job_data.user_op_hash.clone(),
217228
receipt,

0 commit comments

Comments
 (0)