@@ -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 this = self . clone ( ) ;
1155
1187
1156
- self . acquire ( )
1188
+ self . read ( )
1157
1189
. await ?
1158
1190
. with_transaction ( move |txn| -> Result < _ > {
1159
1191
let filter_query = if let Some ( filters) = compute_filters_string ( filters. as_deref ( ) )
@@ -1216,7 +1248,7 @@ impl EventCacheStore for SqliteEventCacheStore {
1216
1248
let event_id = event_id. to_string ( ) ;
1217
1249
let encoded_event = self . encode_event ( & event) ?;
1218
1250
1219
- self . acquire ( )
1251
+ self . write ( )
1220
1252
. await ?
1221
1253
. with_transaction ( move |txn| -> Result < _ > {
1222
1254
txn. execute (
@@ -1248,7 +1280,7 @@ impl EventCacheStore for SqliteEventCacheStore {
1248
1280
let new_uri = self . encode_key ( keys:: MEDIA , to. source . unique_key ( ) ) ;
1249
1281
let new_format = self . encode_key ( keys:: MEDIA , to. format . unique_key ( ) ) ;
1250
1282
1251
- let conn = self . acquire ( ) . await ?;
1283
+ let conn = self . write ( ) . await ?;
1252
1284
conn. execute (
1253
1285
r#"UPDATE media SET uri = ?, format = ? WHERE uri = ? AND format = ?"# ,
1254
1286
( new_uri, new_format, prev_uri, prev_format) ,
@@ -1266,7 +1298,7 @@ impl EventCacheStore for SqliteEventCacheStore {
1266
1298
let uri = self . encode_key ( keys:: MEDIA , request. source . unique_key ( ) ) ;
1267
1299
let format = self . encode_key ( keys:: MEDIA , request. format . unique_key ( ) ) ;
1268
1300
1269
- let conn = self . acquire ( ) . await ?;
1301
+ let conn = self . write ( ) . await ?;
1270
1302
conn. execute ( "DELETE FROM media WHERE uri = ? AND format = ?" , ( uri, format) ) . await ?;
1271
1303
1272
1304
Ok ( ( ) )
@@ -1282,7 +1314,7 @@ impl EventCacheStore for SqliteEventCacheStore {
1282
1314
async fn remove_media_content_for_uri ( & self , uri : & MxcUri ) -> Result < ( ) > {
1283
1315
let uri = self . encode_key ( keys:: MEDIA , uri) ;
1284
1316
1285
- let conn = self . acquire ( ) . await ?;
1317
+ let conn = self . write ( ) . await ?;
1286
1318
conn. execute ( "DELETE FROM media WHERE uri = ?" , ( uri, ) ) . await ?;
1287
1319
1288
1320
Ok ( ( ) )
@@ -1320,15 +1352,15 @@ impl EventCacheStoreMedia for SqliteEventCacheStore {
1320
1352
async fn media_retention_policy_inner (
1321
1353
& self ,
1322
1354
) -> Result < Option < MediaRetentionPolicy > , Self :: Error > {
1323
- let conn = self . acquire ( ) . await ?;
1355
+ let conn = self . read ( ) . await ?;
1324
1356
conn. get_serialized_kv ( keys:: MEDIA_RETENTION_POLICY ) . await
1325
1357
}
1326
1358
1327
1359
async fn set_media_retention_policy_inner (
1328
1360
& self ,
1329
1361
policy : MediaRetentionPolicy ,
1330
1362
) -> Result < ( ) , Self :: Error > {
1331
- let conn = self . acquire ( ) . await ?;
1363
+ let conn = self . write ( ) . await ?;
1332
1364
conn. set_serialized_kv ( keys:: MEDIA_RETENTION_POLICY , policy) . await ?;
1333
1365
Ok ( ( ) )
1334
1366
}
@@ -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 ( last_access) ;
1354
1386
1355
- let conn = self . acquire ( ) . await ?;
1387
+ let conn = self . write ( ) . await ?;
1356
1388
conn. execute (
1357
1389
"INSERT OR REPLACE INTO media (uri, format, data, last_access, ignore_policy) VALUES (?, ?, ?, ?, ?)" ,
1358
1390
( uri, format, data, timestamp, ignore_policy) ,
@@ -1371,7 +1403,7 @@ impl EventCacheStoreMedia for SqliteEventCacheStore {
1371
1403
let format = self . encode_key ( keys:: MEDIA , request. format . unique_key ( ) ) ;
1372
1404
let ignore_policy = ignore_policy. is_yes ( ) ;
1373
1405
1374
- let conn = self . acquire ( ) . await ?;
1406
+ let conn = self . write ( ) . await ?;
1375
1407
conn. execute (
1376
1408
r#"UPDATE media SET ignore_policy = ? WHERE uri = ? AND format = ?"# ,
1377
1409
( ignore_policy, uri, format) ,
@@ -1390,7 +1422,7 @@ impl EventCacheStoreMedia for SqliteEventCacheStore {
1390
1422
let format = self . encode_key ( keys:: MEDIA , request. format . unique_key ( ) ) ;
1391
1423
let timestamp = time_to_timestamp ( current_time) ;
1392
1424
1393
- let conn = self . acquire ( ) . await ?;
1425
+ let conn = self . write ( ) . await ?;
1394
1426
let data = conn
1395
1427
. with_transaction :: < _ , rusqlite:: Error , _ > ( move |txn| {
1396
1428
// Update the last access.
@@ -1421,7 +1453,7 @@ impl EventCacheStoreMedia for SqliteEventCacheStore {
1421
1453
let uri = self . encode_key ( keys:: MEDIA , uri) ;
1422
1454
let timestamp = time_to_timestamp ( current_time) ;
1423
1455
1424
- let conn = self . acquire ( ) . await ?;
1456
+ let conn = self . write ( ) . await ?;
1425
1457
let data = conn
1426
1458
. with_transaction :: < _ , rusqlite:: Error , _ > ( move |txn| {
1427
1459
// Update the last access.
@@ -1451,7 +1483,7 @@ impl EventCacheStoreMedia for SqliteEventCacheStore {
1451
1483
return Ok ( ( ) ) ;
1452
1484
}
1453
1485
1454
- let conn = self . acquire ( ) . await ?;
1486
+ let conn = self . write ( ) . await ?;
1455
1487
let removed = conn
1456
1488
. with_transaction :: < _ , Error , _ > ( move |txn| {
1457
1489
let mut removed = false ;
@@ -1570,7 +1602,7 @@ impl EventCacheStoreMedia for SqliteEventCacheStore {
1570
1602
}
1571
1603
1572
1604
async fn last_media_cleanup_time_inner ( & self ) -> Result < Option < SystemTime > , Self :: Error > {
1573
- let conn = self . acquire ( ) . await ?;
1605
+ let conn = self . read ( ) . await ?;
1574
1606
conn. get_serialized_kv ( keys:: LAST_MEDIA_CLEANUP_TIME ) . await
1575
1607
}
1576
1608
}
@@ -1583,33 +1615,35 @@ async fn with_immediate_transaction<
1583
1615
T : Send + ' static ,
1584
1616
F : FnOnce ( & Transaction < ' _ > ) -> Result < T , Error > + Send + ' static ,
1585
1617
> (
1586
- conn : SqliteAsyncConn ,
1618
+ this : & SqliteEventCacheStore ,
1587
1619
f : F ,
1588
1620
) -> Result < T , Error > {
1589
- conn. interact ( move |conn| -> Result < T , Error > {
1590
- // Start the transaction in IMMEDIATE mode since all updates may cause writes,
1591
- // to avoid read transactions upgrading to write mode and causing
1592
- // SQLITE_BUSY errors. See also: https://www.sqlite.org/lang_transaction.html#deferred_immediate_and_exclusive_transactions
1593
- conn. set_transaction_behavior ( TransactionBehavior :: Immediate ) ;
1594
-
1595
- let code = || -> Result < T , Error > {
1596
- let txn = conn. transaction ( ) ?;
1597
- let res = f ( & txn) ?;
1598
- txn. commit ( ) ?;
1599
- Ok ( res)
1600
- } ;
1601
-
1602
- let res = code ( ) ;
1603
-
1604
- // Reset the transaction behavior to use Deferred, after this transaction has
1605
- // been run, whether it was successful or not.
1606
- conn. set_transaction_behavior ( TransactionBehavior :: Deferred ) ;
1607
-
1608
- res
1609
- } )
1610
- . await
1611
- // SAFETY: same logic as in [`deadpool::managed::Object::with_transaction`].`
1612
- . unwrap ( )
1621
+ this. write ( )
1622
+ . await ?
1623
+ . interact ( move |conn| -> Result < T , Error > {
1624
+ // Start the transaction in IMMEDIATE mode since all updates may cause writes,
1625
+ // to avoid read transactions upgrading to write mode and causing
1626
+ // SQLITE_BUSY errors. See also: https://www.sqlite.org/lang_transaction.html#deferred_immediate_and_exclusive_transactions
1627
+ conn. set_transaction_behavior ( TransactionBehavior :: Immediate ) ;
1628
+
1629
+ let code = || -> Result < T , Error > {
1630
+ let txn = conn. transaction ( ) ?;
1631
+ let res = f ( & txn) ?;
1632
+ txn. commit ( ) ?;
1633
+ Ok ( res)
1634
+ } ;
1635
+
1636
+ let res = code ( ) ;
1637
+
1638
+ // Reset the transaction behavior to use Deferred, after this transaction has
1639
+ // been run, whether it was successful or not.
1640
+ conn. set_transaction_behavior ( TransactionBehavior :: Deferred ) ;
1641
+
1642
+ res
1643
+ } )
1644
+ . await
1645
+ // SAFETY: same logic as in [`deadpool::managed::Object::with_transaction`].`
1646
+ . unwrap ( )
1613
1647
}
1614
1648
1615
1649
fn insert_chunk (
@@ -1716,7 +1750,7 @@ mod tests {
1716
1750
async fn get_event_cache_store_content_sorted_by_last_access (
1717
1751
event_cache_store : & SqliteEventCacheStore ,
1718
1752
) -> Vec < Vec < u8 > > {
1719
- let sqlite_db = event_cache_store. acquire ( ) . await . expect ( "accessing sqlite db failed" ) ;
1753
+ let sqlite_db = event_cache_store. read ( ) . await . expect ( "accessing sqlite db failed" ) ;
1720
1754
sqlite_db
1721
1755
. prepare ( "SELECT data FROM media ORDER BY last_access DESC" , |mut stmt| {
1722
1756
stmt. query ( ( ) ) ?. mapped ( |row| row. get ( 0 ) ) . collect ( )
@@ -2006,7 +2040,7 @@ mod tests {
2006
2040
2007
2041
// Check that cascading worked. Yes, SQLite, I doubt you.
2008
2042
let gaps = store
2009
- . acquire ( )
2043
+ . read ( )
2010
2044
. await
2011
2045
. unwrap ( )
2012
2046
. with_transaction ( |txn| -> rusqlite:: Result < _ > {
@@ -2128,7 +2162,7 @@ mod tests {
2128
2162
2129
2163
// Make sure the position have been updated for the remaining events.
2130
2164
let num_rows: u64 = store
2131
- . acquire ( )
2165
+ . read ( )
2132
2166
. await
2133
2167
. unwrap ( )
2134
2168
. with_transaction ( move |txn| {
@@ -2277,7 +2311,7 @@ mod tests {
2277
2311
2278
2312
// Check that cascading worked. Yes, SQLite, I doubt you.
2279
2313
store
2280
- . acquire ( )
2314
+ . read ( )
2281
2315
. await
2282
2316
. unwrap ( )
2283
2317
. with_transaction ( |txn| -> rusqlite:: Result < _ > {
0 commit comments