From 51873463fd1358658a2fcc3d056b9f4b1e5a3df0 Mon Sep 17 00:00:00 2001 From: KengoWada Date: Tue, 29 Apr 2025 22:55:42 +0300 Subject: [PATCH 1/3] refactor(backoff): Update Calculate method to use min delay and jitter strategy (#14) --- backoff.go | 44 +++++++++++++++++++++++--------------------- 1 file changed, 23 insertions(+), 21 deletions(-) diff --git a/backoff.go b/backoff.go index 674893d..3400b13 100644 --- a/backoff.go +++ b/backoff.go @@ -6,10 +6,8 @@ import ( ) var DefaultBackoffPolicy = BackoffPolicy{ - BaseDelay: 1 * time.Second, - MaxDelay: 30 * time.Second, - UseJitter: true, - JitterRangeMs: 300, + BaseDelay: 1 * time.Second, + MaxDelay: 30 * time.Second, } // Backoff defines the interface for calculating backoff delays between retries. @@ -21,25 +19,29 @@ type Backoff interface { Calculate(retries uint) time.Duration } -// BackoffPolicy defines the configuration for handling retries with backoff logic. -// It provides settings for base delay, maximum delay, jitter, and the range for jitter. +// BackoffPolicy specifies the parameters for calculating exponential backoff with jitter. +// It defines the base delay and the maximum delay allowed between retries. +// +// Fields: +// - BaseDelay: Specifies the initial delay duration. +// - MaxDelay: Sets the upper bound for the backoff delay, preventing unbounded wait times. type BackoffPolicy struct { - BaseDelay time.Duration // Base delay before the first retry (e.g., 1 second) - MaxDelay time.Duration // Maximum delay before retrying again (e.g., 60 seconds) - UseJitter bool // Whether to add random jitter to the delay - JitterRangeMs int // Maximum jitter in milliseconds (e.g., 500ms) + BaseDelay time.Duration + MaxDelay time.Duration } -// Calculate calculates the delay before the next retry based on the backoff policy. -// It considers the retry count, base delay, maximum delay, and jitter. +// Calculate returns a jittered backoff duration based on the number of retries. +// It uses exponential backoff with full jitter, where the delay is randomly chosen +// between 0 and the calculated maximum delay. +// The maximum delay grows exponentially with each retry, capped by MaxDelay. +// +// Example: +// +// BaseDelay = 100ms, MaxDelay = 3s, retries = 3 +// Max calculated delay = min(100ms * 2^3, 3s) = 800ms +// Returned delay = random duration in [0, 800ms) func (b *BackoffPolicy) Calculate(retries uint) time.Duration { - // Calculate the exponential backoff delay, doubling with each retry. - delay := min(b.BaseDelay*time.Duration(1< 0 { - jitter := time.Duration(rand.Intn(b.JitterRangeMs)) * time.Millisecond - delay += jitter - } - - return delay + maxDelay := min(b.BaseDelay*(1< Date: Tue, 29 Apr 2025 22:56:21 +0300 Subject: [PATCH 2/3] test(backoff): Update tests to reflect new Calculate method behavior (#14) --- tests/backoff_test.go | 79 ++++++++++++++----------------------------- tests/manager_test.go | 6 ++-- tests/worker_test.go | 6 ++-- 3 files changed, 29 insertions(+), 62 deletions(-) diff --git a/tests/backoff_test.go b/tests/backoff_test.go index 5ac9c1c..64e0c65 100644 --- a/tests/backoff_test.go +++ b/tests/backoff_test.go @@ -10,74 +10,45 @@ import ( func TestBackoffPolicy(t *testing.T) { tests := []struct { - name string - backoffPolicy *taskqueue.BackoffPolicy - retries uint - expectedDelay time.Duration - allowJitter bool + name string + retries uint + maxDelay time.Duration + baseDelay time.Duration }{ { - name: "retry one", - backoffPolicy: &taskqueue.BackoffPolicy{ - BaseDelay: 1 * time.Millisecond, - MaxDelay: 10 * time.Millisecond, - }, - retries: 1, - expectedDelay: 1 * time.Millisecond, + name: "first retry", + retries: 1, + baseDelay: 1 * time.Millisecond, + maxDelay: 10 * time.Millisecond, }, { - name: "retry three", - backoffPolicy: &taskqueue.BackoffPolicy{ - BaseDelay: 1 * time.Millisecond, - MaxDelay: 10 * time.Millisecond, - }, - retries: 3, - expectedDelay: 4 * time.Millisecond, + name: "second retry", + retries: 1, + baseDelay: 5 * time.Millisecond, + maxDelay: 20 * time.Millisecond, }, { - name: "max delay", - backoffPolicy: &taskqueue.BackoffPolicy{ - BaseDelay: 5 * time.Millisecond, - MaxDelay: 10 * time.Millisecond, - }, - retries: 5, - expectedDelay: 10 * time.Millisecond, // should not exceed MaxDelay + name: "third retry", + retries: 3, + baseDelay: 5 * time.Millisecond, + maxDelay: 17 * time.Millisecond, }, { - name: "jitter applied", - backoffPolicy: &taskqueue.BackoffPolicy{ - BaseDelay: 1 * time.Millisecond, - MaxDelay: 10 * time.Millisecond, - UseJitter: true, - JitterRangeMs: 5, - }, - retries: 1, - expectedDelay: 1 * time.Millisecond, // with jitter this will vary slightly - allowJitter: true, - }, - { - name: "no jitter applied", - backoffPolicy: &taskqueue.BackoffPolicy{ - BaseDelay: 1 * time.Millisecond, - MaxDelay: 10 * time.Millisecond, - UseJitter: false, - }, - retries: 1, - expectedDelay: 1 * time.Millisecond, - allowJitter: false, + name: "fourth retry", + retries: 4, + baseDelay: 3 * time.Millisecond, + maxDelay: 9 * time.Millisecond, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - delay := tt.backoffPolicy.Calculate(tt.retries) + bp := &taskqueue.BackoffPolicy{BaseDelay: tt.baseDelay, MaxDelay: tt.maxDelay} + delay := bp.Calculate(tt.retries) + maxDelay := min(bp.BaseDelay*(1< Date: Tue, 29 Apr 2025 22:56:56 +0300 Subject: [PATCH 3/3] docs(example): Update example to reflect revised backoff Calculate method (#14) --- example/main.go | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/example/main.go b/example/main.go index 8ef1e2a..a73478e 100644 --- a/example/main.go +++ b/example/main.go @@ -33,10 +33,8 @@ func main() { } backoffPolicy := &taskqueue.BackoffPolicy{ - BaseDelay: 2 * time.Second, - MaxDelay: 60 * time.Second, - UseJitter: true, - JitterRangeMs: 500, + BaseDelay: 2 * time.Second, + MaxDelay: 60 * time.Second, } manager := taskqueue.NewManager(broker, taskqueue.DefaultWorkerFactory, 5, taskqueue.WithBackoffPolicy(backoffPolicy))