Quick start
Start with a new Go module, complete one Durable Run, and read a declared outlet. This one-node example uses MemoryStore to demonstrate the minimal calling sequence. It does not retain checkpoints after process exit.
1. Prepare a module
You need Go 1.26 or later and access to Go module downloads. Run:
mkdir durable-quickstart
cd durable-quickstart
go mod init example.com/durable-quickstart
go get github.com/Luo-root/pulse-durable@v0.1.0If you created a module in Installation, continue there. This tutorial targets the v0.1.0 API and Pulse v0.5.0. This is release preparation: check GitHub Releases for publication status; installation commands are unavailable before their tags are published.
2. Create main.go
Save this complete program as main.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
}The program performs these steps:
- Create a
Runtimewith a Store and a codec registry.NewCodecsalready includes theintused here. - Declare the graph input
inputand outletoutput. The node declares what it reads and produces usingRequiresandProvides. - Supply external input
41withSeed. The node reads it withGetand supplies output42withSet. - Create a run with a nonempty
RunIDand businessVersion.Runblocks until this execution finishes. - Check the run error, then read this execution's declared outlet with
ReadOutput.
3. Run and check the result
From the module directory containing main.go, run:
go mod tidy
go run .Expected output:
result: 42 readyready means a business value exists. A zero value, empty string, or valid nil value can also be ready; do not infer presence from whether the value is zero. A normal skip uses state skipped. Check err and state separately.
This example requires ready because the graph is expected to produce an incremented result. In a general graph, an outlet may legitimately be skipped, or pending for an optional pass-through outlet without a supplied input. Those states do not by themselves mean the run failed. See the Execution contract for details.
4. Understand the boundary
Calling Set inside a function does not mean the complete node has been durably confirmed. Durable saves the checkpoint after the full node chain, including Aspects, finishes. It publishes results downstream after that save is confirmed.
Every launch of this example creates a new MemoryStore, so running go run . again executes the node again. Within the same Store, you cannot call Run again for an existing RunID. Open an existing record with Resume, and allocate a new RunID for new business work.
When something fails
no registered codec: check the types of all node keys and declared ports. Business structs and named types need explicit codec registration.- Outlet undeclared or type mismatch: check the keys used in
DeclareOutputandReadOutput. An internal key cannot be read as a graph outlet. - The run returns an error: handle that error first.
ReadOutputon a failed execution returns the failure cause; do not continue business calculations using a zero value. - RunID conflict: decide whether you are creating new business work or recovering existing work, then choose a new RunID or use
Resume.
Next steps
Continue with Recovery after process exit: switch to filestore, actually exit the first process, and complete the run in a second process. See the Execution contract for the complete identity, version, and event constraints.