sync: 同步 v1.0.4 开源代码并脱敏
This commit is contained in:
@@ -112,6 +112,11 @@ type SelfClaimingProcessingExecutor interface {
|
||||
ClaimsProcessingReentry() bool
|
||||
}
|
||||
|
||||
// DurableFailureHandler takes ownership before any generic MarkFailed fallback.
|
||||
type DurableFailureHandler interface {
|
||||
ProtectExecutionFailure(ctx context.Context, batchNo string, batchType int, reason string) (bool, error)
|
||||
}
|
||||
|
||||
type ExecutionFailureHandler interface {
|
||||
HandleExecutionFailure(ctx context.Context, batchNo string, batchType int, reason string) error
|
||||
}
|
||||
|
||||
@@ -136,6 +136,64 @@ func (s *repositoryStub) ReclaimProcessing(_ context.Context, batchNo string, ba
|
||||
return s.reclaimed, s.reclaimErr
|
||||
}
|
||||
|
||||
type durableExecutorStub struct {
|
||||
execute func(context.Context) error
|
||||
protectErr error
|
||||
protectCalls int
|
||||
}
|
||||
|
||||
func (s *durableExecutorStub) Execute(ctx context.Context, _ string) error {
|
||||
if s.execute != nil {
|
||||
return s.execute(ctx)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
func (s *durableExecutorStub) ProtectExecutionFailure(ctx context.Context, _ string, _ int, _ string) (bool, error) {
|
||||
s.protectCalls++
|
||||
if ctx.Err() != nil {
|
||||
return true, ctx.Err()
|
||||
}
|
||||
return true, s.protectErr
|
||||
}
|
||||
|
||||
func TestRuntimeDurableOwnsEveryFailurePath(t *testing.T) {
|
||||
for _, kind := range []string{"error", "deadline-error", "deadline-nil", "failure-reason", "retry-exhausted", "protect-db-down"} {
|
||||
t.Run(kind, func(t *testing.T) {
|
||||
repo := &repositoryStub{mains: []*entity.BatchMain{{BatchNo: "DURABLE", BatchType: Type109, BatchAsyncStatus: AsyncStatusPending}}, claimed: true}
|
||||
exe := &durableExecutorStub{}
|
||||
msg := Message{BatchNo: "DURABLE", BatchType: Type109}
|
||||
switch kind {
|
||||
case "error":
|
||||
exe.execute = func(context.Context) error { return errors.New("执行中断") }
|
||||
case "deadline-error":
|
||||
exe.execute = func(ctx context.Context) error { <-ctx.Done(); return ctx.Err() }
|
||||
case "deadline-nil":
|
||||
exe.execute = func(ctx context.Context) error { <-ctx.Done(); return nil }
|
||||
case "failure-reason", "retry-exhausted":
|
||||
msg.FailureReason = "重试耗尽或失败重投"
|
||||
repo.mains[0].BatchAsyncStatus = AsyncStatusProcessing
|
||||
case "protect-db-down":
|
||||
exe.execute = func(context.Context) error { return errors.New("执行中断") }
|
||||
exe.protectErr = errors.New("数据库不可用")
|
||||
}
|
||||
dispatcher := NewDispatcher(&executorStub{})
|
||||
dispatcher.Register(Type109, exe)
|
||||
err := NewRuntime(repo, dispatcher, WithExecutionTimeout(time.Millisecond)).Handle(context.Background(), msg)
|
||||
if kind == "protect-db-down" {
|
||||
var failure *FailureMarkError
|
||||
if !errors.As(err, &failure) {
|
||||
t.Fatalf("保护失败应可重投: %v", err)
|
||||
}
|
||||
} else if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if exe.protectCalls != 1 || repo.failCalls != 0 {
|
||||
t.Fatalf("通用MarkFailed越过durable保护: protect=%d failed=%d", exe.protectCalls, repo.failCalls)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestMessageJSONContract(t *testing.T) {
|
||||
body, err := json.Marshal(Message{BatchNo: "BATCH-1", BatchType: Type103})
|
||||
if err != nil {
|
||||
|
||||
@@ -8,6 +8,7 @@ import (
|
||||
"github.com/gogf/gf/v2/errors/gerror"
|
||||
"github.com/gogf/gf/v2/frame/g"
|
||||
|
||||
"git.5iot.com/iotlcm-public/iotlcm-worker-svr/internal/library/batchfields"
|
||||
"git.5iot.com/iotlcm-public/iotlcm-worker-svr/internal/model/entity"
|
||||
)
|
||||
|
||||
@@ -84,9 +85,6 @@ func (r *Runtime) Handle(ctx context.Context, message Message) error {
|
||||
}
|
||||
if main.BatchAsyncStatus != AsyncStatusPending {
|
||||
if main.BatchAsyncStatus == AsyncStatusProcessing {
|
||||
if message.Retryable {
|
||||
return r.reclaimAndExecute(ctx, message, executor, main)
|
||||
}
|
||||
if processingExecutor, ok := executor.(ProcessingExecutor); ok {
|
||||
canExecute, checkErr := processingExecutor.CanExecuteProcessing(ctx, main)
|
||||
if checkErr != nil {
|
||||
@@ -98,6 +96,8 @@ func (r *Runtime) Handle(ctx context.Context, message Message) error {
|
||||
}
|
||||
return r.reclaimAndExecute(ctx, message, executor, main)
|
||||
}
|
||||
} else if message.Retryable {
|
||||
return r.reclaimAndExecute(ctx, message, executor, main)
|
||||
}
|
||||
}
|
||||
logUnclaimable(ctx, main)
|
||||
@@ -168,9 +168,19 @@ func (r *Runtime) execute(ctx context.Context, message Message, executor Executo
|
||||
}
|
||||
|
||||
func (r *Runtime) markExecutionFailed(ctx context.Context, message Message, executor Executor, reason string) error {
|
||||
reason = batchfields.FailureReason(reason)
|
||||
finalizationCtx, cancel := context.WithTimeout(context.WithoutCancel(ctx), r.finalizationTimeout)
|
||||
defer cancel()
|
||||
|
||||
if handler, ok := executor.(DurableFailureHandler); ok {
|
||||
handled, err := handler.ProtectExecutionFailure(finalizationCtx, message.BatchNo, message.BatchType, reason)
|
||||
if err != nil {
|
||||
return &FailureMarkError{Reason: reason, Err: err}
|
||||
}
|
||||
if handled {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
failed, markErr := r.repository.MarkFailed(finalizationCtx, message.BatchNo, message.BatchType, reason)
|
||||
if markErr != nil {
|
||||
return &FailureMarkError{Reason: reason, Err: markErr}
|
||||
|
||||
Reference in New Issue
Block a user