The saga — charge, ship, never half-do it
Reserve stock, charge the card, ship. If the process dies between "charged" and "shipped", a queue-plus-flags build has no answer — the money moved and nothing else did.
On the loom: every step lands on the card chain before it runs. A crash
mid-saga replays to the exact pick; exhausted retries hand you an
ordinary error, so compensation is just an if-statement.
func OrderWorkflow(ctx sdk.Context, input []byte) ([]byte, error) {
reserved, err := ctx.ExecuteActivity("reserve", input)
if err != nil {
return nil, err // failed before any money moved
}
// Retries are engine policy (SetRetryPolicy); this error only
// surfaces once every attempt is exhausted.
if _, err := ctx.ExecuteActivity("charge", reserved); err != nil {
ctx.ExecuteActivity("release", reserved) // compensate
return nil, fmt.Errorf("charge failed, released: %w", err)
}
ctx.Sleep("settle", 2*time.Second) // durable across restarts
return ctx.ExecuteActivity("ship", reserved)
}