Menu
Public repository / 3a463af917761506796f6f58d832f707a1e1af3a
AkurAI-Monitor / src/db.rs
Latest change d0b8d14afb524ab72435e5abd0bbd6049a205349 - Migrate storage layer from SQLite to MariaDB by Ólafur Búi Ólafsson
use crate::config;
use crate::schema;
use mysql::{Opts, OptsBuilder, Pool, PoolConstraints, PoolOpts, PooledConn};
use std::sync::LazyLock;
static POOL: LazyLock<Pool> = LazyLock::new(|| {
let cfg = config::get();
let opts = Opts::from_url(&cfg.db_url).expect("invalid MONITOR_DB_URL");
let constraints = PoolConstraints::new(1, 8).expect("valid pool constraints");
let opts =
OptsBuilder::from_opts(opts).pool_opts(PoolOpts::default().with_constraints(constraints));
let pool = Pool::new(opts).expect("failed to create MariaDB pool");
let mut conn = pool.get_conn().expect("failed to connect to MariaDB");
schema::create_tables(&mut conn);
schema::seed_alert_rules(&mut conn);
pool
});
/// Run `f` with a pooled MariaDB connection. Connections are returned to the
/// pool on drop; the pool is bounded (1..=8) so DB memory stays flat.
pub fn with_db<F, R>(f: F) -> R
where
F: FnOnce(&mut PooledConn) -> R,
{
let mut conn = POOL.get_conn().expect("failed to get MariaDB connection");
f(&mut conn)
}
/// Test support: scratch databases on the dev MariaDB instance.
///
/// Tests are skipped (with a note on stderr) unless `MONITOR_TEST_DB_URL`
/// points at a user allowed to CREATE/DROP `akurai_test_%` databases —
/// on Titan/EC2 that is the dev container on port 3307.
#[cfg(test)]
pub mod testing {
use mysql::prelude::Queryable;
use mysql::{Opts, OptsBuilder, Pool, PoolConstraints, PoolOpts, PooledConn};
use std::sync::atomic::{AtomicU32, Ordering};
static COUNTER: AtomicU32 = AtomicU32::new(0);
pub struct TestDb {
pool: Pool,
admin: Pool,
pub name: String,
}
impl TestDb {
/// Returns `None` (after logging) when no test database is configured.
pub fn new() -> Option<TestDb> {
let Ok(url) = std::env::var("MONITOR_TEST_DB_URL") else {
eprintln!("skipping: MONITOR_TEST_DB_URL is not set");
return None;
};
let opts = Opts::from_url(&url).expect("invalid MONITOR_TEST_DB_URL");
let constraints = PoolConstraints::new(1, 2).expect("valid pool constraints");
let small = PoolOpts::default().with_constraints(constraints);
let admin = Pool::new(OptsBuilder::from_opts(opts.clone()).pool_opts(small.clone()))
.expect("connect test MariaDB");
let name = format!(
"akurai_test_mon_{}_{}",
std::process::id(),
COUNTER.fetch_add(1, Ordering::Relaxed)
);
admin
.get_conn()
.expect("admin conn")
.query_drop(format!("CREATE DATABASE `{name}`"))
.expect("create scratch database");
let scoped = OptsBuilder::from_opts(opts)
.db_name(Some(name.clone()))
.pool_opts(small);
let pool = Pool::new(scoped).expect("connect scratch database");
Some(TestDb { pool, admin, name })
}
pub fn conn(&self) -> PooledConn {
self.pool.get_conn().expect("scratch connection")
}
}
impl Drop for TestDb {
fn drop(&mut self) {
if let Ok(mut conn) = self.admin.get_conn() {
conn.query_drop(format!("DROP DATABASE IF EXISTS `{}`", self.name))
.ok();
}
}
}
}