Merge pull request #17334 from roidelapluie/roidelapluie/explain

UI: Add smoothed/anchored in explain
This commit is contained in:
Julius Volz 2025-10-27 14:11:36 +01:00 committed by GitHub
commit dca3289c28
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
10 changed files with 255 additions and 5 deletions

View file

@ -84,6 +84,8 @@ func translateAST(node parser.Expr) any {
"matchers": translateMatchers(vs.LabelMatchers),
"timestamp": vs.Timestamp,
"startOrEnd": getStartOrEnd(vs.StartOrEnd),
"anchored": vs.Anchored,
"smoothed": vs.Smoothed,
}
case *parser.SubqueryExpr:
return map[string]any{
@ -124,6 +126,8 @@ func translateAST(node parser.Expr) any {
"matchers": translateMatchers(n.LabelMatchers),
"timestamp": n.Timestamp,
"startOrEnd": getStartOrEnd(n.StartOrEnd),
"anchored": n.Anchored,
"smoothed": n.Smoothed,
}
}
panic("unsupported node type")

View file

@ -171,9 +171,22 @@ const SelectorExplainView: FC<SelectorExplainViewProps> = ({ node }) => {
<Text fz="sm">
{node.type === nodeType.vectorSelector ? (
<>
This node selects the latest (non-stale) sample value within the
last{" "}
<span className="promql-code promql-duration">{lookbackDelta}</span>
This node {node.smoothed ? "smooths the value" : "selects the latest"}{" "}
{node.anchored || node.smoothed ? "" : "non-stale "}
{!node.smoothed && (
<>
sample value within the last{" "}
<span className="promql-code promql-duration">{lookbackDelta}</span>
</>
)}
{node.smoothed && (
<>
using <code>smoothed</code> mode (linear interpolation with nearest
points within{" "}
<span className="promql-code promql-duration">{lookbackDelta}</span>{" "}
before and after execution timestamp, ignoring staleness markers)
</>
)}
</>
) : (
<>
@ -182,6 +195,21 @@ const SelectorExplainView: FC<SelectorExplainViewProps> = ({ node }) => {
{formatPrometheusDuration(node.range)}
</span>{" "}
of data going backward from the evaluation timestamp
{node.anchored && (
<>
{" "}
using <code>anchored</code> mode (includes first sample before or at
start boundary, and last sample of the range at end boundary, ignoring staleness markers)
</>
)}
{node.smoothed && (
<>
{" "}
using <code>smoothed</code> mode (applies linear
interpolation at the boundaries using nearest samples
before and after boundaries, ignoring staleness markers)
</>
)}
</>
)}
{node.timestamp !== null ? (

View file

@ -56,6 +56,8 @@ const Graph: FC<GraphProps> = ({
offset: node.offset,
timestamp: node.timestamp,
startOrEnd: node.startOrEnd,
anchored: node.anchored,
smoothed: node.smoothed,
}
: node
);

View file

@ -94,6 +94,8 @@ const LabelsExplorer: FC<LabelsExplorerProps> = ({
offset: 0,
timestamp: null,
startOrEnd: null,
anchored: false,
smoothed: false,
};
// Based on the selected pool (if any), load the list of targets.

View file

@ -130,12 +130,16 @@ const TreeNode: FC<{
// more cheaply. E.g. 'foo[7w]' can be expensive to fully fetch, but wrapping it
// in 'last_over_time(foo[7w])' is cheaper and also gives us all the info we
// need (number of series and labels).
// Strip anchored/smoothed modifiers when wrapping, as last_over_time doesn't support them.
let queryNode = node;
if (queryNode.type === nodeType.matrixSelector) {
const matrixNode = { ...queryNode };
matrixNode.anchored = false;
matrixNode.smoothed = false;
queryNode = {
type: nodeType.call,
func: functionSignatures["last_over_time"],
args: [node],
args: [matrixNode],
};
}

View file

@ -147,6 +147,8 @@ export interface MatrixSelector {
offset: number;
timestamp: number | null;
startOrEnd: StartOrEnd;
anchored: boolean;
smoothed: boolean;
}
export interface Subquery {
@ -187,6 +189,8 @@ export interface VectorSelector {
offset: number;
timestamp: number | null;
startOrEnd: StartOrEnd;
anchored: boolean;
smoothed: boolean;
}
export interface Placeholder {

View file

@ -131,6 +131,18 @@ const formatSelector = (
]
</>
)}
{node.anchored && (
<>
{" "}
<span className="promql-keyword">anchored</span>
</>
)}
{node.smoothed && (
<>
{" "}
<span className="promql-keyword">smoothed</span>
</>
)}
{formatAtAndOffset(node.timestamp, node.startOrEnd, node.offset)}
</>
);

View file

@ -56,13 +56,18 @@ const serializeSelector = (node: VectorSelector | MatrixSelector): string => {
node.type === nodeType.matrixSelector
? `[${formatPrometheusDuration(node.range)}]`
: "";
const extendedAttribute = node.anchored
? " anchored"
: node.smoothed
? " smoothed"
: "";
const atAndOffset = serializeAtAndOffset(
node.timestamp,
node.startOrEnd,
node.offset
);
return `${!metricExtendedCharset ? metricName : ""}${matchers.length > 0 ? `{${matchers.join(",")}}` : ""}${range}${atAndOffset}`;
return `${!metricExtendedCharset ? metricName : ""}${matchers.length > 0 ? `{${matchers.join(",")}}` : ""}${range}${extendedAttribute}${atAndOffset}`;
};
const serializeNode = (

View file

@ -24,6 +24,8 @@ describe("serializeNode and formatNode", () => {
offset: 0,
timestamp: null,
startOrEnd: null,
anchored: false,
smoothed: false,
},
output: "metric_name",
},
@ -40,6 +42,8 @@ describe("serializeNode and formatNode", () => {
offset: 0,
timestamp: null,
startOrEnd: null,
anchored: false,
smoothed: false,
},
output:
'metric_name{label1="value1",label2!="value2",label3=~"value3",label4!~"value4"}',
@ -52,6 +56,8 @@ describe("serializeNode and formatNode", () => {
offset: 60000,
timestamp: null,
startOrEnd: null,
anchored: false,
smoothed: false,
},
output: "metric_name offset 1m",
},
@ -63,6 +69,8 @@ describe("serializeNode and formatNode", () => {
offset: -60000,
timestamp: null,
startOrEnd: "start",
anchored: false,
smoothed: false,
},
output: "metric_name @ start() offset -1m",
},
@ -74,6 +82,8 @@ describe("serializeNode and formatNode", () => {
offset: -60000,
timestamp: null,
startOrEnd: "end",
anchored: false,
smoothed: false,
},
output: "metric_name @ end() offset -1m",
},
@ -85,6 +95,8 @@ describe("serializeNode and formatNode", () => {
offset: -60000,
timestamp: 123000,
startOrEnd: null,
anchored: false,
smoothed: false,
},
output: "metric_name @ 123.000 offset -1m",
},
@ -98,6 +110,8 @@ describe("serializeNode and formatNode", () => {
offset: 60000,
timestamp: null,
startOrEnd: null,
anchored: false,
smoothed: false,
},
output: "metric_name offset 1m",
},
@ -110,6 +124,8 @@ describe("serializeNode and formatNode", () => {
offset: 0,
timestamp: null,
startOrEnd: null,
anchored: false,
smoothed: false,
},
output: 'metric_name{label1="\\"\\"\\""}',
},
@ -129,6 +145,8 @@ describe("serializeNode and formatNode", () => {
offset: 600000,
timestamp: null,
startOrEnd: null,
anchored: false,
smoothed: false,
},
output:
'metric_name{label1="value1",label2!="value2",label3=~"value3",label4!~"value4"}[5m] offset 10m',
@ -142,6 +160,8 @@ describe("serializeNode and formatNode", () => {
offset: -600000,
timestamp: 123000,
startOrEnd: null,
anchored: false,
smoothed: false,
},
output: "metric_name[5m] @ 123.000 offset -10m",
},
@ -154,6 +174,8 @@ describe("serializeNode and formatNode", () => {
offset: -600000,
timestamp: null,
startOrEnd: "start",
anchored: false,
smoothed: false,
},
output: "metric_name[5m] @ start() offset -10m",
},
@ -167,11 +189,164 @@ describe("serializeNode and formatNode", () => {
offset: 0,
timestamp: null,
startOrEnd: null,
anchored: false,
smoothed: false,
},
output:
'{label1="value1"}',
},
// Anchored and smoothed modifiers.
{
node: {
type: nodeType.vectorSelector,
name: "metric_name",
matchers: [],
offset: 0,
timestamp: null,
startOrEnd: null,
anchored: true,
smoothed: false,
},
output: "metric_name anchored",
},
{
node: {
type: nodeType.vectorSelector,
name: "metric_name",
matchers: [],
offset: 0,
timestamp: null,
startOrEnd: null,
anchored: false,
smoothed: true,
},
output: "metric_name smoothed",
},
{
node: {
type: nodeType.vectorSelector,
name: "metric_name",
matchers: [],
offset: 60000,
timestamp: null,
startOrEnd: null,
anchored: true,
smoothed: false,
},
output: "metric_name anchored offset 1m",
},
{
node: {
type: nodeType.vectorSelector,
name: "metric_name",
matchers: [],
offset: 60000,
timestamp: null,
startOrEnd: null,
anchored: false,
smoothed: true,
},
output: "metric_name smoothed offset 1m",
},
{
node: {
type: nodeType.vectorSelector,
name: "metric_name",
matchers: [],
offset: 0,
timestamp: 123000,
startOrEnd: null,
anchored: true,
smoothed: false,
},
output: "metric_name anchored @ 123.000",
},
{
node: {
type: nodeType.matrixSelector,
name: "metric_name",
matchers: [],
range: 300000,
offset: 0,
timestamp: null,
startOrEnd: null,
anchored: true,
smoothed: false,
},
output: "metric_name[5m] anchored",
},
{
node: {
type: nodeType.matrixSelector,
name: "metric_name",
matchers: [],
range: 300000,
offset: 0,
timestamp: null,
startOrEnd: null,
anchored: false,
smoothed: true,
},
output: "metric_name[5m] smoothed",
},
{
node: {
type: nodeType.matrixSelector,
name: "metric_name",
matchers: [],
range: 300000,
offset: 600000,
timestamp: null,
startOrEnd: null,
anchored: true,
smoothed: false,
},
output: "metric_name[5m] anchored offset 10m",
},
{
node: {
type: nodeType.matrixSelector,
name: "metric_name",
matchers: [],
range: 300000,
offset: 600000,
timestamp: null,
startOrEnd: null,
anchored: false,
smoothed: true,
},
output: "metric_name[5m] smoothed offset 10m",
},
{
node: {
type: nodeType.matrixSelector,
name: "metric_name",
matchers: [],
range: 300000,
offset: 0,
timestamp: 123000,
startOrEnd: null,
anchored: true,
smoothed: false,
},
output: "metric_name[5m] anchored @ 123.000",
},
{
node: {
type: nodeType.matrixSelector,
name: "metric_name",
matchers: [],
range: 300000,
offset: 600000,
timestamp: 123000,
startOrEnd: null,
anchored: false,
smoothed: true,
},
output: "metric_name[5m] smoothed @ 123.000 offset 10m",
},
// Aggregations.
{
node: {
@ -309,6 +484,8 @@ describe("serializeNode and formatNode", () => {
offset: 0,
timestamp: null,
startOrEnd: null,
anchored: false,
smoothed: false,
},
],
},
@ -681,6 +858,8 @@ describe("serializeNode and formatNode", () => {
startOrEnd: null,
timestamp: null,
type: nodeType.vectorSelector,
anchored: false,
smoothed: false,
},
grouping: ["a", "ä"],
op: aggregationType.sum,
@ -714,6 +893,8 @@ describe("serializeNode and formatNode", () => {
startOrEnd: null,
timestamp: null,
type: nodeType.vectorSelector,
anchored: false,
smoothed: false,
},
grouping: ["d", "ä"],
op: aggregationType.sum,
@ -744,6 +925,8 @@ describe("serializeNode and formatNode", () => {
startOrEnd: null,
timestamp: null,
type: nodeType.vectorSelector,
anchored: false,
smoothed: false,
},
type: nodeType.parenExpr,
},

View file

@ -118,6 +118,8 @@ describe("nodeValueType", () => {
offset: 0,
timestamp: null,
startOrEnd: null,
anchored: false,
smoothed: false,
},
rhs: { type: nodeType.numberLiteral, val: "1" },
matching: null,
@ -138,6 +140,8 @@ describe("nodeValueType", () => {
offset: 0,
timestamp: null,
startOrEnd: null,
anchored: false,
smoothed: false,
},
rhs: {
type: nodeType.vectorSelector,
@ -146,6 +150,8 @@ describe("nodeValueType", () => {
offset: 0,
timestamp: null,
startOrEnd: null,
anchored: false,
smoothed: false,
},
matching: null,
bool: false,