更新 SpacetimeDB CLI、概念和 Rust 模块 skill 到 2.5 口径 删除 TypeScript、C# 和 Unity SpacetimeDB 本地 skill 同步 AGENTS 与 Hermes 决策记录中的 skill 维护范围 补充 2.2.0 到 2.5.0 项目相关差异和 event table 规则
106 lines
7.2 KiB
Markdown
106 lines
7.2 KiB
Markdown
---
|
|
name: spacetimedb-concepts
|
|
description: Understand SpacetimeDB 2.5 architecture, reducer/procedure/table/view semantics, schema evolution, subscriptions, identity, and Genarrative-specific backend boundaries. Use when designing or reviewing SpacetimeDB-backed features.
|
|
---
|
|
|
|
# SpacetimeDB Core Concepts
|
|
|
|
SpacetimeDB is a relational database that also executes application logic in uploaded modules. In Genarrative, it is the data and transaction layer behind `server-rs + Axum + SpacetimeDB`, not a replacement for the `api-server` BFF or external platform adapters.
|
|
|
|
## Genarrative Boundaries
|
|
|
|
- Domain rules live in `module-*`.
|
|
- SpacetimeDB tables, reducers, procedures, migrations, row mappers, and read models live in `spacetime-module`.
|
|
- Backend access goes through `spacetime-client` facades.
|
|
- HTTP/SSE/BFF and external orchestration stay in `api-server`.
|
|
- External side effects stay in `platform-*`.
|
|
- Frontend renders backend truth and must not bypass BFF/projections to invent formal business state.
|
|
|
|
## Critical Rules
|
|
|
|
1. **Reducers are transactional**: they do not return data to callers. Read through subscriptions, read models, views, or BFF endpoints.
|
|
2. **Reducers are deterministic**: no filesystem, network, wall-clock, or external RNG. Use `ctx.timestamp`, `ctx.rng()` / `ctx.random()`, and tables.
|
|
3. **Procedures are stable in 2.5**: they can use explicit transactions and outgoing HTTP via `ctx.http`.
|
|
4. **Identity comes from context**: use `ctx.sender()` or language equivalent for authorization. Never trust identity passed as an argument.
|
|
5. **Auto-increment IDs are not ordering guarantees**: gaps are normal. Use timestamps or explicit sequence columns for ordering.
|
|
6. **Schema changes need migration discipline**: existing Genarrative table fields must be appended with defaults; update migration code, table catalog, generated bindings, and run `npm run check:spacetime-schema`.
|
|
|
|
## Tables
|
|
|
|
- Private tables are the default; only reducers/procedures and database owners can access them.
|
|
- Public tables are exposed to clients through subscriptions. Writes still go through reducers/procedures.
|
|
- Organize data by access pattern when bandwidth or update frequency differs.
|
|
- Existing persistent tables in Genarrative are conservative: no rename, delete, reorder, or type changes without a user-approved migration plan.
|
|
|
|
## Reducers
|
|
|
|
Reducers are deterministic transactional functions. They are the primary client-invoked mutation path.
|
|
|
|
- No global mutable state.
|
|
- No filesystem, network, timers, or non-deterministic RNG.
|
|
- Return `Result<(), String>` for expected sender-visible errors.
|
|
- Use `ctx.sender()` for authorization.
|
|
- Store persistent state in tables.
|
|
|
|
## Procedures
|
|
|
|
Procedures are stable in 2.5. They can be scheduled, can open explicit transactions with `with_tx` / `try_with_tx`, and can use outgoing HTTP (`ctx.http`).
|
|
|
|
Genarrative default: keep external provider protocols in `platform-*` and orchestration in `api-server` unless a task explicitly moves a workflow into a module procedure.
|
|
|
|
Module HTTP handlers/webhooks, unstable view features, and RLS `client_visibility_filter` remain gated behind unstable according to the 2.5 release notes.
|
|
|
|
## Views
|
|
|
|
Views expose computed read-only data. In 2.4.1 Rust and TypeScript gained primary key support for procedural views; in 2.5 C# gained the same. Clients can receive `OnUpdate` events when subscribed to such views with primary keys. Ensure the view never returns duplicate primary keys, because that can fail view refresh and roll back the triggering transaction.
|
|
|
|
## Event Tables
|
|
|
|
Event tables broadcast reducer/procedure-specific facts to subscribers and must be subscribed explicitly. They are excluded from `subscribe_to_all_tables()`.
|
|
|
|
2.5 adds broader layout-altering automigrations for event tables, including column removal, reordering, and type changes that regular tables reject. This relaxed migration behavior is for event-only tables, not persistent tables.
|
|
|
|
Event-table primary keys and constraints are transaction-scoped. They can reject duplicate event rows within one transaction, but event rows are not retained in client cache, so clients observe event tables through insert callbacks only. Do not design Genarrative event tables around `OnUpdate` / `on_update` / `onUpdate`; use a persistent table or a primary-keyed procedural view when update callbacks are required.
|
|
|
|
Official 2.4.1/2.5 release notes document primary-key-backed update callbacks for procedural views, not event tables.
|
|
|
|
## Subscriptions
|
|
|
|
1. Subscribe to SQL queries or generated table/query builders.
|
|
2. Receive initial matching rows.
|
|
3. Receive updates when subscribed rows change.
|
|
4. Render from subscribed data, not reducer return values.
|
|
|
|
Best practices:
|
|
|
|
- Group subscriptions by lifetime.
|
|
- Subscribe to new data before unsubscribing old data during transitions.
|
|
- Avoid overlapping queries that duplicate row delivery.
|
|
- Use indexes for subscribed filters.
|
|
|
|
## 2.2.0 to 2.5.0 Delta
|
|
|
|
Genarrative introduced SpacetimeDB around 2.2.0. Important changes since then:
|
|
|
|
- **2.2.0**: v3 WebSocket transport and TS SDK default, safer production operations (`lock`/`unlock`, safer `delete`, better `publish --yes`), TS React `useProcedure`, table clearing APIs, empty-table drop automigration, primary-key migration fixes, bytes-key B-tree support, durability hardening.
|
|
- **2.3.0**: first-party Godot SDK, more WebSocket pipelining/batching, HTTP/2 backend support, Vue `useProcedure`, Unity 6 WebGL support, commitlog compression/throughput improvements, Rust `DbContext` generics, `ReducerContext::identity` deprecated in favor of `database_identity`, connection lifecycle and unsubscribe fixes.
|
|
- **2.4.0**: unstable module HTTP handlers/webhooks, faster synchronous WASM reducer runtime, commitlog resume truncation fix for silent data loss risk, better commitlog decode context, V8 heap metrics for procedure workers, JS execution-time billing regression reverted.
|
|
- **2.4.1**: Rust and TypeScript procedural views can declare primary keys, enabling `OnUpdate` events for subscribed views; fixed index schema from ST tables.
|
|
- **2.5.0**: procedures are stable, C# procedural views gain primary keys, event tables allow broader layout-altering automigrations, BTreeSet storage makes row insertion deterministic and avoids accidentally quadratic bulk insert behavior, `wasm_memory_bytes` billing metric semantics changed, template version constraints unified, `publish --delete-data` config fallback fixed, CLI `call` accepts hex Identity arguments.
|
|
|
|
## Debugging Checklist
|
|
|
|
1. Is the Genarrative SpacetimeDB server running? Use `npm run dev:spacetime` locally or host-local `systemctl`.
|
|
2. Is the module published to the same server the API uses?
|
|
3. Are generated bindings current? Use `npm run spacetime:generate`.
|
|
4. Is `api-server` using the same database and token?
|
|
5. Is the reducer/procedure actually called?
|
|
6. Did `/healthz` / `/readyz` pass while business SpacetimeDB calls still timeout? Inspect API logs and public route behavior.
|
|
|
|
## Editing Behavior
|
|
|
|
- Make the smallest change necessary.
|
|
- Do not invent SpacetimeDB APIs; verify against current docs, generated bindings, or source.
|
|
- For Genarrative schema edits, update migration code, table catalog/docs, generated bindings, and relevant tests.
|
|
- After schema edits, run `npm run spacetime:generate` and `npm run check:spacetime-schema`.
|