diff --git a/src/components/NetworkTable/NetworkTable.tsx b/src/components/NetworkTable/NetworkTable.tsx new file mode 100644 index 0000000000..57d6d963cc --- /dev/null +++ b/src/components/NetworkTable/NetworkTable.tsx @@ -0,0 +1,40 @@ +import {Nodes} from '../../containers/Nodes/Nodes'; +import type {NodesProps} from '../../containers/Nodes/Nodes'; + +import {getNetworkTableNodesColumns} from './columns'; +import { + NETWORK_DEFAULT_NODES_COLUMNS, + NETWORK_NODES_GROUP_BY_PARAMS, + NETWORK_NODES_TABLE_SELECTED_COLUMNS_KEY, + NETWORK_REQUIRED_NODES_COLUMNS, +} from './constants'; + +type NetworkWrapperProps = Pick< + NodesProps, + 'path' | 'scrollContainerRef' | 'additionalNodesProps' | 'database' +>; + +export function NetworkTable({ + database, + path, + scrollContainerRef, + additionalNodesProps, +}: NetworkWrapperProps) { + return ( + + ); +} diff --git a/src/components/NetworkTable/columns.tsx b/src/components/NetworkTable/columns.tsx new file mode 100644 index 0000000000..6afb89fd83 --- /dev/null +++ b/src/components/NetworkTable/columns.tsx @@ -0,0 +1,41 @@ +import type {NodesPreparedEntity} from '../../store/reducers/nodes/types'; +import type {Column} from '../../utils/tableUtils/types'; +import { + getClockSkewColumn, + getConnectionsColumn, + getCpuColumn, + getDataCenterColumn, + getHostColumn, + getNetworkUtilizationColumn, + getNodeIdColumn, + getPingTimeColumn, + getPoolsColumn, + getRackColumn, + getReceiveThroughputColumn, + getSendThroughputColumn, + getUptimeColumn, +} from '../nodesColumns/columns'; +import {isSortableNodesColumn} from '../nodesColumns/constants'; +import type {GetNodesColumnsParams} from '../nodesColumns/types'; + +export function getNetworkTableNodesColumns(params: GetNodesColumnsParams) { + const columns: Column[] = [ + getNodeIdColumn(), + getHostColumn(params, {statusForIcon: 'ConnectStatus'}), + getDataCenterColumn(), + getRackColumn(), + getUptimeColumn(), + getCpuColumn(), + getPoolsColumn(), + getConnectionsColumn(), + getNetworkUtilizationColumn(), + getSendThroughputColumn(), + getReceiveThroughputColumn(), + getPingTimeColumn(), + getClockSkewColumn(), + ]; + + return columns.map((column) => { + return {...column, sortable: isSortableNodesColumn(column.name)}; + }); +} diff --git a/src/containers/Tenant/Diagnostics/Network/NetworkTable/constants.ts b/src/components/NetworkTable/constants.ts similarity index 79% rename from src/containers/Tenant/Diagnostics/Network/NetworkTable/constants.ts rename to src/components/NetworkTable/constants.ts index e6854dcb7a..a493899109 100644 --- a/src/containers/Tenant/Diagnostics/Network/NetworkTable/constants.ts +++ b/src/components/NetworkTable/constants.ts @@ -1,5 +1,5 @@ -import type {NodesColumnId} from '../../../../../components/nodesColumns/constants'; -import type {NodesGroupByField} from '../../../../../types/api/nodes'; +import type {NodesGroupByField} from '../../types/api/nodes'; +import type {NodesColumnId} from '../nodesColumns/constants'; export const NETWORK_NODES_TABLE_SELECTED_COLUMNS_KEY = 'networkNodesTableSelectedColumns'; diff --git a/src/components/NetworkTable/hooks.ts b/src/components/NetworkTable/hooks.ts new file mode 100644 index 0000000000..d961de340c --- /dev/null +++ b/src/components/NetworkTable/hooks.ts @@ -0,0 +1,20 @@ +import { + useNodesHandlerHasWorkingClusterNetworkStats, + useViewerNodesHandlerHasNetworkStats, +} from '../../store/reducers/capabilities/hooks'; +import {ENABLE_NETWORK_TABLE_KEY} from '../../utils/constants'; +import {useSetting} from '../../utils/hooks'; + +export function useShouldShowDatabaseNetworkTable() { + const viewerNodesHasNetworkStats = useViewerNodesHandlerHasNetworkStats(); + const [networkTableEnabled] = useSetting(ENABLE_NETWORK_TABLE_KEY); + + return Boolean(viewerNodesHasNetworkStats && networkTableEnabled); +} + +export function useShouldShowClusterNetworkTable() { + const nodesHasWorkingClusterNetworkStats = useNodesHandlerHasWorkingClusterNetworkStats(); + const [networkTableEnabled] = useSetting(ENABLE_NETWORK_TABLE_KEY); + + return Boolean(nodesHasWorkingClusterNetworkStats && networkTableEnabled); +} diff --git a/src/containers/Cluster/Cluster.tsx b/src/containers/Cluster/Cluster.tsx index 6d85f748ae..896df61267 100644 --- a/src/containers/Cluster/Cluster.tsx +++ b/src/containers/Cluster/Cluster.tsx @@ -9,9 +9,12 @@ import {AutoRefreshControl} from '../../components/AutoRefreshControl/AutoRefres import {EntityStatus} from '../../components/EntityStatusNew/EntityStatus'; import {EFlagToDescription} from '../../components/EntityStatusNew/utils'; import {InternalLink} from '../../components/InternalLink'; +import {NetworkTable} from '../../components/NetworkTable/NetworkTable'; +import {useShouldShowClusterNetworkTable} from '../../components/NetworkTable/hooks'; import routes, {getLocationObjectFromHref} from '../../routes'; import {useClusterDashboardAvailable} from '../../store/reducers/capabilities/hooks'; import { + INITIAL_DEFAULT_CLUSTER_TAB, clusterApi, selectClusterTabletsWithFqdn, selectClusterTitle, @@ -55,6 +58,8 @@ export function Cluster({ const container = React.useRef(null); const isClusterDashboardAvailable = useClusterDashboardAvailable(); + const shouldShowNetworkTable = useShouldShowClusterNetworkTable(); + const [autoRefreshInterval] = useAutoRefreshInterval(); const dispatch = useTypedDispatch(); @@ -92,6 +97,14 @@ export function Cluster({ dispatch(setHeaderBreadcrumbs('cluster', {})); }, [dispatch]); + const actualClusterTabs = React.useMemo(() => { + if (shouldShowNetworkTable) { + return clusterTabs; + } else { + return clusterTabs.filter((tab) => tab.id !== clusterTabsIds.network); + } + }, [shouldShowNetworkTable]); + const getClusterTitle = () => { if (infoLoading) { return ; @@ -110,8 +123,8 @@ export function Cluster({ }; const activeTab = React.useMemo( - () => clusterTabs.find(({id}) => id === activeTabId), - [activeTabId], + () => actualClusterTabs.find(({id}) => id === activeTabId), + [activeTabId, actualClusterTabs], ); return ( @@ -142,7 +155,7 @@ export function Cluster({ size="l" allowNotSelected={true} activeTab={activeTabId} - items={clusterTabs} + items={actualClusterTabs} wrapTo={({id}, node) => { const path = getClusterPath(id as ClusterTab, {clusterName, backend}); return ( @@ -202,6 +215,19 @@ export function Cluster({ > + {shouldShowNetworkTable && ( + + + + )} state.cluster.defaultClusterTab); + const shouldShowNetworkTable = useShouldShowClusterNetworkTable(); + const match = useRouteMatch<{activeTab: string}>(routes.cluster); const {activeTab: activeTabFromParams} = match?.params || {}; let activeTab: ClusterTab; - if (isClusterTab(activeTabFromParams)) { + + if (!shouldShowNetworkTable && activeTabFromParams === clusterTabsIds.network) { + activeTab = INITIAL_DEFAULT_CLUSTER_TAB; + } else if (isClusterTab(activeTabFromParams)) { activeTab = activeTabFromParams; } else { activeTab = defaultTab; diff --git a/src/containers/Cluster/utils.tsx b/src/containers/Cluster/utils.tsx index 785ebfda94..47c8fa77d9 100644 --- a/src/containers/Cluster/utils.tsx +++ b/src/containers/Cluster/utils.tsx @@ -7,6 +7,7 @@ export const clusterTabsIds = { tenants: 'tenants', nodes: 'nodes', storage: 'storage', + network: 'network', versions: 'versions', tablets: 'tablets', } as const; @@ -25,6 +26,10 @@ const storage = { id: clusterTabsIds.storage, title: 'Storage', }; +const network = { + id: clusterTabsIds.network, + title: 'Network', +}; const versions = { id: clusterTabsIds.versions, title: 'Versions', @@ -34,7 +39,7 @@ const tablets = { title: 'Tablets', }; -export const clusterTabs = [tenants, nodes, storage, tablets, versions]; +export const clusterTabs = [tenants, nodes, storage, network, tablets, versions]; export function isClusterTab(tab: any): tab is ClusterTab { return Object.values(clusterTabsIds).includes(tab); diff --git a/src/containers/Tenant/Diagnostics/Network/NetworkTable/columns.tsx b/src/containers/Tenant/Diagnostics/Network/NetworkTable/columns.tsx deleted file mode 100644 index f98714a93f..0000000000 --- a/src/containers/Tenant/Diagnostics/Network/NetworkTable/columns.tsx +++ /dev/null @@ -1,43 +0,0 @@ -import { - getClockSkewColumn, - getConnectionsColumn, - getCpuColumn, - getDataCenterColumn, - getHostColumn, - getNetworkUtilizationColumn, - getNodeIdColumn, - getPingTimeColumn, - getPoolsColumn, - getRackColumn, - getReceiveThroughputColumn, - getSendThroughputColumn, - getUptimeColumn, -} from '../../../../../components/nodesColumns/columns'; -import {isSortableNodesColumn} from '../../../../../components/nodesColumns/constants'; -import type {GetNodesColumnsParams} from '../../../../../components/nodesColumns/types'; -import type {NodesPreparedEntity} from '../../../../../store/reducers/nodes/types'; -import type {Column} from '../../../../../utils/tableUtils/types'; - -export function getNetworkTableNodesColumns( - params: GetNodesColumnsParams, -): Column[] { - const columns = [ - getNodeIdColumn(), - getHostColumn(params, {statusForIcon: 'ConnectStatus'}), - getDataCenterColumn(), - getRackColumn(), - getUptimeColumn(), - getCpuColumn(), - getPoolsColumn(), - getConnectionsColumn(), - getNetworkUtilizationColumn(), - getSendThroughputColumn(), - getReceiveThroughputColumn(), - getPingTimeColumn(), - getClockSkewColumn(), - ]; - - return columns.map((column) => { - return {...column, sortable: isSortableNodesColumn(column.name)}; - }); -} diff --git a/src/containers/Tenant/Diagnostics/Network/NetworkWrapper.tsx b/src/containers/Tenant/Diagnostics/Network/NetworkWrapper.tsx index 4f3adae4e5..061329e49e 100644 --- a/src/containers/Tenant/Diagnostics/Network/NetworkWrapper.tsx +++ b/src/containers/Tenant/Diagnostics/Network/NetworkWrapper.tsx @@ -1,21 +1,10 @@ import {LoaderWrapper} from '../../../../components/LoaderWrapper/LoaderWrapper'; -import { - useCapabilitiesLoaded, - useViewerNodesHandlerHasNetworkStats, -} from '../../../../store/reducers/capabilities/hooks'; -import {ENABLE_NETWORK_TABLE_KEY} from '../../../../utils/constants'; -import {useSetting} from '../../../../utils/hooks'; +import {NetworkTable} from '../../../../components/NetworkTable/NetworkTable'; +import {useShouldShowDatabaseNetworkTable} from '../../../../components/NetworkTable/hooks'; +import {useCapabilitiesLoaded} from '../../../../store/reducers/capabilities/hooks'; import type {NodesProps} from '../../../Nodes/Nodes'; -import {Nodes} from '../../../Nodes/Nodes'; import {Network} from './Network'; -import {getNetworkTableNodesColumns} from './NetworkTable/columns'; -import { - NETWORK_DEFAULT_NODES_COLUMNS, - NETWORK_NODES_GROUP_BY_PARAMS, - NETWORK_NODES_TABLE_SELECTED_COLUMNS_KEY, - NETWORK_REQUIRED_NODES_COLUMNS, -} from './NetworkTable/constants'; interface NetworkWrapperProps extends Pick { @@ -29,28 +18,16 @@ export function NetworkWrapper({ additionalNodesProps, }: NetworkWrapperProps) { const capabilitiesLoaded = useCapabilitiesLoaded(); - const viewerNodesHasNetworkStats = useViewerNodesHandlerHasNetworkStats(); - const [networkTableEnabled] = useSetting(ENABLE_NETWORK_TABLE_KEY); - - const shouldUseNetworkNodesTable = viewerNodesHasNetworkStats && networkTableEnabled; + const shouldUseNetworkNodesTable = useShouldShowDatabaseNetworkTable(); const renderContent = () => { if (shouldUseNetworkNodesTable) { return ( - ); } diff --git a/src/store/reducers/capabilities/hooks.ts b/src/store/reducers/capabilities/hooks.ts index eb6fc9d455..ca6ae32fba 100644 --- a/src/store/reducers/capabilities/hooks.ts +++ b/src/store/reducers/capabilities/hooks.ts @@ -67,6 +67,13 @@ export const useViewerNodesHandlerHasNetworkStats = () => { return useGetFeatureVersion('/viewer/nodes') > 13; }; +// Before this version handler has very big response size if nodes quantity is more than 100 +// Response size could be up to 20-30MB, it loads very long and freezes UI +// It is not very common for databases, but an often case for clusters +export const useNodesHandlerHasWorkingClusterNetworkStats = () => { + return useGetFeatureVersion('/viewer/nodes') >= 16; +}; + export const useFeatureFlagsAvailable = () => { return useGetFeatureVersion('/viewer/feature_flags') > 1; }; diff --git a/src/store/reducers/cluster/cluster.ts b/src/store/reducers/cluster/cluster.ts index 1fa396b9c8..403e883cbb 100644 --- a/src/store/reducers/cluster/cluster.ts +++ b/src/store/reducers/cluster/cluster.ts @@ -23,13 +23,15 @@ import { parseGroupsStatsQueryResponse, } from './utils'; +export const INITIAL_DEFAULT_CLUSTER_TAB = clusterTabsIds.tenants; + const defaultClusterTabLS = localStorage.getItem(DEFAULT_CLUSTER_TAB_KEY); let defaultClusterTab: ClusterTab; if (isClusterTab(defaultClusterTabLS)) { defaultClusterTab = defaultClusterTabLS; } else { - defaultClusterTab = clusterTabsIds.tenants; + defaultClusterTab = INITIAL_DEFAULT_CLUSTER_TAB; } const initialState: ClusterState = {