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


##########
be/src/storage/segment/segment.cpp:
##########
@@ -379,6 +393,25 @@ bool Segment::is_tso_placeholder_col(int cid, const 
Schema& schema,
     return cid == schema.tso_col_idx();
 }
 
+std::optional<Field> Segment::get_read_time_constant_value(
+        int cid, const Schema& schema, const StorageReadOptions& read_options) 
const {
+    if (read_options.version.first != read_options.version.second) {
+        return std::nullopt;
+    }
+    if (cid == schema.version_col_idx()) {

Review Comment:
   [P1] Use the logical VERSION_COL value on the normalized-predicate path
   
   This helper is only consulted by expression ZoneMap pruning. When the 
runtime filter is ready during scan open, `_normalize_predicate()` turns it 
into `col_id_to_predicates`; `Segment::new_iterator()` and 
`_get_row_ranges_from_conditions()` still test those predicates against the 
physical VERSION value `[0,0]` before `_replace_version_col_if_needed()` 
substitutes the rowset version. Thus a ready `VERSION_COL = 7` filter can 
create an empty iterator for a version-7 rowset, while the forced-late 
regression passes. Please use the logical constant for ordinary segment/page 
predicate pruning too and add a ready-at-open case.



##########
be/src/storage/segment/segment_iterator.cpp:
##########
@@ -3297,22 +3438,127 @@ Status 
SegmentIterator::current_block_row_locations(std::vector<RowLocation>* bl
     return Status::OK();
 }
 
+bool SegmentIterator::_has_arrived_late_runtime_filter() const {
+    return _late_runtime_filter_container != nullptr &&
+           
_late_runtime_filter_container->arrived_cnt.load(std::memory_order_relaxed) > 0;
+}
+
+Status SegmentIterator::_install_late_runtime_filter(const VExprContextSPtrs& 
expr_group,
+                                                     VExprContextSPtrs* 
installed_contexts) {
+    DORIS_CHECK(installed_contexts != nullptr);
+    DORIS_CHECK(!expr_group.empty());
+
+    VExprContextSPtrs local_contexts;
+    local_contexts.reserve(expr_group.size());
+    for (const auto& base_context : expr_group) {
+        DORIS_CHECK(base_context != nullptr);
+        VExprContextSPtr local_context;
+        RETURN_IF_ERROR(base_context->clone(_opts.runtime_state, 
local_context));

Review Comment:
   [P1] Do not reopen a shared expression tree from parallel iterators
   
   Every iterator clones the same published base context, but 
`VExprContext::clone()` shares `_root` and calls `_root->open()` again. 
Parallel scanners/segments therefore write the same non-atomic expression 
fields (`_open_finished`, and for IN predicates `_is_args_all_constant`), and a 
later clone can do so while another iterator or the scanner residual executes 
that tree. This is a C++ data race despite the safe pointer publication. Please 
give each iterator an independently owned expression tree (or otherwise make 
all root state immutable/context-owned); serializing only simultaneous clone 
calls would not prevent a later reopen from racing with execution.



##########
be/src/storage/segment/segment_iterator.cpp:
##########
@@ -1176,6 +1264,21 @@ Status 
SegmentIterator::_get_row_ranges_from_conditions(RowRanges* condition_row
             _opts.stats->rows_stats_filtered +=
                     (pre_expr_zonemap_size - condition_row_ranges->count());
         }
+        if (!_late_runtime_filter_ctxs.empty() && 
!condition_row_ranges->is_empty()) {
+            const auto rows_before_zonemap = condition_row_ranges->count();
+            
RETURN_IF_ERROR(_apply_expr_zonemap_to_row_ranges(_late_runtime_filter_ctxs, 0,
+                                                              
condition_row_ranges));
+            const auto rows_after_zonemap = condition_row_ranges->count();
+            DORIS_CHECK_LE(rows_after_zonemap, rows_before_zonemap);
+            // This is a coarse informational count based on the condition 
ranges. It may include
+            // rows already removed from _row_bitmap by other indexes, or rows 
later excluded by
+            // delete bitmaps and parallel-scan ranges, so it can overestimate 
the rows this
+            // iterator would actually read. The direct delta avoids extra 
range and bitmap
+            // operations in the scan hot path while still reflecting late RF 
ZoneMap pruning.
+            const auto rows_filtered = rows_before_zonemap - 
rows_after_zonemap;
+            _opts.stats->rows_stats_filtered += rows_filtered;
+            _opts.stats->rows_late_runtime_filter_zonemap_filtered += 
rows_filtered;

Review Comment:
   [P2] Keep the late-ZoneMap row counter tied to real candidates
   
   `rows_filtered` is computed before the condition ranges are intersected with 
this iterator's assigned `_row_bitmap` and before its delete bitmap is applied. 
The counter therefore increases even when the late filter removes no row this 
iterator could return; both new overlap tests intentionally expect 2,048 in 
exactly those cases. Because this is exported as 
`RowsLateRuntimeFilterZoneMapFiltered` and documented as incrementally pruned 
rows, profiles can report storage-pruning benefit where none occurred. Please 
count the cardinality removed from the actual candidate bitmap, or rename and 
document this as estimated page-range coverage instead.



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