use std::path::Path;
use std::time::Duration;
use serde::Serialize;
use sqlx::{
Pool, Sqlite,
sqlite::{SqliteConnectOptions, SqliteJournalMode, SqlitePoolOptions},
};
use crate::error::Result;
pub static MIGRATOR: sqlx::migrate::Migrator = sqlx::migrate!("./migrations");
pub async fn open<P: AsRef<Path>>(path: P) -> Result<Pool<Sqlite>> {
let opts = SqliteConnectOptions::new()
.filename(path.as_ref())
.create_if_missing(true)
.journal_mode(SqliteJournalMode::Wal)
.busy_timeout(Duration::from_millis(5000))
.foreign_keys(true);
let pool = SqlitePoolOptions::new()
.max_connections(8)
.connect_with(opts)
.await?;
MIGRATOR.run(&pool).await?;
Ok(pool)
}
#[derive(Debug, Serialize)]
pub struct Health {
pub labels: i64,
pub lease: Option<Lease>,
}
#[derive(Debug, Serialize, sqlx::FromRow)]
pub struct Lease {
pub instance_id: String,
pub acquired_at: i64,
pub last_heartbeat: i64,
}
pub async fn health(pool: &Pool<Sqlite>) -> Result<Health> {
let labels: i64 = sqlx::query_scalar!("SELECT COUNT(*) FROM labels")
.fetch_one(pool)
.await?;
let lease = sqlx::query_as!(
Lease,
"SELECT instance_id, acquired_at, last_heartbeat \
FROM server_instance_lease WHERE id = 1"
)
.fetch_optional(pool)
.await?;
Ok(Health { labels, lease })
}
#[cfg(test)]
mod tests {
use super::*;
#[tokio::test]
async fn migrations_apply_to_fresh_db() {
let dir = tempfile::tempdir().expect("tempdir");
let pool = open(dir.path().join("cairn.db")).await.expect("open pool");
let h = health(&pool).await.expect("health query");
assert_eq!(h.labels, 0);
assert!(h.lease.is_none());
}
}