StoreQueryParams::is_exact_query is a bool computed once, then threaded through StoreQueryPlan / RangeQueryExecutionContext, and finally consulted by execute_store_query (asap-query-engine/src/engines/simple_engine/mod.rs:482) to pick Store::query_precomputed_output_exact vs query_precomputed_output. Every call site that builds a StoreQueryParams has to independently get this derivation right:
That's the actual failure mode: nothing type-checks or enforces that a new call site derives the bool correctly, so the two production call sites already drifted out of sync once (#580 → #582 → still-broken → #608). Originally proposed as a comment on #581 (#581 (comment)); splitting out as its own issue since #581 (broader instant/range unification) isn't going to happen soon and this is a small, self-contained, low-risk change that shouldn't wait on it.
Proposed fix
Remove is_exact_query as a stored/threaded field. Instead, infer query_precomputed_output vs _exact directly from WindowType at the point where the store is actually called, for every caller except the keys path, which keeps its current behavior as an explicit, named choice (not something a blanket "derive from WindowType" rule would get right by accident — keys always scan).
Concretely, likely shape:
execute_store_query (or a thin wrapper) takes a WindowType (or an explicit "always scan" override for keys) instead of reading params.is_exact_query off the struct.
create_store_query_plan and finish_range_context stop setting the field on StoreQueryParams and instead pass/derive WindowType down to the call site.
create_keys_query_params keeps forcing the scan call, but via an explicit parameter/variant rather than a bool field on the same struct as the value-side derivation — so "keys always scan" reads as a decision, not a value that happens to match the default.
Scope
asap-query-engine/src/engines/simple_engine/mod.rs — StoreQueryParams, create_store_query_plan, execute_store_query, create_keys_query_params.
asap-query-engine/src/engines/simple_engine/promql.rs — finish_range_context.
Sequencing
Do this before #608 (Sliding range-query exact-fetch fix), as prep — #608 adds a new Sliding-specific per-step branch in the range pipeline that needs to call the exact-window store method; doing this refactor first means that new branch is written against the post-refactor call shape instead of threading a bool that's about to be removed anyway.
Testing
Existing instant/range/keys query tests should pass unchanged — this is meant to be a zero-behavior-change refactor (mirrors "step 1" of the staged unification strategy in .design_docs/instant-vs-range-pipeline-audit.md). Add/keep a test per call site confirming it still selects the same store method (exact vs scan) it did before the refactor.
Related: #581, #580, #608.
StoreQueryParams::is_exact_queryis a bool computed once, then threaded throughStoreQueryPlan/RangeQueryExecutionContext, and finally consulted byexecute_store_query(asap-query-engine/src/engines/simple_engine/mod.rs:482) to pickStore::query_precomputed_output_exactvsquery_precomputed_output. Every call site that builds aStoreQueryParamshas to independently get this derivation right:create_store_query_plan(mod.rs:427) — instant, correct:is_exact_query = window_type == WindowType::Sliding.finish_range_context(promql.rs:615) — range, currently hardcodedfalseregardless ofWindowType(the bug fixed by Range queries over Sliding-window aggregations use overlap-scan fetch, not exact-window fetch #608).create_keys_query_params(mod.rs:402) — deliberately hardcodedfalsealways, independent ofWindowType("keys always use range queries").That's the actual failure mode: nothing type-checks or enforces that a new call site derives the bool correctly, so the two production call sites already drifted out of sync once (#580 → #582 → still-broken → #608). Originally proposed as a comment on #581 (#581 (comment)); splitting out as its own issue since #581 (broader instant/range unification) isn't going to happen soon and this is a small, self-contained, low-risk change that shouldn't wait on it.
Proposed fix
Remove
is_exact_queryas a stored/threaded field. Instead, inferquery_precomputed_outputvs_exactdirectly fromWindowTypeat the point where the store is actually called, for every caller except the keys path, which keeps its current behavior as an explicit, named choice (not something a blanket "derive from WindowType" rule would get right by accident — keys always scan).Concretely, likely shape:
execute_store_query(or a thin wrapper) takes aWindowType(or an explicit "always scan" override for keys) instead of readingparams.is_exact_queryoff the struct.create_store_query_planandfinish_range_contextstop setting the field onStoreQueryParamsand instead pass/deriveWindowTypedown to the call site.create_keys_query_paramskeeps forcing the scan call, but via an explicit parameter/variant rather than aboolfield on the same struct as the value-side derivation — so "keys always scan" reads as a decision, not a value that happens to match the default.Scope
asap-query-engine/src/engines/simple_engine/mod.rs—StoreQueryParams,create_store_query_plan,execute_store_query,create_keys_query_params.asap-query-engine/src/engines/simple_engine/promql.rs—finish_range_context.Sequencing
Do this before #608 (Sliding range-query exact-fetch fix), as prep — #608 adds a new Sliding-specific per-step branch in the range pipeline that needs to call the exact-window store method; doing this refactor first means that new branch is written against the post-refactor call shape instead of threading a bool that's about to be removed anyway.
Testing
Existing instant/range/keys query tests should pass unchanged — this is meant to be a zero-behavior-change refactor (mirrors "step 1" of the staged unification strategy in
.design_docs/instant-vs-range-pipeline-audit.md). Add/keep a test per call site confirming it still selects the same store method (exact vs scan) it did before the refactor.Related: #581, #580, #608.