guptashivam04 commented on code in PR #69854:
URL: https://github.com/apache/doris/pull/69854#discussion_r4438803269


##########
be/src/exec/operator/olap_scan_operator.cpp:
##########
@@ -526,8 +527,38 @@ static Status 
validate_residual_scan_conjuncts(RuntimeState* state,
     return Status::OK();
 }
 
+static bool expr_reads_slot(const VExprSPtr& expr, int slot_id) {
+    DORIS_CHECK(expr != nullptr);
+    if (expr->is_slot_ref() && assert_cast<const 
VSlotRef*>(expr.get())->slot_id() == slot_id) {
+        return true;
+    }
+    return std::ranges::any_of(expr->children(), [slot_id](const auto& child) {
+        return expr_reads_slot(child, slot_id);
+    });
+}
+
+bool OlapScanLocalState::_conjuncts_read_delete_sign() const {
+    const auto& p = _parent->cast<OlapScanOperatorX>();
+    const auto& slots = p._output_tuple_desc->slots();
+    const auto delete_sign = std::ranges::find_if(
+            slots, [](const SlotDescriptor* slot) { return slot->col_name() == 
DELETE_SIGN; });
+    // A conjunct on the delete sign reads its slot, so the scan outputs it.
+    if (delete_sign == slots.end()) {
+        return false;
+    }
+    const int slot_id = (*delete_sign)->id();
+    auto reads_delete_sign = [slot_id](const VExprContextSPtr& conjunct) {
+        return conjunct->root() != nullptr && 
expr_reads_slot(conjunct->root(), slot_id);
+    };

Review Comment:
   Fixed in 
https://github.com/apache/doris/pull/69854/commits/2f7b088b0e6b2021c9be9863069adee6bb6dffe4.
 Only a scan conjunct that is exactly `__DORIS_DELETE_SIGN__ = 0` (the FE's 
filter, or the same condition written by the query) now hides the delete-sign 
rows from the statistics. Any other predicate on the column, such as `= 1`, `!= 
0` or `>= 0`, leaves them counted, like a predicate on any other column.
   
   Covered by 
`OlapScanOperatorDeleteSignTest.OtherPredicateOnDeleteSignDoesNotFilterIt` and 
a new `show_hidden_columns` + `__DORIS_DELETE_SIGN__ = 1` case in 
`test_bm25_score_delete_sign_statistics` on V2/V3/SNII, which fails without the 
change (80 rows score differently).
   



##########
be/src/storage/index/inverted/common/norm_length_sums.h:
##########
@@ -0,0 +1,117 @@
+// 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.
+
+#pragma once
+
+#include <cstdint>
+#include <functional>
+#include <mutex>
+#include <span>
+#include <string>
+#include <unordered_map>
+#include <utility>
+#include <vector>
+
+#include "common/status.h"
+#include "util/hash_util.hpp"
+
+namespace doris::segment_v2 {
+
+// The sums of norm lengths that merge-on-write BM25 statistics need of an 
index, kept with the
+// index's reader in the searcher cache, so they live and are evicted with it. 
The index never
+// changes, so the sums are computed once and every later query reads no 
norms. They take a few
+// dozen bytes per field.
+class NormLengthSums {
+public:
+    // The sum of the lengths the norms of the non-NULL documents of `field` 
stand for, computed
+    // by `compute` the first time.
+    Status non_null_norm_length_sum(const std::wstring& field,
+                                    const std::function<Status(uint64_t*)>& 
compute,
+                                    uint64_t* out) {
+        {
+            std::lock_guard lock(_mutex);
+            if (auto it = _non_null_sums.find(field); it != 
_non_null_sums.end()) {
+                *out = it->second;
+                return Status::OK();
+            }
+        }
+        // Computed outside the lock, which other queries of the index take to 
read their sums.
+        // Two queries may both compute a sum the index does not hold yet; 
both get the same.
+        RETURN_IF_ERROR(compute(out));

Review Comment:
   Fixed in 
https://github.com/apache/doris/pull/69854/commits/ea8bcafce4a89a2e25d5269709c39b2c2e4d0f3a.
 The first query that needs a sum computes it. Queries that need the same sum 
meanwhile wait for it instead of reading the norms, while other fields and 
other hidden-row sets are not blocked. If the computation fails or throws, the 
key is released and a waiting query computes the sum itself.
   
   Covered by `NormLengthSumsTest.ConcurrentQueriesComputeASumOnce` (50 
repeated runs on ASAN). Also checked on a 3-BE cluster: 16 clients starting 
together on cold caches, and a cancel/timeout stress run with cache evictions. 
No stuck queries, and every row keeps one score.
   



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