From 210a3ff49e220f8ed8268a21dfd030e9dc6d175b Mon Sep 17 00:00:00 2001 From: csirius <85753828+csirius@users.noreply.github.com> Date: Tue, 7 Dec 2021 15:44:51 -0800 Subject: [PATCH] fix: performance issue related to search (#250) Signed-off-by: csirius <85753828+csirius@users.noreply.github.com> --- .../Workflow/SearchableWorkflowNameList.tsx | 170 ++++++++++-------- .../Workflow/useWorkflowInfoItem.ts | 142 ++++++++------- 2 files changed, 172 insertions(+), 140 deletions(-) diff --git a/src/components/Workflow/SearchableWorkflowNameList.tsx b/src/components/Workflow/SearchableWorkflowNameList.tsx index 326c72b1f..846c6a3d8 100644 --- a/src/components/Workflow/SearchableWorkflowNameList.tsx +++ b/src/components/Workflow/SearchableWorkflowNameList.tsx @@ -20,6 +20,7 @@ import { useSearchableListState } from '../common/useSearchableListState'; import { useWorkflowInfoItem } from './useWorkflowInfoItem'; import { Shimmer } from 'components/common/Shimmer'; import { WorkflowExecutionIdentifier } from 'models/Execution/types'; +import { debounce } from 'lodash'; interface SearchableWorkflowNameItemProps { item: WorkflowListStructureItem; @@ -110,86 +111,98 @@ const padExecutionPaths = (items: WorkflowExecutionIdentifier[]) => { * @param item * @returns */ -const SearchableWorkflowNameItem: React.FC = ({ - item -}) => { - const commonStyles = useCommonStyles(); - const listStyles = useNamedEntityListStyles(); - const styles = useStyles(); - const { id, description } = item; - const { data: workflow, isLoading } = useWorkflowInfoItem(id); +const SearchableWorkflowNameItem: React.FC = React.memo( + ({ item }) => { + const commonStyles = useCommonStyles(); + const listStyles = useNamedEntityListStyles(); + const styles = useStyles(); + const { id, description } = item; + const { data: workflow, isLoading } = useWorkflowInfoItem(id); - return ( - -
-
- -
{id.name}
-
-
- {description?.length - ? description - : 'This workflow has no description.'} -
-
-
Last execution time
-
- {isLoading ? ( - - ) : workflow.latestExecutionTime ? ( - workflow.latestExecutionTime - ) : ( - No executions found - )} +
+
+ +
{id.name}
-
-
-
Last 10 executions
- {isLoading ? ( - - ) : ( - + {description?.length + ? description + : 'This workflow has no description.'} +
+
+
+ Last execution time +
+
+ {isLoading ? ( + + ) : workflow.latestExecutionTime ? ( + workflow.latestExecutionTime + ) : ( + No executions found )} - /> - )} -
-
-
Inputs
-
- {isLoading ? ( - - ) : ( - workflow.inputs ?? {workflowNoInputsString} - )} +
-
-
-
Outputs
-
+
+
+ Last 10 executions +
{isLoading ? ( ) : ( - workflow?.outputs ?? No output data found. + )}
+
+
Inputs
+
+ {isLoading ? ( + + ) : ( + workflow.inputs ?? ( + {workflowNoInputsString} + ) + )} +
+
+
+
Outputs
+
+ {isLoading ? ( + + ) : ( + workflow?.outputs ?? ( + No output data found. + ) + )} +
+
-
- - ); -}; + + ); + } +); /** * Renders a searchable list of Workflow names, with associated descriptions @@ -200,15 +213,22 @@ export const SearchableWorkflowNameList: React.FC { const styles = useStyles(); - const { results, searchString, setSearchString } = useSearchableListState({ + const [search, setSearch] = React.useState(''); + const { results, setSearchString } = useSearchableListState({ items: workflows, propertyGetter: ({ id }) => id.name }); + const onSearchChange = (event: React.ChangeEvent) => { const searchString = event.target.value; - setSearchString(searchString); + setSearch(searchString); + const debouncedSearch = debounce( + () => setSearchString(searchString), + 1000 + ); + debouncedSearch(); }; - const onClear = () => setSearchString(''); + const onClear = () => setSearch(''); return ( <> @@ -216,15 +236,15 @@ export const SearchableWorkflowNameList: React.FC
- {results.map((id, idx) => ( + {results.map(({ value }) => ( ))}
diff --git a/src/components/Workflow/useWorkflowInfoItem.ts b/src/components/Workflow/useWorkflowInfoItem.ts index a866f6947..29ed730d8 100644 --- a/src/components/Workflow/useWorkflowInfoItem.ts +++ b/src/components/Workflow/useWorkflowInfoItem.ts @@ -23,80 +23,92 @@ export const useWorkflowInfoItem = ({ data: executionInfo, isLoading: executionLoading, error: executionError - } = useQuery(['workflow-executions', domain, project, name], async () => { - const { entities: executions } = await listExecutions( - { domain, project }, - { - sort: { - key: executionSortFields.createdAt, - direction: SortDirection.DESCENDING - }, - filter: [ - { - key: 'workflow.name', - operation: FilterOperationName.EQ, - value: name - } - ], - limit: 10 + } = useQuery( + ['workflow-executions', domain, project, name], + async () => { + const { entities: executions } = await listExecutions( + { domain, project }, + { + sort: { + key: executionSortFields.createdAt, + direction: SortDirection.DESCENDING + }, + filter: [ + { + key: 'workflow.name', + operation: FilterOperationName.EQ, + value: name + } + ], + limit: 10 + } + ); + const executionIds = executions.map(execution => execution.id); + let latestExecutionTime; + const hasExecutions = executions.length > 0; + if (hasExecutions) { + const latestExecution = executions[0].closure.createdAt; + const timeStamp = { + nanos: latestExecution.nanos, + seconds: Long.fromValue(latestExecution.seconds!) + }; + latestExecutionTime = formatDateUTC(timestampToDate(timeStamp)); } - ); - const executionIds = executions.map(execution => execution.id); - let latestExecutionTime; - const hasExecutions = executions.length > 0; - if (hasExecutions) { - const latestExecution = executions[0].closure.createdAt; - const timeStamp = { - nanos: latestExecution.nanos, - seconds: Long.fromValue(latestExecution.seconds!) + const executionStatus = executions.map( + execution => execution.closure.phase + ); + return { + latestExecutionTime, + executionStatus, + executionIds }; - latestExecutionTime = formatDateUTC(timestampToDate(timeStamp)); + }, + { + staleTime: 1000 * 60 * 5 } - const executionStatus = executions.map( - execution => execution.closure.phase - ); - return { - latestExecutionTime, - executionStatus, - executionIds - }; - }); + ); const { data: workflowInfo, isLoading: workflowLoading, error: workflowError - } = useQuery(['workflow-info', domain, project, name], async () => { - const { - entities: [workflow] - } = await listWorkflows( - { domain, project, name }, - { - limit: 1, - sort: { - key: workflowSortFields.createdAt, - direction: SortDirection.DESCENDING + } = useQuery( + ['workflow-info', domain, project, name], + async () => { + const { + entities: [workflow] + } = await listWorkflows( + { domain, project, name }, + { + limit: 1, + sort: { + key: workflowSortFields.createdAt, + direction: SortDirection.DESCENDING + } } - } - ); - const { id } = workflow; - const { - entities: [launchPlan] - } = await listLaunchPlans({ domain, project, name }, { limit: 1 }); - const parsedInputs = getInputsForWorkflow( - workflow, - launchPlan, - undefined - ); - const inputs = - parsedInputs.length > 0 - ? parsedInputs.map(input => input.label).join(', ') - : undefined; - const parsedOutputs = getOutputsForWorkflow(launchPlan); - const outputs = - parsedOutputs.length > 0 ? parsedOutputs.join(', ') : undefined; - return { id, inputs, outputs }; - }); + ); + const { id } = workflow; + const { + entities: [launchPlan] + } = await listLaunchPlans({ domain, project, name }, { limit: 1 }); + const parsedInputs = getInputsForWorkflow( + workflow, + launchPlan, + undefined + ); + const inputs = + parsedInputs.length > 0 + ? parsedInputs.map(input => input.label).join(', ') + : undefined; + const parsedOutputs = getOutputsForWorkflow(launchPlan); + const outputs = + parsedOutputs.length > 0 ? parsedOutputs.join(', ') : undefined; + return { id, inputs, outputs }; + }, + { + staleTime: 1000 * 60 * 5 + } + ); return { data: {