Skip to content

Commit a704f7c

Browse files
committed
Introduce persisted NodeMetrics struct
Previously, we persisted some of the `latest_` fields exposed via `NodeStatus`. Here, we now refactor this via a persisted `NodeMetrics` struct which allows to persist more fields across restarts. In particular, we now persist the latest time we sync the on-chain wallet, resulting in only doing a full scan on first initialization, and doing incremental syncing afterwards. As both of these operations are really really lightweight, we don't bother to migrate the old persisted timestamps for RGS updates and node announcement broadcasts over to the new data format.
1 parent a9cf57a commit a704f7c

File tree

6 files changed

+193
-189
lines changed

6 files changed

+193
-189
lines changed

bindings/ldk_node.udl

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -211,11 +211,12 @@ dictionary NodeStatus {
211211
boolean is_running;
212212
boolean is_listening;
213213
BestBlock current_best_block;
214-
u64? latest_wallet_sync_timestamp;
214+
u64? latest_lightning_wallet_sync_timestamp;
215215
u64? latest_onchain_wallet_sync_timestamp;
216216
u64? latest_fee_rate_cache_update_timestamp;
217217
u64? latest_rgs_snapshot_timestamp;
218218
u64? latest_node_announcement_broadcast_timestamp;
219+
u32? latest_channel_monitor_archival_height;
219220
};
220221

