feat: recover workflow waits within original deadlines

This commit is contained in:
2026-09-18 06:10:04 +08:00
parent 56b9a2ccfe
commit af1ae4cb21
25 changed files with 941 additions and 52 deletions

View File

@@ -19,6 +19,9 @@ import (
const Action = "workflows:recover"
// MaxWaitActivations 包含首次等待,约束人工换代次数,防止无执行尝试的无限重建。
const MaxWaitActivations uint32 = 32
// Decision 核查基于整个实例的最新证据版本;后台新事实会使旧决定失效,原键仍可恢复回执。
type Decision struct {
ExpectedRevision uint64 `json:"expected_revision,string"` // ExpectedRevision 当前实例版本,不能使用control_revision代替。
@@ -59,6 +62,16 @@ type RetryStepInput struct {
SafeToRetry bool `json:"safe_to_retry" required:"true"` // SafeToRetry 确认旧执行已结束,并按原业务身份再次执行安全;不能只依据未收到响应。
}
type ContinuePollInput struct {
StepDecision
PendingConfirmed bool `json:"pending_confirmed" required:"true"` // PendingConfirmed 确认权威查询结果为处理中,按原间隔与剩余业务轮次接续。
}
type RearmWaitInput struct {
StepDecision
WaitVerified bool `json:"wait_verified" required:"true"` // WaitVerified 已核验当前等待及旧回执,按原契约/截止开启新等待代次。
}
type Result struct {
Instance instance.View `json:"instance"` // Instance 操作提交时的冻结实例回执;核查步骤不自动解除实例核查。
AuditID core.ID `json:"audit_id"` // AuditID 本次流程管理操作的不可变审计标识。
@@ -69,6 +82,8 @@ type Result struct {
}
type Store interface {
ContinuePoll(context.Context, auth.Token, core.ID, string, ContinuePollInput) (Result, error)
RearmWait(context.Context, auth.Token, core.ID, string, RearmWaitInput) (Result, error)
ReviewTask(context.Context, auth.Token, core.ID, string, ReviewTaskInput) (Result, error)
ResolveStep(context.Context, auth.Token, core.ID, string, ResolveStepInput) (Result, error)
Continue(context.Context, auth.Token, core.ID, string, ContinueInput) (Result, error)
@@ -160,3 +175,28 @@ func (s *Service) RetryStep(ctx context.Context, t auth.Token, id core.ID, key s
}
return s.store.RetryStep(ctx, t, id, key, in)
}
func (s *Service) ContinuePoll(ctx context.Context, t auth.Token, id core.ID, key string, in ContinuePollInput) (Result, error) {
if err := validateDecision(id, key, in.Decision); err != nil {
return Result{}, err
}
if err := validateStep(in.StepDecision); err != nil {
return Result{}, err
}
if !in.PendingConfirmed {
return Result{}, fault.Invalid("pending_confirmed=true required")
}
return s.store.ContinuePoll(ctx, t, id, key, in)
}
func (s *Service) RearmWait(ctx context.Context, t auth.Token, id core.ID, key string, in RearmWaitInput) (Result, error) {
if err := validateDecision(id, key, in.Decision); err != nil {
return Result{}, err
}
if err := validateStep(in.StepDecision); err != nil {
return Result{}, err
}
if !in.WaitVerified {
return Result{}, fault.Invalid("wait_verified=true required")
}
return s.store.RearmWait(ctx, t, id, key, in)
}