1#![allow(clippy::needless_lifetimes)]
2
3use std::borrow::Cow;
16use std::str::FromStr;
17use std::sync::atomic::{AtomicU64, Ordering};
18use std::time::Duration;
19use std::{fmt, ops};
20
21use fedimint_core::core::{ModuleInstanceId, ModuleKind};
22use fedimint_core::db::{
23 Database, DatabaseTransaction, IDatabaseTransactionOpsCoreTyped, NonCommittable,
24};
25use fedimint_core::encoding::{Decodable, Encodable};
26use fedimint_core::runtime::{Instant, timeout};
27use fedimint_core::{Amount, apply, async_trait_maybe_send, impl_db_lookup, impl_db_record};
28use fedimint_logging::LOG_CLIENT_EVENT_LOG;
29use futures::{Future, StreamExt};
30use itertools::Itertools;
31use serde::{Deserialize, Serialize};
32use thiserror::Error;
33use tokio::sync::{broadcast, watch};
34use tracing::{debug, trace};
35
36pub const DB_KEY_PREFIX_UNORDERED_EVENT_LOG: u8 = 0x3a;
45pub const DB_KEY_PREFIX_EVENT_LOG: u8 = 0x39;
46pub const DB_KEY_PREFIX_EVENT_LOG_TRIMABLE: u8 = 0x41;
47
48const TRIMABLE_EVENTLOG_MIN_ID_AGE: u64 = 10_000;
50const TRIMABLE_EVENTLOG_MIN_TS_AGE: u64 = 14 * 24 * 60 * 60 * 1_000_000;
52const TRIMABLE_EVENTLOG_MAX_TRIMMED_EVENTS: usize = 100_000;
54
55pub enum EventPersistence {
80 Transient,
82 Trimable,
84 Persistent,
87}
88
89pub trait Event: serde::Serialize + serde::de::DeserializeOwned {
90 const MODULE: Option<ModuleKind>;
91 const KIND: EventKind;
92 const PERSISTENCE: EventPersistence;
93}
94
95static UNORDEREDED_EVENT_LOG_ID_COUNTER: AtomicU64 = AtomicU64::new(0);
98
99#[derive(Debug, Encodable, Decodable)]
106pub struct UnordedEventLogId {
107 ts_usecs: u64,
108 counter: u64,
109}
110
111impl UnordedEventLogId {
112 fn new() -> Self {
113 Self {
114 ts_usecs: u64::try_from(fedimint_core::time::duration_since_epoch().as_micros())
115 .unwrap_or(u64::MAX),
117 counter: UNORDEREDED_EVENT_LOG_ID_COUNTER.fetch_add(1, Ordering::Relaxed),
118 }
119 }
120}
121
122#[derive(
125 Copy,
126 Clone,
127 Debug,
128 Encodable,
129 Decodable,
130 Default,
131 PartialEq,
132 Eq,
133 PartialOrd,
134 Ord,
135 Serialize,
136 Deserialize,
137)]
138pub struct EventLogId(u64);
139
140impl EventLogId {
141 pub const LOG_START: EventLogId = EventLogId(0);
142
143 pub fn next(self) -> EventLogId {
144 Self(self.0 + 1)
145 }
146
147 pub fn saturating_add(self, rhs: u64) -> EventLogId {
148 Self(self.0.saturating_add(rhs))
149 }
150
151 pub fn saturating_sub(self, rhs: u64) -> EventLogId {
152 Self(self.0.saturating_sub(rhs))
153 }
154
155 pub fn checked_sub(self, rhs: u64) -> Option<EventLogId> {
156 self.0.checked_sub(rhs).map(EventLogId)
157 }
158}
159
160impl From<EventLogId> for u64 {
161 fn from(value: EventLogId) -> Self {
162 value.0
163 }
164}
165
166impl FromStr for EventLogId {
167 type Err = <u64 as FromStr>::Err;
168
169 fn from_str(s: &str) -> Result<Self, Self::Err> {
170 u64::from_str(s).map(Self)
171 }
172}
173
174impl fmt::Display for EventLogId {
175 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
176 write!(f, "{}", self.0)
177 }
178}
179
180#[derive(Debug, Clone, Encodable, Decodable, PartialEq, Eq, Serialize, Deserialize)]
181pub struct EventKind(Cow<'static, str>);
182
183impl EventKind {
184 pub const fn from_static(value: &'static str) -> Self {
185 Self(Cow::Borrowed(value))
186 }
187}
188
189impl<'s> From<&'s str> for EventKind {
190 fn from(value: &'s str) -> Self {
191 Self(Cow::Owned(value.to_owned()))
192 }
193}
194
195impl From<String> for EventKind {
196 fn from(value: String) -> Self {
197 Self(Cow::Owned(value))
198 }
199}
200
201impl fmt::Display for EventKind {
202 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
203 f.write_str(&self.0)
204 }
205}
206
207#[derive(Debug, Encodable, Decodable, Clone, Serialize, Deserialize)]
208pub struct EventLogModule {
209 pub kind: ModuleKind,
210 pub id: ModuleInstanceId,
211}
212
213#[derive(Debug, Encodable, Decodable, Clone)]
214pub struct UnorderedEventLogEntry {
215 pub flags: u8,
216 pub inner: EventLogEntry,
217}
218
219impl UnorderedEventLogEntry {
220 pub const FLAG_PERSIST: u8 = 1;
221 pub const FLAG_TRIMABLE: u8 = 2;
222
223 fn persist(&self) -> bool {
224 self.flags & Self::FLAG_PERSIST != 0
225 }
226
227 fn trimable(&self) -> bool {
228 self.flags & Self::FLAG_TRIMABLE != 0
229 }
230}
231
232#[derive(Debug, Encodable, Decodable, Clone)]
233pub struct EventLogEntry {
234 pub kind: EventKind,
241
242 pub module: Option<EventLogModule>,
250
251 pub ts_usecs: u64,
253
254 pub payload: Vec<u8>,
257}
258
259impl EventLogEntry {
260 pub fn module_kind(&self) -> Option<&ModuleKind> {
261 self.module.as_ref().map(|m| &m.kind)
262 }
263
264 pub fn module_id(&self) -> Option<ModuleInstanceId> {
265 self.module.as_ref().map(|m| m.id)
266 }
267
268 pub fn to_event<E>(&self) -> Option<E>
270 where
271 E: Event,
272 {
273 serde_json::from_slice(&self.payload).ok()
274 }
275}
276
277#[derive(Debug, Clone)]
279pub struct PersistedLogEntry {
280 id: EventLogId,
281 inner: EventLogEntry,
282}
283
284impl Serialize for PersistedLogEntry {
285 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
286 where
287 S: serde::Serializer,
288 {
289 use serde::ser::SerializeStruct;
290
291 let mut state = serializer.serialize_struct("PersistedLogEntry", 5)?;
292 state.serialize_field("id", &self.id)?;
293 state.serialize_field("kind", &self.inner.kind)?;
294 state.serialize_field("module", &self.inner.module)?;
295 state.serialize_field("ts_usecs", &self.inner.ts_usecs)?;
296
297 let payload_value: serde_json::Value = serde_json::from_slice(&self.inner.payload)
299 .unwrap_or_else(|_| serde_json::Value::String(hex::encode(&self.inner.payload)));
300 state.serialize_field("payload", &payload_value)?;
301
302 state.end()
303 }
304}
305
306impl<'de> Deserialize<'de> for PersistedLogEntry {
307 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
308 where
309 D: serde::Deserializer<'de>,
310 {
311 use serde::de::{self, MapAccess, Visitor};
312
313 #[derive(Deserialize)]
314 #[serde(field_identifier, rename_all = "snake_case")]
315 enum Field {
316 Id,
317 Kind,
318 Module,
319 TsUsecs,
320 Payload,
321 }
322
323 struct PersistedLogEntryVisitor;
324
325 impl<'de> Visitor<'de> for PersistedLogEntryVisitor {
326 type Value = PersistedLogEntry;
327
328 fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
329 formatter.write_str("struct PersistedLogEntry")
330 }
331
332 fn visit_map<V>(self, mut map: V) -> Result<PersistedLogEntry, V::Error>
333 where
334 V: MapAccess<'de>,
335 {
336 let mut id = None;
337 let mut kind = None;
338 let mut module = None;
339 let mut ts_usecs = None;
340 let mut payload = None;
341
342 while let Some(key) = map.next_key()? {
343 match key {
344 Field::Id => {
345 if id.is_some() {
346 return Err(de::Error::duplicate_field("id"));
347 }
348 id = Some(map.next_value()?);
349 }
350 Field::Kind => {
351 if kind.is_some() {
352 return Err(de::Error::duplicate_field("kind"));
353 }
354 kind = Some(map.next_value()?);
355 }
356 Field::Module => {
357 if module.is_some() {
358 return Err(de::Error::duplicate_field("module"));
359 }
360 module = Some(map.next_value()?);
361 }
362 Field::TsUsecs => {
363 if ts_usecs.is_some() {
364 return Err(de::Error::duplicate_field("ts_usecs"));
365 }
366 ts_usecs = Some(map.next_value()?);
367 }
368 Field::Payload => {
369 if payload.is_some() {
370 return Err(de::Error::duplicate_field("payload"));
371 }
372 let value: serde_json::Value = map.next_value()?;
373 payload = Some(serde_json::to_vec(&value).map_err(de::Error::custom)?);
374 }
375 }
376 }
377
378 let id = id.ok_or_else(|| de::Error::missing_field("id"))?;
379 let kind = kind.ok_or_else(|| de::Error::missing_field("kind"))?;
380 let module = module.ok_or_else(|| de::Error::missing_field("module"))?;
381 let ts_usecs = ts_usecs.ok_or_else(|| de::Error::missing_field("ts_usecs"))?;
382 let payload = payload.ok_or_else(|| de::Error::missing_field("payload"))?;
383
384 Ok(PersistedLogEntry {
385 id,
386 inner: EventLogEntry {
387 kind,
388 module,
389 ts_usecs,
390 payload,
391 },
392 })
393 }
394 }
395
396 const FIELDS: &[&str] = &["id", "kind", "module", "ts_usecs", "payload"];
397 deserializer.deserialize_struct("PersistedLogEntry", FIELDS, PersistedLogEntryVisitor)
398 }
399}
400
401impl PersistedLogEntry {
402 pub fn id(&self) -> EventLogId {
403 self.id
404 }
405
406 pub fn as_raw(&self) -> &EventLogEntry {
407 &self.inner
408 }
409}
410
411impl ops::Deref for PersistedLogEntry {
412 type Target = EventLogEntry;
413
414 fn deref(&self) -> &Self::Target {
415 &self.inner
416 }
417}
418
419impl_db_record!(
420 key = UnordedEventLogId,
421 value = UnorderedEventLogEntry,
422 db_prefix = DB_KEY_PREFIX_UNORDERED_EVENT_LOG,
423);
424
425#[derive(Clone, Debug, Encodable, Decodable)]
426pub struct UnorderedEventLogIdPrefixAll;
427
428impl_db_lookup!(
429 key = UnordedEventLogId,
430 query_prefix = UnorderedEventLogIdPrefixAll
431);
432
433#[derive(Clone, Debug, Encodable, Decodable)]
434pub struct EventLogIdPrefixAll;
435
436#[derive(Clone, Debug, Encodable, Decodable)]
437pub struct EventLogIdPrefix(EventLogId);
438
439impl_db_record!(
440 key = EventLogId,
441 value = EventLogEntry,
442 db_prefix = DB_KEY_PREFIX_EVENT_LOG,
443);
444
445impl_db_lookup!(key = EventLogId, query_prefix = EventLogIdPrefixAll);
446
447impl_db_lookup!(key = EventLogId, query_prefix = EventLogIdPrefix);
448
449#[derive(
450 Copy,
451 Clone,
452 Debug,
453 Encodable,
454 Decodable,
455 Default,
456 PartialEq,
457 Eq,
458 PartialOrd,
459 Ord,
460 Serialize,
461 Deserialize,
462)]
463pub struct EventLogTrimableId(EventLogId);
464
465impl EventLogTrimableId {
466 fn next(&self) -> Self {
467 Self(self.0.next())
468 }
469
470 pub fn saturating_add(self, rhs: u64) -> Self {
471 Self(self.0.saturating_add(rhs))
472 }
473}
474
475impl From<u64> for EventLogTrimableId {
476 fn from(value: u64) -> Self {
477 Self(EventLogId(value))
478 }
479}
480
481#[derive(Clone, Debug, Encodable, Decodable)]
482pub struct EventLogTrimableIdPrefixAll;
483
484#[derive(Clone, Debug, Encodable, Decodable)]
485pub struct EventLogTrimableIdPrefix(EventLogId);
486
487impl_db_record!(
488 key = EventLogTrimableId,
489 value = EventLogEntry,
490 db_prefix = DB_KEY_PREFIX_EVENT_LOG_TRIMABLE,
491);
492
493impl_db_lookup!(
494 key = EventLogTrimableId,
495 query_prefix = EventLogTrimableIdPrefixAll
496);
497
498impl_db_lookup!(
499 key = EventLogTrimableId,
500 query_prefix = EventLogTrimableIdPrefix
501);
502
503#[apply(async_trait_maybe_send!)]
504pub trait DBTransactionEventLogExt {
505 #[allow(clippy::too_many_arguments)]
506 async fn log_event_raw(
507 &mut self,
508 log_ordering_wakeup_tx: watch::Sender<()>,
509 kind: EventKind,
510 module_kind: Option<ModuleKind>,
511 module_id: Option<ModuleInstanceId>,
512 payload: Vec<u8>,
513 persist: EventPersistence,
514 );
515
516 async fn log_event<E>(
521 &mut self,
522 log_ordering_wakeup_tx: watch::Sender<()>,
523 module_id: Option<ModuleInstanceId>,
524 event: E,
525 ) where
526 E: Event + Send,
527 {
528 self.log_event_raw(
529 log_ordering_wakeup_tx,
530 E::KIND,
531 E::MODULE,
532 module_id,
533 serde_json::to_vec(&event).expect("Serialization can't fail"),
534 <E as Event>::PERSISTENCE,
535 )
536 .await;
537 }
538
539 async fn get_next_event_log_id(&mut self) -> EventLogId;
544
545 async fn get_next_event_log_trimable_id(&mut self) -> EventLogTrimableId;
547
548 async fn get_event_log(
550 &mut self,
551 pos: Option<EventLogId>,
552 limit: u64,
553 ) -> Vec<PersistedLogEntry>;
554
555 async fn get_event_log_trimable(
556 &mut self,
557 pos: Option<EventLogTrimableId>,
558 limit: u64,
559 ) -> Vec<PersistedLogEntry>;
560}
561
562#[apply(async_trait_maybe_send!)]
563impl<'tx, Cap> DBTransactionEventLogExt for DatabaseTransaction<'tx, Cap>
564where
565 Cap: Send,
566{
567 async fn log_event_raw(
568 &mut self,
569 log_ordering_wakeup_tx: watch::Sender<()>,
570 kind: EventKind,
571 module_kind: Option<ModuleKind>,
572 module_id: Option<ModuleInstanceId>,
573 payload: Vec<u8>,
574 persist: EventPersistence,
575 ) {
576 assert_eq!(
577 module_kind.is_some(),
578 module_id.is_some(),
579 "Events of modules must have module_id set"
580 );
581
582 let unordered_id = UnordedEventLogId::new();
583 trace!(target: LOG_CLIENT_EVENT_LOG, ?unordered_id, "New unordered event log event");
584
585 if self
586 .insert_entry(
587 &unordered_id,
588 &UnorderedEventLogEntry {
589 flags: match persist {
590 EventPersistence::Transient => 0,
591 EventPersistence::Trimable => {
592 UnorderedEventLogEntry::FLAG_PERSIST
593 | UnorderedEventLogEntry::FLAG_TRIMABLE
594 }
595 EventPersistence::Persistent => UnorderedEventLogEntry::FLAG_PERSIST,
596 },
597 inner: EventLogEntry {
598 kind,
599 module: module_kind.map(|kind| EventLogModule {
600 kind,
601 id: module_id.expect("module_id exists for module events"),
602 }),
603 ts_usecs: unordered_id.ts_usecs,
604 payload,
605 },
606 },
607 )
608 .await
609 .is_some()
610 {
611 panic!("Trying to overwrite event in the client event log");
612 }
613 self.on_commit(move || {
614 log_ordering_wakeup_tx.send_replace(());
615 });
616 }
617
618 async fn get_next_event_log_id(&mut self) -> EventLogId {
619 self.find_by_prefix_sorted_descending(&EventLogIdPrefixAll)
620 .await
621 .next()
622 .await
623 .map(|(k, _v)| k.next())
624 .unwrap_or_default()
625 }
626
627 async fn get_next_event_log_trimable_id(&mut self) -> EventLogTrimableId {
628 EventLogTrimableId(
629 self.find_by_prefix_sorted_descending(&EventLogTrimableIdPrefixAll)
630 .await
631 .next()
632 .await
633 .map(|(k, _v)| k.0.next())
634 .unwrap_or_default(),
635 )
636 }
637
638 async fn get_event_log(
639 &mut self,
640 pos: Option<EventLogId>,
641 limit: u64,
642 ) -> Vec<PersistedLogEntry> {
643 let pos = pos.unwrap_or_default();
644 self.find_by_range(pos..pos.saturating_add(limit))
645 .await
646 .map(|(k, v)| PersistedLogEntry { id: k, inner: v })
647 .collect()
648 .await
649 }
650
651 async fn get_event_log_trimable(
652 &mut self,
653 pos: Option<EventLogTrimableId>,
654 limit: u64,
655 ) -> Vec<PersistedLogEntry> {
656 let pos = pos.unwrap_or_default();
657 self.find_by_range(pos..pos.saturating_add(limit))
658 .await
659 .map(|(k, v)| PersistedLogEntry { id: k.0, inner: v })
660 .collect()
661 .await
662 }
663}
664
665async fn trim_trimable_log(db: &Database, current_time_usecs: u64) {
667 let mut dbtx = db.begin_transaction().await;
668
669 let current_trimable_id = dbtx.get_next_event_log_trimable_id().await;
670 let min_id_threshold = current_trimable_id
671 .0
672 .saturating_sub(TRIMABLE_EVENTLOG_MIN_ID_AGE);
673 let min_ts_threshold = current_time_usecs.saturating_sub(TRIMABLE_EVENTLOG_MIN_TS_AGE);
674
675 let entries_to_delete: Vec<_> = dbtx
676 .find_by_prefix(&EventLogTrimableIdPrefixAll)
677 .await
678 .take_while(|(id, entry)| {
679 let id_old_enough = id.0 <= min_id_threshold;
680 let ts_old_enough = entry.ts_usecs <= min_ts_threshold;
681
682 async move { id_old_enough && ts_old_enough }
684 })
685 .take(TRIMABLE_EVENTLOG_MAX_TRIMMED_EVENTS)
686 .map(|(id, _entry)| id)
687 .collect()
688 .await;
689
690 for id in &entries_to_delete {
691 dbtx.remove_entry(id).await;
692 }
693
694 dbtx.commit_tx().await;
695}
696
697const UNORDERED_SCAN_MARGIN: Duration = Duration::from_secs(60);
708
709const UNORDERED_FULL_SCAN_INTERVAL: Duration = Duration::from_secs(60);
713
714pub async fn run_event_log_ordering_task(
717 db: Database,
718 mut log_ordering_task_wakeup: watch::Receiver<()>,
719 log_event_added: watch::Sender<()>,
720 log_event_added_transient: broadcast::Sender<EventLogEntry>,
721) {
722 debug!(target: LOG_CLIENT_EVENT_LOG, "Event log ordering task started");
723
724 let current_time_usecs =
725 u64::try_from(fedimint_core::time::duration_since_epoch().as_micros()).unwrap_or(u64::MAX);
726 trim_trimable_log(&db, current_time_usecs).await;
727
728 let mut next_entry_id = db
729 .begin_transaction_nc()
730 .await
731 .get_next_event_log_id()
732 .await;
733 let mut next_entry_id_trimable = db
734 .begin_transaction_nc()
735 .await
736 .get_next_event_log_trimable_id()
737 .await;
738
739 let mut scan_from_usecs = 0;
740 let mut next_full_scan = Instant::now();
741
742 loop {
743 let mut dbtx = db.begin_transaction().await;
744
745 let unordered_events = if Instant::now() >= next_full_scan {
746 next_full_scan = Instant::now() + UNORDERED_FULL_SCAN_INTERVAL;
747
748 dbtx.find_by_prefix(&UnorderedEventLogIdPrefixAll)
749 .await
750 .collect::<Vec<_>>()
751 .await
752 } else {
753 let start = UnordedEventLogId {
754 ts_usecs: scan_from_usecs,
755 counter: 0,
756 };
757
758 let end = UnordedEventLogId {
759 ts_usecs: u64::MAX,
760 counter: u64::MAX,
761 };
762
763 dbtx.find_by_range(start..end)
764 .await
765 .collect::<Vec<_>>()
766 .await
767 };
768
769 if let Some(newest_usecs) = unordered_events.iter().map(|entry| entry.0.ts_usecs).max() {
770 let margin_usecs = u64::try_from(UNORDERED_SCAN_MARGIN.as_micros()).unwrap_or(u64::MAX);
771
772 scan_from_usecs = scan_from_usecs.max(newest_usecs.saturating_sub(margin_usecs));
773 }
774 trace!(target: LOG_CLIENT_EVENT_LOG, num=unordered_events.len(), "Fetched unordered events");
775
776 for (unordered_id, entry) in &unordered_events {
777 assert!(
778 dbtx.remove_entry(unordered_id).await.is_some(),
779 "Must never fail to remove entry"
780 );
781 if entry.persist() {
782 if !entry.trimable() {
785 assert!(
786 dbtx.insert_entry(&next_entry_id, &entry.inner)
787 .await
788 .is_none(),
789 "Must never overwrite existing event"
790 );
791 trace!(target: LOG_CLIENT_EVENT_LOG, ?unordered_id, id=?next_entry_id, "Ordered event log event");
792 next_entry_id = next_entry_id.next();
793 }
794
795 assert!(
797 dbtx.insert_entry(&next_entry_id_trimable, &entry.inner)
798 .await
799 .is_none(),
800 "Must never overwrite existing event"
801 );
802 trace!(target: LOG_CLIENT_EVENT_LOG, ?unordered_id, id=?next_entry_id, "Ordered event log event");
803 next_entry_id_trimable = next_entry_id_trimable.next();
804 } else {
805 trace!(target: LOG_CLIENT_EVENT_LOG, ?unordered_id, id=?next_entry_id, "Transient event log event");
807 dbtx.on_commit({
808 let log_event_added_transient = log_event_added_transient.clone();
809 let entry = entry.inner.clone();
810
811 move || {
812 let _ = log_event_added_transient.send(entry);
814 }
815 });
816 }
817 }
818
819 dbtx.commit_tx().await;
823 if !unordered_events.is_empty() {
824 log_event_added.send_replace(());
825 }
826
827 trace!(target: LOG_CLIENT_EVENT_LOG, "Event log ordering task waits for more events");
828
829 if let Ok(Err(_)) = timeout(
833 next_full_scan.saturating_duration_since(Instant::now()),
834 log_ordering_task_wakeup.changed(),
835 )
836 .await
837 {
838 break;
839 }
840 }
841
842 debug!(target: LOG_CLIENT_EVENT_LOG, "Event log ordering task finished");
843}
844
845#[apply(async_trait_maybe_send!)]
859pub trait EventLogNonTrimableTracker {
860 async fn store(
862 &mut self,
863 dbtx: &mut DatabaseTransaction<NonCommittable>,
864 pos: EventLogId,
865 ) -> Result<(), EventLogTrackerError>;
866
867 async fn load(
869 &mut self,
870 dbtx: &mut DatabaseTransaction<NonCommittable>,
871 ) -> Result<Option<EventLogId>, EventLogTrackerError>;
872}
873pub type DynEventLogTracker = Box<dyn EventLogNonTrimableTracker>;
874
875#[apply(async_trait_maybe_send!)]
877pub trait EventLogTrimableTracker {
878 async fn store(
880 &mut self,
881 dbtx: &mut DatabaseTransaction<NonCommittable>,
882 pos: EventLogTrimableId,
883 ) -> Result<(), EventLogTrackerError>;
884
885 async fn load(
887 &mut self,
888 dbtx: &mut DatabaseTransaction<NonCommittable>,
889 ) -> Result<Option<EventLogTrimableId>, EventLogTrackerError>;
890}
891pub type DynEventLogTrimableTracker = Box<dyn EventLogTrimableTracker>;
892
893#[derive(Debug, Error)]
900#[error(transparent)]
901pub struct EventLogTrackerError(Box<dyn std::error::Error + Send + Sync>);
902
903impl EventLogTrackerError {
904 pub fn new<E>(source: E) -> Self
907 where
908 E: Into<Box<dyn std::error::Error + Send + Sync>>,
909 {
910 Self(source.into())
911 }
912}
913
914#[derive(Debug, Error)]
916#[non_exhaustive]
917pub enum EventHandlerError<E> {
918 #[error("The event handler failed")]
920 Handler(#[source] E),
921
922 #[error("The event log tracker failed")]
924 Tracker(#[source] Box<dyn std::error::Error + Send + Sync>),
925}
926
927pub async fn handle_events<F, R, E>(
932 db: Database,
933 mut tracker: DynEventLogTracker,
934 mut log_event_added: watch::Receiver<()>,
935 call_fn: F,
936) -> Result<(), EventHandlerError<E>>
937where
938 F: Fn(&mut DatabaseTransaction<NonCommittable>, EventLogEntry) -> R,
939 R: Future<Output = Result<(), E>>,
940 E: std::error::Error + 'static,
941{
942 let mut next_key: EventLogId = tracker
943 .load(&mut db.begin_transaction_nc().await)
944 .await
945 .map_err(|err| EventHandlerError::Tracker(err.into()))?
946 .unwrap_or_default();
947
948 trace!(target: LOG_CLIENT_EVENT_LOG, ?next_key, "Handling events");
949
950 loop {
951 let mut dbtx = db.begin_transaction().await;
952
953 match dbtx.get_value(&next_key).await {
954 Some(event) => {
955 (call_fn)(&mut dbtx.to_ref_nc(), event)
956 .await
957 .map_err(EventHandlerError::Handler)?;
958
959 next_key = next_key.next();
960
961 tracker
962 .store(&mut dbtx.to_ref_nc(), next_key)
963 .await
964 .map_err(|err| EventHandlerError::Tracker(err.into()))?;
965
966 dbtx.commit_tx().await;
967 }
968 _ => {
969 if log_event_added.changed().await.is_err() {
970 break Ok(());
971 }
972 }
973 }
974 }
975}
976
977pub async fn handle_trimable_events<F, R, E>(
980 db: Database,
981 mut tracker: DynEventLogTrimableTracker,
982 mut log_event_added: watch::Receiver<()>,
983 call_fn: F,
984) -> Result<(), EventHandlerError<E>>
985where
986 F: Fn(&mut DatabaseTransaction<NonCommittable>, EventLogEntry) -> R,
987 R: Future<Output = Result<(), E>>,
988 E: std::error::Error + 'static,
989{
990 let mut next_key: EventLogTrimableId = tracker
991 .load(&mut db.begin_transaction_nc().await)
992 .await
993 .map_err(|err| EventHandlerError::Tracker(err.into()))?
994 .unwrap_or_default();
995 trace!(target: LOG_CLIENT_EVENT_LOG, ?next_key, "Handling trimable events");
996
997 loop {
998 let mut dbtx = db.begin_transaction().await;
999
1000 match dbtx.get_value(&next_key).await {
1001 Some(event) => {
1002 (call_fn)(&mut dbtx.to_ref_nc(), event)
1003 .await
1004 .map_err(EventHandlerError::Handler)?;
1005
1006 next_key = next_key.next();
1007 tracker
1008 .store(&mut dbtx.to_ref_nc(), next_key)
1009 .await
1010 .map_err(|err| EventHandlerError::Tracker(err.into()))?;
1011
1012 dbtx.commit_tx().await;
1013 }
1014 _ => {
1015 if log_event_added.changed().await.is_err() {
1016 break Ok(());
1017 }
1018 }
1019 }
1020 }
1021}
1022
1023pub fn filter_events_by_kind<'a, I>(
1026 all_events: I,
1027 module_kind: ModuleKind,
1028 event_kind: EventKind,
1029) -> impl Iterator<Item = &'a PersistedLogEntry> + 'a
1030where
1031 I: IntoIterator<Item = &'a PersistedLogEntry> + 'a,
1032{
1033 all_events.into_iter().filter(move |e| {
1034 if let Some(module) = &e.inner.module {
1035 e.inner.kind == event_kind && module.kind == module_kind
1036 } else {
1037 false
1038 }
1039 })
1040}
1041
1042pub fn join_events<'a, L, R, Res>(
1053 events_l: &'a [&PersistedLogEntry],
1054 events_r: &'a [&PersistedLogEntry],
1055 max_time_distance: Option<Duration>,
1056 predicate: impl Fn(L, R, Duration) -> Option<Res> + 'a,
1057) -> impl Iterator<Item = Res> + 'a
1058where
1059 L: Event,
1060 R: Event,
1061{
1062 events_l
1063 .iter()
1064 .cartesian_product(events_r)
1065 .filter_map(move |(l, r)| {
1066 if L::MODULE.as_ref() == l.as_raw().module_kind()
1067 && L::KIND == l.as_raw().kind
1068 && R::MODULE.as_ref() == r.as_raw().module_kind()
1069 && R::KIND == r.as_raw().kind
1070 && let Some(latency_usecs) = r.inner.ts_usecs.checked_sub(l.inner.ts_usecs)
1071 && max_time_distance.is_none_or(|max| u128::from(latency_usecs) <= max.as_millis())
1072 && let Some(l) = l.as_raw().to_event()
1073 && let Some(r) = r.as_raw().to_event()
1074 {
1075 predicate(l, r, Duration::from_millis(latency_usecs))
1076 } else {
1077 None
1078 }
1079 })
1080}
1081
1082#[derive(Debug, Default)]
1085pub struct StructuredPaymentEvents {
1086 pub latencies_usecs: Vec<u64>,
1087 pub fees: Vec<Amount>,
1088 pub latencies_failure: Vec<u64>,
1089}
1090
1091impl StructuredPaymentEvents {
1092 pub fn new(
1093 success_stats: &[(u64, Amount)],
1094 failure_stats: Vec<u64>,
1095 ) -> StructuredPaymentEvents {
1096 let mut events = StructuredPaymentEvents {
1097 latencies_usecs: success_stats.iter().map(|(l, _)| *l).collect(),
1098 fees: success_stats.iter().map(|(_, f)| *f).collect(),
1099 latencies_failure: failure_stats,
1100 };
1101 events.sort();
1102 events
1103 }
1104
1105 pub fn combine(&mut self, other: &mut StructuredPaymentEvents) {
1108 self.latencies_usecs.append(&mut other.latencies_usecs);
1109 self.fees.append(&mut other.fees);
1110 self.latencies_failure.append(&mut other.latencies_failure);
1111 self.sort();
1112 }
1113
1114 fn sort(&mut self) {
1117 self.latencies_usecs.sort_unstable();
1118 self.fees.sort_unstable();
1119 self.latencies_failure.sort_unstable();
1120 }
1121}
1122
1123#[cfg(test)]
1124mod tests;