feat: optimize workflow engine error handling

This commit is contained in:
Fu Diwei 2025-08-23 17:44:56 +08:00
parent 6f28b4066e
commit 9d1dc8564f
9 changed files with 40 additions and 24 deletions

View File

@ -232,6 +232,10 @@ func (wd *workflowDispatcher) tryExecuteAsync(task *taskInfo) {
return nil
})
we.OnNodeError(func(ctx context.Context, node *engine.Node, err error) error {
if errors.Is(err, engine.ErrTerminated) || errors.Is(err, engine.ErrBlocksException) {
return nil
}
log := domain.WorkflowLog{}
log.WorkflowId = task.WorkflowId
log.RunId = task.RunId

View File

@ -62,7 +62,7 @@ func (we *workflowEngine) Invoke(ctx context.Context, workflowId string, runId s
SetInputsManager(newInOutManager()).
SetContext(ctx)
if err := we.executeBlocks(wfCtx, runGraph.Nodes); err != nil {
if !errors.Is(err, errInterrupted) {
if !errors.Is(err, ErrTerminated) {
we.fireOnErrorHooks(ctx, err)
return err
}
@ -135,7 +135,7 @@ func (we *workflowEngine) executeNode(wfCtx *WorkflowContext, node *Node) error
execCtx := newNodeExecutionContext(wfCtx, node)
execRes, err := executor.Execute(execCtx)
if err != nil {
if err != nil && !errors.Is(err, ErrTerminated) {
we.fireOnNodeErrorHooks(wfCtx.ctx, node, err)
return err
}
@ -179,11 +179,15 @@ func (we *workflowEngine) executeNode(wfCtx *WorkflowContext, node *Node) error
}
}
if execRes.Interrupted {
return errInterrupted
if execRes.Terminated {
return ErrTerminated
}
}
if err != nil && errors.Is(err, ErrTerminated) {
return err
}
return nil
}

View File

@ -4,4 +4,9 @@ import (
"errors"
)
var errInterrupted = errors.New("workflow engine: interrupted, may be ended")
var (
// 表示工作流引擎执行被中断,可能已结束
ErrTerminated = errors.New("workflow engine: execution was terminated")
// 表示工作流引擎在执行子节点时发生异常
ErrBlocksException = errors.New("workflow engine: error occurred when executing blocks")
)

View File

@ -69,7 +69,7 @@ func newNodeExecutionContext(wfCtx *WorkflowContext, node *Node) *NodeExecutionC
type NodeExecutionResult struct {
node *Node
Interrupted bool // 是否中断执行(通常由 End 节点主动触发)
Terminated bool // 是否终止执行(通常由 End 节点主动触发)
variablesMtx sync.Mutex
Variables []VariableState

View File

@ -33,7 +33,7 @@ func (ne *conditionNodeExecutor) Execute(execCtx *NodeExecutionContext) (*NodeEx
err := engine.executeNode(execCtx.Clone(), node)
if err != nil {
if errors.Is(err, errInterrupted) {
if errors.Is(err, ErrTerminated) {
return execRes, err
}
errs = append(errs, err)
@ -41,7 +41,7 @@ func (ne *conditionNodeExecutor) Execute(execCtx *NodeExecutionContext) (*NodeEx
}
if len(errs) > 0 {
return execRes, fmt.Errorf("error occurred when executing child nodes: %w", errors.Join(errs...))
return execRes, fmt.Errorf("%w: %w", ErrBlocksException, errors.Join(errs...))
}
return execRes, nil
@ -92,7 +92,7 @@ func (ne *branchBlockNodeExecutor) Execute(execCtx *NodeExecutionContext) (*Node
panic("impossible!")
} else {
if err := engine.executeBlocks(execCtx.Clone(), execCtx.Node.Blocks); err != nil {
return execRes, err
return execRes, fmt.Errorf("%w: %w", ErrBlocksException, err)
}
}

View File

@ -8,9 +8,11 @@ type endNodeExecutor struct {
nodeExecutor
}
func (e *endNodeExecutor) Execute(execCtx *NodeExecutionContext) (*NodeExecutionResult, error) {
func (ne *endNodeExecutor) Execute(execCtx *NodeExecutionContext) (*NodeExecutionResult, error) {
execRes := newNodeExecutionResult(execCtx.Node)
execRes.Interrupted = true
execRes.Terminated = true
ne.logger.Info("the is ending")
return execRes, nil
}

View File

@ -8,9 +8,11 @@ type startNodeExecutor struct {
nodeExecutor
}
func (e *startNodeExecutor) Execute(execCtx *NodeExecutionContext) (*NodeExecutionResult, error) {
func (ne *startNodeExecutor) Execute(execCtx *NodeExecutionContext) (*NodeExecutionResult, error) {
execRes := newNodeExecutionResult(execCtx.Node)
ne.logger.Info("")
return execRes, nil
}

View File

@ -33,7 +33,7 @@ func (ne *tryCatchNodeExecutor) Execute(execCtx *NodeExecutionContext) (*NodeExe
err := engine.executeNode(execCtx.Clone(), node)
if err != nil {
if errors.Is(err, errInterrupted) {
if errors.Is(err, ErrTerminated) {
return execRes, err
}
tryErrs = append(tryErrs, err)
@ -52,18 +52,17 @@ func (ne *tryCatchNodeExecutor) Execute(execCtx *NodeExecutionContext) (*NodeExe
err := engine.executeNode(execCtx.Clone(), node)
if err != nil {
if errors.Is(err, errInterrupted) {
if errors.Is(err, ErrTerminated) {
return execRes, err
}
catchErrs = append(catchErrs, err)
}
}
if len(catchErrs) > 0 {
return execRes, fmt.Errorf("error occurred when executing child nodes: %w", errors.Join(append(tryErrs, catchErrs...)...))
}
return execRes, fmt.Errorf("error occurred when executing child nodes: %w", errors.Join(tryErrs...))
errs := make([]error, 0)
errs = append(errs, tryErrs...)
errs = append(errs, catchErrs...)
return execRes, fmt.Errorf("%w: %w", ErrBlocksException, errors.Join(errs...))
}
return execRes, nil
@ -90,7 +89,7 @@ func (ne *tryBlockNodeExecutor) Execute(execCtx *NodeExecutionContext) (*NodeExe
execRes := newNodeExecutionResult(execCtx.Node)
if err := engine.executeBlocks(execCtx.Clone(), execCtx.Node.Blocks); err != nil {
return execRes, err
return execRes, fmt.Errorf("%w: %w", ErrBlocksException, err)
}
return execRes, nil
@ -117,7 +116,7 @@ func (ne *catchBlockNodeExecutor) Execute(execCtx *NodeExecutionContext) (*NodeE
}
if err := engine.executeBlocks(execCtx.Clone(), execCtx.Node.Blocks); err != nil {
return execRes, err
return execRes, fmt.Errorf("%w: %w", ErrBlocksException, err)
}
return execRes, nil

View File

@ -73,11 +73,11 @@ const InternalNodeCard = ({
const isActivated = useMemo(() => nodeRenderData.activated || nodeRenderData.lineActivated, [nodeRenderData.activated, nodeRenderData.lineActivated]);
const [isHovering, setIsHovering] = useState(false);
const [isInvalid, setIsInvalid] = useState(false);
const [isNodeInvalid, setIsNodeInvalid] = useState(false);
const isNodeDisabled = useWatchFormValueIn(nodeRender.node, "disabled");
const formState = useWatchFormState(nodeRender.node);
useEffect(() => setIsInvalid(!!formState?.invalid), [formState?.invalid]);
useEffect(() => setIsNodeInvalid(!!formState?.invalid), [formState?.invalid]);
return (
<Card
@ -110,7 +110,7 @@ const InternalNodeCard = ({
right: "-1px",
bottom: "-1px",
borderWidth: "2px",
borderColor: isHovering ? "var(--color-primary)" : isInvalid ? "var(--color-error)" : void 0,
borderColor: isHovering ? "var(--color-primary)" : isNodeInvalid ? "var(--color-error)" : void 0,
borderStyle: isNodeDisabled ? "dashed" : "solid",
}}
/>