s3: expose retry/backoff constants via environment variables - #296
s3: expose retry/backoff constants via environment variables#296alliasgher wants to merge 4 commits into
Conversation
|
@mantissahz Please review the PR. |
There was a problem hiding this comment.
Pull request overview
Adds environment-based S3 retry tuning as the backupstore-side foundation for longhorn/longhorn#12155.
Changes:
- Adds retry-attempt and backoff environment overrides.
- Preserves defaults for invalid or missing values.
- Adds parsing-focused unit tests.
Reviewed changes
Copilot reviewed 2 out of 2 changed files in this pull request and generated 2 comments.
| File | Description |
|---|---|
s3/s3_service.go |
Defines and applies S3 retry overrides. |
s3/s3_service_retry_test.go |
Tests override parsing and fallback behavior. |
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
| t.Setenv(EnvAWSRetryMaximumAttempts, tc.env) | ||
| if got := retryMaximumAttempts(); got != tc.want { | ||
| t.Fatalf("retryMaximumAttempts() = %d, want %d", got, tc.want) |
There was a problem hiding this comment.
Added in 21c2977 as TestRetryMaximumAttempts_AppliedToClient, covering both the backoff and non-backoff branches.
| if retryBackoff { | ||
| o.Retryer = retry.NewStandard(func(so *retry.StandardOptions) { | ||
| so.MaxAttempts = AWSRetryMaximumAttempts | ||
| so.MaxBackoff = AWSRetryMaximumBackoff | ||
| so.MaxAttempts = retryMaximumAttempts() | ||
| so.MaxBackoff = retryMaximumBackoff() |
There was a problem hiding this comment.
Confirmed and fixed in 21c2977. Reproduced with the vendored SDK: effective MaxAttempts was 5 with AWS_RETRY_MAXIMUM_ATTEMPTS=20, and 20 once o.RetryMaxAttempts is cleared.
mantissahz
left a comment
There was a problem hiding this comment.
LGTM
Hi @alliasgher,
Could you check if the Copilot's comment is valid and helpful?
|
@mantissahz It is valid, and it was a real bug. Fixed in I verified the mechanism rather than taking it on trust. Reproduced with the same SDK versions this repo vendors (s3 v1.97.3, config v1.32.13): So Copilot's first comment was right too: the existing tests only cover the env parsing helpers, so they passed either way. Added one asserting One caveat: |
| EnvAWSRetryMaxAttempts = "AWS_RETRY_MAX_ATTEMPTS" | ||
| // EnvAWSRetryMaximumAttempts overrides AWSRetryMaximumAttempts when set to a positive integer. | ||
| EnvAWSRetryMaximumAttempts = "AWS_RETRY_MAXIMUM_ATTEMPTS" | ||
| // EnvAWSRetryMaximumBackoff overrides AWSRetryMaximumBackoff when set to a Go duration string (e.g. "60s", "5m"). | ||
| EnvAWSRetryMaximumBackoff = "AWS_RETRY_MAXIMUM_BACKOFF" |
There was a problem hiding this comment.
Hi @alliasgher,
Could you try to add the new environment variables into this function setupS3Credential in the util/credential.go as AWSSecretKey?
Then we can allow users to set up the parameters in the Secret and pass them here.
There was a problem hiding this comment.
Done in b9449f8. Added the three names to types next to the other credential keys, copied them in getS3CredentialFromEnvVars and restored them in setupS3Credential, so they can be set through the Secret like AWS_ENDPOINTS and VIRTUAL_HOSTED_STYLE. The s3 package now points at the shared constants so the names are defined once.
There was a problem hiding this comment.
Correct, and fixed in b9449f8 along the lines you describe: shared constants in types/types.go, copied and restored in both S3 credential helpers. Added TestS3CredentialRoundTripCarriesRetrySettings, which fails without the change with all three keys empty on both sides of the round trip.
The S3 backupstore service currently hardcodes three retry-related
constants (`AWSRetryMaxAttempts`, `AWSRetryMaximumAttempts`,
`AWSRetryMaximumBackoff`) so operators running against S3-compatible
endpoints with different latency/reliability characteristics have no way
to tune them without recompiling the binary.
Add three optional environment-variable overrides, following the
existing `AWS_ENDPOINTS` / `VIRTUAL_HOSTED_STYLE` env-var pattern:
* `AWS_RETRY_MAX_ATTEMPTS` — integer, overrides AWSRetryMaxAttempts
* `AWS_RETRY_MAXIMUM_ATTEMPTS` — integer, overrides AWSRetryMaximumAttempts
* `AWS_RETRY_MAXIMUM_BACKOFF` — Go duration (e.g. "60s", "5m"),
overrides AWSRetryMaximumBackoff
Empty / missing / malformed values silently fall back to the existing
defaults, so there's no behaviour change for users that don't set them.
Refs longhorn/longhorn#12155
Signed-off-by: Ali <alliasgher123@gmail.com>
Signed-off-by: Ali <ali@kscope.ai>
NewFromConfig calls finalizeRetryMaxAttempts after the option callback. When o.RetryMaxAttempts is nonzero, which it is because newInstance passes config.WithRetryMaxAttempts, it wraps the retryer in retry.AddWithMaxAttempts. That capped the custom retryer at AWS_RETRY_MAX_ATTEMPTS, so AWS_RETRY_MAXIMUM_ATTEMPTS had no effect above 5. Clear o.RetryMaxAttempts when installing the custom retryer. The non-backoff path is unchanged and still honours AWS_RETRY_MAX_ATTEMPTS. The existing tests only cover the env parsing helpers and passed either way, so add one that asserts Retryer.MaxAttempts() on the constructed client. Signed-off-by: alliasgher <alliasgher123@gmail.com>
21c2977 to
7fd2623
Compare
The retry variables are read by newInstance in whichever process performs the S3 operation, but they are configured in the calling process. The credential map is what bridges the two, and it copies AWS_ENDPOINTS, AWS_CERT, the proxies and VIRTUAL_HOSTED_STYLE but not the new keys, so they were dropped and newInstance always saw the defaults. Add the three names to types alongside the other credential keys, copy them in getS3CredentialFromEnvVars and restore them in setupS3Credential. The s3 package now references the shared constants so the names are defined once. This also lets the values be supplied through the Secret, like the existing S3 credential fields. Signed-off-by: alliasgher <alliasgher123@gmail.com>
…urable-s3-retry # Conflicts: # s3/s3_service.go # util/credential.go
| // NewFromConfig runs finalizeRetryMaxAttempts after this callback, which | ||
| // wraps the retryer above in retry.AddWithMaxAttempts(o.RetryMaxAttempts) | ||
| // and would cap it at AWS_RETRY_MAX_ATTEMPTS. Clear it so the retryer's | ||
| // own AWS_RETRY_MAXIMUM_ATTEMPTS stays effective. | ||
| o.RetryMaxAttempts = 0 |
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## master #296 +/- ##
==========================================
+ Coverage 8.49% 10.63% +2.14%
==========================================
Files 24 24
Lines 2131 2153 +22
==========================================
+ Hits 181 229 +48
+ Misses 1924 1892 -32
- Partials 26 32 +6
Flags with carried forward coverage won't be shown. Click here to find out more. ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
Which issue(s) this PR fixes:
Issue longhorn/longhorn#12155
What this PR does / why we need it:
The S3 backupstore service hardcodes three retry-related constants —
AWSRetryMaxAttempts(5),AWSRetryMaximumAttempts(10), andAWSRetryMaximumBackoff(300s). Operators running against S3-compatible endpoints with different latency or reliability characteristics have no way to tune them without recompiling.This PR adds three optional environment-variable overrides, following the existing
AWS_ENDPOINTS/VIRTUAL_HOSTED_STYLEenv-var pattern already used by this file:AWS_RETRY_MAX_ATTEMPTSAWSRetryMaxAttemptsAWS_RETRY_MAXIMUM_ATTEMPTSAWSRetryMaximumAttemptsAWS_RETRY_MAXIMUM_BACKOFF60s,5m)AWSRetryMaximumBackoffEmpty, missing, or malformed values silently fall back to the existing defaults, so no behaviour change for users that don't set them. Unit tests for each helper cover unset / valid / zero / negative / malformed cases.
Special notes for your reviewer:
longhorn/longhornexposing these as global settings / backup-target URL parameters per the issue description; figured it was cleaner to land the library-side primitive first.Additional documentation or context