// 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> { 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"); // 4. Last alle node_access-rader let access_rows = sqlx::query_as::<_, PgNodeAccess>( "SELECT subject_id, object_id, access::text, \ COALESCE(via_edge::text, '') as via_edge \ FROM node_access" ) .fetch_all(db) .await?; let access_count = access_rows.len(); for row in &access_rows { stdb.upsert_node_access( &row.subject_id.to_string(), &row.object_id.to_string(), &row.access, &row.via_edge, ) .await?; } tracing::info!("Warmup: {access_count} node_access-rader lastet"); let stats = WarmupStats { nodes: node_count, edges: edge_count, access: access_count, }; tracing::info!("Warmup: ferdig ({} noder, {} edges, {} access)", stats.nodes, stats.edges, stats.access); Ok(stats) } pub struct WarmupStats { pub nodes: usize, pub edges: usize, pub access: 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, created_by: String, } #[derive(sqlx::FromRow)] #[allow(dead_code)] struct PgNodeAccess { subject_id: uuid::Uuid, object_id: uuid::Uuid, access: String, via_edge: 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, created_by: String, }