Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Patterns: Use runtime datasource for queries #672

Merged
Merged
Show file tree
Hide file tree
Changes from 14 commits
Commits
Show all changes
41 commits
Select commit Hold shift + click to select a range
9e7aa16
chore: refactor to allow multiple queries
gtk-grafana Aug 6, 2024
d55c9ac
chore: wip - major functionality implemented
gtk-grafana Aug 6, 2024
80879ca
Merge remote-tracking branch 'origin/main' into gtk-grafana/issues/67…
gtk-grafana Aug 7, 2024
bc010ee
chore: refactor patterns
gtk-grafana Aug 7, 2024
b786d67
chore: clean up
gtk-grafana Aug 7, 2024
468361b
chore: clean up
gtk-grafana Aug 7, 2024
af3673f
Merge remote-tracking branch 'origin/main' into gtk-grafana/issues/67…
gtk-grafana Aug 7, 2024
6bda2fd
test: fix test
gtk-grafana Aug 7, 2024
a34dc57
fix: refactor and fix buggy re-rendering in logs panels
gtk-grafana Aug 8, 2024
d3f1486
fix: fix data node not being set from scene cache on route/activation
gtk-grafana Aug 8, 2024
1c2e57b
fix: removing multiple queries per query runner, adding multiple quer…
gtk-grafana Aug 9, 2024
c3a6182
chore: remove console.log
gtk-grafana Aug 9, 2024
365a4ef
chore: docs
gtk-grafana Aug 9, 2024
982d76c
chore: why are the videos gone in failed playwright reports?
gtk-grafana Aug 9, 2024
1ef913a
chore: clean up
gtk-grafana Aug 9, 2024
7bcf816
chore: clean up
gtk-grafana Aug 9, 2024
1f4d517
fix: fix table loading state
gtk-grafana Aug 9, 2024
bf8c10a
chore: clean up
gtk-grafana Aug 9, 2024
9a9a3e0
chore: clean up
gtk-grafana Aug 9, 2024
6a8241a
chore: clean up def
gtk-grafana Aug 9, 2024
e8bbf8c
chore: clean up datasource
gtk-grafana Aug 9, 2024
d2907f0
chore: clean up
gtk-grafana Aug 9, 2024
bc33f3b
Merge remote-tracking branch 'origin/main' into gtk-grafana/issues/67…
gtk-grafana Aug 9, 2024
8e5314c
chore: add loading state
gtk-grafana Aug 9, 2024
a851425
chore: add the dang video
gtk-grafana Aug 9, 2024
41f90e8
chore: remove e2e video in ci
gtk-grafana Aug 12, 2024
d49c8c2
Merge branch 'main' into gtk-grafana/issues/670/patterns-runtime-reso…
gtk-grafana Aug 12, 2024
c4d63aa
fix: dont run when query is already running, or we have cached state
gtk-grafana Aug 12, 2024
9733c28
chore: clean up extra rendering
gtk-grafana Aug 13, 2024
9a5f2ca
chore: remove console.warn
gtk-grafana Aug 13, 2024
4447791
chore: refactor, clean up, remove getPatternsFrames
gtk-grafana Aug 13, 2024
3d56793
chore: rename LogsActionBarScene to ActionBarScene
gtk-grafana Aug 13, 2024
b6cfe4d
chore: refactor counter ternary
gtk-grafana Aug 13, 2024
e9cd5d7
chore: remove nested if
gtk-grafana Aug 13, 2024
080bf3d
chore: clean up
gtk-grafana Aug 13, 2024
5e5101c
chore: partition queries by resource
gtk-grafana Aug 13, 2024
3101bf8
chore: add assertion that queries do not have multiple targets
gtk-grafana Aug 13, 2024
db0b7a7
chore: refactor
gtk-grafana Aug 13, 2024
96f1daf
chore: no need to iterate
gtk-grafana Aug 13, 2024
24a6060
chore: move patterns files
gtk-grafana Aug 13, 2024
4508fc2
chore: update types, prevent runtime error
gtk-grafana Aug 13, 2024
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
6 changes: 3 additions & 3 deletions playwright.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,9 +34,9 @@ export default defineConfig<PluginOptions>({
trace: 'on-first-retry',

// Turn on when debugging local tests
// video: {
// mode: 'on',
// }
video: {
gtk-grafana marked this conversation as resolved.
Show resolved Hide resolved
mode: 'on-first-retry',
}
},
expect: { timeout: 15000 },

Expand Down
1 change: 1 addition & 0 deletions project-words.txt
Original file line number Diff line number Diff line change
Expand Up @@ -469,4 +469,5 @@ subqueries
dbscan
svgs
Dont
REFID
unroute
4 changes: 3 additions & 1 deletion src/Components/IndexScene/IndexScene.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -161,7 +161,9 @@ function getContentScene(drillDownLabel?: string) {
return new ServiceSelectionScene({});
}

return new ServiceScene({ drillDownLabel });
return new ServiceScene({
drillDownLabel,
});
}

