Files
sub2api/backend/internal/service/subscription_maintenance_queue.go
yangjianbo 54fe363257 fix(backend): 修复代码审核发现的 8 个确认问题
- P0-1: subscription_maintenance_queue 使用 RWMutex 防止 channel close/send 竞态
- P0-2: billing_service CalculateCostWithLongContext 修复被吞没的 out-range 错误
- P1-1: timing_wheel_service Schedule/ScheduleRecurring 添加 SetTimer 错误日志
- P1-2: sora_gateway_service StoreFromURLs 失败时降级使用原始 URL
- P1-3: concurrency_cache 用 Pipeline 替代 Lua 脚本兼容 Redis Cluster
- P1-6: sora_media_cleanup_service runCleanup 添加 nil cfg/storage 防护

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-10 17:51:49 +08:00

89 lines
1.8 KiB
Go
Raw 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 service
import (
"fmt"
"log"
"sync"
)
// SubscriptionMaintenanceQueue 提供"有界队列 + 固定 worker"的后台执行器。
// 用于从请求热路径触发维护动作时,避免无限 goroutine 膨胀。
type SubscriptionMaintenanceQueue struct {
queue chan func()
wg sync.WaitGroup
stop sync.Once
mu sync.RWMutex // 保护 closed 标志与 channel 操作的原子性
closed bool
}
func NewSubscriptionMaintenanceQueue(workerCount, queueSize int) *SubscriptionMaintenanceQueue {
if workerCount <= 0 {
workerCount = 1
}
if queueSize <= 0 {
queueSize = 1
}
q := &SubscriptionMaintenanceQueue{
queue: make(chan func(), queueSize),
}
q.wg.Add(workerCount)
for i := 0; i < workerCount; i++ {
go func(workerID int) {
defer q.wg.Done()
for fn := range q.queue {
func() {
defer func() {
if r := recover(); r != nil {
log.Printf("SubscriptionMaintenance worker panic: %v", r)
}
}()
fn()
}()
}
}(i)
}
return q
}
// TryEnqueue 尝试将任务入队。
// 当队列已满时返回 error调用方应该选择跳过并记录告警/限频日志)。
// 当队列已关闭时返回 error不会 panic。
func (q *SubscriptionMaintenanceQueue) TryEnqueue(task func()) error {
if q == nil {
return fmt.Errorf("maintenance queue is nil")
}
if task == nil {
return fmt.Errorf("maintenance task is nil")
}
q.mu.RLock()
defer q.mu.RUnlock()
if q.closed {
return fmt.Errorf("maintenance queue stopped")
}
select {
case q.queue <- task:
return nil
default:
return fmt.Errorf("maintenance queue full")
}
}
func (q *SubscriptionMaintenanceQueue) Stop() {
if q == nil {
return
}
q.stop.Do(func() {
q.mu.Lock()
q.closed = true
close(q.queue)
q.mu.Unlock()
q.wg.Wait()
})
}