feat: add gitlab/github provider API retries - #2854
Conversation
There was a problem hiding this comment.
Code Review
This pull request introduces a rate-limit-aware API retry mechanism for GitHub and GitLab providers, configurable via settings and configmaps, using a new retryhttp package that wraps http.RoundTripper with exponential backoff and jitter. Feedback on the changes highlights three key issues: first, the GitLab retry policy incorrectly disables retries for network-level errors (when resp is nil) even on idempotent requests; second, a potential integer overflow can occur in retryhttp backoff calculations if the attempt count is extremely high; and third, returning a previously closed response when req.GetBody() fails during a retry can lead to unexpected behavior.
Important
The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.
b6b281d to
e0ed9ca
Compare
Codecov Report❌ Patch coverage is Additional details and impacted files@@ Coverage Diff @@
## main #2854 +/- ##
==========================================
+ Coverage 68.77% 68.82% +0.05%
==========================================
Files 197 198 +1
Lines 16866 17041 +175
==========================================
+ Hits 11599 11728 +129
- Misses 4405 4434 +29
- Partials 862 879 +17
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:
|
e0ed9ca to
8145908
Compare
8145908 to
5a1975f
Compare
|
hard to test to be honest, the only way to do this is to have a fake GitHub/Gitlab API server which is a bit crazy overblown |
|
/gemini review |
There was a problem hiding this comment.
Code Review
This pull request introduces Git provider API retry capabilities for GitHub and GitLab in Pipelines-as-Code. It adds configuration settings (enable-api-retry, api-retry-max-attempts, and api-retry-max-wait-seconds) and implements a rate-limit-aware retrying HTTP transport (retryhttp) using exponential backoff with jitter. Feedback on the changes highlights several potential nil pointer dereference vulnerabilities when handling HTTP responses and headers (specifically in retryhttp.go and gitlab.go), a potential timer leak when using time.After in a select block, and unhandled errors when initializing the GitHub enterprise client.
Important
The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.
5a1975f to
eea202d
Compare
Paco Review
|
zakisk
left a comment
There was a problem hiding this comment.
Paco inline comments -- see the Paco Review summary comment for the overview.
5621eac to
f7c11a9
Compare
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 15 out of 15 changed files in this pull request and generated no new comments.
Suppressed comments (2)
pkg/provider/gitlab/gitlab.go:113
- When retries are disabled,
clientOptionsappendsgitlab.WithoutRetries(), which disables the GitLab client-go library’s built-in retries (the vendored client defaults toRetryMax: 5). Previously, the provider created the client with onlyWithBaseURL, so addingWithoutRetries()changes behavior even though the feature is meant to be disabled by default.
opts := []gitlab.ClientOptionFunc{gitlab.WithBaseURL(apiURL)}
if v.pacInfo == nil || !v.pacInfo.EnableAPIRetry {
return append(opts, gitlab.WithoutRetries())
}
pkg/provider/retryhttp/retryhttp.go:140
Retry-Aftercan be either a delay in seconds or an HTTP-date. This transport only parses the seconds form, so an HTTP-date value would be ignored and the code would fall back to exponential backoff (potentially retrying earlier than the server asked).
if s := resp.Header.Get("Retry-After"); s != "" {
if secs, err := strconv.Atoi(s); err == nil {
wait := time.Duration(secs) * time.Second
if wait > t.opts.MaxWait {
return 0, false
f7c11a9 to
a500a4f
Compare
|
Pushed an update that reworks this after the review feedback. Summary of how it behaves now. Default behaviour (
|
| Setting | Default | Meaning |
|---|---|---|
enable-api-retry |
false |
Opt in to the PAC retry policy |
api-retry-max-attempts |
4 |
Total attempts, initial request included |
api-retry-max-wait-seconds |
120 |
Cap on the wait between attempts |
Both providers now read these through the same settings.DefaultAPIRetryMaxAttempts / DefaultAPIRetryMaxWaitSeconds constants, so an unset or invalid api-retry-max-attempts falls back to 4 on GitHub and GitLab. Previously GitLab clipped it to 1, which silently disabled retries (thanks @zakisk).
What gets retried
- Rate limits — 429, and on GitHub 403 with rate-limit headers.
- Transient failures — 5xx and network errors, but only for
GET,HEADandOPTIONS. Mutations are never repeated after an uncertain failure, since the server may already have applied them and we would end up with duplicate comments or statuses.
Backoff
- On an actual rate-limit response,
Retry-After/X-RateLimit-Reset/RateLimit-Resetare honoured, with jitter, capped atapi-retry-max-wait-seconds. If the reset is further away than the cap, PAC gives up rather than holding the event. - On any other retryable failure, a short bounded delay is used.
That second point is a fix from this round: the GitLab backoff used to call LinearJitterBackoff(minWait, maxWait, ...), which draws a uniform random value in [1s, 120s] rather than backing off exponentially. A single transient 500 on a GET could therefore stall event processing for minutes. The rate-limit headers were also being consulted on non-429 responses, and GitLab sets RateLimit-Reset on ordinary responses, so a plain 500 could be delayed until the rate-limit window reset.
Unrelated regressions fixed
While validating this I found the branch had accidentally reverted several nil-safety fixes from e309d1a67 ("fix: detect and prevent nil pointer crashes"), most likely a bad rebase. Four existing tests were failing on the previous tip, two with nil-pointer panics. Restored:
MakeClientreturning an error instead of a nil client on an invalid enterprise URL.expandGlobAndAddRepoIDsreporting an invalid glob pattern.- The
resp == nilguards in three paginated GitHub loops.
make lint and go test ./pkg/... are green.
a500a4f to
5a0859c
Compare
Allow administrators to opt in to retrying temporary GitHub and GitLab API failures. This prevents short rate-limit windows and provider outages from immediately dropping webhook work that could succeed a few moments later. Keep the feature disabled by default so existing installations retain their current behavior. With the setting off, GitHub performs no retries and GitLab keeps the retry behavior of the upstream GitLab client. When enabled, administrators can control how many times an operation is attempted and how long PAC may wait for the provider to recover, and both providers read those settings the same way. Spread retries over time rather than sending every request again at once. Use the provider rate-limit headers only on an actual rate-limit response and give other temporary failures a short bounded delay. Stop retrying when the provider asks PAC to wait longer than the configured limit, avoiding long-running webhook work and reducing the risk of releasing a large backlog in one wave. Apply the same behavior consistently to GitHub and GitLab operations, including GitHub App setup and temporary provider clients. Avoid repeating requests when doing so could accidentally create duplicate changes on the provider. Document the new configuration and cover enabled, disabled, exhausted, and successful retry scenarios with automated tests. A full end-to-end test is not included because safely forcing rate limits on shared live provider accounts is disruptive and unreliable. Co-Authored-By: Claude <noreply@anthropic.com> Jira: https://issues.redhat.com/browse/SRVKP-12884 Signed-off-by: Chmouel Boudjnah <chmouel@redhat.com>
5a0859c to
b52e222
Compare

📝 Description of the Change
Allow administrators to opt in to retrying temporary GitHub and GitLab API failures. This prevents short rate-limit windows and provider outages from immediately dropping webhook work that could succeed a few moments later.
Key aspects:
🔗 Linked GitHub Issue
SRVKP-12884
🧪 Testing Strategy
Testing coverage: Unit tests document enabled, disabled, exhausted, and successful retry scenarios, the attempt-limit and wait-cap behaviour, the idempotency rules, and the transient-failure backoff bounds. A full end-to-end test is not included because safely forcing rate limits on shared live provider accounts is disruptive and unreliable.
🤖 AI Assistance
✅ Submitter Checklist
fix:,feat:) matches the "Type of Change" I selected above.make testandmake lintlocally to check for and fix any issues. For an efficient workflow, I have considered installing pre-commit and runningpre-commit installto automate these checks.