-
Notifications
You must be signed in to change notification settings - Fork 631
[GLUTEN-12013][VL] Fix bloom-filter bytes corruption on whole-stage AQE fallback #12151
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -0,0 +1,78 @@ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| /* | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| * 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.gluten.extension | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import org.apache.gluten.config.GlutenConfig | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import org.apache.gluten.expression.VeloxBloomFilterMightContain | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import org.apache.gluten.expression.aggregate.VeloxBloomFilterAggregate | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import org.apache.spark.sql.SparkSession | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import org.apache.spark.sql.catalyst.expressions.{BloomFilterMightContain, XxHash64} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import org.apache.spark.sql.catalyst.expressions.aggregate.{AggregateExpression, BloomFilterAggregate} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import org.apache.spark.sql.catalyst.rules.Rule | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import org.apache.spark.sql.execution.SparkPlan | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| /** | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| * Physical pre-transform rule that rewrites runtime-filter bloom filters (the ones injected by | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| * Spark's `InjectRuntimeFilter` optimizer rule) to their Velox variants so they offload natively. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| * | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| * Runtime bloom filters cannot be handled by [[BloomFilterMightContainJointRewriteRule]]: that rule | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| * is registered via `injectOptimizerRule`, which lands in Spark's Operator Optimization batch, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| * while `InjectRuntimeFilter` runs in a later batch of `SparkOptimizer`. The runtime-filter | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| * expressions therefore do not exist yet when the logical rule fires, and a physical-level rewrite | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| * (as was always done before the logical rule was introduced) is required to keep | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| * `FilterExecTransformer` and the bloom-filter aggregate native. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| * | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| * The rewrite is restricted to `InjectRuntimeFilter`'s exact expression shapes, which always wrap | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| * the key in [[XxHash64]] on both the producer and the consumer side: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| * - producer: `bloom_filter_agg(xxhash64(key), ...)` -> `velox_bloom_filter_agg(...)` | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| * - consumer: `might_contain(bf, xxhash64(key))` -> `velox_might_contain(...)` | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| * | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| * Because each side is identifiable on its own, both are rewritten consistently to the Velox byte | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| * format (version=1) even when AQE compiles the bloom-filter subquery separately from the consuming | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| * filter stage. The `XxHash64` fingerprint also guarantees the other bloom-filter populations are | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| * never touched: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| * - `DataFrame.stat.bloomFilter()` builds `bloom_filter_agg(col, ...)` on the raw column (no | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| * `XxHash64` wrapper) and deserializes the result with Spark's `BloomFilter.readFrom`, so its | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| * bytes must stay in Spark-native format. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| * - User-facing `might_contain(<scalar subquery>, <value>)` pairs are already rewritten at the | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| * logical level by [[BloomFilterMightContainJointRewriteRule]] (the GLUTEN-12013 fix), making | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| * this rule a no-op for them. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| * - Literal-value pairs (SPARK-54336) contain no `XxHash64` and stay fully vanilla. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| */ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| case class RuntimeBloomFilterRewriteRule(spark: SparkSession) extends Rule[SparkPlan] { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @brijrajk, thank you so much for the update. For runtime bloom filters, could you clarify what happens if the producer and consumer become inconsistent, i.e., one is offloaded to Velox while the other falls back due to the fallback policy?
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @philo-he Good question. Verified at three levels: code inspection, Spark-source trace, and runtime probes in a container. Summary: 1. Operator-level mixes are safe by construction. This rule rewrites the expressions at pre-transform. Wherever the operators land (native or JVM), the expressions stay
2. Full disclosure: whole-stage fallback reversion is a real gap.
3. Caveats bounding the gap:
4. Why not fixable the same way here: it needs a logical rewrite after 5. Candidate fixes (follow-up):
@philo-he @zhztheplayer what do you think the next steps should be for resolving this gap: a follow-up issue (I can file it with the probe D/E reproduction), or do you consider it in scope for this PR?
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @brijrajk, thanks for the investigation. A cleaner fix might be a Spark extension API that lets our logical rule run after Alternatively, we could change the fallback policy to block whole-stage fallback when a stage contains bloom-filter. That keeps both producer and consumer native and consistent, at the cost of overriding the fallback decision. @zhztheplayer, do you have any comments?
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Is there any possible risk, if we always replace @brijrajk by the way, in the current PR status, why
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @zhztheplayer @philo-he I prototyped the injectFinal PoC results (zhztheplayer's Q1)Implemented as keep-preTransform-plus-add-final (a pure move would regress offload: validation needs the Velox expression before
Probe E is deterministic (same missing keys across reruns). A bloom filter cannot produce false negatives, so the filter itself was corrupt. Strictly worse than the crash, so I have not pushed it. Root cause: capacity divergence (new finding)Probe E's plan shows a phase split: native partial (
Correction to my Jul 14 comment: probe E's original crash was this same phase split (vanilla final agg reading native partial buffers), not the consumer reverting; the consumer stayed a native Why the logical rule stays (zhztheplayer's Q2)
The three options (incl. philo-he's proposals)
Proposed sequencing
Happy to file the follow-ups with the probe reproductions. Does this sequencing work for you both?
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @zhztheplayer You're right, I re-verified this by building and running in the container with added diagnostics rather than guessing.
Exactly 2x, deterministic, and independent of table stats or session config.
Probe D reverts the whole subquery stage together, so both ends use the same formula and stay consistent. Probe E reverts only the final-aggregation stage while partial stays native -- native partial sets bits modulo an 8,388,608-bit array; the JVM final buffer (sized for 16,777,216 bits) merges those bits into the first half of a differently-sized array (unchecked, per the Bottom line unchanged: any fix needs the JVM aggregate's buffer sizing reconciled with the native formula (e.g. size from
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @brijrajk Thanks for the summary. So can we just fix the JVM buffer creation code to align with the native code? Then keep one single rule in the planner?
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @zhztheplayer Short answer: the capacity fix stands on its own and is worth doing regardless. Consolidating to one rule doesn't work as a simple move though. I went ahead and built it to check. What I built and ranCapacity fix plus one unified rule, registered at both preTransform and injectFinal, with the logical rule dropped entirely. Ran it against the existing regression suite (GlutenBloomFilterFallbackSuite). What failed4 of the 7 tests crash with the same
The split is clean: anything needing "check the consumer's value, then update the producer to match" fails. Anything that doesn't need that coordination passes. Why it failsI logged the plan before and after collect() on the threshold=2 test to see what was actually happening:
Same rule, same match arm, same subq.withNewPlan(...) call. Only half of it takes effect at runtime. Here's why: That also explains why the runtime-filter rule was never affected: it doesn't reach into the subquery manually at all, it matches the aggregate expression wherever the framework's own traversal already visits it, so it's wired correctly by construction. And it explains why the logical rule works: it rewrites the subquery's logical plan before the cache is even built, so the cache is already correct by the time AQE captures it. SuggestionKeep the two rules as they are for this PR. File the capacity fix as its own follow-up since it doesn't depend on any of this. And treat "can a physical rule ever be made reversion-safe for the coordinated case" as a separate question, one the Spark extension point idea might actually answer better than anything we can do rule-side.
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. From my understanding, if we can fix the capacity issue first, then the error you met on native partial stage + JVM final stage during experimenting If yes can you first open a PR to align the capacity, which can be merged first, then in this PR you can update to an
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @zhztheplayer Yes, confirmed. I built exactly this (capacity fix, plus registering the existing RuntimeBloomFilterRewriteRule at injectFinal too, nothing else changed) and it works cleanly. Full existing regression suite (GlutenBloomFilterFallbackSuite): 7/7 passed, since the logical rule is untouched. The adversarial probe that was silently wrong before (native partial stage + JVM final stage) now returns correct results:
This works because RuntimeBloomFilterRewriteRule never reaches into the subquery's plan and rebuilds it. It matches the producer and the consumer independently, each identified purely by the xxhash64 wrapper, and lets Spark's own traversal wire the rewrite in wherever it lands. That's different from what broke in the full-consolidation version I tried earlier, where the rule had to reach into the subquery manually to coordinate the literal check, and that manual rewrite didn't survive to execution. I opened the capacity fix as its own PR: #12614 (issue #12613). Verified there too: reverting just the fix while keeping the new regression test makes exactly that one test fail, with everything else in the suite unaffected, so the test is genuinely pinned to the bug. Once #12614 merges, I will update this PR to add the injectFinal registration on top. |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| override def apply(plan: SparkPlan): SparkPlan = { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (!GlutenConfig.get.enableNativeBloomFilter) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return plan | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| plan.transformWithSubqueries { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| case p => | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| p.transformExpressions { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| case ae @ AggregateExpression(b: BloomFilterAggregate, _, _, _, _) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if b.child.isInstanceOf[XxHash64] => | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ae.copy(aggregateFunction = VeloxBloomFilterAggregate( | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| b.child, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| b.estimatedNumItemsExpression, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| b.numBitsExpression, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| b.mutableAggBufferOffset, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| b.inputAggBufferOffset)) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| case BloomFilterMightContain(bf, value: XxHash64) => | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| VeloxBloomFilterMightContain(bf, value) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It looks like the early-exit
if (!GlutenConfig.get.enableNativeBloomFilter) return planis untested. Can we add a test case?There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Added a test that sets
spark.gluten.sql.native.bloomFilter=falseand checks the query still returns the correct count and the optimized plan does not containvelox_might_contain.