Main conception is to create easy to use and deploy solution that aims to provide you means for functions pipeline execution of any complexity.
This repository provides abstract api that implements execution graph storage and invocation via http and basic storage interface.
The main step is to provide your execution directional acyclic graph with transient functions. Before storing, each graph will be topographically sorted, checked for cycles and all roots will be found (if applicable). If your graph will contain more than one root then deepest will be selected.
At each cycle executor will check with which value your function returned and will proceed to next node in graph. Each node implicitly has connection to error node which prematurely exits from the pipeline and writes error into provided storage. If function returns value that isn't any connected node name then executor will stop pipeline and consider it finished.
Describe your function with handler signature
func MyBestFunction(ec *fsm.ExecutionContext) (fsm.NodeName, error) {
dep, ok := ec.ExecutionDependencies.Load("myDependencyKey")
// sync map casts everything to interface
// so cast your value to concrete type
dep.(YourConcreteType).YourFieldOrFunction
...
return "nextNodeName", nil
}
Then provide requested dependencies on executor startup
sm := sync.Map{}
sm.Store("myDependencyKey", MySuperImportantDependency)
executor := fsm.NewExecutor(mongoStorage, sm)
Refer to such function that uses param from execution context
func MyBestFunction(ec *fsm.ExecutionContext) (fsm.NodeName, error) {
val, ok := ec.Params["myParameterKey"]
}
Function parameters are passed into graph initiator via the receiver in next fashion. Graph invocations are stored using your storage implementation, so you can review them later.
{
"graphName": "MyBestGraph",
"params": {
"myParameterKey": 1
}
}
func blankFunc(_ *fsm.ExecutionContext) (fsm.NodeName, error) { return "", nil }
stepMap := fsm.NewStepMap()
sm.AddStep("First", []fsm.NodeName{"Second", "Third"}, blankFunc)
sm.AddStep("Second", []fsm.NodeName{"Fourth", "Fifth"}, blankFunc)
sm.AddStep("Third", []fsm.NodeName{"Sixth", "Seventh"}, blankFunc)
mongo := storage.NewMongoStorage("url", "db", "table")
sm := make(sync.Map)
executor := fsm.NewExecutor(mongoStorage, sm)
executor.AddControlGraph("SuperControlGraph", stepMap)
rec := receiver.CreateHttpListener(executor.ExecutorChannel, mongo, executor.GetJobStack())
go func() {
err := rec.ListenAndServe()
if err != nil {os.Exit(1)}
executor.StartProcessing()
}