feat: persist workflow signals and arbitrate poll completion

This commit is contained in:
2026-09-18 03:52:50 +08:00
parent e2baffbe37
commit 7d5ebfcb09
34 changed files with 1958 additions and 113 deletions

View File

@@ -14,7 +14,7 @@ import (
// workflowCheckpoint 是待续表中版本化的内部唤醒身份;不保存内存执行栈或新的业务预算。
type workflowCheckpoint struct {
SchemaVersion uint32 `json:"schema_version"` // SchemaVersion 当前结构版本为1,未知版本禁止推进。
Kind string `json:"kind"` // Kind 为ROOT、TIMER或POLL,决定实际权威记录的读取方式。
Kind string `json:"kind"` // Kind 为ROOT、TIMER、SIGNAL或POLL,决定实际权威记录的读取方式。
ActivationID *core.ID `json:"activation_id,omitempty"` // ActivationID 固定等待激活;ROOT不使用该字段。
}
@@ -23,7 +23,7 @@ func saveWorkflowClock(ctx context.Context, tx *sql.Tx, g *workflowGraph, row *w
if err != nil {
return err
}
if row.view.ActivationID == nil || kind != "TIMER" && kind != "POLL" {
if row.view.ActivationID == nil || kind != "TIMER" && kind != "POLL" && kind != "SIGNAL" {
return errors.New("workflow clock identity invalid")
}
raw, err := json.Marshal(workflowCheckpoint{1, kind, row.view.ActivationID})
@@ -60,8 +60,24 @@ func (r *WorkflowRuntime) advanceClock(ctx context.Context, tx *sql.Tx, g *workf
return false, err
}
if checkpoint.Kind == "POLL" {
node, _ := g.program.Step(row.view.Key)
if node.Poll.FinalSignal != nil {
handled, changed, reason, err := r.driveWorkflowSignal(ctx, tx, g, row, c, nil, allow, now)
if err != nil {
return false, err
}
if handled {
if changed && reason != "" {
return true, r.audit(ctx, tx, g, c.id, reason, now)
}
return changed, nil
}
}
return r.advancePoll(ctx, tx, g, row, c, activated, allow, now)
}
if checkpoint.Kind == "SIGNAL" {
return r.advanceSignalClock(ctx, tx, g, row, c, allow, now)
}
if checkpoint.Kind != "TIMER" || row.view.State != weave.StepWaiting {
return false, errors.New("workflow timer state invalid")
}