Skip to content

Conversation

@lupppig
Copy link

@lupppig lupppig commented Jan 21, 2026

This fixes several issues in the SDK: 1. A context leak in Execute() where the cancel function was deferred incorrectly. 2. Unseeded random jitter in retryDelay(). 3. Improved error reporting in CheckTestServer.

Summary by CodeRabbit

Release Notes

  • Performance & Reliability

    • Optimized retry logic with improved delay handling during network failures
    • Enhanced resource cleanup during request processing for better stability
  • Tests

    • Improved test utility error reporting and response handling

✏️ Tip: You can customize this high-level summary in your review settings.

@lupppig
Copy link
Author

lupppig commented Jan 21, 2026

@mittalyashu

Copy link
Member

@mittalyashu mittalyashu Jan 24, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@lupppig Can you please revert back the change in this file?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

have done that

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@lupppig The PR is still showing this file is being modified, try rebasing the PR.

Copy link
Author

@lupppig lupppig Jan 25, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@coderabbitai
Copy link

coderabbitai bot commented Jan 25, 2026

📝 Walkthrough

Walkthrough

Two files modified to improve resource cleanup and randomness handling. In requestconfig.go, jitter generation now uses locally seeded RNG, and context cancellation timing changed from deferred to explicit invocation across multiple code paths. In testutil.go, response body closing and error logging details were added to the test server check function.

Changes

Cohort / File(s) Summary
Context Cancellation & Jitter Generation
internal/requestconfig/requestconfig.go
Modified retryDelay to use locally seeded RNG instead of global rand.Int63n for jitter generation. In Execute, removed deferred context cancellation and replaced with explicit immediate cancellation after HTTP requests and across multiple failure/cleanup paths (body recreation, error handling, response closure), ensuring earlier resource release.
Test Server Validation
internal/testutil/testutil.go
Updated CheckTestServer to properly close HTTP response bodies on success. Enhanced error logging to include actual error values when requests fail, while maintaining existing env var skip logic.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Poem

🐰 A jitter of seeds, no longer shared,
Context whispers sooner, more carefully paired,
Response bodies tucked in their beds,
Errors now speaking what's in their heads,
Cleanup happens faster, the code's now aware!

🚥 Pre-merge checks | ✅ 2 | ❌ 1
❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately summarizes the main changes: context leak fix, jitter seeding, and test utility improvements are all present in the changeset.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing touches
  • 📝 Generate docstrings

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Updated signed commit message template in CLA workflow.
@lupppig
Copy link
Author

lupppig commented Jan 25, 2026

I have read the CLA Document and I hereby sign the CLA

@lupppig lupppig requested a review from mittalyashu January 25, 2026 11:30
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (2)
internal/testutil/testutil.go (1)

11-24: Close the HTTP response body to avoid leaking connections.

The response body from http.Get(url) must be closed to prevent connection leaks and allow the transport to reuse keep-alive connections.

