Skip to content

Commit 8363daa

Browse files
bbovenziYour friendly bot
authored andcommitted
[v3-1-test] Use TI duration from db instead of UI calculated (#56310)
* Use TI duration from db instead of UI calculated * Fix barchart heights (cherry picked from commit 8847e64) Co-authored-by: Brent Bovenzi <[email protected]>
1 parent 4068681 commit 8363daa

File tree

7 files changed

+25
-35
lines changed

7 files changed

+25
-35
lines changed

airflow-core/src/airflow/ui/src/components/TaskInstanceTooltip.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ import type {
2626
} from "openapi/requests/types.gen";
2727
import Time from "src/components/Time";
2828
import { Tooltip, type TooltipProps } from "src/components/ui";
29-
import { getDuration } from "src/utils";
29+
import { renderDuration } from "src/utils";
3030

3131
type Props = {
3232
readonly taskInstance?: LightGridTaskInstanceSummary | TaskInstanceHistoryResponse | TaskInstanceResponse;
@@ -64,7 +64,7 @@ const TaskInstanceTooltip = ({ children, positioning, taskInstance, ...rest }: P
6464
{translate("endDate")}: <Time datetime={taskInstance.end_date} />
6565
</Text>
6666
<Text>
67-
{translate("duration")}: {getDuration(taskInstance.start_date, taskInstance.end_date)}
67+
{translate("duration")}: {renderDuration(taskInstance.duration)}
6868
</Text>
6969
</>
7070
) : undefined}

airflow-core/src/airflow/ui/src/pages/DagsList/RecentRuns.tsx

Lines changed: 5 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ import { Link } from "react-router-dom";
2525
import type { DAGWithLatestDagRunsResponse } from "openapi/requests/types.gen";
2626
import Time from "src/components/Time";
2727
import { Tooltip } from "src/components/ui";
28-
import { getDuration } from "src/utils";
28+
import { renderDuration } from "src/utils";
2929

3030
dayjs.extend(duration);
3131

@@ -42,19 +42,14 @@ export const RecentRuns = ({
4242
return undefined;
4343
}
4444

45-
const runsWithDuration = latestRuns.map((run) => ({
46-
...run,
47-
duration: dayjs.duration(dayjs(run.end_date).diff(run.start_date)).asSeconds(),
48-
}));
49-
5045
const max = Math.max.apply(
5146
undefined,
52-
runsWithDuration.map((run) => run.duration),
47+
latestRuns.map((run) => run.duration ?? 0),
5348
);
5449

5550
return (
5651
<Flex alignItems="flex-end" flexDirection="row-reverse" pb={1}>
57-
{runsWithDuration.map((run) => (
52+
{latestRuns.map((run) => (
5853
<Tooltip
5954
content={
6055
<Box>
@@ -75,7 +70,7 @@ export const RecentRuns = ({
7570
</Text>
7671
)}
7772
<Text>
78-
{translate("duration")}: {getDuration(run.start_date, run.end_date)}
73+
{translate("duration")}: {renderDuration(run.duration)}
7974
</Text>
8075
</Box>
8176
}
@@ -93,7 +88,7 @@ export const RecentRuns = ({
9388
<Box
9489
bg={`${run.state}.solid`}
9590
borderRadius="4px"
96-
height={`${(run.duration / max) * BAR_HEIGHT}px`}
91+
height={`${run.duration === null ? 1 : (run.duration / max) * BAR_HEIGHT}px`}
9792
minHeight={1}
9893
width="4px"
9994
/>

airflow-core/src/airflow/ui/src/pages/TaskInstance/Details.tsx

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ import { TaskTrySelect } from "src/components/TaskTrySelect";
3030
import Time from "src/components/Time";
3131
import { ClipboardRoot, ClipboardIconButton } from "src/components/ui";
3232
import { SearchParamsKeys } from "src/constants/searchParams";
33-
import { getDuration, useAutoRefresh, isStatePending } from "src/utils";
33+
import { useAutoRefresh, isStatePending, renderDuration } from "src/utils";
3434

3535
import { BlockingDeps } from "./BlockingDeps";
3636
import { ExtraLinks } from "./ExtraLinks";
@@ -147,11 +147,7 @@ export const Details = () => {
147147
</Table.Row>
148148
<Table.Row>
149149
<Table.Cell>{translate("duration")}</Table.Cell>
150-
<Table.Cell>
151-
{Boolean(tryInstance?.start_date) // eslint-disable-next-line unicorn/no-null
152-
? getDuration(tryInstance?.start_date ?? null, tryInstance?.end_date ?? null)
153-
: ""}
154-
</Table.Cell>
150+
<Table.Cell>{renderDuration(tryInstance?.duration)}</Table.Cell>
155151
</Table.Row>
156152
<Table.Row>
157153
<Table.Cell>{translate("startDate")}</Table.Cell>

airflow-core/src/airflow/ui/src/pages/TaskInstance/Header.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ import { HeaderCard } from "src/components/HeaderCard";
2929
import { MarkTaskInstanceAsButton } from "src/components/MarkAs";
3030
import Time from "src/components/Time";
3131
import { usePatchTaskInstance } from "src/queries/usePatchTaskInstance";
32-
import { getDuration, useContainerWidth } from "src/utils";
32+
import { renderDuration, useContainerWidth } from "src/utils";
3333

3434
export const Header = ({
3535
isRefreshing,
@@ -53,7 +53,7 @@ export const Header = ({
5353
{ label: translate("startDate"), value: <Time datetime={taskInstance.start_date} /> },
5454
{ label: translate("endDate"), value: <Time datetime={taskInstance.end_date} /> },
5555
...(Boolean(taskInstance.start_date)
56-
? [{ label: translate("duration"), value: getDuration(taskInstance.start_date, taskInstance.end_date) }]
56+
? [{ label: translate("duration"), value: renderDuration(taskInstance.duration) }]
5757
: []),
5858
{
5959
label: translate("taskInstance.dagVersion"),

airflow-core/src/airflow/ui/src/pages/TaskInstances/TaskInstances.tsx

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ import { StateBadge } from "src/components/StateBadge";
3535
import Time from "src/components/Time";
3636
import { TruncatedText } from "src/components/TruncatedText";
3737
import { SearchParamsKeys, type SearchParamsKeysType } from "src/constants/searchParams";
38-
import { getDuration, useAutoRefresh, isStatePending } from "src/utils";
38+
import { useAutoRefresh, isStatePending, renderDuration } from "src/utils";
3939
import { getTaskInstanceLink } from "src/utils/links";
4040

4141
import DeleteTaskInstanceButton from "./DeleteTaskInstanceButton";
@@ -165,8 +165,8 @@ const taskInstanceColumns = ({
165165
header: translate("task.operator"),
166166
},
167167
{
168-
cell: ({ row: { original } }) =>
169-
Boolean(original.start_date) ? getDuration(original.start_date, original.end_date) : "",
168+
accessorKey: "duration",
169+
cell: ({ row: { original } }) => renderDuration(original.duration),
170170
header: translate("duration"),
171171
},
172172
{

airflow-core/src/airflow/ui/src/utils/datetimeUtils.test.ts

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@
1818
*/
1919
import { describe, it, expect } from "vitest";
2020

21-
import { getDuration } from "./datetimeUtils";
21+
import { getDuration, renderDuration } from "./datetimeUtils";
2222

2323
describe("getDuration", () => {
2424
it("handles durations less than 10 seconds", () => {
@@ -49,8 +49,10 @@ describe("getDuration", () => {
4949
expect(getDuration(start, end)).toBe("02:30:00");
5050
});
5151

52-
it("handles null or undefined dates", () => {
53-
expect(getDuration(null, null)).toBe("00:00:00");
54-
expect(getDuration(undefined, undefined)).toBe("00:00:00");
52+
it("handles small, null or undefined values", () => {
53+
// eslint-disable-next-line unicorn/no-null
54+
expect(getDuration(null, null)).toBe(undefined);
55+
expect(getDuration(undefined, undefined)).toBe(undefined);
56+
expect(renderDuration(0.000_01)).toBe(undefined);
5557
});
5658
});

airflow-core/src/airflow/ui/src/utils/datetimeUtils.ts

Lines changed: 5 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -26,20 +26,17 @@ dayjs.extend(tz);
2626
export const DEFAULT_DATETIME_FORMAT = "YYYY-MM-DD HH:mm:ss";
2727
export const DEFAULT_DATETIME_FORMAT_WITH_TZ = `${DEFAULT_DATETIME_FORMAT} z`;
2828

29-
export const renderDuration = (durationSeconds: number | null | undefined): string => {
30-
if (
31-
durationSeconds === null ||
32-
durationSeconds === undefined ||
33-
isNaN(durationSeconds) ||
34-
durationSeconds <= 0
35-
) {
36-
return "00:00:00";
29+
export const renderDuration = (durationSeconds: number | null | undefined): string | undefined => {
30+
if (durationSeconds === null || durationSeconds === undefined || durationSeconds <= 0.01) {
31+
return undefined;
3732
}
3833

34+
// If under 10 seconds, render as 9s
3935
if (durationSeconds < 10) {
4036
return `${durationSeconds.toFixed(2)}s`;
4137
}
4238

39+
// If under 1 day, render as HH:mm:ss otherwise include the number of days
4340
return durationSeconds < 86_400
4441
? dayjs.duration(durationSeconds, "seconds").format("HH:mm:ss")
4542
: dayjs.duration(durationSeconds, "seconds").format("D[d]HH:mm:ss");

0 commit comments

Comments
 (0)