function getVariableSet(initialDatasourceUid: string, initialFilters?: AdHocVariableFilter[]) {
Expand Down
65 changes: 65 additions & 0 deletions src/Components/ServiceScene/BreakdownViews.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
import { PageSlugs, ValueSlugs } from '../../services/routing';
import { buildLogsListScene } from './LogsListScene';
import { testIds } from '../../services/testIds';
import { buildLabelBreakdownActionScene, buildLabelValuesBreakdownActionScene } from './Breakdowns/LabelBreakdownScene';
import {
buildFieldsBreakdownActionScene,
buildFieldValuesBreakdownActionScene,
} from './Breakdowns/FieldsBreakdownScene';
import { buildPatternsScene } from './Breakdowns/Patterns/PatternsBreakdownScene';
import { SceneObject } from '@grafana/scenes';

interface ValueBreakdownViewDefinition {
displayName: string;
value: ValueSlugs;
testId: string;
getScene: (value: string) => SceneObject;
}

export interface BreakdownViewDefinition {
displayName: string;
value: PageSlugs;
testId: string;
getScene: (changeFields: (f: string[]) => void) => SceneObject;
}

export const breakdownViewsDefinitions: BreakdownViewDefinition[] = [
{
displayName: 'Logs',
value: PageSlugs.logs,
getScene: () => buildLogsListScene(),
testId: testIds.exploreServiceDetails.tabLogs,
},
{
displayName: 'Labels',
value: PageSlugs.labels,
getScene: () => buildLabelBreakdownActionScene(),
testId: testIds.exploreServiceDetails.tabLabels,
},
{
displayName: 'Fields',
value: PageSlugs.fields,
getScene: (f) => buildFieldsBreakdownActionScene(f),
testId: testIds.exploreServiceDetails.tabFields,
},
{
displayName: 'Patterns',
value: PageSlugs.patterns,
getScene: () => buildPatternsScene(),
testId: testIds.exploreServiceDetails.tabPatterns,
},
];
export const valueBreakdownViews: ValueBreakdownViewDefinition[] = [
{
displayName: 'Label',
value: ValueSlugs.label,
getScene: (value: string) => buildLabelValuesBreakdownActionScene(value),
testId: testIds.exploreServiceDetails.tabLabels,
},
{
displayName: 'Field',
value: ValueSlugs.field,
getScene: (value: string) => buildFieldValuesBreakdownActionScene(value),
testId: testIds.exploreServiceDetails.tabFields,
},
];
Original file line number Diff line number Diff line change
Expand Up @@ -287,7 +287,7 @@ export class FieldsBreakdownScene extends SceneObjectBase<FieldsBreakdownSceneSt
legendFormat: `{{${optionValue}}}`,
refId: optionValue,
});
const queryRunner = getQueryRunner(query);
const queryRunner = getQueryRunner([query]);
let body = PanelBuilders.timeseries().setTitle(optionValue).setData(queryRunner);

