快速开始
本页从一个新 Go 模块开始,完成一次 Durable Run,再读取声明出口。示例只有一个节点,使用 MemoryStore 展示最小调用流程;它不会在进程退出后保留检查点。
1. 准备模块
需要 Go 1.26 或更高版本,以及能够下载 Go 模块的环境。执行:
sh
mkdir durable-quickstart
cd durable-quickstart
go mod init example.com/durable-quickstart
go get github.com/Luo-root/pulse-durable@v0.1.0如果已按安装创建模块,直接继续。本教程以目标 v0.1.0 为 API 基线,依赖 Pulse v0.5.0;这是发版准备,发布状态以 GitHub Releases 为准,相应 tag 发布前安装命令尚不可用。
2. 创建 main.go
将以下完整代码保存为 main.go:
go
package main
import (
"context"
"fmt"
"log"
"github.com/Luo-root/pulse"
durable "github.com/Luo-root/pulse-durable"
)
func main() {
if err := run(); err != nil {
log.Fatal(err)
}
}
func run() error {
runtime, err := durable.New(durable.NewMemoryStore(), durable.NewCodecs())
if err != nil {
return err
}
input := pulse.NewKey[int]("input")
output := pulse.NewKey[int]("output")
graph, err := pulse.New("quickstart", "Add one")
if err != nil {
return err
}
if err := graph.DeclareInput(pulse.NewPort(input)); err != nil {
return err
}
if err := graph.DeclareOutput(pulse.NewPort(output)); err != nil {
return err
}
if err := graph.Add(pulse.NewNode("add", "Add one",
pulse.Requires(input), pulse.Provides(output),
func(rc *pulse.RunCtx) error {
value, err := pulse.Get(rc, input)
if err != nil {
return err
}
return pulse.Set(rc, output, value+1)
})); err != nil {
return err
}
var inputs pulse.Inputs
if err := pulse.Seed(&inputs, input, 41); err != nil {
return err
}
execution, err := runtime.Run(context.Background(), graph, inputs,
durable.RunOptions{RunID: "quickstart-001", Version: "increment/1"})
if err != nil {
return err
}
value, state, err := pulse.ReadOutput(execution, output)
if err != nil {
return err
}
if state != pulse.SlotStateReady {
return fmt.Errorf("unexpected output state: %s", state)
}
fmt.Println("result:", value, state)
return nil
}程序依次完成以下操作:
- 创建
Runtime,传入存储与类型编码注册表。NewCodecs已包含本例使用的int。 - 声明图输入
input和图出口output。节点通过Requires、Provides声明它读取和产生的键。 - 用
Seed提供外部输入41。节点的Get读取输入,Set提供输出42。 - 用非空
RunID与业务Version创建运行。Run阻塞到本次执行结束。 - 检查运行错误,再用
ReadOutput读取本次执行的声明出口。
3. 执行并检查结果
在 main.go 所在模块目录执行:
sh
go mod tidy
go run .预期输出:
text
result: 42 readyready 表示存在一个业务值。零值、空字符串或合法的 nil 值也可以是 ready,不能仅按值是否为零判断有无结果。正常跳过使用 skipped 状态;调用方应分别检查 err 和 state。
本例检查 ready,因为这个图明确要求得到一个加一结果。一般图的出口也可能正常处于 skipped,或是未喂值的可选透传出口 pending;这些状态本身不等同于运行失败。详细规则见执行契约。
4. 理解这次运行的边界
节点函数里的 Set 不等于整个节点已持久完成。Durable 在包含切面的完整节点链结束后保存检查点,保存被确认后才向下游发布成果。
每次启动本例都会创建全新的 MemoryStore,所以重复执行 go run . 会再次执行节点。同一存储中已存在的 RunID 不能再次 Run;已有记录应通过 Resume 打开,新业务应分配新 RunID。
遇到错误时
no registered codec:检查所有节点键和声明端口的类型。业务结构体和命名类型需要显式注册编码器。- 出口未声明或类型不符:核对
DeclareOutput与ReadOutput使用的键;内部键不能作为图出口读取。 - 运行返回错误:先处理该错误。失败执行的
ReadOutput会返回失败原因,不能拿零值继续业务计算。 - 相同 RunID 冲突:确认是在创建新业务还是恢复旧业务,再选择新的 RunID 或
Resume。
下一步
继续进程退出后恢复,将存储替换为 filestore,实际退出第一个进程并在第二个进程中完成运行。恢复身份、版本和事件的完整约束见执行契约。