PG er autoritativ, SpacetimeDB er varm cache. Frontend snakker kun med SpacetimeDB, worker håndterer toveissynk. Fase 1 — SpacetimeDB-modul: - delete_message med SyncOutbox-event - edit_message reducer - MessageReaction tabell + add/remove_reaction reducers - load_messages med JSON-parsing (erstatter pipe-format) - clear_channel reducer for duplikat-fri warmup - load_reactions reducer Fase 2 — Worker: - warmup.rs: PG→ST oppvarming ved oppstart (100 msg/kanal) - sync.rs: håndter delete/update/reaction actions - Sync-intervall redusert til 1s Fase 3 — Frontend: - spacetime.svelte.ts: ren SpacetimeDB-adapter, ingen PG-hybrid - ChatConnection interface med edit/delete/react metoder - ChatBlock bruker chat.edit/delete/react direkte - PG-adapter som readonly fallback Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
4 KiB
Erfaring: SpacetimeDB-integrasjon
1. Genererte bindings — navnekonvensjoner
SpacetimeDB sin spacetime generate --lang typescript produserer bindings med inkonsistente konvensjoner. Sjekk alltid de genererte filene i stedet for å gjette.
| Hva | Konvensjon | Eksempel |
|---|---|---|
Tabell-accessor på conn.db |
snake_case | conn.db.chat_message |
Reducer-accessor på conn.reducers |
camelCase | conn.reducers.sendMessage() |
| Felt-navn i tabellrader | camelCase | row.channelId, row.authorName |
| Reducer-parametere | Enkelt objekt | sendMessage({ id, channelId, body, ... }) |
Felle: Man forventer at tabell-accessorer er camelCase (chatMessage), men de er snake_case.
2. DbConnection.builder() — API-detaljer (SDK v2)
DbConnection.builder()
.withUri(spacetimeUrl)
.withDatabaseName(moduleName) // IKKE withModuleName
.withToken(token)
.onConnect((connection) => { // første param er DbConnection, ikke EventContext
connection.subscriptionBuilder()
.subscribe([`SELECT * FROM chat_message WHERE channel_id = '${id}'`]);
})
.build();
Feller:
withModuleName()finnes ikke — brukwithDatabaseName()onConnect-callback mottarDbConnection, ikkeEventContextonConnectError-callback har signatur(ctx, errMessage)dererrMessageer en string
3. BigInt-tidsstempler
SpacetimeDB Timestamp-type bruker microsSinceEpoch som er BigInt. JavaScript kan ikke blande BigInt med Number:
// FEIL — TypeError: Cannot mix BigInt and other types
const ms = micros / 1000;
// RIKTIG — sjekk type, bruk BigInt-divisjon
const ms = typeof micros === 'bigint'
? Number(micros / 1000n)
: Number(micros) / 1000;
Referanse: web/src/lib/chat/spacetime.svelte.ts — spacetimeRowToMessage().
4. Rust-modul — borrow checker med SpacetimeDB-makroer
SpacetimeDB-makroer genererer kode som tar eierskap over struct-felter. Bruk verdier før du flytter dem inn i structs:
// FEIL — channel_id er moved inn i ChatMessage, kan ikke bruke i log!() etterpå
let msg = ChatMessage { channel_id, body, ... };
log::info!("Melding i kanal {}", channel_id); // borrow after move
// RIKTIG — bruk verdien før struct-opprettelse
let log_msg = format!("Melding i kanal {}", channel_id);
let msg = ChatMessage { channel_id, body, ... };
log::info!("{}", log_msg);
5. Publisering og lokal testing
# Publiser modul mot lokal SpacetimeDB (må kjøre i Docker først)
cd spacetimedb
spacetime publish sidelinja-realtime --server local
# Generer TypeScript-bindings
spacetime generate --lang typescript --out-dir ../web/src/lib/chat/module_bindings \
--module-path .
Merk: Bruk ./dev.sh for å starte hele stacken automatisk (inkl. SpacetimeDB publish + binding-generering). ./dev.sh --clean starter blankt.
6. Arkitekturendring: SpacetimeDB som cache foran PG (mars 2026)
Tidligere: Hybrid-adapter der frontend merget data fra PG (historikk) og SpacetimeDB (sanntid) med dedup, deletedIds og BigInt-workarounds.
Ny modell:
- PG autoritativ — all persistent data i PostgreSQL
- SpacetimeDB = varm cache — worker gjør warmup (PG → ST) ved oppstart
- Frontend snakker KUN med ST — ingen PG API-kall fra chat-adapteren
- Worker håndterer toveissynk — ST → PG for nye/redigerte/slettede meldinger og reaksjoner
Warmup-flyt
- Worker starter →
warmup::run()leser siste N meldinger per kanal fra PG - Kaller
clear_channelreducer per kanal (unngår duplikater ved restart) - Kaller
load_messagesreducer med JSON-array (ikke pipe-separert format) - Laster også reaksjoner via
load_reactionsreducer
Sync-flyt (ST → PG)
- SyncOutbox-events prosesseres hver 1. sekund
- Støtter:
messages/insert,messages/delete,messages/update,message_reactions/insert,message_reactions/delete
Fallback
PG-polling adapter (pg.svelte.ts) brukes kun når SpacetimeDB ikke er konfigurert. Markeres som readonly: true.