diff --git a/CHANGELOG.md b/CHANGELOG.md index d867b8bb6e..4e843a33f0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -21,6 +21,7 @@ Post 1.0.0 release, the changelog format is based on [Keep a Changelog](https:// ### Fixed - validator-api, mixnode, gateway should now prefer values in config.toml over mainnet defaults ([#1645]) +- validator-api should now correctly update historical uptimes for all mixnodes and gateways every 24h ([#1721]) - socks5-client: fix bug where in some cases packet reordering could trigger a connection being closed too early ([#1702],[#1724]) ### Changed @@ -47,6 +48,7 @@ Post 1.0.0 release, the changelog format is based on [Keep a Changelog](https:// [#1687]: https://github.com/nymtech/nym/pull/1687 [#1702]: https://github.com/nymtech/nym/pull/1702 [#1703]: https://github.com/nymtech/nym/pull/1703 +[#1721]: https://github.com/nymtech/nym/pull/1721 [#1724]: https://github.com/nymtech/nym/pull/1724 [#1725]: https://github.com/nymtech/nym/pull/1725 diff --git a/validator-api/src/epoch_operations/mod.rs b/validator-api/src/epoch_operations/mod.rs index 1273ac4310..cb33962890 100644 --- a/validator-api/src/epoch_operations/mod.rs +++ b/validator-api/src/epoch_operations/mod.rs @@ -30,6 +30,7 @@ pub(crate) mod error; mod helpers; use crate::epoch_operations::helpers::stake_to_f64; +use crate::node_status_api::ONE_DAY; use error::RewardingError; use mixnet_contract_common::mixnode::MixNodeDetails; use task::ShutdownListener; @@ -50,9 +51,6 @@ impl From for ExecuteMsg { } } -// // Epoch has all the same semantics as interval, but has a lower set duration -// type Epoch = Interval; - pub struct RewardedSetUpdater { nymd_client: Client, validator_cache: ValidatorCache, @@ -268,8 +266,8 @@ impl RewardedSetUpdater { log::info!("Advanced the epoch and updated the rewarded set... SUCCESS"); } - log::info!("Puring all node statuses from the storage..."); - let cutoff = (epoch_end - Duration::from_secs(86400)).unix_timestamp(); + log::info!("Purging old node statuses from the storage..."); + let cutoff = (epoch_end - 2 * ONE_DAY).unix_timestamp(); self.storage.purge_old_statuses(cutoff).await?; Ok(()) diff --git a/validator-api/src/node_status_api/uptime_updater.rs b/validator-api/src/node_status_api/uptime_updater.rs index 0e60650f65..dcb4f6424c 100644 --- a/validator-api/src/node_status_api/uptime_updater.rs +++ b/validator-api/src/node_status_api/uptime_updater.rs @@ -7,9 +7,10 @@ use crate::node_status_api::models::{ use crate::node_status_api::ONE_DAY; use crate::storage::ValidatorApiStorage; use log::error; +use std::time::Duration; use task::ShutdownListener; -use time::OffsetDateTime; -use tokio::time::sleep; +use time::{OffsetDateTime, PrimitiveDateTime, Time}; +use tokio::time::{interval, sleep}; pub(crate) struct HistoricalUptimeUpdater { storage: ValidatorApiStorage, @@ -69,28 +70,51 @@ impl HistoricalUptimeUpdater { } pub(crate) async fn run(&self, mut shutdown: ShutdownListener) { + // update uptimes at 23:00 UTC each day so that we'd have data from the actual [almost] whole day + // and so that we would avoid the edge case of starting validator API at 23:59 and having some + // nodes update for different days + + // the unwrap is fine as 23:00:00 is a valid time + let update_time = Time::from_hms(23, 0, 0).unwrap(); + let now = OffsetDateTime::now_utc(); + // is the current time within 0:00 - 22:59:59 or 23:00 - 23:59:59 ? + let update_date = if now.hour() < 23 { + now.date() + } else { + // the unwrap is fine as (**PRESUMABLY**) we're not running this code in the year 9999 + now.date().next_day().unwrap() + }; + let update_datetime = PrimitiveDateTime::new(update_date, update_time).assume_utc(); + // the unwrap here is fine as we're certain `update_datetime` is in the future and thus the + // resultant Duration is positive + let time_left: Duration = (update_datetime - now).try_into().unwrap(); + + log::info!( + "waiting until {update_datetime} to update the historical uptimes for the first time ({} seconds left)", time_left.as_secs() + ); + + tokio::select! { + biased; + _ = shutdown.recv() => { + trace!("UpdateHandler: Received shutdown"); + } + _ = sleep(time_left) => {} + } + + let mut interval = interval(ONE_DAY); while !shutdown.is_shutdown() { tokio::select! { - _ = sleep(ONE_DAY) => { - tokio::select! { - biased; - _ = shutdown.recv() => { - trace!("UpdateHandler: Received shutdown"); - } - Err(err) = self.update_uptimes() => { - // normally that would have been a warning rather than an error, - // however, in this case it implies some underlying issues with our database - // that might affect the entire program - error!( - "We failed to update daily uptimes of active nodes - {}", - err - ); - } - } - } + biased; _ = shutdown.recv() => { trace!("UpdateHandler: Received shutdown"); } + _ = interval.tick() => { + // we don't want to have another select here; uptime update is relatively speedy + // and we don't want to exit while we're in the middle of database update + if let Err(err) = self.update_uptimes().await { + error!("We failed to update daily uptimes of active nodes - {err}"); + } + } } } }