Files
orbit/adapters/outbox/mysql/runtime.go

77 lines
2.2 KiB
Go
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
package outbox
import (
"context"
"errors"
"time"
orbit "git.yuntuo.hk/k2safe/orbit/sdk/go"
)
var ErrShutdownTimeout = errors.New("outbox sender shutdown deadline exceeded; reconcile persisted records")
// RunOptions 控制进程轮询与关闭,不改变每条记录持久化的 24 小时/100 次预算。
type RunOptions struct {
PollInterval time.Duration // PollInterval 空闲/退避轮询间隔,默认500毫秒,范围10毫秒至10秒。
ShutdownTimeout time.Duration // ShutdownTimeout 取消后等待当前发送及本地确认,默认20秒,最多5分钟。
}
// Run 持续发送;取消后停止领取,限时完成当前交换及确认,调用方必须等待返回。
// 存储/确认错误返回给宿主处理;HTTP暂时失败持久化退避,REVIEW留在数据库供核查。
// 可以多进程并行运行;数据库租约协调领取,同一记录的HTTP仍可能重复,始终复用原键。
func (s *Store) Run(parent context.Context, api *orbit.Client, options RunOptions) error {
if api == nil {
return ErrInvalid
}
if options.PollInterval == 0 {
options.PollInterval = 500 * time.Millisecond
}
if options.ShutdownTimeout == 0 {
options.ShutdownTimeout = 20 * time.Second
}
if options.PollInterval < 10*time.Millisecond || options.PollInterval > 10*time.Second || options.ShutdownTimeout < time.Millisecond || options.ShutdownTimeout > 5*time.Minute {
return ErrInvalid
}
work, cancel := context.WithCancel(context.WithoutCancel(parent))
done := make(chan struct{})
expired := make(chan struct{})
go func() {
defer close(done)
select {
case <-work.Done():
return
case <-parent.Done():
}
timer := time.NewTimer(options.ShutdownTimeout)
defer timer.Stop()
select {
case <-work.Done():
case <-timer.C:
close(expired)
cancel()
}
}()
defer func() { cancel(); <-done }()
for parent.Err() == nil {
_, err := s.SendOnce(work, api)
select {
case <-expired:
return ErrShutdownTimeout
default:
}
if err != nil && !errors.Is(err, ErrIdle) && !errors.Is(err, ErrLeaseLost) {
return err
}
if err == nil {
continue
}
timer := time.NewTimer(options.PollInterval)
select {
case <-parent.Done():
timer.Stop()
case <-timer.C:
}
}
return nil
}