diff --git a/docs/retninger/README.md b/docs/retninger/README.md index 66b9ba4..45b9d98 100644 --- a/docs/retninger/README.md +++ b/docs/retninger/README.md @@ -26,7 +26,7 @@ andre dokumenter. En retning kan også forkastes eller parkeres. | [Universell input og mottak](universell_input.md) | **Besluttet** | Én multimodal input-primitiv, én mottaksflate, kommunikasjonsnoder. Edges definerer alt. | | [Maskinrommet](maskinrommet.md) | **Besluttet** | Én Rust-tjeneste: fang, prosesser, lever. Eier all skriving. Edge-drevet ressursorkestrering. | | [Noder er sentrum](bruker_ikke_workspace.md) | **Besluttet** | Alt er noder (brukere, team, innhold). Edges definerer relasjoner og tilgang. Materialisert tilgangsmatrise for RLS. | -| [Datalaget](datalaget.md) | **Besluttet** | SpacetimeDB holder hele grafen, PG er persistent arkiv, CAS for binærdata, AGE ved behov | +| [Datalaget](datalaget.md) | **Revidert** | PG er eneste datakilde. Sanntid via LISTEN/NOTIFY + WebSocket. SpacetimeDB fases ut. CAS for binærdata, AGE ved behov. | | [Arbeidsflaten](arbeidsflaten.md) | **Besluttet** | Spatial canvas med verktøy-paneler. Drag-and-drop skaper nye noder med edges. | | [Unix-filosofi](unix_filosofi.md) | **Besluttet** | Maskinrommet orkestrerer, CLI-verktøy gjør jobben. Claude deler verktøykasse. | diff --git a/docs/retninger/datalaget.md b/docs/retninger/datalaget.md index 1aca801..9eb54f3 100644 --- a/docs/retninger/datalaget.md +++ b/docs/retninger/datalaget.md @@ -1,10 +1,11 @@ # Datalaget -**Status: Besluttet.** +**Status: Besluttet. Revidert mars 2026 — SpacetimeDB fases ut.** -> SpacetimeDB holder hele grafen i minne og mottar skrivinger først. -> PostgreSQL er persistent arkiv og backup. CAS lagrer binærdata. +> PostgreSQL er eneste datakilde. Sanntid via PG `LISTEN/NOTIFY` +> og WebSocket i portvokteren. CAS lagrer binærdata. > Apache AGE legges til ved behov for Cypher-traverseringer. +> SpacetimeDB fases ut — se migrasjonsplan. ## Lagmodell @@ -12,47 +13,34 @@ GUI (SvelteKit) │ skriv │ les (sanntid, WebSocket) ▼ ▼ -Maskinrommet (Rust) SpacetimeDB ──→ GUI +Portvokteren (Rust) Portvokteren ──WebSocket──→ GUI │ validering ▲ - ├──→ SpacetimeDB (først) ───┘ - └──→ PostgreSQL (asynk, persistent) + └──→ PostgreSQL ──NOTIFY──→──┘ ``` ### Skrivestien -GUI → Maskinrommet → validering → SpacetimeDB (instant) → PG (asynk). -Frontend oppdateres umiddelbart. PG persisterer i bakgrunnen. +GUI → portvokteren → validering → PG. Frontend oppdateres via +WebSocket-push utløst av PG NOTIFY. ### Lesestien (sanntid) -SpacetimeDB → GUI (direkte WebSocket, ~10μs). -Hele grafen er i SpacetimeDB. Frontend har alltid alt tilgjengelig. +PG → portvokteren → WebSocket → GUI. +Portvokteren holder en in-memory cache av aktive subscriptions +og pusher relevante endringer til tilkoblede klienter. ### Lesestien (tunge spørringer) -GUI → Maskinrommet → PG. -Fulltekstsøk, pgvector (semantisk søk), statistikk, AGE-traverseringer. +GUI → portvokteren → PG. +Fulltekstsøk, pgvector, statistikk, AGE-traverseringer. -## SpacetimeDB — hele grafen i minne +## PostgreSQL — eneste datakilde -Node- og edge-skjemaet er minimalt (åtte kolonner hver). For en -liten brukerbase er hele grafen triviell å holde i minne. Fordeler: +Én sannhetskilde. Ingen synk, ingen konsistensproblemer: -- Ingen sync-logikk for å bestemme hva som er "aktivt" -- Ingen henting fra PG når noen åpner noe gammelt -- Frontend har alltid alt via WebSocket -- Én lesekilde — ingen tvetydighet - -Når dette blir problematisk (hundretusenvis av noder), innføres -eviction. Det er en optimalisering, ikke en arkitekturendring. - -## PostgreSQL — arkiv og kraftspørringer - -PG er persistent backup for hele grafen, pluss hjemsted for -tunge operasjoner SpacetimeDB ikke er laget for: - -- **Fulltekstsøk** — `tsvector` på `nodes.content` og `nodes.title` -- **Semantisk søk** — pgvector for embedding-basert likhet -- **Graftraversering** — rekursive CTEs, Apache AGE ved behov -- **Statistikk** — aggregeringer, tidsserier -- **Tilgangsmatrise** — `node_access` beregnes her, speiles til STDB +- **Sanntid:** `LISTEN/NOTIFY` → portvokteren → WebSocket +- **Fulltekstsøk:** `tsvector` på `nodes.content` og `nodes.title` +- **Semantisk søk:** pgvector for embedding-basert likhet +- **Graftraversering:** rekursive CTEs, Apache AGE ved behov +- **Statistikk:** aggregeringer, tidsserier +- **Tilgangsmatrise:** `node_access` beregnet fra edges ### Apache AGE — ved behov @@ -63,8 +51,7 @@ AGE legges til som PG-extension når Cypher-semantikk faktisk trengs: 2. **Når CTEs blir smertefulle:** Legg til AGE 3. **Usannsynlig:** Evaluer Neo4j hvis AGE ikke holder -AGE er en extension, ikke en migrering — boltes på uten å endre -eksisterende kode. +AGE er en extension, ikke en migrering. ## CAS — binærlagring @@ -75,23 +62,105 @@ Selve biten lever utenfor PG. Pruning-regler basert på modalitet, edges og aksessmønstre. Se [maskinrommet](maskinrommet.md). -## SpacetimeDB som utbyttbar +## Sanntid via PG LISTEN/NOTIFY -SpacetimeDB er en sanntidscache, ikke en avhengighet. Hvis den -fjernes: +PG har innebygd pub/sub. Portvokteren lytter og videresender: -- **Sanntid:** PG `LISTEN/NOTIFY` → SvelteKit SSE -- **Skriving:** Maskinrommet → PG direkte -- **Lesing:** Maskinrommet → PG → GUI +``` +PG: NOTIFY node_changed, '{"id":"abc","kind":"content"}' + → Portvokteren mottar + → Sjekker tilgangsmatrise: hvem skal se denne endringen? + → Pusher til relevante WebSocket-tilkoblinger + → Frontend oppdaterer reaktivt +``` -Trenger ikke implementeres, men arkitekturen skal aldri gjøre -det umulig. Se [synkronisering](../infra/synkronisering.md). +### Trigger i PG + +```sql +CREATE OR REPLACE FUNCTION notify_node_change() +RETURNS trigger AS $$ +BEGIN + PERFORM pg_notify('node_changed', + json_build_object( + 'op', TG_OP, + 'id', NEW.id, + 'kind', NEW.node_kind + )::text + ); + RETURN NEW; +END; +$$ LANGUAGE plpgsql; + +CREATE TRIGGER nodes_notify + AFTER INSERT OR UPDATE ON nodes + FOR EACH ROW EXECUTE FUNCTION notify_node_change(); +``` + +Tilsvarende for edges. + +### WebSocket i portvokteren + +Portvokteren holder: +- Map av tilkoblede klienter → brukerens node_id +- Map av node_id → synlige noder (fra node_access) +- Ved NOTIFY: filtrer på tilgang, push til relevante klienter + +Enklere enn STDB-synk: én retning (PG → klient), ingen +reducer-logikk, ingen konsistensproblemer. + +## Hvorfor SpacetimeDB fases ut + +SpacetimeDB var et godt eksperiment. Det løste sanntid elegant +i prototype-fasen. Men for produksjon på én server: + +- **Synk-kompleksitet.** PG ↔ STDB synk er en egen feilkategori. + Erfaringsdocs (adapter_moenster.md, spacetimedb_integrasjon.md) + dokumenterer smerten. +- **Dobbelt vedlikehold.** STDB-modul med reducers må holdes i + synk med PG-skjema. Endring i nodes-tabellen → to steder. +- **Ekstra SPOF.** Enda en tjeneste å overvåke, restarte, debugge. +- **Unødvendig for skalaen.** PG LISTEN/NOTIFY + WebSocket gir + ~5ms latency. STDB ga ~0.01ms. Forskjellen er umerkelig for + brukere. +- **CLI-verktøy forenkles.** Bare PG-tilkobling, ingen STDB-klient. + +## Migrasjonsplan: STDB → PG LISTEN/NOTIFY + +### Fase M1: WebSocket-lag i portvokteren +Implementer LISTEN/NOTIFY-lytter og WebSocket-endepunkt i +portvokteren. Legg til PG-triggers for nodes og edges. +Frontend kobler til begge (STDB + nytt WS) i parallell. + +### Fase M2: Frontend-migrering +Endre frontend fra SpacetimeDB-klient til vanlig WebSocket. +Erstatt STDB-stores med reaktive stores som lytter på +portvokterens WebSocket. Verifiser at all sanntidsfunksjonalitet +fungerer. + +### Fase M3: Fjern skrivestien til STDB +Portvokteren slutter å skrive til SpacetimeDB. All skriving +går kun til PG. NOTIFY-triggere er eneste push-mekanisme. + +### Fase M4: Fjern STDB +- Stopp SpacetimeDB Docker-container +- Fjern STDB-modul (spacetimedb/) +- Fjern STDB-klient fra portvokteren +- Fjern STDB-avhengigheter fra frontend +- Fjern synkroniserings-kode +- Oppdater docs (synkronisering.md → arkiver) +- Oppdater CLAUDE.md + +### Fase M5: Opprydding +- Slett erfaringsdocs som kun gjelder STDB +- Oppdater alle docs-referanser til STDB +- Fjern Docker-konfig for SpacetimeDB ## Forhold til andre retninger - [Noder er sentrum](bruker_ikke_workspace.md) — tilgangsmatrise - beregnet fra edge-grafen, speiles til SpacetimeDB + beregnet fra edge-grafen, brukes for WebSocket-filtrering - [Universell input og mottak](universell_input.md) — noder og edges er datamodellen for alle tre primitiver -- [Maskinrommet](maskinrommet.md) — CAS-pruning, edge-drevet - ressursorkestrering, validering før skriving +- [Maskinrommet / Portvokteren](maskinrommet.md) — CAS-pruning, + edge-drevet ressursorkestrering, validering før skriving, + WebSocket-endepunkt for sanntid diff --git a/tasks.md b/tasks.md index dd1cec5..f8ff62c 100644 --- a/tasks.md +++ b/tasks.md @@ -42,6 +42,7 @@ Fase 3 + 13 → Fase 19 (arbeidsflaten — spatial canvas) Fase 19 → Fase 20 (universell overføring + panelrework) Fase 2 → Fase 21 (CLI-verktøy — unix-filosofi) Alt → Fase 12 (herding) +Fase 12 → Fase 22 (SpacetimeDB-migrering) ``` Hvis en oppgave er `[?]` eller `[!]`, hoppes den over — og alle @@ -269,4 +270,16 @@ kaller dem direkte. Samme verktøy, to brukere. - [x] 12.1 Observerbarhet: strukturert logging, metrikker (request latency, queue depth, AI cost). - [x] 12.2 Backup: PG-dump rutine, STDB → PG gjenoppbygging ved krasj. - [ ] 12.3 Feilhåndtering: retry med backoff i skrivestien, dead letter queue for feilede PG-skrivinger. -- [ ] 12.4 Ytelse: profiler STDB-spørringer, optimaliser node_access-oppdatering. +- [ ] 12.4 Ytelse: profiler PG-spørringer, optimaliser node_access-oppdatering. + +## Fase 22: SpacetimeDB-migrering — PG LISTEN/NOTIFY + +Ref: `docs/retninger/datalaget.md` (revidert mars 2026). Faser ut SpacetimeDB +til fordel for PG LISTEN/NOTIFY + WebSocket i portvokteren. Én datakilde, +ingen synk-kompleksitet. + +- [ ] 22.1 WebSocket-lag i portvokteren: implementer PG LISTEN/NOTIFY-lytter og WebSocket-endepunkt. Legg til PG-triggers (`notify_node_change`, `notify_edge_change`) for nodes og edges. Frontend kobler til begge (STDB + nytt WS) i parallell for verifisering. +- [ ] 22.2 Frontend-migrering: erstatt SpacetimeDB-klient med vanlig WebSocket til portvokteren. Erstatt STDB-stores med reaktive stores som lytter på WebSocket. Verifiser all sanntidsfunksjonalitet (chat, kanban, kalender, mixer, canvas). +- [ ] 22.3 Fjern STDB-skrivestien: portvokteren slutter å skrive til SpacetimeDB. All skriving går kun til PG. NOTIFY-triggere er eneste push-mekanisme. Verifiser at ingenting avhenger av STDB-data. +- [ ] 22.4 Fjern SpacetimeDB: stopp Docker-container, fjern STDB-modul, fjern STDB-klient fra portvokteren og frontend, fjern synkroniseringskode, oppdater docs og CLAUDE.md. +- [ ] 22.5 Opprydding: arkiver STDB-relaterte erfaringsdocs, oppdater alle docs-referanser, fjern Docker-konfig for SpacetimeDB, fjern SpacetimeDB-loven fra feedback-memories.