fix: 停复机指令队列审查问题
All checks were successful
构建并部署到测试环境(无 SSH) / build-and-deploy (push) Successful in 9m24s

- 开关开启但分发器心跳缺失时回退为当场执行,避免指令无人消费
- 待确认指令先查上游状态再判断;执行期间被覆盖时把待确认带到新指令
- 同方向失败退避中重复写入不提前执行时间
- 领取容量把执行上限与本秒剩余 QPS 分开计算
- 队列复机共用单卡复机锁;执行上下文取消后按结果未知确认
- 消费者并行停止;读取最近调用时间失败时不覆盖;成功但终结日志失败按成功处理

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
邹进
2026-10-26 00:43:58 +09:00
parent 934a3a27a1
commit a4e3fe4d8f
9 changed files with 188 additions and 79 deletions

View File

@@ -1,11 +1,29 @@
package carrierqueue
import "context"
import (
"context"
"strconv"
"sync"
"time"
"github.com/break/junhong_cmp_fiber/pkg/constants"
)
// heartbeatFreshness 是分发器心跳有效期:超过该时长没有心跳视为指令无人消费。
const heartbeatFreshness = 30 * time.Second
// heartbeatCacheTTL 是进程内缓存心跳判断的时长,避免每次请求评估都读 Redis。
const heartbeatCacheTTL = 3 * time.Second
// CommandQueue 是停复机指令队列写入端:开关读取受控系统配置,写入走共享缓存 Lua。
type CommandQueue struct {
store *Store
settings *SettingsProvider
mu sync.Mutex
aliveAt time.Time
alive bool
checkedAt time.Time
}
// NewCommandQueue 创建停复机指令队列写入端。
@@ -13,9 +31,34 @@ func NewCommandQueue(store *Store, settings *SettingsProvider) *CommandQueue {
return &CommandQueue{store: store, settings: settings}
}
// Enabled 返回停复机出口开关 polling.stop_resume.queue_enabled。
// Enabled 返回是否经指令队列执行:停复机出口开关开启,且分发器在心跳有效期内运行(有消费方)。
// 开关开启但没有分发器时回退为各入口当场执行,避免指令写入后无人消费。
func (q *CommandQueue) Enabled(ctx context.Context) bool {
return q != nil && q.store != nil && q.settings.Current(ctx).StopResumeQueueEnabled
if q == nil || q.store == nil || !q.settings.Current(ctx).StopResumeQueueEnabled {
return false
}
return q.dispatcherAlive(ctx)
}
func (q *CommandQueue) dispatcherAlive(ctx context.Context) bool {
q.mu.Lock()
defer q.mu.Unlock()
if time.Since(q.checkedAt) < heartbeatCacheTTL {
return q.alive
}
q.checkedAt = time.Now()
beats, err := q.store.redis.HGetAll(ctx, constants.RedisCarrierSchedulerHeartbeatKey).Result()
if err != nil {
return q.alive
}
q.alive = false
for _, raw := range beats {
if ms, convErr := strconv.ParseInt(raw, 10, 64); convErr == nil && time.Since(time.UnixMilli(ms)) < heartbeatFreshness {
q.alive = true
break
}
}
return q.alive
}
// WriteCommand 写入或覆盖卡的停复机指令。