From a5061701ad1b6199913eaea28fd798b8666d94c0 Mon Sep 17 00:00:00 2001 From: Kannan J Date: Wed, 17 Jun 2026 12:52:26 +0000 Subject: [PATCH 1/3] 1. Allow the newly deployed App Engine instance up to 2 minutes to complete its JVM initialization and handle the cold start, to avoid the SocketTimeoutException seen in GAE interop testing action http://shortn/_WEppvnsGXs. 2. Wait and Retry Loop: Added a loop that attempts the check up to 5 times with a 2-second sleep between attempts. This will gracefully absorb any transient warmup or App Engine routing latency (http://shortn/_ONTi0mqEdB) 3. Detailed Failure Logs: If all attempts fail, we now retrieve the response bodies (result.body().string()) and include them along with the status codes in the final error message, allowing for straightforward debugging of any persistent downstream issues. --- gae-interop-testing/gae-jdk8/build.gradle | 32 +++++++++++++++++++---- 1 file changed, 27 insertions(+), 5 deletions(-) diff --git a/gae-interop-testing/gae-jdk8/build.gradle b/gae-interop-testing/gae-jdk8/build.gradle index 07033f403de..29c629fd07e 100644 --- a/gae-interop-testing/gae-jdk8/build.gradle +++ b/gae-interop-testing/gae-jdk8/build.gradle @@ -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++) { + 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) + } + Thread.sleep(2000) + } + if (result1 == null || result2 == null || result1.code() != 200 || result2.code() != 200) { + if (result1 != null) { + result1Body = result1.body().string() + } + 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 From 5c403b5d80ee74471545a2b2127de4931f0dd4b8 Mon Sep 17 00:00:00 2001 From: Kannan J Date: Fri, 31 Jul 2026 12:22:42 +0000 Subject: [PATCH 2/3] Address review comments. --- gae-interop-testing/gae-jdk8/build.gradle | 51 +++++++++++++++++------ 1 file changed, 39 insertions(+), 12 deletions(-) diff --git a/gae-interop-testing/gae-jdk8/build.gradle b/gae-interop-testing/gae-jdk8/build.gradle index 29c629fd07e..a400ecd2334 100644 --- a/gae-interop-testing/gae-jdk8/build.gradle +++ b/gae-interop-testing/gae-jdk8/build.gradle @@ -132,27 +132,54 @@ tasks.register("runInteropTestRemote") { def result2 = null String result1Body = "" String result2Body = "" + boolean success = false + int lastResult1Code = -1 + int lastResult2Code = -1 for (int attempt = 0; attempt < maxChannelReuseRetries; attempt++) { try { result1 = client.newCall(request).execute() result2 = client.newCall(request).execute() - if (result1.code() == 200 && result2.code() == 200) { + if (result1 != null) { + lastResult1Code = result1.code() + } + if (result2 != null) { + lastResult2Code = result2.code() + } + if (lastResult1Code == 200 && lastResult2Code == 200) { + success = true 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) + if (result1 != null) { + try { + result1Body = result1.body().string() + } catch (Exception e) { + result1Body = "Error reading body: " + e.getMessage() + } + } + if (result2 != null) { + try { + result2Body = result2.body().string() + } catch (Exception e) { + result2Body = "Error reading body: " + e.getMessage() + } + } + logger.log(LogLevel.WARN, "Channel reuse attempt ${attempt + 1} failed: result1 code = ${lastResult1Code} (body: ${result1Body}), result2 code = ${lastResult2Code} (body: ${result2Body}). Retrying...") + } catch (Exception e) { + logger.log(LogLevel.WARN, "Channel reuse attempt ${attempt + 1} caught exception: ${e.message}. Retrying...", e) + } finally { + if (result1 != null) { + try { result1.body().close() } catch (Exception ignored) {} + result1 = null + } + if (result2 != null) { + try { result2.body().close() } catch (Exception ignored) {} + result2 = null + } } Thread.sleep(2000) } - if (result1 == null || result2 == null || result1.code() != 200 || result2.code() != 200) { - if (result1 != null) { - result1Body = result1.body().string() - } - 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})") + if (!success) { + throw new GradleException("Unable to reuse same channel across requests. result1: ${lastResult1Code} (body: ${result1Body}), result2: ${lastResult2Code} (body: ${result2Body})") } // The test suite can take a while to run From aa28c83c563253b1c4af3a1af7099acbaf6ad3d0 Mon Sep 17 00:00:00 2001 From: Kannan J Date: Mon, 3 Aug 2026 05:57:02 +0000 Subject: [PATCH 3/3] Avoid using outdated status values from previous iteration or pre-loop initialized values. --- gae-interop-testing/gae-jdk8/build.gradle | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/gae-interop-testing/gae-jdk8/build.gradle b/gae-interop-testing/gae-jdk8/build.gradle index a400ecd2334..af0c27f8350 100644 --- a/gae-interop-testing/gae-jdk8/build.gradle +++ b/gae-interop-testing/gae-jdk8/build.gradle @@ -136,6 +136,10 @@ tasks.register("runInteropTestRemote") { int lastResult1Code = -1 int lastResult2Code = -1 for (int attempt = 0; attempt < maxChannelReuseRetries; attempt++) { + result1Body = "" + result2Body = "" + lastResult1Code = -1 + lastResult2Code = -1 try { result1 = client.newCall(request).execute() result2 = client.newCall(request).execute()