Documentation ¶
Index ¶
- func IsEnd(f Fn) bool
- func IsError(f Fn) bool
- func Run(ctx context.Context, states ...Fn)
- func RunParallel(ctx context.Context, states ...Fn)
- type ErrorFn
- type Fn
- func After(d time.Duration, state Fn) Fn
- func At(t time.Time, state Fn) Fn
- func BackOff(dur StrategyFn, fn Fn) Fn
- func Batch(states ...Fn) Fn
- func ErrorEnd(err error) Fn
- func ErrorRestart(err error) Fn
- func NonBlocking(states ...Fn) Fn
- func Parallel(states ...Fn) Fn
- func Retry(retries int, fn Fn) Fn
- func StartState(ctx context.Context) Fn
- type StrategyFn
Examples ¶
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
func Run ¶
Example ¶
package main import ( "context" "fmt" sm "git.sr.ht/~mariusor/ssm" ) type maxKey string const _max maxKey = "__max" func start(ctx context.Context) sm.Fn { fmt.Print("start ") i := iter(0) return i.next } type iter int func (i *iter) next(ctx context.Context) sm.Fn { fmt.Printf("%d ", *i) if m, ok := ctx.Value(_max).(int); ok { if int(*i) == m { fmt.Print("end") return sm.End } } *i = *i + 1 return i.next } func main() { ctx := context.WithValue(context.Background(), _max, 10) sm.Run(ctx, start) }
Output: start 0 1 2 3 4 5 6 7 8 9 10 end
func RunParallel ¶
Types ¶
type Fn ¶
func After ¶
After runs the received state after d time.Duration has elapsed. This function blocks until the timer elapses, when it returns the next resolved state.
func At ¶
At runs the received state at t time.Time. This function blocks until the time is reached, when it returns the next resolved state.
func BackOff ¶
func BackOff(dur StrategyFn, fn Fn) Fn
BackOff returns an aggregator function which can be used to execute the received state with increasing delays. The function for determining the delay is passed in the StrategyFn "dur" parameter.
There is no end condition, so take care to limit the execution through some external method.
func Batch ¶
Batch executes the received states sequentially, and accumulates the next states. The resulting next state is returned as a sequential batch of all the non End states resolved.
func ErrorRestart ¶
ErrorRestart represents an error state which returns the first iteration passed. This iteration is loaded from the context, and is saved there by the Run and RunParallel functions.
func NonBlocking ¶
NonBlocking executes states in a goroutine and until it resolves it returns a wait state
func Parallel ¶
Parallel executes the received states in parallel goroutines, and accumulates the next states. The resulting next state is returned as a parallel batch of all the non End states resolved.
func Retry ¶
Retry is a way to construct a state machine out of repeating the execution of received state "fn", until the number of "retries" has been reached, or "fn" returns the End state.
The "fn" parameter can be one of the functions accepting a StrategyFn parameters, which wrap the original state Fn, and which provide a way to delay the execution between retries.
Example ¶
package main import ( "context" "fmt" "time" sm "git.sr.ht/~mariusor/ssm" ) func main() { st := time.Now() cnt := 0 fmt.Printf("Retries: ") start := sm.Retry(10, func(_ context.Context) sm.Fn { cnt++ run := time.Now() fmt.Printf("%d:%s ", cnt, run.Sub(st).Truncate(time.Millisecond)) st = run return sm.ErrorEnd(fmt.Errorf("retrying")) }) sm.Run(context.Background(), start) }
Output: Retries: 1:0s 2:0s 3:0s 4:0s 5:0s 6:0s 7:0s 8:0s 9:0s 10:0s
func StartState ¶
StartState retrieves the initial state from ctx context.Context. If nothing is found it returns the End state.
type StrategyFn ¶
StrategyFn is the type that returns the desired time.Duration for the BackOff function.
func Constant ¶
func Constant(d time.Duration) StrategyFn
Constant returns a constant time.Duration for every call.
Example ¶
package main import ( "context" "fmt" "time" sm "git.sr.ht/~mariusor/ssm" ) const delay = 10 * time.Millisecond func main() { st := time.Now() cnt := 0 fmt.Printf("Retries: ") start := sm.Retry(8, sm.BackOff(sm.Constant(delay), func(_ context.Context) sm.Fn { run := time.Now() cnt++ fmt.Printf("%d:%s ", cnt, run.Sub(st).Truncate(10*time.Millisecond)) st = run return sm.ErrorEnd(fmt.Errorf("keep going")) })) sm.Run(context.Background(), start) }
Output: Retries: 1:10ms 2:10ms 3:10ms 4:10ms 5:10ms 6:10ms 7:10ms 8:10ms
func Jitter ¶
func Jitter(max time.Duration, fn StrategyFn) StrategyFn
Jitter adds random jitter of "max" time.Duration for the fn StrategyFn
Example ¶
package main import ( "context" "fmt" "time" sm "git.sr.ht/~mariusor/ssm" ) const delay = 10 * time.Millisecond func main() { st := time.Now() cnt := 0 fmt.Printf("Retries: ") start := sm.Retry(2, sm.BackOff(sm.Jitter(time.Millisecond, sm.Constant(delay)), func(_ context.Context) sm.Fn { run := time.Now() cnt++ // NOTE(marius): The jitter adds a maximum of one ms, so with truncation of 2 ms the output will be correct // This is not a very good example test. fmt.Printf("%d:%s ", cnt, run.Sub(st).Truncate(2*time.Millisecond)) st = run return sm.ErrorEnd(fmt.Errorf("never right")) })) sm.Run(context.Background(), start) }
Output: Retries: 1:10ms 2:10ms
func Linear ¶
func Linear(d time.Duration, m float64) StrategyFn
Linear returns the linear function of the time.Duration multiplied by mul for every call.
Example ¶
package main import ( "context" "fmt" "time" sm "git.sr.ht/~mariusor/ssm" ) const delay = 10 * time.Millisecond func main() { st := time.Now() cnt := 0 fmt.Printf("Retries: ") start := sm.Retry(5, sm.BackOff(sm.Linear(delay, 2), func(_ context.Context) sm.Fn { run := time.Now() cnt++ fmt.Printf("%d:%s ", cnt, run.Sub(st).Truncate(10*time.Millisecond)) st = run return sm.ErrorEnd(fmt.Errorf("don't stop")) })) sm.Run(context.Background(), start) }
Output: Retries: 1:10ms 2:20ms 3:40ms 4:80ms 5:160ms