221222
dictionary BestBlock {

src/builder.rs

Lines changed: 32 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -12,8 +12,8 @@ use crate::connection::ConnectionManager;
1212
use crate::event::EventQueue;
1313
use crate::fee_estimator::OnchainFeeEstimator;
1414
use crate::gossip::GossipSource;
15-
use crate::io;
1615
use crate::io::sqlite_store::SqliteStore;
16+
use crate::io::utils::{read_node_metrics, write_node_metrics};
1717
#[cfg(any(vss, vss_test))]
1818
use crate::io::vss_store::VssStore;
1919
use crate::liquidity::LiquiditySource;
@@ -28,6 +28,7 @@ use crate::types::{
2828
};
2929
use crate::wallet::persist::KVStoreWalletPersister;
3030
use crate::wallet::Wallet;
31+
use crate::{io, NodeMetrics};
3132
use crate::{LogLevel, Node};
3233

3334
use lightning::chain::{chainmonitor, BestBlock, Watch};
@@ -531,12 +532,16 @@ fn build_with_store_internal(
531532
) -> Result<Node, BuildError> {
532533
// Initialize the status fields.
533534
let is_listening = Arc::new(AtomicBool::new(false));
534-
let latest_wallet_sync_timestamp = Arc::new(RwLock::new(None));
535-
let latest_onchain_wallet_sync_timestamp = Arc::new(RwLock::new(None));
536-
let latest_fee_rate_cache_update_timestamp = Arc::new(RwLock::new(None));
537-
let latest_rgs_snapshot_timestamp = Arc::new(RwLock::new(None));
538-
let latest_node_announcement_broadcast_timestamp = Arc::new(RwLock::new(None));
539-
let latest_channel_monitor_archival_height = Arc::new(RwLock::new(None));
535+
let node_metrics = match read_node_metrics(Arc::clone(&kv_store), Arc::clone(&logger)) {
536+
Ok(metrics) => Arc::new(RwLock::new(metrics)),
537+
Err(e) => {
538+
if e.kind() == std::io::ErrorKind::NotFound {
539+
Arc::new(RwLock::new(NodeMetrics::default()))
540+
} else {
541+
return Err(BuildError::ReadFailed);
542+
}
543+
},
544+
};
540545

541546
// Initialize the on-chain wallet and chain access
542547
let xprv = bitcoin::bip32::Xpriv::new_master(config.network, &seed_bytes).map_err(|e| {
@@ -585,12 +590,10 @@ fn build_with_store_internal(
585590
Arc::clone(&wallet),
586591
Arc::clone(&fee_estimator),
587592
Arc::clone(&tx_broadcaster),
593+
Arc::clone(&kv_store),
588594
Arc::clone(&config),
589595
Arc::clone(&logger),
590-
Arc::clone(&latest_wallet_sync_timestamp),
591-
Arc::clone(&latest_onchain_wallet_sync_timestamp),
592-
Arc::clone(&latest_fee_rate_cache_update_timestamp),
593-
latest_channel_monitor_archival_height,
596+
Arc::clone(&node_metrics),
594597
)),
595598
None => {
596599
// Default to Esplora client.
@@ -600,12 +603,10 @@ fn build_with_store_internal(
600603
Arc::clone(&wallet),
601604
Arc::clone(&fee_estimator),
602605
Arc::clone(&tx_broadcaster),
606+
Arc::clone(&kv_store),
603607
Arc::clone(&config),
604608
Arc::clone(&logger),
605-
Arc::clone(&latest_wallet_sync_timestamp),
606-
Arc::clone(&latest_onchain_wallet_sync_timestamp),
607-
Arc::clone(&latest_fee_rate_cache_update_timestamp),
608-
latest_channel_monitor_archival_height,
609+
Arc::clone(&node_metrics),
609610
))
610611
},
611612
};
@@ -797,23 +798,24 @@ fn build_with_store_internal(
797798
Arc::new(GossipSource::new_p2p(Arc::clone(&network_graph), Arc::clone(&logger)));
798799

799800
// Reset the RGS sync timestamp in case we somehow switch gossip sources
800-
io::utils::write_latest_rgs_sync_timestamp(
801-
0,
802-
Arc::clone(&kv_store),
803-
Arc::clone(&logger),
804-
)
805-
.map_err(|e| {
806-
log_error!(logger, "Failed writing to store: {}", e);
807-
BuildError::WriteFailed
808-
})?;
801+
{
802+
let mut locked_node_metrics = node_metrics.write().unwrap();
803+
locked_node_metrics.latest_rgs_snapshot_timestamp = None;
804+
write_node_metrics(
805+
&*locked_node_metrics,
806+
Arc::clone(&kv_store),
807+
Arc::clone(&logger),
808+
)
809+
.map_err(|e| {
810+
log_error!(logger, "Failed writing to store: {}", e);
811+
BuildError::WriteFailed
812+
})?;
813+
}
809814
p2p_source
810815
},
811816
GossipSourceConfig::RapidGossipSync(rgs_server) => {
812-
let latest_sync_timestamp = io::utils::read_latest_rgs_sync_timestamp(
813-
Arc::clone(&kv_store),
814-
Arc::clone(&logger),
815-
)
816-
.unwrap_or(0);
817+
let latest_sync_timestamp =
818+
node_metrics.read().unwrap().latest_rgs_snapshot_timestamp.unwrap_or(0);
817819
Arc::new(GossipSource::new_rgs(
818820
rgs_server.clone(),
819821
latest_sync_timestamp,
@@ -998,11 +1000,7 @@ fn build_with_store_internal(
9981000
peer_store,
9991001
payment_store,
10001002
is_listening,
1001-
latest_wallet_sync_timestamp,
1002-
latest_onchain_wallet_sync_timestamp,
1003-
latest_fee_rate_cache_update_timestamp,
1004-
latest_rgs_snapshot_timestamp,
1005-
latest_node_announcement_broadcast_timestamp,
1003+
node_metrics,
10061004
})
10071005
}
10081006

src/chain/mod.rs

Lines changed: 52 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -15,9 +15,10 @@ use crate::fee_estimator::{
1515
apply_post_estimation_adjustments, get_all_conf_targets, get_num_block_defaults_for_target,
1616
OnchainFeeEstimator,
1717
};
18+
use crate::io::utils::write_node_metrics;
1819
use crate::logger::{log_bytes, log_error, log_info, log_trace, FilesystemLogger, Logger};
19-
use crate::types::{Broadcaster, ChainMonitor, ChannelManager, Sweeper, Wallet};
20-
use crate::Error;
20+
use crate::types::{Broadcaster, ChainMonitor, ChannelManager, DynStore, Sweeper, Wallet};
21+
use crate::{Error, NodeMetrics};
2122

2223
use lightning::chain::{Confirm, Filter};
2324
use lightning::util::ser::Writeable;
@@ -102,23 +103,18 @@ pub(crate) enum ChainSource {
102103
lightning_wallet_sync_status: Mutex<WalletSyncStatus>,
103104
fee_estimator: Arc<OnchainFeeEstimator>,
104105
tx_broadcaster: Arc<Broadcaster>,
106+
kv_store: Arc<DynStore>,
105107
config: Arc<Config>,
106108
logger: Arc<FilesystemLogger>,
107-
latest_wallet_sync_timestamp: Arc<RwLock<Option<u64>>>,
108-
latest_onchain_wallet_sync_timestamp: Arc<RwLock<Option<u64>>>,
109-
latest_fee_rate_cache_update_timestamp: Arc<RwLock<Option<u64>>>,
110-
latest_channel_monitor_archival_height: Arc<RwLock<Option<u32>>>,
109+
node_metrics: Arc<RwLock<NodeMetrics>>,
111110
},
112111
}
113112

114113
impl ChainSource {
115114
pub(crate) fn new_esplora(
116115
server_url: String, onchain_wallet: Arc<Wallet>, fee_estimator: Arc<OnchainFeeEstimator>,
117-
tx_broadcaster: Arc<Broadcaster>, config: Arc<Config>, logger: Arc<FilesystemLogger>,
118-
latest_wallet_sync_timestamp: Arc<RwLock<Option<u64>>>,
119-
latest_onchain_wallet_sync_timestamp: Arc<RwLock<Option<u64>>>,
120-
latest_fee_rate_cache_update_timestamp: Arc<RwLock<Option<u64>>>,
121-
latest_channel_monitor_archival_height: Arc<RwLock<Option<u32>>>,
116+
tx_broadcaster: Arc<Broadcaster>, kv_store: Arc<DynStore>, config: Arc<Config>,
117+
logger: Arc<FilesystemLogger>, node_metrics: Arc<RwLock<NodeMetrics>>,
122118
) -> Self {
123119
let mut client_builder = esplora_client::Builder::new(&server_url);
124120
client_builder = client_builder.timeout(DEFAULT_ESPLORA_CLIENT_TIMEOUT_SECS);
@@ -135,12 +131,10 @@ impl ChainSource {
135131
lightning_wallet_sync_status,
136132
fee_estimator,
137133
tx_broadcaster,
134+
kv_store,
138135
config,
139136
logger,
140-
latest_wallet_sync_timestamp,
141-
latest_onchain_wallet_sync_timestamp,
142-
latest_fee_rate_cache_update_timestamp,
143-
latest_channel_monitor_archival_height,
137+
node_metrics,
144138
}
145139
}
146140

@@ -211,8 +205,9 @@ impl ChainSource {
211205
esplora_client,
212206
onchain_wallet,
213207
onchain_wallet_sync_status,
208+
kv_store,
214209
logger,
215-
latest_onchain_wallet_sync_timestamp,
210+
node_metrics,
216211
..
217212
} => {
218213
let receiver_res = {
@@ -232,7 +227,7 @@ impl ChainSource {
232227
// If this is our first sync, do a full scan with the configured gap limit.
233228
// Otherwise just do an incremental sync.
234229
let incremental_sync =
235-
latest_onchain_wallet_sync_timestamp.read().unwrap().is_some();
230+
node_metrics.read().unwrap().latest_onchain_wallet_sync_timestamp.is_some();
236231

237232
macro_rules! get_and_apply_wallet_update {
238233
($sync_future: expr) => {{
@@ -251,8 +246,11 @@ impl ChainSource {
251246
.duration_since(UNIX_EPOCH)
252247
.ok()
253248
.map(|d| d.as_secs());
254-
*latest_onchain_wallet_sync_timestamp.write().unwrap() =
255-
unix_time_secs_opt;
249+
{
250+
let mut locked_node_metrics = node_metrics.write().unwrap();
251+
locked_node_metrics.latest_onchain_wallet_sync_timestamp = unix_time_secs_opt;
252+
write_node_metrics(&*locked_node_metrics, Arc::clone(&kv_store), Arc::clone(&logger))?;
253+
}
256254
Ok(())
257255
},
258256
Err(e) => Err(e),
@@ -327,9 +325,9 @@ impl ChainSource {
327325
Self::Esplora {
328326
tx_sync,
329327
lightning_wallet_sync_status,
328+
kv_store,
330329
logger,
331-
latest_wallet_sync_timestamp,
332-
latest_channel_monitor_archival_height,
330+
node_metrics,
333331
..
334332
} => {
335333
let sync_cman = Arc::clone(&channel_manager);
@@ -372,13 +370,24 @@ impl ChainSource {
372370
.duration_since(UNIX_EPOCH)
373371
.ok()
374372
.map(|d| d.as_secs());
375-
*latest_wallet_sync_timestamp.write().unwrap() = unix_time_secs_opt;
373+
{
374+
let mut locked_node_metrics = node_metrics.write().unwrap();
375+
locked_node_metrics.latest_lightning_wallet_sync_timestamp =
376+
unix_time_secs_opt;
377+
write_node_metrics(
378+
&*locked_node_metrics,
379+
Arc::clone(&kv_store),
380+
Arc::clone(&logger),
381+
)?;
382+
}
376383

377384
periodically_archive_fully_resolved_monitors(
378385
Arc::clone(&channel_manager),
379386
Arc::clone(&chain_monitor),
380-
Arc::clone(&latest_channel_monitor_archival_height),
381-
);
387+
Arc::clone(&kv_store),
388+
Arc::clone(&logger),
389+
Arc::clone(&node_metrics),
390+
)?;
382391
Ok(())
383392
},
384393
Err(e) => {
@@ -406,8 +415,9 @@ impl ChainSource {
406415
esplora_client,
407416
fee_estimator,
408417
config,
418+
kv_store,
409419
logger,
410-
latest_fee_rate_cache_update_timestamp,
420+
node_metrics,
411421
..
412422
} => {
413423
let now = Instant::now();
@@ -479,7 +489,15 @@ impl ChainSource {
479489
);
480490
let unix_time_secs_opt =
481491
SystemTime::now().duration_since(UNIX_EPOCH).ok().map(|d| d.as_secs());
482-
*latest_fee_rate_cache_update_timestamp.write().unwrap() = unix_time_secs_opt;
492+
{
493+
let mut locked_node_metrics = node_metrics.write().unwrap();
494+
locked_node_metrics.latest_fee_rate_cache_update_timestamp = unix_time_secs_opt;
495+
write_node_metrics(
496+
&*locked_node_metrics,
497+
Arc::clone(&kv_store),
498+
Arc::clone(&logger),
499+
)?;
500+
}
483501

484502
Ok(())
485503
},
@@ -580,16 +598,19 @@ impl Filter for ChainSource {
580598

581599
fn periodically_archive_fully_resolved_monitors(
582600
channel_manager: Arc<ChannelManager>, chain_monitor: Arc<ChainMonitor>,
583-
latest_channel_monitor_archival_height: Arc<RwLock<Option<u32>>>,
584-
) {
585-
let mut latest_archival_height_lock = latest_channel_monitor_archival_height.write().unwrap();
601+
kv_store: Arc<DynStore>, logger: Arc<FilesystemLogger>, node_metrics: Arc<RwLock<NodeMetrics>>,
602+
) -> Result<(), Error> {
603+
let mut locked_node_metrics = node_metrics.write().unwrap();
586604
let cur_height = channel_manager.current_best_block().height;
587-
let should_archive = latest_archival_height_lock
605+
let should_archive = locked_node_metrics
606+
.latest_channel_monitor_archival_height
588607
.as_ref()
589608
.map_or(true, |h| cur_height >= h + RESOLVED_CHANNEL_MONITOR_ARCHIVAL_INTERVAL);
590609

591610
if should_archive {
592611
chain_monitor.archive_fully_resolved_channel_monitors();
593-
*latest_archival_height_lock = Some(cur_height);
612+
locked_node_metrics.latest_channel_monitor_archival_height = Some(cur_height);
613+
write_node_metrics(&*locked_node_metrics, kv_store, logger)?;
594614
}
615+
Ok(())
595616
}

src/io/mod.rs

Lines changed: 4 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -33,15 +33,10 @@ pub(crate) const DEPRECATED_SPENDABLE_OUTPUT_INFO_PERSISTENCE_PRIMARY_NAMESPACE:
3333
"spendable_outputs";
3434
pub(crate) const DEPRECATED_SPENDABLE_OUTPUT_INFO_PERSISTENCE_SECONDARY_NAMESPACE: &str = "";
3535

36-
/// RapidGossipSync's `latest_sync_timestamp` will be persisted under this key.
37-
pub(crate) const LATEST_RGS_SYNC_TIMESTAMP_PRIMARY_NAMESPACE: &str = "";
38-
pub(crate) const LATEST_RGS_SYNC_TIMESTAMP_SECONDARY_NAMESPACE: &str = "";
39-
pub(crate) const LATEST_RGS_SYNC_TIMESTAMP_KEY: &str = "latest_rgs_sync_timestamp";
40-
41-
/// The last time we broadcast a node announcement will be persisted under this key.
42-
pub(crate) const LATEST_NODE_ANN_BCAST_TIMESTAMP_PRIMARY_NAMESPACE: &str = "";
43-
pub(crate) const LATEST_NODE_ANN_BCAST_TIMESTAMP_SECONDARY_NAMESPACE: &str = "";
44-
pub(crate) const LATEST_NODE_ANN_BCAST_TIMESTAMP_KEY: &str = "latest_node_ann_bcast_timestamp";
36+
/// The node metrics will be persisted under this key.
37+
pub(crate) const NODE_METRICS_PRIMARY_NAMESPACE: &str = "";
38+
pub(crate) const NODE_METRICS_SECONDARY_NAMESPACE: &str = "";
39+
pub(crate) const NODE_METRICS_KEY: &str = "node_metrics";
4540

4641
/// The BDK wallet's [`ChangeSet::descriptor`] will be persisted under this key.
4742
///

0 commit comments

Comments
 (0)