fix(workflow): 修复子流程批量执行缓存键冲突
递归识别子工作流中的视频模型以支持 per_second 计费,并为子流程批量子执行添加 SubFlowScope 隔离 async/segment 缓存键。
This commit is contained in:
@@ -34,6 +34,17 @@ func FormLambda(ctx context.Context, input any) (any, error) {
|
||||
return nodeInput, nil
|
||||
}
|
||||
|
||||
// cacheNodeId 缓存键节点 id:scope 非空(子流程批量子执行)时拼上作用域后缀,使各份子执行的
|
||||
// async/segment 缓存行互相隔离(同一 exec 下多份内层节点 id 相同,不隔离会互相命中/覆盖 done 结果,
|
||||
// 见《工作流子流程批量缓存隔离设计.md》);顶层 scope 为空 → 原样返回,行为不变。
|
||||
// 仅用于缓存读写键,节点的 Config.Id / node_execution 记录 / 输出引用一律不受影响。
|
||||
func cacheNodeId(global *flowDto.FlowExecutionInput, nodeId string) string {
|
||||
if global == nil || global.SubFlowScope == "" {
|
||||
return nodeId
|
||||
}
|
||||
return nodeId + global.SubFlowScope
|
||||
}
|
||||
|
||||
// ModelLambda 模型调用节点
|
||||
func ModelLambda(ctx context.Context, input any) (any, error) {
|
||||
nodeInput, ok := input.(*flowDto.NodeExecutionInput)
|
||||
@@ -45,6 +56,8 @@ func ModelLambda(ctx context.Context, input any) (any, error) {
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// async/segment 缓存键节点 id(子流程批量子执行带 scope,顶层即 Config.Id)
|
||||
cNodeId := cacheNodeId(nodeInput.Global, nodeInput.Config.Id)
|
||||
|
||||
// 2. 前置工具:决定模型调用入参(单次/多次)
|
||||
// 入参统一为扁平模型请求体(BuildModelRequestBody 输出,key 为点分路径)。
|
||||
@@ -79,7 +92,7 @@ func ModelLambda(ctx context.Context, input any) (any, error) {
|
||||
// 续跑(!ForceNewRun)时读取该节点已成功段;全新执行不查(BuildExecution 已清旧段),saved 为 nil → 全量重生成
|
||||
var saved map[int]entity.SegmentRef
|
||||
if !nodeInput.Global.ForceNewRun && segVideo {
|
||||
saved, err = flowDao.FlowSegmentResultDao.ListByNode(ctx, nodeInput.Global.ExecutionId, nodeInput.Config.Id)
|
||||
saved, err = flowDao.FlowSegmentResultDao.ListByNode(ctx, nodeInput.Global.ExecutionId, cNodeId)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -102,7 +115,7 @@ func ModelLambda(ctx context.Context, input any) (any, error) {
|
||||
defer wg.Done()
|
||||
// 每段单次调用,不原地重试:段失败即走节点失败收口(HandleFailedNodeExecution → Interrupt),
|
||||
// 下次 reExecute 由 planSegmentResume 复用已成功段、仅重生成失败段
|
||||
results[i], tokenRes[i], isInference[i], errs[i] = ModelCallResultLambda(ctx, nodeInput.Config.ModelConfig.ModelId, nodeInput.Global.SessionId, params, nodeInput.Config.Prompt, nodeInput.Global.ExecutionId, nodeInput.Config.Id, idxList[i])
|
||||
results[i], tokenRes[i], isInference[i], errs[i] = ModelCallResultLambda(ctx, nodeInput.Config.ModelConfig.ModelId, nodeInput.Global.SessionId, params, nodeInput.Config.Prompt, nodeInput.Global.ExecutionId, cNodeId, idxList[i])
|
||||
// 每段成功立即落库:该段刚成功即持久化,其他段仍在跑时已成功段也不丢;
|
||||
// 后续段失败或进程崩溃(panic/OOM/kill)时,已完成段已在库中,reExecute 可直接复用
|
||||
if segVideo && errs[i] == nil {
|
||||
@@ -112,7 +125,7 @@ func ModelLambda(ctx context.Context, input any) (any, error) {
|
||||
if key == "" || url == "" {
|
||||
continue
|
||||
}
|
||||
if err := flowDao.FlowSegmentResultDao.Save(ctx, nodeInput.Global.ExecutionId, nodeInput.Config.Id, idxList[i], key, url); err != nil {
|
||||
if err := flowDao.FlowSegmentResultDao.Save(ctx, nodeInput.Global.ExecutionId, cNodeId, idxList[i], key, url); err != nil {
|
||||
saveErrs[i] = err
|
||||
}
|
||||
}
|
||||
@@ -160,7 +173,7 @@ func ModelLambda(ctx context.Context, input any) (any, error) {
|
||||
}
|
||||
} else {
|
||||
for _, params := range paramsList {
|
||||
res, modelRes, _, err := ModelCallResultLambda(ctx, nodeInput.Config.ModelConfig.ModelId, nodeInput.Global.SessionId, params, nodeInput.Config.Prompt, nodeInput.Global.ExecutionId, nodeInput.Config.Id, flowDao.FlowAsyncSegSentinel)
|
||||
res, modelRes, _, err := ModelCallResultLambda(ctx, nodeInput.Config.ModelConfig.ModelId, nodeInput.Global.SessionId, params, nodeInput.Config.Prompt, nodeInput.Global.ExecutionId, cNodeId, flowDao.FlowAsyncSegSentinel)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user