feat: sync Bot API gateway support

This commit is contained in:
A 2026-07-09 13:49:24 +08:00
parent 9a501f900a
commit 4c0cc2b7a7
44 changed files with 4609 additions and 49 deletions

View file

@ -17,6 +17,16 @@ type TempAuthKeyRetentionStore interface {
DeleteExpired(ctx context.Context, expiredBefore int64, limit int) (int, error)
}
// BotAPIUpdateRetentionStore 回收 Bot API getUpdates 投递队列的死行(性能审计 H1
// 已确认且超过宽限期的行 + 按消息 date 超过保留期的行(官方 Bot API updates 最多保留 24h
type BotAPIUpdateRetentionStore interface {
DeleteDeliveredOrExpired(ctx context.Context, confirmedGrace, maxAge time.Duration, limit int) (int, error)
}
// botAPIConfirmedGrace 是已确认 Bot API update 行的删除宽限:确认水位之下的行不会再被
// getUpdates 读取fromID 恒 > confirmed宽限仅防御 offset 回拨调试;回收目标是清堆积。
const botAPIConfirmedGrace = 15 * time.Minute
// tempAuthKeyExpiryGrace 是 temp key 过期后的回收宽限ResolveAuthKey 对
// 「已过期但 perm 已授权」的绑定是容忍的,立即删除会突然断掉这批宽限中的
// 连接;回收目标是清堆积,晚一天无妨。
@ -31,12 +41,14 @@ const tempAuthKeyExpiryGrace = 24 * time.Hour
// 丢消息。详见 docs/performance-audit.md 与 docs/compatibility-matrix.md。user_update_events
// 长期膨胀作为已知 todo。
type RetentionWorker struct {
outbox DispatchOutboxRetentionStore
tempKeys TempAuthKeyRetentionStore // 可为 nil不回收 temp key 绑定)
logger *zap.Logger
retention time.Duration
interval time.Duration
batch int
outbox DispatchOutboxRetentionStore
tempKeys TempAuthKeyRetentionStore // 可为 nil不回收 temp key 绑定)
botAPIUpdates BotAPIUpdateRetentionStore // 可为 nil不回收 Bot API 队列)
logger *zap.Logger
retention time.Duration
botAPIRetention time.Duration
interval time.Duration
batch int
}
func NewRetentionWorker(outbox DispatchOutboxRetentionStore, tempKeys TempAuthKeyRetentionStore, logger *zap.Logger, retention, interval time.Duration, batch int) *RetentionWorker {
@ -62,6 +74,16 @@ func NewRetentionWorker(outbox DispatchOutboxRetentionStore, tempKeys TempAuthKe
}
}
// WithBotAPIUpdateRetention 启用 bot_api_updates 队列回收retention <=0 时用官方语义默认 24h。
func (w *RetentionWorker) WithBotAPIUpdateRetention(store BotAPIUpdateRetentionStore, retention time.Duration) *RetentionWorker {
if retention <= 0 {
retention = 24 * time.Hour
}
w.botAPIUpdates = store
w.botAPIRetention = retention
return w
}
func (w *RetentionWorker) Run(ctx context.Context) {
w.runOnce(ctx)
ticker := time.NewTicker(w.interval)
@ -92,4 +114,12 @@ func (w *RetentionWorker) runOnce(ctx context.Context) {
w.logger.Info("回收过期 temp auth key 绑定完成", zap.Int("deleted", tempDeleted))
}
}
if w.botAPIUpdates != nil {
botAPIDeleted, err := w.botAPIUpdates.DeleteDeliveredOrExpired(ctx, botAPIConfirmedGrace, w.botAPIRetention, w.batch)
if err != nil {
w.logger.Warn("回收 bot_api_updates 队列失败", zap.Error(err))
} else if botAPIDeleted > 0 {
w.logger.Info("回收 bot_api_updates 队列完成", zap.Int("deleted", botAPIDeleted))
}
}
}