Skip to content

[fix](fe) Keep an ADBC catalog's driver open until the calls still using it finish - #69874

Merged
morningman merged 2 commits into
apache:masterfrom
morningman:fix-adbc-client-close-race
Oct 31, 2026
Merged

morningman merged 2 commits into
apache:masterfrom
morningman:fix-adbc-client-close-race

Conversation

@morningman

@morningman morningman commented Oct 30, 2026 •

Copy link
Copy Markdown
Contributor

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 [improvement](fe) Defer external catalog resource cleanup from replay #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, [test](fe) Run the ADBC connector unit tests in FE UT without the arrow-c-data JNI shim #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.

Release note

Fix an FE crash (SIGSEGV in the ADBC driver) when an ADBC catalog is dropped or altered while a statement or a statistics load is still reading it.

Check List (For Author)

  • Test

    • Regression test
    • Unit Test
    • Manual test (add detailed scripts or steps below)
    • No need to test or manual test. Explain why:
      • This is a refactor/code format and no logic has been changed.
      • Previous test can cover this change.
      • No code files have been changed.
      • Other reason

    Unit test: AdbcClientTest, 12 cases including the five new ones, and all 197 tests of fe-connector-adbc pass with the real JNI bridge and SQLite driver, and still pass with arrow-c-data's JNI shim deleted from its jar, standing in for the FE UT agent, where the shim does not load. The four new cases above fail with close() put back to releasing at once; freeing only the allocator at once fails the getObjects case. Manual: the stress program in the table above, run against the AdbcClient of master and against this PR.

  • Behavior changed:

    • No.
    • Yes.

    DROP and ALTER CATALOG still return without waiting. The native driver of an ADBC catalog is now freed when its last in-flight call finishes, instead of under it.

  • Does this need documentation?

    • No.
    • Yes.

Check List (For Reviewer who merge this PR)

  • Confirm the release note
  • Confirm test cases
  • Confirm document
  • Add branch pick label

…ing it finish

### What problem does this PR solve?

Issue Number: None

Related PR: apache#66331, apache#68353

Problem Summary:

The External regression pipeline loses its FE in the middle of a run, for PRs that do not touch ADBC:
11 of the 834 runs between 2026-10-22 and 2026-10-30. The FE dies with a SIGSEGV or a glibc abort inside
libadbc_driver_sqlite.so, and every suite after that fails with "Communications link failure" (54 to 56
cases per run). The native frame differs from run to run:
nanoarrow::ArrayStreamFactory<VectorArrayStream>::get_next_wrapper+0x4 (6 runs),
PrivateArrowSchemaDeepCopy+0x46 (2), "double free or corruption (fasttop)" (2), no message (1); an
earlier run died in sqlite3FindTable. All of them die at the same moment: test_adbc_sqlite_catalog_scan
drops a catalog that has the metadata cache turned off ("meta.cache.adbc.metadata.enable" = "false")
right after querying it. In the same millisecond fe.log shows the catalog's Arrow allocator closing with
"Memory was leaked by query", and a column-statistics load failing with "Failed to read the ADBC
getObjects result: ArrowArrayStream is already closed".

Root cause: the query started column-statistics loads on the STATS_FETCH pool. A load that finds nothing
in the statistics table asks the table itself, which for an ADBC table goes through
PluginDrivenExternalTable.getColumnStatistic -> AdbcConnectorMetadata.getTableHandle ->
AdbcClient.withConnection -> getObjects. With the metadata cache off, it reaches the driver every time.
DROP CATALOG (and ALTER CATALOG ... SET PROPERTIES, which resets the catalog) closes the catalog's
connector on another thread, and AdbcClient.close() closed the native AdbcDatabase and the allocator at
once. In adbc-driver-jni, JniDatabase.close() first closes every connection still open on the database,
and each connection first closes the results still open on it, all on the closing thread. The JNI bridge
only checks a handle before a call starts, and the C driver does not check at all. So a statistics load
that is reading a getObjects result, or is inside the driver on its connection, uses native memory that
was just freed. Where it dies depends on where that thread is at that moment. A thread that comes back
only after the close has finished gets a Java exception instead: that is the "ArrowArrayStream is
already closed" in the log.

