Visibility-filtrering (oppgave 4.3, del 1/2): - Ny node_access-tabell i STDB-modulen som speiler PG - Reducers: upsert_node_access, delete_node_access, delete_node_access_for_subject - STDB-klient i maskinrommet: metoder for node_access - Warmup synker node_access fra PG til STDB ved oppstart - Tilgangsgivende edges synker node_access til STDB etter PG-commit - clear_all tømmer også node_access Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
141 lines
3.7 KiB
Rust
141 lines
3.7 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");
|
|
|
|
// 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<chrono::Utc>,
|
|
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<chrono::Utc>,
|
|
created_by: String,
|
|
}
|