From d90c8a0d9e588806cde863db2e3383d7195e43ce Mon Sep 17 00:00:00 2001 From: Ruslan Dautkhanov Date: Fri, 22 May 2026 09:37:39 -0600 Subject: [PATCH] fix: XD cold-start scenario races JVM startup on CI runners MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The XD ``test_cold_start_scenario`` introduced in #12 used a blind ``time.sleep(0.25)`` followed by a direct ``JavaGateway()`` connect attempt. On CodSpeed CI runners (and any host slower than a modern laptop) the JVM hadn't yet bound its listen socket when the connect fired, surfacing as ``ConnectionRefusedError: [Errno 111] Connection refused`` and failing the whole codspeed workflow. Switch XD's body to the existing ``fresh_jvm`` context manager with ``readiness_retries=5``. ``fresh_jvm`` polls connection readiness with ``check_connection`` and only proceeds once the JVM is actually accepting calls — same convention the rest of the test suite uses. This also drops a bunch of try/finally boilerplate since ``fresh_jvm`` handles spawn/shutdown. Locally on M-series + JDK 21: XD median ~355 ms (unchanged from post-#12 baseline). The variance is higher in cold-start measurement by nature; what matters is the elimination of hard failures on slower hosts. Co-authored-by: Isaac --- .../src/py4j/tests/perf/scenarios/macro.py | 34 ++++++------------- 1 file changed, 10 insertions(+), 24 deletions(-) diff --git a/py4j-python/src/py4j/tests/perf/scenarios/macro.py b/py4j-python/src/py4j/tests/perf/scenarios/macro.py index c37fcb92..d14373a7 100644 --- a/py4j-python/src/py4j/tests/perf/scenarios/macro.py +++ b/py4j-python/src/py4j/tests/perf/scenarios/macro.py @@ -478,30 +478,16 @@ class XD_FullColdStart(MacroScenario): iterations_per_round = 1 def measure(self, gateway): - # Import inside measure() so the cost of import isn't paid - # repeatedly; jvm helpers are already imported by the fixture - # in any sane run. - from py4j.tests.perf.jvm import spawn_jvm, shutdown_jvm - process = spawn_jvm() - try: - # Brief sleep mirrors fresh_jvm's startup_sleep so we - # exercise the same path; without it on a fast machine the - # first connect attempt can race ahead of the listening - # socket. - import time - time.sleep(0.25) - gw = None - try: - gw = JavaGateway() - gw.jvm.java.lang.System.currentTimeMillis() - finally: - if gw is not None: - try: - gw.shutdown() - except Exception: - pass - finally: - shutdown_jvm(process, None) + # Delegate to fresh_jvm so JVM startup readiness is polled + # rather than blind-slept — CodSpeed CI runners are slower + # than a local laptop and the original 0.25 s sleep was + # racing the JVM's listening socket (causing + # ConnectionRefusedError under CI). fresh_jvm uses a retry + # loop matching the rest of the test suite's conventions and + # cleanly tears down the subprocess in __exit__. + from py4j.tests.perf.jvm import fresh_jvm + with fresh_jvm(readiness_retries=5) as gw: + gw.jvm.java.lang.System.currentTimeMillis() ALL_MACRO_CLASSES = [