if (!isAvgField(optionValue)) {
Expand Down Expand Up @@ -348,7 +348,7 @@ export class FieldsBreakdownScene extends SceneObjectBase<FieldsBreakdownSceneSt
const getFilter = () => this.state.search.state.filter ?? '';

return new LayoutSwitcher({
$data: getQueryRunner(query),
$data: getQueryRunner([query]),
options: [
{ value: 'single', label: 'Single' },
{ value: 'grid', label: 'Grid' },
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -218,9 +218,9 @@ export class LabelBreakdownScene extends SceneObjectBase<LabelBreakdownSceneStat
body: PanelBuilders.timeseries()
.setTitle(optionValue)
.setData(
getQueryRunner(
buildDataQuery(getTimeSeriesExpr(this, optionValue), { legendFormat: `{{${optionValue}}}` })
)
getQueryRunner([
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We don't need multiple queries anymore, and I would highly suggest we don't mix queries that call different API endpoints, but if we wanted to include multiple queries that call the same API endpoint this should work.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

At first glance, I like this ability of running more than one query. What was the original purpose of this change?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Initially I tried to smoosh all the queries into $data, which was a bad idea as queries would end at different times, but if we have multiple data queries, that should work fine as they are all returned from the backend at the same time

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it makes sense for "combined panels", i.e. the ones at the service selection scene, where we do one logs and one metrics query for each service 👍

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good point, that's probably worth opening in another issue. Might be good for the lazy-loaded panels as well to at least batch things up a bit.

buildDataQuery(getTimeSeriesExpr(this, optionValue), { legendFormat: `{{${optionValue}}}` }),
])
)
.setHeaderActions(new SelectLabelAction({ labelName: optionValue }))
.setCustomFieldConfig('stacking', { mode: StackingMode.Normal })
Expand Down Expand Up @@ -276,7 +276,7 @@ export class LabelBreakdownScene extends SceneObjectBase<LabelBreakdownSceneStat
const getFilter = () => this.state.search.state.filter ?? '';

return new LayoutSwitcher({
$data: getQueryRunner(query),
$data: getQueryRunner([query]),
options: [
{ value: 'single', label: 'Single' },
{ value: 'grid', label: 'Grid' },
Expand Down
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
import { css } from '@emotion/css';
import React from 'react';

import { DataFrame, dateTime, FieldType, GrafanaTheme2 } from '@grafana/data';
import { DataFrame, dateTime, GrafanaTheme2 } from '@grafana/data';
import {
CustomVariable,
SceneComponentProps,
SceneDataState,
SceneFlexItem,
SceneFlexLayout,
sceneGraph,
Expand All @@ -15,7 +16,7 @@ import {
import { Text, useStyles2 } from '@grafana/ui';
import { StatusWrapper } from 'Components/ServiceScene/Breakdowns/StatusWrapper';
import { VAR_LABEL_GROUP_BY } from 'services/variables';
import { LokiPattern, ServiceScene } from '../../ServiceScene';
import { getPatternsFrames, ServiceScene } from '../../ServiceScene';
import { IndexScene } from '../../../IndexScene/IndexScene';
import { PatternsFrameScene } from './PatternsFrameScene';
import { PatternsViewTextSearch } from './PatternsViewTextSearch';
Expand Down Expand Up @@ -53,8 +54,8 @@ export class PatternsBreakdownScene extends SceneObjectBase<PatternsBreakdownSce
new SceneVariableSet({
variables: [new CustomVariable({ name: VAR_LABEL_GROUP_BY, defaultToAll: true, includeAll: true })],
}),
patternFilter: '',
loading: true,
patternFilter: '',
...state,
});

Expand All @@ -66,7 +67,8 @@ export class PatternsBreakdownScene extends SceneObjectBase<PatternsBreakdownSce
const { body, loading, blockingMessage } = model.useState();
const { value: timeRange } = sceneGraph.getTimeRange(model).useState();
const logsByServiceScene = sceneGraph.getAncestor(model, ServiceScene);
const { patterns } = logsByServiceScene.useState();
const { $patternsData } = logsByServiceScene.useState();
const patterns = getPatternsFrames($patternsData?.state.data);
gtk-grafana marked this conversation as resolved.
Show resolved Hide resolved
const styles = useStyles2(getStyles);
const timeRangeTooOld = dateTime().diff(timeRange.to, 'hours') >= PATTERNS_MAX_AGE_HOURS;

Expand Down Expand Up @@ -101,21 +103,25 @@ export class PatternsBreakdownScene extends SceneObjectBase<PatternsBreakdownSce
const serviceScene = sceneGraph.getAncestor(this, ServiceScene);
this.setBody();

const patterns = getPatternsFrames(serviceScene.state.$patternsData?.state.data);

// If the patterns exist already, update the dataframe
if (serviceScene.state.patterns) {
this.updatePatternFrames(serviceScene.state.patterns);
if (patterns) {
this.updatePatternFrames(patterns);
}

// Subscribe to changes from pattern API call
this._subs.add(
serviceScene.subscribeToState((newState, prevState) => {
if (!areArraysEqual(newState.patterns, prevState.patterns)) {
this.updatePatternFrames(newState.patterns);
}
})
);
this._subs.add(serviceScene.state.$patternsData.subscribeToState(this.onDataProviderChange));
}

private onDataProviderChange = (newState: SceneDataState, prevState: SceneDataState) => {
gtk-grafana marked this conversation as resolved.
Show resolved Hide resolved
const newFrame = getPatternsFrames(newState.data);
const prevFrame = getPatternsFrames(prevState.data);
if (!areArraysEqual(newFrame, prevFrame) || this.state.loading) {
this.updatePatternFrames(newFrame);
}
};

private setBody() {
this.setState({
body: new SceneFlexLayout({
Expand All @@ -133,74 +139,37 @@ export class PatternsBreakdownScene extends SceneObjectBase<PatternsBreakdownSce
});
}

private updatePatternFrames(lokiPatterns?: LokiPattern[]) {
private updatePatternFrames(lokiPatterns?: DataFrame[]) {
if (!lokiPatterns) {
console.warn('failed to update pattern frames');
gtk-grafana marked this conversation as resolved.
Show resolved Hide resolved
return;
}

const patternFrames = this.buildPatterns(lokiPatterns);

this.setState({
patternFrames,
loading: false,
});
}

private buildPatterns(patterns: LokiPattern[]): PatternFrame[] {
private buildPatterns(patterns: DataFrame[]): PatternFrame[] {
gtk-grafana marked this conversation as resolved.
Show resolved Hide resolved
const serviceScene = sceneGraph.getAncestor(this, ServiceScene);
const appliedPatterns = sceneGraph.getAncestor(serviceScene, IndexScene).state.patterns;

let maxValue = -Infinity;
let minValue = 0;

return patterns
.map((pat) => {
const timeValues: number[] = [];
const sampleValues: number[] = [];
let sum = 0;
pat.samples.forEach(([time, value]) => {
timeValues.push(time * 1000);
const sample = parseFloat(value);
sampleValues.push(sample);
if (sample > maxValue) {
maxValue = sample;
}
if (sample < minValue) {
minValue = sample;
}
sum += sample;
});
const dataFrame: DataFrame = {
refId: pat.pattern,
fields: [
{
name: 'time',
type: FieldType.time,
values: timeValues,
config: {},
},
{
name: pat.pattern,
type: FieldType.number,
values: sampleValues,
config: {},
},
],
length: pat.samples.length,
meta: {
preferredVisualisationType: 'graph',
},
};
const existingPattern = appliedPatterns?.find((appliedPattern) => appliedPattern.pattern === pat.pattern);

return {
dataFrame,
pattern: pat.pattern,
sum,
status: existingPattern?.type,
};
})
.sort((a, b) => b.sum - a.sum);
return patterns.map((dataFrame) => {
const existingPattern = appliedPatterns?.find((appliedPattern) => appliedPattern.pattern === dataFrame.name);

const sum: number = dataFrame.meta?.custom?.sum;
const patternFrame: PatternFrame = {
dataFrame,
pattern: dataFrame.name ?? '',
sum,
status: existingPattern?.type,
};

return patternFrame;
});
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ import {
VizPanel,
} from '@grafana/scenes';
import { LegendDisplayMode, PanelContext, SeriesVisibilityChangeMode } from '@grafana/ui';
import { ServiceScene } from '../../ServiceScene';
import { getPatternsFrames, ServiceScene } from '../../ServiceScene';
import { onPatternClick } from './FilterByPatternsButton';
import { IndexScene } from '../../../IndexScene/IndexScene';
import { PatternsViewTableScene } from './PatternsViewTableScene';
Expand Down Expand Up @@ -44,7 +44,9 @@ export class PatternsFrameScene extends SceneObjectBase<PatternsFrameSceneState>
public static Component = ({ model }: SceneComponentProps<PatternsFrameScene>) => {
const { body, loading } = model.useState();
const logsByServiceScene = sceneGraph.getAncestor(model, ServiceScene);
const { patterns } = logsByServiceScene.useState();
const { $patternsData } = logsByServiceScene.useState();
const patterns = getPatternsFrames($patternsData?.state.data);

return (
<div className={styles.container}>
{!loading && patterns && patterns.length > 0 && <>{body && <body.Component model={body} />}</>}
Expand All @@ -58,7 +60,10 @@ export class PatternsFrameScene extends SceneObjectBase<PatternsFrameSceneState>
// If the patterns have changed, recalculate the dataframes
this._subs.add(
sceneGraph.getAncestor(this, ServiceScene).subscribeToState((newState, prevState) => {
if (!areArraysEqual(newState.patterns, prevState.patterns)) {
const newFrame = getPatternsFrames(newState?.$patternsData?.state?.data);
const prevFrame = getPatternsFrames(prevState?.$patternsData?.state?.data);

if (!areArraysEqual(newFrame, prevFrame)) {
const patternsBreakdownScene = sceneGraph.getAncestor(this, PatternsBreakdownScene);
this.updatePatterns(patternsBreakdownScene.state.patternFrames);

Expand Down Expand Up @@ -107,7 +112,8 @@ export class PatternsFrameScene extends SceneObjectBase<PatternsFrameSceneState>
const patternFrames = patternsBreakdownScene.state.patternFrames;

const serviceScene = sceneGraph.getAncestor(this, ServiceScene);
const lokiPatterns = serviceScene.state.patterns;

const lokiPatterns = getPatternsFrames(serviceScene.state.$patternsData?.state.data);
if (!lokiPatterns || !patternFrames) {
console.warn('Failed to update PatternsFrameScene body');
return;
Expand Down
gtk-grafana marked this conversation as resolved.
Show resolved Hide resolved
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ export class PatternsLogsSampleScene extends SceneObjectBase<PatternsLogsSampleS
this.replacePatternsInQuery(queryWithFilters);

// but if that fails to return results, we fire the query without the filters, instead of showing no-data in the viz
const queryRunnerWithFilters = getQueryRunner(queryWithFilters);
const queryRunnerWithFilters = getQueryRunner([queryWithFilters]);
queryRunnerWithFilters.getResultsStream().subscribe((value) => {
this.onQueryWithFiltersResult(value);
});
Expand Down Expand Up @@ -198,7 +198,7 @@ export class PatternsLogsSampleScene extends SceneObjectBase<PatternsLogsSampleS
const queryWithoutFilters = buildDataQuery(PATTERNS_SAMPLE_SELECTOR_EXPR);
this.replacePatternsInQuery(queryWithoutFilters);

const queryRunnerWithoutFilters = getQueryRunner(queryWithoutFilters);
const queryRunnerWithoutFilters = getQueryRunner([queryWithoutFilters]);

// Subscribe to the secondary query, so we can log errors and update the UI
queryRunnerWithoutFilters.getResultsStream().subscribe(this.onQueryError);
Expand Down
Loading
Loading