| 12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788 |
- package jobs
- import (
- "context"
- "errors"
- "fmt"
- "strings"
- "time"
- "fwh-toolbox-server/cache"
- "go.uber.org/zap"
- )
- const (
- emsLogCallSlotTTL = 2 * time.Minute
- emsLogCallSlotRetry = time.Second
- EmsLogCallTimeout = 30 * time.Minute
- )
- var ErrEmsLogCallTimeout = errors.New("EMS 日志单文件召唤超过 30 分钟")
- var ErrEmsLogCallSlotLost = errors.New("EMS 日志召唤名额已丢失")
- // EmsLogCallSlot serializes EMS calls for one SN across both download flows and instances.
- type EmsLogCallSlot struct {
- lock *cache.RedisLock
- ctx context.Context
- cancel context.CancelFunc
- done chan struct{}
- }
- func TryAcquireEmsLogCallSlot(ctx context.Context, sn string) (*EmsLogCallSlot, bool, error) {
- keySN := strings.ToUpper(strings.TrimSpace(sn))
- if keySN == "" {
- return nil, false, fmt.Errorf("EMS log SN is empty")
- }
- lock, ok, err := cache.TryLock(ctx, "ems-log:call:"+keySN, emsLogCallSlotTTL)
- if err != nil || !ok {
- return nil, ok, err
- }
- leaseCtx, cancel := context.WithCancel(ctx)
- slot := &EmsLogCallSlot{lock: lock, ctx: leaseCtx, cancel: cancel, done: make(chan struct{})}
- go slot.renew()
- return slot, true, nil
- }
- func AcquireEmsLogCallSlot(ctx context.Context, sn string) (*EmsLogCallSlot, error) {
- for {
- slot, ok, err := TryAcquireEmsLogCallSlot(ctx, sn)
- if err != nil || ok {
- return slot, err
- }
- select {
- case <-ctx.Done():
- return nil, ctx.Err()
- case <-time.After(emsLogCallSlotRetry):
- }
- }
- }
- func (s *EmsLogCallSlot) Context() context.Context { return s.ctx }
- func (s *EmsLogCallSlot) Release() {
- s.cancel()
- <-s.done
- s.lock.SafeRelease(context.Background())
- }
- func (s *EmsLogCallSlot) renew() {
- defer close(s.done)
- ticker := time.NewTicker(emsLogCallSlotTTL / 3)
- defer ticker.Stop()
- for {
- select {
- case <-s.ctx.Done():
- return
- case <-ticker.C:
- ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
- err := s.lock.Extend(ctx, emsLogCallSlotTTL)
- cancel()
- if err != nil {
- zap.L().Error("EMS 日志召唤名额续期失败", zap.Error(err))
- s.cancel()
- return
- }
- }
- }
- }
|