server/docs/erfaringer/spacetimedb_integrasjon.md
vegard af8f6f97c2 Docs: oppdater alle dokumenter til ny SpacetimeDB-cache-arkitektur
- synkronisering.md: ~5s → ~1s, trådbasert warmup med per-kanal config
- adapter_moenster.md: omskrevet — dokumenterer nåværende arkitektur + historiske anti-patterns
- chat.md: implementeringsstatus oppdatert til mars 2026
- spacetimedb_integrasjon.md: trådbasert warmup og admin-UI dokumentert

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-16 02:48:15 +01:00

4.6 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 — bruk withDatabaseName()
  • onConnect-callback mottar DbConnection, ikke EventContext
  • onConnectError-callback har signatur (ctx, errMessage) der errMessage er 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.tsspacetimeRowToMessage().

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

  1. Worker starter → warmup::run() leser kanaler med config fra PG
  2. Per kanal: sjekker channels.config.warmup_mode (all/messages/days/none)
  3. Kaller clear_channel reducer (unngår duplikater ved restart)
  4. Trådbasert henting: Finner kvalifiserende tråder, henter alle meldinger i disse (komplett med svar)
    • messages-modus: de N nyeste trådene (sortert etter siste aktivitet)
    • days-modus: alle tråder med minst én melding i tidsvinduet
    • Et svar som kvalifiserer tar med hele tråden (inkludert eldre trådstarter)
  5. Kaller load_messages reducer med JSON-array
  6. Laster også reaksjoner via load_reactions reducer

Per-kanal konfigurasjon

  • Lagres i channels.config JSONB: warmup_mode + warmup_value
  • Admin-UI: /admin/channels — tabell med inline-redigering
  • Default: "all" (last alt). Andre: "messages" (siste N tråder), "days" (siste N dager), "none" (inaktiv)

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.