fix: 停复机指令队列审查问题
All checks were successful
构建并部署到测试环境(无 SSH) / build-and-deploy (push) Successful in 9m24s
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:
@@ -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 写入或覆盖卡的停复机指令。
|
||||
|
||||
Reference in New Issue
Block a user