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

[Lens] Add lens editor performance metrics #163089

Merged
merged 14 commits into from
Aug 7, 2023
Original file line number Diff line number Diff line change
Expand Up @@ -246,7 +246,10 @@ export const GaugeComponent: FC<GaugeRenderProps> = memo(
const onRenderChange = useCallback(
(isRendered: boolean = true) => {
if (isRendered) {
renderComplete();
// this requestAnimationFrame call is a temporary fix for https://github.com/elastic/elastic-charts/issues/2124
window.requestAnimationFrame(() => {
renderComplete();
});
}
},
[renderComplete]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -196,7 +196,10 @@ export const HeatmapComponent: FC<HeatmapRenderProps> = memo(
const onRenderChange = useCallback(
(isRendered: boolean = true) => {
if (isRendered) {
renderComplete();
// this requestAnimationFrame call is a temporary fix for https://github.com/elastic/elastic-charts/issues/2124
window.requestAnimationFrame(() => {
renderComplete();
});
}
},
[renderComplete]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -134,7 +134,10 @@ export const MetricVis = ({
const onRenderChange = useCallback<RenderChangeListener>(
(isRendered) => {
if (isRendered) {
renderComplete();
// this requestAnimationFrame call is a temporary fix for https://github.com/elastic/elastic-charts/issues/2124
window.requestAnimationFrame(() => {
renderComplete();
});
}
},
[renderComplete]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -176,8 +176,11 @@ const PartitionVisComponent = (props: PartitionVisComponentProps) => {
const onRenderChange = useCallback(
(isRendered: boolean = true) => {
if (isRendered) {
props.renderComplete();
setChartIsLoaded(true);
// this requestAnimationFrame call is a temporary fix for https://github.com/elastic/elastic-charts/issues/2124
window.requestAnimationFrame(() => {
props.renderComplete();
setChartIsLoaded(true);
});
}
},
[props]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -145,7 +145,10 @@ export const TagCloudChart = ({
const onRenderChange = useCallback<RenderChangeListener>(
(isRendered) => {
if (isRendered) {
renderComplete();
// this requestAnimationFrame call is a temporary fix for https://github.com/elastic/elastic-charts/issues/2124
window.requestAnimationFrame(() => {
renderComplete();
});
}
},
[renderComplete]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -309,7 +309,10 @@ export function XYChart({
const onRenderChange = useCallback(
(isRendered: boolean = true) => {
if (isRendered) {
renderComplete();
// this requestAnimationFrame call is a temporary fix for https://github.com/elastic/elastic-charts/issues/2124
window.requestAnimationFrame(() => {
renderComplete();
});
}
},
[renderComplete]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -100,3 +100,7 @@
align-items: center;
justify-content: center;
}

.lnsSuggestionPanel__loadingState {
height: $lnsSuggestionHeight;
}
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import {
EuiButtonEmpty,
EuiAccordion,
EuiText,
EuiProgress,
} from '@elastic/eui';
import { IconType } from '@elastic/eui/src/components/icon/icon';
import { Ast, fromExpression, toExpression } from '@kbn/interpreter';
Expand All @@ -30,6 +31,7 @@ import {
ReactExpressionRendererProps,
ReactExpressionRendererType,
} from '@kbn/expressions-plugin/public';
import { css } from '@emotion/react';
import { DONT_CLOSE_DIMENSION_CONTAINER_ON_CLICK_CLASS } from '../../utils';
import {
Datasource,
Expand Down Expand Up @@ -64,7 +66,6 @@ import {
selectFrameDatasourceAPI,
} from '../../state_management';
import { filterAndSortUserMessages } from '../../app_plugin/get_application_user_messages';

const MAX_SUGGESTIONS_DISPLAYED = 5;
const LOCAL_STORAGE_SUGGESTIONS_PANEL = 'LENS_SUGGESTIONS_PANEL_HIDDEN';

Expand Down Expand Up @@ -108,11 +109,13 @@ const PreviewRenderer = ({
ExpressionRendererComponent,
expression,
hasError,
onRender,
}: {
withLabel: boolean;
expression: string | null | undefined;
ExpressionRendererComponent: ReactExpressionRendererType;
hasError: boolean;
onRender: () => void;
}) => {
const onErrorMessage = (
<div className="lnsSuggestionPanel__suggestionIcon">
Expand Down Expand Up @@ -143,6 +146,7 @@ const PreviewRenderer = ({
padding="s"
renderMode="preview"
expression={expression}
onRender$={onRender}
debounce={2000}
renderError={() => {
return onErrorMessage;
Expand All @@ -159,6 +163,7 @@ const SuggestionPreview = ({
selected,
onSelect,
showTitleAsLabel,
onRender,
}: {
onSelect: () => void;
preview: {
Expand All @@ -170,6 +175,7 @@ const SuggestionPreview = ({
ExpressionRenderer: ReactExpressionRendererType;
selected: boolean;
showTitleAsLabel?: boolean;
onRender: () => void;
}) => {
return (
<EuiToolTip content={preview.title}>
Expand All @@ -194,6 +200,7 @@ const SuggestionPreview = ({
expression={preview.expression && toExpression(preview.expression)}
withLabel={Boolean(showTitleAsLabel)}
hasError={Boolean(preview.error)}
onRender={onRender}
/>
) : (
<span className="lnsSuggestionPanel__suggestionIcon">
Expand Down Expand Up @@ -358,20 +365,34 @@ export function SuggestionPanel({
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [existsStagedPreview]);

if (!activeDatasourceId) {
return null;
}

if (suggestions.length === 0) {
return null;
}
const startTime = useRef<number>(0);
const suggestionsRendered = useRef<number>(0);
const totalSuggestions = suggestions.length + 1;

const onSuggestionRender = useCallback(() => {
suggestionsRendered.current++;
if (suggestionsRendered.current === totalSuggestions) {
// console.log(
// 'suggestions took to fetch data and render',
// performance.now() - startTime.current
// );
}
}, [totalSuggestions]);

function rollbackToCurrentVisualization() {
const rollbackToCurrentVisualization = useCallback(() => {
if (lastSelectedSuggestion !== -1) {
setLastSelectedSuggestion(-1);
dispatchLens(rollbackSuggestion());
dispatchLens(applyChanges());
}
}, [dispatchLens, lastSelectedSuggestion]);

if (!activeDatasourceId) {
return null;
}

if (suggestions.length === 0) {
return null;
}

const renderApplyChangesPrompt = () => (
Expand Down Expand Up @@ -400,53 +421,73 @@ export function SuggestionPanel({
</EuiPanel>
);

const renderSuggestionsUI = () => (
<>
{currentVisualization.activeId && !hideSuggestions && (
<SuggestionPreview
preview={{
error: currentStateError,
expression: currentStateExpression,
icon:
visualizationMap[currentVisualization.activeId].getDescription(
currentVisualization.state
).icon || 'empty',
title: i18n.translate('xpack.lens.suggestions.currentVisLabel', {
defaultMessage: 'Current visualization',
}),
}}
ExpressionRenderer={AutoRefreshExpressionRenderer}
onSelect={rollbackToCurrentVisualization}
selected={lastSelectedSuggestion === -1}
showTitleAsLabel
/>
)}
{!hideSuggestions &&
suggestions.map((suggestion, index) => {
return (
<SuggestionPreview
preview={{
expression: suggestion.previewExpression,
icon: suggestion.previewIcon,
title: suggestion.title,
}}
ExpressionRenderer={AutoRefreshExpressionRenderer}
key={index}
onSelect={() => {
if (lastSelectedSuggestion === index) {
rollbackToCurrentVisualization();
} else {
setLastSelectedSuggestion(index);
switchToSuggestion(dispatchLens, suggestion, { applyImmediately: true });
}
}}
selected={index === lastSelectedSuggestion}
/>
);
})}
</>
const renderSuggestionsLoadingState = () => (
<EuiPanel
hasShadow={false}
className="lnsSuggestionPanel__loadingState"
paddingSize="none"
borderRadius="m"
css={css`
overflow: hidden;
`}
>
<EuiProgress size="xs" color="accent" />
</EuiPanel>
);

const renderSuggestionsUI = () => {
suggestionsRendered.current = 0;
startTime.current = performance.now();
return (
<>
{currentVisualization.activeId && !hideSuggestions && (
<SuggestionPreview
preview={{
error: currentStateError,
expression: currentStateExpression,
icon:
visualizationMap[currentVisualization.activeId].getDescription(
currentVisualization.state
).icon || 'empty',
title: i18n.translate('xpack.lens.suggestions.currentVisLabel', {
defaultMessage: 'Current visualization',
}),
}}
ExpressionRenderer={AutoRefreshExpressionRenderer}
onSelect={rollbackToCurrentVisualization}
selected={lastSelectedSuggestion === -1}
showTitleAsLabel
onRender={onSuggestionRender}
/>
)}
{!hideSuggestions &&
suggestions.map((suggestion, index) => {
return (
<SuggestionPreview
preview={{
expression: suggestion.previewExpression,
icon: suggestion.previewIcon,
title: suggestion.title,
}}
ExpressionRenderer={AutoRefreshExpressionRenderer}
key={index}
onSelect={() => {
if (lastSelectedSuggestion === index) {
rollbackToCurrentVisualization();
} else {
setLastSelectedSuggestion(index);
switchToSuggestion(dispatchLens, suggestion, { applyImmediately: true });
}
}}
selected={index === lastSelectedSuggestion}
onRender={onSuggestionRender}
/>
);
})}
</>
);
};

return (
<div className="lnsSuggestionPanel">
<EuiAccordion
Expand Down Expand Up @@ -494,7 +535,11 @@ export function SuggestionPanel({
role="list"
tabIndex={0}
>
{changesApplied ? renderSuggestionsUI() : renderApplyChangesPrompt()}
{changesApplied
? activeData
? renderSuggestionsUI()
: renderSuggestionsLoadingState()
: renderApplyChangesPrompt()}
</div>
</EuiAccordion>
</div>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -192,8 +192,16 @@ export const InnerWorkspacePanel = React.memo(function InnerWorkspacePanel({
dataViews,
};

// NOTE: initialRenderTime is only set once when the component mounts
const initialRenderTime = useRef<number>(performance.now());
const dataReceivedTime = useRef<number>(0);

const onRender$ = useCallback(() => {
if (renderDeps.current) {
// console.log(
// 'visualization took to render after data received',
// performance.now() - dataReceivedTime.current
// );
const datasourceEvents = Object.values(renderDeps.current.datasourceMap).reduce<string[]>(
(acc, datasource) => {
if (!renderDeps.current!.datasourceStates[datasource.id]) return [];
Expand Down Expand Up @@ -232,6 +240,10 @@ export const InnerWorkspacePanel = React.memo(function InnerWorkspacePanel({
const onData$ = useCallback(
(_data: unknown, adapters?: Partial<DefaultInspectorAdapters>) => {
if (renderDeps.current) {
dataReceivedTime.current = performance.now();
// NOTE: this metric is only valid for an initial editor load of a pre-existing visualization
// console.log('data took to arrive', dataReceivedTime.current - initialRenderTime.current);

const [defaultLayerId] = Object.keys(renderDeps.current.datasourceLayers);
const datasource = Object.values(renderDeps.current.datasourceMap)[0];
const datasourceState = Object.values(renderDeps.current.datasourceStates)[0].state;
Expand Down Expand Up @@ -687,6 +699,12 @@ export const VisualizationWrapper = ({
// Used for reporting
const { isRenderComplete, hasDynamicError, setIsRenderComplete, setDynamicError, nodeRef } =
useReportingState(errors);

const onRenderHandler = useCallback(() => {
setIsRenderComplete(true);
onRender$();
}, [setIsRenderComplete, onRender$]);
Comment on lines +724 to +727
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Important to memoize—multiple subscriptions in useExpressionRenderer hook were being created which led to multiple calls to onRender$.


const searchContext: ExecutionContextSearch = useMemo(
() => ({
query: context.query,
Expand Down Expand Up @@ -782,10 +800,7 @@ export const VisualizationWrapper = ({
onEvent={onEvent}
hasCompatibleActions={hasCompatibleActions}
onData$={onData$}
onRender$={() => {
setIsRenderComplete(true);
onRender$();
}}
onRender$={onRenderHandler}
inspectorAdapters={lensInspector.adapters}
executionContext={executionContext}
renderMode="edit"
Expand Down