Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 18 additions & 0 deletions asap-query-engine/src/engines/query_result.rs
Original file line number Diff line number Diff line change
Expand Up @@ -181,6 +181,16 @@ pub struct RangeVector {
pub struct RangeVectorElement {
pub labels: KeyByLabelValues,
pub samples: Vec<Sample>,
/// Optional per-element label-key override. When `Some`, the
/// HTTP serializer uses these keys for the PromQL response's
/// `"metric"` object instead of the query-scoped
/// `KeyByLabelNames` argument. Used by warm-tier `topk` (whose
/// reducer synthesizes an `"item"` key not present in the
/// query's group-by clause) and other adapters that materialize
/// labels the caller doesn't know about. `None` for everyone
/// else — the existing serializer path is unaffected.
#[serde(default, skip_serializing_if = "Option::is_none")]
pub label_keys_override: Option<Vec<String>>,
}

/// A single sample (timestamp, value) pair
Expand All @@ -201,9 +211,17 @@ impl RangeVectorElement {
Self {
labels,
samples: Vec::new(),
label_keys_override: None,
}
}

/// Attach a per-element label-key override (see field doc on
/// `RangeVectorElement::label_keys_override`).
pub fn with_label_keys_override(mut self, keys: Vec<String>) -> Self {
self.label_keys_override = Some(keys);
self
}

pub fn add_sample(&mut self, timestamp: u64, value: f64) {
self.samples.push(Sample::new(timestamp, value));
}
Expand Down
25 changes: 15 additions & 10 deletions asap-query-engine/src/engines/simple/engine.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3585,16 +3585,21 @@ fn warm_tier_result_to_query_result(

let mut elements: Vec<RangeVectorElement> = Vec::with_capacity(result.series.len());
for (label_values, samples) in result.series {
// `KeyByLabelValues` is a `Vec<String>` carrying VALUES only.
// We project the BTreeMap's values in key-sorted order
// (BTreeMap iteration order matches the `group_by_keys`
// BTreeSet iteration order, so the result preserves the
// sketch instance's group-by-key projection without
// re-emitting the keys).
let labels = KeyByLabelValues::new_with_labels(
label_values.into_values().collect::<Vec<_>>(),
);
let mut element = RangeVectorElement::new(labels);
// `KeyByLabelValues` is a `Vec<String>` carrying VALUES only;
// the serializer pairs them with KEYS from a query-scoped
// `KeyByLabelNames`. For most queries the keys ARE the
// query's group-by clause, so the default path works. But
// warm-tier `topk` synthesizes an `"item"` key (the top-k
// entry name) that the original query's group-by doesn't
// carry — without an override the serializer drops it and
// the response shows `"metric": {}`. Project the BTreeMap's
// VALUES in key-sorted order (BTreeMap iteration is
// key-sorted), and stash the BTreeMap's KEYS in the
// per-element override so the serializer can pair them
// correctly.
let (keys, values): (Vec<String>, Vec<String>) = label_values.into_iter().unzip();
let labels = KeyByLabelValues::new_with_labels(values);
let mut element = RangeVectorElement::new(labels).with_label_keys_override(keys);
for (window_end_ms, value) in samples {
// `window_end_ms` is i64 from the index; cast to u64
// for the wire format (window_end is monotonic + post-
Expand Down
14 changes: 12 additions & 2 deletions asap-query-engine/src/utils/http.rs
Original file line number Diff line number Diff line change
Expand Up @@ -175,9 +175,19 @@ pub fn convert_range_result_to_prometheus(
.values
.iter()
.map(|element| {
// Build metric labels object
// Build metric labels object. Per-element override
// (`element.label_keys_override`) wins when the
// adapter knows the keys at materialization time —
// e.g. warm-tier `topk` synthesizes an `"item"` key
// that's not in the query's group-by clause, so the
// outer `label_names` doesn't carry it. Falls back
// to the query-scoped key list for everyone else.
let mut metric = serde_json::Map::new();
for (i, label_name) in label_names.labels.iter().enumerate() {
let effective_keys: &[String] = element
.label_keys_override
.as_deref()
.unwrap_or(&label_names.labels);
for (i, label_name) in effective_keys.iter().enumerate() {
if i < element.labels.labels.len() {
metric.insert(
label_name.clone(),
Expand Down