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


##########
be/src/format_v2/table/lance_reader.cpp:
##########
@@ -126,6 +128,12 @@ Status LanceTableReader::init(TableReadOptions&& options) {
                                                        TUnit::UNIT, 
LANCE_READER_PROFILE, 1);
     _execution_bytes_read = ADD_CHILD_COUNTER_WITH_LEVEL(
             _scanner_profile, "LanceExecutionIOBytesRead", TUnit::BYTES, 
LANCE_READER_PROFILE, 1);
+    _data_cache_bytes_read_from_cache =
+            ADD_CHILD_COUNTER_WITH_LEVEL(_scanner_profile, 
"LanceDataCacheBytesReadFromCache",
+                                         TUnit::BYTES, LANCE_READER_PROFILE, 
1);
+    _data_cache_bytes_read_from_remote =
+            ADD_CHILD_COUNTER_WITH_LEVEL(_scanner_profile, 
"LanceDataCacheBytesReadFromRemote",

Review Comment:
   [P2] Do not expose this logical miss counter as remote I/O bytes. The C ABI 
says `bytes_read_from_remote` counts requested bytes returned after a miss and 
excludes block-aligned amplification; `cached_ranges()` can fetch a full 1 MiB 
block for a tiny range while incrementing only the tiny range. The same field 
is also incremented for local origins. This profile value therefore understates 
physical reads and misattributes local I/O as remote. Please name it as logical 
origin/miss bytes, and separately publish actual aligned origin bytes/requests 
with the real source attribution.



##########
fe/fe-core/src/main/java/org/apache/doris/datasource/lance/source/LanceScalarIndexPlanner.java:
##########
@@ -0,0 +1,139 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements.  See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership.  The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License.  You may obtain a copy of the License at
+//
+//   http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied.  See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+package org.apache.doris.datasource.lance.source;
+
+import org.apache.doris.analysis.CompoundPredicate;
+import org.apache.doris.analysis.Expr;
+import org.apache.doris.analysis.SlotRef;
+import org.apache.doris.datasource.lance.LanceFragmentInfo;
+import org.apache.doris.datasource.lance.LanceIndexSegmentInfo;
+import org.apache.doris.datasource.lance.LanceTableMetadata;
+
+import org.lance.index.IndexType;
+
+import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.TreeMap;
+import java.util.stream.Collectors;
+
+/** Assigns one BTree/Bitmap/LabelList segment and a disjoint fragment domain 
to each ordinary scan task. */
+final class LanceScalarIndexPlanner {
+    static final class Plan {
+        final String indexName;
+        final IndexSegmentSplitPlan splits;
+        private final long coveredRows;
+
+        Plan(String indexName, IndexSegmentSplitPlan splits, long coveredRows) 
{
+            this.indexName = indexName;
+            this.splits = splits;
+            this.coveredRows = coveredRows;
+        }
+    }
+
+    static Plan plan(LanceTableMetadata metadata, List<Expr> pushedConjuncts,
+            Map<Long, LanceFragmentInfo> visibleFragments) {
+        if (metadata.getVersion() <= 0) {
+            return null;
+        }
+        Set<Integer> filterFields = collectFilterFields(metadata, 
pushedConjuncts);
+        if (filterFields.isEmpty()) {
+            return null;
+        }
+        // Group all segments of each logical index before checking coverage. 
Name order
+        // provides a stable winner when multiple indices cover the same 
number of rows.
+        Map<String, List<LanceIndexSegmentInfo>> indices = 
metadata.getIndexSegments().stream()
+                
.collect(Collectors.groupingBy(LanceIndexSegmentInfo::getIndexName,
+                        TreeMap::new, Collectors.toList()));
+        Plan selected = null;
+        for (List<LanceIndexSegmentInfo> segments : indices.values()) {
+            // PR #79 supports one top-level key in BTree/Bitmap/LabelList 
indices. Lance
+            // performs the final typed driver selection and falls back within 
the same domain.
+            LanceIndexSegmentInfo index = segments.get(0);
+            if ((index.getIndexType() != IndexType.BTREE && 
index.getIndexType() != IndexType.BITMAP
+                    && index.getIndexType() != IndexType.LABEL_LIST)
+                    || index.getFieldIds().size() != 1 || 
!filterFields.contains(index.getFieldIds().get(0))) {

Review Comment:
   [P1] Only create a segment split when the pushed filter can actually drive 
this index. This check accepts any pushed expression that mentions the field, 
but Doris pushes predicates such as `!=`, `NOT IN`, and `IS NOT NULL` that 
Lance v11 represents as `Not(Query)`; PR #79's `driver()` only descends 
`Query`/`And`. It then reports `no_driver` after disabling ordinary scalar 
indexes and full-scans the segment's entire fragment domain. With the common 
single segment covering many fragments, this turns the previous 
fragment-parallel scan into one unsplittable full scan on one BE. Please align 
FE eligibility with the typed Lance planner, or retain fragment/global-index 
planning whenever a driver is not guaranteed.



##########
be/src/format_v2/table/lance_reader.cpp:
##########
@@ -1101,12 +1173,43 @@ void LanceTableReader::_close_dataset() {
         _fts_query_context = nullptr;
     }
     if (_dataset != nullptr) {
+        _collect_data_cache_statistics();

Review Comment:
   [P2] Refresh these counters at scan batch boundaries. This is the only call 
to `_collect_data_cache_statistics()`, and `_close_dataset()` normally runs 
only when the reader closes (the same dataset can also span several splits). 
Every successful batch, intermediate split, cancellation, and 
scanner/conversion error therefore leaves the new cache counters at zero until 
the query is already tearing down, contrary to the scanner-v2 requirement that 
child statistics be visible for in-progress scans. Please sample the cumulative 
dataset snapshot on each `get_block()` exit, including EOF/error paths, while 
retaining the close-time refresh.



-- 
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