Problem
Issue #729 introduced a native range-query DAG, but the plan is currently only partly executable. After compilation, NativePlanRuntime still retains &RangeQueryExecutionContext and uses it as the semantic authority for several nodes. A plan can therefore describe one operation while runtime executes behavior derived from duplicated context fields.
This was identified during PR #745 review. The grouped-topk regression discussed there is already fixed; this issue addresses the separate plan/runtime ownership design gap.
Decision
Keep RangeQueryExecutionContext as a compilation-only input.
After QueryPlan::compile_range succeeds, query semantics must live exclusively in QueryPlan. NativePlanRuntime may retain execution resources such as &SimpleEngine, the store, and execution-local caches, but must not retain or consult RangeQueryExecutionContext.
Target flow:
PromQL request + configured precomputes
-> RangeQueryExecutionContext (compile-time only)
-> QueryPlan (complete semantic recipe)
-> NativePlanRuntime { engine, cache } (resources only)
Current plan/context duplication
StoreRead carries a query and strategy, but runtime ignores strategy, compares the node query against context.base.store_plan, and fetches both value/key reads through context.
ComposeWindows declares output timestamps, lookback, window size, and bucket step, but runtime ignores those fields.
Estimate carries statistic and query_kwargs, but runtime calls estimate_range_query(context, ...); range windows, aggregation metadata, labels, and bounds come from context instead.
LimitTopK carries k and grouping labels, but runtime derives row-label order from context.
Format is substantially self-contained already.
This makes the current DAG descriptive rather than the single source of executable semantics.
Scope
- Lower every range-query execution semantic needed after compilation into explicit plan-node data or compact plan-owned specification types.
- Remove
RangeQueryExecutionContext from NativePlanRuntime.
- Make each node execute according to its own declared parameters.
- Make
StoreRead independently identify and perform its requested read strategy; do not select data by comparing against context-owned query fields.
- Resolve the
ComposeWindows mismatch deliberately:
- either make it genuinely compose windows from its declared fields; or
- rename it to an honest indexing/normalization operation and remove unused semantic fields.
- Move top-k row-label ordering into
LimitTopK.
- Preserve current public behavior and error/fallback contract:
Ok(None) is an unsupported native capability miss and may fall back;
Err(QueryExecutionError) is an accepted native execution failure and must remain local.
A likely shape is a plan-owned RangeEstimateSpec containing value/key window specifications, aggregation metadata needed by merging/key resolution, output labels, statistic, kwargs, and per-step bounds inputs. Do not copy the existing context wholesale into a node: extract cohesive plan-owned specs with explicit invariants.
Non-goals
Acceptance criteria
NativePlanRuntime has no RangeQueryExecutionContext field or argument.
- Mutating a compile-time context after plan construction cannot alter plan execution; ideally this is enforced structurally because execution accepts only plan + runtime resources.
- Each node either consumes all fields that define its semantics or those fields are removed/renamed.
- A plan with distinct value/key reads executes the specified node reads without context identity comparisons.
- Existing DAG-only E2E behavior coverage remains green, including tumbling/sliding, separate keys, DeltaSet replay, top-k ties/grouping, capability misses, malformed-plan handling, and local HTTP no-fallback behavior.
- Existing legacy-vs-DAG E2E characterization coverage remains green under
native_query_legacy_test_support.
- The PromQL Docker compliance matrix has no regression relative to
main.
Suggested tests
Prefer public E2E and plan-execution behavior tests over isolated helper tests:
- Compile a range plan, execute it with a runtime that has no query context, and compare public range-query results to the current DAG baseline.
- Exercise two different read/window specifications in one plan shape (value plus separate keys) to prove each node uses plan-owned semantics.
- Ensure grouped top-k uses plan-owned
k, grouping labels, and row-label order.
- Retain malformed-plan validation and failing-store/no-fallback tests; they ensure plan/runtime errors remain explicit.
References
Problem
Issue #729 introduced a native range-query DAG, but the plan is currently only partly executable. After compilation,
NativePlanRuntimestill retains&RangeQueryExecutionContextand uses it as the semantic authority for several nodes. A plan can therefore describe one operation while runtime executes behavior derived from duplicated context fields.This was identified during PR #745 review. The grouped-
topkregression discussed there is already fixed; this issue addresses the separate plan/runtime ownership design gap.Decision
Keep
RangeQueryExecutionContextas a compilation-only input.After
QueryPlan::compile_rangesucceeds, query semantics must live exclusively inQueryPlan.NativePlanRuntimemay retain execution resources such as&SimpleEngine, the store, and execution-local caches, but must not retain or consultRangeQueryExecutionContext.Target flow:
Current plan/context duplication
StoreReadcarries a query and strategy, but runtime ignoresstrategy, compares the node query againstcontext.base.store_plan, and fetches both value/key reads through context.ComposeWindowsdeclares output timestamps, lookback, window size, and bucket step, but runtime ignores those fields.Estimatecarriesstatisticandquery_kwargs, but runtime callsestimate_range_query(context, ...); range windows, aggregation metadata, labels, and bounds come from context instead.LimitTopKcarrieskand grouping labels, but runtime derives row-label order from context.Formatis substantially self-contained already.This makes the current DAG descriptive rather than the single source of executable semantics.
Scope
RangeQueryExecutionContextfromNativePlanRuntime.StoreReadindependently identify and perform its requested read strategy; do not select data by comparing against context-owned query fields.ComposeWindowsmismatch deliberately:LimitTopK.Ok(None)is an unsupported native capability miss and may fall back;Err(QueryExecutionError)is an accepted native execution failure and must remain local.A likely shape is a plan-owned
RangeEstimateSpeccontaining value/key window specifications, aggregation metadata needed by merging/key resolution, output labels, statistic, kwargs, and per-step bounds inputs. Do not copy the existing context wholesale into a node: extract cohesive plan-owned specs with explicit invariants.Non-goals
native_query_legacy_test_support, with DAG as production default, until a later cutover decision.handle_query_promql/handle_range_query_promqlerror API.Acceptance criteria
NativePlanRuntimehas noRangeQueryExecutionContextfield or argument.native_query_legacy_test_support.main.Suggested tests
Prefer public E2E and plan-execution behavior tests over isolated helper tests:
k, grouping labels, and row-label order.References