#[cfg(feature = "backend-postgres")]
pub mod pg;
#[cfg(feature = "backend-postgres")]
pub use pg::PgEngineSchema;
#[cfg(feature = "backend-sqlite")]
pub mod sqlite;
#[cfg(feature = "backend-sqlite")]
pub use sqlite::SqliteEngineSchema;
#[cfg(feature = "backend-postgres")]
pub const SCHEMA_MIGRATION_LOCK: i64 = 0x6173_7361_795f_656e;
#[cfg(feature = "backend-postgres")]
pub async fn acquire_schema_lock(conn: &mut sqlx::PgConnection) -> sqlx::Result<()> {
sqlx::query("SELECT pg_advisory_xact_lock($1)")
.bind(SCHEMA_MIGRATION_LOCK)
.execute(conn)
.await?;
Ok(())
}
#[cfg(feature = "backend-postgres")]
const DDL_CONFLICT_CODES: &[&str] = &["23505", "42P07", "42710"];
#[cfg(feature = "backend-postgres")]
pub fn is_ddl_conflict(err: &anyhow::Error) -> bool {
err.chain().any(|cause| {
cause
.downcast_ref::<sqlx::Error>()
.and_then(|e| e.as_database_error())
.and_then(|db| db.code())
.is_some_and(|code| DDL_CONFLICT_CODES.contains(&code.as_ref()))
})
}
#[cfg(feature = "backend-postgres")]
pub async fn retry_ddl<F, Fut, T>(attempts: usize, mut migrate: F) -> anyhow::Result<T>
where
F: FnMut() -> Fut,
Fut: std::future::Future<Output = anyhow::Result<T>>,
{
let mut last: Option<anyhow::Error> = None;
for attempt in 1..=attempts.max(1) {
match migrate().await {
Ok(value) => return Ok(value),
Err(err) if is_ddl_conflict(&err) && attempt < attempts => {
tracing::warn!(
attempt,
error = %err,
"another engine created this object first; retrying schema setup"
);
last = Some(err);
}
Err(err) => return Err(err),
}
}
Err(last.expect("the loop runs at least once"))
}
#[derive(Debug, Clone, PartialEq)]
pub struct ModuleRecord {
pub name: String,
pub enabled: bool,
pub enabled_at: Option<f64>,
pub enabled_by: Option<String>,
pub version: Option<String>,
pub config: serde_json::Value,
}
#[derive(Debug, Clone, PartialEq)]
pub struct AuditRecord {
pub id: String,
pub ts: f64,
pub actor: Option<String>,
pub action: String,
pub details: serde_json::Value,
}
#[derive(Debug, Clone, PartialEq)]
pub struct InstanceRecord {
pub id: String,
pub started_at: f64,
pub last_heartbeat: f64,
pub namespaces: Vec<String>,
pub version: Option<String>,
}