machine

package
v0.3.1 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Mar 1, 2024 License: MIT Imports: 13 Imported by: 8

Documentation

Overview

Package machine is a minimal implementation of AsyncMachine 1 in Golang using channels and context. It aims at simplicity and speed.

It can be used as a lightweight in-memory Temporal 2 alternative, worker for Asynq 3, or to write simple consensus engines, stateful firewalls, telemetry, bots, etc.

AsyncMachine is a relational state machine which never blocks.

Index

Constants

View Source
const (
	EventTransitionEnd    string = "transition-end"
	EventTransitionStart  string = "transition-start"
	EventTransitionCancel string = "transition-cancel"
	EventQueueEnd         string = "queue-end"
	EventTick             string = "tick"
)

Variables

This section is empty.

Functions

func IsTimeAfter

func IsTimeAfter(time1, time2 T) bool

IsTimeAfter checks if time1 is after time2. Requires ordered results from Machine.Time() (with specified states).

Types

type A

type A map[string]any

A (arguments) is a map of named method arguments.

type Clocks added in v0.3.0

type Clocks map[string]uint64

Clocks is a map of state names to their clocks.

type DefaultRelationsResolver

type DefaultRelationsResolver struct {
	Machine    *Machine
	Transition *Transition
}

DefaultRelationsResolver is the default implementation of the RelationsResolver.

func (*DefaultRelationsResolver) GetAutoMutation

func (rr *DefaultRelationsResolver) GetAutoMutation() *Mutation

func (*DefaultRelationsResolver) GetTargetStates

func (rr *DefaultRelationsResolver) GetTargetStates(
	t *Transition, calledStates S,
) S

func (*DefaultRelationsResolver) SortStates

func (rr *DefaultRelationsResolver) SortStates(states S)

type Event

type Event struct {
	Name    string
	Machine *Machine
	Args    A
	// contains filtered or unexported fields
}

type ExceptionArgsPanic

type ExceptionArgsPanic struct {
	CalledStates S
	StatesBefore S
	Transition   *Transition
	LastStep     *TransitionStep
	StackTrace   []byte
}

ExceptionArgsPanic is an optional argument for the Exception state which describes a panic within a transition handler.

type ExceptionHandler

type ExceptionHandler struct{}

ExceptionHandler provide basic Exception state support.

func (*ExceptionHandler) ExceptionState

func (eh *ExceptionHandler) ExceptionState(e *Event)

ExceptionState is a final entry handler for the Exception state. Args: - err error: The error that caused the Exception state. - panic *ExceptionArgsPanic: Optional details about the panic.

type HandlerBinding

type HandlerBinding struct {
	Ready chan struct{}
}

type LogLevel

type LogLevel int

LogLevel enum

const (
	LogNothing LogLevel = iota
	LogChanges
	LogOps
	LogDecisions
	LogEverything
)

func (LogLevel) String added in v0.3.0

func (l LogLevel) String() string

type Logger

type Logger func(level LogLevel, msg string, args ...any)

type Machine

type Machine struct {
	// Unique ID of this machine. Default: random UUID.
	ID string
	// Time for a handler to execute. Default: time.Second
	// TODO support
	HandlerTimeout time.Duration
	// If true, the machine will print all exceptions to stdout. Default: true.
	// Requires an ExceptionHandler binding and Machine.PanicToException set.
	PrintExceptions bool
	// If true, the machine will catch panic and trigger the Exception state.
	// Default: true.
	PanicToException bool
	// If true, the machine will prefix its logs with the machine ID (5 chars).
	// Default: true.
	LogID bool
	// Relations resolver, used to produce target states of a transition.
	// Default: *DefaultRelationsResolver.
	Resolver RelationsResolver
	// Ctx is the context of the machine.
	Ctx context.Context
	// Err is the last error that occurred.
	Err error
	// States is a map of state names to state definitions.
	States States
	// Queue of mutations to be executed.
	Queue []Mutation
	// Currently executing transition (if any).
	Transition *Transition
	// All states that are currently active.
	ActiveStates S
	// List of all the registered state names.
	StateNames S
	// contains filtered or unexported fields
}

Machine represent states, provides mutation methods, helpers methods and info about the current and scheduled transitions (if any). See https://github.com/pancsta/asyncmachine-go/blob/main/docs/manual.md

func New

func New(ctx context.Context, states States, opts *Opts) *Machine

