This is an automated email from the ASF dual-hosted git repository.
arafat2198 pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/ozone.git
The following commit(s) were added to refs/heads/master by this push:
new 428cb5609b3 HDDS-13542. Separate out the API calls for Overview page
(#9033)
428cb5609b3 is described below
commit 428cb5609b350ee5ee7a205e9180c29c205aab4d
Author: Abhishek Pal <[email protected]>
AuthorDate: Wed Sep 17 20:17:34 2025 +0530
HDDS-13542. Separate out the API calls for Overview page (#9033)
---
.../webapps/recon/ozone-recon-web/src/app.less | 6 +
.../{errorBoundary => errors}/errorBoundary.tsx | 0
.../errors/errorCard.tsx} | 41 ++-
.../components/overviewCard/overviewSimpleCard.tsx | 17 +-
.../overviewCard/overviewStorageCard.tsx | 10 +-
.../overviewCard/overviewSummaryCard.tsx | 11 +-
.../tables/insights/containerMismatchTable.tsx | 2 +-
.../tables/insights/deletePendingDirsTable.tsx | 2 +-
.../tables/insights/deletePendingKeysTable.tsx | 2 +-
.../tables/insights/deletedContainerKeysTable.tsx | 2 +-
.../components/tables/insights/openKeysTable.tsx | 2 +-
.../src/v2/constants/overview.constants.tsx | 50 +++
.../src/v2/hooks/useAPIData.hook.ts | 187 +++++++++++
.../src/v2/hooks/useAutoReload.hook.tsx | 89 ++++++
.../hooks/{debounce.hook.tsx => useDebounce.tsx} | 0
.../src/v2/pages/buckets/buckets.tsx | 2 +-
.../src/v2/pages/containers/containers.tsx | 2 +-
.../src/v2/pages/datanodes/datanodes.tsx | 2 +-
.../src/v2/pages/overview/overview.tsx | 347 ++++++++-------------
.../src/v2/pages/pipelines/pipelines.tsx | 2 +-
.../src/v2/pages/volumes/volumes.tsx | 2 +-
.../ozone-recon-web/src/v2/types/overview.types.ts | 37 +--
22 files changed, 548 insertions(+), 267 deletions(-)
diff --git
a/hadoop-ozone/recon/src/main/resources/webapps/recon/ozone-recon-web/src/app.less
b/hadoop-ozone/recon/src/main/resources/webapps/recon/ozone-recon-web/src/app.less
index 44f53fa9d47..6819ff701e6 100644
---
a/hadoop-ozone/recon/src/main/resources/webapps/recon/ozone-recon-web/src/app.less
+++
b/hadoop-ozone/recon/src/main/resources/webapps/recon/ozone-recon-web/src/app.less
@@ -169,4 +169,10 @@ body {
.data-container {
padding: 24px;
height: 80vh;
+}
+
+#error-icon {
+ font-size: 24px;
+ width: 100%;
+ color: #5A656D;
}
\ No newline at end of file
diff --git
a/hadoop-ozone/recon/src/main/resources/webapps/recon/ozone-recon-web/src/v2/components/errorBoundary/errorBoundary.tsx
b/hadoop-ozone/recon/src/main/resources/webapps/recon/ozone-recon-web/src/v2/components/errors/errorBoundary.tsx
similarity index 100%
rename from
hadoop-ozone/recon/src/main/resources/webapps/recon/ozone-recon-web/src/v2/components/errorBoundary/errorBoundary.tsx
rename to
hadoop-ozone/recon/src/main/resources/webapps/recon/ozone-recon-web/src/v2/components/errors/errorBoundary.tsx
diff --git
a/hadoop-ozone/recon/src/main/resources/webapps/recon/ozone-recon-web/src/v2/hooks/debounce.hook.tsx
b/hadoop-ozone/recon/src/main/resources/webapps/recon/ozone-recon-web/src/v2/components/errors/errorCard.tsx
similarity index 51%
copy from
hadoop-ozone/recon/src/main/resources/webapps/recon/ozone-recon-web/src/v2/hooks/debounce.hook.tsx
copy to
hadoop-ozone/recon/src/main/resources/webapps/recon/ozone-recon-web/src/v2/components/errors/errorCard.tsx
index e66dbd5679b..b904673a93d 100644
---
a/hadoop-ozone/recon/src/main/resources/webapps/recon/ozone-recon-web/src/v2/hooks/debounce.hook.tsx
+++
b/hadoop-ozone/recon/src/main/resources/webapps/recon/ozone-recon-web/src/v2/components/errors/errorCard.tsx
@@ -16,20 +16,37 @@
* limitations under the License.
*/
+
import React from 'react';
+import { DisconnectOutlined } from "@ant-design/icons"
+import { Card } from 'antd';
-export function useDebounce<T>(value: T, timeout: number): T {
- const [debounceValue, setDebounceValue] = React.useState<T>(value);
+type ErrorCardProps = {
+ title: string;
+ compact?: boolean;
+};
- React.useEffect(() => {
- const timeoutHandler = setTimeout(() => {
- setDebounceValue(value);
- }, timeout);
+// ------------- Styles -------------- //
+const cardHeadStyle: React.CSSProperties = { fontSize: '14px' };
+const compactCardBodyStyle: React.CSSProperties = {
+ padding: '24px',
+ justifyContent: 'space-between'
+}
+const cardBodyStyle: React.CSSProperties = {
+ padding: '80px'
+}
- return () => {
- clearTimeout(timeoutHandler);
- }
- }, [value, timeout]); // Need to set new timeout anytime the value or
timeout duration changes
+const ErrorCard: React.FC<ErrorCardProps> = ({ title, compact }) => {
+ return (
+ <Card
+ size='small'
+ title={title}
+ headStyle={cardHeadStyle}
+ bodyStyle={(compact) ? compactCardBodyStyle : cardBodyStyle}
+ data-testid={`error-${title}`}>
+ <DisconnectOutlined id="error-icon" />
+ </Card>
+ )
+};
- return debounceValue;
-}
+export default ErrorCard;
diff --git
a/hadoop-ozone/recon/src/main/resources/webapps/recon/ozone-recon-web/src/v2/components/overviewCard/overviewSimpleCard.tsx
b/hadoop-ozone/recon/src/main/resources/webapps/recon/ozone-recon-web/src/v2/components/overviewCard/overviewSimpleCard.tsx
index 2269ad9663a..5971934381e 100644
---
a/hadoop-ozone/recon/src/main/resources/webapps/recon/ozone-recon-web/src/v2/components/overviewCard/overviewSimpleCard.tsx
+++
b/hadoop-ozone/recon/src/main/resources/webapps/recon/ozone-recon-web/src/v2/components/overviewCard/overviewSimpleCard.tsx
@@ -31,6 +31,7 @@ import {
QuestionCircleOutlined
} from '@ant-design/icons';
import { numberWithCommas } from '@/utils/common';
+import ErrorCard from '@/v2/components/errors/errorCard';
// ------------- Types -------------- //
@@ -40,11 +41,12 @@ type IconOptions = {
type OverviewCardProps = {
icon: string;
- data: number | React.ReactElement;
+ data: number;
title: string;
hoverable?: boolean;
loading?: boolean;
linkToUrl?: string;
+ error?: string | null;
}
// ------------- Styles -------------- //
@@ -109,9 +111,14 @@ const OverviewSimpleCard: React.FC<OverviewCardProps> = ({
title = '',
hoverable = false,
loading = false,
- linkToUrl = ''
+ linkToUrl = '',
+ error
}) => {
+ if (error) {
+ return <ErrorCard title={title} compact={true}/>
+ }
+
const titleElement = (linkToUrl)
? (
<div className='card-title-div'>
@@ -122,7 +129,7 @@ const OverviewSimpleCard: React.FC<OverviewCardProps> = ({
View More
</Link>
</div>)
- : title
+ : title;
return (
<Card
@@ -144,6 +151,6 @@ const OverviewSimpleCard: React.FC<OverviewCardProps> = ({
</Row>
</Card>
);
-}
+};
-export default OverviewSimpleCard;
\ No newline at end of file
+export default OverviewSimpleCard;
diff --git
a/hadoop-ozone/recon/src/main/resources/webapps/recon/ozone-recon-web/src/v2/components/overviewCard/overviewStorageCard.tsx
b/hadoop-ozone/recon/src/main/resources/webapps/recon/ozone-recon-web/src/v2/components/overviewCard/overviewStorageCard.tsx
index 2272f2ca01d..ce9287b3db8 100644
---
a/hadoop-ozone/recon/src/main/resources/webapps/recon/ozone-recon-web/src/v2/components/overviewCard/overviewStorageCard.tsx
+++
b/hadoop-ozone/recon/src/main/resources/webapps/recon/ozone-recon-web/src/v2/components/overviewCard/overviewStorageCard.tsx
@@ -24,11 +24,14 @@ import EChart from '@/v2/components/eChart/eChart';
import OverviewCardWrapper from
'@/v2/components/overviewCard/overviewCardWrapper';
import { StorageReport } from '@/v2/types/overview.types';
+import ErrorMessage from '@/v2/components/errors/errorCard';
+import ErrorCard from '@/v2/components/errors/errorCard';
// ------------- Types -------------- //
type OverviewStorageCardProps = {
loading?: boolean;
storageReport: StorageReport;
+ error?: string | null;
}
const size = filesize.partial({ round: 1 });
@@ -73,9 +76,14 @@ const OverviewStorageCard:
React.FC<OverviewStorageCardProps> = ({
used: 0,
remaining: 0,
committed: 0
- }
+ },
+ error
}) => {
+ if (error) {
+ return <ErrorCard title='Cluster Capacity' />
+ }
+
const {
ozoneUsedPercentage,
nonOzoneUsedPercentage,
diff --git
a/hadoop-ozone/recon/src/main/resources/webapps/recon/ozone-recon-web/src/v2/components/overviewCard/overviewSummaryCard.tsx
b/hadoop-ozone/recon/src/main/resources/webapps/recon/ozone-recon-web/src/v2/components/overviewCard/overviewSummaryCard.tsx
index 8736b3e0d29..9214c456b6c 100644
---
a/hadoop-ozone/recon/src/main/resources/webapps/recon/ozone-recon-web/src/v2/components/overviewCard/overviewSummaryCard.tsx
+++
b/hadoop-ozone/recon/src/main/resources/webapps/recon/ozone-recon-web/src/v2/components/overviewCard/overviewSummaryCard.tsx
@@ -21,6 +21,8 @@ import { Card, Row, Table } from 'antd';
import { ColumnType } from 'antd/es/table';
import { Link } from 'react-router-dom';
+import ErrorMessage from '@/v2/components/errors/errorCard';
+import ErrorCard from '@/v2/components/errors/errorCard';
// ------------- Types -------------- //
type TableData = {
@@ -40,6 +42,7 @@ type OverviewTableCardProps = {
linkToUrl?: string;
showHeader?: boolean;
state?: Record<string, any>;
+ error?: string | null;
}
// ------------- Styles -------------- //
@@ -65,8 +68,14 @@ const OverviewSummaryCard: React.FC<OverviewTableCardProps>
= ({
tableData = [],
linkToUrl = '',
showHeader = false,
- state
+ state,
+ error
}) => {
+
+ if (error) {
+ return <ErrorCard title={title} />;
+ }
+
const titleElement = (linkToUrl)
? (
<div className='card-title-div'>
diff --git
a/hadoop-ozone/recon/src/main/resources/webapps/recon/ozone-recon-web/src/v2/components/tables/insights/containerMismatchTable.tsx
b/hadoop-ozone/recon/src/main/resources/webapps/recon/ozone-recon-web/src/v2/components/tables/insights/containerMismatchTable.tsx
index 818eca37f8e..565acde6db7 100644
---
a/hadoop-ozone/recon/src/main/resources/webapps/recon/ozone-recon-web/src/v2/components/tables/insights/containerMismatchTable.tsx
+++
b/hadoop-ozone/recon/src/main/resources/webapps/recon/ozone-recon-web/src/v2/components/tables/insights/containerMismatchTable.tsx
@@ -39,7 +39,7 @@ import Search from '@/v2/components/search/search';
import SingleSelect, { Option } from '@/v2/components/select/singleSelect';
import { showDataFetchError } from '@/utils/common';
import { AxiosGetHelper } from '@/utils/axiosRequestHelper';
-import { useDebounce } from '@/v2/hooks/debounce.hook';
+import { useDebounce } from '@/v2/hooks/useDebounce';
import { LIMIT_OPTIONS } from '@/v2/constants/limit.constants';
import {
diff --git
a/hadoop-ozone/recon/src/main/resources/webapps/recon/ozone-recon-web/src/v2/components/tables/insights/deletePendingDirsTable.tsx
b/hadoop-ozone/recon/src/main/resources/webapps/recon/ozone-recon-web/src/v2/components/tables/insights/deletePendingDirsTable.tsx
index f0c6fc8161e..190754b9388 100644
---
a/hadoop-ozone/recon/src/main/resources/webapps/recon/ozone-recon-web/src/v2/components/tables/insights/deletePendingDirsTable.tsx
+++
b/hadoop-ozone/recon/src/main/resources/webapps/recon/ozone-recon-web/src/v2/components/tables/insights/deletePendingDirsTable.tsx
@@ -29,7 +29,7 @@ import SingleSelect, { Option } from
'@/v2/components/select/singleSelect';
import { AxiosGetHelper } from '@/utils/axiosRequestHelper';
import { byteToSize, showDataFetchError } from '@/utils/common';
import { getFormattedTime } from '@/v2/utils/momentUtils';
-import { useDebounce } from '@/v2/hooks/debounce.hook';
+import { useDebounce } from '@/v2/hooks/useDebounce';
import { LIMIT_OPTIONS } from '@/v2/constants/limit.constants';
import { DeletedDirInfo } from '@/v2/types/insights.types';
diff --git
a/hadoop-ozone/recon/src/main/resources/webapps/recon/ozone-recon-web/src/v2/components/tables/insights/deletePendingKeysTable.tsx
b/hadoop-ozone/recon/src/main/resources/webapps/recon/ozone-recon-web/src/v2/components/tables/insights/deletePendingKeysTable.tsx
index 65ada495641..81ed9020c2b 100644
---
a/hadoop-ozone/recon/src/main/resources/webapps/recon/ozone-recon-web/src/v2/components/tables/insights/deletePendingKeysTable.tsx
+++
b/hadoop-ozone/recon/src/main/resources/webapps/recon/ozone-recon-web/src/v2/components/tables/insights/deletePendingKeysTable.tsx
@@ -29,7 +29,7 @@ import SingleSelect, { Option } from
'@/v2/components/select/singleSelect';
import ExpandedPendingKeysTable from
'@/v2/components/tables/insights/expandedPendingKeysTable';
import { AxiosGetHelper } from '@/utils/axiosRequestHelper';
import { byteToSize, showDataFetchError } from '@/utils/common';
-import { useDebounce } from '@/v2/hooks/debounce.hook';
+import { useDebounce } from '@/v2/hooks/useDebounce';
import { LIMIT_OPTIONS } from '@/v2/constants/limit.constants';
import {
diff --git
a/hadoop-ozone/recon/src/main/resources/webapps/recon/ozone-recon-web/src/v2/components/tables/insights/deletedContainerKeysTable.tsx
b/hadoop-ozone/recon/src/main/resources/webapps/recon/ozone-recon-web/src/v2/components/tables/insights/deletedContainerKeysTable.tsx
index 9aaf62a63d6..9f665857b88 100644
---
a/hadoop-ozone/recon/src/main/resources/webapps/recon/ozone-recon-web/src/v2/components/tables/insights/deletedContainerKeysTable.tsx
+++
b/hadoop-ozone/recon/src/main/resources/webapps/recon/ozone-recon-web/src/v2/components/tables/insights/deletedContainerKeysTable.tsx
@@ -28,7 +28,7 @@ import Search from '@/v2/components/search/search';
import SingleSelect, { Option } from '@/v2/components/select/singleSelect';
import { AxiosGetHelper } from '@/utils/axiosRequestHelper';
import { showDataFetchError } from '@/utils/common';
-import { useDebounce } from '@/v2/hooks/debounce.hook';
+import { useDebounce } from '@/v2/hooks/useDebounce';
import { LIMIT_OPTIONS } from '@/v2/constants/limit.constants';
import {
diff --git
a/hadoop-ozone/recon/src/main/resources/webapps/recon/ozone-recon-web/src/v2/components/tables/insights/openKeysTable.tsx
b/hadoop-ozone/recon/src/main/resources/webapps/recon/ozone-recon-web/src/v2/components/tables/insights/openKeysTable.tsx
index 02c73c77528..38c57f4cef2 100644
---
a/hadoop-ozone/recon/src/main/resources/webapps/recon/ozone-recon-web/src/v2/components/tables/insights/openKeysTable.tsx
+++
b/hadoop-ozone/recon/src/main/resources/webapps/recon/ozone-recon-web/src/v2/components/tables/insights/openKeysTable.tsx
@@ -36,7 +36,7 @@ import SingleSelect, { Option } from
'@/v2/components/select/singleSelect';
import { AxiosGetHelper } from '@/utils/axiosRequestHelper';
import { byteToSize, showDataFetchError } from '@/utils/common';
import { getFormattedTime } from '@/v2/utils/momentUtils';
-import { useDebounce } from '@/v2/hooks/debounce.hook';
+import { useDebounce } from '@/v2/hooks/useDebounce';
import { LIMIT_OPTIONS } from '@/v2/constants/limit.constants';
import { OpenKeys, OpenKeysResponse } from '@/v2/types/insights.types';
diff --git
a/hadoop-ozone/recon/src/main/resources/webapps/recon/ozone-recon-web/src/v2/constants/overview.constants.tsx
b/hadoop-ozone/recon/src/main/resources/webapps/recon/ozone-recon-web/src/v2/constants/overview.constants.tsx
new file mode 100644
index 00000000000..0429580c8e0
--- /dev/null
+++
b/hadoop-ozone/recon/src/main/resources/webapps/recon/ozone-recon-web/src/v2/constants/overview.constants.tsx
@@ -0,0 +1,50 @@
+/*
+* Licensed to the Apache Software Foundation (ASF) under one
+* or more contributor license agreements. See the NOTICE file
+* distributed with this work for additional information
+* regarding copyright ownership. The ASF licenses this file
+* to you under the Apache License, Version 2.0 (the
+* "License"); you may not use this file except in compliance
+* with the License. You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+
+import { ClusterStateResponse, KeysSummary, TaskStatus } from
"@/v2/types/overview.types";
+
+export const DEFAULT_CLUSTER_STATE: ClusterStateResponse = {
+ missingContainers: 0,
+ totalDatanodes: 0,
+ healthyDatanodes: 0,
+ pipelines: 0,
+ storageReport: { capacity: 0, used: 0, remaining: 0, committed: 0 },
+ containers: 0,
+ volumes: 0,
+ buckets: 0,
+ keys: 0,
+ openContainers: 0,
+ deletedContainers: 0,
+ keysPendingDeletion: 0,
+ scmServiceId: 'N/A',
+ omServiceId: 'N/A'
+};
+
+export const DEFAULT_TASK_STATUS: TaskStatus[] = [];
+
+export const DEFAULT_OPEN_KEYS_SUMMARY: KeysSummary & {totalOpenKeys: number}
= {
+ totalUnreplicatedDataSize: 0,
+ totalReplicatedDataSize: 0,
+ totalOpenKeys: 0
+};
+
+export const DEFAULT_DELETE_PENDING_KEYS_SUMMARY: KeysSummary &
{totalDeletedKeys: number} = {
+ totalUnreplicatedDataSize: 0,
+ totalReplicatedDataSize: 0,
+ totalDeletedKeys: 0
+};
diff --git
a/hadoop-ozone/recon/src/main/resources/webapps/recon/ozone-recon-web/src/v2/hooks/useAPIData.hook.ts
b/hadoop-ozone/recon/src/main/resources/webapps/recon/ozone-recon-web/src/v2/hooks/useAPIData.hook.ts
new file mode 100644
index 00000000000..dfcdec0cefa
--- /dev/null
+++
b/hadoop-ozone/recon/src/main/resources/webapps/recon/ozone-recon-web/src/v2/hooks/useAPIData.hook.ts
@@ -0,0 +1,187 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import { useState, useEffect, useRef } from 'react';
+import { AxiosGetHelper } from '@/utils/axiosRequestHelper';
+
+export interface ApiState<T> {
+ data: T;
+ loading: boolean;
+ error: string | null;
+ lastUpdated: number | null;
+}
+
+export interface UseApiDataOptions {
+ retryAttempts?: number;
+ retryDelay?: number;
+ initialFetch?: boolean;
+ onError?: (error: string) => void;
+};
+
+export function useApiData<T>(
+ url: string,
+ defaultValue: T,
+ options: UseApiDataOptions = {}
+): ApiState<T> & {
+ refetch: () => void;
+ clearError: () => void;
+} {
+ const {
+ retryAttempts = 3,
+ retryDelay = 1000,
+ initialFetch = true,
+ onError
+ } = options;
+
+ const [state, setState] = useState<ApiState<T>>({
+ data: defaultValue,
+ loading: initialFetch,
+ error: null,
+ lastUpdated: null
+ });
+
+ const controllerRef = useRef<AbortController>();
+ const retryCountRef = useRef(0);
+ const retryTimeoutRef = useRef<NodeJS.Timeout>();
+
+ // Store stable references
+ const urlRef = useRef(url);
+ const retryAttemptsRef = useRef(retryAttempts);
+ const retryDelayRef = useRef(retryDelay);
+ const onErrorRef = useRef(onError);
+
+ // Update refs when props change
+ useEffect(() => {
+ urlRef.current = url;
+ }, [url]);
+
+ useEffect(() => {
+ retryAttemptsRef.current = retryAttempts;
+ }, [retryAttempts]);
+
+ useEffect(() => {
+ retryDelayRef.current = retryDelay;
+ }, [retryDelay]);
+
+ useEffect(() => {
+ onErrorRef.current = onError;
+ }, [onError]);
+
+
+ const fetchData = async (isRetry = false) => {
+ if (!isRetry) {
+ setState(prev => ({ ...prev, loading: true, error: null }));
+ retryCountRef.current = 0;
+ }
+
+ try {
+ const { request, controller } = AxiosGetHelper(
+ urlRef.current,
+ controllerRef.current,
+ 'Request cancelled due to component unmount or new request'
+ );
+ controllerRef.current = controller;
+
+ const response = await request;
+
+ setState({
+ data: response.data,
+ loading: false,
+ error: null,
+ lastUpdated: Date.now()
+ });
+
+ retryCountRef.current = 0;
+ } catch (error: any) {
+ if (error.name === 'CanceledError') {
+ return;
+ }
+
+ const errorMessage = error.response?.data?.message ||
+ error.response?.statusText ||
+ error.message ||
+ `Request failed with status:
${error.response?.status || 'unknown'}`;
+
+ // Clear any existing retry timeout
+ if (retryTimeoutRef.current) {
+ clearTimeout(retryTimeoutRef.current);
+ }
+
+ // Retry logic for network errors and 5xx errors
+ if (retryCountRef.current < retryAttemptsRef.current &&
+ (!error.response?.status || error.response?.status >= 500)) {
+ retryCountRef.current++;
+ retryTimeoutRef.current = setTimeout(() => {
+ fetchData(true);
+ }, retryDelayRef.current * retryCountRef.current);
+ return;
+ }
+
+ if (onErrorRef.current) {
+ onErrorRef.current(errorMessage);
+ }
+
+ setState({
+ data: defaultValue,
+ loading: false,
+ error: errorMessage,
+ lastUpdated: Date.now()
+ });
+ }
+ };
+
+ const refetch = () => {
+ fetchData();
+ };
+
+ const clearError = () => {
+ setState(prev => ({ ...prev, error: null }));
+ };
+
+ // Initial fetch only
+ useEffect(() => {
+ if (initialFetch) {
+ fetchData();
+ }
+
+ // Cleanup retry timeout on unmount
+ return () => {
+ if (retryTimeoutRef.current) {
+ clearTimeout(retryTimeoutRef.current);
+ }
+ };
+ }, []); // Empty dependency array
+
+ // Cleanup on unmount
+ useEffect(() => {
+ return () => {
+ if (controllerRef.current) {
+ controllerRef.current.abort('Component unmounted');
+ }
+ if (retryTimeoutRef.current) {
+ clearTimeout(retryTimeoutRef.current);
+ }
+ };
+ }, []);
+
+ return {
+ ...state,
+ refetch,
+ clearError
+ };
+}
diff --git
a/hadoop-ozone/recon/src/main/resources/webapps/recon/ozone-recon-web/src/v2/hooks/useAutoReload.hook.tsx
b/hadoop-ozone/recon/src/main/resources/webapps/recon/ozone-recon-web/src/v2/hooks/useAutoReload.hook.tsx
new file mode 100644
index 00000000000..baa8190bfc9
--- /dev/null
+++
b/hadoop-ozone/recon/src/main/resources/webapps/recon/ozone-recon-web/src/v2/hooks/useAutoReload.hook.tsx
@@ -0,0 +1,89 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import { useEffect, useRef, useState } from 'react';
+import { AUTO_RELOAD_INTERVAL_DEFAULT } from
'@/constants/autoReload.constants';
+
+export function useAutoReload(
+ refreshFunction: () => void,
+ interval: number = AUTO_RELOAD_INTERVAL_DEFAULT
+) {
+ const intervalRef = useRef<number>(0);
+ const [isPolling, setIsPolling] = useState<boolean>(false);
+ const refreshFunctionRef = useRef(refreshFunction);
+ const lastPollCallRef = useRef<number>(0); // This is used to store the last
time poll was called
+
+ // Update the ref when the function changes
+ refreshFunctionRef.current = refreshFunction;
+
+ const stopPolling = () => {
+ if (intervalRef.current > 0) {
+ clearTimeout(intervalRef.current);
+ intervalRef.current = 0;
+ setIsPolling(false);
+ }
+ };
+
+ const startPolling = () => {
+ stopPolling();
+ const poll = () => {
+ /**
+ * Prevent any extra polling calls within 100ms of the last call,
+ * This is done in case at any place multiple API calls are made, for
example
+ * the useEffect on mount in this component will call the startPolling()
function.
+ * If this startPolling() function is called elsewhere in a different
component then
+ * race condition can occur where this gets called in succession
multiple times.
+ */
+ if (Date.now() - lastPollCallRef.current > 100) {
+ refreshFunctionRef.current();
+ lastPollCallRef.current = Date.now();
+ }
+ intervalRef.current = window.setTimeout(poll, interval);
+ };
+ poll();
+ setIsPolling(true);
+ };
+
+ const handleAutoReloadToggle = (checked: boolean) => {
+ sessionStorage.setItem('autoReloadEnabled', JSON.stringify(checked));
+ if (checked) {
+ startPolling();
+ } else {
+ stopPolling();
+ }
+ };
+
+ // Initialize polling on mount if auto-reload is enabled
+ useEffect(() => {
+ const autoReloadEnabled = sessionStorage.getItem('autoReloadEnabled') !==
'false';
+ if (autoReloadEnabled) {
+ startPolling();
+ }
+
+ return () => {
+ stopPolling();
+ };
+ }, []); // Empty dependency array
+
+ return {
+ startPolling,
+ stopPolling,
+ isPolling,
+ handleAutoReloadToggle
+ };
+}
diff --git
a/hadoop-ozone/recon/src/main/resources/webapps/recon/ozone-recon-web/src/v2/hooks/debounce.hook.tsx
b/hadoop-ozone/recon/src/main/resources/webapps/recon/ozone-recon-web/src/v2/hooks/useDebounce.tsx
similarity index 100%
rename from
hadoop-ozone/recon/src/main/resources/webapps/recon/ozone-recon-web/src/v2/hooks/debounce.hook.tsx
rename to
hadoop-ozone/recon/src/main/resources/webapps/recon/ozone-recon-web/src/v2/hooks/useDebounce.tsx
diff --git
a/hadoop-ozone/recon/src/main/resources/webapps/recon/ozone-recon-web/src/v2/pages/buckets/buckets.tsx
b/hadoop-ozone/recon/src/main/resources/webapps/recon/ozone-recon-web/src/v2/pages/buckets/buckets.tsx
index 1c039f42709..7d2c77de3c3 100644
---
a/hadoop-ozone/recon/src/main/resources/webapps/recon/ozone-recon-web/src/v2/pages/buckets/buckets.tsx
+++
b/hadoop-ozone/recon/src/main/resources/webapps/recon/ozone-recon-web/src/v2/pages/buckets/buckets.tsx
@@ -32,7 +32,7 @@ import { AutoReloadHelper } from '@/utils/autoReloadHelper';
import { AxiosGetHelper, cancelRequests } from "@/utils/axiosRequestHelper";
import { showDataFetchError } from '@/utils/common';
import { LIMIT_OPTIONS } from '@/v2/constants/limit.constants';
-import { useDebounce } from '@/v2/hooks/debounce.hook';
+import { useDebounce } from '@/v2/hooks/useDebounce';
import {
Bucket,
diff --git
a/hadoop-ozone/recon/src/main/resources/webapps/recon/ozone-recon-web/src/v2/pages/containers/containers.tsx
b/hadoop-ozone/recon/src/main/resources/webapps/recon/ozone-recon-web/src/v2/pages/containers/containers.tsx
index 5cdbdd52625..3a784bb9932 100644
---
a/hadoop-ozone/recon/src/main/resources/webapps/recon/ozone-recon-web/src/v2/pages/containers/containers.tsx
+++
b/hadoop-ozone/recon/src/main/resources/webapps/recon/ozone-recon-web/src/v2/pages/containers/containers.tsx
@@ -29,7 +29,7 @@ import AutoReloadPanel from
"@/components/autoReloadPanel/autoReloadPanel";
import { showDataFetchError } from "@/utils/common";
import { AutoReloadHelper } from "@/utils/autoReloadHelper";
import { AxiosGetHelper, cancelRequests } from "@/utils/axiosRequestHelper";
-import { useDebounce } from "@/v2/hooks/debounce.hook";
+import { useDebounce } from "@/v2/hooks/useDebounce";
import {
Container,
diff --git
a/hadoop-ozone/recon/src/main/resources/webapps/recon/ozone-recon-web/src/v2/pages/datanodes/datanodes.tsx
b/hadoop-ozone/recon/src/main/resources/webapps/recon/ozone-recon-web/src/v2/pages/datanodes/datanodes.tsx
index 33dd661d97b..7c044b2ae1e 100644
---
a/hadoop-ozone/recon/src/main/resources/webapps/recon/ozone-recon-web/src/v2/pages/datanodes/datanodes.tsx
+++
b/hadoop-ozone/recon/src/main/resources/webapps/recon/ozone-recon-web/src/v2/pages/datanodes/datanodes.tsx
@@ -45,7 +45,7 @@ import {
cancelRequests
} from '@/utils/axiosRequestHelper';
-import { useDebounce } from '@/v2/hooks/debounce.hook';
+import { useDebounce } from '@/v2/hooks/useDebounce';
import {
Datanode,
DatanodeDecomissionInfo,
diff --git
a/hadoop-ozone/recon/src/main/resources/webapps/recon/ozone-recon-web/src/v2/pages/overview/overview.tsx
b/hadoop-ozone/recon/src/main/resources/webapps/recon/ozone-recon-web/src/v2/pages/overview/overview.tsx
index 6014577f90a..70c774cd441 100644
---
a/hadoop-ozone/recon/src/main/resources/webapps/recon/ozone-recon-web/src/v2/pages/overview/overview.tsx
+++
b/hadoop-ozone/recon/src/main/resources/webapps/recon/ozone-recon-web/src/v2/pages/overview/overview.tsx
@@ -16,31 +16,28 @@
* limitations under the License.
*/
-import React, { useEffect, useRef, useState } from 'react';
-import moment from 'moment';
-import filesize from 'filesize';
-import axios from 'axios';
+import React, { useState, useRef, useEffect } from 'react';
import { Row, Col, Button } from 'antd';
-import {
- CheckCircleFilled,
- WarningFilled
-} from '@ant-design/icons';
+import { CheckCircleFilled, WarningFilled } from '@ant-design/icons';
import { Link } from 'react-router-dom';
+import moment from 'moment';
+import filesize from 'filesize';
import AutoReloadPanel from '@/components/autoReloadPanel/autoReloadPanel';
+import OverviewSimpleCard from
'@/v2/components/overviewCard/overviewSimpleCard';
import OverviewSummaryCard from
'@/v2/components/overviewCard/overviewSummaryCard';
import OverviewStorageCard from
'@/v2/components/overviewCard/overviewStorageCard';
-import OverviewSimpleCard from
'@/v2/components/overviewCard/overviewSimpleCard';
-
-import { AutoReloadHelper } from '@/utils/autoReloadHelper';
-import { checkResponseError, showDataFetchError } from '@/utils/common';
-import { AxiosGetHelper, cancelRequests, PromiseAllSettledGetHelper } from
'@/utils/axiosRequestHelper';
-
-import { ClusterStateResponse, OverviewState, StorageReport } from
'@/v2/types/overview.types';
+import { AxiosGetHelper } from '@/utils/axiosRequestHelper';
+import { showDataFetchError } from '@/utils/common';
+import { cancelRequests } from '@/utils/axiosRequestHelper';
+import { useApiData } from '@/v2/hooks/useAPIData.hook';
+import { useAutoReload } from '@/v2/hooks/useAutoReload.hook';
+import * as CONSTANTS from '@/v2/constants/overview.constants';
+import { ClusterStateResponse, KeysSummary, OverviewState, TaskStatus } from
'@/v2/types/overview.types';
import './overview.less';
-
+// ------------- Helper Functions -------------- //
const size = filesize.partial({ round: 1 });
const getHealthIcon = (value: string): React.ReactElement => {
@@ -82,165 +79,70 @@ const getSummaryTableValue = (
return size(value as number)
}
+// ------------- Main Component -------------- //
const Overview: React.FC<{}> = () => {
-
- const cancelOverviewSignal = useRef<AbortController>();
const cancelOMDBSyncSignal = useRef<AbortController>();
-
const [state, setState] = useState<OverviewState>({
- loading: false,
- datanodes: '',
- pipelines: 0,
- containers: 0,
- volumes: 0,
- buckets: 0,
- keys: 0,
- missingContainersCount: 0,
- lastRefreshed: 0,
- lastUpdatedOMDBDelta: 0,
- lastUpdatedOMDBFull: 0,
omStatus: '',
- openContainers: 0,
- deletedContainers: 0,
- openSummarytotalUnrepSize: 0,
- openSummarytotalRepSize: 0,
- openSummarytotalOpenKeys: 0,
- deletePendingSummarytotalUnrepSize: 0,
- deletePendingSummarytotalRepSize: 0,
- deletePendingSummarytotalDeletedKeys: 0,
- scmServiceId: '',
- omServiceId: ''
- })
- const [storageReport, setStorageReport] = useState<StorageReport>({
- capacity: 0,
- used: 0,
- remaining: 0,
- committed: 0
- })
+ lastRefreshed: 0
+ });
- // Component mounted, fetch initial data
- useEffect(() => {
- loadOverviewPageData();
- autoReloadHelper.startPolling();
- return (() => {
- // Component will Un-mount
- autoReloadHelper.stopPolling();
- cancelRequests([
- cancelOMDBSyncSignal.current!,
- cancelOverviewSignal.current!
- ]);
- })
- }, [])
-
- const loadOverviewPageData = () => {
- setState({
- ...state,
- loading: true
- });
-
- // Cancel any previous pending requests
- cancelRequests([
- cancelOMDBSyncSignal.current!,
- cancelOverviewSignal.current!
- ]);
+ // Individual API calls using custom hook (no auto-refresh)
+ const clusterState = useApiData<ClusterStateResponse>(
+ '/api/v1/clusterState',
+ CONSTANTS.DEFAULT_CLUSTER_STATE,
+ {
+ retryAttempts: 2,
+ initialFetch: false,
+ onError: (error) => showDataFetchError(error)
+ }
+ );
- const { requests, controller } = PromiseAllSettledGetHelper([
- '/api/v1/clusterState',
- '/api/v1/task/status',
- '/api/v1/keys/open/summary',
- '/api/v1/keys/deletePending/summary'
- ], cancelOverviewSignal.current);
- cancelOverviewSignal.current = controller;
+ const taskStatus = useApiData<TaskStatus[]>(
+ '/api/v1/task/status',
+ CONSTANTS.DEFAULT_TASK_STATUS,
+ {
+ retryAttempts: 2,
+ initialFetch: false,
+ onError: (error) => showDataFetchError(error)
+ }
+ );
- requests.then(axios.spread((
- clusterStateResponse: Awaited<Promise<any>>,
- taskstatusResponse: Awaited<Promise<any>>,
- openResponse: Awaited<Promise<any>>,
- deletePendingResponse: Awaited<Promise<any>>
- ) => {
+ const openKeysSummary = useApiData<KeysSummary & { totalOpenKeys: number}>(
+ '/api/v1/keys/open/summary',
+ CONSTANTS.DEFAULT_OPEN_KEYS_SUMMARY,
+ {
+ retryAttempts: 2,
+ initialFetch: false,
+ onError: (error) => showDataFetchError(error)
+ }
+ );
- checkResponseError([
- clusterStateResponse,
- taskstatusResponse,
- openResponse,
- deletePendingResponse
- ]);
+ const deletePendingKeysSummary = useApiData<KeysSummary & {
totalDeletedKeys: number}>(
+ '/api/v1/keys/deletePending/summary',
+ CONSTANTS.DEFAULT_DELETE_PENDING_KEYS_SUMMARY,
+ {
+ retryAttempts: 2,
+ initialFetch: false,
+ onError: (error) => showDataFetchError(error)
+ }
+ );
- const clusterState: ClusterStateResponse =
clusterStateResponse.value?.data ?? {
- missingContainers: 'N/A',
- totalDatanodes: 'N/A',
- healthyDatanodes: 'N/A',
- pipelines: 'N/A',
- storageReport: {
- capacity: 0,
- used: 0,
- remaining: 0,
- committed: 0
- },
- containers: 'N/A',
- volumes: 'N/A',
- buckets: 'N/A',
- keys: 'N/A',
- openContainers: 'N/A',
- deletedContainers: 'N/A',
- keysPendingDeletion: 'N/A',
- scmServiceId: 'N/A',
- omServiceId: 'N/A',
- };
- const taskStatus = taskstatusResponse.value?.data ?? [{
- taskName: 'N/A',
- lastUpdatedTimestamp: 0,
- lastUpdatedSeqNumber: 0
- }];
- const missingContainersCount = clusterState.missingContainers;
- const omDBDeltaObject = taskStatus && taskStatus.find((item: any) =>
item.taskName === 'OmDeltaRequest');
- const omDBFullObject = taskStatus && taskStatus.find((item: any) =>
item.taskName === 'OmSnapshotRequest');
+ const omDBDeltaObject = taskStatus.data?.find((item: TaskStatus) =>
item.taskName === 'OmDeltaRequest');
+ const omDBFullObject = taskStatus.data?.find((item: TaskStatus) =>
item.taskName === 'OmSnapshotRequest');
- setState({
- ...state,
- loading: false,
- datanodes:
`${clusterState.healthyDatanodes}/${clusterState.totalDatanodes}`,
- pipelines: clusterState.pipelines,
- containers: clusterState.containers,
- volumes: clusterState.volumes,
- buckets: clusterState.buckets,
- keys: clusterState.keys,
- missingContainersCount: missingContainersCount,
- openContainers: clusterState.openContainers,
- deletedContainers: clusterState.deletedContainers,
- lastRefreshed: Number(moment()),
- lastUpdatedOMDBDelta: omDBDeltaObject?.lastUpdatedTimestamp,
- lastUpdatedOMDBFull: omDBFullObject?.lastUpdatedTimestamp,
- openSummarytotalUnrepSize:
openResponse?.value?.data?.totalUnreplicatedDataSize,
- openSummarytotalRepSize:
openResponse?.value?.data?.totalReplicatedDataSize,
- openSummarytotalOpenKeys: openResponse?.value?.data?.totalOpenKeys,
- deletePendingSummarytotalUnrepSize:
deletePendingResponse?.value?.data?.totalUnreplicatedDataSize,
- deletePendingSummarytotalRepSize:
deletePendingResponse?.value?.data?.totalReplicatedDataSize,
- deletePendingSummarytotalDeletedKeys:
deletePendingResponse?.value?.data?.totalDeletedKeys,
- scmServiceId: clusterState?.scmServiceId ?? 'N/A',
- omServiceId: clusterState?.omServiceId ?? 'N/A'
- });
- setStorageReport({
- ...storageReport,
- ...clusterState.storageReport
- });
- })).catch((error: Error) => {
- setState({
- ...state,
- loading: false
- });
- showDataFetchError(error.toString());
- });
- }
-
- let autoReloadHelper: AutoReloadHelper = new
AutoReloadHelper(loadOverviewPageData);
+ const loadOverviewPageData = () => {
+ clusterState.refetch();
+ taskStatus.refetch();
+ openKeysSummary.refetch();
+ deletePendingKeysSummary.refetch();
+ setState(prev => ({ ...prev, lastRefreshed: Number(moment()) }));
+ };
+
+ const autoReload = useAutoReload(loadOverviewPageData);
+ // OM DB Sync function
const syncOmData = () => {
- setState({
- ...state,
- loading: true
- });
-
const { request, controller } = AxiosGetHelper(
'/api/v1/triggerdbsync/om',
cancelOMDBSyncSignal.current,
@@ -250,56 +152,36 @@ const Overview: React.FC<{}> = () => {
request.then(omStatusResponse => {
const omStatus = omStatusResponse.data;
- setState({
- ...state,
- loading: false,
- omStatus: omStatus
- });
+ setState(prev => ({ ...prev, omStatus }));
}).catch((error: Error) => {
- setState({
- ...state,
- loading: false
- });
showDataFetchError(error.toString());
});
};
- const {
- loading, datanodes, pipelines,
- containers, volumes, buckets,
- openSummarytotalUnrepSize,
- openSummarytotalRepSize,
- openSummarytotalOpenKeys,
- deletePendingSummarytotalUnrepSize,
- deletePendingSummarytotalRepSize,
- deletePendingSummarytotalDeletedKeys,
- keys, missingContainersCount,
- lastRefreshed, lastUpdatedOMDBDelta,
- lastUpdatedOMDBFull,
- omStatus, openContainers,
- deletedContainers, scmServiceId, omServiceId
- } = state;
+ useEffect(() => {
+ return () => {
+ cancelRequests([cancelOMDBSyncSignal.current!]);
+ };
+ }, []);
const healthCardIndicators = (
<>
<Col span={14}>
Datanodes
- {getHealthIcon(datanodes)}
+
{getHealthIcon(`${clusterState.data?.healthyDatanodes}/${clusterState.data?.totalDatanodes}`)}
</Col>
<Col span={10}>
Containers
- {getHealthIcon(`${(containers -
missingContainersCount)}/${containers}`)}
+ {getHealthIcon(`${(clusterState.data?.containers || 0) -
(clusterState.data?.missingContainers || 0)}/${clusterState.data?.containers}`)}
</Col>
</>
- )
+ );
const datanodesLink = (
- <Button
- type='link'
- size='small'>
+ <Button type='link' size='small'>
<Link to='/Datanodes'> View More </Link>
</Button>
- )
+ );
const containersLink = (
<Button
@@ -309,13 +191,39 @@ const Overview: React.FC<{}> = () => {
</Button>
)
+ const loading = clusterState.loading || taskStatus.loading ||
openKeysSummary.loading || deletePendingKeysSummary.loading;
+ const {
+ healthyDatanodes,
+ totalDatanodes,
+ containers,
+ missingContainers,
+ storageReport,
+ volumes,
+ buckets,
+ keys,
+ pipelines,
+ deletedContainers,
+ omServiceId,
+ scmServiceId
+ } = clusterState.data;
+ const {
+ totalReplicatedDataSize: openSummarytotalRepSize,
+ totalUnreplicatedDataSize: openSummarytotalUnrepSize,
+ totalOpenKeys: openSummarytotalOpenKeys,
+ } = openKeysSummary.data ?? {};
+ const {
+ totalReplicatedDataSize: deletePendingSummarytotalRepSize,
+ totalUnreplicatedDataSize: deletePendingSummarytotalUnrepSize,
+ totalDeletedKeys: deletePendingSummarytotalDeletedKeys
+ } = deletePendingKeysSummary.data ?? {};
+
return (
<>
<div className='page-header-v2'>
Overview
- <AutoReloadPanel isLoading={loading} lastRefreshed={lastRefreshed}
- lastUpdatedOMDBDelta={lastUpdatedOMDBDelta}
lastUpdatedOMDBFull={lastUpdatedOMDBFull}
- togglePolling={autoReloadHelper.handleAutoReloadToggle}
onReload={loadOverviewPageData} omSyncLoad={syncOmData} omStatus={omStatus} />
+ <AutoReloadPanel isLoading={loading}
lastRefreshed={state.lastRefreshed}
+ lastUpdatedOMDBDelta={omDBDeltaObject?.lastUpdatedTimestamp}
lastUpdatedOMDBFull={omDBFullObject?.lastUpdatedTimestamp}
+ togglePolling={autoReload.handleAutoReloadToggle}
onReload={loadOverviewPageData} omSyncLoad={syncOmData}
omStatus={state.omStatus} />
</div>
<div className='data-container'>
<Row
@@ -333,6 +241,7 @@ const Overview: React.FC<{}> = () => {
title='Health'
data={healthCardIndicators}
showHeader={true}
+ loading={clusterState.loading}
columns={[
{
title: '',
@@ -356,20 +265,21 @@ const Overview: React.FC<{}> = () => {
{
key: 'datanodes',
name: 'Datanodes',
- value: datanodes,
+ value: `${healthyDatanodes}/${totalDatanodes}`,
action: datanodesLink
},
{
key: 'containers',
name: 'Containers',
- value: `${(containers -
missingContainersCount)}/${containers}`,
+ value: `${containers - missingContainers}/${containers}`,
action: containersLink
}
]}
+ error={clusterState.error}
/>
</Col>
<Col xs={24} sm={24} md={24} lg={14} xl={14}>
- <OverviewStorageCard storageReport={storageReport}
loading={loading} />
+ <OverviewStorageCard storageReport={storageReport}
loading={loading} error={clusterState.error}/>
</Col>
</Row>
<Row gutter={[
@@ -384,39 +294,44 @@ const Overview: React.FC<{}> = () => {
<OverviewSimpleCard
title='Volumes'
icon='inbox'
- loading={loading}
+ loading={clusterState.loading}
data={volumes}
- linkToUrl='/Volumes' />
+ linkToUrl='/Volumes'
+ error={clusterState.error} />
</Col>
<Col flex="1 0 20%">
<OverviewSimpleCard
title='Buckets'
icon='folder-open'
- loading={loading}
+ loading={clusterState.loading}
data={buckets}
- linkToUrl='/Buckets' />
+ linkToUrl='/Buckets'
+ error={clusterState.error} />
</Col>
<Col flex="1 0 20%">
<OverviewSimpleCard
title='Keys'
icon='file-text'
- loading={loading}
- data={keys} />
+ loading={clusterState.loading}
+ data={keys}
+ error={clusterState.error} />
</Col>
<Col flex="1 0 20%">
<OverviewSimpleCard
title='Pipelines'
icon='deployment-unit'
- loading={loading}
+ loading={clusterState.loading}
data={pipelines}
- linkToUrl='/Pipelines' />
+ linkToUrl='/Pipelines'
+ error={clusterState.error} />
</Col>
<Col flex="1 0 20%">
<OverviewSimpleCard
title='Deleted Containers'
icon='delete'
- loading={loading}
- data={deletedContainers} />
+ loading={clusterState.loading}
+ data={deletedContainers}
+ error={clusterState.error} />
</Col>
</Row>
<Row gutter={[
@@ -430,7 +345,7 @@ const Overview: React.FC<{}> = () => {
<Col xs={24} sm={24} md={24} lg={12} xl={12}>
<OverviewSummaryCard
title='Open Keys Summary'
- loading={loading}
+ loading={openKeysSummary.loading}
columns={[
{
title: 'Name',
@@ -465,12 +380,13 @@ const Overview: React.FC<{}> = () => {
}
]}
linkToUrl='/Om'
- state={{activeTab: '2'}} />
+ state={{activeTab: '2'}}
+ error={openKeysSummary.error} />
</Col>
<Col xs={24} sm={24} md={24} lg={12} xl={12}>
<OverviewSummaryCard
title='Delete Pending Keys Summary'
- loading={loading}
+ loading={deletePendingKeysSummary.loading}
columns={[
{
title: 'Name',
@@ -505,7 +421,8 @@ const Overview: React.FC<{}> = () => {
}
]}
linkToUrl='/Om'
- state={{activeTab: '3'}} />
+ state={{activeTab: '3'}}
+ error={deletePendingKeysSummary.error} />
</Col>
</Row>
<span style={{ paddingLeft: '8px' }}>
@@ -522,4 +439,4 @@ const Overview: React.FC<{}> = () => {
);
}
-export default Overview;
\ No newline at end of file
+export default Overview;
diff --git
a/hadoop-ozone/recon/src/main/resources/webapps/recon/ozone-recon-web/src/v2/pages/pipelines/pipelines.tsx
b/hadoop-ozone/recon/src/main/resources/webapps/recon/ozone-recon-web/src/v2/pages/pipelines/pipelines.tsx
index f6ff87c7e13..19a3c9447a6 100644
---
a/hadoop-ozone/recon/src/main/resources/webapps/recon/ozone-recon-web/src/v2/pages/pipelines/pipelines.tsx
+++
b/hadoop-ozone/recon/src/main/resources/webapps/recon/ozone-recon-web/src/v2/pages/pipelines/pipelines.tsx
@@ -31,7 +31,7 @@ import PipelinesTable, { COLUMNS } from
'@/v2/components/tables/pipelinesTable';
import { showDataFetchError } from '@/utils/common';
import { AutoReloadHelper } from '@/utils/autoReloadHelper';
import { AxiosGetHelper, cancelRequests } from '@/utils/axiosRequestHelper';
-import { useDebounce } from '@/v2/hooks/debounce.hook';
+import { useDebounce } from '@/v2/hooks/useDebounce';
import {
Pipeline,
diff --git
a/hadoop-ozone/recon/src/main/resources/webapps/recon/ozone-recon-web/src/v2/pages/volumes/volumes.tsx
b/hadoop-ozone/recon/src/main/resources/webapps/recon/ozone-recon-web/src/v2/pages/volumes/volumes.tsx
index b4614d387f3..a10b71282ce 100644
---
a/hadoop-ozone/recon/src/main/resources/webapps/recon/ozone-recon-web/src/v2/pages/volumes/volumes.tsx
+++
b/hadoop-ozone/recon/src/main/resources/webapps/recon/ozone-recon-web/src/v2/pages/volumes/volumes.tsx
@@ -31,7 +31,7 @@ import { showDataFetchError } from '@/utils/common';
import { AutoReloadHelper } from '@/utils/autoReloadHelper';
import { AxiosGetHelper, cancelRequests } from "@/utils/axiosRequestHelper";
import { LIMIT_OPTIONS } from '@/v2/constants/limit.constants';
-import { useDebounce } from '@/v2/hooks/debounce.hook';
+import { useDebounce } from '@/v2/hooks/useDebounce';
import {
Volume,
diff --git
a/hadoop-ozone/recon/src/main/resources/webapps/recon/ozone-recon-web/src/v2/types/overview.types.ts
b/hadoop-ozone/recon/src/main/resources/webapps/recon/ozone-recon-web/src/v2/types/overview.types.ts
index f8390fd4346..ef3043f8955 100644
---
a/hadoop-ozone/recon/src/main/resources/webapps/recon/ozone-recon-web/src/v2/types/overview.types.ts
+++
b/hadoop-ozone/recon/src/main/resources/webapps/recon/ozone-recon-web/src/v2/types/overview.types.ts
@@ -33,29 +33,15 @@ export type ClusterStateResponse = {
omServiceId: string;
}
-export type OverviewState = {
- loading: boolean;
- datanodes: string;
- pipelines: number;
- containers: number;
- volumes: number;
- buckets: number;
- keys: number;
- missingContainersCount: number;
- lastRefreshed: number;
- lastUpdatedOMDBDelta: number;
- lastUpdatedOMDBFull: number;
- omStatus: string;
- openContainers: number;
- deletedContainers: number;
- openSummarytotalUnrepSize: number;
- openSummarytotalRepSize: number;
- openSummarytotalOpenKeys: number;
- deletePendingSummarytotalUnrepSize: number;
- deletePendingSummarytotalRepSize: number;
- deletePendingSummarytotalDeletedKeys: number;
- scmServiceId: string;
- omServiceId: string;
+export type TaskStatus = {
+ taskName: 'OmDeltaRequest' | 'OmSnapshotRequest' | string;
+ lastUpdatedTimestamp: number;
+ lastUpdatedSeqNumber: number;
+}
+
+export type KeysSummary = {
+ totalUnreplicatedDataSize: number;
+ totalReplicatedDataSize: number;
}
export type StorageReport = {
@@ -64,3 +50,8 @@ export type StorageReport = {
remaining: number;
committed: number;
}
+
+export type OverviewState = {
+ omStatus: string;
+ lastRefreshed: number;
+}
\ No newline at end of file
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]