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


##########
regression-test/suites/inverted_index_p0/cloud_build_index/test_cloud_build_index_update.groovy:
##########
@@ -91,7 +91,7 @@ suite("test_cloud_build_index_update") {
     sql """build index on test_cloud_build_idx_uq_table"""
     wait_for_last_build_index_finish("test_cloud_build_idx_uq_table", timeout)
     check_inverted_index_filter_rows("select * from 
test_cloud_build_idx_uq_table where address='hhhhh'" +
-            " order by user_id,username,age,address", 12)
+            " order by user_id,username,age,address", 4)

Review Comment:
   [P1] Restore the non-phrase filter count after the final ordering gate. This 
query is a plain equality predicate, so `can_consume_candidate_rows()` is false 
and the final `prune_before_index` gate leaves scan/delete restrictions after 
inverted-index accounting. That restores the base metric ordering, where this 
assertion was 12; expecting 4 reflects the earlier unconditional reorder and 
should make this Cloud P0 case fail at the final head. Please keep the expected 
count at 12 (or change the query to a candidate-consuming phrase predicate if 
this case is meant to exercise early pruning).



##########
be/src/common/config.cpp:
##########
@@ -1320,6 +1322,18 @@ DEFINE_Bool(enable_inverted_index_cache_check_timestamp, 
"true");
 DEFINE_mBool(enable_inverted_index_correct_term_write, "true");
 DEFINE_Int32(inverted_index_fd_number_limit_percent, "20"); // 20%
 DEFINE_Int32(inverted_index_query_cache_shards, "256");
+DEFINE_mDouble(inverted_index_candidate_pushdown_ratio, "0.3");
+DEFINE_Validator(inverted_index_candidate_pushdown_ratio,
+                 [](const double v) -> bool { return std::isfinite(v) && v <= 
1.0; });
+static std::atomic<double> published_inverted_index_candidate_pushdown_ratio 
{0.0};
+static std::mutex inverted_index_candidate_pushdown_ratio_update_lock;
+DEFINE_ON_UPDATE(inverted_index_candidate_pushdown_ratio, [](double, double 
value) {

Review Comment:
   [P2] Keep failed persistent updates coherent with the scan-side value. 
`UPDATE_FIELD` assigns the raw ratio and `full_conf_map` before calling 
`persist_config()`, but a persistence error returns before this callback runs. 
A request such as `persist=true` against an unwritable custom config therefore 
reports `BAD` and SHOW CONFIG exposes the new ratio, while every scan continues 
reading the old atomic value indefinitely. Please either roll back the earlier 
mutations on persistence failure or publish a single coherent in-memory state, 
and cover this exit in the config test.



##########
be/src/storage/index/snii/query/phrase_prefix_exec.cpp:
##########
@@ -493,12 +494,56 @@ Status collect_merged_tail_matches(const 
LogicalIndexReader& idx,
     return Status::OK();
 }
 
+// Picks how the leading phrase of a multi-tail prefix query is restricted. A 
tail union much
+// smaller than the leading candidate set prefilters it; otherwise scan 
candidates, when present,
+// restrict it directly. The union is kept whenever the unrestricted query 
would use it, so scan
+// candidates never make the leading phrase costlier; they can also enable it, 
since they bound
+// the leading positions to decode.
+Status restrict_prefix_leading_phrase(const LogicalIndexReader& idx,
+                                      const internal::ResolvedPhrasePlan& 
exact_plan,
+                                      const std::vector<ResolvedQueryTerm>& 
tail_terms,
+                                      const roaring::Roaring* candidates,
+                                      std::vector<uint32_t>* storage,
+                                      CandidateRestriction* restriction) {
+    uint32_t min_lead_df = std::numeric_limits<uint32_t>::max();
+    for (const ResolvedQueryTerm& term : exact_plan.unique_terms) {
+        min_lead_df = std::min(min_lead_df, term.entry.df);
+    }
+    uint64_t tail_df_sum = 0;
+    for (const ResolvedQueryTerm& tail : tail_terms) {
+        tail_df_sum += tail.entry.df;
+    }
+    const bool union_pays_off_unrestricted =
+            min_lead_df >= prefix_leading_prefilter_min_df(
+                                   idx, exact_plan.phrase_plan_index.size() == 
1) &&
+            tail_df_sum <= min_lead_df / kPrefixLeadingToTailDfRatio;
+    const uint64_t lead_bound =
+            candidates == nullptr ? 0 : std::min<uint64_t>(min_lead_df, 
candidates->cardinality());
+    const bool union_pays_off_restricted = lead_bound >= 
kMinPrefixLeadingPrefilterMinDf &&
+                                           tail_df_sum <= lead_bound / 
kPrefixLeadingToTailDfRatio;
+    if (!union_pays_off_unrestricted && !union_pays_off_restricted) {

Review Comment:
   [P2] Let selective candidates bypass the full prefix-tail union. When 
candidates are present, this OR still honors `union_pays_off_unrestricted` even 
if the candidate-aware bound says the union cannot pay off. For example, lead 
df 1M, tail df sum 100k, and one candidate builds and decodes the entire 100k 
tail union, intersects it to one row, and then replans/decodes those tail 
postings during verification; directly restricting the leading phrase to that 
one candidate avoids the preliminary union. Please use the candidate-aware 
decision whenever `candidates != nullptr` (keeping the unrestricted decision 
only without candidates) and add a selective-candidate counter/benchmark case 
alongside the dense-candidate test.



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