This is an automated email from the ASF dual-hosted git repository.

pjfanning pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/pekko-management.git


The following commit(s) were added to refs/heads/main by this push:
     new f8cc98a4 Fix flaky KubernetesApiSpec by decoupling functional tests 
from request timeout #216 (#842)
f8cc98a4 is described below

commit f8cc98a47aa08b0837b7ddbd3ff97980a96777df
Author: Arun Selvamani <[email protected]>
AuthorDate: Fri Jun 19 12:07:16 2026 -0700

    Fix flaky KubernetesApiSpec by decoupling functional tests from request 
timeout #216 (#842)
    
    * Every request in AbstractKubernetesApiImpl.makeRequest is raced against
      apiServerRequestTimeout. The first HTTP request pays Pekko HTTP 
connection-pool
      and JIT warm-up cost and could exceed the hard-coded 1s deadline on a 
loaded CI
      runner, failing functional tests with a spurious LeaseTimeoutException.
    * Give the functional and auth tests a generous 10s request timeout so 
first-request
      warm-up no longer trips the deadline.
    * Route the deliberate "timeout on ..." tests through a dedicated 
short-timeout (1s)
      client so they still fail fast; their relative delays scale off that 
short value.
    
    Co-authored-by: Arun Selvamani <[email protected]>
    Co-authored-by: Claude Opus 4.8 <[email protected]>
---
 .../lease/kubernetes/KubernetesApiSpec.scala       | 48 +++++++++++++++++-----
 1 file changed, 37 insertions(+), 11 deletions(-)

diff --git 
a/lease-kubernetes/src/test/scala/org/apache/pekko/coordination/lease/kubernetes/KubernetesApiSpec.scala
 
b/lease-kubernetes/src/test/scala/org/apache/pekko/coordination/lease/kubernetes/KubernetesApiSpec.scala
index 63811d4b..3b1943cc 100644
--- 
a/lease-kubernetes/src/test/scala/org/apache/pekko/coordination/lease/kubernetes/KubernetesApiSpec.scala
+++ 
b/lease-kubernetes/src/test/scala/org/apache/pekko/coordination/lease/kubernetes/KubernetesApiSpec.scala
@@ -53,7 +53,27 @@ class KubernetesApiSpec
   val wireMockServer = new WireMockServer(wireMockConfig().port(0))
   wireMockServer.start()
 
