core_crypto_keystore/transaction/
mod.rs1mod entity_read;
2mod entity_write;
3mod fetch_from_database;
4mod mls;
5#[cfg(feature = "proteus-keystore")]
6pub mod proteus;
7mod specializations;
8
9use std::{borrow::Cow, collections::HashSet, sync::Arc};
10
11use async_lock::{RwLock, SemaphoreGuardArc};
12use itertools::Itertools;
13use ordermap::OrderMap;
14
15use crate::{
16 CryptoKeystoreError, CryptoKeystoreResult, Database, UniqueArc,
17 traits::{Entity, KeyType},
18 transaction::dynamic_dispatch::EntityId,
19};
20
21pub(crate) mod dynamic_dispatch;
22
23pub struct Transaction {
34 cache: RwLock<OrderMap<EntityId, dynamic_dispatch::Entity>>,
35 deleted: RwLock<HashSet<EntityId>>,
36 _semaphore_guard: Arc<SemaphoreGuardArc>,
37 database: Arc<Database>,
38}
39
40impl Transaction {
41 pub(crate) async fn new(
45 semaphore_guard: SemaphoreGuardArc,
46 database: Arc<Database>,
47 ) -> CryptoKeystoreResult<UniqueArc<Self>> {
48 let transaction = UniqueArc::from(Self {
49 cache: Default::default(),
50 deleted: Default::default(),
51 _semaphore_guard: Arc::new(semaphore_guard),
52 database,
53 });
54
55 let weak = UniqueArc::downgrade(&transaction);
56
57 {
58 let mut transaction_guard = transaction.database.transaction.lock().await;
59 *transaction_guard = Some(weak);
65 }
66
67 Ok(transaction)
68 }
69
70 async fn merge_records<'a, E>(
78 &self,
79 records_a: impl IntoIterator<Item = Cow<'a, E>>,
80 records_b: impl IntoIterator<Item = Cow<'a, E>>,
81 ) -> Vec<E>
82 where
83 E: 'static + Clone + Entity,
84 {
85 let deleted_records = self.deleted.read().await;
86
87 records_a
88 .into_iter()
89 .chain(records_b)
90 .unique_by(|e| e.primary_key().bytes().into_owned())
91 .filter_map(|record| {
92 let id = EntityId::from_entity(record.as_ref())?;
93 (!deleted_records.contains(&id)).then_some(record.into_owned())
94 })
95 .collect()
96 }
97}
98
99impl UniqueArc<Transaction> {
100 pub async fn commit(self) -> Result<(), CryptoKeystoreError> {
103 let Transaction {
104 cache,
105 deleted,
106 database,
107 _semaphore_guard,
108 } = UniqueArc::into_inner(self).await;
109 let cache = cache.into_inner();
110 let deleted_ids = deleted.into_inner();
111
112 if cache.is_empty() && deleted_ids.is_empty() {
113 log::debug!("Empty transaction was committed.");
114 return Ok(());
115 }
116
117 *database.transaction.lock().await = None;
119
120 let mut conn = database.conn().await;
124 let tx = conn.transaction()?;
125
126 for entity in cache.values() {
127 entity.execute_save(&tx)?;
128 }
129
130 for deleted_id in deleted_ids.iter() {
131 deleted_id.execute_delete(&tx)?;
132 }
133
134 tx.commit()?;
136
137 Ok(())
138 }
139}