Fix: AdbcClient counts the calls inside withConnection. close() now only refuses new calls. It releases
the database and the allocator itself when no call is in flight. Otherwise the last call to leave
releases them. A failure of that deferred release is logged with the driver's path, as the catalog logs
a failed connector close, rather than thrown at a caller that did not close anything: that call's own
work succeeded. close() does not wait for the calls in flight, so DROP and ALTER CATALOG are not held up
by a slow remote source.

### Release note

Fix an FE crash (SIGSEGV in the ADBC driver) when an ADBC catalog is dropped or altered while a
statement or a statistics load is still reading it.

### Check List (For Author)

- Test: Unit Test, Manual test
    - AdbcClientTest +5, run with the real JNI bridge and SQLite driver (0 skipped; all 197 tests of
      fe-connector-adbc pass): a call still on its connection when the client is closed keeps using it;
      a call still reading a getObjects result when the client is closed reads it to the end; the last
      call to leave a closed client releases the database exactly once and only after it leaves; a
      release that fails as the last call leaves does not fail that call; an idle client releases at
      once. With close() put back to releasing at once, the first four fail ("Native connection handle
      is closed", "Attempting operation on allocator when allocator is closed", "the database was
      released under an open connection").
    - Manual test: a stress program (not committed) closes the client 200 times while four threads loop
      over getObjects and getTableSchema through withConnection. With the AdbcClient of master, 5 of 5
      runs crashed the JVM about a second after it started (SIGSEGV in PrivateArrowSchemaDeepCopy, under
      AdbcObjectsReader.readNamespaces). With this change, 5 of 5 runs completed all 200 closes (6,019 to
      7,238 calls each), no call failed because the close reached it, and no close or deferred release
      failed (checked with WARN logging on, where the unit test's deliberate release failure shows up as
      one warning).
- Behavior changed: No (DROP/ALTER CATALOG still return without waiting; the native driver of an ADBC
  catalog is now freed when its last in-flight call finishes instead of under it)
- Does this need documentation: No

Co-Authored-By: Claude Opus 5.5 <[email protected]>
@hello-stephen

Copy link
Copy Markdown
Contributor

Thank you for your contribution to Apache Doris.
Don't know what should be done next? See How to process your PR.

Please clearly describe your PR:

  1. What problem was fixed (it's best to include specific error reporting information). How it was fixed.
  2. Which behaviors were modified. What was the previous behavior, what is it now, why was it modified, and what possible impacts might there be.
  3. What features were added. Why was this function added?
  4. Which code was refactored and why was this part of the code refactored?
  5. Which functions were optimized and what is the difference before and after the optimization?

@morningman

Copy link
Copy Markdown
Contributor Author

run buildall

@morningman

Copy link
Copy Markdown
Contributor Author

/review

@github-actions github-actions Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reviewed PR 69874 at the verified head 50196fe. The production lifecycle change meets its goal: every successful AdbcClient enter is paired with one leave across connection, body, and close failures; close marks the client closed before deciding whether to defer; release detaches ownership under the monitor before native close; and all metadata, scan, and catalog teardown paths keep borrowed ADBC children inside withConnection. I found no additional production concurrency, lifecycle, error-handling, compatibility, or downstream-path defect.

The accepted issue is test placement. The new closeLeavesAResultACallIsStillReadingAlone test iterates a real native ArrowReader in the FE connector unit-test module. AdbcNativeTestSupport explicitly documents that ArrowReader tests belong in regression-test/suites/external_table_p0/adbc because the arrow-c-data JNI shim requires CXXABI_1.3.9 and causes FE unit-test failures on supported CentOS 7 hosts. Move that test to the regression suite or replace the native reader with a non-native fake.

Critical checkpoints: the task goal is implemented in production; the code remains focused; synchronized state and detached cleanup avoid release races; no configuration, protocol, persistence, or storage-format changes are involved; and the added tests were inspected for API compatibility and lifecycle coverage. Tests were not executed under this static-only review. No user-provided focus points were supplied. No existing P0/P1 inline findings apply. The review converged after round 1.

Review converged after 1 round(s).

@@ -74,6 +89,139 @@ void usingAClosedClientFailsLoud(@TempDir Path tempDir) {
Assertions.assertTrue(e.getMessage().contains("closed"), e.getMessage());
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[P1] Move this real ArrowReader materialization test out of FE unit tests (or replace it with a non-native fake). AdbcNativeTestSupport explicitly documents that tests which iterate an ArrowReader must live in the ADBC regression suites because the arrow-c-data JNI shim requires CXXABI_1.3.9 and makes FE unit tests fail on supported CentOS 7 hosts. This test calls getObjects and AdbcObjectsReader.readNamespaces on the native SQLite path, so the connector unit suite will fail on those hosts before it can verify the lifecycle fix.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in 2db8c88 with the second option. The case now runs on the recording client: the recording connection answers getObjects with SQLite's answer, built in memory by AdbcObjectsReaderTest.response (now package-private) from the allocator the client hands to openDatabase, which is the allocator a driver's result lives in. Nothing on that path touches org.apache.arrow.c. With the shim deleted from a copy of the arrow-c-data jar, standing in for the FE UT agent, the case as you reviewed it fails at its first read, as it did in FE UT build 1086409, while with this commit all 197 tests of the module pass.

The case keeps the coverage the native version had. With close() releasing at once, it fails along with the other three new cases ("the database was released under an open connection"). With only the allocator freed at once and the database left to the last call, it is still the only case in the class that fails ("Attempting operation on allocator when allocator is closed"). That is why I rewrote it rather than dropped it: the recording cases never allocate, and the native getCurrentCatalog case only reads a Java string.

I did not move it to the regression suites. A suite has no way to hold a statistics load between getObjects returning and the read, because the connector has no pause point to hold it at: DebugPointUtil lives in fe-common, which fe-connector-adbc does not depend on. The end-to-end form of this race is already covered, though not deterministically: test_adbc_sqlite_catalog_scan drops its _nocache catalog right after querying it, which is where the 11 External FE crashes in the description came from.

@hello-stephen

Copy link
Copy Markdown
Contributor
TPC-H: Total hot run time: 28593 ms
machine: 'aliyun_ecs.c7a.8xlarge_32C64G'
scripts: https://github.com/apache/doris/tree/master/tools/tpch-tools
Tpch sf100 test result on commit 50196fe30b5177f74d8bbfd0d132e96c6242c0b7, data reload: false

------ Round 1 ----------------------------------
============================================
q1	17600	5177	5027	5027
q2	2110	315	257	257
q3	10247	1369	809	809
q4	4677	462	338	338
q5	7433	802	552	552
q6	185	184	154	154
q7	759	796	586	586
q8	9250	1346	1033	1033
q9	5464	4174	4149	4149
q10	6805	1287	1038	1038
q11	427	249	228	228
q12	635	393	275	275
q13	18127	3640	3011	3011
q14	277	264	249	249
q15	q16	737	727	648	648
q17	1224	748	576	576
q18	7003	6182	6095	6095
q19	1255	969	616	616
q20	386	337	220	220
q21	6503	2444	2474	2444
q22	389	335	288	288
Total cold run time: 101493 ms
Total hot run time: 28593 ms

----- Round 2, with runtime_filter_mode=off -----
============================================
q1	5935	5896	6130	5896
q2	722	633	561	561
q3	5708	5771	5293	5293
q4	2036	2168	1552	1552
q5	5798	5822	5574	5574
q6	238	187	140	140
q7	2221	2007	1785	1785
q8	3066	2767	2646	2646
q9	7927	7489	7498	7489
q10	3587	3627	3130	3130
q11	568	435	378	378
q12	670	671	494	494
q13	3288	3632	3028	3028
q14	286	291	279	279
q15	q16	667	679	609	609
q17	7655	6972	6809	6809
q18	12901	12171	12757	12171
q19	905	789	769	769
q20	2139	2139	1922	1922
q21	5503	4543	4730	4543
q22	495	458	423	423
Total cold run time: 72315 ms
Total hot run time: 65491 ms

@hello-stephen

Copy link
Copy Markdown
Contributor
TPC-DS: Total hot run time: 150890 ms
machine: 'aliyun_ecs.c7a.8xlarge_32C64G'
scripts: https://github.com/apache/doris/tree/master/tools/tpcds-tools
TPC-DS sf100 test result on commit 50196fe30b5177f74d8bbfd0d132e96c6242c0b7, data reload: false

query5	4320	589	450	450
query6	433	213	195	195
query7	4811	441	234	234
query8	324	172	159	159
query9	8789	3220	3228	3220
query10	479	315	252	252
query11	6073	3841	3579	3579
query12	142	94	89	89
query13	1253	445	327	327
query14	6149	3881	3634	3634
query14_1	3402	3387	3445	3387
query15	205	201	196	196
query16	943	434	402	402
query17	944	656	522	522
query18	2412	430	317	317
query19	215	168	133	133
query20	88	88	88	88
query21	210	130	106	106
query22	12953	12989	12708	12708
query23	13417	12921	12457	12457
query23_1	12544	12449	12667	12449
query24	7360	837	481	481
query24_1	491	491	492	491
query25	528	417	362	362
query26	1266	259	142	142
query27	2759	442	273	273
query28	4625	2074	2061	2061
query29	1603	597	445	445
query30	321	217	186	186
query31	1002	931	819	819
query32	148	96	102	96
query33	538	306	241	241
query34	908	853	510	510
query35	732	758	644	644
query36	843	813	725	725
query37	125	105	86	86
query38	1792	1757	1654	1654
query39	788	783	747	747
query39_1	742	726	742	726
query40	236	126	100	100
query41	72	68	66	66
query42	90	86	84	84
query43	345	367	312	312
query44	1323	721	721	721
query45	186	185	171	171
query46	853	962	575	575
query47	2896	2785	2901	2785
query48	295	313	221	221
query49	589	390	291	291
query50	690	280	220	220
query51	10675	10462	10647	10462
query52	81	80	69	69
query53	184	207	154	154
query54	240	190	193	190
query55	78	65	66	65
query56	246	230	203	203
query57	1648	1602	1572	1572
query58	277	265	245	245
query59	2127	2188	1937	1937
query60	279	243	219	219
query61	146	147	144	144
query62	395	341	288	288
query63	188	163	159	159
query64	2779	911	742	742
query65	3329	3272	3249	3249
query66	1782	413	300	300
query67	20378	20254	19764	19764
query68	2931	1001	620	620
query69	390	292	253	253
query70	858	766	754	754
query71	300	235	220	220
query72	2764	2568	2324	2324
query73	516	531	304	304
query74	4603	4499	4336	4336
query75	2307	2282	1936	1936
query76	2321	1010	609	609
query77	345	370	276	276
query78	9654	9676	9112	9112
query79	1002	858	539	539
query80	1141	437	356	356
query81	591	313	269	269
query82	585	134	103	103
query83	305	200	184	184
query84	316	126	104	104
query85	845	429	362	362
query86	367	244	233	233
query87	1975	1935	1815	1815
query88	3820	2841	2839	2839
query89	333	286	243	243
query90	1879	168	167	167
query91	165	154	130	130
query92	99	86	89	86
query93	990	1008	581	581
query94	575	310	262	262
query95	572	337	305	305
query96	708	490	242	242
query97	2441	2434	2311	2311
query98	162	153	147	147
query99	762	755	662	662
Total cold run time: 235491 ms
Total hot run time: 150890 ms

@hello-stephen

Copy link
Copy Markdown
Contributor
ClickBench: Total hot run time: 24.19 s
machine: 'aliyun_ecs.c7a.8xlarge_32C64G'
scripts: https://github.com/apache/doris/tree/master/tools/clickbench-tools
ClickBench test result on commit 50196fe30b5177f74d8bbfd0d132e96c6242c0b7, data reload: false

query1	0.01	0.01	0.01
query2	0.09	0.05	0.05
query3	0.26	0.08	0.08
query4	1.61	0.10	0.13
query5	0.23	0.25	0.24
query6	1.17	0.68	0.67
query7	0.04	0.01	0.00
query8	0.05	0.04	0.04
query9	0.41	0.36	0.34
query10	0.58	0.57	0.56
query11	0.21	0.15	0.15
query12	0.19	0.16	0.15
query13	0.49	0.49	0.48
query14	1.04	1.02	1.01
query15	0.61	0.60	0.60
query16	0.34	0.35	0.33
query17	1.12	1.14	1.12
query18	0.21	0.20	0.19
query19	2.09	1.95	2.02
query20	0.02	0.01	0.02
query21	15.49	0.19	0.14
query22	4.82	0.05	0.05
query23	16.30	0.31	0.12
query24	2.78	0.62	0.35
query25	0.10	0.08	0.07
query26	1.42	0.22	0.14
query27	0.05	0.04	0.03
query28	3.78	0.86	0.36
query29	12.48	4.13	3.20
query30	0.30	0.13	0.10
query31	2.77	0.55	0.33
query32	3.22	0.60	0.50
query33	3.16	3.21	3.25
query34	15.33	3.97	3.38
query35	3.36	3.34	3.33
query36	0.55	0.46	0.45
query37	0.09	0.06	0.07
query38	0.05	0.05	0.03
query39	0.03	0.03	0.03
query40	0.17	0.16	0.14
query41	0.08	0.03	0.03
query42	0.03	0.03	0.03
query43	0.04	0.03	0.04
Total cold run time: 97.17 s
Total hot run time: 24.19 s

@hello-stephen

Copy link
Copy Markdown
Contributor

FE Regression Coverage Report

Increment line coverage 0.00% (0/404) 🎉
Increment coverage report
Complete coverage report

… 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]>
@morningman

Copy link
Copy Markdown
Contributor Author

run buildall

@morningman

Copy link
Copy Markdown
Contributor Author

/review

@github-actions github-actions Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reviewed all three changed files at 2db8c88 and the metadata/statistics, scan-planning and DROP/ALTER cleanup callers. No new actionable findings. The review converged in one round: both complete-review agents and the independent lifecycle-risk agent returned NO_NEW_VALUABLE_FINDINGS, and all risk hypotheses were independently resolved.

Existing P1 4420412731 is addressed. The result-reading test now uses RecordingClient and an Arrow IPC reader built with the client's allocator, avoiding the native C-data reader. Reading starts after close returns and still allocates through that allocator, preserving early-release coverage (Arrow 19 IPC allocation). No existing P0/P1 finding remains applicable; no duplicate inline comment is posted. No additional user-provided focus was specified.

Critical checkpoint conclusions:

  1. Goal: the counted lease protects an admitted call through connection/result cleanup. Close refuses subsequent calls and leaves release to the last active caller. The five added tests express the intended ordering; they were inspected, not executed.
  2. Scope and clarity: one production class and two related test classes; the package-private database factory and fixture visibility changes support recording tests without expanding the connector SPI.
  3. Concurrency: statistics/planning threads can race catalog reclamation. The client monitor guards closed, count, database and allocator. Connect, callback, connection close and resource destruction occur outside it. Cold initialization already performed I/O under this monitor. No new lock-order inversion was found.
  4. Lifecycle and memory: reader/statement/connection cleanup precedes decrement. Closed admission prevents reopening, and atomic detachment gives the database/allocator pair to one releaser even with competing close/last-leave calls. Failed entry, nested calls and exceptional exits were checked. No new ownership cycle or static-initialization dependency was found. Deferred native cleanup uses an already-captured closer (ADBC native handle); the inspected close chain does not depend on the restored TCCL.
  5. Configuration: no settings or dynamic-update behavior changed.
  6. Compatibility: no shared SPI, stored format or rolling-upgrade protocol change.
  7. Thrift/protobuf: no definitions or field IDs changed.
  8. Parallel paths: metadata listings, schema fallback, vendor probe, validation and partition planning all use the lease. Returned schemas/names are Java data; partition descriptors own Java byte arrays (ADBC partition result). No native reader escapes its callback.
  9. Conditions and errors: closed/null/count checks represent reachable lifecycle states. Driver errors retain translation, allocator cleanup remains in finally, idle-close failures propagate, and deferred-release failures are warned without replacing completed call results.
  10. Test coverage: latch/Future synchronization covers active connection and reader preservation, late rejection, last-call release, repeated close, release-failure isolation and idle release. The existing cache-disabled SQLite query/drop regression path was also inspected. Multi-caller and connect/callback/connection-close failure interleavings were code-reviewed but are not directly added as test cases.
  11. Test results: expected-result files are unchanged; new assertions agree with the IPC fixture's namespace and close counts.
  12. Observability: deferred failures include driver path and cause at WARN. No new high-volume logging or additional metric requirement was identified.
  13. Transactions and persistence: no EditLog, replay, commit/visibility or failover semantics changed; opening remains lazy.
  14. Data writes and atomicity: no data-write path changed; rowset/version/delete-bitmap invariants are unaffected. The relevant allocator/native ownership was checked above.
  15. FE/BE propagation: no cross-process variables or send paths changed.
  16. Performance: constant-time monitor admission/exit per metadata call; no new row loop, copy or remote call in production, and destruction runs outside the client monitor.
  17. Other issues: no additional verified defect after the final changed-file, candidate and risk sweep.

Limits: static review only. No builds, unit/regression tests, stress runs or native/platform execution were performed, and the author's reported execution results were not independently reproduced. No unresolved review candidate or uncovered changed-code scope remains.

Review converged after 1 round(s).

@hello-stephen

Copy link
Copy Markdown
Contributor
TPC-H: Total hot run time: 28282 ms
machine: 'aliyun_ecs.c7a.8xlarge_32C64G'
scripts: https://github.com/apache/doris/tree/master/tools/tpch-tools
Tpch sf100 test result on commit 2db8c889311ccc293292630caa96ffb8e2f43e6b, data reload: false

------ Round 1 ----------------------------------
============================================
q1	17625	5148	5020	5020
q2	2124	299	251	251
q3	10282	1406	822	822
q4	4687	465	342	342
q5	7429	794	524	524
q6	183	205	155	155
q7	737	806	605	605
q8	9278	1350	1043	1043
q9	5657	4194	4184	4184
q10	6803	1308	1038	1038
q11	416	249	223	223
q12	631	395	275	275
q13	18107	3570	3017	3017
q14	276	269	244	244
q15	q16	713	719	643	643
q17	1208	767	579	579
q18	6938	6158	6109	6109
q19	1121	948	601	601
q20	373	331	228	228
q21	4827	2121	2276	2121
q22	341	301	258	258
Total cold run time: 99756 ms
Total hot run time: 28282 ms

----- Round 2, with runtime_filter_mode=off -----
============================================
q1	5592	5599	5594	5594
q2	679	577	527	527
q3	5249	5404	4960	4960
q4	2008	2140	1552	1552
q5	5272	5172	5545	5172
q6	268	211	159	159
q7	2325	2095	1909	1909
q8	3022	2683	2680	2680
q9	8107	7992	7958	7958
q10	3748	3895	3246	3246
q11	633	457	428	428
q12	702	688	551	551
q13	3575	3862	3314	3314
q14	310	315	301	301
q15	q16	692	688	653	653
q17	8218	7067	6903	6903
q18	13029	12214	12899	12214
q19	905	781	784	781
q20	2160	2168	1907	1907
q21	5505	4576	4669	4576
q22	485	435	408	408
Total cold run time: 72484 ms
Total hot run time: 65793 ms

@hello-stephen

Copy link
Copy Markdown
Contributor
TPC-DS: Total hot run time: 151736 ms
machine: 'aliyun_ecs.c7a.8xlarge_32C64G'
scripts: https://github.com/apache/doris/tree/master/tools/tpcds-tools
TPC-DS sf100 test result on commit 2db8c889311ccc293292630caa96ffb8e2f43e6b, data reload: false

query5	4311	579	442	442
query6	424	220	200	200
query7	4803	442	225	225
query8	312	164	161	161
query9	8742	3157	3172	3157
query10	478	310	262	262
query11	5940	3868	3585	3585
query12	147	93	85	85
query13	1255	465	333	333
query14	6113	3955	3677	3677
query14_1	3456	3398	3473	3398
query15	210	198	184	184
query16	941	449	435	435
query17	858	670	540	540
query18	2424	429	348	348
query19	204	175	137	137
query20	86	85	86	85
query21	211	131	106	106
query22	13077	13082	12797	12797
query23	13408	13071	12450	12450
query23_1	12637	12538	12471	12471
query24	7388	837	467	467
query24_1	482	492	481	481
query25	547	432	361	361
query26	1292	263	140	140
query27	2771	443	277	277
query28	4606	2023	2040	2023
query29	1582	602	467	467
query30	305	224	192	192
query31	1016	939	841	841
query32	159	94	97	94
query33	539	306	250	250
query34	902	864	516	516
query35	739	758	648	648
query36	820	821	704	704
query37	129	105	85	85
query38	1795	1770	1698	1698
query39	788	810	751	751
query39_1	743	733	746	733
query40	223	122	99	99
query41	72	85	84	84
query42	94	87	94	87
query43	361	376	323	323
query44	1335	701	707	701
query45	180	182	167	167
query46	847	975	583	583
query47	2801	2949	2760	2760
query48	346	305	217	217
query49	570	404	292	292
query50	703	293	207	207
query51	10546	10830	10522	10522
query52	80	79	72	72
query53	184	208	161	161
query54	217	191	177	177
query55	74	78	67	67
query56	246	223	222	222
query57	1651	1598	1556	1556
query58	280	263	264	263
query59	2191	2146	2004	2004
query60	278	245	225	225
query61	142	146	146	146
query62	384	339	316	316
query63	190	159	165	159
query64	2754	954	736	736
query65	3355	3284	3304	3284
query66	1787	426	301	301
query67	20206	20139	20039	20039
query68	2926	1007	614	614
query69	385	290	238	238
query70	840	748	782	748
query71	304	240	221	221
query72	2720	2576	2329	2329
query73	523	530	301	301
query74	4590	4542	4352	4352
query75	2324	2304	1942	1942
query76	2322	1021	596	596
query77	325	369	276	276
query78	9660	9763	9175	9175
query79	990	849	548	548
query80	1138	457	344	344
query81	574	325	280	280
query82	578	133	103	103
query83	306	199	187	187
query84	321	119	102	102
query85	962	436	380	380
query86	366	237	217	217
query87	2038	1978	1809	1809
query88	3770	2816	2812	2812
query89	339	286	246	246
query90	1806	172	168	168
query91	171	163	130	130
query92	103	90	91	90
query93	996	1006	581	581
query94	566	308	281	281
query95	562	374	309	309
query96	687	482	242	242
query97	2425	2484	2335	2335
query98	166	170	154	154
query99	774	762	651	651
Total cold run time: 235076 ms
Total hot run time: 151736 ms

@hello-stephen

Copy link
Copy Markdown
Contributor
ClickBench: Total hot run time: 24.39 s
machine: 'aliyun_ecs.c7a.8xlarge_32C64G'
scripts: https://github.com/apache/doris/tree/master/tools/clickbench-tools
ClickBench test result on commit 2db8c889311ccc293292630caa96ffb8e2f43e6b, data reload: false

query1	0.01	0.01	0.01
query2	0.10	0.05	0.05
query3	0.26	0.09	0.08
query4	1.61	0.10	0.10
query5	0.28	0.24	0.24
query6	1.17	0.68	0.67
query7	0.04	0.01	0.00
query8	0.05	0.04	0.04
query9	0.42	0.35	0.34
query10	0.60	0.57	0.58
query11	0.20	0.14	0.14
query12	0.18	0.16	0.14
query13	0.48	0.50	0.49
query14	1.02	1.02	1.03
query15	0.63	0.60	0.61
query16	0.34	0.34	0.34
query17	1.20	1.12	1.21
query18	0.21	0.18	0.20
query19	2.14	2.04	2.02
query20	0.01	0.01	0.02
query21	15.49	0.20	0.14
query22	4.65	0.05	0.05
query23	16.27	0.33	0.12
query24	2.53	0.56	0.78
query25	0.11	0.04	0.06
query26	1.66	0.21	0.16
query27	0.04	0.03	0.04
query28	3.05	0.69	0.38
query29	12.49	4.13	3.24
query30	0.30	0.13	0.10
query31	2.76	0.55	0.33
query32	3.22	0.59	0.50
query33	3.25	3.10	3.30
query34	15.44	3.95	3.37
query35	3.36	3.32	3.31
query36	0.56	0.44	0.44
query37	0.09	0.07	0.06
query38	0.05	0.04	0.04
query39	0.04	0.04	0.03
query40	0.17	0.14	0.14
query41	0.09	0.03	0.03
query42	0.04	0.03	0.03
query43	0.04	0.03	0.03
Total cold run time: 96.65 s
Total hot run time: 24.39 s

@hello-stephen

Copy link
Copy Markdown
Contributor

FE UT Coverage Report

Increment line coverage `` 🎉
Increment coverage report
Complete coverage report

@morningman
morningman merged commit a72dcaa into apache:master Oct 31, 2026
42 of 44 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants