Skip to content

Commit

Permalink
[dagit] RunTimeline: Fix overlap with ongoing run (#7245)
Browse files Browse the repository at this point in the history
  • Loading branch information
hellendag committed Apr 1, 2022
1 parent 99d40dd commit b61db9d
Show file tree
Hide file tree
Showing 4 changed files with 401 additions and 88 deletions.
66 changes: 52 additions & 14 deletions js_modules/dagit/packages/core/src/runs/RunTimeline.stories.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ export default {
} as Meta;

export const OneRow = () => {
const twoHoursAgo = React.useMemo(() => Date.now() - 6 * 60 * 60 * 1000, []);
const sixHoursAgo = React.useMemo(() => Date.now() - 6 * 60 * 60 * 1000, []);
const now = React.useMemo(() => Date.now(), []);

const jobs = React.useMemo(() => {
Expand All @@ -23,21 +23,21 @@ export const OneRow = () => {
key: jobKey,
jobName: jobKey,
path: `/${jobKey}`,
runs: generateRunMocks(6, [twoHoursAgo, now]),
runs: generateRunMocks(6, [sixHoursAgo, now]),
},
];
}, [twoHoursAgo, now]);
}, [sixHoursAgo, now]);

return <RunTimeline jobs={jobs} range={[twoHoursAgo, now]} />;
return <RunTimeline jobs={jobs} range={[sixHoursAgo, now]} />;
};

export const RowWithOverlappingRuns = () => {
const twoHoursAgo = React.useMemo(() => Date.now() - 6 * 60 * 60 * 1000, []);
const sixHoursAgo = React.useMemo(() => Date.now() - 6 * 60 * 60 * 1000, []);
const now = React.useMemo(() => Date.now(), []);

const jobs = React.useMemo(() => {
const jobKey = faker.random.words(2).split(' ').join('-').toLowerCase();
const [first, second, third] = generateRunMocks(3, [twoHoursAgo, now]);
const [first, second, third] = generateRunMocks(3, [sixHoursAgo, now]);
return [
{
key: jobKey,
Expand All @@ -46,13 +46,51 @@ export const RowWithOverlappingRuns = () => {
runs: [{...first}, {...first}, {...second}, {...second}, {...second}, third],
},
];
}, [sixHoursAgo, now]);

return <RunTimeline jobs={jobs} range={[sixHoursAgo, now]} />;
};

export const OverlapWithRunning = () => {
const twoHoursAgo = React.useMemo(() => Date.now() - 2 * 60 * 60 * 1000, []);
const now = React.useMemo(() => Date.now(), []);

const jobs = React.useMemo(() => {
const jobKey = faker.random.words(2).split(' ').join('-').toLowerCase();
return [
{
key: jobKey,
jobName: jobKey,
path: `/${jobKey}`,
runs: [
{
id: faker.datatype.uuid(),
status: RunStatus.SUCCESS,
startTime: twoHoursAgo + 20 * 60 * 1000,
endTime: twoHoursAgo + 95 * 60 * 1000,
},
{
id: faker.datatype.uuid(),
status: RunStatus.SUCCESS,
startTime: twoHoursAgo + 90 * 60 * 1000,
endTime: twoHoursAgo + 110 * 60 * 1000,
},
{
id: faker.datatype.uuid(),
status: RunStatus.STARTED,
startTime: twoHoursAgo + 60 * 60 * 1000,
endTime: now,
},
],
},
];
}, [twoHoursAgo, now]);

return <RunTimeline jobs={jobs} range={[twoHoursAgo, now]} />;
return <RunTimeline jobs={jobs} range={[twoHoursAgo, now + 60 * 6000]} />;
};

export const ManyRows = () => {
const twoHoursAgo = React.useMemo(() => Date.now() - 6 * 60 * 60 * 1000, []);
const sixHoursAgo = React.useMemo(() => Date.now() - 6 * 60 * 60 * 1000, []);
const now = React.useMemo(() => Date.now(), []);

const jobs = React.useMemo(() => {
Expand All @@ -64,18 +102,18 @@ export const ManyRows = () => {
key: jobKey,
jobName: jobKey,
path: `/${jobKey}`,
runs: generateRunMocks(6, [twoHoursAgo, now]),
runs: generateRunMocks(6, [sixHoursAgo, now]),
},
];
}, []);
}, [twoHoursAgo, now]);
}, [sixHoursAgo, now]);

return <RunTimeline jobs={jobs} range={[twoHoursAgo, now]} />;
return <RunTimeline jobs={jobs} range={[sixHoursAgo, now]} />;
};

export const VeryLongRunning = () => {
const fourHoursAgo = React.useMemo(() => Date.now() - 4 * 60 * 60 * 1000, []);
const twoHoursAgo = React.useMemo(() => Date.now() - 2 * 60 * 60 * 1000, []);
const sixHoursAgo = React.useMemo(() => Date.now() - 2 * 60 * 60 * 1000, []);
const twoDaysAgo = React.useMemo(() => Date.now() - 48 * 60 * 60 * 1000, []);
const future = React.useMemo(() => Date.now() + 1 * 60 * 60 * 1000, []);

Expand All @@ -92,7 +130,7 @@ export const VeryLongRunning = () => {
id: faker.datatype.uuid(),
status: RunStatus.FAILURE,
startTime: twoDaysAgo,
endTime: twoHoursAgo,
endTime: sixHoursAgo,
},
],
},
Expand All @@ -110,7 +148,7 @@ export const VeryLongRunning = () => {
],
},
];
}, [twoDaysAgo, twoHoursAgo]);
}, [twoDaysAgo, sixHoursAgo]);

return <RunTimeline jobs={jobs} range={[fourHoursAgo, future]} />;
};
87 changes: 13 additions & 74 deletions js_modules/dagit/packages/core/src/runs/RunTimeline.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ import {
successStatuses,
} from './RunStatuses';
import {TimeElapsed} from './TimeElapsed';
import {batchRunsForTimeline, overlap, RunBatch} from './batchRunsForTimeline';
import {RunTimelineQuery, RunTimelineQueryVariables} from './types/RunTimelineQuery';

const ROW_HEIGHT = 24;
Expand Down Expand Up @@ -350,17 +351,6 @@ const DividerLine = styled.div`
width: 1px;
`;

const overlap = (a: {start: number; end: number}, b: {start: number; end: number}) =>
!(a.end < b.start || b.end < a.start);

type RunBatch = {
runs: TimelineRun[];
startTime: number;
endTime: number;
left: number;
width: number;
};

const mergeStatusToColor = (runs: TimelineRun[]) => {
let anyInProgress = false;
let anyQueued = false;
Expand Down Expand Up @@ -417,73 +407,22 @@ const RunTimelineRow = ({
}) => {
// const {jobKey, jobLabel, jobPath, runs, top, range, width: containerWidth} = props;
const [start, end] = range;
const rangeLength = end - start;
const width = containerWidth - LABEL_WIDTH;
const {runs} = job;

// Batch overlapping runs in this row.
const batched = React.useMemo(() => {
const batches: RunBatch[] = job.runs
.map((run) => {
const startTime = run.startTime;
const endTime = run.endTime || Date.now();
const left = Math.max(0, Math.floor(((startTime - start) / rangeLength) * width));
const runWidth = Math.max(
MIN_CHUNK_WIDTH,
Math.min(
Math.ceil(((endTime - startTime) / rangeLength) * width),
Math.ceil(((endTime - start) / rangeLength) * width),
),
);

return {
runs: [run],
startTime,
endTime,
left,
width: runWidth,
};
})
.sort((a, b) => b.left - a.left);

const consolidated = [];
while (batches.length) {
const current = batches.shift();
const next = batches[0];
if (current) {
if (
next &&
overlap(
{
start: current.left,
end: current.left + Math.max(current.width, MIN_WIDTH_FOR_MULTIPLE),
},
{start: next.left, end: next.left + Math.max(next.width, MIN_WIDTH_FOR_MULTIPLE)},
)
) {
// Remove `next`, consolidate it with `current`, and unshift it back on.
// This way, we keep looking for batches to consolidate with.
batches.shift();
current.runs = [...current.runs, ...next.runs];
current.startTime = Math.min(current.startTime, next.startTime);
current.endTime = Math.max(current.endTime, next.endTime);
current.left = Math.min(current.left, next.left);
const right = Math.max(
current.left + MIN_WIDTH_FOR_MULTIPLE,
current.left + current.width,
next.left + next.width,
);
current.width = right - current.left;
batches.unshift(current);
} else {
// If the next batch doesn't overlap, we've consolidated this batch
// all we can. Move on!
consolidated.push(current);
}
}
}
const batches: RunBatch<TimelineRun>[] = batchRunsForTimeline({
runs,
start,
end,
width,
minChunkWidth: MIN_CHUNK_WIDTH,
minMultipleWidth: MIN_WIDTH_FOR_MULTIPLE,
});

return consolidated;
}, [rangeLength, job, start, width]);
return batches;
}, [runs, start, end, width]);

if (!job.runs.length) {
return null;
Expand Down Expand Up @@ -612,7 +551,7 @@ const BatchCount = styled.div`

interface RunHoverContentProps {
jobKey: string;
batch: RunBatch;
batch: RunBatch<TimelineRun>;
}

const RunHoverContent = (props: RunHoverContentProps) => {
Expand Down

0 comments on commit b61db9d

Please sign in to comment.