Skip to content

Commit 660d4e7

Browse files
committed
feat(sqlite): Add StoreOpenConfig and open_with_config for all stores.
This patch adds a new `StoreOpenConfing` type to configure the store when opening it and when creating the pool of connections to SQLite via `deadpool_sqlite`. This patch also adds a new `open_with_config` constructor on all stores, namely `SqliteCryptoStore`, `SqliteEventCacheStore` and `SqliteStateStore`.
1 parent 404dd39 commit 660d4e7

File tree

6 files changed

+184
-34
lines changed

6 files changed

+184
-34
lines changed

Cargo.lock

Lines changed: 1 addition & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

crates/matrix-sdk-sqlite/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ itertools = { workspace = true }
2323
matrix-sdk-base = { workspace = true, optional = true }
2424
matrix-sdk-crypto = { workspace = true, optional = true }
2525
matrix-sdk-store-encryption = { workspace = true }
26+
num_cpus = "1.16.0"
2627
rmp-serde = { workspace = true }
2728
ruma = { workspace = true }
2829
rusqlite = { version = "0.32.1", features = ["limits"] }

crates/matrix-sdk-sqlite/src/crypto_store.rs

Lines changed: 38 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -51,9 +51,12 @@ use crate::{
5151
repeat_vars, Key, SqliteAsyncConnExt, SqliteKeyValueStoreAsyncConnExt,
5252
SqliteKeyValueStoreConnExt,
5353
},
54-
OpenStoreError,
54+
OpenStoreError, SqliteStoreConfig,
5555
};
5656

