snapshot of https://github.com/cortezaproject/corteza.git (2024.9.x, 2026-09-18; blobs > 131072B omitted)
Some checks failed
SonarQube / SonarQube (push) Failing after 5m28s
E2e test on main branch push / server and client test (map[name:admin port:8080]) (push) Failing after 3m33s
Server and lib tests / Library test (js) (push) Failing after 1m36s
Server and lib tests / Library test (vue) (push) Successful in 2m24s
Server and lib tests / Web client test (admin) (push) Successful in 2m52s
Server and lib tests / Web client test (compose) (push) Successful in 3m23s
Server and lib tests / Web client test (discovery) (push) Successful in 2m49s
Server and lib tests / Web client test (one) (push) Successful in 2m52s
Server and lib tests / Web client test (privacy) (push) Successful in 2m44s
Server and lib tests / Web client test (reporter) (push) Successful in 2m56s
Server and lib tests / Web client test (workflow) (push) Successful in 3m0s
Server and lib tests / Server tests (push) Failing after 4m55s
E2e test on main branch push / notify-failure (push) Failing after 9s
Server and lib tests / Send matrix testing notification (push) Successful in 2s
Mark stale issues and pull requests / stale (push) Successful in 40s
Some checks failed
SonarQube / SonarQube (push) Failing after 5m28s
E2e test on main branch push / server and client test (map[name:admin port:8080]) (push) Failing after 3m33s
Server and lib tests / Library test (js) (push) Failing after 1m36s
Server and lib tests / Library test (vue) (push) Successful in 2m24s
Server and lib tests / Web client test (admin) (push) Successful in 2m52s
Server and lib tests / Web client test (compose) (push) Successful in 3m23s
Server and lib tests / Web client test (discovery) (push) Successful in 2m49s
Server and lib tests / Web client test (one) (push) Successful in 2m52s
Server and lib tests / Web client test (privacy) (push) Successful in 2m44s
Server and lib tests / Web client test (reporter) (push) Successful in 2m56s
Server and lib tests / Web client test (workflow) (push) Successful in 3m0s
Server and lib tests / Server tests (push) Failing after 4m55s
E2e test on main branch push / notify-failure (push) Failing after 9s
Server and lib tests / Send matrix testing notification (push) Successful in 2s
Mark stale issues and pull requests / stale (push) Successful in 40s
This commit is contained in:
284
server/pkg/wfexec/state.go
Normal file
284
server/pkg/wfexec/state.go
Normal file
@@ -0,0 +1,284 @@
|
||||
package wfexec
|
||||
|
||||
import (
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/cortezaproject/corteza/server/pkg/auth"
|
||||
"github.com/cortezaproject/corteza/server/pkg/expr"
|
||||
)
|
||||
|
||||
type (
|
||||
|
||||
// state holds information about Session ID
|
||||
State struct {
|
||||
created time.Time
|
||||
completed *time.Time
|
||||
|
||||
// state identifier
|
||||
stateId uint64
|
||||
|
||||
// who's running this?
|
||||
owner auth.Identifiable
|
||||
|
||||
// Session identifier
|
||||
sessionId uint64
|
||||
|
||||
// parent, parent step
|
||||
parent Step
|
||||
|
||||
// current step
|
||||
step Step
|
||||
|
||||
// next steps
|
||||
next Steps
|
||||
|
||||
// step error (if any)
|
||||
err error
|
||||
|
||||
// input variables that were sent to resume the session
|
||||
input *expr.Vars
|
||||
|
||||
// scope
|
||||
scope *expr.Vars
|
||||
|
||||
// step execution results
|
||||
results *expr.Vars
|
||||
|
||||
// error handling step
|
||||
errHandler Step
|
||||
|
||||
// error handler result variable names
|
||||
//
|
||||
// Holds the error/errorMessage/errorStepID to variable-name mapping
|
||||
// declared on the error handling step. Kept apart from results
|
||||
// (previous step outputs) so that a normal step running between the
|
||||
// handler and the actual error does not overwrite it.
|
||||
errHandlerResults *expr.Vars
|
||||
|
||||
// error handled flag, this gets restarted on every new state!
|
||||
errHandled bool
|
||||
|
||||
loops []Iterator
|
||||
|
||||
// names of variables written on this path since it branched off
|
||||
//
|
||||
// Used by the join gateway to merge only what each parallel branch
|
||||
// actually changed instead of whole scopes; reset for every path
|
||||
// when execution branches.
|
||||
dirty map[string]bool
|
||||
|
||||
action string
|
||||
}
|
||||
)
|
||||
|
||||
func NewState(ses *Session, owner auth.Identifiable, caller, current Step, scope *expr.Vars) *State {
|
||||
return &State{
|
||||
stateId: nextID(),
|
||||
owner: owner,
|
||||
sessionId: ses.id,
|
||||
created: *now(),
|
||||
parent: caller,
|
||||
step: current,
|
||||
scope: scope,
|
||||
|
||||
loops: make([]Iterator, 0, 4),
|
||||
}
|
||||
}
|
||||
|
||||
func FinalState(ses *Session, scope *expr.Vars) *State {
|
||||
return &State{
|
||||
stateId: nextID(),
|
||||
sessionId: ses.id,
|
||||
created: *now(),
|
||||
completed: now(),
|
||||
scope: scope,
|
||||
}
|
||||
}
|
||||
|
||||
func (s State) Next(current Step, scope *expr.Vars) *State {
|
||||
return &State{
|
||||
stateId: nextID(),
|
||||
owner: s.owner,
|
||||
sessionId: s.sessionId,
|
||||
parent: s.step,
|
||||
errHandler: s.errHandler,
|
||||
errHandlerResults: s.errHandlerResults,
|
||||
results: s.results,
|
||||
loops: s.loops,
|
||||
dirty: s.dirty,
|
||||
|
||||
step: current,
|
||||
scope: scope,
|
||||
}
|
||||
}
|
||||
|
||||
// NextBranch returns next state for one of the parallel paths
|
||||
//
|
||||
// Variables written before the branching point are not part of the branch's
|
||||
// changes, so the set of written variables starts empty.
|
||||
func (s State) NextBranch(current Step, scope *expr.Vars) *State {
|
||||
st := s.Next(current, scope)
|
||||
st.dirty = nil
|
||||
return st
|
||||
}
|
||||
|
||||
// markDirty records variables written by the current step
|
||||
func (s *State) markDirty(vv *expr.Vars) {
|
||||
if vv.IsEmpty() {
|
||||
return
|
||||
}
|
||||
|
||||
var nn []string
|
||||
_ = vv.Each(func(k string, _ expr.TypedValue) error {
|
||||
nn = append(nn, k)
|
||||
return nil
|
||||
})
|
||||
|
||||
s.markDirtyNames(nn...)
|
||||
}
|
||||
|
||||
// markDirtyNames records variables written directly to the scope
|
||||
func (s *State) markDirtyNames(nn ...string) {
|
||||
if len(nn) == 0 {
|
||||
return
|
||||
}
|
||||
|
||||
if s.dirty == nil {
|
||||
s.dirty = make(map[string]bool)
|
||||
} else {
|
||||
// state's set is shared with the states it spawned; copy before write
|
||||
dirty := make(map[string]bool, len(s.dirty)+1)
|
||||
for k := range s.dirty {
|
||||
dirty[k] = true
|
||||
}
|
||||
s.dirty = dirty
|
||||
}
|
||||
|
||||
for _, n := range nn {
|
||||
s.dirty[n] = true
|
||||
}
|
||||
}
|
||||
|
||||
func (s State) MakeRequest() *ExecRequest {
|
||||
return &ExecRequest{
|
||||
SessionID: s.sessionId,
|
||||
StateID: s.stateId,
|
||||
Scope: s.scope,
|
||||
Input: s.input,
|
||||
Results: s.results,
|
||||
Parent: s.parent,
|
||||
dirty: s.dirty,
|
||||
}
|
||||
}
|
||||
|
||||
func (s *State) newLoop(i Iterator) {
|
||||
s.loops = append(s.loops, i)
|
||||
}
|
||||
|
||||
// ends loop and returns step that leads out of the loop
|
||||
func (s *State) loopEnd() (out Steps) {
|
||||
l := len(s.loops) - 1
|
||||
if l < 0 {
|
||||
panic("not inside a loop")
|
||||
}
|
||||
|
||||
out = Steps{s.loops[l].Break()}
|
||||
s.loops = s.loops[:l]
|
||||
return
|
||||
}
|
||||
|
||||
func (s State) loopCurr() Iterator {
|
||||
l := len(s.loops)
|
||||
if l > 0 {
|
||||
return s.loops[l-1]
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// MakeLightFrame builds a frame that describes the step without snapshotting
|
||||
// any of the state's variables
|
||||
//
|
||||
// Cloning the scope is what makes stacktrace collection expensive, and a frame
|
||||
// only needs its variables when someone is going to read them back.
|
||||
func (s State) MakeLightFrame() *Frame {
|
||||
f := &Frame{
|
||||
CreatedAt: s.created,
|
||||
SessionID: s.sessionId,
|
||||
StateID: s.stateId,
|
||||
NextSteps: s.next.IDs(),
|
||||
Action: s.action,
|
||||
}
|
||||
|
||||
s.describe(f)
|
||||
return f
|
||||
}
|
||||
|
||||
// describe fills in the parts of a frame that do not require cloning
|
||||
func (s State) describe(f *Frame) {
|
||||
if s.err != nil {
|
||||
f.Error = s.err.Error()
|
||||
}
|
||||
|
||||
if s.step != nil {
|
||||
f.StepID = s.step.ID()
|
||||
}
|
||||
|
||||
if s.parent != nil {
|
||||
f.ParentID = s.parent.ID()
|
||||
}
|
||||
|
||||
if s.completed != nil {
|
||||
f.StepTime = uint(s.completed.Sub(s.created) / time.Millisecond)
|
||||
}
|
||||
}
|
||||
|
||||
// MakeFrame builds a frame with a full snapshot of the state's variables
|
||||
func (s State) MakeFrame() *Frame {
|
||||
var (
|
||||
// might not be the most optimal way but we need to
|
||||
// un-reference scope, input, result variables
|
||||
unref = func(vars *expr.Vars) *expr.Vars {
|
||||
aux, err := vars.Clone()
|
||||
if err != nil {
|
||||
return expr.EmptyVars()
|
||||
}
|
||||
|
||||
// Since we're cloning vars, this will always hold
|
||||
return aux.(*expr.Vars)
|
||||
}
|
||||
)
|
||||
|
||||
f := s.MakeLightFrame()
|
||||
|
||||
var wg sync.WaitGroup
|
||||
wg.Add(3)
|
||||
|
||||
go func() {
|
||||
f.Input = unref(s.input)
|
||||
wg.Done()
|
||||
}()
|
||||
|
||||
go func() {
|
||||
f.Scope = unref(s.scope)
|
||||
wg.Done()
|
||||
}()
|
||||
|
||||
go func() {
|
||||
f.Results = unref(s.results)
|
||||
wg.Done()
|
||||
}()
|
||||
|
||||
wg.Wait()
|
||||
|
||||
return f
|
||||
}
|
||||
|
||||
func (s *State) Error() string {
|
||||
if s.err == nil {
|
||||
return ""
|
||||
}
|
||||
|
||||
return s.err.Error()
|
||||
}
|
||||
Reference in New Issue
Block a user