158 lines
3.5 KiB
Go
158 lines
3.5 KiB
Go
package batch
|
|
|
|
import (
|
|
"context"
|
|
"errors"
|
|
|
|
"github.com/gogf/gf/v2/os/gtime"
|
|
|
|
"git.5iot.com/iotlcm-public/iotlcm-worker-svr/internal/model/entity"
|
|
)
|
|
|
|
const (
|
|
Type3 = 3
|
|
Type4 = 4
|
|
Type11 = 11
|
|
Type12 = 12
|
|
Type40 = 40
|
|
Type41 = 41
|
|
Type101 = 101
|
|
Type102 = 102
|
|
Type103 = 103
|
|
Type104 = 104
|
|
Type105 = 105
|
|
Type106 = 106
|
|
Type107 = 107
|
|
Type108 = 108
|
|
Type109 = 109
|
|
Type110 = 110
|
|
|
|
AsyncStatusPending = 1
|
|
AsyncStatusProcessing = 2
|
|
AsyncStatusSuccess = 3
|
|
AsyncStatusFailed = 4
|
|
RunResultRunning = 1
|
|
RunResultSuccess = 2
|
|
RunResultFailed = 3
|
|
|
|
DetailStatusPending = 0
|
|
DetailStatusRunning = 1
|
|
DetailStatusSuccess = 2
|
|
DetailStatusFailed = 3
|
|
)
|
|
|
|
type Message struct {
|
|
BatchNo string `json:"batchNo"`
|
|
BatchType int `json:"batchType"`
|
|
FailureReason string `json:"failureReason,omitempty"`
|
|
Retryable bool `json:"retryable,omitempty"`
|
|
}
|
|
|
|
// RetryableError marks an executor failure that may succeed after delayed redelivery.
|
|
// The batch remains in processing until retries are exhausted and finalized.
|
|
type RetryableError struct {
|
|
Err error
|
|
}
|
|
|
|
func (e *RetryableError) Error() string {
|
|
if e == nil || e.Err == nil {
|
|
return "retryable batch execution error"
|
|
}
|
|
return e.Err.Error()
|
|
}
|
|
|
|
func (e *RetryableError) Unwrap() error {
|
|
if e == nil {
|
|
return nil
|
|
}
|
|
return e.Err
|
|
}
|
|
|
|
func Retryable(err error) error {
|
|
if err == nil {
|
|
return nil
|
|
}
|
|
if IsRetryable(err) {
|
|
return err
|
|
}
|
|
return &RetryableError{Err: err}
|
|
}
|
|
|
|
func IsRetryable(err error) bool {
|
|
var retryableErr *RetryableError
|
|
return errors.As(err, &retryableErr)
|
|
}
|
|
|
|
// FailureMarkError 表示执行已结束,但失败终态尚未成功写入数据库。
|
|
// 消费者重投这类消息时只能补写失败终态,不能再次执行具体业务。
|
|
type FailureMarkError struct {
|
|
Reason string
|
|
Err error
|
|
}
|
|
|
|
func (e *FailureMarkError) Error() string {
|
|
return "mark batch failed after executor error: " + e.Reason + ": " + e.Err.Error()
|
|
}
|
|
|
|
func (e *FailureMarkError) Unwrap() error {
|
|
return e.Err
|
|
}
|
|
|
|
type Executor interface {
|
|
Execute(ctx context.Context, batchNo string) error
|
|
}
|
|
|
|
type ProcessingExecutor interface {
|
|
CanExecuteProcessing(ctx context.Context, main *entity.BatchMain) (bool, error)
|
|
}
|
|
|
|
// SelfClaimingProcessingExecutor marks executors that perform their own
|
|
// processing-state CAS before executing a re-entry phase.
|
|
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
|
|
}
|
|
|
|
type ProcessingClaimer interface {
|
|
ReclaimProcessing(ctx context.Context, batchNo string, batchType int, startedAt *gtime.Time) (bool, error)
|
|
}
|
|
|
|
type Dispatcher struct {
|
|
executors map[int]Executor
|
|
}
|
|
|
|
func NewDispatcher(executor Executor) *Dispatcher {
|
|
return &Dispatcher{executors: map[int]Executor{
|
|
Type103: executor,
|
|
Type104: executor,
|
|
}}
|
|
}
|
|
|
|
func (d *Dispatcher) Register(batchType int, executor Executor) {
|
|
if d == nil || executor == nil {
|
|
return
|
|
}
|
|
d.executors[batchType] = executor
|
|
}
|
|
|
|
func (d *Dispatcher) Executor(batchType int) (Executor, bool) {
|
|
if d == nil {
|
|
return nil, false
|
|
}
|
|
executor, ok := d.executors[batchType]
|
|
return executor, ok && executor != nil
|
|
}
|
|
|
|
func (d *Dispatcher) Has(batchType int) bool {
|
|
_, ok := d.Executor(batchType)
|
|
return ok
|
|
}
|