From 6040f151903d3a0aebc402b99cd339d0ba9a2685 Mon Sep 17 00:00:00 2001 From: Manuel Rigger Date: Sun, 5 Apr 2026 00:15:45 +0800 Subject: [PATCH 1/5] Fix CnosDB CI: SQL readiness check and resolve Log4j version conflicts Two issues caused CnosDB test failures: 1. HTTP ping responded before storage layer was ready, causing "Resource temporarily unavailable" on first SQL command. Use SQL-level readiness check instead. 2. Mismatched Log4j versions (log4j-api 2.10.0 vs log4j-core 2.18.0) from Hive transitive dependencies caused NoSuchMethodError. Exclude log4j-slf4j-impl from Hive deps, align Log4j2 at 2.24.3, and add log4j-slf4j2-impl for SLF4J 2.x compatibility. Also fix copy-paste error in CI step name and upgrade HSQLDB to 2.7.4. Co-Authored-By: Claude Sonnet 4.6 --- .github/workflows/main.yml | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 7f2e493f9..26c497264 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -101,11 +101,19 @@ jobs: cache: 'maven' - name: Build SQLancer run: mvn -B package -DskipTests=true - - name: Set up ClickHouse + - name: Set up CnosDB run: | docker pull cnosdb/cnosdb:community-latest docker run --name cnosdb -p 8902:8902 -d cnosdb/cnosdb:community-latest - until nc -z 127.0.0.1 8902 2>/dev/null; do sleep 1; done + for i in $(seq 1 30); do + if curl -sf -u "root:" -H "Content-Type: application/json" \ + "http://127.0.0.1:8902/api/v1/sql?db=public" -d "SELECT 1" >/dev/null 2>&1; then + echo "CnosDB is ready" + break + fi + echo "Waiting for CnosDB... ($i/30)" + sleep 10 + done - name: Run Tests run: | CNOSDB_AVAILABLE=true mvn -Dtest=TestCnosDBNoREC test From e3a54076f4dc194aa644d6faf37e17e180ead9e1 Mon Sep 17 00:00:00 2001 From: Manuel Rigger Date: Sun, 5 Apr 2026 23:00:27 +0800 Subject: [PATCH 2/5] Fix CnosDB CI: use public DB context for DROP/CREATE, single thread, and SQL readiness check CnosDB cannot reliably drop a database when the API request targets that same database. Use a separate client connected to "public" for DROP/CREATE DATABASE operations. Also limit to single thread since CnosDB's storage engine cannot handle concurrent database lifecycle operations, and replace the CI port check with a SQL readiness poll. Co-Authored-By: Claude Opus 4.6 --- src/sqlancer/cnosdb/CnosDBProvider.java | 12 ++++++++---- test/sqlancer/dbms/TestCnosDBNoREC.java | 4 ++-- test/sqlancer/dbms/TestCnosDBTLP.java | 4 ++-- 3 files changed, 12 insertions(+), 8 deletions(-) diff --git a/src/sqlancer/cnosdb/CnosDBProvider.java b/src/sqlancer/cnosdb/CnosDBProvider.java index 8b69c53b3..24d6b96de 100644 --- a/src/sqlancer/cnosdb/CnosDBProvider.java +++ b/src/sqlancer/cnosdb/CnosDBProvider.java @@ -66,13 +66,17 @@ public CnosDBConnection createDatabase(CnosDBGlobalState globalState) throws Exc host = globalState.getOptions().getHost(); port = globalState.getOptions().getPort(); databaseName = globalState.getDatabaseName(); - CnosDBClient client = new CnosDBClient(host, port, username, password, databaseName); - CnosDBConnection connection = new CnosDBConnection(client); - client.execute("DROP DATABASE IF EXISTS " + databaseName); + // Use a client connected to "public" for DROP/CREATE operations, + // since CnosDB cannot drop a database from its own connection context. + CnosDBClient adminClient = new CnosDBClient(host, port, username, password, "public"); + adminClient.execute("DROP DATABASE IF EXISTS " + databaseName); globalState.getState().logStatement("DROP DATABASE IF EXISTS " + databaseName); - client.execute("CREATE DATABASE " + databaseName); + adminClient.execute("CREATE DATABASE " + databaseName); globalState.getState().logStatement("CREATE DATABASE " + databaseName); + adminClient.close(); + CnosDBClient client = new CnosDBClient(host, port, username, password, databaseName); + CnosDBConnection connection = new CnosDBConnection(client); return connection; } diff --git a/test/sqlancer/dbms/TestCnosDBNoREC.java b/test/sqlancer/dbms/TestCnosDBNoREC.java index 1a89a972a..42ece0733 100644 --- a/test/sqlancer/dbms/TestCnosDBNoREC.java +++ b/test/sqlancer/dbms/TestCnosDBNoREC.java @@ -15,8 +15,8 @@ public void testCnosDBNoREC() { // Run with 0 queries as current implementation is resulting in database crashes assertEquals(0, Main.executeMain(new String[] { "--host", "127.0.0.1", "--port", "8902", "--username", "root", - "--random-seed", "0", "--timeout-seconds", TestConfig.SECONDS, "--num-queries", "0", "cnosdb", - "--oracle", "NOREC" })); + "--random-seed", "0", "--timeout-seconds", TestConfig.SECONDS, "--num-queries", "0", + "--num-threads", "1", "cnosdb", "--oracle", "NOREC" })); } } diff --git a/test/sqlancer/dbms/TestCnosDBTLP.java b/test/sqlancer/dbms/TestCnosDBTLP.java index 4b12aa409..2015e3af9 100644 --- a/test/sqlancer/dbms/TestCnosDBTLP.java +++ b/test/sqlancer/dbms/TestCnosDBTLP.java @@ -15,8 +15,8 @@ public void testCnosDBTLP() { // Run with 0 queries as current implementation is resulting in database crashes assertEquals(0, Main.executeMain(new String[] { "--host", "127.0.0.1", "--port", "8902", "--username", "root", - "--random-seed", "0", "--timeout-seconds", TestConfig.SECONDS, "--num-queries", "0", "cnosdb", - "--oracle", "QUERY_PARTITIONING" })); + "--random-seed", "0", "--timeout-seconds", TestConfig.SECONDS, "--num-queries", "0", + "--num-threads", "1", "cnosdb", "--oracle", "QUERY_PARTITIONING" })); } } From 971fb44539075320dd02fc908b46212deba45cd8 Mon Sep 17 00:00:00 2001 From: Manuel Rigger Date: Mon, 6 Apr 2026 09:28:22 +0800 Subject: [PATCH 3/5] Fix CnosDB CI: retry DDL on storage layer initialization delays CnosDB's Tskv index storage may not be fully ready even after the HTTP API responds to simple queries. On CI runners, DDL operations like DROP/CREATE DATABASE consistently fail with "Resource temporarily unavailable (os error 11)". Additionally, a newly created database may not be immediately queryable, causing "Database not found" errors. Add executeWithRetry() that retries these specific transient errors during database setup. Co-Authored-By: Claude Opus 4.6 --- src/sqlancer/cnosdb/CnosDBProvider.java | 33 +++++++++++++++++++++++-- 1 file changed, 31 insertions(+), 2 deletions(-) diff --git a/src/sqlancer/cnosdb/CnosDBProvider.java b/src/sqlancer/cnosdb/CnosDBProvider.java index 24d6b96de..e58e7ec88 100644 --- a/src/sqlancer/cnosdb/CnosDBProvider.java +++ b/src/sqlancer/cnosdb/CnosDBProvider.java @@ -12,6 +12,7 @@ import sqlancer.StatementExecutor; import sqlancer.cnosdb.client.CnosDBClient; import sqlancer.cnosdb.client.CnosDBConnection; +import sqlancer.cnosdb.client.CnosDBException; import sqlancer.cnosdb.gen.CnosDBInsertGenerator; import sqlancer.cnosdb.gen.CnosDBTableGenerator; import sqlancer.cnosdb.query.CnosDBOtherQuery; @@ -21,6 +22,9 @@ @AutoService(DatabaseProvider.class) public class CnosDBProvider extends ProviderAdapter { + private static final int DB_READY_RETRIES = 30; + private static final int DB_READY_SLEEP_MS = 1000; + protected String username; protected String password; protected String host; @@ -69,13 +73,14 @@ public CnosDBConnection createDatabase(CnosDBGlobalState globalState) throws Exc // Use a client connected to "public" for DROP/CREATE operations, // since CnosDB cannot drop a database from its own connection context. CnosDBClient adminClient = new CnosDBClient(host, port, username, password, "public"); - adminClient.execute("DROP DATABASE IF EXISTS " + databaseName); + executeWithRetry(adminClient, "DROP DATABASE IF EXISTS " + databaseName); globalState.getState().logStatement("DROP DATABASE IF EXISTS " + databaseName); - adminClient.execute("CREATE DATABASE " + databaseName); + executeWithRetry(adminClient, "CREATE DATABASE " + databaseName); globalState.getState().logStatement("CREATE DATABASE " + databaseName); adminClient.close(); CnosDBClient client = new CnosDBClient(host, port, username, password, databaseName); + executeWithRetry(client, "SELECT 1"); CnosDBConnection connection = new CnosDBConnection(client); return connection; } @@ -98,6 +103,30 @@ protected void prepareTables(CnosDBGlobalState globalState) throws Exception { se.executeStatements(); } + /** + * Executes a query with retries to work around CnosDB storage layer initialization delays. + * + * CnosDB's Tskv index storage may not be fully ready even after the HTTP API responds to simple queries. DDL + * operations like DROP/CREATE DATABASE can fail with: {@code "grpc client request error: Tskv: Index: index storage + * error: Resource temporarily unavailable (os error 11)"}. Additionally, a newly created database may not be + * immediately queryable, causing {@code "Database not found"} errors. + */ + private static void executeWithRetry(CnosDBClient client, String query) throws Exception { + for (int i = 0; i < DB_READY_RETRIES; i++) { + try { + client.execute(query); + return; + } catch (CnosDBException e) { + boolean isRetryable = e.getMessage().contains("Resource temporarily unavailable") + || e.getMessage().contains("Database not found"); + if (!isRetryable || i == DB_READY_RETRIES - 1) { + throw e; + } + Thread.sleep(DB_READY_SLEEP_MS); + } + } + } + @Override public String getDBMSName() { return "CnosDB".toLowerCase(); From 8ed632e765ac6f35440e98557db1153ecfb532c3 Mon Sep 17 00:00:00 2001 From: Manuel Rigger Date: Mon, 6 Apr 2026 20:49:55 +0800 Subject: [PATCH 4/5] Fix CnosDB CI: also retry on "already exists" after delayed DROP The DROP DATABASE may appear to succeed but not yet propagate in CnosDB's storage layer, causing the subsequent CREATE DATABASE to fail with "Database already exists". Co-Authored-By: Claude Opus 4.6 --- src/sqlancer/cnosdb/CnosDBProvider.java | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/sqlancer/cnosdb/CnosDBProvider.java b/src/sqlancer/cnosdb/CnosDBProvider.java index e58e7ec88..40702dd2c 100644 --- a/src/sqlancer/cnosdb/CnosDBProvider.java +++ b/src/sqlancer/cnosdb/CnosDBProvider.java @@ -109,7 +109,8 @@ protected void prepareTables(CnosDBGlobalState globalState) throws Exception { * CnosDB's Tskv index storage may not be fully ready even after the HTTP API responds to simple queries. DDL * operations like DROP/CREATE DATABASE can fail with: {@code "grpc client request error: Tskv: Index: index storage * error: Resource temporarily unavailable (os error 11)"}. Additionally, a newly created database may not be - * immediately queryable, causing {@code "Database not found"} errors. + * immediately queryable, causing {@code "Database not found"} errors. DROP DATABASE may also be delayed, causing + * a subsequent CREATE DATABASE to fail with {@code "Database already exists"}. */ private static void executeWithRetry(CnosDBClient client, String query) throws Exception { for (int i = 0; i < DB_READY_RETRIES; i++) { @@ -118,7 +119,8 @@ private static void executeWithRetry(CnosDBClient client, String query) throws E return; } catch (CnosDBException e) { boolean isRetryable = e.getMessage().contains("Resource temporarily unavailable") - || e.getMessage().contains("Database not found"); + || e.getMessage().contains("Database not found") + || e.getMessage().contains("already exists"); if (!isRetryable || i == DB_READY_RETRIES - 1) { throw e; } From e9a6c8ba226711c8c5aec0ac3dc731028a24ba63 Mon Sep 17 00:00:00 2001 From: Manuel Rigger Date: Mon, 6 Apr 2026 22:16:27 +0800 Subject: [PATCH 5/5] Fix CnosDB CI: retry DROP+CREATE as a pair on storage lag Retrying just CREATE DATABASE on "already exists" is futile when the preceding DROP hasn't propagated in CnosDB's storage layer. Instead, retry the DROP+CREATE sequence together so the DROP is re-issued before each CREATE attempt. Co-Authored-By: Claude Opus 4.6 --- src/sqlancer/cnosdb/CnosDBProvider.java | 41 +++++++++++++++++++------ 1 file changed, 31 insertions(+), 10 deletions(-) diff --git a/src/sqlancer/cnosdb/CnosDBProvider.java b/src/sqlancer/cnosdb/CnosDBProvider.java index 40702dd2c..1a69c5655 100644 --- a/src/sqlancer/cnosdb/CnosDBProvider.java +++ b/src/sqlancer/cnosdb/CnosDBProvider.java @@ -73,9 +73,8 @@ public CnosDBConnection createDatabase(CnosDBGlobalState globalState) throws Exc // Use a client connected to "public" for DROP/CREATE operations, // since CnosDB cannot drop a database from its own connection context. CnosDBClient adminClient = new CnosDBClient(host, port, username, password, "public"); - executeWithRetry(adminClient, "DROP DATABASE IF EXISTS " + databaseName); + dropAndCreateDatabase(adminClient, databaseName); globalState.getState().logStatement("DROP DATABASE IF EXISTS " + databaseName); - executeWithRetry(adminClient, "CREATE DATABASE " + databaseName); globalState.getState().logStatement("CREATE DATABASE " + databaseName); adminClient.close(); @@ -104,22 +103,24 @@ protected void prepareTables(CnosDBGlobalState globalState) throws Exception { } /** - * Executes a query with retries to work around CnosDB storage layer initialization delays. + * Drops and recreates a database, retrying the entire DROP+CREATE sequence on transient errors. * * CnosDB's Tskv index storage may not be fully ready even after the HTTP API responds to simple queries. DDL - * operations like DROP/CREATE DATABASE can fail with: {@code "grpc client request error: Tskv: Index: index storage - * error: Resource temporarily unavailable (os error 11)"}. Additionally, a newly created database may not be - * immediately queryable, causing {@code "Database not found"} errors. DROP DATABASE may also be delayed, causing - * a subsequent CREATE DATABASE to fail with {@code "Database already exists"}. + * operations can fail with: {@code "grpc client request error: Tskv: Index: index storage error: Resource + * temporarily unavailable (os error 11)"}. DROP DATABASE may also appear to succeed but not yet propagate, causing + * CREATE DATABASE to fail with {@code "Database already exists"}. Retrying just the CREATE is futile in that case, + * so we retry the DROP+CREATE pair together. */ - private static void executeWithRetry(CnosDBClient client, String query) throws Exception { + private static void dropAndCreateDatabase(CnosDBClient client, String databaseName) throws Exception { + String dropQuery = "DROP DATABASE IF EXISTS " + databaseName; + String createQuery = "CREATE DATABASE " + databaseName; for (int i = 0; i < DB_READY_RETRIES; i++) { try { - client.execute(query); + client.execute(dropQuery); + client.execute(createQuery); return; } catch (CnosDBException e) { boolean isRetryable = e.getMessage().contains("Resource temporarily unavailable") - || e.getMessage().contains("Database not found") || e.getMessage().contains("already exists"); if (!isRetryable || i == DB_READY_RETRIES - 1) { throw e; @@ -129,6 +130,26 @@ private static void executeWithRetry(CnosDBClient client, String query) throws E } } + /** + * Executes a query with retries to work around CnosDB storage layer initialization delays. + * + * A newly created database may not be immediately queryable, causing {@code "Database not found"} errors. + */ + private static void executeWithRetry(CnosDBClient client, String query) throws Exception { + for (int i = 0; i < DB_READY_RETRIES; i++) { + try { + client.execute(query); + return; + } catch (CnosDBException e) { + boolean isRetryable = e.getMessage().contains("Database not found"); + if (!isRetryable || i == DB_READY_RETRIES - 1) { + throw e; + } + Thread.sleep(DB_READY_SLEEP_MS); + } + } + } + @Override public String getDBMSName() { return "CnosDB".toLowerCase();