1
0
mirror of https://github.com/gilbertchen/duplicacy synced 2025-12-15 15:53:26 +00:00

Slightly different backoff for GCD rate limit handling

This commit is contained in:
Gilbert Chen
2017-10-17 15:20:01 -04:00
parent 4640c20dec
commit b788b9887c

View File

@@ -27,11 +27,11 @@ import (
type GCDStorage struct { type GCDStorage struct {
RateLimitedStorage RateLimitedStorage
service *drive.Service service *drive.Service
idCache map[string]string idCache map[string]string
idCacheLock *sync.Mutex idCacheLock *sync.Mutex
backoffs []float64 // desired backoff time in seconds for each thread backoffs []int // desired backoff time in seconds for each thread
attempts []int // number of tries for each thread attempts []int // number of failed attempts since last success for each thread
isConnected bool isConnected bool
numberOfThreads int numberOfThreads int
@@ -45,20 +45,18 @@ type GCDConfig struct {
Token oauth2.Token `json:"token"` Token oauth2.Token `json:"token"`
} }
func (storage *GCDStorage) computeInitialBackoff() float64 {
minimumInitialDelay := float64(storage.numberOfThreads) * 0.1
maximumInitialDelay := float64(storage.numberOfThreads) * 0.2
return rand.Float64() * (maximumInitialDelay - minimumInitialDelay + 1.0) + minimumInitialDelay
}
func (storage *GCDStorage) shouldRetry(threadIndex int, err error) (bool, error) { func (storage *GCDStorage) shouldRetry(threadIndex int, err error) (bool, error) {
const MAX_BACKOFF_TIME = 64
const MAX_ATTEMPTS = 15 const MAX_ATTEMPTS = 15
maximumBackoff := 64
if maximumBackoff < storage.numberOfThreads {
maximumBackoff = storage.numberOfThreads
}
retry := false retry := false
message := "" message := ""
if err == nil { if err == nil {
storage.backoffs[threadIndex] = storage.computerInitialBackoff() storage.backoffs[threadIndex] = 1
storage.attempts[threadIndex] = 0 storage.attempts[threadIndex] = 0
return false, nil return false, nil
} else if e, ok := err.(*googleapi.Error); ok { } else if e, ok := err.(*googleapi.Error); ok {
@@ -94,23 +92,23 @@ func (storage *GCDStorage) shouldRetry(threadIndex int, err error) (bool, error)
} }
if !retry || storage.attempts[threadIndex] >= MAX_ATTEMPTS { if !retry || storage.attempts[threadIndex] >= MAX_ATTEMPTS {
LOG_INFO("GCD_RETRY", "[%d] Maximum number of retries reached (backoff: %.2f, attempts: %d)", LOG_INFO("GCD_RETRY", "[%d] Maximum number of retries reached (backoff: %d, attempts: %d)",
threadIndex, storage.backoffs[threadIndex], storage.attempts[threadIndex]) threadIndex, storage.backoffs[threadIndex], storage.attempts[threadIndex])
storage.backoffs[threadIndex] = storage.computerInitialBackoff() storage.backoffs[threadIndex] = 1
storage.attempts[threadIndex] = 0 storage.attempts[threadIndex] = 0
return false, err return false, err
} }
if storage.backoffs[threadIndex] < MAX_BACKOFF_TIME { if storage.backoffs[threadIndex] < maximumBackoff {
storage.backoffs[threadIndex] *= 2.0 storage.backoffs[threadIndex] *= 2
} }
if storage.backoffs[threadIndex] > MAX_BACKOFF_TIME { if storage.backoffs[threadIndex] > maximumBackoff {
storage.backoffs[threadIndex] = MAX_BACKOFF_TIME storage.backoffs[threadIndex] = maximumBackoff
} }
storage.attempts[threadIndex] += 1 storage.attempts[threadIndex] += 1
delay := storage.backoffs[threadIndex]*rand.Float64() delay := float64(storage.backoffs[threadIndex]) * rand.Float64() * 2
LOG_DEBUG("GCD_RETRY", "[%d] %s; retrying after %.2f seconds (backoff: %.2f, attempts: %d)", LOG_DEBUG("GCD_RETRY", "[%d] %s; retrying after %.2f seconds (backoff: %d, attempts: %d)",
threadIndex, message, delay, storage.backoffs[threadIndex], storage.attempts[threadIndex]) threadIndex, message, delay, storage.backoffs[threadIndex], storage.attempts[threadIndex])
time.Sleep(time.Duration(delay * float64(time.Second))) time.Sleep(time.Duration(delay * float64(time.Second)))
return true, nil return true, nil
@@ -295,12 +293,13 @@ func CreateGCDStorage(tokenFile string, storagePath string, threads int) (storag
numberOfThreads: threads, numberOfThreads: threads,
idCache: make(map[string]string), idCache: make(map[string]string),
idCacheLock: &sync.Mutex{}, idCacheLock: &sync.Mutex{},
backoffs: make([]float64, threads), backoffs: make([]int, threads),
attempts: make([]int, threads), attempts: make([]int, threads),
} }
for b := range storage.backoffs { for i := range storage.backoffs {
storage.backoffs[b] = 0.1 * float64(storage.numberOfThreads) // at the first error, we should still sleep some amount storage.backoffs[i] = 1
storage.attempts[i] = 0
} }
storagePathID, err := storage.getIDFromPath(0, storagePath) storagePathID, err := storage.getIDFromPath(0, storagePath)