use std::time::{Duration, Instant};
use super::sentinel::FloorOutcome;
use super::{manifest, LeanConfig, LeanResult, Syncer};
#[derive(Debug, Clone)]
pub struct NewsClock {
poll: Duration,
active_for: Duration,
min_interval: Duration,
active_until: Instant,
pending: bool,
last_early: Option<Instant>,
}
impl NewsClock {
pub fn new(cfg: &LeanConfig, now: Instant) -> NewsClock {
let active_for = Duration::from_secs(cfg.news_active_secs);
NewsClock {
poll: Duration::from_secs(cfg.news_poll_secs),
active_for,
min_interval: Duration::from_secs(cfg.news_min_interval_secs),
active_until: now + active_for,
pending: false,
last_early: None,
}
}
pub fn enabled(&self) -> bool {
!self.poll.is_zero()
}
pub fn every(&self) -> Duration {
self.poll.max(Duration::from_secs(1))
}
pub fn active(&self, now: Instant) -> bool {
self.enabled() && now < self.active_until
}
pub fn touch(&mut self, now: Instant) {
self.active_until = self.active_until.max(now + self.active_for);
}
pub fn saw_news(&mut self, now: Instant) {
self.pending = true;
self.touch(now);
}
pub fn taken(&mut self) {
self.pending = false;
}
pub fn pending(&self) -> bool {
self.pending
}
pub fn ran_early(&mut self, now: Instant) {
self.last_early = Some(now);
}
pub fn due(&self, now: Instant) -> bool {
self.pending
&& self.last_early.map(|t| now.saturating_duration_since(t) >= self.min_interval).unwrap_or(true)
}
}
pub fn worked(o: &FloorOutcome) -> bool {
!o.no_change || !o.acks.is_empty() || o.uploaded > 0 || o.deleted > 0 || o.consumed > 0
}
impl Syncer {
pub async fn news_poll(&mut self) -> LeanResult<Option<u64>> {
if !self.cfg.access.is_read() {
self.read_cell_requests().await?;
}
let Some(p) = manifest::load_pointer(self.store.as_ref(), &self.cfg).await? else {
return Ok(None);
};
let t = self.load_remote_seq();
if p.pointer.seq <= t.observed_seq {
return Ok(None);
}
self.touch_remote_seq(Some(p.pointer.seq), Some(p.etag), t.integrated_seq)?;
Ok(Some(p.pointer.seq))
}
pub fn news_start(&self) -> LeanResult<()> {
self.ticker_from(None, None)
}
pub async fn news_tick(&mut self) -> LeanResult<FloorOutcome> {
let out = FloorOutcome::default();
if self.cfg.access.is_read() {
self.reader_floor(out).await
} else {
self.cadence_floor(out).await
}
}
pub async fn news_arm(&mut self, clock: &mut NewsClock, now: Instant) -> LeanResult<Option<FloorOutcome>> {
if !clock.active(now) {
return Ok(None);
}
if self.news_poll().await?.is_some() {
clock.saw_news(now);
}
if !clock.due(now) {
return Ok(None);
}
clock.ran_early(now);
let out = self.news_tick().await?;
clock.taken();
Ok(Some(out))
}
}