Repository navigation
[test](fe) Run the ADBC connector unit tests in FE UT without the arrow-c-data JNI shim - #68510
Conversation
### What problem does this PR solve? Issue Number: None Related PR: apache#66399 Problem Summary: The FE unit test runner selects fe-core and its Maven prerequisites, but fe-connector-adbc is not one of those prerequisites. Its tests therefore do not run in FE UT. Add the ADBC module explicitly so the native connector tests are exercised. This extracts the ADBC test-runner change from PR apache#66399 onto upstream-apache/master for isolated CI investigation. ### Release note None ### Check List (For Author) - Test: Manual test - Ran bash -n run-fe-ut.sh and git diff --check. - Full FE UT was not run; the CI agent currently fails to load Arrow C Data JNI due to a missing CXXABI_1.3.9 symbol. - Behavior changed: Yes (FE UT now runs the ADBC connector tests) - Does this need documentation: No
…p the rest in FE UT ### What problem does this PR solve? Issue Number: None Related PR: apache#66399 Problem Summary: Running the fe-connector-adbc module in FE UT (the runner line this branch carries from apache#66399) turned 23 of its tests red on the CI agent with one error: java.lang.UnsatisfiedLinkError: /tmp/jnilib-*.tmp: /lib64/libstdc++.so.6: version `CXXABI_1.3.9' not found (required by /tmp/jnilib-*.tmp) Root cause: materializing a driver's result as Arrow needs a SECOND native library besides the ADBC JNI shim thirdparty builds -- arrow-c-data's `arrow_cdata_jni`, which travels inside the arrow-c-data jar as an upstream build and is picked up through the `arrow.cdata.library.path` property. Nothing in this repository sets that property, so the jar's binary is what gets loaded, and it requires CXXABI_1.3.9 (GCC 4.9+), which hosts Doris still supports do not have (CentOS 7 stops at CXXABI_1.3.7). The ADBC shim does not have this problem because thirdparty builds it locally -- which is exactly why `libadbc_driver_jni.so` loads on that host and arrow-c-data's does not. Fix: FE UT must not reach that library. The tests that read a driver's result are removed -- they cannot run on such a host, so they verify nothing there -- and what they asserted is preserved rather than dropped: - The two metadata cases that need the driver but never read a result (one ends in a thrown exception, the other never asks the source) move to AdbcConnectorMetadataNativeTest unchanged. - The four getObjects parsing cases are rewritten against hand-built responses (the file already had the in-memory Arrow technique), so they run in FE UT without any native library. - The end-to-end half of the removed driver-backed assertions moves to the regression suites in the next commit. - AdbcNativeTestSupport now states the rule where the boundary lives: tests that iterate an ArrowReader or touch org.apache.arrow.c belong in the suites, not in this module's test sources. Before/after, CI: 204 tests with 23 failures -> 187 tests with 0. Locally on macOS (where the SQLite driver is absent) the skips drop from 30 to 7, and the four rewritten reader cases now execute instead of skipping. ### Release note None ### Check List (For Author) - Test: Manual test - `mvn -f fe/pom.xml -pl :fe-connector-adbc -am test` -> 187 tests, 0 failures, 7 skipped (the 7 skips are the SQLite driver being absent on this host; they run in CI). - The 23 previously failing tests no longer exist, so nothing is left failing. - Behavior changed: No (test sources only) - Does this need documentation: No Co-Authored-By: Claude Code <[email protected]>
### What problem does this PR solve? Issue Number: None Related PR: apache#66399 Problem Summary: The previous commit removed the fe-connector-adbc tests that need arrow-cdata's JNI shim (an upstream binary that cannot load on every host Doris supports). Removing a test is only safe if what it asserted still has a home, so this adds the end-to-end half of those behaviours to the ADBC suites, as assertions rather than baselines: 1. test_adbc_sqlite_catalog_scan: SHOW DATABASES must not invent a database named "" out of the empty db_schema SQLite reports for its missing level. Membership ("main is among them") would not notice the extra name, which is what the removed unit test guarded. The same suite gains a BLOB column: the driver reports binary and Doris has no binary column type, so DESC must show the string type. 2. test_adbc_sqlite_catalog_scan and test_adbc_metadata_ops: the schema read once must still be the one served before any REFRESH. Without this half, the existing "REFRESH makes the new column visible" assertion would pass for a connector that re-read on every statement and cached nothing. 3. test_adbc_catalog_scan: a second database in the source, so a listing that mixed namespaces would offer its table under the wrong database. The sibling's own listing is asserted to hold exactly its table -- an exact set, which the suite did not assert before. 4. test_adbc_predicate_pushdown: the remote statement addresses the table through the handle's remote coordinates (two levels: schema and table, or catalog and table when the source has no schema level), not through the Doris catalog the user wrote. Verified on a local single-FE/single-BE cluster built from this branch: all 20 suites under external_table_p0/adbc pass (Test 20 suites, failed 0 suites, fatal 0 scripts, skipped 0 scripts). test_adbc_multi_backend self-skips on a single-backend cluster by design, as it does in CI. ### Release note None ### Check List (For Author) - Test: Regression test - `bash run-regression-test.sh --run -d external_table_p0/adbc -suiteParallel 1` -> Test 20 suites, failed 0 suites, fatal 0 scripts, skipped 0 scripts - Behavior changed: No (test sources only) - Does this need documentation: No Co-Authored-By: Claude Code <[email protected]>
|
Thank you for your contribution to Apache Doris. Please clearly describe your PR:
|
|
run buildall |
|
/review |
TPC-H: Total hot run time: 27674 ms |
There was a problem hiding this comment.
Static review of exact head b5dd0479e68f2cc5355cb8c78b386e2fe6ad1006.
Requesting changes for four test-coverage regressions called out inline: the pure descriptor contract remains gated by optional native libraries; the new schema-cache assertion is masked by fe-core's cache; live database-list reload wiring loses its discriminating test; and getTableHandle's own stale-list fallback loses its direct test.
Checkpoint conclusions:
- Goal, scope, and minimality: the change is focused on tests and FE UT enrollment. The Maven selector, failure reporting, and intended JNI boundary are coherent.
- Correctness and test proof: the four inline gaps mean the replacement suite does not preserve every deleted guarantee.
- Parallel paths: reviewed default, coverage, and targeted FE UT modes; native versus pure-Java tests; SQLite versus Flight SQL; and partitioned versus single-statement paths.
- Concurrency, lifecycle, configuration, compatibility, persistence, data writes, FE/BE contracts, observability, and runtime performance: no production behavior changes apply here. Test resources close on normal paths; the only performance effect is the intended CI-time module expansion.
- User focus: none was provided.
Validation was static only, as required by the review task: I did not run builds or tests, and I did not treat author-reported or CI results as independent execution evidence.
|
|
||
| execute(client, "CREATE TABLE t_new (a INTEGER)", "INSERT INTO t_new VALUES (1)"); | ||
|
|
||
| Optional<ConnectorTableHandle> handle = metadata(client).getTableHandle(null, "main", "t_new"); |
There was a problem hiding this comment.
[P2] Please retain a portable test for getTableHandle's own last-chance reload before deleting this case. The created-later regression queries do not reach that branch: fe-core first refreshes ExternalDatabase's missing name through AdbcConnectorMetadata.listTableNames, which calls cache.reloadTableNames; by the time getTableHandle runs, its initial cache.tableNames lookup already contains the new table. Removing the fallback reload in tableExists would therefore leave those regressions green. A pure-Java recording source can seed a stale connector listing, expose a new table, call getTableHandle directly, and assert the source is reread.
TPC-DS: Total hot run time: 152381 ms |
ClickBench: Total hot run time: 24.1 s |
… memory ### What problem does this PR solve? Issue Number: None Related PR: apache#68510 Problem Summary: AdbcClientTest.closeLeavesAResultACallIsStillReadingAlone checks that a call still reading a getObjects result when its AdbcClient is closed reads that result to the end. It read the result from the real SQLite driver. Reading a driver's result into Arrow loads arrow-c-data's own JNI shim, an upstream binary that needs CXXABI_1.3.9, and the FE UT agent's /lib64/libstdc++.so.6 does not have it. So the case failed FE UT at its first read with "UnsatisfiedLinkError: ... version `CXXABI_1.3.9' not found", before it could check anything about closing. It was the only failure of FE UT build 1086409 that counted; the other 11 cases of the class passed there, the native ones included. AdbcNativeTestSupport has stated the rule since apache#68510: tests that iterate an ArrowReader do not belong in this module's test sources. The case now runs on the recording client. The recording connection answers getObjects with SQLite's answer, built in memory by AdbcObjectsReaderTest.response (package-private now, with catalog and schema) from the allocator the client hands to openDatabase, which is the allocator a driver's result lives in. The case still checks what it was added for. It is also still the only case in the class that fails when close() frees the allocator at once and leaves only the database to the last call, which is why it is rewritten rather than dropped: the recording cases never allocate, and the native getCurrentCatalog case reads a Java string. ### Release note None ### Check List (For Author) - Test: Unit Test - fe-connector-adbc: all 197 tests pass, 0 skipped, with the real JNI bridge and SQLite driver; checkstyle reports no violations. - With the shim deleted from a copy of the arrow-c-data jar, standing in for the FE UT agent: the case as it was fails at its first read while the other 11 cases of the class pass, as on the agent; with this commit all 197 tests pass. - Mutations of AdbcClient.close(): releasing at once fails the same 4 cases as before this commit, this one included ("the database was released under an open connection"); freeing the allocator at once and deferring only the database fails this case alone ("Attempting operation on allocator when allocator is closed"), as it did before this commit. That message is checked only with Java assertions on, which surefire enables by default and no pom here turns off. - Behavior changed: No - Does this need documentation: No Co-Authored-By: Claude Opus 5.5 <[email protected]>
…ing it finish (#69874) ### What problem does this PR solve? Issue Number: None Related PR: #66331, #68353 Problem Summary: **In short:** dropping or altering an ADBC catalog can crash the FE process. Closing the catalog frees the native ADBC objects at once, including the connections and results that other threads are still reading, such as the column-statistics loads that a query on the catalog has just started. This kills the FE in about one External regression run in 75, whatever the PR under test changes. With this PR, closing a client only refuses new calls, and the last call still in flight releases the driver as it leaves. DROP and ALTER CATALOG still return without waiting. This commit was first pushed as the last commit of #68353, which is still in review. It shares nothing with the rest of that PR, so it is carved out here to land on its own. #### Background An ADBC catalog (#66331) reads its metadata through `AdbcClient`, which owns one native `AdbcDatabase` and one Arrow allocator per catalog. Every metadata call borrows a fresh connection through `AdbcClient.withConnection` and closes it afterwards. FE talks to the C driver through adbc-driver-jni 0.24. In that bridge, closing a parent closes everything still open under it, on the closing thread: `JniDatabase.close()` closes every connection still open on the database, and `JniConnection.close()` closes every result (`getObjects` and similar readers) still open on the connection. The bridge only checks a handle before a call starts, and the C driver does not check at all, so freeing an object that another thread is still using is a use-after-free in native code, which takes the whole JVM down rather than failing one call. Column statistics reach the driver too. When a query plans against an ADBC table, the STATS_FETCH pool loads column statistics in the background. A load that finds nothing in the statistics table asks the table itself: `PluginDrivenExternalTable.getColumnStatistic` → `AdbcConnectorMetadata.getTableHandle` → `withConnection` → `getObjects`. With `"meta.cache.adbc.metadata.enable" = "false"`, every such load calls into the driver. #### The problem, and what it cost - `AdbcClient.close()` closed the database and then the allocator immediately, whatever was still in flight. DROP CATALOG and ALTER CATALOG ... SET PROPERTIES close the catalog's connector this way, on their own thread (since #69665, on the `ExternalResourceReclaimer` pool; before that, synchronously on the statement thread, with the same race). - A statistics load that was reading a `getObjects` result, or was inside the driver on its connection, then read memory that had just been freed. Where it dies depends on where that thread is at that moment. - In CI, the External regression pipeline lost its FE in 11 of the 834 runs between 2026-10-22 and 2026-10-30. Each time, 54 to 56 suites then failed with `Communications link failure`, so the PR under test showed a red run it did not cause. All 11 died at the same moment: `test_adbc_sqlite_catalog_scan` drops its catalog with the metadata cache turned off right after querying it. In the same millisecond, fe.log shows the allocator closing with `Memory was leaked by query` and a statistics load failing with `Failed to read the ADBC getObjects result: ArrowArrayStream is already closed`. That is the same race, caught by a thread that came back only after the close had finished. | FE crash in `fe.out` | Runs | | --- | --- | | `SIGSEGV` in `nanoarrow::ArrayStreamFactory<VectorArrayStream>::get_next_wrapper+0x4` | 6 | | `SIGSEGV` in `PrivateArrowSchemaDeepCopy+0x46` | 2 | | glibc `double free or corruption (fasttop)` | 2 | | no message | 1 | | `SIGSEGV` in `sqlite3FindTable` (an earlier run, 2026-10-06) | 1 | - Users can hit this too, not only CI: DROP or ALTER an ADBC catalog while a statement or a statistics load is still reading it. #### How this PR fixes it - `AdbcClient` counts the calls inside `withConnection` (`enter` / `leave`, under the client's monitor, which also opens the database on first use). - `close()` marks the client closed, so new calls are refused with `AdbcClient has been closed`. It releases the database and the allocator itself only when no call is in flight. Otherwise it returns at once, and the last call to leave releases them. - A deferred release that fails is logged with the driver's path, as the catalog already logs a failed connector close. It is not thrown at that last caller, whose own work succeeded and which did not close anything. - `close()` never waits for the calls in flight, so DROP and ALTER CATALOG are not held up by a slow remote source. #### Results Unit tests (0 skipped). The first case runs against the real JNI bridge and the SQLite driver, the others against a recording database: | `AdbcClientTest` case | `close()` releasing at once, as before this PR | This PR | | --- | --- | --- | | a call still on its connection when the client is closed keeps using it | fails: `Native connection handle is closed` | passes | | a call still reading a `getObjects` result when the client is closed reads it to the end | fails: `the database was released under an open connection` | passes | | the last call to leave a closed client releases the database exactly once, after it leaves | fails: `the database was released under an open connection` | passes | | a release that fails as the last call leaves does not fail that call | fails: `the database was released under an open connection` | passes | | an idle client releases at once | passes | passes | Freeing only the allocator at once, and leaving the database to the last call, fails the `getObjects` case alone: `Attempting operation on allocator when allocator is closed`. A stress program (not committed) closes a client 200 times while four threads loop over `getObjects` and `getTableSchema` through `withConnection` against the SQLite driver: | | `AdbcClient` of master | This PR | | --- | --- | --- | | Runs | 5 | 5 | | JVM crashed | 5 of 5, 1.1 to 1.3 s after the JVM started: `SIGSEGV` in `PrivateArrowSchemaDeepCopy+0x40`, under `ArrowArrayStreamReader.readSchema` ← `AdbcObjectsReader.readNamespaces` ← `AdbcClient.withConnection` | 0 of 5 | | Closes completed per run | crashed before finishing | 200 | | Metadata calls per run | crashed before reporting | 6,019 to 7,238 | | Calls the close reached while they were using the driver | | 0 | | Failed closes, failed deferred releases | | 0, 0 (checked with WARN logging on, where the unit test's deliberate release failure shows up as one warning) | The stress runs used the macOS arm64 build of the same SQLite driver, so the offset differs from the Linux one in CI. #### Classes, and how they call each other - `AdbcClient`: `withConnection` brackets the body with `enter` / `leave`; `close` refuses new calls and releases only when idle; `release` closes the database, then the allocator; `openDatabase` is the one place the database is created, so a test can stand a recording database in for the driver. - `AdbcClientTest`: five new cases, one against the real driver and four against a recording database. The recording connection answers `getObjects` with a result built in memory in the client's allocator: reading a driver's result into Arrow loads arrow-c-data's JNI shim, which needs CXXABI_1.3.9 and does not load on the FE UT agent (see `AdbcNativeTestSupport`, #68510). - `AdbcObjectsReaderTest`: `response`, `catalog` and `schema` are package-private, so `AdbcClientTest` builds that result with them. ``` STATS_FETCH: ColumnStatisticsCacheLoader.doLoad -> PluginDrivenExternalTable.getColumnStatistic -> AdbcConnectorMetadata.getTableHandle (namespaces, table names) -> AdbcClient.withConnection enter() refused once closed; callsInFlight++ -> AdbcConnection.getObjects -> AdbcObjectsReader.readNamespaces / readTableNames leave() callsInFlight--; the last one out of a closed client -> release() DROP CATALOG / ALTER CATALOG ... SET PROPERTIES -> ExternalCatalog.onClose / resetToUninitialized -> ExternalResourceReclaimer -> AdbcConnector.close -> AdbcClient.close() closed = true; release() now only if no call is in flight release(): AdbcDatabase.close (closes its connections and their results) -> allocator.close ``` Not changed: `AdbcConnector`, the catalog lifecycle and `ExternalResourceReclaimer`, BE, and the regression suites; `test_adbc_sqlite_catalog_scan` stays as it is, since what it exposes is this bug.
What problem does this PR solve?
Issue Number: None
Related PR: #66399
Problem Summary:
Context.
fe-connector-adbcis not a Maven prerequisite of fe-core, so-amnever reached it and its tests never ran in FE UT; #66399 therefore names the module in the runner. Running it there for the first time turned 23 of its tests red on the CI agent, all with the same error:Two different native libraries are on that path, and only one of them was ours. The ADBC JNI shim is built by
thirdpartylocally, which is why it loads anywhere. Reading a driver's result as Arrow additionally needs arrow-c-data'sarrow_cdata_jni, which travels inside the arrow-c-data jar as an upstream build: it is selected through thearrow.cdata.library.pathproperty, nothing in this repository sets that property, so the jar's binary is always the one loaded -- and it needs CXXABI_1.3.9 (GCC 4.9+), which a host Doris still supports does not have (CentOS 7 stops at CXXABI_1.3.7).1. The problem, and what it cost
ArrowReaderlooks exactly like one that does not, so the next person adding a test to this module walks into the same wall.test_adbc_metadata_ops,test_adbc_sqlite_catalog_scan,test_adbc_catalog_scanandtest_adbc_predicate_pushdowneach say so in a comment), so deleting them without replacements would have moved that coverage out of CI silently.2. What this PR does, and why it helps
Commit 1 --
[test](fe) Drop the ADBC tests that need arrow-c-data's JNI shim, keep the rest in FE UT:AdbcQueryBuilderNativeTest(its text-level twinAdbcQueryBuilderTeststays, and "the source accepts the generated SQL" is now asserted end to end),AdbcMetadataCacheNativeTest(the cache's mechanics stay covered by the pure-JavaAdbcMetadataCacheTest, 18 cases), andAdbcConnectorMetadataTest.AdbcConnectorMetadataNativeTestunchanged.AdbcObjectsReaderTestis rewritten against hand-builtgetObjectsresponses (the file already carried the in-memory Arrow technique), so its four parsing cases run in FE UT with no native library at all; two more cases are added for the shapes a driver reports for an absent schema level, and for a namespace reported twice.AdbcNativeTestSupportnow states the boundary: tests that iterate anArrowReaderor touchorg.apache.arrow.cbelong inregression-test/suites/external_table_p0/adbc, not in this module's test sources.Commit 2 --
[test](regression) Cover the ADBC cases the removed unit tests pinned: the end-to-end half of those behaviours, as assertions rather than baselines, in the four suites listed below.What it buys: FE UT runs 187 ADBC tests with no dependency on the upstream C-data binary; the behaviours that need a real driver or a real cluster are asserted where they can be; and the rule lives in the class that owns the boundary.
3. The classes, and how they call each other
AdbcObjectsReader-- parses the nestedgetObjectsresponse. Now fed by fixtures that reproduce the same shapes a driver emits (a schema entry named the empty string, a null schema list, advisory filters that return other namespaces' rows), and covered again in FE UT.AdbcNativeTestSupport-- the single funnel every driver-backed ADBC test goes through; its javadoc is where the two-library distinction is now recorded.AdbcConnectorMetadata/AdbcMetadataCache-- the two surviving driver-backed cases never reach a result (one throws before it, one never asks the source), which is what makes them safe in FE UT.test_adbc_sqlite_catalog_scan(empty-named database, BLOB mapping, schema served until REFRESH),test_adbc_metadata_ops(same REFRESH negative),test_adbc_catalog_scan(a second database in the source; the sibling listing asserted as an exact set),test_adbc_predicate_pushdown(the remote statement's two-level qualification).run-fe-ut.shkeeps the module line, which is the point of the whole change.Release note
None
Check List (For Author)
mvn -f fe/pom.xml -pl :fe-connector-adbc -am test-> 187 tests, 0 failures (7 skipped locally: the SQLite driver is absent on macOS; they run in CI).bash run-regression-test.sh --run -d external_table_p0/adbc -suiteParallel 1-> Test 20 suites, failed 0 suites, fatal 0 scripts, skipped 0 scripts (test_adbc_multi_backendself-skips on one backend by design, as in CI).