diff --git a/README.md b/README.md index 6fbede5..3ec1f24 100644 --- a/README.md +++ b/README.md @@ -85,7 +85,7 @@ The following environment variables can be specified: | `RUN_GRAVITY` | false | true | Specifies whether to run gravity after syncing | | `TZ` | n/a | `Europe/London` | Specifies the timezone for logs and cron | | `CLIENT_SKIP_TLS_VERIFICATION` | false | true | Skips SSL certificate verification | -| `CLIENT_RETRY_DELAY` | 1 | 5 | Seconds to delay betweeen connection attempts | +| `CLIENT_RETRY_DELAY_SECONDS` | 1 | 5 | Seconds to delay betweeen connection attempts | > **Note:** The following optional settings apply only if `FULL_SYNC=false`. They allow for granular control of synchronization if a full sync is not wanted. diff --git a/internal/config/config.go b/internal/config/config.go index 6c6eb3e..22dbd0d 100644 --- a/internal/config/config.go +++ b/internal/config/config.go @@ -20,8 +20,8 @@ type Config struct { } type Client struct { - SkipSSLVerification bool `default:"false" envconfig:"CLIENT_SKIP_TLS_VERIFICATION"` - RetryDelay uint `default:"1" envconfig:"CLIENT_RETRY_DELAY"` + SkipSSLVerification bool `default:"false" envconfig:"CLIENT_SKIP_TLS_VERIFICATION"` + RetryDelay int64 `default:"1" envconfig:"CLIENT_RETRY_DELAY_SECONDS"` } type GravitySettings struct { diff --git a/internal/sync/retry.go b/internal/sync/retry.go index 2491dc1..be1223e 100644 --- a/internal/sync/retry.go +++ b/internal/sync/retry.go @@ -16,13 +16,14 @@ const ( AttemptsDeleteSession = 3 ) -func withRetry(retryFunc func() error, attempts, delay uint) error { +func withRetry(retryFunc func() error, attempts uint, delay int64) error { return retry.Do( func() error { return retryFunc() }, retry.Attempts(attempts), retry.Delay(time.Duration(delay)*time.Second), + retry.DelayType(retry.FixedDelay), retry.OnRetry(func(n uint, err error) { log.Debug().Msg(fmt.Sprintf("Retrying(%d): %v", n+1, err)) }), diff --git a/internal/sync/retry_test.go b/internal/sync/retry_test.go new file mode 100644 index 0000000..e5ca1cd --- /dev/null +++ b/internal/sync/retry_test.go @@ -0,0 +1,77 @@ +package sync + +import ( + "errors" + "testing" + "time" + + "github.com/stretchr/testify/assert" +) + +// Test that the retries are actually causing the expected +// delay and additionally make sure they are a fixed delay +// and not the default backoff value. +func TestWithRetry_DelayBetweenRetries(t *testing.T) { + t.Parallel() + + counter := 0 + start := time.Now() + err := withRetry(func() error { + counter++ + if counter < 3 { + return errors.New("test error") + } + return nil + }, 3, 1) // 3 attempts, 1-second delay + + elapsed := time.Since(start) + + assert.NoError(t, err, "Expected success before max attempts") + assert.GreaterOrEqual(t, elapsed.Seconds(), 2.0, "Expected at least 2 seconds of delay between all retries") + assert.LessOrEqual(t, elapsed.Seconds(), 2.5, "Expected at most 2.5 seconds of delay between all retries") +} + +// Test that we do not retry on immediate success. +func TestWithRetry_NoRetriesOnImmediateSuccess(t *testing.T) { + t.Parallel() + + counter := 0 + err := withRetry(func() error { + counter++ + return nil + }, 5, 2) // 5 attempts, 2-second delay + + assert.NoError(t, err, "Expected no error when function succeeds immediately") + assert.Equal(t, 1, counter, "Expected function to run only once without retries") +} + +// Test that we properly succeed after a few but not max retires. +func TestWithRetry_SuccessAfterRetries(t *testing.T) { + t.Parallel() + + counter := 0 + err := withRetry(func() error { + counter++ + if counter < 2 { + return errors.New("test error") + } + return nil + }, 3, 1) // 3 attempts, 1-second delay + + assert.NoError(t, err, "Expected success before max attempts") + assert.Equal(t, 2, counter, "Expected function to retry once before success") +} + +// Test to make sure we properly fail after max amount of retries. +func TestWithRetry_MaxAttemptsFailure(t *testing.T) { + t.Parallel() + + counter := 0 + err := withRetry(func() error { + counter++ + return errors.New("test error") + }, 3, 1) // 3 attempts, 1-second delay + + assert.Error(t, err, "Expected an error after max attempts") + assert.Equal(t, 3, counter, "Expected function to be retried 3 times") +}