From f213df7e717af22c239de6bb451c00a720d27f4d Mon Sep 17 00:00:00 2001 From: Abhishek Pal Date: Mon, 15 Jul 2024 12:15:23 +0530 Subject: [PATCH 1/9] Migrate AxiosAll to PromiseAllSettled and added fallback for error in fetching data --- .../src/utils/axiosRequestHelper.tsx | 7 ++- .../src/views/insights/insights.tsx | 35 ++++++++--- .../src/views/overview/overview.tsx | 62 +++++++++++++++---- 3 files changed, 80 insertions(+), 24 deletions(-) diff --git a/hadoop-ozone/recon/src/main/resources/webapps/recon/ozone-recon-web/src/utils/axiosRequestHelper.tsx b/hadoop-ozone/recon/src/main/resources/webapps/recon/ozone-recon-web/src/utils/axiosRequestHelper.tsx index 45fa3b58b6da..836ff499529c 100644 --- a/hadoop-ozone/recon/src/main/resources/webapps/recon/ozone-recon-web/src/utils/axiosRequestHelper.tsx +++ b/hadoop-ozone/recon/src/main/resources/webapps/recon/ozone-recon-web/src/utils/axiosRequestHelper.tsx @@ -48,11 +48,11 @@ export const AxiosPutHelper = ( } } -export const AxiosAllGetHelper = ( +export const PromiseAllSettledGetHelper = ( urls: string[], controller: AbortController, message: string = '' -): { requests: Promise[]>; controller: AbortController } => { +): { requests: Promise>[]>; controller: AbortController } => { controller && controller.abort(message); controller = new AbortController(); // generate new AbortController for the upcoming request @@ -63,8 +63,9 @@ export const AxiosAllGetHelper = ( axiosGetRequests.push(axios.get(url, { signal: controller.signal })) }); + console.log(axiosGetRequests); return { - requests: axios.all(axiosGetRequests), + requests: Promise.allSettled(axiosGetRequests), controller: controller } } diff --git a/hadoop-ozone/recon/src/main/resources/webapps/recon/ozone-recon-web/src/views/insights/insights.tsx b/hadoop-ozone/recon/src/main/resources/webapps/recon/ozone-recon-web/src/views/insights/insights.tsx index 2412febdee3b..faa44feaeba2 100644 --- a/hadoop-ozone/recon/src/main/resources/webapps/recon/ozone-recon-web/src/views/insights/insights.tsx +++ b/hadoop-ozone/recon/src/main/resources/webapps/recon/ozone-recon-web/src/views/insights/insights.tsx @@ -17,17 +17,17 @@ */ import React from 'react'; -import axios from 'axios'; +import axios, { CanceledError } from 'axios'; import filesize from 'filesize'; import { Row, Col, Tabs } from 'antd'; import { LoadingOutlined } from '@ant-design/icons'; import { ActionMeta, ValueType } from 'react-select'; -import { format, type EChartsOption } from 'echarts'; +import { type EChartsOption } from 'echarts'; import { EChart } from '@/components/eChart/eChart'; import { MultiSelect, IOption } from '@/components/multiSelect/multiSelect'; import { showDataFetchError } from '@/utils/common'; -import { AxiosAllGetHelper } from '@/utils/axiosRequestHelper'; +import { PromiseAllSettledGetHelper } from '@/utils/axiosRequestHelper'; import './insights.less'; @@ -261,15 +261,34 @@ export class Insights extends React.Component, IInsightsS this.setState({ isLoading: true }); - const { requests, controller } = AxiosAllGetHelper([ + const { requests, controller } = PromiseAllSettledGetHelper([ '/api/v1/utilization/fileCount', '/api/v1/utilization/containerCount' ], cancelInsightSignal); cancelInsightSignal = controller; - requests.then(axios.spread((fileCountresponse, containerCountresponse) => { - const fileCountsResponse: IFileCountResponse[] = fileCountresponse.data; - const containerCountResponse: IContainerCountResponse[] = containerCountresponse.data; + requests.then(axios.spread(( + fileCountresponse: Awaited>, + containerCountresponse: Awaited> + ) => { + if ([ + fileCountresponse, + containerCountresponse].some( + (resp) => + resp.status === 'rejected' && resp.reason.toString().includes('CanceledError') + )) { + throw new CanceledError('canceled', "ERR_CANCELED",) + } + const fileCountsResponse: IFileCountResponse[] = fileCountresponse.value?.data ?? { + volume: '0', + bucket: '0', + fileSize: '0', + count: 0 + }; + const containerCountResponse: IContainerCountResponse[] = containerCountresponse.value?.data ?? { + containerSize: 0, + count: 0 + }; // Construct volume -> bucket[] map for populating filters // Ex: vol1 -> [bucket1, bucket2], vol2 -> [bucket1] const volumeBucketMap: Map> = fileCountsResponse.reduce( @@ -280,7 +299,7 @@ export class Insights extends React.Component, IInsightsS const buckets = Array.from(map.get(volume)!); map.set(volume, new Set([...buckets, bucket])); } else { - map.set(volume, new Set().add(bucket)); + map.set(volume, new Set().add(bucket)); } return map; diff --git a/hadoop-ozone/recon/src/main/resources/webapps/recon/ozone-recon-web/src/views/overview/overview.tsx b/hadoop-ozone/recon/src/main/resources/webapps/recon/ozone-recon-web/src/views/overview/overview.tsx index 5b27b3f0d021..9f8802577cfd 100644 --- a/hadoop-ozone/recon/src/main/resources/webapps/recon/ozone-recon-web/src/views/overview/overview.tsx +++ b/hadoop-ozone/recon/src/main/resources/webapps/recon/ozone-recon-web/src/views/overview/overview.tsx @@ -17,7 +17,7 @@ */ import React from 'react'; -import axios from 'axios'; +import axios, { CanceledError } from 'axios'; import moment from 'moment'; import filesize from 'filesize'; import { Row, Col, Tooltip } from 'antd'; @@ -28,7 +28,7 @@ import OverviewCard from '@/components/overviewCard/overviewCard'; import AutoReloadPanel from '@/components/autoReloadPanel/autoReloadPanel'; import { AutoReloadHelper } from '@/utils/autoReloadHelper'; import { showDataFetchError, byteToSize } from '@/utils/common'; -import { AxiosAllGetHelper, AxiosGetHelper, cancelRequests } from '@/utils/axiosRequestHelper'; +import { AxiosGetHelper, cancelRequests, PromiseAllSettledGetHelper } from '@/utils/axiosRequestHelper'; import './overview.less'; @@ -127,7 +127,7 @@ export class Overview extends React.Component, IOverviewS cancelOverviewSignal ]); - const { requests, controller } = AxiosAllGetHelper([ + const { requests, controller } = PromiseAllSettledGetHelper([ '/api/v1/clusterState', '/api/v1/task/status', '/api/v1/keys/open/summary', @@ -135,10 +135,46 @@ export class Overview extends React.Component, IOverviewS ], cancelOverviewSignal); cancelOverviewSignal = controller; - requests.then(axios.spread((clusterStateResponse, taskstatusResponse, openResponse, deletePendingResponse) => { - - const clusterState: IClusterStateResponse = clusterStateResponse.data; - const taskStatus = taskstatusResponse.data; + requests.then(axios.spread(( + clusterStateResponse: Awaited>, + taskstatusResponse: Awaited>, + openResponse: Awaited>, + deletePendingResponse: Awaited> + ) => { + if ([ + clusterStateResponse, + taskstatusResponse, + openResponse, + deletePendingResponse].some( + (resp) => + resp.status === 'rejected' && resp.reason.toString().includes('CanceledError') + )) { + throw new CanceledError('canceled', "ERR_CANCELED",) + } + const clusterState: IClusterStateResponse = clusterStateResponse.value?.data ?? { + missingContainers: '??', + totalDatanodes: '??', + healthyDatanodes: '??', + pipelines: '??', + storageReport: { + capacity: 0, + used: 0, + remaining: 0, + committed: 0 + }, + containers: '??', + volumes: '??', + buckets: '??', + keys: '??', + openContainers: '??', + deletedContainers: '??', + keysPendingDeletion: '??', + scmServiceId: '??', + omServiceId: '??' + }; + const taskStatus = taskstatusResponse.value?.data ?? [{ + taskName: '??', 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'); @@ -158,12 +194,12 @@ export class Overview extends React.Component, IOverviewS lastRefreshed: Number(moment()), lastUpdatedOMDBDelta: omDBDeltaObject && omDBDeltaObject.lastUpdatedTimestamp, lastUpdatedOMDBFull: omDBFullObject && omDBFullObject.lastUpdatedTimestamp, - openSummarytotalUnrepSize: openResponse.data && openResponse.data.totalUnreplicatedDataSize, - openSummarytotalRepSize: openResponse.data && openResponse.data.totalReplicatedDataSize, - openSummarytotalOpenKeys: openResponse.data && openResponse.data.totalOpenKeys, - deletePendingSummarytotalUnrepSize: deletePendingResponse.data && deletePendingResponse.data.totalUnreplicatedDataSize, - deletePendingSummarytotalRepSize: deletePendingResponse.data && deletePendingResponse.data.totalReplicatedDataSize, - deletePendingSummarytotalDeletedKeys: deletePendingResponse.data && deletePendingResponse.data.totalDeletedKeys, + openSummarytotalUnrepSize: openResponse.value.data && openResponse.value.data.totalUnreplicatedDataSize, + openSummarytotalRepSize: openResponse.value.data && openResponse.value.data.totalReplicatedDataSize, + openSummarytotalOpenKeys: openResponse.value.data && openResponse.value.data.totalOpenKeys, + deletePendingSummarytotalUnrepSize: deletePendingResponse.value.data && deletePendingResponse.value.data.totalUnreplicatedDataSize, + deletePendingSummarytotalRepSize: deletePendingResponse.value.data && deletePendingResponse.value.data.totalReplicatedDataSize, + deletePendingSummarytotalDeletedKeys: deletePendingResponse.value.data && deletePendingResponse.value.data.totalDeletedKeys, scmServiceId: clusterState.scmServiceId, omServiceId: clusterState.omServiceId }); From dbdd3787d6ca2b906eb24fd2fff234c8f08785ae Mon Sep 17 00:00:00 2001 From: Abhishek Pal Date: Mon, 15 Jul 2024 12:20:18 +0530 Subject: [PATCH 2/9] Added undefined check for summary data --- .../ozone-recon-web/src/views/overview/overview.tsx | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/hadoop-ozone/recon/src/main/resources/webapps/recon/ozone-recon-web/src/views/overview/overview.tsx b/hadoop-ozone/recon/src/main/resources/webapps/recon/ozone-recon-web/src/views/overview/overview.tsx index 9f8802577cfd..a77dde7116b0 100644 --- a/hadoop-ozone/recon/src/main/resources/webapps/recon/ozone-recon-web/src/views/overview/overview.tsx +++ b/hadoop-ozone/recon/src/main/resources/webapps/recon/ozone-recon-web/src/views/overview/overview.tsx @@ -194,12 +194,12 @@ export class Overview extends React.Component, IOverviewS lastRefreshed: Number(moment()), lastUpdatedOMDBDelta: omDBDeltaObject && omDBDeltaObject.lastUpdatedTimestamp, lastUpdatedOMDBFull: omDBFullObject && omDBFullObject.lastUpdatedTimestamp, - openSummarytotalUnrepSize: openResponse.value.data && openResponse.value.data.totalUnreplicatedDataSize, - openSummarytotalRepSize: openResponse.value.data && openResponse.value.data.totalReplicatedDataSize, - openSummarytotalOpenKeys: openResponse.value.data && openResponse.value.data.totalOpenKeys, - deletePendingSummarytotalUnrepSize: deletePendingResponse.value.data && deletePendingResponse.value.data.totalUnreplicatedDataSize, - deletePendingSummarytotalRepSize: deletePendingResponse.value.data && deletePendingResponse.value.data.totalReplicatedDataSize, - deletePendingSummarytotalDeletedKeys: deletePendingResponse.value.data && deletePendingResponse.value.data.totalDeletedKeys, + 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, omServiceId: clusterState.omServiceId }); From 84e58611c12d22876b59850361ded82bfc1ee486 Mon Sep 17 00:00:00 2001 From: Abhishek Pal Date: Mon, 15 Jul 2024 21:57:02 +0530 Subject: [PATCH 3/9] Added error boundaries and changed message to N/A in case of API failure --- .../components/overviewCard/overviewCard.tsx | 10 +- .../src/views/overview/overview.tsx | 161 +++++++++++------- 2 files changed, 104 insertions(+), 67 deletions(-) diff --git a/hadoop-ozone/recon/src/main/resources/webapps/recon/ozone-recon-web/src/components/overviewCard/overviewCard.tsx b/hadoop-ozone/recon/src/main/resources/webapps/recon/ozone-recon-web/src/components/overviewCard/overviewCard.tsx index 93c0cae1f16c..c11a05ebad09 100644 --- a/hadoop-ozone/recon/src/main/resources/webapps/recon/ozone-recon-web/src/components/overviewCard/overviewCard.tsx +++ b/hadoop-ozone/recon/src/main/resources/webapps/recon/ozone-recon-web/src/components/overviewCard/overviewCard.tsx @@ -139,8 +139,14 @@ class OverviewCard extends React.Component { render() { let { icon, data, title, loading, hoverable, storageReport, linkToUrl, error } = this.props; + console.log(title); + console.log(data); + console.log(typeof data) let meta = ; - const errorClass = error ? 'card-error' : ''; + let errorClass = error ? 'card-error' : ''; + if (typeof data === 'string' && data === 'N/A'){ + errorClass = 'card-error'; + } if (storageReport) { meta = (
@@ -156,7 +162,7 @@ class OverviewCard extends React.Component { return ( - + diff --git a/hadoop-ozone/recon/src/main/resources/webapps/recon/ozone-recon-web/src/views/overview/overview.tsx b/hadoop-ozone/recon/src/main/resources/webapps/recon/ozone-recon-web/src/views/overview/overview.tsx index a77dde7116b0..354874c1c64b 100644 --- a/hadoop-ozone/recon/src/main/resources/webapps/recon/ozone-recon-web/src/views/overview/overview.tsx +++ b/hadoop-ozone/recon/src/main/resources/webapps/recon/ozone-recon-web/src/views/overview/overview.tsx @@ -35,18 +35,18 @@ import './overview.less'; const size = filesize.partial({ round: 1 }); interface IClusterStateResponse { - missingContainers: number; - totalDatanodes: number; - healthyDatanodes: number; - pipelines: number; + missingContainers: number | string; + totalDatanodes: number | string; + healthyDatanodes: number | string; + pipelines: number | string; storageReport: IStorageReport; - containers: number; - volumes: number; - buckets: number; - keys: number; - openContainers: number; - deletedContainers: number; - keysPendingDeletion: number; + containers: number | string; + volumes: number | string; + buckets: number | string; + keys: number | string; + openContainers: number | string; + deletedContainers: number | string; + keysPendingDeletion: number | string; scmServiceId: string; omServiceId: string; } @@ -54,25 +54,25 @@ interface IClusterStateResponse { interface IOverviewState { loading: boolean; datanodes: string; - pipelines: number; + pipelines: number | string; storageReport: IStorageReport; - containers: number; - volumes: number; - buckets: number; - keys: number; - missingContainersCount: number; - lastRefreshed: number; - lastUpdatedOMDBDelta: number; - lastUpdatedOMDBFull: number; + containers: number | string; + volumes: number | string; + buckets: number | string; + keys: number | string; + missingContainersCount: number | string; + lastRefreshed: number | string; + lastUpdatedOMDBDelta: number | string; + lastUpdatedOMDBFull: number | string; omStatus: string; - openContainers: number; - deletedContainers: number; - openSummarytotalUnrepSize: number; - openSummarytotalRepSize: number; - openSummarytotalOpenKeys: number; - deletePendingSummarytotalUnrepSize: number; - deletePendingSummarytotalRepSize: number; - deletePendingSummarytotalDeletedKeys: number; + openContainers: number | string; + deletedContainers: number | string; + openSummarytotalUnrepSize: number | string; + openSummarytotalRepSize: number | string; + openSummarytotalOpenKeys: number | string; + deletePendingSummarytotalUnrepSize: number | string; + deletePendingSummarytotalRepSize: number | string; + deletePendingSummarytotalDeletedKeys: number | string; scmServiceId: string; omServiceId: string; } @@ -148,32 +148,32 @@ export class Overview extends React.Component, IOverviewS deletePendingResponse].some( (resp) => resp.status === 'rejected' && resp.reason.toString().includes('CanceledError') - )) { + )) { throw new CanceledError('canceled', "ERR_CANCELED",) } const clusterState: IClusterStateResponse = clusterStateResponse.value?.data ?? { - missingContainers: '??', - totalDatanodes: '??', - healthyDatanodes: '??', - pipelines: '??', + missingContainers: 'N/A', + totalDatanodes: 'N/A', + healthyDatanodes: 'N/A', + pipelines: 'N/A', storageReport: { capacity: 0, used: 0, remaining: 0, committed: 0 }, - containers: '??', - volumes: '??', - buckets: '??', - keys: '??', - openContainers: '??', - deletedContainers: '??', - keysPendingDeletion: '??', - scmServiceId: '??', - omServiceId: '??' + 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: '??', lastUpdatedTimestamp: 0, lastUpdatedSeqNumber: 0 + taskName: 'N/A', lastUpdatedTimestamp: 0, lastUpdatedSeqNumber: 0 }]; const missingContainersCount = clusterState.missingContainers; const omDBDeltaObject = taskStatus && taskStatus.find((item: any) => item.taskName === 'OmDeltaRequest'); @@ -181,7 +181,9 @@ export class Overview extends React.Component, IOverviewS this.setState({ loading: false, - datanodes: `${clusterState.healthyDatanodes}/${clusterState.totalDatanodes}`, + datanodes: clusterState.healthyDatanodes !== 'N/A' + ? `${clusterState.healthyDatanodes}/${clusterState.totalDatanodes}` + : `N/A`, storageReport: clusterState.storageReport, pipelines: clusterState.pipelines, containers: clusterState.containers, @@ -257,11 +259,24 @@ export class Overview extends React.Component, IOverviewS keys, missingContainersCount, lastRefreshed, lastUpdatedOMDBDelta, lastUpdatedOMDBFull, omStatus, openContainers, deletedContainers, scmServiceId, omServiceId } = this.state; - const datanodesElement = ( - - {datanodes} HEALTHY - - ); + console.log(datanodes); + const datanodesElement = datanodes !== 'N/A' + ? ( + + {datanodes} HEALTHY + + ) + : ( + + + + {datanodes} + UNHEALTHY + + + ) const openSummaryData = (
{openSummarytotalRepSize !== undefined ? byteToSize(openSummarytotalRepSize, 1) : '0'} Total Replicated Data Size
@@ -277,23 +292,38 @@ export class Overview extends React.Component, IOverviewS
); const containersTooltip = missingContainersCount === 1 ? 'container is missing' : 'containers are missing'; - const containersLink = missingContainersCount > 0 ? '/MissingContainers' : '/Containers'; + const containersLink = missingContainersCount as number > 0 ? '/MissingContainers' : '/Containers'; const volumesLink = '/Volumes'; const bucketsLink = '/Buckets'; - const containersElement = missingContainersCount > 0 ? ( - - 1000 ? `1000+ Containers are missing. For more information, go to the Containers page.` : `${missingContainersCount} ${containersTooltip}`}> - - - {containers - missingContainersCount}/{containers} - - ) : -
- {containers.toString()} - - ({openContainers}) - -
+ const containersElement = missingContainersCount !== 'N/A' + ? missingContainersCount as number > 0 + ? ( + 1000 + ? `1000+ Containers are missing. For more information, go to the Containers page.` + : `${missingContainersCount} ${containersTooltip}`}> + + + {(containers as number) - (missingContainersCount as number)}/{containers} + + ) + : + (
+ {containers.toString()} + + ({openContainers}) + +
) + : ( + + + + {missingContainersCount} + + + ) const clusterCapacity = `${size(storageReport.capacity - storageReport.remaining)}/${size(storageReport.capacity)}`; return (
@@ -308,6 +338,7 @@ export class Overview extends React.Component, IOverviewS @@ -326,7 +357,7 @@ export class Overview extends React.Component, IOverviewS 0} linkToUrl={containersLink} /> + error={missingContainersCount as number > 0 || missingContainersCount === 'N/A'} linkToUrl={containersLink} /> From 719aa34ceeac95d62f86d22e55e166166706285d Mon Sep 17 00:00:00 2001 From: Abhishek Pal Date: Tue, 16 Jul 2024 13:39:04 +0530 Subject: [PATCH 4/9] removed console.log lines --- .../src/components/overviewCard/overviewCard.tsx | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/hadoop-ozone/recon/src/main/resources/webapps/recon/ozone-recon-web/src/components/overviewCard/overviewCard.tsx b/hadoop-ozone/recon/src/main/resources/webapps/recon/ozone-recon-web/src/components/overviewCard/overviewCard.tsx index c11a05ebad09..88ac6db05485 100644 --- a/hadoop-ozone/recon/src/main/resources/webapps/recon/ozone-recon-web/src/components/overviewCard/overviewCard.tsx +++ b/hadoop-ozone/recon/src/main/resources/webapps/recon/ozone-recon-web/src/components/overviewCard/overviewCard.tsx @@ -139,9 +139,7 @@ class OverviewCard extends React.Component { render() { let { icon, data, title, loading, hoverable, storageReport, linkToUrl, error } = this.props; - console.log(title); - console.log(data); - console.log(typeof data) + let meta = ; let errorClass = error ? 'card-error' : ''; if (typeof data === 'string' && data === 'N/A'){ From e89c747a157f42a3b02d3486ad2f76991ba23745 Mon Sep 17 00:00:00 2001 From: Abhishek Pal Date: Wed, 17 Jul 2024 11:06:50 +0530 Subject: [PATCH 5/9] Made changes to give N/A for summary endpoints and also handled insights .reduce() error --- .../components/overviewCard/overviewCard.tsx | 2 + .../src/utils/axiosRequestHelper.tsx | 1 - .../src/views/insights/insights.tsx | 15 +++--- .../src/views/overview/overview.tsx | 49 +++++++++++++++---- 4 files changed, 49 insertions(+), 18 deletions(-) diff --git a/hadoop-ozone/recon/src/main/resources/webapps/recon/ozone-recon-web/src/components/overviewCard/overviewCard.tsx b/hadoop-ozone/recon/src/main/resources/webapps/recon/ozone-recon-web/src/components/overviewCard/overviewCard.tsx index 88ac6db05485..78ba56e54891 100644 --- a/hadoop-ozone/recon/src/main/resources/webapps/recon/ozone-recon-web/src/components/overviewCard/overviewCard.tsx +++ b/hadoop-ozone/recon/src/main/resources/webapps/recon/ozone-recon-web/src/components/overviewCard/overviewCard.tsx @@ -142,9 +142,11 @@ class OverviewCard extends React.Component { let meta = ; let errorClass = error ? 'card-error' : ''; + if (typeof data === 'string' && data === 'N/A'){ errorClass = 'card-error'; } + if (storageReport) { meta = (
diff --git a/hadoop-ozone/recon/src/main/resources/webapps/recon/ozone-recon-web/src/utils/axiosRequestHelper.tsx b/hadoop-ozone/recon/src/main/resources/webapps/recon/ozone-recon-web/src/utils/axiosRequestHelper.tsx index 836ff499529c..41774088c503 100644 --- a/hadoop-ozone/recon/src/main/resources/webapps/recon/ozone-recon-web/src/utils/axiosRequestHelper.tsx +++ b/hadoop-ozone/recon/src/main/resources/webapps/recon/ozone-recon-web/src/utils/axiosRequestHelper.tsx @@ -63,7 +63,6 @@ export const PromiseAllSettledGetHelper = ( axiosGetRequests.push(axios.get(url, { signal: controller.signal })) }); - console.log(axiosGetRequests); return { requests: Promise.allSettled(axiosGetRequests), controller: controller diff --git a/hadoop-ozone/recon/src/main/resources/webapps/recon/ozone-recon-web/src/views/insights/insights.tsx b/hadoop-ozone/recon/src/main/resources/webapps/recon/ozone-recon-web/src/views/insights/insights.tsx index faa44feaeba2..c7cd5c54bfec 100644 --- a/hadoop-ozone/recon/src/main/resources/webapps/recon/ozone-recon-web/src/views/insights/insights.tsx +++ b/hadoop-ozone/recon/src/main/resources/webapps/recon/ozone-recon-web/src/views/insights/insights.tsx @@ -195,10 +195,6 @@ export class Insights extends React.Component, IInsightsS return (size(value)); }); - console.log(xyFileCountMap); - console.log(xContainerCountValues); - console.log(xyContainerCountMap); - this.setState({ fileCountData: { title: { @@ -279,16 +275,19 @@ export class Insights extends React.Component, IInsightsS )) { throw new CanceledError('canceled', "ERR_CANCELED",) } - const fileCountsResponse: IFileCountResponse[] = fileCountresponse.value?.data ?? { + const fileCountsResponse: IFileCountResponse[] = fileCountresponse.value?.data ?? [{ volume: '0', bucket: '0', fileSize: '0', count: 0 - }; - const containerCountResponse: IContainerCountResponse[] = containerCountresponse.value?.data ?? { + }]; + const containerCountResponse: IContainerCountResponse[] = containerCountresponse.value?.data ?? [{ containerSize: 0, count: 0 - }; + }]; + + console.log(fileCountsResponse); + console.log(containerCountResponse); // Construct volume -> bucket[] map for populating filters // Ex: vol1 -> [bucket1, bucket2], vol2 -> [bucket1] const volumeBucketMap: Map> = fileCountsResponse.reduce( diff --git a/hadoop-ozone/recon/src/main/resources/webapps/recon/ozone-recon-web/src/views/overview/overview.tsx b/hadoop-ozone/recon/src/main/resources/webapps/recon/ozone-recon-web/src/views/overview/overview.tsx index 354874c1c64b..68597ffd1ac8 100644 --- a/hadoop-ozone/recon/src/main/resources/webapps/recon/ozone-recon-web/src/views/overview/overview.tsx +++ b/hadoop-ozone/recon/src/main/resources/webapps/recon/ozone-recon-web/src/views/overview/overview.tsx @@ -151,6 +151,7 @@ export class Overview extends React.Component, IOverviewS )) { throw new CanceledError('canceled', "ERR_CANCELED",) } + const clusterState: IClusterStateResponse = clusterStateResponse.value?.data ?? { missingContainers: 'N/A', totalDatanodes: 'N/A', @@ -259,7 +260,25 @@ export class Overview extends React.Component, IOverviewS keys, missingContainersCount, lastRefreshed, lastUpdatedOMDBDelta, lastUpdatedOMDBFull, omStatus, openContainers, deletedContainers, scmServiceId, omServiceId } = this.state; - console.log(datanodes); + let openKeysError: boolean = false; + let pendingDeleteKeysError: boolean = false; + if ([ + openSummarytotalRepSize, + openSummarytotalUnrepSize, + openSummarytotalOpenKeys].some( + (data) => data === undefined + )) { + openKeysError = true; + } + + if ([ + deletePendingSummarytotalRepSize, + deletePendingSummarytotalUnrepSize, + deletePendingSummarytotalDeletedKeys].some( + (data) => data === undefined + )) { + pendingDeleteKeysError = true; + } const datanodesElement = datanodes !== 'N/A' ? ( @@ -279,16 +298,16 @@ export class Overview extends React.Component, IOverviewS ) const openSummaryData = (
- {openSummarytotalRepSize !== undefined ? byteToSize(openSummarytotalRepSize, 1) : '0'} Total Replicated Data Size
- {openSummarytotalUnrepSize !== undefined ? byteToSize(openSummarytotalUnrepSize, 1) : '0'} Total UnReplicated Data Size
- {openSummarytotalOpenKeys !== undefined ? openSummarytotalOpenKeys : '0'} Total Open Keys + {openSummarytotalRepSize !== undefined ? byteToSize(openSummarytotalRepSize, 1) : 'N/A'} Total Replicated Data Size
+ {openSummarytotalUnrepSize !== undefined ? byteToSize(openSummarytotalUnrepSize, 1) : 'N/A'} Total UnReplicated Data Size
+ {openSummarytotalOpenKeys !== undefined ? openSummarytotalOpenKeys : 'N/A'} Total Open Keys
); const deletePendingSummaryData = (
- {deletePendingSummarytotalRepSize !== undefined ? byteToSize(deletePendingSummarytotalRepSize, 1) : '0'} Total Replicated Data Size
- {deletePendingSummarytotalUnrepSize !== undefined ? byteToSize(deletePendingSummarytotalUnrepSize, 1) : '0'} Total UnReplicated Data Size
- {deletePendingSummarytotalDeletedKeys !== undefined ? deletePendingSummarytotalDeletedKeys : '0'} Total Pending Delete Keys + {deletePendingSummarytotalRepSize !== undefined ? byteToSize(deletePendingSummarytotalRepSize, 1) : 'N/A'} Total Replicated Data Size
+ {deletePendingSummarytotalUnrepSize !== undefined ? byteToSize(deletePendingSummarytotalUnrepSize, 1) : 'N/A'} Total UnReplicated Data Size
+ {deletePendingSummarytotalDeletedKeys !== undefined ? deletePendingSummarytotalDeletedKeys : 'N/A'} Total Pending Delete Keys
); const containersTooltip = missingContainersCount === 1 ? 'container is missing' : 'containers are missing'; @@ -372,10 +391,22 @@ export class Overview extends React.Component, IOverviewS - + - + {scmServiceId && From ea2e800728e33c3ec828371cb646f6ba0f4899c8 Mon Sep 17 00:00:00 2001 From: Abhishek Pal Date: Wed, 17 Jul 2024 17:21:04 +0530 Subject: [PATCH 6/9] added an Error wrapper for all errors in Promise --- .../src/utils/axiosRequestHelper.tsx | 20 +++++++++++ .../src/views/diskUsage/diskUsage.tsx | 6 ---- .../src/views/insights/insights.tsx | 34 +++++++++++++------ 3 files changed, 44 insertions(+), 16 deletions(-) diff --git a/hadoop-ozone/recon/src/main/resources/webapps/recon/ozone-recon-web/src/utils/axiosRequestHelper.tsx b/hadoop-ozone/recon/src/main/resources/webapps/recon/ozone-recon-web/src/utils/axiosRequestHelper.tsx index 41774088c503..2de31d9fe755 100644 --- a/hadoop-ozone/recon/src/main/resources/webapps/recon/ozone-recon-web/src/utils/axiosRequestHelper.tsx +++ b/hadoop-ozone/recon/src/main/resources/webapps/recon/ozone-recon-web/src/utils/axiosRequestHelper.tsx @@ -69,6 +69,26 @@ export const PromiseAllSettledGetHelper = ( } } +export class PromiseAllSettledError extends Error { + + _urls: string[]; + _errors: string[]; + + constructor(urls: string[], errors: string[], ...opts){ + super(...opts); + this._urls = urls; + this._errors = errors; + } + + getFaultyUrls() { + return this._urls; + } + + getResponseErrors() { + return this._errors; + } +} + export const cancelRequests = (cancelSignal: AbortController[]) => { cancelSignal.forEach((signal) => { signal && signal.abort(); diff --git a/hadoop-ozone/recon/src/main/resources/webapps/recon/ozone-recon-web/src/views/diskUsage/diskUsage.tsx b/hadoop-ozone/recon/src/main/resources/webapps/recon/ozone-recon-web/src/views/diskUsage/diskUsage.tsx index 60b779bdf089..091d8a146d3b 100644 --- a/hadoop-ozone/recon/src/main/resources/webapps/recon/ozone-recon-web/src/views/diskUsage/diskUsage.tsx +++ b/hadoop-ozone/recon/src/main/resources/webapps/recon/ozone-recon-web/src/views/diskUsage/diskUsage.tsx @@ -556,12 +556,6 @@ export class DiskUsage extends React.Component, IDUState> ) - console.log(plotData); - console.log(plotData.map((value) => { - return { - name: value.name - } - })) const eChartsOptions = { title: { text: `Disk Usage for ${returnPath} (Total Size: ${byteToSize(duResponse.size, 1)})`, diff --git a/hadoop-ozone/recon/src/main/resources/webapps/recon/ozone-recon-web/src/views/insights/insights.tsx b/hadoop-ozone/recon/src/main/resources/webapps/recon/ozone-recon-web/src/views/insights/insights.tsx index c7cd5c54bfec..82cf0955b8c1 100644 --- a/hadoop-ozone/recon/src/main/resources/webapps/recon/ozone-recon-web/src/views/insights/insights.tsx +++ b/hadoop-ozone/recon/src/main/resources/webapps/recon/ozone-recon-web/src/views/insights/insights.tsx @@ -27,7 +27,7 @@ import { type EChartsOption } from 'echarts'; import { EChart } from '@/components/eChart/eChart'; import { MultiSelect, IOption } from '@/components/multiSelect/multiSelect'; import { showDataFetchError } from '@/utils/common'; -import { PromiseAllSettledGetHelper } from '@/utils/axiosRequestHelper'; +import { PromiseAllSettledGetHelper, PromiseAllSettledError } from '@/utils/axiosRequestHelper'; import './insights.less'; @@ -267,14 +267,22 @@ export class Insights extends React.Component, IInsightsS fileCountresponse: Awaited>, containerCountresponse: Awaited> ) => { - if ([ + let responseError = [ fileCountresponse, - containerCountresponse].some( - (resp) => - resp.status === 'rejected' && resp.reason.toString().includes('CanceledError') - )) { - throw new CanceledError('canceled', "ERR_CANCELED",) + containerCountresponse + ].filter((resp) => resp.status === 'rejected'); + + if (responseError.length !== 0) { + let urls: string[] = []; + let errors: string[] = []; + responseError.forEach((resp) => { + console.log(resp); + urls.push(resp.reason.config.url); + errors.push(`${resp.reason.name}: ${resp.reason.message}`); + }); + throw new PromiseAllSettledError(urls, errors) } + const fileCountsResponse: IFileCountResponse[] = fileCountresponse.value?.data ?? [{ volume: '0', bucket: '0', @@ -286,8 +294,6 @@ export class Insights extends React.Component, IInsightsS count: 0 }]; - console.log(fileCountsResponse); - console.log(containerCountResponse); // Construct volume -> bucket[] map for populating filters // Ex: vol1 -> [bucket1, bucket2], vol2 -> [bucket1] const volumeBucketMap: Map> = fileCountsResponse.reduce( @@ -325,7 +331,15 @@ export class Insights extends React.Component, IInsightsS this.setState({ isLoading: false }); - showDataFetchError(error.toString()); + if (error instanceof PromiseAllSettledError){ + const messages = error.getResponseErrors(); + error.getFaultyUrls().forEach((url, idx) => { + showDataFetchError(`Request to ${url} failed due to\n${messages[idx]}`) + }) + } + else { + showDataFetchError(error.toString()); + } }); } From ae3dad1322aaaf8998afaf751d9f5e12f26d09a3 Mon Sep 17 00:00:00 2001 From: Abhishek Pal Date: Wed, 17 Jul 2024 18:00:10 +0530 Subject: [PATCH 7/9] Added error message for other axios errors --- .../src/utils/axiosRequestHelper.tsx | 20 ------------- .../src/views/insights/insights.tsx | 28 ++++++++----------- .../src/views/overview/overview.tsx | 21 ++++++++++---- 3 files changed, 26 insertions(+), 43 deletions(-) diff --git a/hadoop-ozone/recon/src/main/resources/webapps/recon/ozone-recon-web/src/utils/axiosRequestHelper.tsx b/hadoop-ozone/recon/src/main/resources/webapps/recon/ozone-recon-web/src/utils/axiosRequestHelper.tsx index 2de31d9fe755..41774088c503 100644 --- a/hadoop-ozone/recon/src/main/resources/webapps/recon/ozone-recon-web/src/utils/axiosRequestHelper.tsx +++ b/hadoop-ozone/recon/src/main/resources/webapps/recon/ozone-recon-web/src/utils/axiosRequestHelper.tsx @@ -69,26 +69,6 @@ export const PromiseAllSettledGetHelper = ( } } -export class PromiseAllSettledError extends Error { - - _urls: string[]; - _errors: string[]; - - constructor(urls: string[], errors: string[], ...opts){ - super(...opts); - this._urls = urls; - this._errors = errors; - } - - getFaultyUrls() { - return this._urls; - } - - getResponseErrors() { - return this._errors; - } -} - export const cancelRequests = (cancelSignal: AbortController[]) => { cancelSignal.forEach((signal) => { signal && signal.abort(); diff --git a/hadoop-ozone/recon/src/main/resources/webapps/recon/ozone-recon-web/src/views/insights/insights.tsx b/hadoop-ozone/recon/src/main/resources/webapps/recon/ozone-recon-web/src/views/insights/insights.tsx index 82cf0955b8c1..28e7d14d9d5a 100644 --- a/hadoop-ozone/recon/src/main/resources/webapps/recon/ozone-recon-web/src/views/insights/insights.tsx +++ b/hadoop-ozone/recon/src/main/resources/webapps/recon/ozone-recon-web/src/views/insights/insights.tsx @@ -273,14 +273,16 @@ export class Insights extends React.Component, IInsightsS ].filter((resp) => resp.status === 'rejected'); if (responseError.length !== 0) { - let urls: string[] = []; - let errors: string[] = []; - responseError.forEach((resp) => { - console.log(resp); - urls.push(resp.reason.config.url); - errors.push(`${resp.reason.name}: ${resp.reason.message}`); - }); - throw new PromiseAllSettledError(urls, errors) + responseError.forEach((err) => { + if (err.reason.toString().includes("CanceledError")){ + throw new CanceledError('canceled', "ERR_CANCELED"); + } + else { + const reqMethod = err.reason.config.method; + const reqURL = err.reason.config.url + showDataFetchError(`Failed to ${reqMethod} URL ${reqURL}\n${err.reason.toString()}`); + } + }) } const fileCountsResponse: IFileCountResponse[] = fileCountresponse.value?.data ?? [{ @@ -331,15 +333,7 @@ export class Insights extends React.Component, IInsightsS this.setState({ isLoading: false }); - if (error instanceof PromiseAllSettledError){ - const messages = error.getResponseErrors(); - error.getFaultyUrls().forEach((url, idx) => { - showDataFetchError(`Request to ${url} failed due to\n${messages[idx]}`) - }) - } - else { - showDataFetchError(error.toString()); - } + showDataFetchError(error.toString()); }); } diff --git a/hadoop-ozone/recon/src/main/resources/webapps/recon/ozone-recon-web/src/views/overview/overview.tsx b/hadoop-ozone/recon/src/main/resources/webapps/recon/ozone-recon-web/src/views/overview/overview.tsx index 68597ffd1ac8..bb2e5e02f2c6 100644 --- a/hadoop-ozone/recon/src/main/resources/webapps/recon/ozone-recon-web/src/views/overview/overview.tsx +++ b/hadoop-ozone/recon/src/main/resources/webapps/recon/ozone-recon-web/src/views/overview/overview.tsx @@ -141,15 +141,24 @@ export class Overview extends React.Component, IOverviewS openResponse: Awaited>, deletePendingResponse: Awaited> ) => { - if ([ + let responseError = [ clusterStateResponse, taskstatusResponse, openResponse, - deletePendingResponse].some( - (resp) => - resp.status === 'rejected' && resp.reason.toString().includes('CanceledError') - )) { - throw new CanceledError('canceled', "ERR_CANCELED",) + deletePendingResponse + ].filter((resp) => resp.status === 'rejected'); + + if (responseError.length !== 0) { + responseError.forEach((err) => { + if (err.reason.toString().includes("CanceledError")){ + throw new CanceledError('canceled', "ERR_CANCELED"); + } + else { + const reqMethod = err.reason.config.method; + const reqURL = err.reason.config.url + showDataFetchError(`Failed to ${reqMethod} URL ${reqURL}\n${err.reason.toString()}`); + } + }) } const clusterState: IClusterStateResponse = clusterStateResponse.value?.data ?? { From 6994a04aa0c562bf6676cf7548f711677ccfa6f6 Mon Sep 17 00:00:00 2001 From: Abhishek Pal Date: Wed, 17 Jul 2024 22:01:55 +0530 Subject: [PATCH 8/9] Fixed indentation --- .../src/views/insights/insights.tsx | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/hadoop-ozone/recon/src/main/resources/webapps/recon/ozone-recon-web/src/views/insights/insights.tsx b/hadoop-ozone/recon/src/main/resources/webapps/recon/ozone-recon-web/src/views/insights/insights.tsx index 28e7d14d9d5a..697f8e579fde 100644 --- a/hadoop-ozone/recon/src/main/resources/webapps/recon/ozone-recon-web/src/views/insights/insights.tsx +++ b/hadoop-ozone/recon/src/main/resources/webapps/recon/ozone-recon-web/src/views/insights/insights.tsx @@ -17,9 +17,9 @@ */ import React from 'react'; -import axios, { CanceledError } from 'axios'; +import axios, { CanceledError, AxiosError } from 'axios'; import filesize from 'filesize'; -import { Row, Col, Tabs } from 'antd'; +import { Row, Col, Tabs, message } from 'antd'; import { LoadingOutlined } from '@ant-design/icons'; import { ActionMeta, ValueType } from 'react-select'; import { type EChartsOption } from 'echarts'; @@ -274,7 +274,7 @@ export class Insights extends React.Component, IInsightsS if (responseError.length !== 0) { responseError.forEach((err) => { - if (err.reason.toString().includes("CanceledError")){ + if (err.reason.toString().includes("CanceledError")) { throw new CanceledError('canceled', "ERR_CANCELED"); } else { @@ -286,10 +286,10 @@ export class Insights extends React.Component, IInsightsS } const fileCountsResponse: IFileCountResponse[] = fileCountresponse.value?.data ?? [{ - volume: '0', - bucket: '0', - fileSize: '0', - count: 0 + volume: '0', + bucket: '0', + fileSize: '0', + count: 0 }]; const containerCountResponse: IContainerCountResponse[] = containerCountresponse.value?.data ?? [{ containerSize: 0, From 5f59b5279bcbd4c7dfc7e0662b1277424c586f3d Mon Sep 17 00:00:00 2001 From: Abhishek Pal Date: Wed, 17 Jul 2024 23:44:05 +0530 Subject: [PATCH 9/9] Added No data overlay for error in network response --- .../src/views/insights/insights.tsx | 107 ++++++++++++++++-- 1 file changed, 98 insertions(+), 9 deletions(-) diff --git a/hadoop-ozone/recon/src/main/resources/webapps/recon/ozone-recon-web/src/views/insights/insights.tsx b/hadoop-ozone/recon/src/main/resources/webapps/recon/ozone-recon-web/src/views/insights/insights.tsx index 697f8e579fde..f273f758ea96 100644 --- a/hadoop-ozone/recon/src/main/resources/webapps/recon/ozone-recon-web/src/views/insights/insights.tsx +++ b/hadoop-ozone/recon/src/main/resources/webapps/recon/ozone-recon-web/src/views/insights/insights.tsx @@ -22,7 +22,7 @@ import filesize from 'filesize'; import { Row, Col, Tabs, message } from 'antd'; import { LoadingOutlined } from '@ant-design/icons'; import { ActionMeta, ValueType } from 'react-select'; -import { type EChartsOption } from 'echarts'; +import { graphic, type EChartsOption } from 'echarts'; import { EChart } from '@/components/eChart/eChart'; import { MultiSelect, IOption } from '@/components/multiSelect/multiSelect'; @@ -58,6 +58,8 @@ interface IInsightsState { bucketOptions: IOption[]; volumeOptions: IOption[]; isBucketSelectionDisabled: boolean; + fileCountError: string | undefined; + containerSizeError: string | undefined; } const allVolumesOption: IOption = { @@ -86,7 +88,9 @@ export class Insights extends React.Component, IInsightsS selectedVolumes: [], bucketOptions: [], volumeOptions: [], - isBucketSelectionDisabled: false + isBucketSelectionDisabled: false, + fileCountError: undefined, + containerSizeError: undefined }; } @@ -195,6 +199,82 @@ export class Insights extends React.Component, IInsightsS return (size(value)); }); + let renderFileCountError = (this.state.fileCountError) ? { + type: 'group', + left: 'center', + top: 'middle', + z: 100, + children: [ + { + type: 'rect', + left: 'center', + top: 'middle', + z: 100, + shape: { + width: 500, + height: 40 + }, + style: { + fill: '#FC909B' + } + }, + { + type: 'text', + left: 'center', + top: 'middle', + z: 100, + style: { + text: `No data available. ${this.state.fileCountError}`, + font: '20px sans-serif' + } + } + ] + } : undefined + let renderContainerSizeError = (this.state.containerSizeError) ? { + type: 'group', + left: 'center', + top: 'middle', + z: 100, + children: [ + { + type: 'rect', + left: 'center', + top: 'middle', + z: 100, + shape: { + width: 500, + height: 500 + }, + style: { + fill: 'rgba(256, 256, 256, 0.5)' + } + }, + { + type: 'rect', + left: 'center', + top: 'middle', + z: 100, + shape: { + width: 500, + height: 40 + }, + style: { + fill: '#FC909B' + } + }, + { + type: 'text', + left: 'center', + top: 'middle', + z: 100, + style: { + text: `No data available. ${this.state.containerSizeError}`, + font: '20px sans-serif' + } + } + ] + } : undefined + this.setState({ fileCountData: { title: { @@ -220,7 +300,8 @@ export class Insights extends React.Component, IInsightsS }, data: Array.from(xyFileCountMap.values()), type: 'bar' - } + }, + graphic: renderFileCountError }, containerCountData: { title: { @@ -246,7 +327,8 @@ export class Insights extends React.Component, IInsightsS name: xContainerCountValues[idx] } }), - } + }, + graphic: renderContainerSizeError } }); } @@ -267,6 +349,8 @@ export class Insights extends React.Component, IInsightsS fileCountresponse: Awaited>, containerCountresponse: Awaited> ) => { + let fileAPIError = undefined; + let containerAPIError = undefined; let responseError = [ fileCountresponse, containerCountresponse @@ -278,9 +362,12 @@ export class Insights extends React.Component, IInsightsS throw new CanceledError('canceled', "ERR_CANCELED"); } else { - const reqMethod = err.reason.config.method; - const reqURL = err.reason.config.url - showDataFetchError(`Failed to ${reqMethod} URL ${reqURL}\n${err.reason.toString()}`); + if (err.reason.config.url.includes("fileCount")) { + fileAPIError = err.reason.toString(); + } + else { + containerAPIError = err.reason.toString(); + } } }) } @@ -323,7 +410,9 @@ export class Insights extends React.Component, IInsightsS volumeBucketMap, fileCountsResponse, containerCountResponse, - volumeOptions + volumeOptions, + fileCountError: fileAPIError, + containerSizeError: containerAPIError }, () => { this.updatePlotData(); // Select all volumes by default @@ -331,7 +420,7 @@ export class Insights extends React.Component, IInsightsS }); })).catch(error => { this.setState({ - isLoading: false + isLoading: false, }); showDataFetchError(error.toString()); });