7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1 | // Copyright 2022 The Matrix.org Foundation C.I.C. |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 2 | // |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 3 | // Licensed under the Apache License, Version 2.0 (the "License"); |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 4 | // you may not use this file except in compliance with the License. |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 5 | // You may obtain a copy of the License at |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 6 | // |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 7 | // http://www.apache.org/licenses/LICENSE-2.0 |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 8 | // |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 9 | // Unless required by applicable law or agreed to in writing, software |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 10 | // distributed under the License is distributed on an "AS IS" BASIS, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 12 | // See the License for the specific language governing permissions and |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 13 | // limitations under the License. |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 14 | |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 15 | #![cfg_attr( |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 16 | not(any(feature = "state-store", feature = "crypto-store", feature = "event-cache")), |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 17 | allow(dead_code, unused_imports) |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 18 | )] |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 19 | |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 20 | mod connection; |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 21 | #[cfg(feature = "crypto-store")] |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 22 | mod crypto_store; |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 23 | mod error; |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 24 | #[cfg(feature = "event-cache")] |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 25 | mod event_cache_store; |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 26 | #[cfg(feature = "event-cache")] |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 27 | mod media_store; |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 28 | #[cfg(feature = "state-store")] |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 29 | mod state_store; |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 30 | mod utils; |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 31 | use std::{ |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 32 | cmp::max, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 33 | fmt, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 34 | path::{Path, PathBuf}, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 35 | }; |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 36 | |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 37 | use deadpool::managed::PoolConfig; |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 38 | use zeroize::{Zeroize, ZeroizeOnDrop, Zeroizing}; |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 39 | |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 40 | #[cfg(feature = "crypto-store")] |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 41 | pub use self::crypto_store::SqliteCryptoStore; |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 42 | pub use self::error::OpenStoreError; |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 43 | #[cfg(feature = "event-cache")] |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 44 | pub use self::event_cache_store::SqliteEventCacheStore; |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 45 | #[cfg(feature = "event-cache")] |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 46 | pub use self::media_store::SqliteMediaStore; |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 47 | #[cfg(feature = "state-store")] |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 48 | pub use self::state_store::{DATABASE_NAME as STATE_STORE_DATABASE_NAME, SqliteStateStore}; |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 49 | |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 50 | #[cfg(test)] |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 51 | matrix_sdk_test_utils::init_tracing_for_tests!(); |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 52 | |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 53 | /// An enum used to store the secret that gives access to a store |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 54 | #[derive(Clone, Debug, PartialEq, Zeroize, ZeroizeOnDrop)] |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 55 | pub enum Secret { |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 56 | // Cryptographic key used to open the store |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 57 | Key(Box<[u8; 32]>), |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 58 | // Passphrase used to open the store |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 59 | PassPhrase(Zeroizing<String>), |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 60 | } |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 61 | |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 62 | /// A configuration structure used for opening a store. |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 63 | #[derive(Clone)] |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 64 | pub struct SqliteStoreConfig { |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 65 | /// Path to the database, without the file name. |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 66 | path: PathBuf, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 67 | /// Secret to open the store, if any |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 68 | secret: Option<Secret>, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 69 | /// The pool configuration for [`deadpool`]. |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 70 | pool_config: PoolConfig, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 71 | /// The runtime configuration to apply when opening an SQLite connection. |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 72 | runtime_config: RuntimeConfig, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 73 | } |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 74 | |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 75 | impl fmt::Debug for SqliteStoreConfig { |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 76 | fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result { |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 77 | formatter |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 78 | .debug_struct("SqliteStoreConfig") |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 79 | .field("path", &self.path) |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 80 | .field("pool_config", &self.pool_config) |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 81 | .field("runtime_config", &self.runtime_config) |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 82 | .finish_non_exhaustive() |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 83 | } |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 84 | } |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 85 | |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 86 | /// The minimum size of the connections pool. |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 87 | /// |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 88 | /// We need at least 2 connections: one connection for write operations, and one |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 89 | /// connection for read operations. |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 90 | const POOL_MINIMUM_SIZE: usize = 2; |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 91 | |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 92 | impl SqliteStoreConfig { |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 93 | /// Create a new [`SqliteStoreConfig`] with a path representing the |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 94 | /// directory containing the store database. |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 95 | pub fn new<P>(path: P) -> Self |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 96 | where |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 97 | P: AsRef<Path>, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 98 | { |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 99 | Self { |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 100 | path: path.as_ref().to_path_buf(), |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 101 | pool_config: PoolConfig::new(max(POOL_MINIMUM_SIZE, num_cpus::get_physical() * 4)), |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 102 | runtime_config: RuntimeConfig::default(), |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 103 | secret: None, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 104 | } |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 105 | } |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 106 | |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 107 | /// Similar to [`SqliteStoreConfig::new`], but with defaults tailored for a |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 108 | /// low memory usage environment. |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 109 | /// |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 110 | /// The following defaults are set: |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 111 | /// |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 112 | /// * The `pool_max_size` is set to the number of physical CPU, so one |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 113 | /// connection per physical thread, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 114 | /// * The `cache_size` is set to 500Kib, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 115 | /// * The `journal_size_limit` is set to 2Mib. |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 116 | pub fn with_low_memory_config<P>(path: P) -> Self |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 117 | where |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 118 | P: AsRef<Path>, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 119 | { |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 120 | Self::new(path) |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 121 | // Maximum one connection per physical thread. |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 122 | .pool_max_size(num_cpus::get_physical()) |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 123 | // Cache size is 500Kib. |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 124 | .cache_size(500_000) |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 125 | // Journal size limit is 2Mib. |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 126 | .journal_size_limit(2_000_000) |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 127 | } |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 128 | |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 129 | /// Override the path. |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 130 | pub fn path<P>(mut self, path: P) -> Self |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 131 | where |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 132 | P: AsRef<Path>, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 133 | { |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 134 | self.path = path.as_ref().to_path_buf(); |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 135 | self |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 136 | } |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 137 | |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 138 | /// Define the passphrase if the store is encoded. |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 139 | pub fn passphrase(mut self, passphrase: Option<&str>) -> Self { |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 140 | self.secret = |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 141 | passphrase.map(|passphrase| Secret::PassPhrase(Zeroizing::new(passphrase.to_owned()))); |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 142 | self |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 143 | } |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 144 | |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 145 | /// Define the key if the store is encoded. |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 146 | pub fn key(mut self, key: Option<&[u8; 32]>) -> Self { |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 147 | self.secret = key.map(|key| Secret::Key(Box::new(*key))); |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 148 | self |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 149 | } |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 150 | |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 151 | /// Define the maximum pool size for [`deadpool`]. |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 152 | /// |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 153 | /// See [`deadpool::managed::PoolConfig::max_size`] to learn more. |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 154 | pub fn pool_max_size(mut self, max_size: usize) -> Self { |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 155 | self.pool_config.max_size = max(POOL_MINIMUM_SIZE, max_size); |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 156 | self |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 157 | } |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 158 | |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 159 | /// Optimize the database. |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 160 | /// |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 161 | /// The SQLite documentation recommends to run this regularly and after any |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 162 | /// schema change. The easiest is to do it consistently when the store is |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 163 | /// constructed, after eventual migrations. |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 164 | /// |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 165 | /// See [`PRAGMA optimize`] to learn more. |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 166 | /// |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 167 | /// The default value is `true`. |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 168 | /// |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 169 | /// [`PRAGMA optimize`]: https://www.sqlite.org/pragma.html#pragma_optimize |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 170 | pub fn optimize(mut self, optimize: bool) -> Self { |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 171 | self.runtime_config.optimize = optimize; |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 172 | self |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 173 | } |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 174 | |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 175 | /// Define the maximum size in **bytes** the SQLite cache can use. |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 176 | /// |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 177 | /// See [`PRAGMA cache_size`] to learn more. |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 178 | /// |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 179 | /// The default value is 2Mib. |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 180 | /// |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 181 | /// [`PRAGMA cache_size`]: https://www.sqlite.org/pragma.html#pragma_cache_size |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 182 | pub fn cache_size(mut self, cache_size: u32) -> Self { |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 183 | self.runtime_config.cache_size = cache_size; |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 184 | self |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 185 | } |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 186 | |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 187 | /// Limit the size of the WAL file, in **bytes**. |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 188 | /// |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 189 | /// By default, while the DB connections of the databases are open, [the |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 190 | /// size of the WAL file can keep increasing][size_wal_file] depending on |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 191 | /// the size needed for the transactions. A critical case is `VACUUM` |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 192 | /// which basically writes the content of the DB file to the WAL file |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 193 | /// before writing it back to the DB file, so we end up taking twice the |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 194 | /// size of the database. |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 195 | /// |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 196 | /// By setting this limit, the WAL file is truncated after its content is |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 197 | /// written to the database, if it is bigger than the limit. |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 198 | /// |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 199 | /// See [`PRAGMA journal_size_limit`] to learn more. The value `limit` |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 200 | /// corresponds to `N` in `PRAGMA journal_size_limit = N`. |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 201 | /// |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 202 | /// The default value is 10Mib. |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 203 | /// |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 204 | /// [size_wal_file]: https://www.sqlite.org/wal.html#avoiding_excessively_large_wal_files |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 205 | /// [`PRAGMA journal_size_limit`]: https://www.sqlite.org/pragma.html#pragma_journal_size_limit |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 206 | pub fn journal_size_limit(mut self, limit: u32) -> Self { |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 207 | self.runtime_config.journal_size_limit = limit; |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 208 | self |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 209 | } |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 210 | |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 211 | /// Returns the pool configuration. |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 212 | pub(crate) fn pool_config(&self) -> PoolConfig { |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 213 | self.pool_config |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 214 | } |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 215 | |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 216 | /// Returns the runtime configuration. |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 217 | pub(crate) fn runtime_config(&self) -> RuntimeConfig { |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 218 | self.runtime_config |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 219 | } |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 220 | |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 221 | /// Build a pool of active connections to a particular database. |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 222 | pub fn build_pool_of_connections( |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 223 | &self, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 224 | database_name: &str, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 225 | ) -> Result<connection::Pool, connection::CreatePoolError> { |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 226 | let path = self.path.join(database_name); |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 227 | let manager = connection::Manager::new(path); |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 228 | |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 229 | connection::Pool::builder(manager) |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 230 | .config(self.pool_config) |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 231 | .runtime(connection::RUNTIME) |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 232 | .build() |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 233 | .map_err(connection::CreatePoolError::Build) |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 234 | } |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 235 | } |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 236 | |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 237 | /// This type represents values to set at runtime when a database is opened. |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 238 | /// |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 239 | /// This configuration is applied by |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 240 | /// [`utils::SqliteAsyncConnExt::apply_runtime_config`]. |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 241 | #[derive(Clone, Copy, Debug)] |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 242 | struct RuntimeConfig { |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 243 | /// If `true`, [`utils::SqliteAsyncConnExt::optimize`] will be called. |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 244 | optimize: bool, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 245 | |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 246 | /// Regardless of the value, [`utils::SqliteAsyncConnExt::cache_size`] will |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 247 | /// always be called with this value. |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 248 | cache_size: u32, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 249 | |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 250 | /// Regardless of the value, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 251 | /// [`utils::SqliteAsyncConnExt::journal_size_limit`] will always be called |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 252 | /// with this value. |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 253 | journal_size_limit: u32, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 254 | } |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 255 | |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 256 | impl Default for RuntimeConfig { |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 257 | fn default() -> Self { |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 258 | Self { |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 259 | // Optimize is always applied. |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 260 | optimize: true, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 261 | // A cache of 2Mib. |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 262 | cache_size: 2_000_000, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 263 | // A limit of 10Mib. |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 264 | journal_size_limit: 10_000_000, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 265 | } |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 266 | } |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 267 | } |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 268 | |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 269 | #[cfg(test)] |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 270 | mod tests { |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 271 | use std::{ |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 272 | ops::Not, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 273 | path::{Path, PathBuf}, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 274 | }; |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 275 | |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 276 | use super::{POOL_MINIMUM_SIZE, Secret, SqliteStoreConfig}; |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 277 | |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 278 | #[test] |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 279 | fn test_new() { |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 280 | let store_config = SqliteStoreConfig::new(Path::new("foo")); |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 281 | |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 282 | assert_eq!(store_config.pool_config.max_size, num_cpus::get_physical() * 4); |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 283 | assert!(store_config.runtime_config.optimize); |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 284 | assert_eq!(store_config.runtime_config.cache_size, 2_000_000); |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 285 | assert_eq!(store_config.runtime_config.journal_size_limit, 10_000_000); |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 286 | } |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 287 | |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 288 | #[test] |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 289 | fn test_with_low_memory_config() { |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 290 | let store_config = SqliteStoreConfig::with_low_memory_config(Path::new("foo")); |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 291 | |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 292 | assert_eq!(store_config.pool_config.max_size, num_cpus::get_physical()); |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 293 | assert!(store_config.runtime_config.optimize); |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 294 | assert_eq!(store_config.runtime_config.cache_size, 500_000); |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 295 | assert_eq!(store_config.runtime_config.journal_size_limit, 2_000_000); |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 296 | } |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 297 | |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 298 | #[test] |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 299 | fn test_store_config_when_passphrase() { |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 300 | let store_config = SqliteStoreConfig::new(Path::new("foo")) |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 301 | .passphrase(Some("bar")) |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 302 | .pool_max_size(42) |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 303 | .optimize(false) |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 304 | .cache_size(43) |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 305 | .journal_size_limit(44); |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 306 | |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 307 | assert_eq!(store_config.path, PathBuf::from("foo")); |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 308 | assert_eq!(store_config.secret, Some(Secret::PassPhrase("bar".to_owned().into()))); |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 309 | assert_eq!(store_config.pool_config.max_size, 42); |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 310 | assert!(store_config.runtime_config.optimize.not()); |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 311 | assert_eq!(store_config.runtime_config.cache_size, 43); |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 312 | assert_eq!(store_config.runtime_config.journal_size_limit, 44); |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 313 | } |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 314 | |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 315 | #[test] |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 316 | fn test_store_config_when_key() { |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 317 | let store_config = SqliteStoreConfig::new(Path::new("foo")) |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 318 | .key(Some(&[ |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 319 | 143, 27, 202, 78, 96, 55, 13, 149, 247, 8, 33, 120, 204, 92, 171, 66, 19, 238, 61, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 320 | 107, 132, 211, 40, 244, 71, 190, 99, 14, 173, 225, 6, 156, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 321 | ])) |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 322 | .pool_max_size(42) |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 323 | .optimize(false) |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 324 | .cache_size(43) |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 325 | .journal_size_limit(44); |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 326 | |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 327 | assert_eq!(store_config.path, PathBuf::from("foo")); |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 328 | assert_eq!( |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 329 | store_config.secret, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 330 | Some(Secret::Key(Box::new([ |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 331 | 143, 27, 202, 78, 96, 55, 13, 149, 247, 8, 33, 120, 204, 92, 171, 66, 19, 238, 61, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 332 | 107, 132, 211, 40, 244, 71, 190, 99, 14, 173, 225, 6, 156, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 333 | ]))) |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 334 | ); |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 335 | assert_eq!(store_config.pool_config.max_size, 42); |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 336 | assert!(store_config.runtime_config.optimize.not()); |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 337 | assert_eq!(store_config.runtime_config.cache_size, 43); |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 338 | assert_eq!(store_config.runtime_config.journal_size_limit, 44); |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 339 | } |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 340 | |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 341 | #[test] |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 342 | fn test_store_config_path() { |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 343 | let store_config = SqliteStoreConfig::new(Path::new("foo")).path(Path::new("bar")); |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 344 | |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 345 | assert_eq!(store_config.path, PathBuf::from("bar")); |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 346 | } |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 347 | |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 348 | #[test] |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 349 | fn test_pool_size_has_a_minimum() { |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 350 | let store_config = SqliteStoreConfig::new(Path::new("foo")).pool_max_size(1); |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 351 | |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 352 | assert_eq!(store_config.pool_config.max_size, POOL_MINIMUM_SIZE); |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 353 | } |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 354 | } |