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

feat: runtime metrics UI #747

Merged
merged 28 commits into from
May 10, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
28 commits
Select commit Hold shift + click to select a range
c4aed52
feat: runtime metrics
4nalog Apr 19, 2023
e75a795
feat: add mock data
4nalog Apr 20, 2023
0c901df
fix: chart distance spacing breaking
4nalog Apr 20, 2023
a209ccf
chore: use traversal logic to get operation id from nodes
4nalog Apr 26, 2023
f07566d
chore: minor fixes
4nalog Apr 26, 2023
24708f2
fix: phase color
4nalog Apr 26, 2023
a5294ad
chore: threshold on milliseconds
4nalog Apr 27, 2023
b3b241a
chore: add borders
4nalog Apr 27, 2023
0083bb9
feat: runtime metrics
4nalog Apr 28, 2023
fadfbfb
chore: minor fixes
4nalog Apr 28, 2023
b1c95f8
chore: remove tooltip datasets, sanitize tooltip design
4nalog May 1, 2023
8c4342f
Added parsing function for span data
jsonporter May 3, 2023
6437a66
chore: minor fixes
4nalog May 4, 2023
b9d550a
chore: minor fixes
4nalog May 8, 2023
53b0899
chore: minor fixes
4nalog May 8, 2023
fe9faa7
chore: minor fixes
4nalog May 8, 2023
ce5257d
fix: width
4nalog May 8, 2023
4c5375e
chore: remove highlight, fix fonts
4nalog May 10, 2023
648db01
fixed parsing algo to include node operations
jsonporter May 10, 2023
e4f48d3
chore: support ms
4nalog May 10, 2023
4c14b8a
fix: formatting
4nalog May 10, 2023
1f7f465
chore: cleanup
4nalog May 10, 2023
17865a6
Minor fixes
jsonporter May 10, 2023
2b7436e
Fixed formatting issue with displayed duration
jsonporter May 10, 2023
ccdec21
chore: fix yarn.lock
ursucarina May 10, 2023
49f906d
Merge branch 'master' into feat/runtime-metrics
ursucarina May 10, 2023
c04f84a
chore: up package version
ursucarina May 10, 2023
3bc01f9
fix: add space
4nalog May 10, 2023
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
4 changes: 3 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,7 @@
"react-dom": "^16.13.1",
"serve-static": "^1.12.3",
"source-map-loader": "^4.0.1",
"traverse": "^0.6.7",
"ts-jest": "^26.3.0",
"ts-loader": "^9.2.6",
"ts-node": "^8.0.2",
Expand All @@ -126,7 +127,8 @@
"@storybook/builder-webpack5": "^6.4.19",
"@storybook/manager-webpack5": "^6.4.19",
"@storybook/react": "^6.4.19",
"@storybook/testing-library": "^0.0.9"
"@storybook/testing-library": "^0.0.9",
"@types/traverse": "^0.6.32"
},
"resolutions": {
"@babel/cli": "~7.16.0",
Expand Down
2 changes: 1 addition & 1 deletion packages/console/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@flyteorg/console",
"version": "0.0.28",
"version": "0.0.29",
"description": "Flyteconsole main app module",
"main": "./dist/index.js",
"module": "./lib/index.js",
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,10 @@
import React, { createRef, useEffect, useRef, useState } from 'react';
import React, {
createRef,
useContext,
useEffect,
useRef,
useState,
} from 'react';
import { makeStyles, Typography } from '@material-ui/core';
import { tableHeaderColor } from 'components/Theme/constants';
import { timestampToDate } from 'common/utils';
Expand All @@ -10,13 +16,19 @@ import {
import { useQueryClient } from 'react-query';
import { eq, merge } from 'lodash';
import { useNodeExecutionsById } from 'components/Executions/contextProvider/NodeExecutionDetails';
import { ExecutionContext } from 'components/Executions/contexts';
import { useExecutionMetrics } from 'components/Executions/useExecutionMetrics';
import { convertToPlainNodes } from './helpers';
import { ChartHeader } from './ChartHeader';
import { useScaleContext } from './scaleContext';
import { TaskNames } from './TaskNames';
import { getChartDurationData } from './TimelineChart/chartData';
import { TimelineChart } from './TimelineChart';
import t from '../strings';
import {
getExecutionMetricsOperationIds,
parseSpanData,
} from './TimelineChart/utils';

interface StyleProps {
chartWidth: number;
Expand Down Expand Up @@ -91,6 +103,8 @@ export const ExecutionTimeline: React.FC<ExProps> = ({
const { nodeExecutionsById, setCurrentNodeExecutionsById } =
useNodeExecutionsById();
const { chartInterval: chartTimeInterval } = useScaleContext();
const { execution } = useContext(ExecutionContext);
const executionMetricsData = useExecutionMetrics(execution.id, 10);

useEffect(() => {
setOriginalNodes(ogn => {
Expand Down Expand Up @@ -178,6 +192,12 @@ export const ExecutionTimeline: React.FC<ExProps> = ({
setOriginalNodes([...originalNodes]);
};

const operationIds = getExecutionMetricsOperationIds(
executionMetricsData.value,
);

const parsedExecutionMetricsData = parseSpanData(executionMetricsData.value);

return (
<>
<div className={styles.taskNames}>
Expand Down Expand Up @@ -213,6 +233,9 @@ export const ExecutionTimeline: React.FC<ExProps> = ({
<TimelineChart
items={barItemsData}
chartTimeIntervalSec={chartTimeInterval}
operationIds={operationIds}
parsedExecutionMetricsData={parsedExecutionMetricsData}
nodes={showNodes}
/>
</div>
</div>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { Chart as ChartJS, registerables, Tooltip } from 'chart.js';
import ChartDataLabels from 'chartjs-plugin-datalabels';
import { isEqual, isNil } from 'lodash';

ChartJS.register(...registerables, ChartDataLabels);

Expand All @@ -11,6 +12,9 @@ Tooltip.positioners.cursor = function (_chartElements, coordinates) {
export const getBarOptions = (
chartTimeIntervalSec: number,
tooltipLabels: string[][],
chartRef: React.MutableRefObject<any>,
tooltip: any,
setTooltip: any,
) => {
return {
animation: false as const,
Expand All @@ -31,17 +35,56 @@ export const getBarOptions = (
},
tooltip: {
// Setting up tooltip: https://www.chartjs.org/docs/latest/configuration/tooltip.html
enabled: false,
position: 'cursor',
filter: function (tooltipItem) {
// no tooltip for offsets
return tooltipItem.datasetIndex === 1;
return tooltipItem.datasetIndex !== 0;
},
callbacks: {
label: function (context) {
const index = context.dataIndex;
return tooltipLabels[index] ?? '';

return tooltipLabels ? [`${tooltipLabels[index]}`] : '';
},
labelColor: function () {
return {
fontColor: 'white',
};
},
},
external: context => {
const tooltipModel = context.tooltip;

if (!chartRef || !chartRef.current) {
return;
}

if (tooltipModel.opacity === 0) {
if (tooltip.opacity !== 0)
setTooltip(prev => ({ ...prev, opacity: 0 }));
return;
}

const position = context.chart.canvas.getBoundingClientRect();

const dataIndex = tooltipModel.dataPoints[0]?.dataIndex;

if (isNil(dataIndex)) {
return;
}

const newTooltipData = {
opacity: 1,
left: position.left + tooltipModel.caretX,
top: position.top + tooltipModel.caretY,
dataIndex: dataIndex,
};

if (!isEqual(tooltip, newTooltipData)) {
setTooltip(newTooltipData);
}
},
},
},
scales: {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,22 +1,117 @@
import * as React from 'react';
import { Bar } from 'react-chartjs-2';
import { dNode } from 'models/Graph/types';
import { Box, Theme, makeStyles } from '@material-ui/core';

import { NodeExecutionPhase } from 'models';
import { getNodeExecutionPhaseConstants } from 'components/Executions/utils';
import {
BarItemData,
formatSecondsToHmsFormat,
generateChartData,
getChartData,
getDuration,
parseSpanData,
} from './utils';
import { getBarOptions } from './barOptions';
import { BarItemData, generateChartData, getChartData } from './utils';

interface TimelineChartProps {
items: BarItemData[];
nodes: dNode[];
chartTimeIntervalSec: number;
operationIds: string[];
parsedExecutionMetricsData: ReturnType<typeof parseSpanData>;
}

interface StyleProps {
opacity: number;
top: number;
left: number;
phaseColor: string;
}

const useStyles = makeStyles<Theme, StyleProps>(theme => ({
tooltipContainer: {
position: 'absolute',
background: theme.palette.grey[100],
color: theme.palette.common.black,
padding: theme.spacing(2),
borderRadius: 8,
width: 'fit-content',
maxContent: 'fit-content',
top: ({ top }) => top + 10,
left: ({ left }) => left + 10,
display: ({ opacity }) => (opacity ? 'block' : 'none'),
},
phaseText: {
width: 'fit-content',
marginBlockEnd: theme.spacing(1),
},
tooltipText: {
minWidth: '50px',
},
tooltipTextContainer: {
display: 'flex',
gap: 1,
color: theme.palette.grey[700],
},
operationIdContainer: {
textAlign: 'left',
flex: 1,
},
}));

export const TimelineChart = (props: TimelineChartProps) => {
const [tooltip, setTooltip] = React.useState({
opacity: 0,
top: 0,
left: 0,
dataIndex: -1,
});
const chartRef = React.useRef<any>(null);
const phaseData = generateChartData(props.items);

const options = getBarOptions(
props.chartTimeIntervalSec,
phaseData.tooltipLabel,
chartRef,
tooltip,
setTooltip,
) as any;

const data = getChartData(phaseData);
const node = props.nodes[tooltip.dataIndex];
const phase = node?.execution?.closure.phase ?? NodeExecutionPhase.UNDEFINED;
const phaseConstant = getNodeExecutionPhaseConstants(phase);
const spans = (node && props.parsedExecutionMetricsData[node.scopedId]) || [];

const styles = useStyles({
opacity: tooltip.opacity,
top: tooltip.top,
left: tooltip.left,
phaseColor: phaseConstant.badgeColor,
});

return (
<Bar
options={
getBarOptions(props.chartTimeIntervalSec, phaseData.tooltipLabel) as any
}
data={getChartData(phaseData)}
/>
<>
<Bar options={options} data={data} ref={chartRef} />
<Box className={styles.tooltipContainer}>
{phase && <Box className={styles.phaseText}>{phaseConstant.text}</Box>}
{spans?.map(span => (
<Box className={styles.tooltipTextContainer}>
<Box className={styles.tooltipText}>
{formatSecondsToHmsFormat(
Math.round(
(getDuration(span.startTime, span.endTime) / 1000) * 100,
) / 100,
)}
</Box>
<Box className={styles.operationIdContainer}>
{span.operationId}
</Box>
</Box>
))}
</Box>
</>
);
};
Loading
Loading