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