github-actions[bot] commented on code in PR #65503:
URL: https://github.com/apache/doris/pull/65503#discussion_r3567485087


##########
be/src/format_v2/jni/jni_table_reader.cpp:
##########
@@ -329,11 +354,15 @@ Status JniTableReader::_close_jni_scanner() {
     _collect_jni_scanner_profile(env);
 
     // _fill_jni_block may fail before releasing the current Java table. 
JniScanner::releaseTable()
-    // is idempotent, so closing the split always releases it.
-    RETURN_IF_ERROR(_jni_scanner_obj.call_void_method(env, 
_jni_scanner_release_table).call());
-    RETURN_IF_ERROR(_jni_scanner_obj.call_void_method(env, 
_jni_scanner_close).call());
+    // is idempotent, so closing the split always releases it. Java close must 
still run if that
+    // release fails; otherwise connector resources such as JDBC connections 
can leak.
+    auto cleanup_status = _jni_scanner_obj.call_void_method(env, 
_jni_scanner_release_table).call();
+    auto java_close_status = _jni_scanner_obj.call_void_method(env, 
_jni_scanner_close).call();

Review Comment:
   The retry path here is still not actually retryable. If `releaseTable()` or 
Java `close()` returns a non-OK status, this line drops `_jni_scanner_obj` and 
clears `_scanner_opened`; `JniTableReader::close()` then leaves `_closed` 
false, but the next `close()` call enters `_close_jni_scanner()` with no Java 
object and returns OK without invoking Java cleanup again. That matters for 
scanners such as Hudi where `close()` can throw while the underlying reader 
resource still exists, and more generally it makes a Java cleanup failure look 
retryable when the retry has already been discarded. Please preserve the Java 
object/open state until cleanup succeeds, or otherwise make the failure 
terminal instead of pretending the next close can retry it. The new 
`FailedCloseCanBeRetried` test should also use a faithful fake, because the 
current override increments `close_calls` even after it has cleared the scanner 
state.



##########
be/src/format_v2/jni/jni_table_reader.cpp:
##########
@@ -371,14 +399,42 @@ Status JniTableReader::_open_jni_scanner() {
     SCOPED_RAW_TIMER(&_jni_scanner_open_watcher);
     RETURN_IF_ERROR(_register_jni_class_functions_once(env));
     RETURN_IF_ERROR(_create_jni_scanner_object(env, 
cast_set<int>(_batch_size)));
-    // call open() method in JAVA side.
-    RETURN_IF_ERROR(_jni_scanner_obj.call_void_method(env, 
_jni_scanner_open).call());
-    RETURN_ERROR_IF_EXC(env);
-
+    // Once the Java object exists, close it even if open() fails partway 
through initialization.
+    // Connector implementations may already own streams, off-heap tables, or 
JDBC connections.
     _scanner_opened = true;
+    // call open() method in JAVA side.
+    const auto open_status = _jni_scanner_obj.call_void_method(env, 
_jni_scanner_open).call();
+    if (!open_status.ok()) {
+        const auto close_status = _close_jni_scanner();
+        if (!close_status.ok()) {
+            LOG(WARNING) << "failed to clean up JNI scanner after open 
failure: " << close_status;
+        }

Review Comment:
   This still misses the first Paimon JNI physical batch. For a JNI split, 
`prepare_split()` has already called `_open_jni_scanner()`, and 
`_open_jni_scanner()` falls back to `RuntimeState::batch_size()` when 
`_batch_size` is still zero. FileScannerV2 only initializes/predicts the 
adaptive probe after that and then calls this method right before 
`get_block()`. That updates `JniScanner.batchSize`, so Doris returns only the 
probe rows, but Paimon already copied the constructor batch into 
`CoreOptions.READ_BATCH_SIZE` during Java `open()` when it built the 
table/reader; `setBatchSize()` does not rebuild that reader. The result is that 
the first Paimon `reader.readBatch()` can still allocate/read the full runtime 
batch, which defeats the adaptive memory cap this change is trying to provide. 
Please seed the first adaptive size before eager JNI open, or update the Paimon 
JNI reader's physical read batch before the first `readBatch()`, and add a 
timing test for prepare -> adaptive probe -> 
 first JNI get.



##########
be/src/exec/operator/file_scan_operator.cpp:
##########
@@ -116,10 +116,19 @@ bool 
FileScanLocalState::_should_use_file_scanner_v2(const TQueryOptions& query_
     const bool is_transactional_hive =
             scan_params.__isset.table_format_params &&
             scan_params.table_format_params.table_format_type == 
"transactional_hive";
+    const bool uses_paimon_cpp_reader =

Review Comment:
   This check does not match the shape of real Paimon scan params. 
`PaimonScanNode` sets the table-format descriptor on each `TFileRangeDesc` in 
`setPaimonParams()`, while scan-level params only get values such as 
`paimon_predicate` and backend options. So for a normal Paimon query with 
`enable_file_scanner_v2=true` and `enable_paimon_cpp_reader=true`, 
`scan_params.__isset.table_format_params` can still be false here and V2 
remains selected. The first PAIMON_CPP `FORMAT_JNI` split then reaches V2 even 
though `PaimonHybridReader` only dispatches `PAIMON_JNI` as JNI. Please key 
this exclusion off information that is actually available at scan-selection 
time, or have FE set an explicit scan-level marker, and add a test using 
FE-shaped params with no scan-level `table_format_params`.



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to