Skip to content

Commit 788c253

Browse files
committed
make metrics poll interval configurable
1 parent 1cbdcb8 commit 788c253

6 files changed

Lines changed: 65 additions & 12 deletions

File tree

e2e-tests/src/lib.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -143,6 +143,7 @@ client_trusts_lsp = true
143143
144144
[metrics]
145145
enabled = true
146+
poll_metrics_interval = 1
146147
"#,
147148
storage_dir = storage_dir.display(),
148149
);

e2e-tests/tests/e2e.rs

Lines changed: 28 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1024,10 +1024,36 @@ async fn test_metrics_endpoint() {
10241024
assert!(metrics.contains("ldk_server_total_anchor_channels_reserve_sats 0"));
10251025
assert!(metrics.contains("ldk_server_total_lightning_balance_sats 0"));
10261026

1027-
// Set up channel and make a payment to trigger metric update
1027+
// Set up channel and make a payment to trigger metrics update
10281028
setup_funded_channel(&bitcoind, &server_a, &server_b, 100_000).await;
10291029

1030-
// Make a payment to trigger payment metric updates.
1030+
// Poll for channel, peer and balance metrics.
1031+
let timeout = Duration::from_secs(10);
1032+
let start = std::time::Instant::now();
1033+
loop {
1034+
let metrics = client.get_metrics().await.unwrap();
1035+
if metrics.contains("ldk_server_total_peers_count 1")
1036+
&& metrics.contains("ldk_server_total_channels_count 1")
1037+
&& metrics.contains("ldk_server_total_public_channels_count 1")
1038+
&& metrics.contains("ldk_server_total_payments_count 2")
1039+
&& !metrics.contains("ldk_server_total_lightning_balance_sats 0")
1040+
&& !metrics.contains("ldk_server_total_onchain_balance_sats 0")
1041+
&& !metrics.contains("ldk_server_spendable_onchain_balance_sats 0")
1042+
&& !metrics.contains("ldk_server_total_anchor_channels_reserve_sats 0")
1043+
{
1044+
break;
1045+
}
1046+
1047+
if start.elapsed() > timeout {
1048+
let current_metrics = client.get_metrics().await.unwrap();
1049+
panic!(
1050+
"Timed out waiting for channel, peer and balance metrics to update. Current metrics:\n{}",
1051+
current_metrics
1052+
);
1053+
}
1054+
tokio::time::sleep(Duration::from_secs(1)).await;
1055+
}
1056+
10311057
let invoice_resp = server_b
10321058
.client()
10331059
.bolt11_receive(Bolt11ReceiveRequest {
@@ -1048,7 +1074,6 @@ async fn test_metrics_endpoint() {
10481074
loop {
10491075
let metrics = client.get_metrics().await.unwrap();
10501076
if metrics.contains("ldk_server_total_successful_payments_count 1")
1051-
&& metrics.contains("ldk_server_total_channels_count 1")
10521077
&& !metrics.contains("ldk_server_total_lightning_balance_sats 0")
10531078
&& !metrics.contains("ldk_server_total_onchain_balance_sats 0")
10541079
&& !metrics.contains("ldk_server_spendable_onchain_balance_sats 0")

ldk-server/ldk-server-config.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -92,3 +92,4 @@ client_trusts_lsp = false
9292
# Metrics settings
9393
[metrics]
9494
enabled = false
95+
poll_metrics_interval = 60 # The polling interval for metrics in seconds. Defaults to 60secs if unset and metrics enabled.

ldk-server/src/main.rs

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ use std::fs;
1616
use std::os::unix::fs::PermissionsExt;
1717
use std::path::{Path, PathBuf};
1818
use std::sync::Arc;
19-
use std::time::{SystemTime, UNIX_EPOCH};
19+
use std::time::{Duration, SystemTime, UNIX_EPOCH};
2020

2121
use clap::Parser;
2222
use hex::DisplayHex;
@@ -50,7 +50,7 @@ use crate::io::persist::{
5050
use crate::service::NodeService;
5151
use crate::util::config::{load_config, ArgsConfig, ChainSource};
5252
use crate::util::logger::ServerLogger;
53-
use crate::util::metrics::{Metrics, BUILD_METRICS_INTERVAL};
53+
use crate::util::metrics::Metrics;
5454
use crate::util::proto_adapter::{forwarded_payment_to_proto, payment_to_proto};
5555
use crate::util::systemd;
5656
use crate::util::tls::get_or_generate_tls_config;
@@ -276,8 +276,9 @@ fn main() {
276276
let event_node = Arc::clone(&node);
277277

278278
let metrics: Option<Arc<Metrics>> = if config_file.metrics_enabled {
279+
let poll_metrics_interval = Duration::from_secs(config_file.poll_metrics_interval.unwrap_or(60));
279280
let metrics_node = Arc::clone(&node);
280-
let mut interval = tokio::time::interval(BUILD_METRICS_INTERVAL);
281+
let mut interval = tokio::time::interval(poll_metrics_interval);
281282
let metrics = Arc::new(Metrics::new());
282283
let metrics_bg = Arc::clone(&metrics);
283284

ldk-server/src/util/config.rs

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,7 @@ pub struct Config {
6161
pub log_file_path: Option<String>,
6262
pub pathfinding_scores_source_url: Option<String>,
6363
pub metrics_enabled: bool,
64+
pub poll_metrics_interval: Option<u64>,
6465
}
6566

6667
#[derive(Debug, Clone, PartialEq, Eq)]
@@ -107,6 +108,7 @@ struct ConfigBuilder {
107108
log_file_path: Option<String>,
108109
pathfinding_scores_source_url: Option<String>,
109110
metrics_enabled: Option<bool>,
111+
poll_metrics_interval: Option<u64>,
110112
}
111113

112114
impl ConfigBuilder {
@@ -169,6 +171,8 @@ impl ConfigBuilder {
169171

170172
if let Some(metrics) = toml.metrics {
171173
self.metrics_enabled = metrics.enabled.or(self.metrics_enabled);
174+
self.poll_metrics_interval =
175+
metrics.poll_metrics_interval.or(self.poll_metrics_interval);
172176
}
173177
}
174178

@@ -216,6 +220,10 @@ impl ConfigBuilder {
216220
if args.metrics_enabled {
217221
self.metrics_enabled = Some(true);
218222
}
223+
224+
if let Some(poll_metrics_interval) = &args.poll_metrics_interval {
225+
self.poll_metrics_interval = Some(*poll_metrics_interval);
226+
}
219227
}
220228

221229
fn build(self) -> io::Result<Config> {
@@ -376,6 +384,8 @@ impl ConfigBuilder {
376384

377385
let metrics_enabled = self.metrics_enabled.unwrap_or(false);
378386

387+
let poll_metrics_interval = self.poll_metrics_interval;
388+
379389
Ok(Config {
380390
network,
381391
listening_addrs,
@@ -394,6 +404,7 @@ impl ConfigBuilder {
394404
log_file_path: self.log_file_path,
395405
pathfinding_scores_source_url,
396406
metrics_enabled,
407+
poll_metrics_interval,
397408
})
398409
}
399410
}
@@ -473,6 +484,7 @@ struct TomlTlsConfig {
473484
#[derive(Deserialize, Serialize)]
474485
struct MetricsTomlConfig {
475486
enabled: Option<bool>,
487+
poll_metrics_interval: Option<u64>,
476488
}
477489

478490
#[derive(Deserialize, Serialize)]
@@ -640,6 +652,14 @@ pub struct ArgsConfig {
640652
help = "The option to enable the metrics endpoint. WARNING: This endpoint is unauthenticated."
641653
)]
642654
metrics_enabled: bool,
655+
656+
#[arg(
657+
long,
658+
env = "LDK_SERVER_POLL_METRICS_INTERVAL",
659+
help = "The polling interval for metrics in seconds. Required when
660+
metrics is enabled, but defaults to 60secs if unset."
661+
)]
662+
poll_metrics_interval: Option<u64>,
643663
}
644664

645665
pub fn load_config(args: &ArgsConfig) -> io::Result<Config> {
@@ -774,6 +794,7 @@ mod tests {
774794
node_alias: Some(String::from("LDK Server CLI")),
775795
pathfinding_scores_source_url: Some(String::from("https://example.com/")),
776796
metrics_enabled: false,
797+
poll_metrics_interval: None,
777798
}
778799
}
779800

@@ -791,6 +812,7 @@ mod tests {
791812
storage_dir_path: None,
792813
pathfinding_scores_source_url: None,
793814
metrics_enabled: false,
815+
poll_metrics_interval: None,
794816
}
795817
}
796818

@@ -868,6 +890,7 @@ mod tests {
868890
log_file_path: Some("/var/log/ldk-server.log".to_string()),
869891
pathfinding_scores_source_url: None,
870892
metrics_enabled: false,
893+
poll_metrics_interval: None,
871894
};
872895

873896
assert_eq!(config.listening_addrs, expected.listening_addrs);
@@ -1193,6 +1216,7 @@ mod tests {
11931216
log_file_path: Some("/var/log/ldk-server.log".to_string()),
11941217
pathfinding_scores_source_url: Some("https://example.com/".to_string()),
11951218
metrics_enabled: false,
1219+
poll_metrics_interval: None,
11961220
};
11971221

11981222
assert_eq!(config.listening_addrs, expected.listening_addrs);
@@ -1306,6 +1330,7 @@ mod tests {
13061330
log_file_path: Some("/var/log/ldk-server.log".to_string()),
13071331
pathfinding_scores_source_url: Some("https://example.com/".to_string()),
13081332
metrics_enabled: false,
1333+
poll_metrics_interval: None,
13091334
};
13101335

13111336
assert_eq!(config.listening_addrs, expected.listening_addrs);

ldk-server/src/util/metrics.rs

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -14,26 +14,25 @@
1414
//!
1515
//! The metrics are updated through two main mechanisms:
1616
//! 1. **Periodic Polling**: The `update_all_pollable_metrics` function is called at a regular
17-
//! interval (`BUILD_METRICS_INTERVAL`) to perform a full recount of metrics like peer count,
18-
//! channels count, and balances.
17+
//! interval (`poll_metrics_interval`) configurable via the config file but defaults to 60secs if unset, to perform a full recount of metrics like peer count,
18+
//! payments count, and channels metrics.
1919
//! 2. **Event-Driven Updates**: For metrics that can change frequently and where a full recount
20-
//! would be inefficient (e.g., total_successful_payments_count), a hybrid approach is used.
20+
//! would be inefficient (e.g., total_successful_payments_count, balances), a hybrid approach is used.
2121
//! - `initialize_payment_metrics` is called once at startup to get the accurate persisted state.
2222
//! - `update_payments_count` is called incrementally whenever a relevant event (like
2323
//! `PaymentSuccessful` or `PaymentFailed`) occurs.
24+
//! - `update_all_balances` is called when we receive a `PaymentSuccessful` event to update all balance metrics.
25+
//! - `update_channels_count` is called when we receive a `ChannelReady` or `ChannelClosed` event to update the channels metrics.
2426
//!
2527
//! The `gather_metrics` function collects all current metric values and formats them into the
2628
//! plain-text format that Prometheus scrapers expect. This output is exposed via an
2729
//! unauthenticated `/metrics` HTTP endpoint on the rest service address.
2830
2931
use std::sync::atomic::{AtomicI64, AtomicU64, Ordering};
30-
use std::time::Duration;
3132

3233
use ldk_node::payment::PaymentStatus;
3334
use ldk_node::Node;
3435

35-
pub const BUILD_METRICS_INTERVAL: Duration = Duration::from_secs(60);
36-
3736
/// Holds all the metrics that are tracked for LDK Server.
3837
///
3938
/// These metrics are exposed in a Prometheus-compatible format. The values are stored
@@ -153,6 +152,7 @@ impl Metrics {
153152
self.total_private_channels_count.store(private_channels_count, Ordering::Relaxed);
154153

155154
self.update_peer_count(node);
155+
self.update_all_balances(node);
156156
}
157157

158158
/// Gathers all metrics and formats them into the Prometheus text-based format.

0 commit comments

Comments
 (0)