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    /// Create a new notifier for a specific module instance that can only
42    /// subscribe to the instance's state transitions
43    pub fn module_notifier<S>(
44        &self,
45        module_instance: ModuleInstanceId,
46        client: FinalClientIface,
47    ) -> ModuleNotifier<S>
48    where
49        S: fedimint_client_module::sm::State,
50    {
51        ModuleNotifier::new(self.broadcast.clone(), module_instance, client)
52    }
53
54    /// Create a [`NotifierSender`] handle that lets the owner trigger
55    /// notifications without having to hold a full `Notifier`.
56    pub fn sender(&self) -> NotifierSender {
57        NotifierSender {
58            sender: self.broadcast.clone(),
59        }
60    }
61}
62
63/// Notifier send handle that can be shared to places where we don't need an
64/// entire [`Notifier`] but still need to trigger notifications. The main use
65/// case is triggering notifications when a DB transaction was committed
66/// successfully.
67pub struct NotifierSender {
68    sender: tokio::sync::broadcast::Sender<DynState>,
69}
70
71impl NotifierSender {
72    /// Notify all subscribers of a state transition
73    pub fn notify(&self, state: DynState) {
74        let _res = self.sender.send(state);
75    }
76}