Files

89 lines
2.0 KiB
Go

package allocate
import (
"context"
"strings"
"sync"
"time"
"github.com/gogf/gf/v2/frame/g"
)
const (
defaultAllocateDeadlockMaxRetries = 3
defaultAllocateDeadlockRetryDelay = 200 * time.Millisecond
)
type allocateParentLock struct {
mu sync.Mutex
refs int
}
var allocateParentLocks = struct {
mu sync.Mutex
values map[string]*allocateParentLock
}{values: make(map[string]*allocateParentLock)}
func acquireAllocateParentLock(batchNo string) func() {
allocateParentLocks.mu.Lock()
lock := allocateParentLocks.values[batchNo]
if lock == nil {
lock = &allocateParentLock{}
allocateParentLocks.values[batchNo] = lock
}
lock.refs++
allocateParentLocks.mu.Unlock()
lock.mu.Lock()
return func() {
lock.mu.Unlock()
allocateParentLocks.mu.Lock()
lock.refs--
if lock.refs == 0 {
delete(allocateParentLocks.values, batchNo)
}
allocateParentLocks.mu.Unlock()
}
}
func allocateDeadlockMaxRetries(ctx context.Context) int {
retries := g.Cfg().MustGet(ctx, "batch.allocate.deadlockMaxRetries", defaultAllocateDeadlockMaxRetries).Int()
if retries < 0 {
return 0
}
return retries
}
func allocateDeadlockRetryDelay(ctx context.Context, attempt int) time.Duration {
delay := g.Cfg().MustGet(ctx, "batch.allocate.deadlockRetryDelay", defaultAllocateDeadlockRetryDelay).Duration()
if delay <= 0 {
delay = defaultAllocateDeadlockRetryDelay
}
for i := 0; i < attempt; i++ {
if delay >= time.Second {
return time.Second
}
delay *= 2
}
return delay
}
func waitAllocateDeadlockRetry(ctx context.Context, attempt int) error {
timer := time.NewTimer(allocateDeadlockRetryDelay(ctx, attempt))
defer timer.Stop()
select {
case <-ctx.Done():
return ctx.Err()
case <-timer.C:
return nil
}
}
func isAllocateDeadlock(err error) bool {
if err == nil {
return false
}
message := strings.ToLower(err.Error())
return strings.Contains(message, "deadlock found") || strings.Contains(message, "error 1213") || strings.Contains(message, "1213 (40001)")
}