Skip to content

Commit

Permalink
fix: performance issue related to search (flyteorg#250)
Browse files Browse the repository at this point in the history
Signed-off-by: csirius <[email protected]>
  • Loading branch information
govalt authored Dec 7, 2021
1 parent 2b3db60 commit 210a3ff
Show file tree
Hide file tree
Showing 2 changed files with 172 additions and 140 deletions.
170 changes: 95 additions & 75 deletions src/components/Workflow/SearchableWorkflowNameList.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -110,86 +111,98 @@ const padExecutionPaths = (items: WorkflowExecutionIdentifier[]) => {
* @param item
* @returns
*/
const SearchableWorkflowNameItem: React.FC<SearchableWorkflowNameItemProps> = ({
item
}) => {
const commonStyles = useCommonStyles();
const listStyles = useNamedEntityListStyles();
const styles = useStyles();
const { id, description } = item;
const { data: workflow, isLoading } = useWorkflowInfoItem(id);
const SearchableWorkflowNameItem: React.FC<SearchableWorkflowNameItemProps> = React.memo(
({ item }) => {
const commonStyles = useCommonStyles();
const listStyles = useNamedEntityListStyles();
const styles = useStyles();
const { id, description } = item;
const { data: workflow, isLoading } = useWorkflowInfoItem(id);

return (
<Link
className={commonStyles.linkUnstyled}
to={Routes.WorkflowDetails.makeUrl(id.project, id.domain, id.name)}
>
<div
className={classNames(
listStyles.searchResult,
styles.itemContainer
return (
<Link
className={commonStyles.linkUnstyled}
to={Routes.WorkflowDetails.makeUrl(
id.project,
id.domain,
id.name
)}
>
<div className={styles.itemName}>
<DeviceHub className={styles.itemIcon} />
<div>{id.name}</div>
</div>
<div className={styles.itemDescriptionRow}>
{description?.length
? description
: 'This workflow has no description.'}
</div>
<div className={styles.itemRow}>
<div className={styles.itemLabel}>Last execution time</div>
<div className={styles.w100}>
{isLoading ? (
<Shimmer />
) : workflow.latestExecutionTime ? (
workflow.latestExecutionTime
) : (
<em>No executions found</em>
)}
<div
className={classNames(
listStyles.searchResult,
styles.itemContainer
)}
>
<div className={styles.itemName}>
<DeviceHub className={styles.itemIcon} />
<div>{id.name}</div>
</div>
</div>
<div className={styles.itemRow}>
<div className={styles.itemLabel}>Last 10 executions</div>
{isLoading ? (
<Shimmer />
) : (
<ProjectStatusBar
items={padExecutions(
workflow.executionStatus || []
)}
paths={padExecutionPaths(
workflow.executionIds || []
<div className={styles.itemDescriptionRow}>
{description?.length
? description
: 'This workflow has no description.'}
</div>
<div className={styles.itemRow}>
<div className={styles.itemLabel}>
Last execution time
</div>
<div className={styles.w100}>
{isLoading ? (
<Shimmer />
) : workflow.latestExecutionTime ? (
workflow.latestExecutionTime
) : (
<em>No executions found</em>
)}
/>
)}
</div>
<div className={styles.itemRow}>
<div className={styles.itemLabel}>Inputs</div>
<div className={styles.w100}>
{isLoading ? (
<Shimmer />
) : (
workflow.inputs ?? <em>{workflowNoInputsString}</em>
)}
</div>
</div>
</div>
<div className={styles.itemRow}>
<div className={styles.itemLabel}>Outputs</div>
<div className={styles.w100}>
<div className={styles.itemRow}>
<div className={styles.itemLabel}>
Last 10 executions
</div>
{isLoading ? (
<Shimmer />
) : (
workflow?.outputs ?? <em>No output data found.</em>
<ProjectStatusBar
items={padExecutions(
workflow.executionStatus || []
)}
paths={padExecutionPaths(
workflow.executionIds || []
)}
/>
)}
</div>
<div className={styles.itemRow}>
<div className={styles.itemLabel}>Inputs</div>
<div className={styles.w100}>
{isLoading ? (
<Shimmer />
) : (
workflow.inputs ?? (
<em>{workflowNoInputsString}</em>
)
)}
</div>
</div>
<div className={styles.itemRow}>
<div className={styles.itemLabel}>Outputs</div>
<div className={styles.w100}>
{isLoading ? (
<Shimmer />
) : (
workflow?.outputs ?? (
<em>No output data found.</em>
)
)}
</div>
</div>
</div>
</div>
</Link>
);
};
</Link>
);
}
);

/**
* Renders a searchable list of Workflow names, with associated descriptions
Expand All @@ -200,31 +213,38 @@ export const SearchableWorkflowNameList: React.FC<SearchableWorkflowNameListProp
workflows
}) => {
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<HTMLInputElement>) => {
const searchString = event.target.value;
setSearchString(searchString);
setSearch(searchString);
const debouncedSearch = debounce(
() => setSearchString(searchString),
1000
);
debouncedSearch();
};
const onClear = () => setSearchString('');
const onClear = () => setSearch('');

return (
<>
<SearchableInput
onClear={onClear}
onSearchChange={onSearchChange}
variant="normal"
value={searchString}
value={search}
className={styles.searchInputContainer}
placeholder="Search Workflow Name"
/>
<div className={styles.container}>
{results.map((id, idx) => (
{results.map(({ value }) => (
<SearchableWorkflowNameItem
item={id.value}
key={`workflow-name-item-${idx}`}
item={value}
key={`workflow-name-item-${value.id.domain}-${value.id.name}-${value.id.project}`}
/>
))}
</div>
Expand Down
142 changes: 77 additions & 65 deletions src/components/Workflow/useWorkflowInfoItem.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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: {
Expand Down

0 comments on commit 210a3ff

Please sign in to comment.