+  // Generous request deadline for the functional tests. Every request inside
+  // `AbstractKubernetesApiImpl.makeRequest` is raced against 
`apiServerRequestTimeout`, and the
+  // first HTTP request through Pekko HTTP pays connection-pool/JIT warm-up 
cost (observed ~0.8s
+  // locally, more on a loaded CI box). A 1s deadline here is what made this 
suite flaky: the
+  // warm-up request would lose the race and fail with a spurious 
LeaseTimeoutException.
+  // See https://github.com/apache/pekko-management/issues/216
+  private val robustRequestTimeout = 10.seconds
+
   val settings = new KubernetesSettings(
+    "",
+    "",
+    "localhost",
+    wireMockServer.port(),
+    namespace = Some("lease"),
+    "",
+    apiServerRequestTimeout = robustRequestTimeout,
+    secure = false,
+    bodyReadTimeout = robustRequestTimeout)
+
+  // Short deadline used ONLY by the deliberate "timeout on ..." tests, so 
they fail fast.
+  val timeoutSettings = new KubernetesSettings(
     "",
     "",
     "localhost",
@@ -65,7 +85,6 @@ class KubernetesApiSpec
 
   WireMock.configureFor(settings.apiServerPort)
 
-  // double the default timeout for CI 
(https://github.com/apache/pekko-management/issues/216)
   implicit val patience: PatienceConfig = 
PatienceConfig(testKitSettings.DefaultTimeout.duration.*(2))
 
   val underTest = new KubernetesApiImpl(system, settings) {
@@ -73,6 +92,13 @@ class KubernetesApiSpec
     override protected def readConfigVarFromFilesystem(path: String, name: 
String): Future[Option[String]] =
       Future.successful(None)
   }
+
+  // Same as `underTest`, but with the short `timeoutSettings`, for the 
deliberate timeout tests.
+  val underTestTimeouts = new KubernetesApiImpl(system, timeoutSettings) {
+    // avoid touching slow CI filesystem
+    override protected def readConfigVarFromFilesystem(path: String, name: 
String): Future[Option[String]] =
+      Future.successful(None)
+  }
   val leaseName = "lease-1"
   val client1 = "client-1"
   val client2 = "client-2"
@@ -201,7 +227,7 @@ class KubernetesApiSpec
       stubFor(
         
get(urlEqualTo(s"/apis/pekko.apache.org/v1/namespaces/lease/leases/$lease")).willReturn(
           aResponse()
-            .withFixedDelay((settings.apiServerRequestTimeout * 
2).toMillis.toInt) // Oh noes
+            .withFixedDelay((timeoutSettings.apiServerRequestTimeout * 
2).toMillis.toInt) // Oh noes
             .withStatus(StatusCodes.OK.intValue)
             .withHeader("Content-Type", "application/json")
             .withBody(s"""
@@ -222,7 +248,7 @@ class KubernetesApiSpec
                |}
             """.stripMargin)))
 
-      underTest
+      underTestTimeouts
         .readOrCreateLeaseResource(lease)
         .failed
         .futureValue
@@ -239,11 +265,11 @@ class KubernetesApiSpec
       stubFor(
         
post(urlEqualTo(s"/apis/pekko.apache.org/v1/namespaces/lease/leases/$lease")).willReturn(
           aResponse()
-            .withFixedDelay((settings.apiServerRequestTimeout * 
2).toMillis.toInt) // Oh noes
+            .withFixedDelay((timeoutSettings.apiServerRequestTimeout * 
2).toMillis.toInt) // Oh noes
             .withStatus(StatusCodes.OK.intValue)
             .withHeader("Content-Type", "application/json")))
 
-      underTest
+      underTestTimeouts
         .readOrCreateLeaseResource(lease)
         .failed
         .futureValue
@@ -256,11 +282,11 @@ class KubernetesApiSpec
       stubFor(
         
put(urlEqualTo(s"/apis/pekko.apache.org/v1/namespaces/lease/leases/$lease")).willReturn(
           aResponse()
-            .withFixedDelay((settings.apiServerRequestTimeout * 
2).toMillis.toInt) // Oh noes
+            .withFixedDelay((timeoutSettings.apiServerRequestTimeout * 
2).toMillis.toInt) // Oh noes
             .withStatus(StatusCodes.OK.intValue)
             .withHeader("Content-Type", "application/json")))
 
-      underTest.updateLeaseResource(lease, owner, 
"1").failed.futureValue.getMessage shouldEqual
+      underTestTimeouts.updateLeaseResource(lease, owner, 
"1").failed.futureValue.getMessage shouldEqual
       s"Timed out updating lease [$lease] to owner [$owner]. It is not known 
if the update happened. Is the API server up?"
     }
 
@@ -269,10 +295,10 @@ class KubernetesApiSpec
       stubFor(
         
delete(urlEqualTo(s"/apis/pekko.apache.org/v1/namespaces/lease/leases/$lease")).willReturn(
           aResponse()
-            .withFixedDelay((settings.apiServerRequestTimeout * 
2).toMillis.toInt) // Oh noes
+            .withFixedDelay((timeoutSettings.apiServerRequestTimeout * 
2).toMillis.toInt) // Oh noes
             .withStatus(StatusCodes.OK.intValue)))
 
-      underTest.removeLease(lease).failed.futureValue.getMessage shouldEqual
+      underTestTimeouts.removeLease(lease).failed.futureValue.getMessage 
shouldEqual
       s"Timed out removing lease [$lease]. It is not known if the remove 
happened. Is the API server up?"
     }
 
@@ -309,7 +335,7 @@ class KubernetesApiSpec
         wireMockServer.port(),
         namespace = Some("lease"),
         "",
-        apiServerRequestTimeout = 1.second,
+        apiServerRequestTimeout = robustRequestTimeout,
         secure = false)
 
       val toFail = new AtomicBoolean(true)
@@ -340,7 +366,7 @@ class KubernetesApiSpec
         wireMockServer.port(),
         namespace = Some("lease"),
         "",
-        apiServerRequestTimeout = 1.second,
+        apiServerRequestTimeout = robustRequestTimeout,
         secure = false)
 
       val retryUnauthorized = new KubernetesApiImpl(system, newSettings) {


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to