Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
32 changes: 27 additions & 5 deletions gae-interop-testing/gae-jdk8/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -124,13 +124,35 @@ tasks.register("runInteropTestRemote") {
logger.log(LogLevel.INFO, "the appURL=" + appUrl)
def client = new com.squareup.okhttp.OkHttpClient()
// The '?jdk8' argument is ignored by the server, it exists only to tag the request log entry
client.setReadTimeout(30, java.util.concurrent.TimeUnit.SECONDS)
client.setReadTimeout(120, java.util.concurrent.TimeUnit.SECONDS)
def request = new com.squareup.okhttp.Request.Builder()
.url("${appUrl}/long_lived_channel?jdk8").build()
def result1 = client.newCall(request).execute()
def result2 = client.newCall(request).execute()
if (result1.code() != 200 || result2.code() != 200) {
throw new GradleException("Unable to reuse same channel across requests")
int maxChannelReuseRetries = 5
def result1 = null
def result2 = null
String result1Body = ""
String result2Body = ""
for (int attempt = 0; attempt < maxChannelReuseRetries; attempt++) {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Since OkHttp relies on the response body being closed to recycle connections, failing to close the bodies inside the loop on a non-200 response (or when an exception is thrown) will leak connections. If result1 succeeds but result2 throws an exception, the reference to result1 is overwritten in the next iteration and its connection is leaked.

Consider adding a finally block or explicitly closing the bodies before retrying ??

try {
result1 = client.newCall(request).execute()
result2 = client.newCall(request).execute()
if (result1.code() == 200 && result2.code() == 200) {
break
}
logger.log(LogLevel.WARN, "Channel reuse attempt ${attempt + 1} failed: result1 code = ${result1?.code()}, result2 code = ${result2?.code()}. Retrying...")
} catch (Throwable t) {
logger.log(LogLevel.WARN, "Channel reuse attempt ${attempt + 1} caught exception: ${t.message}. Retrying...", t)
}
Comment on lines +143 to +145

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Catching Throwable might be a bit too broad as it catches things like OutOfMemoryError or StackOverflowError which usually shouldn't be retried. Consider changing catch (Throwable t) to catch (Exception e) ??

Thread.sleep(2000)
}
if (result1 == null || result2 == null || result1.code() != 200 || result2.code() != 200) {
if (result1 != null) {
result1Body = result1.body().string()

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

result.body().string() can throw an IOException (e.g., if the body is too large or the stream breaks). If this happens, the script will throw that IOException instead of your detailed GradleException, masking the actual HTTP codes you're trying to surface.
You might want to wrap the body parsing in a safe try-catch block

}
if (result2 != null) {
result2Body = result2.body().string()
}
throw new GradleException("Unable to reuse same channel across requests. result1: ${result1?.code()} (body: ${result1Body}), result2: ${result2?.code()} (body: ${result2Body})")
}

// The test suite can take a while to run
Expand Down