synops/maskinrommet/src/warmup.rs
vegard c13a39317e Fullfør oppgave 2.3: STDB-klient, warmup og docs
- Fiks NULL-håndtering i warmup (COALESCE for title/content/created_by)
- Renere health check (delete nonexistent node i stedet for create+delete)
- Dokumenter HTTP API-format og warmup-flyt i erfaringer
- Lagre STDB-token i server .env
- Republiser STDB-modul etter containerrestart

Verifisert: warmup laster 2 noder + 1 edge, /health viser stdb=connected.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-17 12:49:50 +01:00

109 lines
2.9 KiB
Rust

// Warmup: last hele grafen fra PG til SpacetimeDB ved oppstart.
//
// Sekvens: clear_all → noder → edges.
// Edges refererer til noder, så noder må lastes først.
//
// Ref: docs/infra/synkronisering.md
use sqlx::PgPool;
use crate::stdb::StdbClient;
/// Last hele grafen fra PG til SpacetimeDB.
pub async fn run(db: &PgPool, stdb: &StdbClient) -> Result<WarmupStats, Box<dyn std::error::Error>> {
tracing::info!("Warmup: starter (PG → SpacetimeDB)");
// 1. Tøm STDB for å unngå duplikater ved restart
stdb.clear_all().await?;
tracing::info!("Warmup: STDB tømt");
// 2. Last alle noder
let nodes = sqlx::query_as::<_, PgNode>(
"SELECT id, node_kind::text, COALESCE(title, '') as title, \
COALESCE(content, '') as content, visibility::text, \
COALESCE(metadata::text, '{}') as metadata, \
created_at, COALESCE(created_by::text, '') as created_by \
FROM nodes ORDER BY created_at"
)
.fetch_all(db)
.await?;
let node_count = nodes.len();
for node in &nodes {
stdb.create_node(
&node.id.to_string(),
&node.node_kind,
&node.title,
&node.content,
&node.visibility,
&node.metadata,
&node.created_by,
)
.await?;
}
tracing::info!("Warmup: {node_count} noder lastet");
// 3. Last alle edges
let edges = sqlx::query_as::<_, PgEdge>(
"SELECT id, source_id, target_id, edge_type, \
COALESCE(metadata::text, '{}') as metadata, \
system, created_at, COALESCE(created_by::text, '') as created_by \
FROM edges ORDER BY created_at"
)
.fetch_all(db)
.await?;
let edge_count = edges.len();
for edge in &edges {
stdb.create_edge(
&edge.id.to_string(),
&edge.source_id.to_string(),
&edge.target_id.to_string(),
&edge.edge_type,
&edge.metadata,
edge.system,
&edge.created_by,
)
.await?;
}
tracing::info!("Warmup: {edge_count} edges lastet");
let stats = WarmupStats {
nodes: node_count,
edges: edge_count,
};
tracing::info!("Warmup: ferdig ({} noder, {} edges)", stats.nodes, stats.edges);
Ok(stats)
}
pub struct WarmupStats {
pub nodes: usize,
pub edges: usize,
}
// PG-radtyper for sqlx
#[derive(sqlx::FromRow)]
#[allow(dead_code)]
struct PgNode {
id: uuid::Uuid,
node_kind: String,
title: String,
content: String,
visibility: String,
metadata: String,
created_at: chrono::DateTime<chrono::Utc>,
created_by: String,
}
#[derive(sqlx::FromRow)]
#[allow(dead_code)]
struct PgEdge {
id: uuid::Uuid,
source_id: uuid::Uuid,
target_id: uuid::Uuid,
edge_type: String,
metadata: String,
system: bool,
created_at: chrono::DateTime<chrono::Utc>,
created_by: String,
}