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