Files
wasmeld/crates/wasmeld-console/src/persistence.rs
T

102 lines
3.0 KiB
Rust
Raw Normal View History

//! 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
}
}