spacetimedb-concepts
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChineseSpacetimeDB Core Concepts
SpacetimeDB核心概念
SpacetimeDB is a relational database that is also a server. It lets you upload application logic directly into the database via WebAssembly modules, eliminating the traditional web/game server layer entirely.
SpacetimeDB是一款兼具服务器功能的关系型数据库。它允许你通过WebAssembly模块将应用逻辑直接上传至数据库中,彻底省去了传统的Web/游戏服务器层。
Critical Rules (Read First)
重要规则(请先阅读)
These five rules prevent the most common SpacetimeDB mistakes:
- Reducers are transactional — they do not return data to callers. Use subscriptions to read data.
- Reducers must be deterministic — no filesystem, network, timers, or random. All state must come from tables.
- Read data via tables/subscriptions — not reducer return values. Clients get data through subscribed queries.
- Auto-increment IDs are not sequential — gaps are normal, do not use for ordering. Use timestamps or explicit sequence columns.
- is the authenticated principal — never trust identity passed as arguments. Always use
ctx.senderfor authorization.ctx.sender
以下五条规则可避免最常见的SpacetimeDB使用错误:
- Reducer是事务性的——它们不会向调用者返回数据。请使用订阅(subscriptions)来读取数据。
- Reducer必须是确定性的——不能访问文件系统、网络、定时器,也不能生成随机数。所有状态必须来自数据表。
- 通过数据表/订阅读取数据——不要依赖Reducer的返回值。客户端通过已订阅的查询获取数据。
- 自增ID并非连续的——出现间隙是正常现象,请勿将其用于排序。请使用时间戳或显式的序列列。
- 是已认证的主体——永远不要信任作为参数传递的身份信息。请始终使用
ctx.sender进行授权验证。ctx.sender
Feature Implementation Checklist
功能实现检查清单
When implementing a feature that spans backend and client:
- Backend: Define table(s) to store the data
- Backend: Define reducer(s) to mutate the data
- Client: Subscribe to the table(s)
- Client: Call the reducer(s) from UI — do not skip this step
- Client: Render the data from the table(s)
Common mistake: Building backend tables/reducers but forgetting to wire up the client to call them.
当实现跨后端与客户端的功能时:
- 后端:定义用于存储数据的数据表
- 后端:定义用于修改数据的Reducer
- 客户端:订阅对应的数据表
- 客户端:从UI调用Reducer——请勿跳过此步骤
- 客户端:渲染来自数据表的数据
常见错误:构建了后端数据表/Reducer,但忘记在客户端中调用它们。
Debugging Checklist
调试检查清单
When things are not working:
- Is SpacetimeDB server running? ()
spacetime start - Is the module published? ()
spacetime publish - Are client bindings generated? ()
spacetime generate - Check server logs for errors ()
spacetime logs <db-name> - Is the reducer actually being called from the client?
当功能无法正常工作时:
- SpacetimeDB服务器是否在运行?(执行)
spacetime start - 模块是否已发布?(执行)
spacetime publish - 是否生成了客户端绑定?(执行)
spacetime generate - 检查服务器日志中的错误(执行)
spacetime logs <db-name> - 客户端是否实际调用了Reducer?
CLI Commands
CLI命令
bash
undefinedbash
undefinedStart local SpacetimeDB
启动本地SpacetimeDB
spacetime start
spacetime start
Publish module
发布模块
spacetime publish <db-name> --project-path <module-path>
spacetime publish <db-name> --project-path <module-path>
Clear and republish
清空并重新发布
spacetime publish <db-name> --clear-database -y --project-path <module-path>
spacetime publish <db-name> --clear-database -y --project-path <module-path>
Generate client bindings
生成客户端绑定
spacetime generate --lang <lang> --out-dir <out> --project-path <module-path>
spacetime generate --lang <lang> --out-dir <out> --project-path <module-path>
View logs
查看日志
spacetime logs <db-name>
---spacetime logs <db-name>
---What SpacetimeDB Is
SpacetimeDB是什么
SpacetimeDB combines a database and application server into a single deployable unit. Clients connect directly to the database and execute application logic inside it. The system is optimized for real-time applications requiring maximum speed and minimum latency.
Key characteristics:
- In-memory execution: All application state lives in memory for sub-millisecond access
- Persistent storage: Data is automatically persisted to a write-ahead log (WAL) for durability
- Real-time synchronization: Changes are automatically pushed to subscribed clients
- Single deployment: No separate servers, containers, or infrastructure to manage
SpacetimeDB powers BitCraft Online, an MMORPG where the entire game backend (chat, items, resources, terrain, player positions) runs as a single SpacetimeDB module.
SpacetimeDB将数据库与应用服务器整合为一个可部署的独立单元。客户端直接连接到数据库,并在其中执行应用逻辑。该系统针对需要极致速度与最低延迟的实时应用进行了优化。
核心特性:
- 内存内执行:所有应用状态都存储在内存中,实现亚毫秒级访问
- 持久化存储:数据会自动持久化到预写日志(WAL)中,确保数据耐用性
- 实时同步:数据变化会自动推送给已订阅的客户端
- 单一部署单元:无需管理单独的服务器、容器或基础设施
SpacetimeDB为MMORPG游戏BitCraft Online提供支持,该游戏的整个后端(聊天、物品、资源、地形、玩家位置)都作为单个SpacetimeDB模块运行。
The Five Zen Principles
五大核心原则
SpacetimeDB is built on five core principles that guide both development and usage:
-
Everything is a Table: Your entire application state lives in tables. No separate cache layer, no Redis, no in-memory state to synchronize. The database IS your state.
-
Everything is Persistent: SpacetimeDB persists everything by default, including full history. Persistence only increases latency, never decreases throughput. Modern SSDs can write 15+ GB/s.
-
Everything is Real-Time: Clients are replicas of server state. Subscribe to data and it flows automatically. No polling, no fetching.
-
Everything is Transactional: Every reducer runs atomically. Either all changes succeed or all roll back. No partial updates, no corrupted state.
-
Everything is Programmable: Modules are real code (Rust, C#, TypeScript) running inside the database. Full Turing-complete power for any logic.
SpacetimeDB基于五大核心原则构建,指导其开发与使用:
-
一切皆为数据表:你的整个应用状态都存储在数据表中。无需单独的缓存层、Redis或需要同步的内存状态。数据库就是你的状态。
-
一切皆可持久化:SpacetimeDB默认持久化所有数据,包括完整历史记录。持久化只会增加延迟,绝不会降低吞吐量。现代SSD的写入速度可达15GB/s以上。
-
一切皆为实时:客户端是服务器状态的副本。订阅数据后,数据会自动同步。无需轮询或主动获取。
-
一切皆为事务性:每个Reducer都以原子方式运行。要么所有更改都成功,要么全部回滚。不会出现部分更新或损坏的状态。
-
一切皆可编程:模块是运行在数据库内部的真实代码(Rust、C#、TypeScript)。拥有图灵完备的能力,可实现任何逻辑。
Tables
数据表
Tables store all data in SpacetimeDB. They use the relational model and support SQL queries for subscriptions.
数据表存储SpacetimeDB中的所有数据。它们采用关系模型,并支持通过SQL查询进行订阅。
Defining Tables
定义数据表
Tables are defined using language-specific attributes:
Rust:
rust
#[spacetimedb::table(name = player, public)]
pub struct Player {
#[primary_key]
#[auto_inc]
id: u32,
#[index(btree)]
name: String,
#[unique]
email: String,
}C#:
csharp
[SpacetimeDB.Table(Name = "Player", Public = true)]
public partial struct Player
{
[SpacetimeDB.PrimaryKey]
[SpacetimeDB.AutoInc]
public uint Id;
[SpacetimeDB.Index.BTree]
public string Name;
[SpacetimeDB.Unique]
public string Email;
}TypeScript:
typescript
const players = table(
{ name: 'players', public: true },
{
id: t.u32().primaryKey().autoInc(),
name: t.string().index('btree'),
email: t.string().unique(),
}
);数据表使用特定语言的属性进行定义:
Rust:
rust
#[spacetimedb::table(name = player, public)]
pub struct Player {
#[primary_key]
#[auto_inc]
id: u32,
#[index(btree)]
name: String,
#[unique]
email: String,
}C#:
csharp
[SpacetimeDB.Table(Name = "Player", Public = true)]
public partial struct Player
{
[SpacetimeDB.PrimaryKey]
[SpacetimeDB.AutoInc]
public uint Id;
[SpacetimeDB.Index.BTree]
public string Name;
[SpacetimeDB.Unique]
public string Email;
}TypeScript:
typescript
const players = table(
{ name: 'players', public: true },
{
id: t.u32().primaryKey().autoInc(),
name: t.string().index('btree'),
email: t.string().unique(),
}
);Table Visibility
数据表可见性
- Private tables (default): Only accessible by reducers and the database owner
- Public tables: Exposed for client read access through subscriptions. Writes still require reducers.
- 私有数据表(默认):仅Reducer和数据库所有者可访问
- 公共数据表:允许客户端通过订阅进行读取访问。写入仍需通过Reducer完成。
Table Design Principles
数据表设计原则
Organize data by access pattern, not by entity:
Decomposed approach (recommended):
Player PlayerState PlayerStats
id <-- player_id player_id
name position_x total_kills
position_y total_deaths
velocity_x play_time
velocity_yBenefits:
- Reduced bandwidth (clients subscribing to positions do not receive settings updates)
- Cache efficiency (similar update frequencies in contiguous memory)
- Schema evolution (add columns without affecting other tables)
- Semantic clarity (each table has single responsibility)
根据访问模式而非实体来组织数据:
推荐的分解方式:
Player PlayerState PlayerStats
id <-- player_id player_id
name position_x total_kills
position_y total_deaths
velocity_x play_time
velocity_y优势:
- 减少带宽消耗(订阅位置的客户端不会收到设置更新)
- 缓存效率更高(更新频率相似的数据存储在连续内存中)
- 架构演进更灵活(添加列不会影响其他数据表)
- 语义更清晰(每个数据表只有单一职责)
Reducers
Reducer
Reducers are transactional functions that modify database state. They are the ONLY way to mutate tables in SpacetimeDB.
Reducer是用于修改数据库状态的事务性函数。它们是SpacetimeDB中唯一可以修改数据表的方式。
Key Properties
核心属性
- Transactional: Run in isolated database transactions
- Atomic: Either all changes succeed or all roll back
- Isolated: Cannot interact with the outside world (no network, no filesystem)
- Callable: Clients invoke reducers as remote procedure calls
- 事务性:在隔离的数据库事务中运行
- 原子性:要么所有更改都成功,要么全部回滚
- 隔离性:无法与外部世界交互(无网络请求、无文件系统访问)
- 可调用:客户端可将Reducer作为远程过程调用(RPC)来调用
Critical Reducer Rules
Reducer重要规则
- No global state: Relying on static variables is undefined behavior
- No side effects: Reducers cannot make network requests or access files
- Store state in tables: All persistent state must be in tables
- No return data: Reducers do not return data to callers — use subscriptions
- Must be deterministic: No random, no timers, no external I/O
- 无全局状态:依赖静态变量会导致未定义行为
- 无副作用:Reducer不能发起网络请求或访问文件
- 状态存储在数据表中:所有持久化状态必须存储在数据表中
- 不返回数据:Reducer不会向调用者返回数据——请使用订阅
- 必须是确定性的:不能生成随机数、不能使用定时器、不能进行外部I/O
Defining Reducers
定义Reducer
Rust:
rust
#[spacetimedb::reducer]
pub fn create_user(ctx: &ReducerContext, name: String, email: String) -> Result<(), String> {
if name.is_empty() {
return Err("Name cannot be empty".to_string());
}
ctx.db.user().insert(User { id: 0, name, email });
Ok(())
}C#:
csharp
[SpacetimeDB.Reducer]
public static void CreateUser(ReducerContext ctx, string name, string email)
{
if (string.IsNullOrEmpty(name))
throw new ArgumentException("Name cannot be empty");
ctx.Db.User.Insert(new User { Id = 0, Name = name, Email = email });
}Rust:
rust
#[spacetimedb::reducer]
pub fn create_user(ctx: &ReducerContext, name: String, email: String) -> Result<(), String> {
if name.is_empty() {
return Err("Name cannot be empty".to_string());
}
ctx.db.user().insert(User { id: 0, name, email });
Ok(())
}C#:
csharp
[SpacetimeDB.Reducer]
public static void CreateUser(ReducerContext ctx, string name, string email)
{
if (string.IsNullOrEmpty(name))
throw new ArgumentException("Name cannot be empty");
ctx.Db.User.Insert(new User { Id = 0, Name = name, Email = email });
}ReducerContext
ReducerContext
Every reducer receives a providing:
ReducerContext- : Access to all tables (read and write)
ctx.db - : The Identity of the caller (use this for authorization, never trust args)
ctx.sender - : The connection ID of the caller
ctx.connection_id - : The current timestamp
ctx.timestamp
每个Reducer都会收到一个,提供以下功能:
ReducerContext- :访问所有数据表(读/写)
ctx.db - :调用者的身份信息(请使用此信息进行授权,永远不要信任参数)
ctx.sender - :调用者的连接ID
ctx.connection_id - :当前时间戳
ctx.timestamp
Subscriptions
订阅
Subscriptions replicate database rows to clients in real-time. When you subscribe to a query, SpacetimeDB sends matching rows immediately and pushes updates whenever those rows change.
订阅功能可将数据库行实时复制到客户端。当你订阅某个查询时,SpacetimeDB会立即发送匹配的行,并在这些行发生变化时推送更新。
How Subscriptions Work
订阅工作原理
- Subscribe: Register SQL queries describing needed data
- Receive initial data: All matching rows are sent immediately
- Receive updates: Real-time updates when subscribed rows change
- React to changes: Use callbacks (,
onInsert,onDelete) to handle changesonUpdate
- 订阅:注册描述所需数据的SQL查询
- 接收初始数据:立即发送所有匹配的行
- 接收更新:当订阅的行发生变化时,实时推送更新
- 响应变化:使用回调函数(、
onInsert、onDelete)处理变化onUpdate
Client-Side Usage
客户端使用示例
TypeScript:
typescript
const conn = DbConnection.builder()
.withUri('wss://maincloud.spacetimedb.com')
.withModuleName('my_module')
.onConnect((ctx) => {
ctx.subscriptionBuilder()
.onApplied(() => console.log('Subscription ready!'))
.subscribe(['SELECT * FROM user', 'SELECT * FROM message']);
})
.build();
// React to changes
conn.db.user.onInsert((ctx, user) => console.log(`New user: ${user.name}`));
conn.db.user.onDelete((ctx, user) => console.log(`User left: ${user.name}`));
conn.db.user.onUpdate((ctx, old, new_) => console.log(`${old.name} -> ${new_.name}`));TypeScript:
typescript
const conn = DbConnection.builder()
.withUri('wss://maincloud.spacetimedb.com')
.withModuleName('my_module')
.onConnect((ctx) => {
ctx.subscriptionBuilder()
.onApplied(() => console.log('Subscription ready!'))
.subscribe(['SELECT * FROM user', 'SELECT * FROM message']);
})
.build();
// 响应变化
conn.db.user.onInsert((ctx, user) => console.log(`New user: ${user.name}`));
conn.db.user.onDelete((ctx, user) => console.log(`User left: ${user.name}`));
conn.db.user.onUpdate((ctx, old, new_) => console.log(`${old.name} -> ${new_.name}`));Subscription Best Practices
订阅最佳实践
- Group subscriptions by lifetime: Keep always-needed data separate from temporary subscriptions
- Subscribe before unsubscribing: When updating subscriptions, subscribe to new data first
- Avoid overlapping queries: Distinct queries returning overlapping data cause redundant processing
- Use indexes: Queries on indexed columns are efficient; full table scans are expensive
- 按生命周期分组订阅:将始终需要的数据与临时订阅分开
- 先订阅再取消订阅:更新订阅时,先订阅新数据
- 避免重叠查询:返回重叠数据的不同查询会导致冗余处理
- 使用索引:针对索引列的查询效率更高;全表扫描开销很大
Modules
模块
Modules are WebAssembly bundles containing application logic that runs inside the database.
模块是包含应用逻辑的WebAssembly包,运行在数据库内部。
Module Components
模块组件
- Tables: Define the data schema
- Reducers: Define callable functions that modify state
- Views: Define read-only computed queries
- Procedures: (Beta) Functions that can have side effects (HTTP requests)
- 数据表:定义数据架构
- Reducer:定义可调用的状态修改函数
- 视图:定义只读的计算查询
- 过程(测试版):可产生副作用的函数(如HTTP请求)
Module Languages
模块支持的语言
Server-side modules can be written in:
- Rust
- C#
- TypeScript (beta)
服务器端模块可使用以下语言编写:
- Rust
- C#
- TypeScript(测试版)
Module Lifecycle
模块生命周期
- Write: Define tables and reducers in your chosen language
- Compile: Build to WebAssembly using the SpacetimeDB CLI
- Publish: Upload to a SpacetimeDB host with
spacetime publish - Hot-swap: Republish to update code without disconnecting clients
- 编写:使用你选择的语言定义数据表和Reducer
- 编译:使用SpacetimeDB CLI构建为WebAssembly
- 发布:使用上传至SpacetimeDB主机
spacetime publish - 热替换:重新发布以更新代码,无需断开客户端连接
Identity
身份认证
Identity is SpacetimeDB's authentication system based on OpenID Connect (OIDC).
身份认证是SpacetimeDB基于OpenID Connect(OIDC)的认证系统。
Identity Concepts
身份认证概念
- Identity: A long-lived, globally unique identifier for a user. Derived from OIDC issuer and subject claims.
- ConnectionId: Identifies a specific client connection. A user may have multiple connections.
- Identity:用户的长期全局唯一标识符。由OIDC发行方和主体声明派生而来。
- ConnectionId:标识特定的客户端连接。一个用户可以拥有多个连接。
Identity in Reducers
Reducer中的身份认证
rust
#[spacetimedb::reducer]
pub fn do_something(ctx: &ReducerContext) {
let caller_identity = ctx.sender; // Who is calling this reducer?
// Use identity for authorization checks
// NEVER trust identity passed as a reducer argument
}rust
#[spacetimedb::reducer]
pub fn do_something(ctx: &ReducerContext) {
let caller_identity = ctx.sender; // 谁在调用这个Reducer?
// 使用身份信息进行授权检查
// 永远不要信任作为Reducer参数传递的身份信息
}Authentication Providers
认证提供商
SpacetimeDB works with any OIDC provider:
- SpacetimeAuth: Built-in managed provider (simple, production-ready)
- Third-party: Auth0, Clerk, Keycloak, Google, GitHub, etc.
SpacetimeDB可与任何OIDC提供商配合使用:
- SpacetimeAuth:内置的托管提供商(简单、可用于生产环境)
- 第三方提供商:Auth0、Clerk、Keycloak、Google、GitHub等
SATS (SpacetimeDB Algebraic Type System)
SATS(SpacetimeDB代数类型系统)
SATS is the type system and serialization format used throughout SpacetimeDB.
SATS是SpacetimeDB使用的类型系统和序列化格式。
Core Types
核心类型
| Category | Types |
|---|---|
| Primitives | |
| Composite | |
| Collections | |
| Special | |
| 类别 | 类型 |
|---|---|
| 基本类型 | |
| 复合类型 | |
| 集合类型 | |
| 特殊类型 | |
Serialization Formats
序列化格式
- BSATN: Binary format for module-host communication and row storage
- SATS-JSON: JSON format for HTTP API and WebSocket text protocol
- BSATN:用于模块与主机通信以及行存储的二进制格式
- SATS-JSON:用于HTTP API和WebSocket文本协议的JSON格式
Type Compatibility
类型兼容性
Types must implement to be used in tables and reducers. This is automatic for primitive types and structs using the appropriate attributes.
SpacetimeType类型必须实现才能在数据表和Reducer中使用。对于基本类型和使用适当属性定义的结构体,这是自动完成的。
SpacetimeTypeClient-Server Data Flow
客户端-服务器数据流
Write Path (Client to Database)
写入路径(客户端到数据库)
- Client calls reducer (e.g., )
ctx.reducers.createUser("Alice") - Request sent over WebSocket to SpacetimeDB host
- Host validates identity and executes reducer in transaction
- On success, changes are committed; on error, all changes roll back
- Subscribed clients receive updates for affected rows
- 客户端调用Reducer(例如)
ctx.reducers.createUser("Alice") - 请求通过WebSocket发送至SpacetimeDB主机
- 主机验证身份并在事务中执行Reducer
- 成功则提交更改;失败则所有更改回滚
- 已订阅的客户端收到受影响行的更新
Read Path (Database to Client)
读取路径(数据库到客户端)
- Client subscribes with SQL queries (e.g., )
SELECT * FROM user - Server evaluates query and sends matching rows
- Client maintains local cache of subscribed data
- When subscribed data changes, server pushes delta updates
- Client cache is automatically updated; callbacks fire
- 客户端使用SQL查询进行订阅(例如)
SELECT * FROM user - 服务器执行查询并发送匹配的行
- 客户端维护订阅数据的本地缓存
- 当订阅的数据发生变化时,服务器推送增量更新
- 客户端缓存自动更新;触发回调函数
Data Flow Diagram
数据流图
┌─────────────────────────────────────────────────────────┐
│ CLIENT │
│ ┌─────────────┐ ┌─────────────────────────────┐ │
│ │ Reducers │────>│ Local Cache (Read) │ │
│ │ (Write) │ │ - Tables from subscriptions│ │
│ └─────────────┘ │ - Automatically synced │ │
│ │ └─────────────────────────────┘ │
└─────────│──────────────────────────│───────────────────┘
│ WebSocket │ Updates pushed
v │
┌─────────────────────────────────────────────────────────┐
│ SpacetimeDB │
│ ┌─────────────────────────────────────────────────┐ │
│ │ Module │ │
│ │ - Reducers (transactional logic) │ │
│ │ - Tables (in-memory + persisted) │ │
│ │ - Subscriptions (real-time queries) │ │
│ └─────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────┘┌─────────────────────────────────────────────────────────┐
│ CLIENT │
│ ┌─────────────┐ ┌─────────────────────────────┐ │
│ │ Reducers │────>│ Local Cache (Read) │ │
│ │ (Write) │ │ - Tables from subscriptions│ │
│ └─────────────┘ │ - Automatically synced │ │
│ │ └─────────────────────────────┘ │
└─────────│──────────────────────────│───────────────────┘
│ WebSocket │ Updates pushed
v │
┌─────────────────────────────────────────────────────────┐
│ SpacetimeDB │
│ ┌─────────────────────────────────────────────────┐ │
│ │ Module │ │
│ │ - Reducers (transactional logic) │ │
│ │ - Tables (in-memory + persisted) │ │
│ │ - Subscriptions (real-time queries) │ │
│ └─────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────┘When to Use SpacetimeDB
何时使用SpacetimeDB
Ideal Use Cases
理想使用场景
- Real-time games: MMOs, multiplayer games, turn-based games
- Collaborative applications: Document editing, whiteboards, design tools
- Chat and messaging: Real-time communication with presence
- Live dashboards: Streaming analytics and monitoring
- IoT applications: Sensor data with real-time updates
- 实时游戏:MMORPG、多人游戏、回合制游戏
- 协作应用:文档编辑、白板、设计工具
- 聊天与消息:带在线状态的实时通信
- 实时仪表盘:流式分析与监控
- IoT应用:带实时更新的传感器数据
Key Decision Factors
关键决策因素
Choose SpacetimeDB when you need:
- Sub-10ms latency for reads and writes
- Automatic real-time synchronization
- Transactional guarantees for all operations
- Simplified architecture (no separate cache, queue, or server)
当你需要以下特性时,选择SpacetimeDB:
- 读写延迟低于10毫秒
- 自动实时同步
- 所有操作的事务保证
- 简化的架构(无需单独的缓存、队列或服务器)
Less Suitable For
不太适合的场景
- Batch analytics: SpacetimeDB is optimized for OLTP, not OLAP
- Large blob storage: Better suited for structured relational data
- Stateless APIs: Traditional REST APIs do not need real-time sync
- 批量分析:SpacetimeDB针对OLTP优化,而非OLAP
- 大对象存储:更适合结构化关系数据
- 无状态API:传统REST API不需要实时同步
Comparison to Traditional Architectures
与传统架构的对比
Traditional Stack
传统技术栈
Client
│
v
Load Balancer
│
v
Web/Game Servers (stateless or stateful)
│
├──> Cache (Redis)
│
v
Database (PostgreSQL, MySQL)
│
v
Message Queue (for real-time)Pain points:
- Multiple systems to deploy and manage
- Cache invalidation complexity
- State synchronization between servers
- Manual real-time implementation
- Horizontal scaling complexity
Client
│
v
Load Balancer
│
v
Web/Game Servers (stateless or stateful)
│
├──> Cache (Redis)
│
v
Database (PostgreSQL, MySQL)
│
v
Message Queue (for real-time)痛点:
- 需要部署和管理多个系统
- 缓存失效的复杂性
- 服务器间的状态同步
- 手动实现实时功能
- 水平扩展的复杂性
SpacetimeDB Stack
SpacetimeDB技术栈
Client
│
v
SpacetimeDB Host
│
v
Module (your logic + tables)Benefits:
- Single deployment target
- No cache layer needed (in-memory by design)
- Automatic real-time synchronization
- Built-in horizontal scaling (future)
- Transactional guarantees everywhere
Client
│
v
SpacetimeDB Host
│
v
Module (your logic + tables)优势:
- 单一部署目标
- 无需缓存层(设计上就是内存内存储)
- 自动实时同步
- 内置水平扩展(未来支持)
- 所有操作都有事务保证
Smart Contract Comparison
与智能合约的对比
SpacetimeDB modules are conceptually similar to smart contracts:
- Application logic runs inside the data layer
- Transactions are atomic and verified
- State changes are deterministic
Key differences:
- SpacetimeDB is orders of magnitude faster (no consensus overhead)
- Full relational database capabilities
- No blockchain or cryptocurrency involved
- Designed for real-time, not eventual consistency
SpacetimeDB模块在概念上与智能合约类似:
- 应用逻辑运行在数据层内部
- 事务是原子的且经过验证
- 状态变化是确定性的
核心区别:
- SpacetimeDB速度快几个数量级(无需共识开销)
- 具备完整的关系数据库功能
- 不涉及区块链或加密货币
- 专为实时设计,而非最终一致性
Common Patterns
常见模式
Authentication check in reducer:
rust
#[spacetimedb::reducer]
fn admin_action(ctx: &ReducerContext) -> Result<(), String> {
let admin = ctx.db.admin().identity().find(&ctx.sender)
.ok_or("Not an admin")?;
// ... perform admin action
Ok(())
}Moving between tables (state machine):
rust
#[spacetimedb::reducer]
fn login(ctx: &ReducerContext) -> Result<(), String> {
let player = ctx.db.logged_out_player().identity().find(&ctx.sender)
.ok_or("Not found")?;
ctx.db.player().insert(player.clone());
ctx.db.logged_out_player().identity().delete(&ctx.sender);
Ok(())
}Scheduled reducer:
rust
#[spacetimedb::table(name = reminder, scheduled(send_reminder))]
pub struct Reminder {
#[primary_key]
#[auto_inc]
id: u64,
scheduled_at: ScheduleAt,
message: String,
}
#[spacetimedb::reducer]
fn send_reminder(ctx: &ReducerContext, reminder: Reminder) {
// This runs at the scheduled time
log::info!("Reminder: {}", reminder.message);
}Reducer中的认证检查:
rust
#[spacetimedb::reducer]
fn admin_action(ctx: &ReducerContext) -> Result<(), String> {
let admin = ctx.db.admin().identity().find(&ctx.sender)
.ok_or("Not an admin")?;
// ... 执行管理员操作
Ok(())
}数据表间的状态转移(状态机):
rust
#[spacetimedb::reducer]
fn login(ctx: &ReducerContext) -> Result<(), String> {
let player = ctx.db.logged_out_player().identity().find(&ctx.sender)
.ok_or("Not found")?;
ctx.db.player().insert(player.clone());
ctx.db.logged_out_player().identity().delete(&ctx.sender);
Ok(())
}定时执行的Reducer:
rust
#[spacetimedb::table(name = reminder, scheduled(send_reminder))]
pub struct Reminder {
#[primary_key]
#[auto_inc]
id: u64,
scheduled_at: ScheduleAt,
message: String,
}
#[spacetimedb::reducer]
fn send_reminder(ctx: &ReducerContext, reminder: Reminder) {
// 此函数会在预定时间执行
log::info!("Reminder: {}", reminder.message);
}Editing Behavior
编辑规范
When modifying SpacetimeDB code:
- Make the smallest change necessary
- Do NOT touch unrelated files, configs, or dependencies
- Do NOT invent new SpacetimeDB APIs — use only what exists in docs or this repo
修改SpacetimeDB代码时:
- 只做必要的最小改动
- 不要修改无关文件、配置或依赖
- 不要发明新的SpacetimeDB API——仅使用文档或此仓库中已有的API