🐛 Proposed fix
-	if _, err := http.Get(url); err != nil {
+	resp, err := http.Get(url)
+	if resp != nil {
+		defer resp.Body.Close()
+	}
+	if err != nil {
internal/requestconfig/requestconfig.go (1)

472-484: Canceling the timeout context immediately before reading the response body will cause context-related errors during body reads.

When cancel() is called at line 480 right after handler(req), the context becomes cancelled and its error state becomes non-nil. This interferes with subsequent body reading operations (at lines 529 and 560), which may fail with context cancelled errors. According to Go's net/http semantics, the request context controls the entire lifetime including response body reading, so the cancellation must be deferred until after the body is fully read or before retrying.

The fix should:

  1. Only cancel the timeout context if it was actually cancelled during the request (move the cancel calls into the error-handling paths)
  2. Delay cancellation until after body reading completes (after io.ReadAll calls)
  3. Cancel before retrying (to prevent accumulation of context resources)
  4. Rely on the existing bodyWithTimeout wrapper for cases where the body is returned without being read immediately
Proposed fix (defer cancel until after body operations)
		res, err = handler(req)
-
-		// Close the timeout context for this attempt if one was created
-		if cancel != nil {
-			cancel()
-			cancel = nil
-		}
		if ctx != nil && ctx.Err() != nil {
+			if cancel != nil {
+				cancel()
+				cancel = nil
+			}
			return ctx.Err()
		}

		// Close the response body before retrying to prevent connection leaks
		if res != nil && res.Body != nil {
			res.Body.Close()
		}
+		if cancel != nil {
+			cancel()
+			cancel = nil
+		}
		time.Sleep(retryDelay(res, retryCount))

	if err != nil {
+		if cancel != nil {
+			cancel()
+			cancel = nil
+		}
		return err
	}

		contents, err := io.ReadAll(res.Body)
		res.Body.Close()
+		if cancel != nil {
+			cancel()
+			cancel = nil
+		}

	contents, err := io.ReadAll(res.Body)
	res.Body.Close()
+	if cancel != nil {
+		cancel()
+		cancel = nil
+	}
🤖 Fix all issues with AI agents
In @.github/workflows/cla.yml:
- Around line 36-37: Replace the hard-coded CLA commit message under the
signed-commit-message key so it uses CLA Assistant Lite template variables
(e.g., $contributorName and $pullRequestNo) instead of "Darasimi Kelani" and
"#14"; update the string to reference $contributorName and $pullRequestNo
(optionally include $owner and $repo) so each signature records the correct
contributor and PR number.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@lupppig The PR is still showing this file is being modified, try rebasing the PR.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Fix all issues with AI agents
In `@internal/requestconfig/requestconfig.go`:
- Around line 479-483: In the ctx.Err() and handler error-return paths, ensure
any non-nil *http.Response returned by the custom HTTPDoer is closed to avoid
connection leaks: before calling cancel() or returning on error, check if resp
!= nil and call resp.Body.Close() (ignoring the close error) and then set resp =
nil; apply this change in the ctx.Err() branch (where cancel is cleared) and in
the handler error-return branch (where handler returns err) so both error paths
mirror the retry-loop cleanup that currently closes resp.Body.
🧹 Nitpick comments (1)
internal/testutil/testutil.go (1)

11-14: Add a timeout to the test server probe.

http.Get uses the default client with no timeout, which can hang tests on stalled DNS/connection attempts. Consider a small timeout or context deadline.

🔧 Suggested change
 import (
 	"net/http"
 	"os"
 	"strconv"
 	"testing"
+	"time"
 )
 
 func CheckTestServer(t *testing.T, url string) bool {
-	res, err := http.Get(url)
+	client := &http.Client{Timeout: 5 * time.Second}
+	res, err := client.Get(url)
 	if err == nil {
 		res.Body.Close()
 	}

Comment on lines 479 to +483
if ctx != nil && ctx.Err() != nil {
if cancel != nil {
cancel()
cancel = nil
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

cat -n internal/requestconfig/requestconfig.go | sed -n '470,495p'

Repository: logchimp/logchimp-go

Length of output: 773


🏁 Script executed:

cat -n internal/requestconfig/requestconfig.go | sed -n '450,520p'

Repository: logchimp/logchimp-go

Length of output: 2469


🏁 Script executed:

cat -n internal/requestconfig/requestconfig.go | sed -n '520,560p'

Repository: logchimp/logchimp-go

Length of output: 1371


🏁 Script executed:

cat -n internal/requestconfig/requestconfig.go | sed -n '400,450p'

Repository: logchimp/logchimp-go

Length of output: 1931


🌐 Web query:

http.Client.Do returns response with error Go standard library behavior

💡 Result:

In Go’s standard library (net/http), (*http.Client).Do(req) follows this contract:

  • HTTP status codes (including 4xx/5xx) do not produce an error. You get resp != nil, err == nil and must read/close resp.Body. [1]
  • err != nil means either:
    • a client policy error (most commonly redirect handling via CheckRedirect), or
    • a failure to speak HTTP (network/protocol/TLS, etc.). [1]
  • The only standard-library case where both resp != nil and err != nil occur is when CheckRedirect fails (including the default redirect policy such as “stopped after too many redirects”). In that case, resp.Body is already closed. [1][2]
  • Any error returned by Do is a *url.Error (useful for Timeout() checks, etc.). [1]

Practical handling pattern:

resp, err := client.Do(req)
if err != nil {
    // resp may be non-nil only on CheckRedirect failure; Body is already closed then.
    return err
}
defer resp.Body.Close()

Sources: [1] net/http docs for Client.Do (pkg.go.dev) (pkg.go.dev), [2] source-level explanation/examples of the redirect special-case (stackoverflow.com)

Citations:


🏁 Script executed:

rg -n "CustomHTTPDoer" internal/requestconfig/requestconfig.go

Repository: logchimp/logchimp-go

Length of output: 166


🏁 Script executed:

rg -B 5 -A 5 "interface.*HTTPDoer\|type HTTPDoer" internal/

Repository: logchimp/logchimp-go

Length of output: 46


🏁 Script executed:

rg -n "HTTPDoer" --type go

Repository: logchimp/logchimp-go

Length of output: 465


🏁 Script executed:

cat -n internal/requestconfig/requestconfig.go | sed -n '230,240p'

Repository: logchimp/logchimp-go

Length of output: 561


🏁 Script executed:

cat -n internal/requestconfig/requestconfig.go | sed -n '1,30p'

Repository: logchimp/logchimp-go

Length of output: 889


Close response bodies in all error paths to avoid connection leaks with custom HTTPDoer implementations.

While http.Client.Do typically doesn't return non-nil response with context errors, this code supports custom HTTPDoer implementations that may violate this contract. Additionally, the code inconsistently closes response bodies: it properly closes in the retry loop (line 506-508) but not in error paths.

Two locations need fixing:

  • Line 479-484: ctx.Err() return path
  • Line 529-534: handler error return path
🐛 Suggested fix
 if ctx != nil && ctx.Err() != nil {
+	if res != nil && res.Body != nil {
+		res.Body.Close()
+	}
 	if cancel != nil {
 		cancel()
 		cancel = nil
 	}
 	return ctx.Err()
 }
 ...
 if err != nil {
+	if res != nil && res.Body != nil {
+		res.Body.Close()
+	}
 	if cancel != nil {
 		cancel()
 		cancel = nil
 	}
 	return err
 }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
if ctx != nil && ctx.Err() != nil {
if cancel != nil {
cancel()
cancel = nil
}
if ctx != nil && ctx.Err() != nil {
if res != nil && res.Body != nil {
res.Body.Close()
}
if cancel != nil {
cancel()
cancel = nil
}
🤖 Prompt for AI Agents
In `@internal/requestconfig/requestconfig.go` around lines 479 - 483, In the
ctx.Err() and handler error-return paths, ensure any non-nil *http.Response
returned by the custom HTTPDoer is closed to avoid connection leaks: before
calling cancel() or returning on error, check if resp != nil and call
resp.Body.Close() (ignoring the close error) and then set resp = nil; apply this
change in the ctx.Err() branch (where cancel is cleared) and in the handler
error-return branch (where handler returns err) so both error paths mirror the
retry-loop cleanup that currently closes resp.Body.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants