hoprd_db_api/
db.rs

1use hoprd_migration::{MigratorMetadata, MigratorTrait};
2use sea_orm::SqlxSqliteConnector;
3use sqlx::pool::PoolOptions;
4use sqlx::sqlite::{SqliteAutoVacuum, SqliteConnectOptions, SqliteJournalMode, SqliteSynchronous};
5use sqlx::{ConnectOptions, SqlitePool};
6use std::path::Path;
7use std::time::Duration;
8use tracing::log::LevelFilter;
9
10use crate::HoprdDbAllOperations;
11
12#[derive(Debug, Clone)]
13pub struct HoprdDb {
14    pub(crate) metadata: sea_orm::DatabaseConnection,
15}
16
17pub const SQL_DB_METADATA_FILE_NAME: &str = "hopr_metadata.db";
18
19impl HoprdDb {
20    pub async fn new(directory: String) -> Result<Self, anyhow::Error> {
21        let dir = Path::new(&directory);
22        std::fs::create_dir_all(dir)
23            .map_err(|e| anyhow::anyhow!("Cannot create main database directory {}: {}", dir.display(), e))?;
24
25        // Default SQLite config values for all 3 DBs.
26        // Each DB can customize with its own specific values
27        let cfg_template = SqliteConnectOptions::default()
28            .create_if_missing(true)
29            .log_slow_statements(LevelFilter::Warn, Duration::from_millis(150))
30            .log_statements(LevelFilter::Debug)
31            .journal_mode(SqliteJournalMode::Wal)
32            .synchronous(SqliteSynchronous::Normal)
33            .auto_vacuum(SqliteAutoVacuum::Full)
34            //.optimize_on_close(true, None) // Removed, because it causes optimization on each connection, due to min_connections being set to 0
35            .page_size(4096)
36            .pragma("cache_size", "-30000") // 32M
37            .pragma("busy_timeout", "1000"); // 1000ms
38
39        // Peers database
40        let metadata = PoolOptions::new()
41            .min_connections(0) // Default is 0
42            .acquire_timeout(Duration::from_secs(60)) // Default is 30
43            .idle_timeout(Some(Duration::from_secs(10 * 60))) // This is the default
44            .max_lifetime(Some(Duration::from_secs(30 * 60))) // This is the default
45            .max_connections(50) // Default is 10
46            .connect_with(cfg_template.filename(dir.join(SQL_DB_METADATA_FILE_NAME)))
47            .await?;
48
49        Self::new_sqlx_sqlite(metadata).await
50    }
51
52    pub async fn new_in_memory() -> Self {
53        Self::new_sqlx_sqlite(SqlitePool::connect(":memory:").await.unwrap())
54            .await
55            .unwrap()
56    }
57
58    async fn new_sqlx_sqlite(metadata_db: SqlitePool) -> Result<Self, anyhow::Error> {
59        let metadata_db = SqlxSqliteConnector::from_sqlx_sqlite_pool(metadata_db);
60
61        MigratorMetadata::up(&metadata_db, None)
62            .await
63            .expect("cannot apply database migration");
64
65        Ok(Self { metadata: metadata_db })
66    }
67}
68
69impl HoprdDbAllOperations for HoprdDb {}
70
71#[cfg(test)]
72mod tests {
73    use crate::db::HoprdDb;
74    use hoprd_migration::{MigratorMetadata, MigratorTrait};
75
76    #[async_std::test]
77    async fn test_basic_db_init() {
78        let db = HoprdDb::new_in_memory().await;
79
80        MigratorMetadata::status(&db.metadata).await.expect("status must be ok");
81    }
82}