1use std::any;
106use std::collections::{BTreeMap, BTreeSet};
107use std::error::Error;
108use std::fmt::{self, Debug};
109use std::marker::{self, PhantomData};
110use std::ops::{self, DerefMut, Range};
111use std::path::Path;
112use std::pin::Pin;
113use std::sync::Arc;
114use std::time::Duration;
115
116use bitcoin::hex::DisplayHex as _;
117use fedimint_core::util::BoxFuture;
118use fedimint_logging::LOG_DB;
119use fedimint_util_error::FmtCompact as _;
120use futures::{Stream, StreamExt};
121use macro_rules_attribute::apply;
122use rand::Rng;
123use serde::Serialize;
124use strum_macros::EnumIter;
125use thiserror::Error;
126use tracing::{debug, info, instrument, trace, warn};
127
128use crate::core::{ModuleInstanceId, ModuleKind};
129use crate::encoding::{Decodable, DecodeError, Encodable};
130use crate::fmt_utils::AbbreviateHexBytes;
131use crate::task::{MaybeSend, MaybeSync};
132use crate::{async_trait_maybe_send, maybe_add_send, maybe_add_send_sync, timing};
133
134pub mod mem_impl;
135pub mod notifications;
136
137pub use test_utils::*;
138
139use self::notifications::{Notifications, NotifyQueue};
140use crate::module::registry::{ModuleDecoderRegistry, ModuleRegistry};
141
142pub const MODULE_GLOBAL_PREFIX: u8 = 0xff;
143
144pub type DatabaseResult<T> = std::result::Result<T, DatabaseError>;
146
147pub trait DatabaseKeyPrefix: Debug {
148 fn to_bytes(&self) -> Vec<u8>;
149}
150
151pub trait DatabaseRecord: DatabaseKeyPrefix {
154 const DB_PREFIX: u8;
155 const NOTIFY_ON_MODIFY: bool = false;
156 type Key: DatabaseKey + Debug;
157 type Value: DatabaseValue + Debug;
158}
159
160pub trait DatabaseLookup: DatabaseKeyPrefix {
163 type Record: DatabaseRecord;
164}
165
166impl<Record> DatabaseLookup for Record
168where
169 Record: DatabaseRecord + Debug + Decodable + Encodable,
170{
171 type Record = Record;
172}
173
174pub trait DatabaseKey: Sized {
177 const NOTIFY_ON_MODIFY: bool = false;
185 fn from_bytes(
186 data: &[u8],
187 modules: &ModuleDecoderRegistry,
188 ) -> std::result::Result<Self, DecodingError>;
189}
190
191pub trait DatabaseKeyWithNotify {}
193
194pub trait DatabaseValue: Sized + Debug {
196 fn from_bytes(
197 data: &[u8],
198 modules: &ModuleDecoderRegistry,
199 ) -> std::result::Result<Self, DecodingError>;
200 fn to_bytes(&self) -> Vec<u8>;
201}
202
203pub type PrefixStream<'a> = Pin<Box<maybe_add_send!(dyn Stream<Item = (Vec<u8>, Vec<u8>)> + 'a)>>;
204
205pub type PhantomBound<'big, 'small> = PhantomData<&'small &'big ()>;
209
210#[derive(Debug, Error)]
212pub enum AutocommitError<E> {
213 #[error("Commit Failed: {last_error}")]
215 CommitFailed {
216 attempts: usize,
218 last_error: DatabaseError,
220 },
221 #[error("Closure error: {error}")]
224 ClosureError {
225 attempts: usize,
231 error: E,
233 },
234}
235
236pub trait AutocommitResultExt<T, E> {
237 fn unwrap_autocommit(self) -> std::result::Result<T, E>;
241}
242
243impl<T, E> AutocommitResultExt<T, E> for std::result::Result<T, AutocommitError<E>> {
244 fn unwrap_autocommit(self) -> std::result::Result<T, E> {
245 match self {
246 Ok(value) => Ok(value),
247 Err(AutocommitError::CommitFailed { .. }) => {
248 panic!("`unwrap_autocommit` called on a autocommit result with finite retries");
249 }
250 Err(AutocommitError::ClosureError { error, .. }) => Err(error),
251 }
252 }
253}
254
255#[apply(async_trait_maybe_send!)]
264pub trait IRawDatabase: Debug + MaybeSend + MaybeSync + 'static {
265 type Transaction<'a>: IRawDatabaseTransaction + Debug;
267
268 async fn begin_transaction<'a>(&'a self) -> Self::Transaction<'a>;
270
271 fn checkpoint(&self, backup_path: &Path) -> DatabaseResult<()>;
273}
274
275#[apply(async_trait_maybe_send!)]
276impl<T> IRawDatabase for Box<T>
277where
278 T: IRawDatabase,
279{
280 type Transaction<'a> = <T as IRawDatabase>::Transaction<'a>;
281
282 async fn begin_transaction<'a>(&'a self) -> Self::Transaction<'a> {
283 (**self).begin_transaction().await
284 }
285
286 fn checkpoint(&self, backup_path: &Path) -> DatabaseResult<()> {
287 (**self).checkpoint(backup_path)
288 }
289}
290
291pub trait IRawDatabaseExt: IRawDatabase + Sized {
293 fn into_database(self) -> Database {
297 Database::new(self, ModuleRegistry::default())
298 }
299}
300
301impl<T> IRawDatabaseExt for T where T: IRawDatabase {}
302
303impl<T> From<T> for Database
304where
305 T: IRawDatabase,
306{
307 fn from(raw: T) -> Self {
308 Self::new(raw, ModuleRegistry::default())
309 }
310}
311
312#[apply(async_trait_maybe_send!)]
315pub trait IDatabase: Debug + MaybeSend + MaybeSync + 'static {
316 async fn begin_transaction<'a>(&'a self) -> Box<dyn IDatabaseTransaction + 'a>;
318 async fn register<'a>(&'a self, key: &[u8]) -> BoxFuture<'a, ()>;
328 async fn notify(&self, key: &[u8]);
330
331 fn is_global(&self) -> bool;
334
335 fn checkpoint(&self, backup_path: &Path) -> DatabaseResult<()>;
337}
338
339#[apply(async_trait_maybe_send!)]
340impl<T> IDatabase for Arc<T>
341where
342 T: IDatabase + ?Sized,
343{
344 async fn begin_transaction<'a>(&'a self) -> Box<dyn IDatabaseTransaction + 'a> {
345 (**self).begin_transaction().await
346 }
347 async fn register<'a>(&'a self, key: &[u8]) -> BoxFuture<'a, ()> {
348 (**self).register(key).await
349 }
350 async fn notify(&self, key: &[u8]) {
351 (**self).notify(key).await;
352 }
353
354 fn is_global(&self) -> bool {
355 (**self).is_global()
356 }
357
358 fn checkpoint(&self, backup_path: &Path) -> DatabaseResult<()> {
359 (**self).checkpoint(backup_path)
360 }
361}
362
363struct BaseDatabase<RawDatabase> {
367 notifications: Arc<Notifications>,
368 raw: RawDatabase,
369}
370
371impl<RawDatabase> fmt::Debug for BaseDatabase<RawDatabase> {
372 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
373 f.write_str("BaseDatabase")
374 }
375}
376
377#[apply(async_trait_maybe_send!)]
378impl<RawDatabase: IRawDatabase + MaybeSend + 'static> IDatabase for BaseDatabase<RawDatabase> {
379 async fn begin_transaction<'a>(&'a self) -> Box<dyn IDatabaseTransaction + 'a> {
380 Box::new(BaseDatabaseTransaction::new(
381 self.raw.begin_transaction().await,
382 self.notifications.clone(),
383 ))
384 }
385 async fn register<'a>(&'a self, key: &[u8]) -> BoxFuture<'a, ()> {
386 Box::pin(self.notifications.register(key))
387 }
388 async fn notify(&self, key: &[u8]) {
389 self.notifications.notify(key);
390 }
391
392 fn is_global(&self) -> bool {
393 true
394 }
395
396 fn checkpoint(&self, backup_path: &Path) -> DatabaseResult<()> {
397 self.raw.checkpoint(backup_path)
398 }
399}
400
401#[derive(Clone, Debug)]
407pub struct Database {
408 inner: Arc<dyn IDatabase + 'static>,
409 module_decoders: ModuleDecoderRegistry,
410}
411
412impl Database {
413 pub fn strong_count(&self) -> usize {
414 Arc::strong_count(&self.inner)
415 }
416
417 pub fn into_inner(self) -> Arc<dyn IDatabase + 'static> {
418 self.inner
419 }
420}
421
422impl Database {
423 pub fn new(raw: impl IRawDatabase + 'static, module_decoders: ModuleDecoderRegistry) -> Self {
428 let inner = BaseDatabase {
429 raw,
430 notifications: Arc::new(Notifications::new()),
431 };
432 Self::new_from_arc(
433 Arc::new(inner) as Arc<dyn IDatabase + 'static>,
434 module_decoders,
435 )
436 }
437
438 pub fn new_from_arc(
440 inner: Arc<dyn IDatabase + 'static>,
441 module_decoders: ModuleDecoderRegistry,
442 ) -> Self {
443 Self {
444 inner,
445 module_decoders,
446 }
447 }
448
449 pub fn with_prefix(&self, prefix: Vec<u8>) -> Self {
451 Self {
452 inner: Arc::new(PrefixDatabase {
453 inner: self.inner.clone(),
454 global_dbtx_access_token: None,
455 prefix,
456 }),
457 module_decoders: self.module_decoders.clone(),
458 }
459 }
460
461 pub fn with_prefix_module_id(
465 &self,
466 module_instance_id: ModuleInstanceId,
467 ) -> (Self, GlobalDBTxAccessToken) {
468 let prefix = module_instance_id_to_byte_prefix(module_instance_id);
469 let global_dbtx_access_token = GlobalDBTxAccessToken::from_prefix(&prefix);
470 (
471 Self {
472 inner: Arc::new(PrefixDatabase {
473 inner: self.inner.clone(),
474 global_dbtx_access_token: Some(global_dbtx_access_token),
475 prefix,
476 }),
477 module_decoders: self.module_decoders.clone(),
478 },
479 global_dbtx_access_token,
480 )
481 }
482
483 pub fn with_decoders(&self, module_decoders: ModuleDecoderRegistry) -> Self {
484 Self {
485 inner: self.inner.clone(),
486 module_decoders,
487 }
488 }
489
490 pub fn is_global(&self) -> bool {
492 self.inner.is_global()
493 }
494
495 pub fn ensure_global(&self) -> DatabaseResult<()> {
497 if !self.is_global() {
498 return Err(DatabaseError::NotGlobal);
499 }
500
501 Ok(())
502 }
503
504 pub fn ensure_isolated(&self) -> DatabaseResult<()> {
506 if self.is_global() {
507 return Err(DatabaseError::NotIsolated);
508 }
509
510 Ok(())
511 }
512
513 pub async fn begin_transaction<'s, 'tx>(&'s self) -> DatabaseTransaction<'tx, Committable>
515 where
516 's: 'tx,
517 {
518 DatabaseTransaction::<Committable>::new(
519 self.inner.begin_transaction().await,
520 self.module_decoders.clone(),
521 )
522 }
523
524 pub async fn begin_transaction_nc<'s, 'tx>(&'s self) -> DatabaseTransaction<'tx, NonCommittable>
526 where
527 's: 'tx,
528 {
529 self.begin_transaction().await.into_nc()
530 }
531
532 pub fn checkpoint(&self, backup_path: &Path) -> DatabaseResult<()> {
533 self.inner.checkpoint(backup_path)
534 }
535
536 pub async fn autocommit<'s, 'dbtx, F, T, E>(
564 &'s self,
565 tx_fn: F,
566 max_attempts: Option<usize>,
567 ) -> std::result::Result<T, AutocommitError<E>>
568 where
569 's: 'dbtx,
570 for<'r, 'o> F: Fn(
571 &'r mut DatabaseTransaction<'o>,
572 PhantomBound<'dbtx, 'o>,
573 ) -> BoxFuture<'r, std::result::Result<T, E>>,
574 {
575 assert_ne!(max_attempts, Some(0));
576 let mut curr_attempts: usize = 0;
577
578 loop {
579 curr_attempts = curr_attempts
584 .checked_add(1)
585 .expect("db autocommit attempt counter overflowed");
586
587 let mut dbtx = self.begin_transaction().await;
588
589 let tx_fn_res = tx_fn(&mut dbtx.to_ref_nc(), PhantomData).await;
590 let val = match tx_fn_res {
591 Ok(val) => val,
592 Err(err) => {
593 dbtx.ignore_uncommitted();
594 return Err(AutocommitError::ClosureError {
595 attempts: curr_attempts,
596 error: err,
597 });
598 }
599 };
600
601 let _timing = timing::TimeReporter::new("autocommit - commit_tx");
602
603 match dbtx.commit_tx_result().await {
604 Ok(()) => {
605 return Ok(val);
606 }
607 Err(err) => {
608 if max_attempts.is_some_and(|max_att| max_att <= curr_attempts) {
609 warn!(
610 target: LOG_DB,
611 curr_attempts,
612 err = %err.fmt_compact(),
613 "Database commit failed in an autocommit block - terminating"
614 );
615 return Err(AutocommitError::CommitFailed {
616 attempts: curr_attempts,
617 last_error: err,
618 });
619 }
620
621 let delay = (2u64.pow(curr_attempts.min(7) as u32) * 10).min(1000);
622 let delay = rand::thread_rng().gen_range(delay..(2 * delay));
623 warn!(
624 target: LOG_DB,
625 curr_attempts,
626 err = %err.fmt_compact(),
627 delay_ms = %delay,
628 "Database commit failed in an autocommit block - retrying"
629 );
630 crate::runtime::sleep(Duration::from_millis(delay)).await;
631 }
632 }
633 }
634 }
635
636 pub async fn wait_key_check<'a, K, T>(
641 &'a self,
642 key: &K,
643 checker: impl Fn(Option<K::Value>) -> Option<T>,
644 ) -> (T, DatabaseTransaction<'a, Committable>)
645 where
646 K: DatabaseKey + DatabaseRecord + DatabaseKeyWithNotify,
647 {
648 let key_bytes = key.to_bytes();
649 loop {
650 let notify = self.inner.register(&key_bytes).await;
652
653 let mut tx = self.inner.begin_transaction().await;
655
656 let maybe_value_bytes = tx
657 .raw_get_bytes(&key_bytes)
658 .await
659 .expect("Unrecoverable error when reading from database")
660 .map(|value_bytes| {
661 decode_value_expect(&value_bytes, &self.module_decoders, &key_bytes)
662 });
663
664 if let Some(value) = checker(maybe_value_bytes) {
665 return (
666 value,
667 DatabaseTransaction::new(tx, self.module_decoders.clone()),
668 );
669 }
670
671 notify.await;
673 }
676 }
677
678 pub async fn wait_key_exists<K>(&self, key: &K) -> K::Value
680 where
681 K: DatabaseKey + DatabaseRecord + DatabaseKeyWithNotify,
682 {
683 self.wait_key_check(key, std::convert::identity).await.0
684 }
685}
686
687fn module_instance_id_to_byte_prefix(module_instance_id: u16) -> Vec<u8> {
688 let mut bytes = vec![MODULE_GLOBAL_PREFIX];
689 bytes.append(&mut module_instance_id.consensus_encode_to_vec());
690 bytes
691}
692
693#[derive(Clone, Debug)]
696struct PrefixDatabase<Inner>
697where
698 Inner: Debug,
699{
700 prefix: Vec<u8>,
701 global_dbtx_access_token: Option<GlobalDBTxAccessToken>,
702 inner: Inner,
703}
704
705impl<Inner> PrefixDatabase<Inner>
706where
707 Inner: Debug,
708{
709 fn get_full_key(&self, key: &[u8]) -> Vec<u8> {
713 let mut full_key = self.prefix.clone();
714 full_key.extend_from_slice(key);
715 full_key
716 }
717}
718
719#[apply(async_trait_maybe_send!)]
720impl<Inner> IDatabase for PrefixDatabase<Inner>
721where
722 Inner: Debug + MaybeSend + MaybeSync + 'static + IDatabase,
723{
724 async fn begin_transaction<'a>(&'a self) -> Box<dyn IDatabaseTransaction + 'a> {
725 Box::new(PrefixDatabaseTransaction {
726 inner: self.inner.begin_transaction().await,
727 global_dbtx_access_token: self.global_dbtx_access_token,
728 prefix: self.prefix.clone(),
729 })
730 }
731 async fn register<'a>(&'a self, key: &[u8]) -> BoxFuture<'a, ()> {
732 self.inner.register(&self.get_full_key(key)).await
733 }
734
735 async fn notify(&self, key: &[u8]) {
736 self.inner.notify(&self.get_full_key(key)).await;
737 }
738
739 fn is_global(&self) -> bool {
740 if self.global_dbtx_access_token.is_some() {
741 false
742 } else {
743 self.inner.is_global()
744 }
745 }
746
747 fn checkpoint(&self, backup_path: &Path) -> DatabaseResult<()> {
748 self.inner.checkpoint(backup_path)
749 }
750}
751
752#[derive(Debug)]
757struct PrefixDatabaseTransaction<Inner> {
758 inner: Inner,
759 global_dbtx_access_token: Option<GlobalDBTxAccessToken>,
760 prefix: Vec<u8>,
761}
762
763impl<Inner> PrefixDatabaseTransaction<Inner> {
764 fn get_full_key(&self, key: &[u8]) -> Vec<u8> {
768 let mut full_key = self.prefix.clone();
769 full_key.extend_from_slice(key);
770 full_key
771 }
772
773 fn get_full_range(&self, range: Range<&[u8]>) -> Range<Vec<u8>> {
774 Range {
775 start: self.get_full_key(range.start),
776 end: self.get_full_key(range.end),
777 }
778 }
779
780 fn adapt_prefix_stream(stream: PrefixStream<'_>, prefix_len: usize) -> PrefixStream<'_> {
781 Box::pin(stream.map(move |(k, v)| (k[prefix_len..].to_owned(), v)))
782 }
783}
784
785#[apply(async_trait_maybe_send!)]
786impl<Inner> IDatabaseTransaction for PrefixDatabaseTransaction<Inner>
787where
788 Inner: IDatabaseTransaction,
789{
790 async fn commit_tx(&mut self) -> DatabaseResult<()> {
791 self.inner.commit_tx().await
792 }
793
794 fn is_global(&self) -> bool {
795 if self.global_dbtx_access_token.is_some() {
796 false
797 } else {
798 self.inner.is_global()
799 }
800 }
801
802 fn global_dbtx(
803 &mut self,
804 access_token: GlobalDBTxAccessToken,
805 ) -> &mut dyn IDatabaseTransaction {
806 if let Some(self_global_dbtx_access_token) = self.global_dbtx_access_token {
807 assert_eq!(
808 access_token, self_global_dbtx_access_token,
809 "Invalid access key used to access global_dbtx"
810 );
811 &mut self.inner
812 } else {
813 self.inner.global_dbtx(access_token)
814 }
815 }
816}
817
818#[apply(async_trait_maybe_send!)]
819impl<Inner> IDatabaseTransactionOpsCore for PrefixDatabaseTransaction<Inner>
820where
821 Inner: IDatabaseTransactionOpsCore,
822{
823 async fn raw_insert_bytes(
824 &mut self,
825 key: &[u8],
826 value: &[u8],
827 ) -> DatabaseResult<Option<Vec<u8>>> {
828 let key = self.get_full_key(key);
829 self.inner.raw_insert_bytes(&key, value).await
830 }
831
832 async fn raw_get_bytes(&mut self, key: &[u8]) -> DatabaseResult<Option<Vec<u8>>> {
833 let key = self.get_full_key(key);
834 self.inner.raw_get_bytes(&key).await
835 }
836
837 async fn raw_remove_entry(&mut self, key: &[u8]) -> DatabaseResult<Option<Vec<u8>>> {
838 let key = self.get_full_key(key);
839 self.inner.raw_remove_entry(&key).await
840 }
841
842 async fn raw_find_by_prefix(&mut self, key_prefix: &[u8]) -> DatabaseResult<PrefixStream<'_>> {
843 let key = self.get_full_key(key_prefix);
844 let stream = self.inner.raw_find_by_prefix(&key).await?;
845 Ok(Self::adapt_prefix_stream(stream, self.prefix.len()))
846 }
847
848 async fn raw_find_by_prefix_sorted_descending(
849 &mut self,
850 key_prefix: &[u8],
851 ) -> DatabaseResult<PrefixStream<'_>> {
852 let key = self.get_full_key(key_prefix);
853 let stream = self
854 .inner
855 .raw_find_by_prefix_sorted_descending(&key)
856 .await?;
857 Ok(Self::adapt_prefix_stream(stream, self.prefix.len()))
858 }
859
860 async fn raw_find_by_range(&mut self, range: Range<&[u8]>) -> DatabaseResult<PrefixStream<'_>> {
861 let range = self.get_full_range(range);
862 let stream = self
863 .inner
864 .raw_find_by_range(Range {
865 start: &range.start,
866 end: &range.end,
867 })
868 .await?;
869 Ok(Self::adapt_prefix_stream(stream, self.prefix.len()))
870 }
871
872 async fn raw_remove_by_prefix(&mut self, key_prefix: &[u8]) -> DatabaseResult<()> {
873 let key = self.get_full_key(key_prefix);
874 self.inner.raw_remove_by_prefix(&key).await
875 }
876}
877
878impl<Inner> IDatabaseTransactionOps for PrefixDatabaseTransaction<Inner> where
879 Inner: IDatabaseTransactionOps
880{
881}
882
883#[apply(async_trait_maybe_send!)]
887pub trait IDatabaseTransactionOpsCore: MaybeSend {
888 async fn raw_insert_bytes(
890 &mut self,
891 key: &[u8],
892 value: &[u8],
893 ) -> DatabaseResult<Option<Vec<u8>>>;
894
895 async fn raw_get_bytes(&mut self, key: &[u8]) -> DatabaseResult<Option<Vec<u8>>>;
897
898 async fn raw_remove_entry(&mut self, key: &[u8]) -> DatabaseResult<Option<Vec<u8>>>;
900
901 async fn raw_find_by_prefix(&mut self, key_prefix: &[u8]) -> DatabaseResult<PrefixStream<'_>>;
904
905 async fn raw_find_by_prefix_sorted_descending(
907 &mut self,
908 key_prefix: &[u8],
909 ) -> DatabaseResult<PrefixStream<'_>>;
910
911 async fn raw_find_by_range(&mut self, range: Range<&[u8]>) -> DatabaseResult<PrefixStream<'_>>;
915
916 async fn raw_remove_by_prefix(&mut self, key_prefix: &[u8]) -> DatabaseResult<()>;
918}
919
920#[apply(async_trait_maybe_send!)]
921impl<T> IDatabaseTransactionOpsCore for Box<T>
922where
923 T: IDatabaseTransactionOpsCore + ?Sized,
924{
925 async fn raw_insert_bytes(
926 &mut self,
927 key: &[u8],
928 value: &[u8],
929 ) -> DatabaseResult<Option<Vec<u8>>> {
930 (**self).raw_insert_bytes(key, value).await
931 }
932
933 async fn raw_get_bytes(&mut self, key: &[u8]) -> DatabaseResult<Option<Vec<u8>>> {
934 (**self).raw_get_bytes(key).await
935 }
936
937 async fn raw_remove_entry(&mut self, key: &[u8]) -> DatabaseResult<Option<Vec<u8>>> {
938 (**self).raw_remove_entry(key).await
939 }
940
941 async fn raw_find_by_prefix(&mut self, key_prefix: &[u8]) -> DatabaseResult<PrefixStream<'_>> {
942 (**self).raw_find_by_prefix(key_prefix).await
943 }
944
945 async fn raw_find_by_prefix_sorted_descending(
946 &mut self,
947 key_prefix: &[u8],
948 ) -> DatabaseResult<PrefixStream<'_>> {
949 (**self)
950 .raw_find_by_prefix_sorted_descending(key_prefix)
951 .await
952 }
953
954 async fn raw_find_by_range(&mut self, range: Range<&[u8]>) -> DatabaseResult<PrefixStream<'_>> {
955 (**self).raw_find_by_range(range).await
956 }
957
958 async fn raw_remove_by_prefix(&mut self, key_prefix: &[u8]) -> DatabaseResult<()> {
959 (**self).raw_remove_by_prefix(key_prefix).await
960 }
961}
962
963#[apply(async_trait_maybe_send!)]
964impl<T> IDatabaseTransactionOpsCore for &mut T
965where
966 T: IDatabaseTransactionOpsCore + ?Sized,
967{
968 async fn raw_insert_bytes(
969 &mut self,
970 key: &[u8],
971 value: &[u8],
972 ) -> DatabaseResult<Option<Vec<u8>>> {
973 (**self).raw_insert_bytes(key, value).await
974 }
975
976 async fn raw_get_bytes(&mut self, key: &[u8]) -> DatabaseResult<Option<Vec<u8>>> {
977 (**self).raw_get_bytes(key).await
978 }
979
980 async fn raw_remove_entry(&mut self, key: &[u8]) -> DatabaseResult<Option<Vec<u8>>> {
981 (**self).raw_remove_entry(key).await
982 }
983
984 async fn raw_find_by_prefix(&mut self, key_prefix: &[u8]) -> DatabaseResult<PrefixStream<'_>> {
985 (**self).raw_find_by_prefix(key_prefix).await
986 }
987
988 async fn raw_find_by_prefix_sorted_descending(
989 &mut self,
990 key_prefix: &[u8],
991 ) -> DatabaseResult<PrefixStream<'_>> {
992 (**self)
993 .raw_find_by_prefix_sorted_descending(key_prefix)
994 .await
995 }
996
997 async fn raw_find_by_range(&mut self, range: Range<&[u8]>) -> DatabaseResult<PrefixStream<'_>> {
998 (**self).raw_find_by_range(range).await
999 }
1000
1001 async fn raw_remove_by_prefix(&mut self, key_prefix: &[u8]) -> DatabaseResult<()> {
1002 (**self).raw_remove_by_prefix(key_prefix).await
1003 }
1004}
1005
1006pub trait IDatabaseTransactionOps: IDatabaseTransactionOpsCore + MaybeSend {}
1012
1013impl<T> IDatabaseTransactionOps for Box<T> where T: IDatabaseTransactionOps + ?Sized {}
1014
1015impl<T> IDatabaseTransactionOps for &mut T where T: IDatabaseTransactionOps + ?Sized {}
1016
1017#[apply(async_trait_maybe_send!)]
1023pub trait IDatabaseTransactionOpsCoreTyped<'a> {
1024 async fn get_value<K>(&mut self, key: &K) -> Option<K::Value>
1025 where
1026 K: DatabaseKey + DatabaseRecord + MaybeSend + MaybeSync;
1027
1028 async fn insert_entry<K>(&mut self, key: &K, value: &K::Value) -> Option<K::Value>
1029 where
1030 K: DatabaseKey + DatabaseRecord + MaybeSend + MaybeSync,
1031 K::Value: MaybeSend + MaybeSync;
1032
1033 async fn insert_new_entry<K>(&mut self, key: &K, value: &K::Value)
1034 where
1035 K: DatabaseKey + DatabaseRecord + MaybeSend + MaybeSync,
1036 K::Value: MaybeSend + MaybeSync;
1037
1038 async fn find_by_range<K>(
1039 &mut self,
1040 key_range: Range<K>,
1041 ) -> Pin<Box<maybe_add_send!(dyn Stream<Item = (K, K::Value)> + '_)>>
1042 where
1043 K: DatabaseKey + DatabaseRecord + MaybeSend + MaybeSync,
1044 K::Value: MaybeSend + MaybeSync;
1045
1046 async fn find_by_prefix<KP>(
1047 &mut self,
1048 key_prefix: &KP,
1049 ) -> Pin<
1050 Box<
1051 maybe_add_send!(
1052 dyn Stream<
1053 Item = (
1054 KP::Record,
1055 <<KP as DatabaseLookup>::Record as DatabaseRecord>::Value,
1056 ),
1057 > + '_
1058 ),
1059 >,
1060 >
1061 where
1062 KP: DatabaseLookup + MaybeSend + MaybeSync,
1063 KP::Record: DatabaseKey;
1064
1065 async fn find_by_prefix_sorted_descending<KP>(
1066 &mut self,
1067 key_prefix: &KP,
1068 ) -> Pin<
1069 Box<
1070 maybe_add_send!(
1071 dyn Stream<
1072 Item = (
1073 KP::Record,
1074 <<KP as DatabaseLookup>::Record as DatabaseRecord>::Value,
1075 ),
1076 > + '_
1077 ),
1078 >,
1079 >
1080 where
1081 KP: DatabaseLookup + MaybeSend + MaybeSync,
1082 KP::Record: DatabaseKey;
1083
1084 async fn remove_entry<K>(&mut self, key: &K) -> Option<K::Value>
1085 where
1086 K: DatabaseKey + DatabaseRecord + MaybeSend + MaybeSync;
1087
1088 async fn remove_by_prefix<KP>(&mut self, key_prefix: &KP)
1089 where
1090 KP: DatabaseLookup + MaybeSend + MaybeSync;
1091}
1092
1093#[apply(async_trait_maybe_send!)]
1096impl<T> IDatabaseTransactionOpsCoreTyped<'_> for T
1097where
1098 T: IDatabaseTransactionOpsCore + WithDecoders,
1099{
1100 async fn get_value<K>(&mut self, key: &K) -> Option<K::Value>
1101 where
1102 K: DatabaseKey + DatabaseRecord + MaybeSend + MaybeSync,
1103 {
1104 let key_bytes = key.to_bytes();
1105 let raw = self
1106 .raw_get_bytes(&key_bytes)
1107 .await
1108 .expect("Unrecoverable error occurred while reading and entry from the database");
1109 raw.map(|value_bytes| {
1110 decode_value_expect::<K::Value>(&value_bytes, self.decoders(), &key_bytes)
1111 })
1112 }
1113
1114 async fn insert_entry<K>(&mut self, key: &K, value: &K::Value) -> Option<K::Value>
1115 where
1116 K: DatabaseKey + DatabaseRecord + MaybeSend + MaybeSync,
1117 K::Value: MaybeSend + MaybeSync,
1118 {
1119 let key_bytes = key.to_bytes();
1120 self.raw_insert_bytes(&key_bytes, &value.to_bytes())
1121 .await
1122 .expect("Unrecoverable error occurred while inserting entry into the database")
1123 .map(|value_bytes| {
1124 decode_value_expect::<K::Value>(&value_bytes, self.decoders(), &key_bytes)
1125 })
1126 }
1127
1128 async fn insert_new_entry<K>(&mut self, key: &K, value: &K::Value)
1129 where
1130 K: DatabaseKey + DatabaseRecord + MaybeSend + MaybeSync,
1131 K::Value: MaybeSend + MaybeSync,
1132 {
1133 if let Some(prev) = self.insert_entry(key, value).await {
1134 panic!(
1135 "Database overwriting element when expecting insertion of new entry. Key: {key:?} Prev Value: {prev:?}"
1136 );
1137 }
1138 }
1139
1140 async fn find_by_range<K>(
1141 &mut self,
1142 key_range: Range<K>,
1143 ) -> Pin<Box<maybe_add_send!(dyn Stream<Item = (K, K::Value)> + '_)>>
1144 where
1145 K: DatabaseKey + DatabaseRecord + MaybeSend + MaybeSync,
1146 K::Value: MaybeSend + MaybeSync,
1147 {
1148 let decoders = self.decoders().clone();
1149 Box::pin(
1150 self.raw_find_by_range(Range {
1151 start: &key_range.start.to_bytes(),
1152 end: &key_range.end.to_bytes(),
1153 })
1154 .await
1155 .expect("Unrecoverable error occurred while listing entries from the database")
1156 .map(move |(key_bytes, value_bytes)| {
1157 let key = decode_key_expect(&key_bytes, &decoders);
1158 let value = decode_value_expect(&value_bytes, &decoders, &key_bytes);
1159 (key, value)
1160 }),
1161 )
1162 }
1163
1164 async fn find_by_prefix<KP>(
1165 &mut self,
1166 key_prefix: &KP,
1167 ) -> Pin<
1168 Box<
1169 maybe_add_send!(
1170 dyn Stream<
1171 Item = (
1172 KP::Record,
1173 <<KP as DatabaseLookup>::Record as DatabaseRecord>::Value,
1174 ),
1175 > + '_
1176 ),
1177 >,
1178 >
1179 where
1180 KP: DatabaseLookup + MaybeSend + MaybeSync,
1181 KP::Record: DatabaseKey,
1182 {
1183 let decoders = self.decoders().clone();
1184 Box::pin(
1185 self.raw_find_by_prefix(&key_prefix.to_bytes())
1186 .await
1187 .expect("Unrecoverable error occurred while listing entries from the database")
1188 .map(move |(key_bytes, value_bytes)| {
1189 let key = decode_key_expect(&key_bytes, &decoders);
1190 let value = decode_value_expect(&value_bytes, &decoders, &key_bytes);
1191 (key, value)
1192 }),
1193 )
1194 }
1195
1196 async fn find_by_prefix_sorted_descending<KP>(
1197 &mut self,
1198 key_prefix: &KP,
1199 ) -> Pin<
1200 Box<
1201 maybe_add_send!(
1202 dyn Stream<
1203 Item = (
1204 KP::Record,
1205 <<KP as DatabaseLookup>::Record as DatabaseRecord>::Value,
1206 ),
1207 > + '_
1208 ),
1209 >,
1210 >
1211 where
1212 KP: DatabaseLookup + MaybeSend + MaybeSync,
1213 KP::Record: DatabaseKey,
1214 {
1215 let decoders = self.decoders().clone();
1216 Box::pin(
1217 self.raw_find_by_prefix_sorted_descending(&key_prefix.to_bytes())
1218 .await
1219 .expect("Unrecoverable error occurred while listing entries from the database")
1220 .map(move |(key_bytes, value_bytes)| {
1221 let key = decode_key_expect(&key_bytes, &decoders);
1222 let value = decode_value_expect(&value_bytes, &decoders, &key_bytes);
1223 (key, value)
1224 }),
1225 )
1226 }
1227 async fn remove_entry<K>(&mut self, key: &K) -> Option<K::Value>
1228 where
1229 K: DatabaseKey + DatabaseRecord + MaybeSend + MaybeSync,
1230 {
1231 let key_bytes = key.to_bytes();
1232 self.raw_remove_entry(&key_bytes)
1233 .await
1234 .expect("Unrecoverable error occurred while inserting removing entry from the database")
1235 .map(|value_bytes| {
1236 decode_value_expect::<K::Value>(&value_bytes, self.decoders(), &key_bytes)
1237 })
1238 }
1239 async fn remove_by_prefix<KP>(&mut self, key_prefix: &KP)
1240 where
1241 KP: DatabaseLookup + MaybeSend + MaybeSync,
1242 {
1243 self.raw_remove_by_prefix(&key_prefix.to_bytes())
1244 .await
1245 .expect("Unrecoverable error when removing entries from the database");
1246 }
1247}
1248
1249pub trait WithDecoders {
1252 fn decoders(&self) -> &ModuleDecoderRegistry;
1253}
1254
1255#[apply(async_trait_maybe_send!)]
1257pub trait IRawDatabaseTransaction: MaybeSend + IDatabaseTransactionOps {
1258 async fn commit_tx(self) -> DatabaseResult<()>;
1259}
1260
1261#[apply(async_trait_maybe_send!)]
1265pub trait IDatabaseTransaction: MaybeSend + IDatabaseTransactionOps + fmt::Debug {
1266 async fn commit_tx(&mut self) -> DatabaseResult<()>;
1268
1269 fn is_global(&self) -> bool;
1271
1272 #[doc(hidden)]
1277 fn global_dbtx(&mut self, access_token: GlobalDBTxAccessToken)
1278 -> &mut dyn IDatabaseTransaction;
1279}
1280
1281#[apply(async_trait_maybe_send!)]
1282impl<T> IDatabaseTransaction for Box<T>
1283where
1284 T: IDatabaseTransaction + ?Sized,
1285{
1286 async fn commit_tx(&mut self) -> DatabaseResult<()> {
1287 (**self).commit_tx().await
1288 }
1289
1290 fn is_global(&self) -> bool {
1291 (**self).is_global()
1292 }
1293
1294 fn global_dbtx(
1295 &mut self,
1296 access_token: GlobalDBTxAccessToken,
1297 ) -> &mut dyn IDatabaseTransaction {
1298 (**self).global_dbtx(access_token)
1299 }
1300}
1301
1302#[apply(async_trait_maybe_send!)]
1303impl<'a, T> IDatabaseTransaction for &'a mut T
1304where
1305 T: IDatabaseTransaction + ?Sized,
1306{
1307 async fn commit_tx(&mut self) -> DatabaseResult<()> {
1308 (**self).commit_tx().await
1309 }
1310
1311 fn is_global(&self) -> bool {
1312 (**self).is_global()
1313 }
1314
1315 fn global_dbtx(&mut self, access_key: GlobalDBTxAccessToken) -> &mut dyn IDatabaseTransaction {
1316 (**self).global_dbtx(access_key)
1317 }
1318}
1319
1320struct BaseDatabaseTransaction<Tx> {
1323 raw: Option<Tx>,
1325 notify_queue: Option<NotifyQueue>,
1326 notifications: Arc<Notifications>,
1327}
1328
1329impl<Tx> fmt::Debug for BaseDatabaseTransaction<Tx>
1330where
1331 Tx: fmt::Debug,
1332{
1333 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1334 f.write_fmt(format_args!(
1335 "BaseDatabaseTransaction{{ raw={:?} }}",
1336 self.raw
1337 ))
1338 }
1339}
1340impl<Tx> BaseDatabaseTransaction<Tx>
1341where
1342 Tx: IRawDatabaseTransaction,
1343{
1344 fn new(dbtx: Tx, notifications: Arc<Notifications>) -> Self {
1345 Self {
1346 raw: Some(dbtx),
1347 notifications,
1348 notify_queue: Some(NotifyQueue::new()),
1349 }
1350 }
1351
1352 fn add_notification_key(&mut self, key: &[u8]) -> DatabaseResult<()> {
1353 self.notify_queue
1354 .as_mut()
1355 .ok_or(DatabaseError::TransactionConsumed)?
1356 .add(key);
1357 Ok(())
1358 }
1359}
1360
1361#[apply(async_trait_maybe_send!)]
1362impl<Tx: IRawDatabaseTransaction> IDatabaseTransactionOpsCore for BaseDatabaseTransaction<Tx> {
1363 async fn raw_insert_bytes(
1364 &mut self,
1365 key: &[u8],
1366 value: &[u8],
1367 ) -> DatabaseResult<Option<Vec<u8>>> {
1368 self.add_notification_key(key)?;
1369 self.raw
1370 .as_mut()
1371 .ok_or(DatabaseError::TransactionConsumed)?
1372 .raw_insert_bytes(key, value)
1373 .await
1374 }
1375
1376 async fn raw_get_bytes(&mut self, key: &[u8]) -> DatabaseResult<Option<Vec<u8>>> {
1377 self.raw
1378 .as_mut()
1379 .ok_or(DatabaseError::TransactionConsumed)?
1380 .raw_get_bytes(key)
1381 .await
1382 }
1383
1384 async fn raw_remove_entry(&mut self, key: &[u8]) -> DatabaseResult<Option<Vec<u8>>> {
1385 self.add_notification_key(key)?;
1386 self.raw
1387 .as_mut()
1388 .ok_or(DatabaseError::TransactionConsumed)?
1389 .raw_remove_entry(key)
1390 .await
1391 }
1392
1393 async fn raw_find_by_range(
1394 &mut self,
1395 key_range: Range<&[u8]>,
1396 ) -> DatabaseResult<PrefixStream<'_>> {
1397 self.raw
1398 .as_mut()
1399 .ok_or(DatabaseError::TransactionConsumed)?
1400 .raw_find_by_range(key_range)
1401 .await
1402 }
1403
1404 async fn raw_find_by_prefix(&mut self, key_prefix: &[u8]) -> DatabaseResult<PrefixStream<'_>> {
1405 self.raw
1406 .as_mut()
1407 .ok_or(DatabaseError::TransactionConsumed)?
1408 .raw_find_by_prefix(key_prefix)
1409 .await
1410 }
1411
1412 async fn raw_find_by_prefix_sorted_descending(
1413 &mut self,
1414 key_prefix: &[u8],
1415 ) -> DatabaseResult<PrefixStream<'_>> {
1416 self.raw
1417 .as_mut()
1418 .ok_or(DatabaseError::TransactionConsumed)?
1419 .raw_find_by_prefix_sorted_descending(key_prefix)
1420 .await
1421 }
1422
1423 async fn raw_remove_by_prefix(&mut self, key_prefix: &[u8]) -> DatabaseResult<()> {
1424 self.raw
1425 .as_mut()
1426 .ok_or(DatabaseError::TransactionConsumed)?
1427 .raw_remove_by_prefix(key_prefix)
1428 .await
1429 }
1430}
1431
1432impl<Tx: IRawDatabaseTransaction> IDatabaseTransactionOps for BaseDatabaseTransaction<Tx> {}
1433
1434#[apply(async_trait_maybe_send!)]
1435impl<Tx: IRawDatabaseTransaction + fmt::Debug> IDatabaseTransaction
1436 for BaseDatabaseTransaction<Tx>
1437{
1438 async fn commit_tx(&mut self) -> DatabaseResult<()> {
1439 self.raw
1440 .take()
1441 .ok_or(DatabaseError::TransactionConsumed)?
1442 .commit_tx()
1443 .await?;
1444 self.notifications.submit_queue(
1445 &self
1446 .notify_queue
1447 .take()
1448 .expect("commit must be called only once"),
1449 );
1450 Ok(())
1451 }
1452
1453 fn is_global(&self) -> bool {
1454 true
1455 }
1456
1457 fn global_dbtx(
1458 &mut self,
1459 _access_token: GlobalDBTxAccessToken,
1460 ) -> &mut dyn IDatabaseTransaction {
1461 panic!("Illegal to call global_dbtx on BaseDatabaseTransaction");
1462 }
1463}
1464
1465#[derive(Clone)]
1468struct CommitTracker {
1469 is_committed: bool,
1471 has_writes: bool,
1473 ignore_uncommitted: bool,
1475}
1476
1477impl Drop for CommitTracker {
1478 fn drop(&mut self) {
1479 if self.has_writes && !self.is_committed {
1480 if self.ignore_uncommitted {
1481 trace!(
1482 target: LOG_DB,
1483 "DatabaseTransaction has writes and has not called commit, but that's expected."
1484 );
1485 } else {
1486 warn!(
1487 target: LOG_DB,
1488 location = ?backtrace::Backtrace::new(),
1489 "DatabaseTransaction has writes and has not called commit."
1490 );
1491 }
1492 }
1493 }
1494}
1495
1496enum MaybeRef<'a, T> {
1497 Owned(T),
1498 Borrowed(&'a mut T),
1499}
1500
1501impl<T> ops::Deref for MaybeRef<'_, T> {
1502 type Target = T;
1503
1504 fn deref(&self) -> &Self::Target {
1505 match self {
1506 MaybeRef::Owned(o) => o,
1507 MaybeRef::Borrowed(r) => r,
1508 }
1509 }
1510}
1511
1512impl<T> ops::DerefMut for MaybeRef<'_, T> {
1513 fn deref_mut(&mut self) -> &mut Self::Target {
1514 match self {
1515 MaybeRef::Owned(o) => o,
1516 MaybeRef::Borrowed(r) => r,
1517 }
1518 }
1519}
1520
1521pub struct Committable;
1525
1526pub struct NonCommittable;
1530
1531pub struct DatabaseTransaction<'tx, Cap = NonCommittable> {
1535 tx: Box<dyn IDatabaseTransaction + 'tx>,
1536 decoders: ModuleDecoderRegistry,
1537 commit_tracker: MaybeRef<'tx, CommitTracker>,
1538 on_commit_hooks: MaybeRef<'tx, Vec<Box<maybe_add_send!(dyn FnOnce())>>>,
1539 capability: marker::PhantomData<Cap>,
1540}
1541
1542impl<Cap> fmt::Debug for DatabaseTransaction<'_, Cap> {
1543 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1544 f.write_fmt(format_args!(
1545 "DatabaseTransaction {{ tx: {:?}, decoders={:?} }}",
1546 self.tx, self.decoders
1547 ))
1548 }
1549}
1550
1551impl<Cap> WithDecoders for DatabaseTransaction<'_, Cap> {
1552 fn decoders(&self) -> &ModuleDecoderRegistry {
1553 &self.decoders
1554 }
1555}
1556
1557#[instrument(target = LOG_DB, level = "trace", skip_all, fields(value_type = std::any::type_name::<V>()), err)]
1558fn decode_value<V: DatabaseValue>(
1559 value_bytes: &[u8],
1560 decoders: &ModuleDecoderRegistry,
1561) -> std::result::Result<V, DecodingError> {
1562 trace!(
1563 bytes = %AbbreviateHexBytes(value_bytes),
1564 "decoding value",
1565 );
1566 V::from_bytes(value_bytes, decoders)
1567}
1568
1569#[track_caller]
1570fn decode_value_expect<V: DatabaseValue>(
1571 value_bytes: &[u8],
1572 decoders: &ModuleDecoderRegistry,
1573 key_bytes: &[u8],
1574) -> V {
1575 decode_value(value_bytes, decoders).unwrap_or_else(|err| {
1576 panic!(
1577 "Unrecoverable decoding DatabaseValue as {}; err={}, key_bytes={}, val_bytes={}",
1578 any::type_name::<V>(),
1579 err.fmt_compact(),
1580 AbbreviateHexBytes(key_bytes),
1581 AbbreviateHexBytes(value_bytes),
1582 )
1583 })
1584}
1585
1586#[track_caller]
1587fn decode_key_expect<K: DatabaseKey>(key_bytes: &[u8], decoders: &ModuleDecoderRegistry) -> K {
1588 trace!(
1589 bytes = %AbbreviateHexBytes(key_bytes),
1590 "decoding key",
1591 );
1592 K::from_bytes(key_bytes, decoders).unwrap_or_else(|err| {
1593 panic!(
1594 "Unrecoverable decoding DatabaseKey as {}; err={}; bytes={}",
1595 any::type_name::<K>(),
1596 err.fmt_compact(),
1597 AbbreviateHexBytes(key_bytes)
1598 )
1599 })
1600}
1601
1602impl<'tx, Cap> DatabaseTransaction<'tx, Cap> {
1603 pub fn into_nc(self) -> DatabaseTransaction<'tx, NonCommittable> {
1605 DatabaseTransaction {
1606 tx: self.tx,
1607 decoders: self.decoders,
1608 commit_tracker: self.commit_tracker,
1609 on_commit_hooks: self.on_commit_hooks,
1610 capability: PhantomData::<NonCommittable>,
1611 }
1612 }
1613
1614 pub fn to_ref_nc<'s, 'a>(&'s mut self) -> DatabaseTransaction<'a, NonCommittable>
1616 where
1617 's: 'a,
1618 {
1619 self.to_ref().into_nc()
1620 }
1621
1622 pub fn with_prefix<'a: 'tx>(self, prefix: Vec<u8>) -> DatabaseTransaction<'a, Cap>
1624 where
1625 'tx: 'a,
1626 {
1627 DatabaseTransaction {
1628 tx: Box::new(PrefixDatabaseTransaction {
1629 inner: self.tx,
1630 global_dbtx_access_token: None,
1631 prefix,
1632 }),
1633 decoders: self.decoders,
1634 commit_tracker: self.commit_tracker,
1635 on_commit_hooks: self.on_commit_hooks,
1636 capability: self.capability,
1637 }
1638 }
1639
1640 pub fn with_prefix_module_id<'a: 'tx>(
1644 self,
1645 module_instance_id: ModuleInstanceId,
1646 ) -> (DatabaseTransaction<'a, Cap>, GlobalDBTxAccessToken)
1647 where
1648 'tx: 'a,
1649 {
1650 let prefix = module_instance_id_to_byte_prefix(module_instance_id);
1651 let global_dbtx_access_token = GlobalDBTxAccessToken::from_prefix(&prefix);
1652 (
1653 DatabaseTransaction {
1654 tx: Box::new(PrefixDatabaseTransaction {
1655 inner: self.tx,
1656 global_dbtx_access_token: Some(global_dbtx_access_token),
1657 prefix,
1658 }),
1659 decoders: self.decoders,
1660 commit_tracker: self.commit_tracker,
1661 on_commit_hooks: self.on_commit_hooks,
1662 capability: self.capability,
1663 },
1664 global_dbtx_access_token,
1665 )
1666 }
1667
1668 pub fn to_ref<'s, 'a>(&'s mut self) -> DatabaseTransaction<'a, Cap>
1670 where
1671 's: 'a,
1672 {
1673 let decoders = self.decoders.clone();
1674
1675 DatabaseTransaction {
1676 tx: Box::new(&mut self.tx),
1677 decoders,
1678 commit_tracker: match self.commit_tracker {
1679 MaybeRef::Owned(ref mut o) => MaybeRef::Borrowed(o),
1680 MaybeRef::Borrowed(ref mut b) => MaybeRef::Borrowed(b),
1681 },
1682 on_commit_hooks: match self.on_commit_hooks {
1683 MaybeRef::Owned(ref mut o) => MaybeRef::Borrowed(o),
1684 MaybeRef::Borrowed(ref mut b) => MaybeRef::Borrowed(b),
1685 },
1686 capability: self.capability,
1687 }
1688 }
1689
1690 pub fn to_ref_with_prefix<'a>(&'a mut self, prefix: Vec<u8>) -> DatabaseTransaction<'a, Cap>
1692 where
1693 'tx: 'a,
1694 {
1695 DatabaseTransaction {
1696 tx: Box::new(PrefixDatabaseTransaction {
1697 inner: &mut self.tx,
1698 global_dbtx_access_token: None,
1699 prefix,
1700 }),
1701 decoders: self.decoders.clone(),
1702 commit_tracker: match self.commit_tracker {
1703 MaybeRef::Owned(ref mut o) => MaybeRef::Borrowed(o),
1704 MaybeRef::Borrowed(ref mut b) => MaybeRef::Borrowed(b),
1705 },
1706 on_commit_hooks: match self.on_commit_hooks {
1707 MaybeRef::Owned(ref mut o) => MaybeRef::Borrowed(o),
1708 MaybeRef::Borrowed(ref mut b) => MaybeRef::Borrowed(b),
1709 },
1710 capability: self.capability,
1711 }
1712 }
1713
1714 pub fn to_ref_with_prefix_module_id<'a>(
1715 &'a mut self,
1716 module_instance_id: ModuleInstanceId,
1717 ) -> (DatabaseTransaction<'a, Cap>, GlobalDBTxAccessToken)
1718 where
1719 'tx: 'a,
1720 {
1721 let prefix = module_instance_id_to_byte_prefix(module_instance_id);
1722 let global_dbtx_access_token = GlobalDBTxAccessToken::from_prefix(&prefix);
1723 (
1724 DatabaseTransaction {
1725 tx: Box::new(PrefixDatabaseTransaction {
1726 inner: &mut self.tx,
1727 global_dbtx_access_token: Some(global_dbtx_access_token),
1728 prefix,
1729 }),
1730 decoders: self.decoders.clone(),
1731 commit_tracker: match self.commit_tracker {
1732 MaybeRef::Owned(ref mut o) => MaybeRef::Borrowed(o),
1733 MaybeRef::Borrowed(ref mut b) => MaybeRef::Borrowed(b),
1734 },
1735 on_commit_hooks: match self.on_commit_hooks {
1736 MaybeRef::Owned(ref mut o) => MaybeRef::Borrowed(o),
1737 MaybeRef::Borrowed(ref mut b) => MaybeRef::Borrowed(b),
1738 },
1739 capability: self.capability,
1740 },
1741 global_dbtx_access_token,
1742 )
1743 }
1744
1745 pub fn is_global(&self) -> bool {
1747 self.tx.is_global()
1748 }
1749
1750 pub fn ensure_global(&self) -> DatabaseResult<()> {
1752 if !self.is_global() {
1753 return Err(DatabaseError::NotGlobal);
1754 }
1755
1756 Ok(())
1757 }
1758
1759 pub fn ensure_isolated(&self) -> DatabaseResult<()> {
1761 if self.is_global() {
1762 return Err(DatabaseError::NotIsolated);
1763 }
1764
1765 Ok(())
1766 }
1767
1768 pub fn ignore_uncommitted(&mut self) -> &mut Self {
1770 self.commit_tracker.ignore_uncommitted = true;
1771 self
1772 }
1773
1774 pub fn warn_uncommitted(&mut self) -> &mut Self {
1776 self.commit_tracker.ignore_uncommitted = false;
1777 self
1778 }
1779
1780 #[instrument(target = LOG_DB, level = "trace", skip_all)]
1782 pub fn on_commit(&mut self, f: maybe_add_send!(impl FnOnce() + 'static)) {
1783 self.on_commit_hooks.push(Box::new(f));
1784 }
1785
1786 pub fn global_dbtx<'a>(
1787 &'a mut self,
1788 access_token: GlobalDBTxAccessToken,
1789 ) -> DatabaseTransaction<'a, Cap>
1790 where
1791 'tx: 'a,
1792 {
1793 let decoders = self.decoders.clone();
1794
1795 DatabaseTransaction {
1796 tx: Box::new(self.tx.global_dbtx(access_token)),
1797 decoders,
1798 commit_tracker: match self.commit_tracker {
1799 MaybeRef::Owned(ref mut o) => MaybeRef::Borrowed(o),
1800 MaybeRef::Borrowed(ref mut b) => MaybeRef::Borrowed(b),
1801 },
1802 on_commit_hooks: match self.on_commit_hooks {
1803 MaybeRef::Owned(ref mut o) => MaybeRef::Borrowed(o),
1804 MaybeRef::Borrowed(ref mut b) => MaybeRef::Borrowed(b),
1805 },
1806 capability: self.capability,
1807 }
1808 }
1809}
1810
1811#[derive(Copy, Clone, Debug, PartialEq, Eq)]
1813pub struct GlobalDBTxAccessToken(u32);
1814
1815impl GlobalDBTxAccessToken {
1816 fn from_prefix(prefix: &[u8]) -> Self {
1827 Self(prefix.iter().fold(0, |acc, b| acc + u32::from(*b)) + 513)
1828 }
1829}
1830
1831impl<'tx> DatabaseTransaction<'tx, Committable> {
1832 pub fn new(dbtx: Box<dyn IDatabaseTransaction + 'tx>, decoders: ModuleDecoderRegistry) -> Self {
1833 Self {
1834 tx: dbtx,
1835 decoders,
1836 commit_tracker: MaybeRef::Owned(CommitTracker {
1837 is_committed: false,
1838 has_writes: false,
1839 ignore_uncommitted: false,
1840 }),
1841 on_commit_hooks: MaybeRef::Owned(vec![]),
1842 capability: PhantomData,
1843 }
1844 }
1845
1846 pub async fn commit_tx_result(mut self) -> DatabaseResult<()> {
1847 self.commit_tracker.is_committed = true;
1848 let commit_result = self.tx.commit_tx().await;
1849
1850 if commit_result.is_ok() {
1852 for hook in self.on_commit_hooks.deref_mut().drain(..) {
1853 hook();
1854 }
1855 }
1856
1857 commit_result
1858 }
1859
1860 pub async fn commit_tx(mut self) {
1861 self.commit_tracker.is_committed = true;
1862 self.commit_tx_result()
1863 .await
1864 .expect("Unrecoverable error occurred while committing to the database.");
1865 }
1866}
1867
1868#[apply(async_trait_maybe_send!)]
1869impl<Cap> IDatabaseTransactionOpsCore for DatabaseTransaction<'_, Cap>
1870where
1871 Cap: Send,
1872{
1873 async fn raw_insert_bytes(
1874 &mut self,
1875 key: &[u8],
1876 value: &[u8],
1877 ) -> DatabaseResult<Option<Vec<u8>>> {
1878 self.commit_tracker.has_writes = true;
1879 self.tx.raw_insert_bytes(key, value).await
1880 }
1881
1882 async fn raw_get_bytes(&mut self, key: &[u8]) -> DatabaseResult<Option<Vec<u8>>> {
1883 self.tx.raw_get_bytes(key).await
1884 }
1885
1886 async fn raw_remove_entry(&mut self, key: &[u8]) -> DatabaseResult<Option<Vec<u8>>> {
1887 self.tx.raw_remove_entry(key).await
1888 }
1889
1890 async fn raw_find_by_range(
1891 &mut self,
1892 key_range: Range<&[u8]>,
1893 ) -> DatabaseResult<PrefixStream<'_>> {
1894 self.tx.raw_find_by_range(key_range).await
1895 }
1896
1897 async fn raw_find_by_prefix(&mut self, key_prefix: &[u8]) -> DatabaseResult<PrefixStream<'_>> {
1898 self.tx.raw_find_by_prefix(key_prefix).await
1899 }
1900
1901 async fn raw_find_by_prefix_sorted_descending(
1902 &mut self,
1903 key_prefix: &[u8],
1904 ) -> DatabaseResult<PrefixStream<'_>> {
1905 self.tx
1906 .raw_find_by_prefix_sorted_descending(key_prefix)
1907 .await
1908 }
1909
1910 async fn raw_remove_by_prefix(&mut self, key_prefix: &[u8]) -> DatabaseResult<()> {
1911 self.commit_tracker.has_writes = true;
1912 self.tx.raw_remove_by_prefix(key_prefix).await
1913 }
1914}
1915impl IDatabaseTransactionOps for DatabaseTransaction<'_, Committable> {}
1916
1917impl<T> DatabaseKeyPrefix for T
1918where
1919 T: DatabaseLookup + crate::encoding::Encodable + Debug,
1920{
1921 fn to_bytes(&self) -> Vec<u8> {
1922 let mut data = vec![<Self as DatabaseLookup>::Record::DB_PREFIX];
1923 data.append(&mut self.consensus_encode_to_vec());
1924 data
1925 }
1926}
1927
1928impl<T> DatabaseKey for T
1929where
1930 T: DatabaseRecord + crate::encoding::Decodable + Sized,
1933{
1934 const NOTIFY_ON_MODIFY: bool = <T as DatabaseRecord>::NOTIFY_ON_MODIFY;
1935 fn from_bytes(
1936 data: &[u8],
1937 modules: &ModuleDecoderRegistry,
1938 ) -> std::result::Result<Self, DecodingError> {
1939 if data.is_empty() {
1940 return Err(DecodingError::wrong_length(1, 0));
1942 }
1943
1944 if data[0] != Self::DB_PREFIX {
1945 return Err(DecodingError::wrong_prefix(Self::DB_PREFIX, data[0]));
1946 }
1947
1948 <Self as crate::encoding::Decodable>::consensus_decode_whole(&data[1..], modules)
1949 .map_err(DecodingError::from)
1950 }
1951}
1952
1953impl<T> DatabaseValue for T
1954where
1955 T: Debug + Encodable + Decodable,
1956{
1957 fn from_bytes(
1958 data: &[u8],
1959 modules: &ModuleDecoderRegistry,
1960 ) -> std::result::Result<Self, DecodingError> {
1961 T::consensus_decode_whole(data, modules).map_err(DecodingError::from)
1962 }
1963
1964 fn to_bytes(&self) -> Vec<u8> {
1965 self.consensus_encode_to_vec()
1966 }
1967}
1968
1969#[macro_export]
2030macro_rules! impl_db_record {
2031 (key = $key:ty, value = $val:ty, db_prefix = $db_prefix:expr_2021 $(, notify_on_modify = $notify:tt)? $(,)?) => {
2032 impl $crate::db::DatabaseRecord for $key {
2033 const DB_PREFIX: u8 = $db_prefix as u8;
2034 $(const NOTIFY_ON_MODIFY: bool = $notify;)?
2035 type Key = Self;
2036 type Value = $val;
2037 }
2038 $(
2039 impl_db_record! {
2040 @impl_notify_marker key = $key, notify_on_modify = $notify
2041 }
2042 )?
2043 };
2044 (@impl_notify_marker key = $key:ty, notify_on_modify = true) => {
2046 impl $crate::db::DatabaseKeyWithNotify for $key {}
2047 };
2048 (@impl_notify_marker key = $key:ty, notify_on_modify = false) => {};
2050}
2051
2052#[macro_export]
2053macro_rules! impl_db_lookup{
2054 (key = $key:ty $(, query_prefix = $query_prefix:ty)* $(,)?) => {
2055 $(
2056 impl $crate::db::DatabaseLookup for $query_prefix {
2057 type Record = $key;
2058 }
2059 )*
2060 };
2061}
2062
2063#[derive(Debug, Encodable, Decodable, Serialize)]
2065pub struct DatabaseVersionKeyV0;
2066
2067#[derive(Debug, Encodable, Decodable, Serialize)]
2068pub struct DatabaseVersionKey(pub ModuleInstanceId);
2069
2070#[derive(Debug, Encodable, Decodable, Serialize, Clone, PartialOrd, Ord, PartialEq, Eq, Copy)]
2071pub struct DatabaseVersion(pub u64);
2072
2073impl_db_record!(
2074 key = DatabaseVersionKeyV0,
2075 value = DatabaseVersion,
2076 db_prefix = DbKeyPrefix::DatabaseVersion
2077);
2078
2079impl_db_record!(
2080 key = DatabaseVersionKey,
2081 value = DatabaseVersion,
2082 db_prefix = DbKeyPrefix::DatabaseVersion
2083);
2084
2085impl std::fmt::Display for DatabaseVersion {
2086 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
2087 write!(f, "{}", self.0)
2088 }
2089}
2090
2091impl DatabaseVersion {
2092 pub fn increment(&self) -> Self {
2093 Self(self.0 + 1)
2094 }
2095}
2096
2097impl std::fmt::Display for DbKeyPrefix {
2098 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
2099 write!(f, "{self:?}")
2100 }
2101}
2102
2103#[repr(u8)]
2104#[derive(Clone, EnumIter, Debug)]
2105pub enum DbKeyPrefix {
2106 DatabaseVersion = 0x50,
2107 ClientBackup = 0x51,
2108}
2109
2110#[derive(Debug, Error)]
2111#[non_exhaustive]
2112pub enum DecodingError {
2113 #[error("Key had a wrong prefix, expected {expected} but got {found}")]
2114 WrongPrefix { expected: u8, found: u8 },
2115 #[error("Key had a wrong length, expected {expected} but got {found}")]
2116 WrongLength { expected: usize, found: usize },
2117 #[error("Other decoding error")]
2118 Other(#[source] Box<dyn Error + Send + Sync>),
2119 #[error("Invalid consensus encoding")]
2121 Decode(#[from] DecodeError),
2122}
2123
2124impl DecodingError {
2125 pub fn other<E: Error + Send + Sync + 'static>(error: E) -> Self {
2126 Self::Other(Box::new(error))
2127 }
2128
2129 pub fn wrong_prefix(expected: u8, found: u8) -> Self {
2130 Self::WrongPrefix { expected, found }
2131 }
2132
2133 pub fn wrong_length(expected: usize, found: usize) -> Self {
2134 Self::WrongLength { expected, found }
2135 }
2136}
2137
2138#[derive(Debug, Error)]
2140#[non_exhaustive]
2141pub enum DatabaseError {
2142 #[error("Write-write conflict detected")]
2145 WriteConflict,
2146
2147 #[error("Transaction snapshot is older than the retained write history: {0}")]
2159 SnapshotTooOld(Box<dyn Error + Send + Sync>),
2160
2161 #[error("Transaction already consumed")]
2164 TransactionConsumed,
2165
2166 #[error("Database backend error: {0}")]
2168 DatabaseBackend(#[from] Box<dyn Error + Send + Sync>),
2169
2170 #[error("Database instance is not global")]
2172 NotGlobal,
2173
2174 #[error("Database instance is not isolated")]
2176 NotIsolated,
2177}
2178
2179impl DatabaseError {
2180 pub fn backend<E: Error + Send + Sync + 'static>(error: E) -> Self {
2182 Self::DatabaseBackend(Box::new(error))
2183 }
2184
2185 pub fn snapshot_too_old<E: Error + Send + Sync + 'static>(error: E) -> Self {
2187 Self::SnapshotTooOld(Box::new(error))
2188 }
2189}
2190
2191#[macro_export]
2192macro_rules! push_db_pair_items {
2193 ($dbtx:ident, $prefix_type:expr_2021, $key_type:ty, $value_type:ty, $map:ident, $key_literal:literal) => {
2194 let db_items =
2195 $crate::db::IDatabaseTransactionOpsCoreTyped::find_by_prefix($dbtx, &$prefix_type)
2196 .await
2197 .map(|(key, val)| {
2198 (
2199 $crate::encoding::Encodable::consensus_encode_to_hex(&key),
2200 val,
2201 )
2202 })
2203 .collect::<BTreeMap<String, $value_type>>()
2204 .await;
2205
2206 $map.insert($key_literal.to_string(), Box::new(db_items));
2207 };
2208}
2209
2210#[macro_export]
2211macro_rules! push_db_key_items {
2212 ($dbtx:ident, $prefix_type:expr_2021, $key_type:ty, $map:ident, $key_literal:literal) => {
2213 let db_items =
2214 $crate::db::IDatabaseTransactionOpsCoreTyped::find_by_prefix($dbtx, &$prefix_type)
2215 .await
2216 .map(|(key, _)| key)
2217 .collect::<Vec<$key_type>>()
2218 .await;
2219
2220 $map.insert($key_literal.to_string(), Box::new(db_items));
2221 };
2222}
2223
2224pub struct DbMigrationFnContext<'tx, C> {
2238 dbtx: DatabaseTransaction<'tx>,
2239 module_instance_id: Option<ModuleInstanceId>,
2240 ctx: C,
2241 __please_use_constructor: (),
2242}
2243
2244impl<'tx, C> DbMigrationFnContext<'tx, C> {
2245 pub fn new(
2246 dbtx: DatabaseTransaction<'tx>,
2247 module_instance_id: Option<ModuleInstanceId>,
2248 ctx: C,
2249 ) -> Self {
2250 dbtx.ensure_global().expect("Must pass global dbtx");
2251 Self {
2252 dbtx,
2253 module_instance_id,
2254 ctx,
2255 __please_use_constructor: (),
2257 }
2258 }
2259
2260 pub fn map<R>(self, f: impl FnOnce(C) -> R) -> DbMigrationFnContext<'tx, R> {
2261 DbMigrationFnContext::new(self.dbtx, self.module_instance_id, f(self.ctx))
2262 }
2263
2264 #[doc(hidden)]
2266 pub fn split_dbtx_ctx<'s>(&'s mut self) -> (&'s mut DatabaseTransaction<'tx>, &'s C) {
2267 let Self { dbtx, ctx, .. } = self;
2268
2269 (dbtx, ctx)
2270 }
2271
2272 pub fn dbtx(&'_ mut self) -> DatabaseTransaction<'_> {
2273 if let Some(module_instance_id) = self.module_instance_id {
2274 self.dbtx.to_ref_with_prefix_module_id(module_instance_id).0
2275 } else {
2276 self.dbtx.to_ref_nc()
2277 }
2278 }
2279
2280 #[doc(hidden)]
2282 pub fn module_instance_id(&self) -> Option<ModuleInstanceId> {
2283 self.module_instance_id
2284 }
2285}
2286
2287pub type GeneralDbMigrationFn = DbMigrationFn<()>;
2289pub type GeneralDbMigrationFnContext<'tx> = DbMigrationFnContext<'tx, ()>;
2290
2291pub type ClientCoreDbMigrationFn = DbMigrationFn<()>;
2296pub type ClientCoreDbMigrationFnContext<'tx> = DbMigrationFnContext<'tx, ()>;
2297
2298pub type DbMigrationFn<C> = Box<
2309 maybe_add_send_sync!(
2310 dyn for<'tx> Fn(
2311 DbMigrationFnContext<'tx, C>,
2312 ) -> Pin<
2313 Box<maybe_add_send!(dyn futures::Future<Output = Result<(), DbMigrationError>> + 'tx)>,
2314 >
2315 ),
2316>;
2317
2318#[derive(Debug, Error)]
2320#[non_exhaustive]
2321pub enum DbMigrationError {
2322 #[error("Database error")]
2324 Database(#[from] DatabaseError),
2325 #[error("Failed to consensus-decode a database entry")]
2327 Decode(#[from] DecodeError),
2328 #[error(
2330 "On disk database version {on_disk} for module {kind} is higher than the code \
2331 database version {target}"
2332 )]
2333 VersionTooHigh {
2334 kind: String,
2335 on_disk: DatabaseVersion,
2336 target: DatabaseVersion,
2337 },
2338 #[error("Migration failed")]
2340 Other(#[source] Box<dyn Error + Send + Sync>),
2341}
2342
2343impl DbMigrationError {
2344 pub fn other<E>(error: E) -> Self
2347 where
2348 E: Into<Box<dyn Error + Send + Sync>>,
2349 {
2350 Self::Other(error.into())
2351 }
2352}
2353
2354pub fn get_current_database_version<F>(
2358 migrations: &BTreeMap<DatabaseVersion, F>,
2359) -> DatabaseVersion {
2360 let versions = migrations.keys().copied().collect::<Vec<_>>();
2361
2362 if !versions
2365 .windows(2)
2366 .all(|window| window[0].increment() == window[1])
2367 {
2368 panic!("Database Migrations are not defined contiguously");
2369 }
2370
2371 versions
2372 .last()
2373 .map_or(DatabaseVersion(0), DatabaseVersion::increment)
2374}
2375
2376pub async fn apply_migrations<C>(
2377 db: &Database,
2378 ctx: C,
2379 kind: String,
2380 migrations: BTreeMap<DatabaseVersion, DbMigrationFn<C>>,
2381 module_instance_id: Option<ModuleInstanceId>,
2382 external_prefixes_above: Option<u8>,
2385) -> Result<(), DbMigrationError>
2386where
2387 C: Clone,
2388{
2389 let mut dbtx = db.begin_transaction().await;
2390 apply_migrations_dbtx(
2391 &mut dbtx.to_ref_nc(),
2392 ctx,
2393 kind,
2394 migrations,
2395 module_instance_id,
2396 external_prefixes_above,
2397 )
2398 .await?;
2399
2400 Ok(dbtx.commit_tx_result().await?)
2401}
2402pub async fn apply_migrations_dbtx<C>(
2414 global_dbtx: &mut DatabaseTransaction<'_>,
2415 ctx: C,
2416 kind: String,
2417 migrations: BTreeMap<DatabaseVersion, DbMigrationFn<C>>,
2418 module_instance_id: Option<ModuleInstanceId>,
2419 external_prefixes_above: Option<u8>,
2422) -> Result<(), DbMigrationError>
2423where
2424 C: Clone,
2425{
2426 let is_new_db = global_dbtx
2429 .raw_find_by_prefix(&[])
2430 .await?
2431 .filter(|(key, _v)| {
2432 std::future::ready(
2433 external_prefixes_above.is_none_or(|external_prefixes_above| {
2434 !key.is_empty() && key[0] < external_prefixes_above
2435 }),
2436 )
2437 })
2438 .next()
2439 .await
2440 .is_none();
2441
2442 let target_db_version = get_current_database_version(&migrations);
2443
2444 create_database_version_dbtx(
2446 global_dbtx,
2447 target_db_version,
2448 module_instance_id,
2449 kind.clone(),
2450 is_new_db,
2451 )
2452 .await;
2453
2454 let module_instance_id_key = module_instance_id_or_global(module_instance_id);
2455
2456 let disk_version = global_dbtx
2457 .get_value(&DatabaseVersionKey(module_instance_id_key))
2458 .await;
2459
2460 let db_version = if let Some(disk_version) = disk_version {
2461 let mut current_db_version = disk_version;
2462
2463 if current_db_version > target_db_version {
2464 return Err(DbMigrationError::VersionTooHigh {
2465 kind,
2466 on_disk: current_db_version,
2467 target: target_db_version,
2468 });
2469 }
2470
2471 while current_db_version < target_db_version {
2472 if let Some(migration) = migrations.get(¤t_db_version) {
2473 info!(target: LOG_DB, ?kind, ?current_db_version, ?target_db_version, "Migrating module...");
2474 migration(DbMigrationFnContext::new(
2475 global_dbtx.to_ref_nc(),
2476 module_instance_id,
2477 ctx.clone(),
2478 ))
2479 .await?;
2480 } else {
2481 warn!(target: LOG_DB, ?current_db_version, "Missing server db migration");
2482 }
2483
2484 current_db_version = current_db_version.increment();
2485
2486 global_dbtx
2487 .insert_entry(
2488 &DatabaseVersionKey(module_instance_id_key),
2489 ¤t_db_version,
2490 )
2491 .await;
2492 }
2493
2494 current_db_version
2495 } else {
2496 target_db_version
2497 };
2498
2499 debug!(target: LOG_DB, ?kind, ?db_version, "DB Version");
2500 Ok(())
2501}
2502
2503pub async fn create_database_version(
2504 db: &Database,
2505 target_db_version: DatabaseVersion,
2506 module_instance_id: Option<ModuleInstanceId>,
2507 kind: String,
2508 is_new_db: bool,
2509) -> Result<(), DbMigrationError> {
2510 let mut dbtx = db.begin_transaction().await;
2511
2512 create_database_version_dbtx(
2513 &mut dbtx.to_ref_nc(),
2514 target_db_version,
2515 module_instance_id,
2516 kind,
2517 is_new_db,
2518 )
2519 .await;
2520
2521 dbtx.commit_tx_result().await?;
2522 Ok(())
2523}
2524
2525pub async fn create_database_version_dbtx(
2529 global_dbtx: &mut DatabaseTransaction<'_>,
2530 target_db_version: DatabaseVersion,
2531 module_instance_id: Option<ModuleInstanceId>,
2532 kind: String,
2533 is_new_db: bool,
2534) {
2535 let key_module_instance_id = module_instance_id_or_global(module_instance_id);
2536
2537 if global_dbtx
2541 .get_value(&DatabaseVersionKey(key_module_instance_id))
2542 .await
2543 .is_none()
2544 {
2545 let current_version_in_module = if let Some(module_instance_id) = module_instance_id {
2554 remove_current_db_version_if_exists(
2555 &mut global_dbtx
2556 .to_ref_with_prefix_module_id(module_instance_id)
2557 .0
2558 .into_nc(),
2559 is_new_db,
2560 target_db_version,
2561 )
2562 .await
2563 } else {
2564 remove_current_db_version_if_exists(
2565 &mut global_dbtx.to_ref().into_nc(),
2566 is_new_db,
2567 target_db_version,
2568 )
2569 .await
2570 };
2571
2572 debug!(target: LOG_DB, ?kind, ?current_version_in_module, ?target_db_version, ?is_new_db, "Creating DatabaseVersionKey...");
2574 global_dbtx
2575 .insert_new_entry(
2576 &DatabaseVersionKey(key_module_instance_id),
2577 ¤t_version_in_module,
2578 )
2579 .await;
2580 }
2581}
2582
2583async fn remove_current_db_version_if_exists(
2588 version_dbtx: &mut DatabaseTransaction<'_>,
2589 is_new_db: bool,
2590 target_db_version: DatabaseVersion,
2591) -> DatabaseVersion {
2592 let current_version_in_module = version_dbtx.remove_entry(&DatabaseVersionKeyV0).await;
2596 match current_version_in_module {
2597 Some(database_version) => database_version,
2598 None if is_new_db => target_db_version,
2599 None => DatabaseVersion(0),
2600 }
2601}
2602
2603fn module_instance_id_or_global(module_instance_id: Option<ModuleInstanceId>) -> ModuleInstanceId {
2606 module_instance_id.unwrap_or_else(|| MODULE_GLOBAL_PREFIX.into())
2608}
2609#[allow(unused_imports)]
2610mod test_utils;
2611
2612pub async fn find_by_prefix_sorted_descending<'r, 'inner, KP>(
2613 tx: &'r mut (dyn IDatabaseTransaction + 'inner),
2614 decoders: ModuleDecoderRegistry,
2615 key_prefix: &KP,
2616) -> impl Stream<
2617 Item = (
2618 KP::Record,
2619 <<KP as DatabaseLookup>::Record as DatabaseRecord>::Value,
2620 ),
2621>
2622+ 'r
2623+ use<'r, KP>
2624where
2625 'inner: 'r,
2626 KP: DatabaseLookup,
2627 KP::Record: DatabaseKey,
2628{
2629 debug!(target: LOG_DB, "find by prefix sorted descending");
2630 let prefix_bytes = key_prefix.to_bytes();
2631 tx.raw_find_by_prefix_sorted_descending(&prefix_bytes)
2632 .await
2633 .expect("Error doing prefix search in database")
2634 .map(move |(key_bytes, value_bytes)| {
2635 let key = decode_key_expect(&key_bytes, &decoders);
2636 let value = decode_value_expect(&value_bytes, &decoders, &key_bytes);
2637 (key, value)
2638 })
2639}
2640
2641pub async fn verify_module_db_integrity_dbtx(
2642 dbtx: &mut DatabaseTransaction<'_>,
2643 module_id: ModuleInstanceId,
2644 module_kind: ModuleKind,
2645 prefixes: &BTreeSet<u8>,
2646) {
2647 let module_db_prefix = module_instance_id_to_byte_prefix(module_id);
2648 if module_id < 250 {
2649 assert_eq!(module_db_prefix.len(), 2);
2650 }
2651 let mut records = dbtx
2652 .raw_find_by_prefix(&module_db_prefix)
2653 .await
2654 .expect("DB fail");
2655 while let Some((k, v)) = records.next().await {
2656 assert!(
2657 prefixes.contains(&k[module_db_prefix.len()]),
2658 "Unexpected module {module_kind} {module_id} db record found: {}: {}",
2659 k.as_hex(),
2660 v.as_hex()
2661 );
2662 }
2663}
2664
2665#[cfg(test)]
2666mod tests;