From 17b2c44bddbc6e5f063359b560915939961f4ddf Mon Sep 17 00:00:00 2001 From: vegard Date: Tue, 17 Mar 2026 02:29:43 +0100 Subject: [PATCH] Oppdater docs etter SpacetimeDB-loven-implementering MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit synkronisering.md: oppdater implementeringsstatus, marker PG-lekkasjer som fikset spacetimedb_integrasjon.md: nye lærdommer om timestamps, reducer-params, schema-migrering og AI-worker-flyt Co-Authored-By: Claude Opus 4.6 --- docs/erfaringer/spacetimedb_integrasjon.md | 71 +++++++++++++++++++--- docs/infra/synkronisering.md | 20 +++--- 2 files changed, 71 insertions(+), 20 deletions(-) diff --git a/docs/erfaringer/spacetimedb_integrasjon.md b/docs/erfaringer/spacetimedb_integrasjon.md index 225bb75..a267ac0 100644 --- a/docs/erfaringer/spacetimedb_integrasjon.md +++ b/docs/erfaringer/spacetimedb_integrasjon.md @@ -32,21 +32,36 @@ DbConnection.builder() - `onConnect`-callback mottar `DbConnection`, ikke `EventContext` - `onConnectError`-callback har signatur `(ctx, errMessage)` der `errMessage` er en string -## 3. BigInt-tidsstempler +## 3. Timestamps — bruk SDK-metoder, ikke interne felter -SpacetimeDB `Timestamp`-type bruker `microsSinceEpoch` som er `BigInt`. JavaScript kan ikke blande BigInt med Number: +SpacetimeDB `Timestamp`-objektet har en intern property `__timestamp_micros_since_unix_epoch__` (BigInt). **Ikke bruk den direkte** — bruk SDK-metodene: ```typescript -// FEIL — TypeError: Cannot mix BigInt and other types -const ms = micros / 1000; +// FEIL — microsSinceEpoch finnes ikke, __timestamp_micros_since_unix_epoch__ er internt +const micros = row.createdAt?.microsSinceEpoch; -// RIKTIG — sjekk type, bruk BigInt-divisjon -const ms = typeof micros === 'bigint' - ? Number(micros / 1000n) - : Number(micros) / 1000; +// RIKTIG — bruk SDK-metoder +const iso = row.createdAt?.toISOString(); // "2026-03-15T23:57:11.677139Z" +const date = row.createdAt?.toDate(); // Date-objekt +const ms = row.createdAt?.toDate()?.getTime(); // millisekunder for sortering ``` -**Referanse:** `web/src/lib/chat/spacetime.svelte.ts` — `spacetimeRowToMessage()`. +### Timestamp-parsing i Rust-modul (warmup) + +PG returnerer timestamps som `"2026-03-15 23:57:11.677139+00"`. chrono parser IKKE `+00` — krever `+00:00`: + +```rust +// FEIL — chrono gir ParseError(TooShort) på "+00" +let dt = s.parse::>(); + +// RIKTIG — normaliser PG-offset først +let normalized = if s.ends_with("+00") { format!("{}:00", s) } else { s.to_string() }; +let dt = chrono::DateTime::parse_from_str(&normalized, "%Y-%m-%d %H:%M:%S%.f%:z"); +``` + +Uten korrekt parsing faller `load_messages` tilbake til `ctx.timestamp` (nåtidspunkt), og alle meldinger får samme klokkeslett. + +**Referanse:** `spacetimedb/src/lib.rs` — `parse_timestamp()`, `web/src/lib/chat/spacetime.svelte.ts` — `spacetimeRowToMessage()`. ## 4. Rust-modul — borrow checker med SpacetimeDB-makroer @@ -105,7 +120,8 @@ Ny modell: ### Sync-flyt (ST → PG) - SyncOutbox-events prosesseres hver 1. sekund -- Støtter: `messages/insert`, `messages/delete`, `messages/update`, `message_reactions/insert`, `message_reactions/delete` +- Støtter: `messages/insert`, `messages/delete`, `messages/update`, `messages/ai_update`, `message_reactions/insert`, `message_reactions/delete` +- `ai_update`-action: oppdaterer body + metadata + edited_at i PG, inserter revisjon ## 7. Subscription-begrensninger @@ -130,3 +146,38 @@ Eksempel: ### Fallback PG-polling adapter (`pg.svelte.ts`) brukes kun når SpacetimeDB ikke er konfigurert. Markeres som `readonly: true`. + +## 8. Reducer-parameternavn — unngå underscore-prefix + +SpacetimeDB eksponerer Rust-parameternavn direkte i HTTP JSON API-et. Underscore-prefix (`_workspace_id`) blir til `_workspace_id` i JSON, ikke `workspace_id`: + +```rust +// FEIL — HTTP-kall med {"workspace_id": "..."} feiler med 400 +pub fn set_ai_processing(ctx: &ReducerContext, id: String, _workspace_id: String) { ... } + +// RIKTIG — bruk vanlig navn, suppress warning med let _ = &var; +pub fn set_ai_processing(ctx: &ReducerContext, id: String, workspace_id: String) -> Result<(), String> { + let _ = &workspace_id; + // ... +} +``` + +## 9. Schema-migrering ved nye kolonner + +Å legge til kolonner på eksisterende SpacetimeDB-tabeller krever `--delete-data` ved publish. Dette sletter all data og krever warmup på nytt: + +```bash +# Feiler uten --delete-data: +# "Adding a column metadata to table chat_message requires a default value annotation" +echo "y" | spacetime publish sidelinja-realtime --server local --delete-data +``` + +## 10. AI-worker-flyt via SpacetimeDB + +Worker som gjør AI-behandling av meldinger: +1. Leser meldingens body fra PG (OK — PG er persistent lager) +2. Kaller `set_ai_processing` reducer → frontend ser pulsering umiddelbart +3. Kaller AI Gateway med prompt +4. Kaller `ai_update_message` reducer → SpacetimeDB oppdaterer body/metadata/edited_at atomisk, lagrer revisjon, legger outbox-entry +5. Sync-worker persisterer til PG via `ai_update` action +6. Ved feil: `clear_ai_processing` reducer rydder flagget diff --git a/docs/infra/synkronisering.md b/docs/infra/synkronisering.md index ea6642e..aef0fd5 100644 --- a/docs/infra/synkronisering.md +++ b/docs/infra/synkronisering.md @@ -150,20 +150,20 @@ Skillet er: **data frontend viser** → SpacetimeDB. **Data kun worker trenger** ### Ferdig - **SpacetimeDB som cache foran PG:** PG er autoritativ, SpacetimeDB er varm cache. Frontend snakker kun med SpacetimeDB. -- **SpacetimeDB Rust-modul** (`spacetimedb/src/lib.rs`): `ChatMessage`, `MessageReaction` og `SyncOutbox`-tabeller. Reducers: `send_message`, `delete_message`, `edit_message`, `add_reaction`, `remove_reaction`, `load_messages`, `load_reactions`, `clear_channel`, `mark_synced`. -- **Worker warmup** (`worker/src/warmup.rs`): Ved oppstart lastes siste 100 meldinger + reaksjoner per kanal fra PG → SpacetimeDB. Kanaler ryddes først med `clear_channel` for å unngå duplikater. -- **Worker sync** (`worker/src/sync.rs`): Poller `sync_outbox` hvert 1. sekund. Håndterer insert/delete/update for meldinger og insert/delete for reaksjoner. -- **SpacetimeDB-adapter** (`web/src/lib/chat/spacetime.svelte.ts`): Ren SpacetimeDB-adapter. Ingen PG API-kall. Bruker `onInsert`/`onUpdate`/`onDelete` callbacks for sanntid. Reaksjoner bygges fra `message_reaction`-tabellen. +- **SpacetimeDB Rust-modul** (`spacetimedb/src/lib.rs`): `ChatMessage` (med `metadata`, `edited_at`), `MessageReaction`, `MessageRevision` og `SyncOutbox`-tabeller. Reducers: `send_message`, `delete_message`, `edit_message`, `add_reaction`, `remove_reaction`, `load_messages`, `load_reactions`, `load_revisions`, `clear_channel`, `mark_synced`, `set_ai_processing`, `clear_ai_processing`, `ai_update_message`. +- **Worker warmup** (`worker/src/warmup.rs`): Ved oppstart lastes meldinger (med metadata/edited_at), reaksjoner og revisjoner per kanal fra PG → SpacetimeDB. Originale timestamps parses korrekt. Kanaler ryddes først med `clear_channel` for å unngå duplikater. +- **Worker sync** (`worker/src/sync.rs`): Poller `sync_outbox` hvert 1. sekund. Håndterer insert/delete/update/ai_update for meldinger og insert/delete for reaksjoner. +- **SpacetimeDB-adapter** (`web/src/lib/chat/spacetime.svelte.ts`): Ren SpacetimeDB-adapter. Null PG API-kall (`fetch()`). Bruker `onInsert`/`onUpdate`/`onDelete` callbacks for sanntid. Reaksjoner bygges fra `message_reaction`-tabellen. Revisjoner leses fra `message_revision`-tabellen. - **PG-fallback** (`web/src/lib/chat/pg.svelte.ts`): Brukes kun når SpacetimeDB ikke er konfigurert. Markert som `readonly: true`. - **Adapter-mønster:** `ChatConnection`-interface med `send`, `edit`, `delete`, `react` metoder. Factory velger basert på env-variabel. -### Gjenstår — brudd på SpacetimeDB-loven som må fikses -- **`enrichFromPg` i spacetime-adapteren** — frontend leser metadata/edited_at fra PG fordi SpacetimeDB-modulen mangler disse feltene. MÅ fjernes ved å utvide SpacetimeDB-modulen. -- **Worker AI-vask skriver til PG direkte** — metadata, body og revisjoner skrives til PG. MÅ endres til å skrive til SpacetimeDB via reducers. -- **Revisjoner kun i PG** — `message_revisions`-tabell finnes kun i PG. MÅ speiles i SpacetimeDB-modulen. -- **`ChatMessage` mangler felter** — `metadata` (jsonb), `edited_at` (timestamp) mangler i SpacetimeDB-modulen. +### Fikset (mars 2026) +- **`enrichFromPg` fjernet** — SpacetimeDB-modulen har nå `metadata` og `edited_at` på `ChatMessage`. Frontend leser alt fra SpacetimeDB, null `fetch()`-kall i adapteren. +- **Worker AI-vask via reducers** — `set_ai_processing`, `ai_update_message` og `clear_ai_processing` reducers erstatter direkte PG-skriving. Sync-worker persisterer til PG via `ai_update` outbox-action. +- **Revisjoner i SpacetimeDB** — `MessageRevision`-tabell med `load_revisions` warmup-reducer. Frontend leser revisjoner via `getRevisions()` fra `conn.db.message_revision`. +- **Warmup med metadata** — `load_messages` inkluderer `metadata`, `edited_at` og parser originale timestamps korrekt. -### Gjenstår — andre +### Gjenstår - **Workspace-partisjonering (§7):** SpacetimeDB-modulen har `workspace_id`-felt men bruker ikke workspace-token på tilkobling ennå. - **Pin/konvertering via SpacetimeDB:** Pin og kanban/kalender-konvertering går fortsatt direkte til PG API. - **Lazy warmup per kanal:** Alle aktive kanaler oppvarmes ved oppstart. Kan optimaliseres til per-kanal ved tilkobling.