如何用 SpacetimeDB 计划表让 Reducer 在指定时间或按间隔定时执行
如何用 SpacetimeDB 计划表让 Reducer 在指定时间或按间隔定时执行【免费下载链接】SpacetimeDBDevelopment at the speed of light项目地址: https://gitcode.com/GitHub_Trending/sp/SpacetimeDBSpacetimeDB 的模块中Reducer 是修改数据库状态的唯一途径但 Reducer 默认只能被客户端或系统事件触发无法自己“在某个时间醒来”执行。计划表Schedule Table解决的就是这个问题在表中包含一个特殊的调度列ScheduleAt类型SpacetimeDB 就会监视这张表并在时间到达时自动调用你绑定的 Reducer参数就是这张表的一行。本文以 TypeScript 模块为例走一遍“定义计划表 → 绑定 Reducer → 插入定时行 → 调用并验证执行”的完整流程适用前提是本机已安装 SpacetimeDB CLI 和 Node.js 18。准备工作启动本地服务器与 TypeScript 模块先安装spacetimeCLI参考 Getting Started然后创建项目并启动本地 Standalone 服务器spacetime dev --template basic-ts这条命令会同时启动本地 SpacetimeDB 服务器、发布你的模块并生成客户端绑定服务器默认监听3000端口。模块的服务器端代码位于spacetimedb/src/index.ts下面的修改都在这一个文件里完成。定义计划表并绑定要定时执行的 Reducer计划表是一张普通表但其中必须包含一个ScheduleAt类型的列用来声明“什么时候执行”。用onSchedule选项把 Reducer 绑定到这张表import { ScheduleAt } from spacetimedb; import { schema, table, t } from spacetimedb/server; const reminder table( { name: reminder }, { scheduledId: t.u64().primaryKey().autoInc(), scheduledAt: t.scheduleAt(), message: t.string(), } ); const spacetimedb schema({ reminder }); export default spacetimedb; export const sendReminder spacetimedb.reducer( { onSchedule: reminder }, { arg: reminder.rowType }, (_ctx, { arg }) { console.info(Scheduled reminder fired: ${arg.message}); } );几个必须注意的点文档特别强调ScheduleAt类要从spacetimedb导入不是spacetimedb/server表列类型则用t.scheduleAt()。onSchedule在 Reducer 上声明绑定因此计划表和 Reducer 可以放在不同文件里不会产生循环导入。一张计划表最多只能绑定一个 Reducer 或 Procedure再绑定第二个是 schema 错误。旧代码也可能在表定义里写scheduled: () sendReminder带 thunk 的前向引用这种写法仍然有效但会强制表和函数写在同一文件并破坏类型推断新代码建议用onSchedule。如果你用其他语言写服务器模块绑定方式不同Rust 在表上使用#[table(..., scheduled(send_reminder))]字段类型为ScheduleAtC# 使用[SpacetimeDB.Table(..., Scheduled SendReminder, ScheduledAt ScheduledAt)]其中ScheduledAt的值必须精确等于表中某个ScheduleAt类型字段的字段名C 使用SPACETIMEDB_SCHEDULE(reminder, 1, send_reminder)第二个参数是scheduled_at列的 0 起始下标。完整示例见 Schedule Tables。插入 ScheduleAt 行间隔执行与指定时间执行向计划表插入一行即完成一次“排程”ScheduleAt有两种取值方式对应标题中的两种场景。按间隔重复执行interval适合游戏 tick、心跳、周期性维护等任务。TypeScript 中ScheduleAt.interval(...)的参数单位是微秒export const schedulePeriodicTasks spacetimedb.reducer((ctx) { // 每 5 秒执行一次5,000,000 微秒 ctx.db.reminder.insert({ scheduledId: 0n, scheduledAt: ScheduleAt.interval(5_000_000n), message: Check for updates, }); // 每 100 毫秒执行一次 ctx.db.reminder.insert({ scheduledId: 0n, scheduledAt: ScheduleAt.interval(100_000n), // 100ms 换算成微秒 message: Game tick, }); });scheduledId写0n因为列声明了autoInc()实际主键由数据库自动分配。Rust 中等价的间隔写法用Duration表达ctx.db.reminder().insert(Reminder { scheduled_id: 0, message: Check for updates.to_string(), scheduled_at: ScheduleAt::Interval(Duration::from_secs(5).into()), });关于错过的 tick文档说明 interval 调度锚定在“原本计划的执行时刻”上。如果数据库繁忙或离线导致错过了一个或多个间隔 tickSpacetimeDB 会排定下一个未来的 tick而不是把错过的 tick 连续补跑也不会以延迟后的执行时间为基准重新漂移。在指定时间一次性执行time适合“某个时刻发送提醒”“内容到期失效”这类单次动作。时间戳是“自 Unix 纪元起的微秒数”可以用ctx.timestamp相对计算也可以给绝对时间戳export const scheduleTimedTasks spacetimedb.reducer((ctx) { // 10 秒后执行 const tenSecondsFromNow ctx.timestamp.microsSinceUnixEpoch 10_000_000n; ctx.db.reminder.insert({ scheduledId: 0n, scheduledAt: ScheduleAt.time(tenSecondsFromNow), message: Your auction has ended, }); // 指定绝对 Unix 时间戳微秒1735689600_000_000n 是文档给出的 2025-01-01 00:00:00 UTC const targetTime 1735689600_000_000n; ctx.db.reminder.insert({ scheduledId: 0n, scheduledAt: ScheduleAt.time(targetTime), message: Happy New Year!, }); });Rust 中可以用ctx.timestamp Duration::from_secs(10)计算 10 秒后的时刻也可以把ScheduleAt::Time(ctx.timestamp.clone())作为“立即执行”的排程值文档示例。C# 侧则使用new ScheduleAt.Time(DateTimeOffset)与ctx.Timestamp new TimeDuration(10_000_000)微秒。验证定时 Reducer 是否按时执行模块代码保存后spacetime dev会自动重新发布。在另一个终端进入项目目录调用上面定义的触发 Reducer 把定时行写入计划表spacetime call schedulePeriodicTasks然后观察模块日志spacetime logs如果 interval 排程生效你会看到被调度的 Reducer 体内的日志按间隔周期性出现。以 Rust 示例为例文档给出的send_reminder会在每次 tick 时执行init里插入的 50ms 间隔行让它在模块发布后立即开始按 tick 运行。spacetime logs的具体输出内容因你的 Reducer 代码而异文档中展示的INFO: Hello, Alice!这类行是 TypeScript 快速上手示例的输出只说明日志查看方式不是计划表的固定输出。定时行的生命周期执行前还是执行后删除这一点直接决定你能否在被调度的函数里回查这行数据被调度的 Procedure行在执行前被删除所以执行期间schedule_table.find(scheduled_id)返回null.update()会失败。被调度的 Reducer行在执行后被删除所以 Reducer 运行期间这行在计划表里仍然可见。interval 调度行永远不会被自动删除。只有 one-shot指定时间行在跑完后才移除interval 行需要你自己处理例如不再需要时删除否则它会一直按间隔触发。可选分支调度 Procedure 而不是 ReducerReducer 运行在隔离环境里不能发网络请求、不能访问文件系统。如果定时任务需要产生数据库之外的副作用比如定时发起 HTTP 请求把同一套排程模式用在 Procedure 上即可TypeScript 中用spacetimedb.procedure({ onSchedule: fetchSchedule }, ...)且 Procedure 的返回类型必须是t.unit()Rust 中在表上写scheduled(fetch_external_data)。详细示例见 Reducers 文档的 Scheduling Procedures 一节。限制与适用边界绑定关系是一张计划表对一个 Reducer 或 Procedure重复绑定是 schema 错误。interval 行不会自动删除清理责任在你one-shot 行按上文规则在 Procedure 执行前、Reducer 执行后删除。错过 interval tick 时只跳到下一个未来边界不会补跑也不以延迟后的时刻为锚点。C 模块的排程写法见 Schedule Tables 中的 C 选项卡注意其SPACETIMEDB_SCHEDULE的列下标从 0 开始计数。完成上述步骤后如果你的 Reducer 日志按预期出现说明计划表链路已经打通需要对外提供定时数据的实时推送时下一步可以看 Subscriptions。【免费下载链接】SpacetimeDBDevelopment at the speed of light项目地址: https://gitcode.com/GitHub_Trending/sp/SpacetimeDB创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
上一篇/下一篇内容由系统自动关联
返回资讯列表 →