- 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>
109 lines
2.9 KiB
Rust
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,
|
|
}
|