You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@airflow.apache.org by ka...@apache.org on 2025/09/09 21:45:31 UTC
(airflow) 04/14: Optimize Gantt group expansion with debouncing and deferred rendering (#55334)
This is an automated email from the ASF dual-hosted git repository.
kaxilnaik pushed a commit to branch v3-1-test
in repository https://gitbox.apache.org/repos/asf/airflow.git
commit bb53eedfddab0bebe06d66e25360f86847a33691
Author: Guan Ming(Wesley) Chiu <10...@users.noreply.github.com>
AuthorDate: Wed Sep 10 01:41:00 2025 +0800
Optimize Gantt group expansion with debouncing and deferred rendering (#55334)
---
.../src/context/openGroups/OpenGroupsProvider.tsx | 32 ++++++++++++++++++----
.../airflow/ui/src/layouts/Details/Gantt/Gantt.tsx | 8 ++++--
2 files changed, 33 insertions(+), 7 deletions(-)
diff --git a/airflow-core/src/airflow/ui/src/context/openGroups/OpenGroupsProvider.tsx b/airflow-core/src/airflow/ui/src/context/openGroups/OpenGroupsProvider.tsx
index 1fde16745c3..c65e9a9e1c3 100644
--- a/airflow-core/src/airflow/ui/src/context/openGroups/OpenGroupsProvider.tsx
+++ b/airflow-core/src/airflow/ui/src/context/openGroups/OpenGroupsProvider.tsx
@@ -17,6 +17,7 @@
* under the License.
*/
import { useCallback, useMemo, useEffect, type PropsWithChildren } from "react";
+import { useDebouncedCallback } from "use-debounce";
import { useLocalStorage } from "usehooks-ts";
import { useStructureServiceStructureData } from "openapi/queries";
@@ -59,20 +60,41 @@ export const OpenGroupsProvider = ({ children, dagId }: Props) => {
}
}, [structure.nodes, allGroupIds, setAllGroupIds]);
+ const debouncedSetOpenGroupIds = useDebouncedCallback(
+ (newGroupIds: Array<string>) => {
+ setOpenGroupIds(newGroupIds);
+ },
+ 100, // 100ms debounce for batch operations
+ );
+
const toggleGroupId = useCallback(
(groupId: string) => {
if (openGroupIds.includes(groupId)) {
- setOpenGroupIds(openGroupIds.filter((id) => id !== groupId));
+ debouncedSetOpenGroupIds(openGroupIds.filter((id) => id !== groupId));
} else {
- setOpenGroupIds([...openGroupIds, groupId]);
+ debouncedSetOpenGroupIds([...openGroupIds, groupId]);
}
},
- [openGroupIds, setOpenGroupIds],
+ [openGroupIds, debouncedSetOpenGroupIds],
+ );
+
+ const setOpenGroupIdsImmediate = useCallback(
+ (newGroupIds: Array<string>) => {
+ debouncedSetOpenGroupIds.cancel();
+ setOpenGroupIds(newGroupIds);
+ },
+ [debouncedSetOpenGroupIds, setOpenGroupIds],
);
const value = useMemo<OpenGroupsContextType>(
- () => ({ allGroupIds, openGroupIds, setAllGroupIds, setOpenGroupIds, toggleGroupId }),
- [allGroupIds, openGroupIds, setAllGroupIds, setOpenGroupIds, toggleGroupId],
+ () => ({
+ allGroupIds,
+ openGroupIds,
+ setAllGroupIds,
+ setOpenGroupIds: setOpenGroupIdsImmediate,
+ toggleGroupId,
+ }),
+ [allGroupIds, openGroupIds, setAllGroupIds, setOpenGroupIdsImmediate, toggleGroupId],
);
return <OpenGroupsContext.Provider value={value}>{children}</OpenGroupsContext.Provider>;
diff --git a/airflow-core/src/airflow/ui/src/layouts/Details/Gantt/Gantt.tsx b/airflow-core/src/airflow/ui/src/layouts/Details/Gantt/Gantt.tsx
index 3b6bdceb2da..004272fa4e6 100644
--- a/airflow-core/src/airflow/ui/src/layouts/Details/Gantt/Gantt.tsx
+++ b/airflow-core/src/airflow/ui/src/layouts/Details/Gantt/Gantt.tsx
@@ -33,7 +33,7 @@ import {
import "chart.js/auto";
import "chartjs-adapter-dayjs-4/dist/chartjs-adapter-dayjs-4.esm";
import annotationPlugin from "chartjs-plugin-annotation";
-import { useMemo, useRef } from "react";
+import { useMemo, useRef, useDeferredValue } from "react";
import { Bar } from "react-chartjs-2";
import { useTranslation } from "react-i18next";
import { useParams, useNavigate, useLocation } from "react-router-dom";
@@ -77,6 +77,7 @@ const MIN_BAR_WIDTH = 10;
export const Gantt = ({ limit }: Props) => {
const { dagId = "", groupId: selectedGroupId, runId = "", taskId: selectedTaskId } = useParams();
const { openGroupIds } = useOpenGroups();
+ const deferredOpenGroupIds = useDeferredValue(openGroupIds);
const { t: translate } = useTranslation("common");
const { selectedTimezone } = useTimezone();
const { colorMode } = useColorMode();
@@ -119,7 +120,10 @@ export const Gantt = ({ limit }: Props) => {
},
);
- const { flatNodes } = useMemo(() => flattenNodes(dagStructure, openGroupIds), [dagStructure, openGroupIds]);
+ const { flatNodes } = useMemo(
+ () => flattenNodes(dagStructure, deferredOpenGroupIds),
+ [dagStructure, deferredOpenGroupIds],
+ );
const isLoading = runsLoading || structureLoading || summariesLoading || tiLoading;