From 6ca50e4bcc5dff2b7a09f2107cbc8ca0e2189a52 Mon Sep 17 00:00:00 2001 From: ihsandemir Date: Mon, 1 Jun 2026 13:48:10 +0000 Subject: [PATCH] Fix SqlTest.select Windows flake: move bounds-check EXPECT_THROWs outside row loop The test iterated over 4096 SQL rows and called EXPECT_THROW three times per row to verify get_object() throws on an invalid index and unknown column name. That is ~12 000 C++ exception throws per test run. On Windows the CI step attaches ProcDump as a debugger (procdump -e -ma -w client_test.exe). Every C++ throw raises SEH code 0xE06D7363, which the OS delivers as a first-chance debug event to ProcDump before the C++ catch handler can run. ProcDump logs the line [HH:MM:SS]Exception: E06D7363.?AVindex_out_of_bounds@... then calls ContinueDebugEvent() so execution can proceed -- two mandatory kernel-mode transitions per throw. 12 000 throws * 2 = 24 000 kernel round-trips, combined with 4096 synchronous map->get() server calls, made the loop slow enough for the server-side connection health-check to fire and cancel the SQL cursor (Client cannot be reached), failing the test. Fix: fetch the first page before the loop and run the three EXPECT_THROW checks on its first row only. Exception count drops from 12 000 to 3. Closes #1470 --- hazelcast/test/src/sql_test.cpp | 40 +++++++++++++++++++++++++-------- 1 file changed, 31 insertions(+), 9 deletions(-) diff --git a/hazelcast/test/src/sql_test.cpp b/hazelcast/test/src/sql_test.cpp index 36585c616a..ebb14bf0e6 100644 --- a/hazelcast/test/src/sql_test.cpp +++ b/hazelcast/test/src/sql_test.cpp @@ -1870,8 +1870,34 @@ TEST_F(SqlTest, select) std::unordered_set unique_keys; - for (auto itr = res->iterator(); itr.has_next();) { - auto page = itr.next().get(); + // Fetch the first page up front so we can verify the bounds-checking API + // on a single representative row before the main loop. + // Background: on Windows, C++ exceptions are built on SEH. When ProcDump + // is attached as a debugger (CI step: procdump -e -ma -w client_test.exe), + // every throw raises SEH code 0xE06D7363, which the OS delivers as a + // first-chance debug event to ProcDump before the C++ catch handler runs. + // ProcDump logs the line "[HH:MM:SS]Exception: E06D7363.?AV..." and then + // calls ContinueDebugEvent so execution can proceed. This is a mandatory + // two-kernel-mode-transition round-trip for every single throw. + // With the EXPECT_THROW calls inside the 4096-row loop that is ~12 000 + // round-trips, stalling the loop long enough to trigger connection + // timeouts on the server side (Windows-only flaky failure). + auto itr = res->iterator(); + ASSERT_TRUE(itr.has_next()); + auto page = itr.next().get(); + ASSERT_FALSE(page->rows().empty()); + { + const auto& first_row = page->rows().front(); + EXPECT_THROW(first_row.get_object(-1), + exception::index_out_of_bounds); + EXPECT_THROW( + first_row.get_object(first_row.row_metadata().column_count()), + exception::index_out_of_bounds); + EXPECT_THROW(first_row.get_object("unknown_field"), + exception::illegal_argument); + } + + for (;;) { for (const auto& row : page->rows()) { ASSERT_EQ(row_metadata, res->row_metadata()); @@ -1903,14 +1929,10 @@ TEST_F(SqlTest, select) sql_column_type::varchar, val.varchar_val, row, "varcharVal"); unique_keys.emplace(*key0); - - EXPECT_THROW(row.get_object(-1), - exception::index_out_of_bounds); - EXPECT_THROW(row.get_object(row.row_metadata().column_count()), - exception::index_out_of_bounds); - EXPECT_THROW(row.get_object("unknown_field"), - exception::illegal_argument); } + if (!itr.has_next()) + break; + page = itr.next().get(); } EXPECT_THROW(res->iterator(), exception::illegal_state);