Skip to content

Commit

Permalink
Reduce getPipeline calls in RunList (kubeflow#1852)
Browse files Browse the repository at this point in the history
* Skips calling getPipeline in RunList if the pipeline name is in the pipeline_spec

* Update fixed data to include pipeline names in pipeline specs

* Remove redundant getRuns call
  • Loading branch information
rileyjbauer authored and k8s-ci-robot committed Aug 15, 2019
1 parent 39e5840 commit 0517114
Show file tree
Hide file tree
Showing 10 changed files with 163 additions and 35 deletions.
22 changes: 16 additions & 6 deletions frontend/mock-backend/fixed-data.ts
Original file line number Diff line number Diff line change
Expand Up @@ -136,6 +136,7 @@ const jobs: ApiJob[] = [
}
],
pipeline_id: pipelines[0].id,
pipeline_name: pipelines[0].name,
},
resource_references: [{
key: {
Expand Down Expand Up @@ -177,6 +178,7 @@ const jobs: ApiJob[] = [
}
],
pipeline_id: pipelines[1].id,
pipeline_name: pipelines[1].name,
},
resource_references: [{
key: {
Expand Down Expand Up @@ -221,6 +223,7 @@ const jobs: ApiJob[] = [
}
],
pipeline_id: pipelines[2].id,
pipeline_name: pipelines[2].name,
},
resource_references: [{
key: {
Expand Down Expand Up @@ -290,7 +293,8 @@ const runs: ApiRunDetail[] = [
{ name: 'paramName1', value: 'paramVal1' },
{ name: 'paramName2', value: 'paramVal2' },
],
pipeline_id: '8fbe3bd6-a01f-11e8-98d0-529269fb1459',
pipeline_id: pipelines[0].id,
pipeline_name: pipelines[0].name,
},
resource_references: [{
key: {
Expand Down Expand Up @@ -327,7 +331,8 @@ const runs: ApiRunDetail[] = [
{ name: 'paramName1', value: 'paramVal1' },
{ name: 'paramName2', value: 'paramVal2' },
],
pipeline_id: '8fbe3bd6-a01f-11e8-98d0-529269fb1459',
pipeline_id: pipelines[0].id,
pipeline_name: pipelines[0].name,
},
resource_references: [{
key: {
Expand Down Expand Up @@ -360,7 +365,8 @@ const runs: ApiRunDetail[] = [
{ name: 'paramName1', value: 'paramVal1' },
{ name: 'paramName2', value: 'paramVal2' },
],
pipeline_id: '8fbe41b2-a01f-11e8-98d0-529269fb1459',
pipeline_id: pipelines[2].id,
pipeline_name: pipelines[2].name,
},
resource_references: [{
key: {
Expand Down Expand Up @@ -393,7 +399,8 @@ const runs: ApiRunDetail[] = [
{ name: 'paramName1', value: 'paramVal1' },
{ name: 'paramName2', value: 'paramVal2' },
],
pipeline_id: '8fbe42f2-a01f-11e8-98d0-529269fb1459',
pipeline_id: pipelines[3].id,
pipeline_name: pipelines[3].name,
},
scheduled_at: new Date('2018-06-17T22:58:23.000Z'),
status: 'Failed',
Expand Down Expand Up @@ -439,7 +446,8 @@ const runs: ApiRunDetail[] = [
{ name: 'paramName1', value: 'paramVal1' },
{ name: 'paramName2', value: 'paramVal2' },
],
pipeline_id: '8fbe3f78-a01f-11e8-98d0-529269fb1459',
pipeline_id: pipelines[1].id,
pipeline_name: pipelines[1].name,
},
resource_references: [{
key: {
Expand Down Expand Up @@ -488,7 +496,8 @@ const runs: ApiRunDetail[] = [
{ name: 'paramName1', value: 'paramVal1' },
{ name: 'paramName2', value: 'paramVal2' },
],
pipeline_id: '8fbe3f78-a01f-11e8-98d0-529269fb1459',
pipeline_id: pipelines[1].id,
pipeline_name: pipelines[1].name,
},
resource_references: [{
key: {
Expand Down Expand Up @@ -603,6 +612,7 @@ function generateNRuns(): ApiRunDetail[] {
{ name: 'paramName2', value: 'paramVal2' },
],
pipeline_id: 'Some-pipeline-id-' + i,
pipeline_name: 'Kubeflow Pipeline number ' + i,
},
resource_references: [{
key: {
Expand Down
12 changes: 12 additions & 0 deletions frontend/src/apis/job/api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -274,6 +274,12 @@ export interface ApiPipelineSpec {
* @memberof ApiPipelineSpec
*/
pipeline_id?: string;
/**
* Optional output field. The name of the pipeline. Not empty if the pipeline id is not empty.
* @type {string}
* @memberof ApiPipelineSpec
*/
pipeline_name?: string;
/**
* Optional input field. The marshalled raw argo JSON workflow. This will be deprecated when pipeline_manifest is in use.
* @type {string}
Expand Down Expand Up @@ -337,6 +343,12 @@ export interface ApiResourceReference {
* @memberof ApiResourceReference
*/
key?: ApiResourceKey;
/**
* The name of the resource that referred to.
* @type {string}
* @memberof ApiResourceReference
*/
name?: string;
/**
* Required field. The relationship from referred resource to the object.
* @type {ApiRelationship}
Expand Down
12 changes: 12 additions & 0 deletions frontend/src/apis/run/api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -156,6 +156,12 @@ export interface ApiPipelineSpec {
* @memberof ApiPipelineSpec
*/
pipeline_id?: string;
/**
* Optional output field. The name of the pipeline. Not empty if the pipeline id is not empty.
* @type {string}
* @memberof ApiPipelineSpec
*/
pipeline_name?: string;
/**
* Optional input field. The marshalled raw argo JSON workflow. This will be deprecated when pipeline_manifest is in use.
* @type {string}
Expand Down Expand Up @@ -267,6 +273,12 @@ export interface ApiResourceReference {
* @memberof ApiResourceReference
*/
key?: ApiResourceKey;
/**
* The name of the resource that referred to.
* @type {string}
* @memberof ApiResourceReference
*/
name?: string;
/**
* Required field. The relationship from referred resource to the object.
* @type {ApiRelationship}
Expand Down
10 changes: 8 additions & 2 deletions frontend/src/lib/RunUtils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,11 @@ function getPipelineId(run?: ApiRun | ApiJob): string | null {
return (run && run.pipeline_spec && run.pipeline_spec.pipeline_id) || null;
}

function getPipelineSpec(run?: ApiRun | ApiJob): string | null {
function getPipelineName(run?: ApiRun | ApiJob): string | null {
return (run && run.pipeline_spec && run.pipeline_spec.pipeline_name) || null;
}

function getWorkflowManifest(run?: ApiRun | ApiJob): string | null {
return (run && run.pipeline_spec && run.pipeline_spec.workflow_manifest) || null;
}

Expand Down Expand Up @@ -123,6 +127,7 @@ function getRecurringRunId(run?: ApiRun): string {
return '';
}

// TODO: This file needs tests
export default {
extractMetricMetadata,
getAllExperimentReferences,
Expand All @@ -131,7 +136,8 @@ export default {
getParametersFromRun,
getParametersFromRuntime,
getPipelineId,
getPipelineSpec,
getPipelineName,
getRecurringRunId,
getWorkflowManifest,
runsToMetricMetadataMap,
};
10 changes: 5 additions & 5 deletions frontend/src/pages/ExperimentDetails.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -222,7 +222,11 @@ class ExperimentDetails extends Page<{}, ExperimentDetailsState> {
}

public async refresh(): Promise<void> {
return this.load();
await this.load();
if (this._runlistRef.current) {
await this._runlistRef.current.refresh();
}
return;
}

public async componentDidMount(): Promise<void> {
Expand Down Expand Up @@ -272,10 +276,6 @@ class ExperimentDetails extends Page<{}, ExperimentDetailsState> {
await this.showPageError(`Error: failed to retrieve experiment: ${experimentId}.`, err);
logger.error(`Error loading experiment: ${experimentId}`, err);
}

if (this._runlistRef.current) {
this._runlistRef.current.refresh();
}
}

private _selectionChanged(selectedIds: string[]): void {
Expand Down
4 changes: 2 additions & 2 deletions frontend/src/pages/NewRun.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -489,7 +489,7 @@ class NewRun extends Page<{}, NewRunState> {

try {
runWithEmbeddedPipeline = await Apis.runServiceApi.getRun(embeddedPipelineRunId);
embeddedPipelineSpec = RunUtils.getPipelineSpec(runWithEmbeddedPipeline.run);
embeddedPipelineSpec = RunUtils.getWorkflowManifest(runWithEmbeddedPipeline.run);
} catch (err) {
await this.showPageError(
`Error: failed to retrieve the specified run: ${embeddedPipelineRunId}.`, err);
Expand Down Expand Up @@ -538,7 +538,7 @@ class NewRun extends Page<{}, NewRunState> {
const referencePipelineId = RunUtils.getPipelineId(originalRun);
// This corresponds to a run where the pipeline has not been uploaded, such as runs started from
// the CLI or notebooks
const embeddedPipelineSpec = RunUtils.getPipelineSpec(originalRun);
const embeddedPipelineSpec = RunUtils.getWorkflowManifest(originalRun);
if (referencePipelineId) {
try {
pipeline = await Apis.pipelineServiceApi.getPipeline(referencePipelineId);
Expand Down
2 changes: 1 addition & 1 deletion frontend/src/pages/PipelineDetails.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -258,7 +258,7 @@ class PipelineDetails extends Page<{}, PipelineDetailsState> {

// Convert the run's pipeline spec to YAML to be displayed as the pipeline's source.
try {
const pipelineSpec = JSON.parse(RunUtils.getPipelineSpec(runDetails.run) || '{}');
const pipelineSpec = JSON.parse(RunUtils.getWorkflowManifest(runDetails.run) || '{}');
try {
templateString = JsYaml.safeDump(pipelineSpec);
} catch (err) {
Expand Down
19 changes: 14 additions & 5 deletions frontend/src/pages/RunList.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -310,7 +310,16 @@ describe('RunList', () => {
});

it('shows pipeline name', async () => {
mockNRuns(1, { run: { pipeline_spec: { pipeline_id: 'test-pipeline-id' } } });
mockNRuns(1, { run: { pipeline_spec: { pipeline_id: 'test-pipeline-id', pipeline_name: 'pipeline name' } } });
const props = generateProps();
tree = shallow(<RunList {...props} />);
await (tree.instance() as RunListTest)._loadRuns({});
expect(props.onError).not.toHaveBeenCalled();
expect(tree).toMatchSnapshot();
});

it('retrieves pipeline from backend to display name if not in spec', async () => {
mockNRuns(1, { run: { pipeline_spec: { pipeline_id: 'test-pipeline-id' /* no pipeline_name */ } } });
getPipelineSpy.mockImplementationOnce(() => ({ name: 'test pipeline' }));
const props = generateProps();
tree = shallow(<RunList {...props} />);
Expand Down Expand Up @@ -372,19 +381,19 @@ describe('RunList', () => {
});

it('renders pipeline name as link to its details page', () => {
expect(getMountedInstance()._pipelineCustomRenderer({ value: { displayName: 'test pipeline', id: 'pipeline-id', showLink: false }, id: 'run-id' })).toMatchSnapshot();
expect(getMountedInstance()._pipelineCustomRenderer({ value: { displayName: 'test pipeline', id: 'pipeline-id', usePlaceholder: false }, id: 'run-id' })).toMatchSnapshot();
});

it('handles no pipeline id given', () => {
expect(getMountedInstance()._pipelineCustomRenderer({ value: { displayName: 'test pipeline', showLink: false }, id: 'run-id' })).toMatchSnapshot();
expect(getMountedInstance()._pipelineCustomRenderer({ value: { displayName: 'test pipeline', usePlaceholder: false }, id: 'run-id' })).toMatchSnapshot();
});

it('shows "View pipeline" button if pipeline is embedded in run', () => {
expect(getMountedInstance()._pipelineCustomRenderer({ value: { displayName: 'test pipeline', id: 'pipeline-id', showLink: true }, id: 'run-id' })).toMatchSnapshot();
expect(getMountedInstance()._pipelineCustomRenderer({ value: { displayName: 'test pipeline', id: 'pipeline-id', usePlaceholder: true }, id: 'run-id' })).toMatchSnapshot();
});

it('handles no pipeline name', () => {
expect(getMountedInstance()._pipelineCustomRenderer({ value: { /* no displayName */ showLink: true }, id: 'run-id' })).toMatchSnapshot();
expect(getMountedInstance()._pipelineCustomRenderer({ value: { /* no displayName */ usePlaceholder: true }, id: 'run-id' })).toMatchSnapshot();
});

it('renders pipeline name as link to its details page', () => {
Expand Down
32 changes: 20 additions & 12 deletions frontend/src/pages/RunList.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ interface PipelineInfo {
displayName?: string;
id?: string;
runId?: string;
showLink: boolean;
usePlaceholder: boolean;
}

interface RecurringRunInfo {
Expand Down Expand Up @@ -191,17 +191,17 @@ class RunList extends React.PureComponent<RunListProps, RunListState> {

public _pipelineCustomRenderer: React.FC<CustomRendererProps<PipelineInfo>> = (props: CustomRendererProps<PipelineInfo>) => {
// If the getPipeline call failed or a run has no pipeline, we display a placeholder.
if (!props.value || (!props.value.showLink && !props.value.id)) {
if (!props.value || (!props.value.usePlaceholder && !props.value.id)) {
return <div>-</div>;
}
const search = new URLParser(this.props).build({ [QUERY_PARAMS.fromRunId]: props.id });
const url = props.value.showLink ?
const url = props.value.usePlaceholder ?
RoutePage.PIPELINE_DETAILS.replace(':' + RouteParams.pipelineId + '?', '') + search :
RoutePage.PIPELINE_DETAILS.replace(':' + RouteParams.pipelineId, props.value.id || '');
return (
<Link className={commonCss.link} onClick={(e) => e.stopPropagation()}
to={url}>
{props.value.showLink ? '[View pipeline]' : props.value.displayName}
{props.value.usePlaceholder ? '[View pipeline]' : props.value.displayName}
</Link>
);
}
Expand Down Expand Up @@ -357,15 +357,23 @@ class RunList extends React.PureComponent<RunListProps, RunListState> {
private async _getAndSetPipelineNames(displayRun: DisplayRun): Promise<void> {
const pipelineId = RunUtils.getPipelineId(displayRun.run);
if (pipelineId) {
try {
const pipeline = await Apis.pipelineServiceApi.getPipeline(pipelineId);
displayRun.pipeline = { displayName: pipeline.name || '', id: pipelineId, showLink: false };
} catch (err) {
// This could be an API exception, or a JSON parse exception.
displayRun.error = 'Failed to get associated pipeline: ' + await errorToMessage(err);
let pipelineName = RunUtils.getPipelineName(displayRun.run);
if (!pipelineName) {
try {
const pipeline = await Apis.pipelineServiceApi.getPipeline(pipelineId);
pipelineName = pipeline.name || '';
} catch (err) {
displayRun.error = 'Failed to get associated pipeline: ' + await errorToMessage(err);
return;
}
}
} else if (!!RunUtils.getPipelineSpec(displayRun.run)) {
displayRun.pipeline = { showLink: true };
displayRun.pipeline = {
displayName: pipelineName,
id: pipelineId,
usePlaceholder: false
};
} else if (!!RunUtils.getWorkflowManifest(displayRun.run)) {
displayRun.pipeline = { usePlaceholder: true };
}
}

Expand Down
Loading

0 comments on commit 0517114

Please sign in to comment.