New creates a new Machine instance, bound to context and modified with optional Opts

func (*Machine) Add

func (m *Machine) Add(states S, args A) Result

Add activates a list of states in the machine, returning the result of the transition (Executed, Queued, Canceled). Like every mutation method, it will resolve relations and trigger handlers.

func (*Machine) AddErr

func (m *Machine) AddErr(err error) Result

AddErr is a shorthand method to add the Exception state with the passed error. Like every mutation method, it will resolve relations and trigger handlers.

func (*Machine) AddErrStr

func (m *Machine) AddErrStr(err string) Result

AddErrStr is a shorthand method to add the Exception state with the passed error string. Like every mutation method, it will resolve relations and trigger handlers.

func (*Machine) Any

func (m *Machine) Any(states ...S) bool

Any is group call to `Is`, returns true if any of the params return true from Is.

``` machine.StringAll() // ()[Foo:0 Bar:0 Baz:0] machine.Add(S{"Foo"}) // is(Foo, Bar) or is(Bar) machine.Any(S{"Foo", "Bar"}, S{"Bar"}) // false // is(Foo) or is(Bar) machine.Any(S{"Foo"}, S{"Bar"}) // true ```

func (*Machine) BindHandlers

func (m *Machine) BindHandlers(handlers any) error

BindHandlers binds a struct of handler methods to the machine's states. Returns a HandlerBinding object, which signals when the binding is ready.

func (*Machine) Clock

func (m *Machine) Clock(state string) uint64

Clock return the current tick for a state.

func (*Machine) Dispose

func (m *Machine) Dispose()

Dispose disposes the machine and all its emitters.

func (*Machine) DuringTransition

func (m *Machine) DuringTransition() bool

DuringTransition checks if the machine is currently during a transition.

func (*Machine) From

func (m *Machine) From() S

From returns the states from before the transition. Assumes DuringTransition.

func (*Machine) GetLogLevel added in v0.3.0

func (m *Machine) GetLogLevel() LogLevel

func (*Machine) GetLogger added in v0.3.0

func (m *Machine) GetLogger() Logger

GetLogger returns the current custom logger function, or nil.

func (*Machine) GetRelationsBetween

func (m *Machine) GetRelationsBetween(fromState, toState string) []Relation

GetRelationsBetween returns a list of relation types between the given states.

func (*Machine) GetRelationsOf

func (m *Machine) GetRelationsOf(fromState string) []Relation

GetRelationsOf returns a list of relation types of the given state.

func (*Machine) GetStateCtx

func (m *Machine) GetStateCtx(state string) context.Context

GetStateCtx returns a context, bound to the current clock tick of the passed state.

Context cancels when the state has been de-activated, or right away, if it isn't currently active.

func (*Machine) Has

func (m *Machine) Has(states S) bool

Has return true is passed states are registered in the machine.

func (*Machine) HasStateChanged

func (m *Machine) HasStateChanged(before S, clocks Clocks) bool

HasStateChanged checks current active states have changed from the passed ones. Optionally also compares clock ticks.

func (*Machine) Inspect

func (m *Machine) Inspect(states S) string

Inspect returns a multi-line string representation of the machine (states, relations, clocks). states: param for ordered or partial results.

func (*Machine) Is

func (m *Machine) Is(states S) bool

Is checks if all the passed states are currently active.

``` machine.StringAll() // ()[Foo:0 Bar:0 Baz:0] machine.Add(S{"Foo"}) machine.Is(S{"Foo"}) // true machine.Is(S{"Foo", "Bar"}) // false ```

func (*Machine) IsClock

func (m *Machine) IsClock(state string, tick uint64) bool

IsClock checks if the passed state's clock equals the passed tick.

``` tick := m.Clock("A") m.Remove("A") m.Add("A") m.Is("A", tick) // -> false ```

func (*Machine) IsErr

func (m *Machine) IsErr() bool

IsErr checks if the machine has the Exception state currently active.

func (*Machine) IsQueued

func (m *Machine) IsQueued(mutationType MutationType, states S,
	withoutArgsOnly bool, statesStrictEqual bool, startIndex int,
) int

IsQueued checks if a particular mutation has been queued. Returns an index of the match or -1 if not found.

mutationType: add, remove, set states: list of states used in the mutation withoutParamsOnly: matches only mutation without the arguments object statesStrictEqual: states of the mutation have to be exactly like `states` and not a superset.

