feat: persist workflow signals and arbitrate poll completion
This commit is contained in:
@@ -16,31 +16,42 @@ import (
|
||||
weave "git.yuntuo.hk/k2safe/orbit/weave/model"
|
||||
)
|
||||
|
||||
func (r *WorkflowRuntime) advancePoll(ctx context.Context, tx *sql.Tx, g *workflowGraph, row *workflowStep, c workflowCandidate, activated time.Time, allow bool, now time.Time) (bool, error) {
|
||||
// readWorkflowPollState 校验当前激活的完整轮次与原预算;时钟和最终信号共用相同检查。
|
||||
func readWorkflowPollState(ctx context.Context, tx *sql.Tx, g *workflowGraph, row *workflowStep, activated time.Time) (weave.PollState, core.ID, error) {
|
||||
var p weave.PollState
|
||||
err := tx.QueryRowContext(ctx, `SELECT id,revision,max_rounds,used_rounds,max_attempts_per_round,interval_ms,deadline_at,next_poll_at,current_task_id,resolved_at FROM weave_poll_states WHERE namespace_id=? AND workflow_id=? AND activation_id=? FOR UPDATE`, c.ns, c.workflow, row.view.ActivationID).Scan(&p.ID, &p.Revision, &p.MaxRounds, &p.UsedRounds, &p.MaxAttemptsPerRound, &p.IntervalMS, &p.DeadlineAt, &p.NextPollAt, &p.CurrentTaskID, &p.ResolvedAt)
|
||||
err := tx.QueryRowContext(ctx, `SELECT id,revision,max_rounds,used_rounds,max_attempts_per_round,interval_ms,deadline_at,next_poll_at,current_task_id,resolved_at FROM weave_poll_states WHERE namespace_id=? AND workflow_id=? AND activation_id=? FOR UPDATE`, g.view.NamespaceID, g.view.ID, row.view.ActivationID).Scan(&p.ID, &p.Revision, &p.MaxRounds, &p.UsedRounds, &p.MaxAttemptsPerRound, &p.IntervalMS, &p.DeadlineAt, &p.NextPollAt, &p.CurrentTaskID, &p.ResolvedAt)
|
||||
if err != nil {
|
||||
return p, "", err
|
||||
}
|
||||
node, _ := g.program.Step(row.view.Key)
|
||||
policy := node.Poll
|
||||
if policy == nil || p.MaxRounds != policy.MaxRounds || p.MaxAttemptsPerRound != policy.Task.Retry.MaxAttempts || p.IntervalMS != policy.IntervalMS || p.UsedRounds < 1 || !p.DeadlineAt.Equal(minimumTime(g.view.DeadlineAt, activated.Add(time.Duration(policy.TimeoutMS)*time.Millisecond))) {
|
||||
return p, "", errors.New("workflow poll policy mismatch")
|
||||
}
|
||||
if p.ResolvedAt != nil {
|
||||
return p, "", errors.New("unsettled poll step has resolved budget")
|
||||
}
|
||||
var count, minRound, maxRound uint32
|
||||
if err := tx.QueryRowContext(ctx, "SELECT COUNT(*),COALESCE(MIN(`round`),0),COALESCE(MAX(`round`),0) FROM weave_poll_rounds WHERE namespace_id=? AND workflow_id=? AND activation_id=?", g.view.NamespaceID, g.view.ID, row.view.ActivationID).Scan(&count, &minRound, &maxRound); err != nil {
|
||||
return p, "", err
|
||||
}
|
||||
if count != p.UsedRounds || minRound != 1 || maxRound != p.UsedRounds {
|
||||
return p, "", errors.New("workflow poll rounds incomplete")
|
||||
}
|
||||
var lastTask core.ID
|
||||
if err := tx.QueryRowContext(ctx, "SELECT task_id FROM weave_poll_rounds WHERE namespace_id=? AND workflow_id=? AND activation_id=? AND `round`=?", g.view.NamespaceID, g.view.ID, row.view.ActivationID, p.UsedRounds).Scan(&lastTask); err != nil {
|
||||
return p, "", err
|
||||
}
|
||||
return p, lastTask, nil
|
||||
}
|
||||
|
||||
func (r *WorkflowRuntime) advancePoll(ctx context.Context, tx *sql.Tx, g *workflowGraph, row *workflowStep, c workflowCandidate, activated time.Time, allow bool, now time.Time) (bool, error) {
|
||||
p, lastTask, err := readWorkflowPollState(ctx, tx, g, row, activated)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
node, _ := g.program.Step(row.view.Key)
|
||||
policy := node.Poll
|
||||
if policy == nil || p.MaxRounds != policy.MaxRounds || p.MaxAttemptsPerRound != policy.Task.Retry.MaxAttempts || p.IntervalMS != policy.IntervalMS || p.UsedRounds < 1 || !p.DeadlineAt.Equal(minimumTime(g.view.DeadlineAt, activated.Add(time.Duration(policy.TimeoutMS)*time.Millisecond))) {
|
||||
return false, errors.New("workflow poll policy mismatch")
|
||||
}
|
||||
if p.ResolvedAt != nil {
|
||||
return false, errors.New("unsettled poll step has resolved budget")
|
||||
}
|
||||
var count, minRound, maxRound uint32
|
||||
if err := tx.QueryRowContext(ctx, "SELECT COUNT(*),COALESCE(MIN(`round`),0),COALESCE(MAX(`round`),0) FROM weave_poll_rounds WHERE namespace_id=? AND workflow_id=? AND activation_id=?", c.ns, c.workflow, row.view.ActivationID).Scan(&count, &minRound, &maxRound); err != nil {
|
||||
return false, err
|
||||
}
|
||||
if count != p.UsedRounds || minRound != 1 || maxRound != p.UsedRounds {
|
||||
return false, errors.New("workflow poll rounds incomplete")
|
||||
}
|
||||
var lastTask core.ID
|
||||
if err := tx.QueryRowContext(ctx, "SELECT task_id FROM weave_poll_rounds WHERE namespace_id=? AND workflow_id=? AND activation_id=? AND `round`=?", c.ns, c.workflow, row.view.ActivationID, p.UsedRounds).Scan(&lastTask); err != nil {
|
||||
return false, err
|
||||
}
|
||||
if p.CurrentTaskID != nil {
|
||||
if *p.CurrentTaskID != lastTask || p.NextPollAt != nil || row.view.State != weave.StepActive {
|
||||
return false, errors.New("workflow in-flight poll mismatch")
|
||||
|
||||
Reference in New Issue
Block a user