xmtp_db/sql_key_store/
transactions.rs1use super::*;
2use crate::DbConnection;
3use crate::TransactionOutcome;
4use crate::TransactionOutcome::{Continue, Rollback};
5
6pub struct MutableTransactionConnection<'a> {
10 pub(crate) conn: parking_lot::Mutex<&'a mut SqliteConnection>,
15}
16
17impl<'a> MutableTransactionConnection<'a> {
18 pub fn new(conn: &'a mut SqliteConnection) -> Self {
19 Self {
20 conn: parking_lot::Mutex::new(conn),
21 }
22 }
23}
24
25impl<'a> ConnectionExt for MutableTransactionConnection<'a> {
26 fn raw_query<T, F>(&self, fun: F) -> Result<T, crate::ConnectionError>
27 where
28 F: FnOnce(&mut SqliteConnection) -> Result<T, diesel::result::Error>,
29 Self: Sized,
30 {
31 let mut conn = self.conn.try_lock().expect("Lock is held somewhere else");
32 fun(&mut conn).map_err(crate::ConnectionError::from)
33 }
34
35 fn disconnect(&self) -> Result<(), crate::ConnectionError> {
37 Err(crate::ConnectionError::DisconnectInTransaction)
38 }
39
40 fn reconnect(&self) -> Result<(), crate::ConnectionError> {
41 Err(crate::ConnectionError::ReconnectInTransaction)
42 }
43}
44
45impl<C: ConnectionExt> XmtpMlsStorageProvider for SqlKeyStore<C> {
46 type Connection = C;
47
48 type TxQuery = SqliteConnection;
49
50 type DbQuery<'a>
51 = DbConnection<&'a C>
52 where
53 Self::Connection: 'a;
54
55 fn db<'a>(&'a self) -> Self::DbQuery<'a> {
56 DbConnection::new(&self.conn)
57 }
58
59 #[xmtp_common::db_span]
60 fn transaction<T, E, F>(&self, f: F) -> Result<TransactionOutcome<T>, E>
61 where
62 F: FnOnce(&mut Self::TxQuery) -> Result<TransactionOutcome<T>, E>,
63 E: From<diesel::result::Error> + From<crate::ConnectionError> + std::error::Error,
64 {
65 let conn = &self.conn;
66
67 let mut rolled_back = false;
87 let inner_result: Result<TransactionOutcome<T>, E> = conn
88 .raw_query(|c| {
89 Ok(c.immediate_transaction(|sqlite_c| match f(sqlite_c) {
90 Ok(Continue(v)) => Ok(Continue(v)),
91 Ok(Rollback) => {
92 rolled_back = true;
93 Err(E::from(diesel::result::Error::RollbackTransaction))
94 }
95 Err(e) => Err(e),
96 }))
97 })
98 .map_err(E::from)?;
99
100 match inner_result {
103 Ok(outcome) => Ok(outcome),
104 Err(_) if rolled_back => Ok(Rollback),
105 Err(e) => Err(e),
106 }
107 }
108
109 fn savepoint<T, E, F>(&self, f: F) -> Result<TransactionOutcome<T>, E>
111 where
112 F: FnOnce(&mut Self::TxQuery) -> Result<TransactionOutcome<T>, E>,
113 E: From<diesel::result::Error> + From<crate::ConnectionError> + std::error::Error,
114 {
115 let mut rolled_back = false;
116 let inner_result: Result<TransactionOutcome<T>, E> = self
117 .conn
118 .raw_query(|c| {
119 Ok(c.transaction(|sqlite_c| match f(sqlite_c) {
120 Ok(Continue(v)) => Ok(Continue(v)),
121 Ok(Rollback) => {
122 rolled_back = true;
123 Err(E::from(diesel::result::Error::RollbackTransaction))
124 }
125 Err(e) => Err(e),
126 }))
127 })
128 .map_err(E::from)?;
129
130 match inner_result {
131 Ok(outcome) => Ok(outcome),
132 Err(_) if rolled_back => Ok(Rollback),
133 Err(e) => Err(e),
134 }
135 }
136
137 fn read<V: Entity<CURRENT_VERSION>>(
138 &self,
139 label: &[u8],
140 key: &[u8],
141 ) -> Result<Option<V>, SqlKeyStoreError> {
142 self.read(label, key)
143 }
144
145 fn read_list<V: Entity<CURRENT_VERSION>>(
146 &self,
147 label: &[u8],
148 key: &[u8],
149 ) -> Result<Vec<V>, <Self as StorageProvider<CURRENT_VERSION>>::Error> {
150 self.read_list(label, key)
151 }
152
153 fn delete(
154 &self,
155 label: &[u8],
156 key: &[u8],
157 ) -> Result<(), <Self as StorageProvider<CURRENT_VERSION>>::Error> {
158 self.delete::<CURRENT_VERSION>(label, key)
159 }
160
161 fn write(
162 &self,
163 label: &[u8],
164 key: &[u8],
165 value: &[u8],
166 ) -> Result<(), <Self as StorageProvider<CURRENT_VERSION>>::Error> {
167 self.write::<CURRENT_VERSION>(label, key, value)
168 }
169
170 #[cfg(feature = "test-utils")]
171 fn hash_all(&self) -> Result<Vec<u8>, SqlKeyStoreError> {
172 self.conn
173 .raw_query(OpenMlsKeyValue::hash_all)
174 .map_err(Into::into)
175 }
176}
177
178#[cfg(test)]
179mod tests {
180
181 #![allow(unused)]
182
183 use crate::{
184 TestDb, TransactionOutcome, XmtpTestDb,
185 group_intent::{IntentKind, IntentState, NewGroupIntent},
186 prelude::QueryGroupIntent,
187 };
188 use xmtp_proto::types::GroupId;
189
190 use super::*;
191
192 struct Foo<C> {
198 key_store: SqlKeyStore<C>,
199 }
200
201 impl<C> Foo<C>
202 where
203 C: ConnectionExt,
204 {
205 async fn long_async_call(&self) {
206 xmtp_common::time::sleep(std::time::Duration::from_millis(10)).await;
207 }
208
209 async fn db_op(&self) {
210 self.long_async_call().await;
211
212 self.key_store
213 .transaction(|conn| {
214 let storage = conn.key_store();
215 storage
216 .db()
217 .insert_group_intent(NewGroupIntent {
218 kind: IntentKind::SendMessage,
219 group_id: GroupId::default(),
220 data: vec![],
221 should_push: false,
222 state: IntentState::ToPublish,
223 })
224 .map(Continue)
225 })
226 .unwrap();
227 self.long_async_call().await;
228 }
229 }
230}