57+
/// The database name.
58+
const DATABASE_NAME: &str = "matrix-sdk-crypto.sqlite3";
59+
5760
/// A sqlite based cryptostore.
5861
#[derive(Clone)]
5962
pub struct SqliteCryptoStore {
@@ -79,12 +82,21 @@ impl SqliteCryptoStore {
7982
path: impl AsRef<Path>,
8083
passphrase: Option<&str>,
8184
) -> Result<Self, OpenStoreError> {
82-
let path = path.as_ref();
83-
fs::create_dir_all(path).await.map_err(OpenStoreError::CreateDir)?;
84-
let cfg = deadpool_sqlite::Config::new(path.join("matrix-sdk-crypto.sqlite3"));
85-
let pool = cfg.create_pool(Runtime::Tokio1)?;
85+
Self::open_with_config(SqliteStoreConfig::new(path).passphrase(passphrase)).await
86+
}
87+
88+
/// Open the sqlite-based crypto store with the config open config.
89+
pub async fn open_with_config(config: SqliteStoreConfig) -> Result<Self, OpenStoreError> {
90+
let SqliteStoreConfig { path, passphrase, pool_config } = config;
91+
92+
fs::create_dir_all(&path).await.map_err(OpenStoreError::CreateDir)?;
93+
94+
let mut config = deadpool_sqlite::Config::new(path.join(DATABASE_NAME));
95+
config.pool = Some(pool_config);
8696

87-
Self::open_with_pool(pool, passphrase).await
97+
let pool = config.create_pool(Runtime::Tokio1)?;
98+
99+
Self::open_with_pool(pool, passphrase.as_deref()).await
88100
}
89101

90102
/// Create a sqlite-based crypto store using the given sqlite database pool.
@@ -1421,6 +1433,7 @@ mod tests {
14211433
use tokio::fs;
14221434

14231435
use super::SqliteCryptoStore;
1436+
use crate::SqliteStoreConfig;
14241437

14251438
static TMP_DIR: Lazy<TempDir> = Lazy::new(|| tempdir().unwrap());
14261439

@@ -1431,8 +1444,8 @@ mod tests {
14311444
database: SqliteCryptoStore,
14321445
}
14331446

1434-
async fn get_test_db(data_path: &str, passphrase: Option<&str>) -> TestDb {
1435-
let db_name = "matrix-sdk-crypto.sqlite3";
1447+
fn copy_db(data_path: &str) -> TempDir {
1448+
let db_name = super::DATABASE_NAME;
14361449

14371450
let manifest_path = Path::new(env!("CARGO_MANIFEST_DIR")).join("../..");
14381451
let database_path = manifest_path.join(data_path).join(db_name);
@@ -1443,13 +1456,29 @@ mod tests {
14431456
// Copy the test database to the tempdir so our test runs are idempotent.
14441457
std::fs::copy(&database_path, destination).unwrap();
14451458

1459+
tmpdir
1460+
}
1461+
1462+
async fn get_test_db(data_path: &str, passphrase: Option<&str>) -> TestDb {
1463+
let tmpdir = copy_db(data_path);
1464+
14461465
let database = SqliteCryptoStore::open(tmpdir.path(), passphrase)
14471466
.await
14481467
.expect("Can't open the test store");
14491468

14501469
TestDb { _dir: tmpdir, database }
14511470
}
14521471

1472+
#[async_test]
1473+
async fn test_pool_size() {
1474+
let store_open_config =
1475+
SqliteStoreConfig::new(TMP_DIR.path().join("test_pool_size")).pool_max_size(42);
1476+
1477+
let store = SqliteCryptoStore::open_with_config(store_open_config).await.unwrap();
1478+
1479+
assert_eq!(store.pool.status().max_size, 42);
1480+
}
1481+
14531482
/// Test that we didn't regress in our storage layer by loading data from a
14541483
/// pre-filled database, or in other words use a test vector for this.
14551484
#[async_test]
@@ -1787,6 +1816,7 @@ mod tests {
17871816
assert_eq!(backup_keys.backup_version.unwrap(), "6");
17881817
assert!(backup_keys.decryption_key.is_some());
17891818
}
1819+
17901820
async fn get_store(
17911821
name: &str,
17921822
passphrase: Option<&str>,

crates/matrix-sdk-sqlite/src/event_cache_store.rs

Lines changed: 36 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@ use crate::{
4747
repeat_vars, time_to_timestamp, Key, SqliteAsyncConnExt, SqliteKeyValueStoreAsyncConnExt,
4848
SqliteKeyValueStoreConnExt, SqliteTransactionExt,
4949
},
50-
OpenStoreError,
50+
OpenStoreError, SqliteStoreConfig,
5151
};
5252

5353
mod keys {
@@ -60,6 +60,9 @@ mod keys {
6060
pub const MEDIA: &str = "media";
6161
}
6262

63+
/// The database name.
64+
const DATABASE_NAME: &str = "matrix-sdk-event-cache.sqlite3";
65+
6366
/// Identifier of the latest database version.
6467
///
6568
/// This is used to figure whether the SQLite database requires a migration.
@@ -96,9 +99,21 @@ impl SqliteEventCacheStore {
9699
path: impl AsRef<Path>,
97100
passphrase: Option<&str>,
98101
) -> Result<Self, OpenStoreError> {
99-
let pool = create_pool(path.as_ref()).await?;
102+
Self::open_with_config(SqliteStoreConfig::new(path).passphrase(passphrase)).await
103+
}
104+
105+
/// Open the sqlite-based event cache store with the config open config.
106+
pub async fn open_with_config(config: SqliteStoreConfig) -> Result<Self, OpenStoreError> {
107+
let SqliteStoreConfig { path, passphrase, pool_config } = config;
108+
109+
fs::create_dir_all(&path).await.map_err(OpenStoreError::CreateDir)?;
110+
111+
let mut config = deadpool_sqlite::Config::new(path.join(DATABASE_NAME));
112+
config.pool = Some(pool_config);
100113

101-
Self::open_with_pool(pool, passphrase).await
114+
let pool = config.create_pool(Runtime::Tokio1)?;
115+
116+
Self::open_with_pool(pool, passphrase.as_deref()).await
102117
}
103118

104119
/// Open an SQLite-based event cache store using the given SQLite database
@@ -294,12 +309,6 @@ impl TransactionExtForLinkedChunks for Transaction<'_> {
294309
}
295310
}
296311

297-
async fn create_pool(path: &Path) -> Result<SqlitePool, OpenStoreError> {
298-
fs::create_dir_all(path).await.map_err(OpenStoreError::CreateDir)?;
299-
let cfg = deadpool_sqlite::Config::new(path.join("matrix-sdk-event-cache.sqlite3"));
300-
Ok(cfg.create_pool(Runtime::Tokio1)?)
301-
}
302-
303312
/// Run migrations for the given version of the database.
304313
async fn run_migrations(conn: &SqliteAsyncConn, version: u8) -> Result<()> {
305314
if version == 0 {
@@ -1348,6 +1357,7 @@ fn insert_chunk(
13481357
#[cfg(test)]
13491358
mod tests {
13501359
use std::{
1360+
path::PathBuf,
13511361
sync::atomic::{AtomicU32, Ordering::SeqCst},
13521362
time::Duration,
13531363
};
@@ -1373,14 +1383,18 @@ mod tests {
13731383
use tempfile::{tempdir, TempDir};
13741384

13751385
use super::SqliteEventCacheStore;
1376-
use crate::utils::SqliteAsyncConnExt;
1386+
use crate::{utils::SqliteAsyncConnExt, SqliteStoreConfig};
13771387

13781388
static TMP_DIR: Lazy<TempDir> = Lazy::new(|| tempdir().unwrap());
13791389
static NUM: AtomicU32 = AtomicU32::new(0);
13801390

1381-
async fn get_event_cache_store() -> Result<SqliteEventCacheStore, EventCacheStoreError> {
1391+
fn new_event_cache_store_workspace() -> PathBuf {
13821392
let name = NUM.fetch_add(1, SeqCst).to_string();
1383-
let tmpdir_path = TMP_DIR.path().join(name);
1393+
TMP_DIR.path().join(name)
1394+
}
1395+
1396+
async fn get_event_cache_store() -> Result<SqliteEventCacheStore, EventCacheStoreError> {
1397+
let tmpdir_path = new_event_cache_store_workspace();
13841398

13851399
tracing::info!("using event cache store @ {}", tmpdir_path.to_str().unwrap());
13861400

@@ -1403,6 +1417,16 @@ mod tests {
14031417
.expect("querying media cache content by last access failed")
14041418
}
14051419

1420+
#[async_test]
1421+
async fn test_pool_size() {
1422+
let tmpdir_path = new_event_cache_store_workspace();
1423+
let store_open_config = SqliteStoreConfig::new(tmpdir_path).pool_max_size(42);
1424+
1425+
let store = SqliteEventCacheStore::open_with_config(store_open_config).await.unwrap();
1426+
1427+
assert_eq!(store.pool.status().max_size, 42);
1428+
}
1429+
14061430
#[async_test]
14071431
async fn test_last_access() {
14081432
let event_cache_store = get_event_cache_store().await.expect("creating media cache failed");

crates/matrix-sdk-sqlite/src/lib.rs

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,9 @@ mod event_cache_store;
2424
#[cfg(feature = "state-store")]
2525
mod state_store;
2626
mod utils;
27+
use std::path::{Path, PathBuf};
28+
29+
use deadpool_sqlite::PoolConfig;
2730

2831
#[cfg(feature = "crypto-store")]
2932
pub use self::crypto_store::SqliteCryptoStore;
@@ -35,3 +38,59 @@ pub use self::state_store::SqliteStateStore;
3538

3639
#[cfg(test)]
3740
matrix_sdk_test::init_tracing_for_tests!();
41+
42+
/// A configuration structure used for opening a store.
43+
pub struct SqliteStoreConfig {
44+
/// Path to the database, without the file name.
45+
path: PathBuf,
46+
/// Passphrase to open the store, if any.
47+
passphrase: Option<String>,
48+
/// The pool configuration for [`deadpool_sqlite`].
49+
pool_config: PoolConfig,
50+
}
51+
52+
impl SqliteStoreConfig {
53+
/// Create a new [`SqliteStoreConfig`] with a path representing the
54+
/// directory containing the store database.
55+
pub fn new<P>(path: P) -> Self
56+
where
57+
P: AsRef<Path>,
58+
{
59+
Self {
60+
path: path.as_ref().to_path_buf(),
61+
passphrase: None,
62+
pool_config: PoolConfig::new(num_cpus::get_physical() * 4),
63+
}
64+
}
65+
66+
/// Define the passphrase if the store is encoded.
67+
pub fn passphrase(mut self, passphrase: Option<&str>) -> Self {
68+
self.passphrase = passphrase.map(|passphrase| passphrase.to_owned());
69+
self
70+
}
71+
72+
/// Define the maximum pool size for [`deadpool_sqlite`].
73+
///
74+
/// See [`deadpool_sqlite::PoolConfig::max_size`] to learn more.
75+
pub fn pool_max_size(mut self, max_size: usize) -> Self {
76+
self.pool_config.max_size = max_size;
77+
self
78+
}
79+
}
80+
81+
#[cfg(test)]
82+
mod tests {
83+
use std::path::{Path, PathBuf};
84+
85+
use super::SqliteStoreConfig;
86+
87+
#[test]
88+
fn test_store_open_config() {
89+
let store_open_config =
90+
SqliteStoreConfig::new(Path::new("foo")).passphrase(Some("bar")).pool_max_size(42);
91+
92+
assert_eq!(store_open_config.path, PathBuf::from("foo"));
93+
assert_eq!(store_open_config.passphrase, Some("bar".to_owned()));
94+
assert_eq!(store_open_config.pool_config.max_size, 42);
95+
}
96+
}

0 commit comments

Comments
 (0)