102 lines
3.0 KiB
Rust
102 lines
3.0 KiB
Rust
|
|
//! Toasty models and transactional libSQL snapshots for Console control data.
|
||
|
|
//!
|
||
|
|
//! Component bytes and WIT packages remain filesystem artifacts. This module
|
||
|
|
//! persists only service manifests, metrics, and the bounded event history.
|
||
|
|
|
||
|
|
use std::path::Path;
|
||
|
|
|
||
|
|
use toasty::Db;
|
||
|
|
use toasty_driver_turso::Turso;
|
||
|
|
use tokio::sync::Mutex;
|
||
|
|
|
||
|
|
#[derive(Clone, Debug, toasty::Model)]
|
||
|
|
pub(crate) struct StoredService {
|
||
|
|
#[key]
|
||
|
|
pub service_key: String,
|
||
|
|
pub manifest_toml: String,
|
||
|
|
pub updated_at_ms: u64,
|
||
|
|
pub calls: u64,
|
||
|
|
pub errors: u64,
|
||
|
|
pub last_latency_ms: Option<u64>,
|
||
|
|
}
|
||
|
|
|
||
|
|
#[derive(Clone, Debug, toasty::Model)]
|
||
|
|
pub(crate) struct StoredEvent {
|
||
|
|
#[key]
|
||
|
|
pub id: u64,
|
||
|
|
pub timestamp_ms: u64,
|
||
|
|
pub kind: String,
|
||
|
|
pub service_id: Option<String>,
|
||
|
|
pub revision: Option<String>,
|
||
|
|
pub message: String,
|
||
|
|
}
|
||
|
|
|
||
|
|
/// Serialized access to the Toasty database connection.
|
||
|
|
pub(crate) struct Persistence {
|
||
|
|
db: Mutex<Db>,
|
||
|
|
}
|
||
|
|
|
||
|
|
impl Persistence {
|
||
|
|
/// Opens the database and installs the schema for a new file.
|
||
|
|
pub async fn open(path: &Path) -> toasty::Result<Self> {
|
||
|
|
let is_new_database = !path.exists();
|
||
|
|
let db = Db::builder()
|
||
|
|
.models(toasty::models!(StoredService, StoredEvent))
|
||
|
|
.build(Turso::file(path))
|
||
|
|
.await?;
|
||
|
|
if is_new_database {
|
||
|
|
db.push_schema().await?;
|
||
|
|
}
|
||
|
|
Ok(Self { db: Mutex::new(db) })
|
||
|
|
}
|
||
|
|
|
||
|
|
/// Loads the complete service set and retained event history.
|
||
|
|
pub async fn load(&self) -> toasty::Result<(Vec<StoredService>, Vec<StoredEvent>)> {
|
||
|
|
let mut db = self.db.lock().await;
|
||
|
|
let services = StoredService::all().exec(&mut *db).await?;
|
||
|
|
let events = StoredEvent::all().exec(&mut *db).await?;
|
||
|
|
Ok((services, events))
|
||
|
|
}
|
||
|
|
|
||
|
|
/// Atomically upserts a Console snapshot and prunes expired events.
|
||
|
|
pub async fn sync(
|
||
|
|
&self,
|
||
|
|
services: Vec<StoredService>,
|
||
|
|
events: Vec<StoredEvent>,
|
||
|
|
) -> toasty::Result<()> {
|
||
|
|
let mut db = self.db.lock().await;
|
||
|
|
let mut tx = db.transaction().await?;
|
||
|
|
|
||
|
|
for service in services {
|
||
|
|
StoredService::upsert_by_service_key(service.service_key)
|
||
|
|
.manifest_toml(service.manifest_toml)
|
||
|
|
.updated_at_ms(service.updated_at_ms)
|
||
|
|
.calls(service.calls)
|
||
|
|
.errors(service.errors)
|
||
|
|
.last_latency_ms(service.last_latency_ms)
|
||
|
|
.exec(&mut tx)
|
||
|
|
.await?;
|
||
|
|
}
|
||
|
|
|
||
|
|
if let Some(oldest_event) = events.last() {
|
||
|
|
StoredEvent::filter(StoredEvent::fields().id().lt(oldest_event.id))
|
||
|
|
.delete()
|
||
|
|
.exec(&mut tx)
|
||
|
|
.await?;
|
||
|
|
}
|
||
|
|
|
||
|
|
for event in events {
|
||
|
|
StoredEvent::upsert_by_id(event.id)
|
||
|
|
.timestamp_ms(event.timestamp_ms)
|
||
|
|
.kind(event.kind)
|
||
|
|
.service_id(event.service_id)
|
||
|
|
.revision(event.revision)
|
||
|
|
.message(event.message)
|
||
|
|
.exec(&mut tx)
|
||
|
|
.await?;
|
||
|
|
}
|
||
|
|
|
||
|
|
tx.commit().await
|
||
|
|
}
|
||
|
|
}
|