didbot_serve/
confinement_poll.rs1use std::panic::AssertUnwindSafe;
12use std::sync::Arc;
13use std::time::Duration as StdDuration;
14
15use didbot_pds::Registry;
16use tokio::sync::Notify;
17use tokio::task::JoinHandle;
18use tokio::time::MissedTickBehavior;
19use tracing::{info, warn};
20
21pub const DEFAULT_CONFINEMENT_INTERVAL: StdDuration = StdDuration::from_secs(5 * 60);
26
27pub struct ConfinementPoll {
29 handle: JoinHandle<()>,
30 stop: Arc<Notify>,
31}
32
33impl ConfinementPoll {
34 pub fn spawn(registry: Arc<dyn Registry>, interval: StdDuration) -> Self {
36 let stop = Arc::new(Notify::new());
37 let notified = stop.clone();
38 let handle = tokio::spawn(async move {
39 let mut ticker = tokio::time::interval(interval);
40 ticker.set_missed_tick_behavior(MissedTickBehavior::Delay);
41 loop {
42 tokio::select! {
43 biased;
44 () = notified.notified() => break,
45 _ = ticker.tick() => {}
46 }
47 reconcile_once(registry.clone()).await;
48 }
49 });
50 Self { handle, stop }
51 }
52
53 pub async fn stop(mut self) {
55 self.stop.notify_one();
56 let _ = (&mut self.handle).await;
57 }
58}
59
60impl Drop for ConfinementPoll {
61 fn drop(&mut self) {
62 self.handle.abort();
63 }
64}
65
66pub async fn reconcile_once(registry: Arc<dyn Registry>) {
68 let outcome = tokio::task::spawn_blocking(move || {
69 std::panic::catch_unwind(AssertUnwindSafe(|| registry.reconcile_confinement()))
70 })
71 .await;
72 match outcome {
73 Ok(Ok(confinement)) => {
74 if !confinement.quarantined.is_empty() {
75 warn!(
76 accounts = ?confinement.quarantined,
77 "quarantined accounts whose parent's vouch has lapsed"
78 );
79 }
80 if !confinement.released.is_empty() {
81 info!(
82 accounts = ?confinement.released,
83 "released accounts whose parent's vouch returned"
84 );
85 }
86 }
87 Ok(Err(_)) => warn!("the confinement poll panicked; the next tick tries again"),
88 Err(error) => {
89 warn!(%error, "the confinement poll did not finish; the next tick tries again")
90 }
91 }
92}