func (*Machine) Log

func (m *Machine) Log(msg string, args ...any)

Log logs an [external] message with the LogChanges level (highest one). Optionally redirects to a custom logger from SetLogger.

func (*Machine) MustParseStates

func (m *Machine) MustParseStates(states S) S

MustParseStates parses the states and returns them as a list. Panics when a state is not defined.

func (*Machine) Not

func (m *Machine) Not(states S) bool

Not checks if none of the passed states are currently active.

``` machine.StringAll() // ()[A:0 B:0 C:0 D:0] machine.Add(S{"A", "B"}) // not(A) and not(C) machine.Not(S{"A", "C"}) // false // not(C) and not(D) machine.Not(S{"C", "D"}) // true ```

func (*Machine) On

func (m *Machine) On(events []string, ctx context.Context) chan *Event

On returns a channel that will be notified with *Event, when any of the passed events happen. It's quick substitute for a predefined transition handler, although it does not guarantee a deterministic order of execution. ctx: optional context to dispose the emitter earlier. It's not supported to nest On() calls, as it would cause a deadlock.

func (*Machine) Remove

func (m *Machine) Remove(states S, args A) Result

Remove de-activates a list of states in the machine, returning the result of the transition (Executed, Queued, Canceled). Like every mutation method, it will resolve relations and trigger handlers.

func (*Machine) Set

func (m *Machine) Set(states S, args A) Result

Set de-activates a list of states in the machine, returning the result of the transition (Executed, Queued, Canceled). Like every mutation method, it will resolve relations and trigger handlers.

func (*Machine) SetLogLevel

func (m *Machine) SetLogLevel(level LogLevel)

SetLogLevel sets the log level of the machine.

func (*Machine) SetLogger

func (m *Machine) SetLogger(fn Logger)

SetLogger sets a custom logger function.

func (*Machine) String

func (m *Machine) String() string

String returns a one line representation of the currently active states, with their clock values. Inactive states are omitted. Eg: (Foo:2 Bar:1)

func (*Machine) StringAll

func (m *Machine) StringAll() string

StringAll returns a one line representation of all the states, with their clock values. Inactive states are in square brackets. Eg: (Foo:2 Bar:1)[Baz:0]

func (*Machine) Switch added in v0.3.0

func (m *Machine) Switch(states ...string) string

Switch returns the first state from the passed list that is currently active, making it useful for switch statements.

func (*Machine) Time

func (m *Machine) Time(states S) T

Time returns a list of logical clocks of specified states (or all the states if nil). states: optionally passing a list of states param guarantees a deterministic order of the result.

func (*Machine) TimeSum

func (m *Machine) TimeSum(states S) uint64

TimeSum returns the sum of logical clocks of specified states (or all states if nil). It's a very inaccurate, yet simple way to measure the machine's time.

func (*Machine) To

func (m *Machine) To() S

To returns the target states of the transition. Assumes DuringTransition.

func (*Machine) VerifyStates added in v0.3.0

func (m *Machine) VerifyStates(states S) error

VerifyStates verifies an array of state names and returns an error in case at least one isn't defined. It also retains the order and uses it for StateNames (only if all states have been passed).

func (*Machine) When

func (m *Machine) When(states []string, ctx context.Context) chan struct{}

When returns a channel that will be closed when all the passed states become active or the machine gets disposed. ctx: optional context that will close the channel when done. Useful when listening on 2 When() channels within the same `select` to GC the 2nd one.

func (*Machine) WhenErr

func (m *Machine) WhenErr(ctx context.Context) chan struct{}

WhenErr returns a channel that will be closed when the machine is in the Exception state. ctx: optional context defaults to the machine's context.

func (*Machine) WhenNot

func (m *Machine) WhenNot(states []string, ctx context.Context) chan struct{}

WhenNot returns a channel that will be closed when all the passed states become inactive or the machine gets disposed. ctx: optional context that will close the channel when done. Useful when listening on 2 WhenNot() channels within the same `select` to GC the 2nd one.

type Mutation

type Mutation struct {
	Type         MutationType
	CalledStates S
	Args         A
	// this mutation has been triggered by an auto state
	Auto bool
}

type MutationType

type MutationType int

MutationType enum

const (
	MutationTypeAdd MutationType = iota
	MutationTypeRemove
	MutationTypeSet
)

