Skip to main content

fedimint_client/sm/
notifier.rs

1use fedimint_client_module::module::FinalClientIface;
2use fedimint_client_module::sm::{DynState, ModuleNotifier};
3use fedimint_core::core::ModuleInstanceId;
4use fedimint_core::util::FmtCompact;
5use tracing::{debug, trace};
6
7/// State transition notifier owned by the modularized client used to inform
8/// modules of state transitions.
9///
10/// To not lose any state transitions that happen before a module subscribes to
11/// the operation the notifier loads all belonging past state transitions from
12/// the DB. State transitions may be reported multiple times and out of order.
13#[derive(Clone)]
14pub struct Notifier {
15    /// Broadcast channel used to send state transitions to all subscribers
16    broadcast: tokio::sync::broadcast::Sender<DynState>,
17}
18
19impl Notifier {
20    #[allow(clippy::new_without_default)]
21    pub fn new() -> Self {
22        let (sender, _receiver) = tokio::sync::broadcast::channel(10_000);
23        Self { broadcast: sender }
24    }
25
26    /// Notify all subscribers of a state transition
27    pub fn notify(&self, state: DynState) {
28        let queue_len = self.broadcast.len();
29        trace!(?state, %queue_len, "Sending notification about state transition");
30        // FIXME: use more robust notification mechanism
31        if let Err(err) = self.broadcast.send(state) {
32            debug!(
33                err = %err.fmt_compact(),
34                %queue_len,
35                receivers=self.broadcast.receiver_count(),
36                "Could not send state transition notification, no active receivers"
37            );
38        }
39    }
40
41    /// Subscribe to the state transitions of every module and operation.
42    ///
43    /// Unlike a [`ModuleNotifier`] subscription this replays nothing from the
44    /// database: the receiver only sees transitions notified after this call.
45    pub fn subscribe(&self) -> tokio::sync::broadcast::Receiver<DynState> {
46        self.broadcast.subscribe()
47    }
48
49    /// Create a new notifier for a specific module instance that can only
50    /// subscribe to the instance's state transitions
51    pub fn module_notifier<S>(
52        &self,
53        module_instance: ModuleInstanceId,
54        client: FinalClientIface,
55    ) -> ModuleNotifier<S>
56    where
57        S: fedimint_client_module::sm::State,
58    {
59        ModuleNotifier::new(self.broadcast.clone(), module_instance, client)
60    }
61
62    /// Create a [`NotifierSender`] handle that lets the owner trigger
63    /// notifications without having to hold a full `Notifier`.
64    pub fn sender(&self) -> NotifierSender {
65        NotifierSender {
66            sender: self.broadcast.clone(),
67        }
68    }
69}
70
71/// Notifier send handle that can be shared to places where we don't need an
72/// entire [`Notifier`] but still need to trigger notifications. The main use
73/// case is triggering notifications when a DB transaction was committed
74/// successfully.
75pub struct NotifierSender {
76    sender: tokio::sync::broadcast::Sender<DynState>,
77}
78
79impl NotifierSender {
80    /// Notify all subscribers of a state transition
81    pub fn notify(&self, state: DynState) {
82        let _res = self.sender.send(state);
83    }
84}