@@ -43,7 +43,10 @@ use ruma::{
43
43
OwnedEventId , RoomId ,
44
44
} ;
45
45
use rusqlite:: { params_from_iter, OptionalExtension , ToSql , Transaction , TransactionBehavior } ;
46
- use tokio:: fs;
46
+ use tokio:: {
47
+ fs,
48
+ sync:: { Mutex , OwnedMutexGuard } ,
49
+ } ;
47
50
use tracing:: { debug, error, trace} ;
48
51
49
52
use crate :: {
@@ -86,7 +89,16 @@ const CHUNK_TYPE_GAP_TYPE_STRING: &str = "G";
86
89
#[ derive( Clone ) ]
87
90
pub struct SqliteEventCacheStore {
88
91
store_cipher : Option < Arc < StoreCipher > > ,
92
+
93
+ /// The pool of connections.
89
94
pool : SqlitePool ,
95
+
96
+ /// We make the difference between connections for read operations, and for
97
+ /// write operations. We keep a single connection apart from write
98
+ /// operations. All other connections are used for read operations. The
99
+ /// lock is used to ensure there is one owner at a time.
100
+ write_connection : Arc < Mutex < SqliteAsyncConn > > ,
101
+
90
102
media_service : MediaService ,
91
103
}
92
104
@@ -125,7 +137,7 @@ impl SqliteEventCacheStore {
125
137
let pool = config. create_pool ( Runtime :: Tokio1 ) ?;
126
138
127
139
let this = Self :: open_with_pool ( pool, passphrase. as_deref ( ) ) . await ?;
128
- this. pool . get ( ) . await ?. apply_runtime_config ( runtime_config) . await ?;
140
+ this. write ( ) . await ?. apply_runtime_config ( runtime_config) . await ?;
129
141
130
142
Ok ( this)
131
143
}
@@ -151,10 +163,17 @@ impl SqliteEventCacheStore {
151
163
let last_media_cleanup_time = conn. get_serialized_kv ( keys:: LAST_MEDIA_CLEANUP_TIME ) . await ?;
152
164
media_service. restore ( media_retention_policy, last_media_cleanup_time) ;
153
165
154
- Ok ( Self { store_cipher, pool, media_service } )
166
+ Ok ( Self {
167
+ store_cipher,
168
+ pool,
169
+ // Use `conn` as our selected write connections.
170
+ write_connection : Arc :: new ( Mutex :: new ( conn) ) ,
171
+ media_service,
172
+ } )
155
173
}
156
174
157
- async fn acquire ( & self ) -> Result < SqliteAsyncConn > {
175
+ // Acquire a connection for executing read operations.
176
+ async fn read ( & self ) -> Result < SqliteAsyncConn > {
158
177
let connection = self . pool . get ( ) . await ?;
159
178
160
179
// Per https://www.sqlite.org/foreignkeys.html#fk_enable, foreign key
@@ -166,6 +185,19 @@ impl SqliteEventCacheStore {
166
185
Ok ( connection)
167
186
}
168
187
188
+ // Acquire a connection for executing write operations.
189
+ async fn write ( & self ) -> Result < OwnedMutexGuard < SqliteAsyncConn > > {
190
+ let connection = self . write_connection . clone ( ) . lock_owned ( ) . await ;
191
+
192
+ // Per https://www.sqlite.org/foreignkeys.html#fk_enable, foreign key
193
+ // support must be enabled on a per-connection basis. Execute it every
194
+ // time we try to get a connection, since we can't guarantee a previous
195
+ // connection did enable it before.
196
+ connection. execute_batch ( "PRAGMA foreign_keys = ON;" ) . await ?;
197
+
198
+ Ok ( connection)
199
+ }
200
+
169
201
fn map_row_to_chunk (
170
202
row : & rusqlite:: Row < ' _ > ,
171
203
) -> Result < ( u64 , Option < u64 > , Option < u64 > , String ) , rusqlite:: Error > {
@@ -425,7 +457,7 @@ impl EventCacheStore for SqliteEventCacheStore {
425
457
let expiration = now + lease_duration_ms as u64 ;
426
458
427
459
let num_touched = self
428
- . acquire ( )
460
+ . write ( )
429
461
. await ?
430
462
. with_transaction ( move |txn| {
431
463
txn. execute (
@@ -457,7 +489,7 @@ impl EventCacheStore for SqliteEventCacheStore {
457
489
let linked_chunk_id = linked_chunk_id. to_owned ( ) ;
458
490
let this = self . clone ( ) ;
459
491
460
- with_immediate_transaction ( self . acquire ( ) . await ? , move |txn| {
492
+ with_immediate_transaction ( self , move |txn| {
461
493
for up in updates {
462
494
match up {
463
495
Update :: NewItemsChunk { previous, new, next } => {
@@ -783,7 +815,7 @@ impl EventCacheStore for SqliteEventCacheStore {
783
815
let this = self . clone ( ) ;
784
816
785
817
let result = self
786
- . acquire ( )
818
+ . read ( )
787
819
. await ?
788
820
. with_transaction ( move |txn| -> Result < _ > {
789
821
let mut items = Vec :: new ( ) ;
@@ -821,7 +853,7 @@ impl EventCacheStore for SqliteEventCacheStore {
821
853
let hashed_linked_chunk_id =
822
854
self . encode_key ( keys:: LINKED_CHUNKS , linked_chunk_id. storage_key ( ) ) ;
823
855
824
- self . acquire ( )
856
+ self . read ( )
825
857
. await ?
826
858
. with_transaction ( move |txn| -> Result < _ > {
827
859
// I'm not a DB analyst, so for my own future sanity: this query joins the
@@ -884,7 +916,7 @@ impl EventCacheStore for SqliteEventCacheStore {
884
916
let this = self . clone ( ) ;
885
917
886
918
self
887
- . acquire ( )
919
+ . read ( )
888
920
. await ?
889
921
. with_transaction ( move |txn| -> Result < _ > {
890
922
// Find the latest chunk identifier to generate a `ChunkIdentifierGenerator`, and count the number of chunks.
@@ -977,7 +1009,7 @@ impl EventCacheStore for SqliteEventCacheStore {
977
1009
let this = self . clone ( ) ;
978
1010
979
1011
self
980
- . acquire ( )
1012
+ . read ( )
981
1013
. await ?
982
1014
. with_transaction ( move |txn| -> Result < _ > {
983
1015
// Find the chunk before the chunk identified by `before_chunk_identifier`.
@@ -1018,7 +1050,7 @@ impl EventCacheStore for SqliteEventCacheStore {
1018
1050
}
1019
1051
1020
1052
async fn clear_all_linked_chunks ( & self ) -> Result < ( ) , Self :: Error > {
1021
- self . acquire ( )
1053
+ self . write ( )
1022
1054
. await ?
1023
1055
. with_transaction ( move |txn| {
1024
1056
// Remove all the chunks, and let cascading do its job.
@@ -1047,7 +1079,7 @@ impl EventCacheStore for SqliteEventCacheStore {
1047
1079
self . encode_key ( keys:: LINKED_CHUNKS , linked_chunk_id. storage_key ( ) ) ;
1048
1080
let linked_chunk_id = linked_chunk_id. to_owned ( ) ;
1049
1081
1050
- self . acquire ( )
1082
+ self . read ( )
1051
1083
. await ?
1052
1084
. with_transaction ( move |txn| -> Result < _ > {
1053
1085
txn. chunk_large_query_over ( events, None , move |txn, events| {
@@ -1119,7 +1151,7 @@ impl EventCacheStore for SqliteEventCacheStore {
1119
1151
1120
1152
let hashed_room_id = self . encode_key ( keys:: LINKED_CHUNKS , room_id) ;
1121
1153
1122
- self . acquire ( )
1154
+ self . read ( )
1123
1155
. await ?
1124
1156
. with_transaction ( move |txn| -> Result < _ > {
1125
1157
let Some ( event) = txn
@@ -1153,7 +1185,7 @@ impl EventCacheStore for SqliteEventCacheStore {
1153
1185
let filters = filters. map ( ToOwned :: to_owned) ;
1154
1186
let store = self . clone ( ) ;
1155
1187
1156
- self . acquire ( )
1188
+ self . read ( )
1157
1189
. await ?
1158
1190
. with_transaction ( move |txn| -> Result < _ > {
1159
1191
find_event_relations_transaction (
@@ -1178,7 +1210,7 @@ impl EventCacheStore for SqliteEventCacheStore {
1178
1210
let event_id = event_id. to_string ( ) ;
1179
1211
let encoded_event = self . encode_event ( & event) ?;
1180
1212
1181
- self . acquire ( )
1213
+ self . write ( )
1182
1214
. await ?
1183
1215
. with_transaction ( move |txn| -> Result < _ > {
1184
1216
txn. execute (
@@ -1210,7 +1242,7 @@ impl EventCacheStore for SqliteEventCacheStore {
1210
1242
let new_uri = self . encode_key ( keys:: MEDIA , to. source . unique_key ( ) ) ;
1211
1243
let new_format = self . encode_key ( keys:: MEDIA , to. format . unique_key ( ) ) ;
1212
1244
1213
- let conn = self . acquire ( ) . await ?;
1245
+ let conn = self . write ( ) . await ?;
1214
1246
conn. execute (
1215
1247
r#"UPDATE media SET uri = ?, format = ? WHERE uri = ? AND format = ?"# ,
1216
1248
( new_uri, new_format, prev_uri, prev_format) ,
@@ -1228,7 +1260,7 @@ impl EventCacheStore for SqliteEventCacheStore {
1228
1260
let uri = self . encode_key ( keys:: MEDIA , request. source . unique_key ( ) ) ;
1229
1261
let format = self . encode_key ( keys:: MEDIA , request. format . unique_key ( ) ) ;
1230
1262
1231
- let conn = self . acquire ( ) . await ?;
1263
+ let conn = self . write ( ) . await ?;
1232
1264
conn. execute ( "DELETE FROM media WHERE uri = ? AND format = ?" , ( uri, format) ) . await ?;
1233
1265
1234
1266
Ok ( ( ) )
@@ -1244,7 +1276,7 @@ impl EventCacheStore for SqliteEventCacheStore {
1244
1276
async fn remove_media_content_for_uri ( & self , uri : & MxcUri ) -> Result < ( ) > {
1245
1277
let uri = self . encode_key ( keys:: MEDIA , uri) ;
1246
1278
1247
- let conn = self . acquire ( ) . await ?;
1279
+ let conn = self . write ( ) . await ?;
1248
1280
conn. execute ( "DELETE FROM media WHERE uri = ?" , ( uri, ) ) . await ?;
1249
1281
1250
1282
Ok ( ( ) )
@@ -1282,15 +1314,15 @@ impl EventCacheStoreMedia for SqliteEventCacheStore {
1282
1314
async fn media_retention_policy_inner (
1283
1315
& self ,
1284
1316
) -> Result < Option < MediaRetentionPolicy > , Self :: Error > {
1285
- let conn = self . acquire ( ) . await ?;
1317
+ let conn = self . read ( ) . await ?;
1286
1318
conn. get_serialized_kv ( keys:: MEDIA_RETENTION_POLICY ) . await
1287
1319
}
1288
1320
1289
1321
async fn set_media_retention_policy_inner (
1290
1322
& self ,
1291
1323
policy : MediaRetentionPolicy ,
1292
1324
) -> Result < ( ) , Self :: Error > {
1293
- let conn = self . acquire ( ) . await ?;
1325
+ let conn = self . write ( ) . await ?;
1294
1326
conn. set_serialized_kv ( keys:: MEDIA_RETENTION_POLICY , policy) . await ?;
1295
1327
Ok ( ( ) )
1296
1328
}
@@ -1314,7 +1346,7 @@ impl EventCacheStoreMedia for SqliteEventCacheStore {
1314
1346
let format = self . encode_key ( keys:: MEDIA , request. format . unique_key ( ) ) ;
1315
1347
let timestamp = time_to_timestamp ( last_access) ;
1316
1348
1317
- let conn = self . acquire ( ) . await ?;
1349
+ let conn = self . write ( ) . await ?;
1318
1350
conn. execute (
1319
1351
"INSERT OR REPLACE INTO media (uri, format, data, last_access, ignore_policy) VALUES (?, ?, ?, ?, ?)" ,
1320
1352
( uri, format, data, timestamp, ignore_policy) ,
@@ -1333,7 +1365,7 @@ impl EventCacheStoreMedia for SqliteEventCacheStore {
1333
1365
let format = self . encode_key ( keys:: MEDIA , request. format . unique_key ( ) ) ;
1334
1366
let ignore_policy = ignore_policy. is_yes ( ) ;
1335
1367
1336
- let conn = self . acquire ( ) . await ?;
1368
+ let conn = self . write ( ) . await ?;
1337
1369
conn. execute (
1338
1370
r#"UPDATE media SET ignore_policy = ? WHERE uri = ? AND format = ?"# ,
1339
1371
( ignore_policy, uri, format) ,
@@ -1352,7 +1384,7 @@ impl EventCacheStoreMedia for SqliteEventCacheStore {
1352
1384
let format = self . encode_key ( keys:: MEDIA , request. format . unique_key ( ) ) ;
1353
1385
let timestamp = time_to_timestamp ( current_time) ;
1354
1386
1355
- let conn = self . acquire ( ) . await ?;
1387
+ let conn = self . write ( ) . await ?;
1356
1388
let data = conn
1357
1389
. with_transaction :: < _ , rusqlite:: Error , _ > ( move |txn| {
1358
1390
// Update the last access.
@@ -1383,7 +1415,7 @@ impl EventCacheStoreMedia for SqliteEventCacheStore {
1383
1415
let uri = self . encode_key ( keys:: MEDIA , uri) ;
1384
1416
let timestamp = time_to_timestamp ( current_time) ;
1385
1417
1386
- let conn = self . acquire ( ) . await ?;
1418
+ let conn = self . write ( ) . await ?;
1387
1419
let data = conn
1388
1420
. with_transaction :: < _ , rusqlite:: Error , _ > ( move |txn| {
1389
1421
// Update the last access.
@@ -1413,7 +1445,7 @@ impl EventCacheStoreMedia for SqliteEventCacheStore {
1413
1445
return Ok ( ( ) ) ;
1414
1446
}
1415
1447
1416
- let conn = self . acquire ( ) . await ?;
1448
+ let conn = self . write ( ) . await ?;
1417
1449
let removed = conn
1418
1450
. with_transaction :: < _ , Error , _ > ( move |txn| {
1419
1451
let mut removed = false ;
@@ -1532,7 +1564,7 @@ impl EventCacheStoreMedia for SqliteEventCacheStore {
1532
1564
}
1533
1565
1534
1566
async fn last_media_cleanup_time_inner ( & self ) -> Result < Option < SystemTime > , Self :: Error > {
1535
- let conn = self . acquire ( ) . await ?;
1567
+ let conn = self . read ( ) . await ?;
1536
1568
conn. get_serialized_kv ( keys:: LAST_MEDIA_CLEANUP_TIME ) . await
1537
1569
}
1538
1570
}
@@ -1630,33 +1662,35 @@ async fn with_immediate_transaction<
1630
1662
T : Send + ' static ,
1631
1663
F : FnOnce ( & Transaction < ' _ > ) -> Result < T , Error > + Send + ' static ,
1632
1664
> (
1633
- conn : SqliteAsyncConn ,
1665
+ this : & SqliteEventCacheStore ,
1634
1666
f : F ,
1635
1667
) -> Result < T , Error > {
1636
- conn. interact ( move |conn| -> Result < T , Error > {
1637
- // Start the transaction in IMMEDIATE mode since all updates may cause writes,
1638
- // to avoid read transactions upgrading to write mode and causing
1639
- // SQLITE_BUSY errors. See also: https://www.sqlite.org/lang_transaction.html#deferred_immediate_and_exclusive_transactions
1640
- conn. set_transaction_behavior ( TransactionBehavior :: Immediate ) ;
1641
-
1642
- let code = || -> Result < T , Error > {
1643
- let txn = conn. transaction ( ) ?;
1644
- let res = f ( & txn) ?;
1645
- txn. commit ( ) ?;
1646
- Ok ( res)
1647
- } ;
1648
-
1649
- let res = code ( ) ;
1650
-
1651
- // Reset the transaction behavior to use Deferred, after this transaction has
1652
- // been run, whether it was successful or not.
1653
- conn. set_transaction_behavior ( TransactionBehavior :: Deferred ) ;
1654
-
1655
- res
1656
- } )
1657
- . await
1658
- // SAFETY: same logic as in [`deadpool::managed::Object::with_transaction`].`
1659
- . unwrap ( )
1668
+ this. write ( )
1669
+ . await ?
1670
+ . interact ( move |conn| -> Result < T , Error > {
1671
+ // Start the transaction in IMMEDIATE mode since all updates may cause writes,
1672
+ // to avoid read transactions upgrading to write mode and causing
1673
+ // SQLITE_BUSY errors. See also: https://www.sqlite.org/lang_transaction.html#deferred_immediate_and_exclusive_transactions
1674
+ conn. set_transaction_behavior ( TransactionBehavior :: Immediate ) ;
1675
+
1676
+ let code = || -> Result < T , Error > {
1677
+ let txn = conn. transaction ( ) ?;
1678
+ let res = f ( & txn) ?;
1679
+ txn. commit ( ) ?;
1680
+ Ok ( res)
1681
+ } ;
1682
+
1683
+ let res = code ( ) ;
1684
+
1685
+ // Reset the transaction behavior to use Deferred, after this transaction has
1686
+ // been run, whether it was successful or not.
1687
+ conn. set_transaction_behavior ( TransactionBehavior :: Deferred ) ;
1688
+
1689
+ res
1690
+ } )
1691
+ . await
1692
+ // SAFETY: same logic as in [`deadpool::managed::Object::with_transaction`].`
1693
+ . unwrap ( )
1660
1694
}
1661
1695
1662
1696
fn insert_chunk (
@@ -1763,7 +1797,7 @@ mod tests {
1763
1797
async fn get_event_cache_store_content_sorted_by_last_access (
1764
1798
event_cache_store : & SqliteEventCacheStore ,
1765
1799
) -> Vec < Vec < u8 > > {
1766
- let sqlite_db = event_cache_store. acquire ( ) . await . expect ( "accessing sqlite db failed" ) ;
1800
+ let sqlite_db = event_cache_store. read ( ) . await . expect ( "accessing sqlite db failed" ) ;
1767
1801
sqlite_db
1768
1802
. prepare ( "SELECT data FROM media ORDER BY last_access DESC" , |mut stmt| {
1769
1803
stmt. query ( ( ) ) ?. mapped ( |row| row. get ( 0 ) ) . collect ( )
@@ -2053,7 +2087,7 @@ mod tests {
2053
2087
2054
2088
// Check that cascading worked. Yes, SQLite, I doubt you.
2055
2089
let gaps = store
2056
- . acquire ( )
2090
+ . read ( )
2057
2091
. await
2058
2092
. unwrap ( )
2059
2093
. with_transaction ( |txn| -> rusqlite:: Result < _ > {
@@ -2175,7 +2209,7 @@ mod tests {
2175
2209
2176
2210
// Make sure the position have been updated for the remaining events.
2177
2211
let num_rows: u64 = store
2178
- . acquire ( )
2212
+ . read ( )
2179
2213
. await
2180
2214
. unwrap ( )
2181
2215
. with_transaction ( move |txn| {
@@ -2324,7 +2358,7 @@ mod tests {
2324
2358
2325
2359
// Check that cascading worked. Yes, SQLite, I doubt you.
2326
2360
store
2327
- . acquire ( )
2361
+ . read ( )
2328
2362
. await
2329
2363
. unwrap ( )
2330
2364
. with_transaction ( |txn| -> rusqlite:: Result < _ > {
0 commit comments