Repository navigation
feat(asap-tools): rqe query protocol and frontend switch for e2e runs - #890
Conversation
Add `asap_types::rqe_expr`: a serde type for RQE expression trees (rqe leaves, agg, binary, scalar nodes), a `validate` check run at load, and `leaves` / `output_labels` helpers. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_011gKTCAEz3sJ1Hzde16xxU1
An e2e query's optional `expr` (an RQE expression tree, validated at load) now names the plan RQEs it reads. Each tree leaf becomes one more optimizer item and Raqe, served by its plan pick like a text leaf, and gets an entry in a new `rqe_configs` section of the inference config: RQE id -> the aggregations it reads and, for an exact roll-up, the query-time stage that regroups to the RQE's grouping. `query_configs` are unchanged; a table without trees converts as before and writes no `rqe_configs`. A query may have only a tree: its language is that of the metrics its leaves read (records -> SQL). The engine's `InferenceConfig` loads `rqe_configs` (empty when absent); nothing reads it yet. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_011gKTCAEz3sJ1Hzde16xxU1
…uery `SimpleEngine::handle_rqe_query` evaluates a validated RQE tree at a query time without parsing PromQL/SQL. A leaf reads the aggregations its `rqe_configs` entry names, over the lookback ending at the time aligned as the PromQL instant path aligns it, through the shared context tail and executor; a roll-up config reads at the aggregation's grouping and its query-time stages regroup to the leaf's. `agg` nodes regroup with the query-time aggregation pipeline, `binary` nodes reuse PromQL's vector and scalar combiners. Output labels are the root's labels, never `__name__`. The route answers Prometheus instant-vector JSON whatever the adapter's language and never forwards: invalid body/tree, unknown RQE id or bad time is a 400 `bad_data`, an execution failure a 500, an empty window an empty result. Top-k leaves rank globally (a leaf has no bucketing labels). The SQL context tail moves to `build_execution_context_tail` (now returning the planning error) and `get_aggregation_id_info` takes aggregation refs, so both the SQL and RQE builders share them. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_011gKTCAEz3sJ1Hzde16xxU1
The querier gains an `rqe` protocol client. Queries keep their PromQL/SQL text in the config; a JSON side file (query text -> RQE expression tree) named by the server's `rqe_exprs_file` gives the tree, which is POSTed with the evaluation time to `/api/v1/rqe_query`. The response is parsed as the Prometheus client parses an instant vector, so `QueryResult.result` and fidelity scoring are unchanged. The lookup uses the config's unrendered text (clients now also get `config_query`); a query missing from the side file fails without a request. A relative side file resolves against the client config's directory, and the compose generator mounts it there. `experiment_run_e2e` takes `query_engine.frontend: promql|rqe` (default promql) and `query_engine.rqe_exprs_file`. Under rqe the side file is checked to cover every query, copied next to the client configs, and the sketchdb mode's server switches to the rqe protocol; baselines keep PromQL/SQL. `sketch_bench_e2e_configs.py` writes `<config stem>.rqe_exprs.json` from the table's `e2e.queries[].expr`, keyed by the exact PromQL/SQL text in the config. `--frontend rqe` sets the frontend and leaves out queries without `expr` (e.g. rate) with a note. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_011gKTCAEz3sJ1Hzde16xxU1
An RQE leaf may name `topk_by` labels: like PromQL `topk by (...)`, a top-k leaf then ranks its input independently within each value combination of those labels, while `grouping` stays its output labels. Absent, the whole input is one bucket. Validation allows `topk_by` only on `topk_by_value` leaves, non-empty and within `grouping`. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_011gKTCAEz3sJ1Hzde16xxU1
…tion' into 882-featplanner-planner-saved-reads-rqe-trees-and-writes-rqe_configs
A top-k leaf's optimizer item now carries its `topk_by` labels as `topk_by_labels`, as a PromQL `topk by (...)` query's does, so its Raqe is grouped by the buckets (the plan's top-k RQE grouping) and its heap is one per bucket, keyed by the remaining labels. Without `topk_by` the Raqe is a single bucket and can't share the deployment of a `topk by (...)` query. Tests run the pilot random workload (seed-862/workload-01-pilot) and its saved plans: `topk by (region, service)` leaves read the same heaps as their PromQL queries. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_011gKTCAEz3sJ1Hzde16xxU1
…s-rqe_configs' into 883-featquery-engine-rqe-tree-query-route
A top-k leaf now ranks per bucket the way the PromQL path ranks `topk by (...)`: its aggregation keeps one heap per bucket (the aggregation's grouping) and the instant plan's top-k limit ranks within each. The leaf's `topk_by` (none: one bucket) must equal that grouping; otherwise the leaf fails as an execution error instead of answering a different ranking. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_011gKTCAEz3sJ1Hzde16xxU1
…tasap-tools-rqe-query-protocol-and-frontend-switch-for-e2e-runs
milindsrivastava1997
left a comment
There was a problem hiding this comment.
Code review: 10 findings.
Findings on lines outside the diff:
asap-tools/experiments/experiment_only_ingest_path.py:172 — Only experiment_run_e2e.py passes rqe_exprs_file into generate_controller_client_configs. This runner (and experiment_run_grafana_demo.py:146) never call rqe_exprs_file_for_frontend, so query_engine.frontend=rqe is silently ignored.
Failure scenario: Running with query_engine.frontend=rqe keeps protocol prometheus and sends PromQL with no error; results are labelled RQE but measure the PromQL frontend (breaks 'fail loud').
| @@ -212,11 +243,27 @@ def build_config( | |||
| prebuilt_output_dir = os.path.abspath(prebuilt_output_dir) | |||
| check_prebuilt_controller_output(prebuilt_output_dir) | |||
There was a problem hiding this comment.
With --frontend rqe, queries without an expr (e.g. rate) are dropped from query_groups, but --prebuilt-output-dir is accepted as-is and never checked against the reduced set.
Failure scenario: A planner output built for all 42 queries used with --frontend rqe makes ASAPQuery maintain sketches for queries never sent, skewing the sketchdb arm's memory/CPU vs the baseline with no warning.
| ) | ||
| if response.status_code == 200: | ||
| return response.json().get("data", {}) | ||
| except Exception: |
There was a problem hiding this comment.
fetch_runtime_info is now shared by the Prometheus and rqe clients, and swallows every exception (except Exception: pass), returning None.
Failure scenario: rqe server unreachable or malformed JSON during query-time alignment → None instead of the real error, surfacing later as a confusing assert/misalignment (code-design-review rule 1).
There was a problem hiding this comment.
Fixed in b74fae7: fetch_runtime_info raises on HTTP and connection errors and on a malformed body, and no longer returns None.
| ) -> None: | ||
| """Write the config and, next to it, its RQE side file.""" | ||
| os.makedirs(os.path.dirname(output_path), exist_ok=True) | ||
| side_file = os.path.abspath(rqe_exprs_path(output_path)) |
There was a problem hiding this comment.
write_config always writes <stem>.rqe_exprs.json and sets query_engine.rqe_exprs_file to an absolute local path, even with --frontend promql where it's never read. Write the file and key only when frontend == rqe.
There was a problem hiding this comment.
Fixed in 396b534: the side file and query_engine.rqe_exprs_file are written only when frontend == rqe.
| self, | ||
| query: str, | ||
| query_time: Optional[int] = None, | ||
| config_query: Optional[str] = None, |
There was a problem hiding this comment.
A new config_query parameter is threaded through every QueryClient.execute_query signature and caller, but only RqeQueryClient uses it, to look up a tree by exact unrendered query text.
Failure scenario: Prometheus/ClickHouse clients take an ignored parameter, and any normalization of the query text (whitespace, template edit) between generator, YAML round-trip and querier gives a runtime 'no RQE expression' failure. Attaching the tree to the query entry in the client config (per-query rqe_expr) or keying by query index avoids both.
There was a problem hiding this comment.
Keeping this as is (decided on #886). Templated SQL renders differently on every run, so the unrendered config text is the only stable key; an index-keyed side file breaks when the config is edited. The writer produces the config and the side file together, and the runner checks that every query has a tree before any service starts. 4e469a6 explains this in the docstrings.
| debug_adapter = DebugHTTPAdapter(server.name) | ||
| client.session.mount("http://", debug_adapter) | ||
| client.session.mount("https://", debug_adapter) | ||
| elif protocol == "rqe": |
There was a problem hiding this comment.
The protocol name 'rqe' is a bare string literal in ~6 places: here, classes/config.py:18, generate_prometheus_client_compose.py:34, the factory registry, RqeQueryClient.protocol_name, and experiment_utils/config.py RQE_PROTOCOL. Code-design-review rule 2 (one source of truth); a rename in one place silently breaks the compose mount or ServerConfig check.
There was a problem hiding this comment.
Fixed in 1cc5f2e: one RQE_PROTOCOL per package. In the querier it lives in rqe_query_client.py; in the experiments it lives in constants.py. These are separate packages and images, so one constant each is the floor.
| ), | ||
| ) | ||
|
|
||
| # Mount debug adapter for HTTP request logging |
There was a problem hiding this comment.
The rqe branch copies the DebugHTTPAdapter creation and http/https mount block, now in all three protocol branches. Mount once after the if/elif/else that builds the client.
There was a problem hiding this comment.
Fixed in a762a00: mounted once after the if/elif/else.
| so the file keeps that relative place inside the container. | ||
| """ | ||
| with open(config_file) as f: | ||
| servers = yaml.safe_load(f)["servers"] |
There was a problem hiding this comment.
rqe_exprs_mounts runs for every compose generation and indexes yaml.safe_load(f)['servers'] unchecked.
Failure scenario: A non-rqe client config without servers (or a not-yet-rsynced path) used to produce a compose file; it now fails with an unexplained KeyError/FileNotFoundError.
There was a problem hiding this comment.
Fixed in beb812f: returns [] when the config has no servers or the file is missing. A missing side file for an rqe server still raises a clear FileNotFoundError, because Docker would otherwise mount an empty directory.
| "--frontend rqe does)" | ||
| ) | ||
| with open(os.path.join(output_dir, RQE_EXPRS_FILENAME), "w") as f: | ||
| json.dump(exprs, f, indent=1) |
There was a problem hiding this comment.
_copy_rqe_exprs re-serializes the whole side file with json.dump instead of copying it, and copies every entry rather than only the queries that run. shutil.copyfile after validation, or writing only the run's subset, is simpler.
There was a problem hiding this comment.
Fixed in 786d0f6: validate, then shutil.copyfile.
| error_message=f"RQE query failed: {body}", | ||
| raw_response=body, | ||
| ) | ||
| raw_result = body["data"]["result"] |
There was a problem hiding this comment.
A 200 response is assumed to hold an instant vector: body['data']['result'] goes to normalize_vector with no resultType check, and a broad except turns mismatches into a generic failure.
Failure scenario: A scalar or matrix result makes normalize_vector call .get on a float/list → reported as 'AttributeError: ...' rather than 'unexpected result type'.
There was a problem hiding this comment.
Fixed in c02311c: a resultType other than vector fails with 'unexpected result type X'.
Require every subtree that feeds the result (root, agg operand, one side of each binary op) to read at least one RQE leaf; allow a repeated leaf id only for identical leaves; reject duplicate labels in grouping, by and topk_by; reject an empty id or metric and a zero lookback. Document that label lists are sets matched by name. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_011gKTCAEz3sJ1Hzde16xxU1
…tion' into 882-featplanner-planner-saved-reads-rqe-trees-and-writes-rqe_configs
…frontend=rqe experiment_only_ingest_path.py and experiment_run_grafana_demo.py never switch the querier to the rqe protocol, so an rqe run there silently measured the PromQL frontend. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_011gKTCAEz3sJ1Hzde16xxU1
…nd rqe drops With --frontend rqe, queries without an expr are left out of the config, but a --prebuilt-output-dir planned for them made ASAPQuery maintain sketches for queries never sent. Refuse it and point to `asap-planner --planner saved --expr-only`. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_011gKTCAEz3sJ1Hzde16xxU1
It swallowed every exception and returned None, hiding an unreachable server or a malformed body behind a later confusing failure. No caller relies on the None. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_011gKTCAEz3sJ1Hzde16xxU1
Under promql nothing reads <stem>.rqe_exprs.json, so neither the file nor query_engine.rqe_exprs_file is written. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_011gKTCAEz3sJ1Hzde16xxU1
…uery text Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_011gKTCAEz3sJ1Hzde16xxU1
The querier's config, factory, client and main share RQE_PROTOCOL from rqe_query_client; the experiments' config generation and compose mounts share constants.RQE_PROTOCOL. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_011gKTCAEz3sJ1Hzde16xxU1
Every protocol branch mounted the same DebugHTTPAdapter; mount it after the client is built. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_011gKTCAEz3sJ1Hzde16xxU1
A client config without `servers`, or not on disk yet, now gets no rqe mounts instead of a KeyError/FileNotFoundError; a missing side file of an rqe server is a clear error rather than an empty directory mount. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_011gKTCAEz3sJ1Hzde16xxU1
…serializing it Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_011gKTCAEz3sJ1Hzde16xxU1
A scalar or matrix result reached normalize_vector and surfaced as an AttributeError; check data.resultType first and report the mismatch. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_011gKTCAEz3sJ1Hzde16xxU1
|
Review-body finding ( |
- Compare a query's `rqes` with its tree's leaves as sets. - Allow one RQE leaf at several repeat intervals: each interval is a Raqe, and the interval-independent rqe_configs entry is written once. Different leaves under one id are still refused. - Name a query in errors by its text, else its tree's RQE ids. - Compute and check a query's RQE ids once. - Check a tree's leaves read one kind of metric without dedup(). - Describe the e2e-tables fixture instead of citing its PR. - --expr-only (with --planner saved) plans only the e2e queries that have an RQE tree; the others (e.g. rate) get no deployment. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_011gKTCAEz3sJ1Hzde16xxU1
…s-rqe_configs' into 883-featquery-engine-rqe-tree-query-route
Before reading a leaf, check it against the aggregation its rqe_configs entry names: same metric (table.value_column for SQL), a type that serves its capability with value weighting, a lookback its windows tile, top-k buckets equal to the heap grouping, and a grouping the aggregation serves (its own labels, a mergeable subset of its groups, or what its query-time stages regroup to). A mismatch is the request's fault: 400 bad_data, not 500. Also: drop error paths validation already rules out (missing phi/k, scalar root), make the end-timestamp alignment warning language-neutral, and stop claiming a config snapshot. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_011gKTCAEz3sJ1Hzde16xxU1
…ery-route' into 884-featasap-tools-rqe-query-protocol-and-frontend-switch-for-e2e-runs
milindsrivastava1997
left a comment
There was a problem hiding this comment.
Automated code review: 4 finding(s), unverified candidates.
| so the file keeps that relative place inside the container. A config | ||
| that isn't there (yet) or lists no servers has no rqe server to mount for. | ||
| """ | ||
| if not os.path.isfile(config_file): |
There was a problem hiding this comment.
rqe_exprs_mounts silently returns [] when the client config file doesn't exist at generation time, so an rqe server's side file is not mounted.
Failure scenario: Compose generated before client configs are rsynced (or wrong path): no rqe_exprs mount, container starts, RqeQueryClient.__init__ raises FileNotFoundError inside it. Since prometheus_client_service runs gen; docker compose up with ;, nothing fails at generation time.
(from automated /code-review; unverified)
| pass | ||
| return result | ||
|
|
||
| def get_runtime_info(self) -> Optional[Dict[str, Any]]: |
There was a problem hiding this comment.
get_runtime_info changed from returning None on error to raising, and it has no callers; meanwhile main_prometheus_client.get_query_start_times still does its own requests.get to /api/v1/status/runtimeinfo instead of fetch_runtime_info.
Failure scenario: Two implementations of the runtime-info fetch that will drift; a future caller relying on the Optional return type gets an exception instead of None.
(from automated /code-review; unverified)
There was a problem hiding this comment.
Fixed in da6b110: get_query_start_times uses fetch_runtime_info; the unused get_runtime_info methods are deleted.
| return config, notes, rqe_exprs | ||
|
|
||
|
|
||
| def check_prebuilt_plans_no_dropped_queries( |
There was a problem hiding this comment.
check_prebuilt_plans_no_dropped_queries inspects only inference_config queries by substring match and ignores streaming_config aggregations.
Failure scenario: Dropped sum(rate(x[5m])) and kept sum(rate(x[5m])) / 2 share a leaf: the check passes but the rate aggregation planned for the dropped query is still deployed, skewing cost. A planned entry without a query key raises KeyError instead of the intended ValueError.
(from automated /code-review; unverified)
There was a problem hiding this comment.
(line 517) Query-time alignment (get_query_start_times) waits only on aggregation ids under inference_config queries, never on ids referenced only by rqe_configs.
Failure scenario: Under the rqe frontend, a tree leaf served by an aggregation no text query reads isn't in required_aggregation_ids; alignment starts querying before it has data, so early RQE results are empty and score as misses.
(from automated /code-review; unverified)
There was a problem hiding this comment.
Fixed in 10725d9: alignment also waits on aggregations named in rqe_configs, and works when there are no query configs.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_011gKTCAEz3sJ1Hzde16xxU1
…tion' into 882-featplanner-planner-saved-reads-rqe-trees-and-writes-rqe_configs
A client config not on disk at generation time would otherwise leave its rqe servers' side files unmounted; a config without `servers` still gets no mounts. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_011gKTCAEz3sJ1Hzde16xxU1
get_query_start_times read /api/v1/status/runtimeinfo with its own request; it now goes through fetch_runtime_info, keeping its behaviour of raising (not retrying) on an unreachable server or a bad response. The clients' get_runtime_info methods had no callers and are removed. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_011gKTCAEz3sJ1Hzde16xxU1
- A query with `promql`/`sql` must name its `rqes`: text leaves map to plan RQEs by `rqes`, never by the tree's leaf order. Only a tree-only query may omit them. - `--expr-only` no longer plans query text: it writes only the tree leaves' deployments and `rqe_configs`, and no `query_configs`. - `leaf_item` drops the lookback check `RqeExpr::validate` owns, and shares one metric lookup (`leaf_metric`) with `is_sql`. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_011gKTCAEz3sJ1Hzde16xxU1
An aggregation read only by an RQE leaf was not waited on, so the first RQE queries could run before it had data. Alignment now also waits on the aggregation ids of every inference_config `rqe_configs` entry, and works for an `--expr-only` plan that has no `queries` section. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_011gKTCAEz3sJ1Hzde16xxU1
…s-rqe_configs' into 883-featquery-engine-rqe-tree-query-route
The substring check for planned queries left out of an rqe config missed aggregations shared with kept queries and raised KeyError on entries without `query`. Under --frontend rqe a --prebuilt-output-dir must now be an --expr-only plan: non-empty `rqe_configs` and no per-query configs under `queries`; otherwise a ValueError says to plan with `asap-planner --planner saved --expr-only`. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_011gKTCAEz3sJ1Hzde16xxU1
Under query_engine.frontend=rqe the querier sends the side file's trees, and an --expr-only plan has no per-query configs, so copy_prebuilt_controller_output refused every query. It now checks that every leaf id of every tree in the side file has an `rqe_configs` entry, listing the missing ids otherwise. The promql check is unchanged. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_011gKTCAEz3sJ1Hzde16xxU1
The start command chained its steps with `;`, so a failed compose generation (e.g. a missing rqe side file) still ran `docker compose up`. The steps are now chained with `&&`. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_011gKTCAEz3sJ1Hzde16xxU1
…isabled handling - A leaf whose lookback reads more closed windows than its `rqe_configs` entry retains of the value aggregation is a 400. Windows read are `(lookback - window) / slide + 1`, as the planner sizes retention. - A top-k leaf whose `k` exceeds its CountMinSketchWithHeap's `heapsize` is a 400 (`cms_heap_params` is now shared with the accumulator factory). - `POST /api/v1/rqe_query` answers 500 `internal` when request handling is disabled: it has no fallback, like the instant route without one. - The tree reads one streaming-config snapshot for all its leaves. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_011gKTCAEz3sJ1Hzde16xxU1
…ery-route' into 884-featasap-tools-rqe-query-protocol-and-frontend-switch-for-e2e-runs
…qe-expression-tree-type-and-validation
…on-tree-type-and-validation' into 882-featplanner-planner-saved-reads-rqe-trees-and-writes-rqe_configs # Conflicts: # asap-planner-rs/src/optimizer/milp_output.rs
…ads-rqe-trees-and-writes-rqe_configs' into 883-featquery-engine-rqe-tree-query-route # Conflicts: # asap-query-engine/src/engines/simple_engine/sql.rs # asap-query-engine/src/tests/mod.rs
…ery-route' into 884-featasap-tools-rqe-query-protocol-and-frontend-switch-for-e2e-runs
build_config now returns (config, notes, rqe_exprs); main's random_workload_artifacts.pilot_configs still unpacked two values. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_011gKTCAEz3sJ1Hzde16xxU1
…e-query-protocol-and-frontend-switch-for-e2e-runs
Part of #884 (code; the end-to-end check is still to run) and #886. Stacked on #889 (→ #888 → #887).
rqeprotocol (RqeQueryClient). Configs keep the PromQL/SQL text. A side file maps that text, unrendered, to its RQE tree. The client POSTs{"expr", "time"}to/api/v1/rqe_queryand parses the response exactly like the Prometheus client, socalculate_fidelityis unchanged.config_query(the unrendered text).query_engine.frontend: promql|rqe(defaultpromql) andquery_engine.rqe_exprs_file.rqe,generate_controller_client_configschecks that every query has a tree, copies the side file next to the client configs (rsynced with them), and switches only the sketchdb mode's server toprotocol: rqe.sketch_bench_e2e_configs.pywrites<stem>.rqe_exprs.jsonfrome2e.queries[].expr. With--frontend rqe, it drops queries without a tree (rate) from every mode, so the arms run the same queries, and prints a note listing them.Run:
Tests:
Not in scope: random workloads, which will go through the same writer once S2 (sketch-bench#223) produces their eval tables.
🤖 Generated with Claude Code