func (MutationType) String

func (m MutationType) String() string

type Opts

type Opts struct {
	// Unique ID of this machine. Default: random UUID.
	ID string
	// Time for a handler to execute. Default: time.Second
	HandlerTimeout time.Duration
	// If true, the machine will NOT print all exceptions to stdout.
	DontPrintExceptions bool
	// If true, the machine will die on panics.
	DontPanicToException bool
	// If true, the machine will NOT prefix its logs with its ID.
	DontLogID bool
	// Custom relations resolver. Default: *DefaultRelationsResolver.
	Resolver RelationsResolver
	// Log level of the machine. Default: LogNothing.
	LogLevel LogLevel
}

type Relation

type Relation int

Relation enum

const (
	RelationAfter Relation = iota
	RelationAdd
	RelationRequire
	RelationRemove
)

func (Relation) String

func (r Relation) String() string

type RelationsResolver

type RelationsResolver interface {
	// GetTargetStates returns the target states after parsing the relations.
	GetTargetStates(t *Transition, calledStates S) S
	// GetAutoMutation returns an (optional) auto mutation which is appended to
	// the queue after the transition is executed.
	GetAutoMutation() *Mutation
	// SortStates sorts the states in the order their handlers should be
	// executed.
	SortStates(states S)
}

RelationsResolver is an interface for parsing relations between states. TODO support custom relation types

type Result

type Result int

Result enum is the result of a state Transition

const (
	Executed Result = 1 << iota
	Canceled
	Queued
)

func (Result) String

func (r Result) String() string

type S

type S []string

S (state names) is a string list of state names.

func DiffStates

func DiffStates(states1 S, states2 S) S

DiffStates returns the states that are in states1 but not in states2.

type State

type State struct {
	Auto    bool
	Multi   bool
	Require S
	Add     S
	Remove  S
	After   S
}

State defines a single state of a machine, its properties and relations.

type States

type States = map[string]State

States is a map of state names to state definitions.

type T

type T []uint64

T is an ordered list of state clocks.

type Transition

type Transition struct {
	ID string
	// List of steps taken by this transition (so far).
	Steps []*TransitionStep
	// When true, execution of the transition has been completed.
	IsCompleted bool
	// states before the transition
	StatesBefore S
	// clocks of the states from before the transition
	ClocksBefore Clocks
	// States with "enter" handlers to execute
	Enters S
	// States with "exit" handlers to executed
	Exits S
	// target states after parsing the relations
	TargetStates S
	// was the transition accepted (during the negotiation phase)
	Accepted bool
	// Mutation call which cased this transition
	Mutation *Mutation
	// Parent machine
	Machine *Machine
	// Log entries produced during the transition
	LogEntries []string
	// contains filtered or unexported fields
}

Transition represents processing of a single mutation withing a machine.

func (*Transition) Args

func (t *Transition) Args() A

Args returns the argument object passed to the mutation method (if any).

func (*Transition) CalledStates

func (t *Transition) CalledStates() S

CalledStates return explicitly called / requested states of the transition.

func (*Transition) IsAuto

func (t *Transition) IsAuto() bool

IsAuto returns true if the transition was triggered by an auto state. Thus, it cant trigger any other auto state mutations.

func (*Transition) String

func (t *Transition) String() string

String representation of the transition and the steps taken so far. TODO: implement, test

func (*Transition) Type

func (t *Transition) Type() MutationType

Type returns the type of the mutation (add, remove, set).

type TransitionStep

type TransitionStep struct {
	ID        string
	FromState string
	ToState   string
	Type      TransitionStepType
	// eg a transition method name, relation type
	Data any
	// marks a final handler (FooState, FooEnd)
	IsFinal bool
	// marks a self handler (FooFoo)
	IsSelf bool
	// marks an enter handler (FooEnter). Requires IsFinal.
	IsEnter bool
}

type TransitionStepType

type TransitionStepType int

TransitionStepType enum TODO rename to StepType

const (
	TransitionStepTypeRelation TransitionStepType = 1 << iota
	TransitionStepTypeTransition
	TransitionStepTypeSet
	TransitionStepTypeRemove
	// TODO rename to StepTypeRemoveNotSet
	TransitionStepTypeNoSet
	TransitionStepTypeRequested
	TransitionStepTypeCancel
)

func (TransitionStepType) String

func (tt TransitionStepType) String() string

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL