diff --git a/src/components/UsageLabel/UsageLabel.scss b/src/components/UsageLabel/UsageLabel.scss new file mode 100644 index 000000000..e93b0d435 --- /dev/null +++ b/src/components/UsageLabel/UsageLabel.scss @@ -0,0 +1,6 @@ +.ydb-usage-label { + &_overload { + color: var(--yc-color-text-light-primary); + background-color: var(--yc-color-base-danger-heavy); + } +} diff --git a/src/components/UsageLabel/UsageLabel.tsx b/src/components/UsageLabel/UsageLabel.tsx new file mode 100644 index 000000000..3a20e2df6 --- /dev/null +++ b/src/components/UsageLabel/UsageLabel.tsx @@ -0,0 +1,18 @@ +import cn from 'bem-cn-lite'; + +import {Label, type LabelProps} from '@gravity-ui/uikit'; + +const b = cn('ydb-usage-label'); + +interface UsageLabelProps extends LabelProps { + value: number | string; + overloadThreshold?: number; +} + +export function UsageLabel({value, overloadThreshold = 90, theme}: UsageLabelProps) { + return ( + + ); +} diff --git a/src/containers/Nodes/Nodes.scss b/src/containers/Nodes/Nodes.scss index 466375efa..74aacb99f 100644 --- a/src/containers/Nodes/Nodes.scss +++ b/src/containers/Nodes/Nodes.scss @@ -20,11 +20,4 @@ &__node_unavailable { opacity: 0.6; } - - &__usage-label { - &_overload { - color: var(--yc-color-text-light-primary); - background-color: var(--yc-color-base-danger-heavy); - } - } } diff --git a/src/containers/Nodes/getNodesColumns.tsx b/src/containers/Nodes/getNodesColumns.tsx index a3ce434ec..df8583057 100644 --- a/src/containers/Nodes/getNodesColumns.tsx +++ b/src/containers/Nodes/getNodesColumns.tsx @@ -1,7 +1,5 @@ -import cn from 'bem-cn-lite'; - import DataTable, {type Column} from '@gravity-ui/react-data-table'; -import {Label, Popover} from '@gravity-ui/uikit'; +import {Popover} from '@gravity-ui/uikit'; import {PoolsGraph} from '../../components/PoolsGraph/PoolsGraph'; import {ProgressViewer} from '../../components/ProgressViewer/ProgressViewer'; @@ -9,10 +7,9 @@ import {TabletsStatistic} from '../../components/TabletsStatistic'; import {NodeHostWrapper} from '../../components/NodeHostWrapper/NodeHostWrapper'; import {formatBytesToGigabyte} from '../../utils/dataFormatters/dataFormatters'; import type {NodesPreparedEntity} from '../../store/reducers/nodes/types'; -import type {NodeAddress} from '../../types/additionalProps'; +import type {GetNodeRefFunc} from '../../types/additionalProps'; import {getLoadSeverityForNode} from '../../store/reducers/tenantOverview/topNodesByLoad/utils'; - -const b = cn('node'); +import {UsageLabel} from '../../components/UsageLabel/UsageLabel'; const NODES_COLUMNS_IDS = { NodeId: 'NodeId', @@ -25,11 +22,12 @@ const NODES_COLUMNS_IDS = { CPU: 'CPU', LoadAverage: 'LoadAverage', Tablets: 'Tablets', + TopNodesLoadAverage: 'TopNodesLoadAverage', }; interface GetNodesColumnsProps { tabletsPath?: string; - getNodeRef?: (node?: NodeAddress) => string | null; + getNodeRef?: GetNodeRefFunc; } const nodeIdColumn: Column = { @@ -41,7 +39,7 @@ const nodeIdColumn: Column = { }; const getHostColumn = ( - getNodeRef?: (node?: NodeAddress) => string | null, + getNodeRef?: GetNodeRefFunc, fixedWidth = false, ): Column => ({ name: NODES_COLUMNS_IDS.Host, @@ -154,21 +152,14 @@ const getTabletsColumn = (tabletsPath?: string): Column => }); const topNodesLoadAverageColumn: Column = { - name: NODES_COLUMNS_IDS.LoadAverage, + name: NODES_COLUMNS_IDS.TopNodesLoadAverage, header: 'Load', - sortAccessor: ({LoadAverage = []}) => - LoadAverage.slice(0, 1).reduce((acc, item) => acc + item, 0), - defaultOrder: DataTable.DESCENDING, render: ({row}) => row.LoadAverage && row.LoadAverage.length > 0 ? ( - <> - - + ) : ( '—' ), @@ -195,14 +186,14 @@ export function getNodesColumns({ ]; } -export function getTopNodesColumns( - getNodeRef?: (node?: NodeAddress) => string | null, +export function getTopNodesByLoadColumns( + getNodeRef?: GetNodeRefFunc, ): Column[] { return [topNodesLoadAverageColumn, nodeIdColumn, getHostColumn(getNodeRef), versionColumn]; } -export function getTopPoolsColumns( - getNodeRef?: (node?: NodeAddress) => string | null, +export function getTopNodesByCpuColumns( + getNodeRef?: GetNodeRefFunc, ): Column[] { return [cpuColumn, nodeIdColumn, getHostColumn(getNodeRef)]; } diff --git a/src/containers/Tenant/Diagnostics/TenantOverview/TenantCpu/TenantCpu.scss b/src/containers/Tenant/Diagnostics/TenantOverview/TenantCpu/TenantCpu.scss deleted file mode 100644 index 66903d7ce..000000000 --- a/src/containers/Tenant/Diagnostics/TenantOverview/TenantCpu/TenantCpu.scss +++ /dev/null @@ -1,31 +0,0 @@ -@import '../../../../../styles/mixins.scss'; - -.tenant-overview-cpu { - &__title { - margin-bottom: 10px; - - font-size: var(--yc-text-body-2-font-size); - font-weight: 700; - line-height: var(--yc-text-body-2-line-height); - } - - &__table { - width: var(--diagnostics-section-table-width); - - @include table-styles(); - - &:not(:last-child) { - margin-bottom: var(--diagnostics-section-margin); - } - - th { - height: 40px; - - vertical-align: middle; - } - - &_top-queries tr { - cursor: pointer; - } - } -} diff --git a/src/containers/Tenant/Diagnostics/TenantOverview/TenantCpu/TenantCpu.tsx b/src/containers/Tenant/Diagnostics/TenantOverview/TenantCpu/TenantCpu.tsx index ff91aeb8d..bf4df1d27 100644 --- a/src/containers/Tenant/Diagnostics/TenantOverview/TenantCpu/TenantCpu.tsx +++ b/src/containers/Tenant/Diagnostics/TenantOverview/TenantCpu/TenantCpu.tsx @@ -1,7 +1,4 @@ -import {useSelector} from 'react-redux'; - import type {AdditionalNodesProps} from '../../../../../types/additionalProps'; -import type {EPathType, TEvDescribeSchemeResult} from '../../../../../types/api/schema'; import {TopNodesByLoad} from './TopNodesByLoad'; import {TopNodesByCpu} from './TopNodesByCpu'; import {TopShards} from './TopShards'; @@ -13,21 +10,10 @@ interface TenantCpuProps { } export function TenantCpu({path, additionalNodesProps}: TenantCpuProps) { - const {currentSchemaPath, currentSchema: currentItem = {}} = useSelector( - (state: any) => state.schema, - ); - const {PathType: preloadedPathType} = useSelector( - (state: any) => state.schema.data[currentSchemaPath]?.PathDescription?.Self || {}, - ); - const {PathType: currentPathType} = - (currentItem as TEvDescribeSchemeResult).PathDescription?.Self || {}; - - const type: EPathType | undefined = preloadedPathType || currentPathType; - return ( <> - - + + diff --git a/src/containers/Tenant/Diagnostics/TenantOverview/TenantCpu/TopNodesByCpu.tsx b/src/containers/Tenant/Diagnostics/TenantOverview/TenantCpu/TopNodesByCpu.tsx index 90c90f26b..c91ce7503 100644 --- a/src/containers/Tenant/Diagnostics/TenantOverview/TenantCpu/TopNodesByCpu.tsx +++ b/src/containers/Tenant/Diagnostics/TenantOverview/TenantCpu/TopNodesByCpu.tsx @@ -1,45 +1,30 @@ -import cn from 'bem-cn-lite'; import {useDispatch} from 'react-redux'; import {useCallback} from 'react'; -import DataTable from '@gravity-ui/react-data-table'; - -import { - TENANT_OVERVIEW_TABLES_LIMIT, - TENANT_OVERVIEW_TABLES_SETTINGS, -} from '../../../../../utils/constants'; import {useAutofetcher, useTypedSelector} from '../../../../../utils/hooks'; import { - getTopComputeNodesByCpu, getTopNodesByCpu, selectTopNodesByCpu, setDataWasNotLoaded, } from '../../../../../store/reducers/tenantOverview/topNodesByCpu/topNodesByCpu'; -import type {EPathType} from '../../../../../types/api/schema'; import type {AdditionalNodesProps} from '../../../../../types/additionalProps'; -import {TableSkeleton} from '../../../../../components/TableSkeleton/TableSkeleton'; -import {ResponseError} from '../../../../../components/Errors/ResponseError'; -import {getTopPoolsColumns} from '../../../../Nodes/getNodesColumns'; -import {isDatabaseEntityType} from '../../../utils/schema'; +import {getTopNodesByCpuColumns} from '../../../../Nodes/getNodesColumns'; +import {TenantOverviewTableLayout} from '../TenantOverviewTableLayout'; import i18n from '../i18n'; -import './TenantCpu.scss'; - -const b = cn('tenant-overview-cpu'); interface TopNodesByCpuProps { - path?: string; - type?: EPathType; + path: string; additionalNodesProps?: AdditionalNodesProps; } -export function TopNodesByCpu({path, type, additionalNodesProps}: TopNodesByCpuProps) { +export function TopNodesByCpu({path, additionalNodesProps}: TopNodesByCpuProps) { const dispatch = useDispatch(); const {wasLoaded, loading, error} = useTypedSelector((state) => state.topNodesByCpu); const {autorefresh} = useTypedSelector((state) => state.schema); const topNodes = useTypedSelector(selectTopNodesByCpu); - const columns = getTopPoolsColumns(additionalNodesProps?.getNodeRef); + const columns = getTopNodesByCpuColumns(additionalNodesProps?.getNodeRef); const fetchNodes = useCallback( (isBackground) => { @@ -47,43 +32,22 @@ export function TopNodesByCpu({path, type, additionalNodesProps}: TopNodesByCpuP dispatch(setDataWasNotLoaded()); } - // For not DB entities we always use /compute endpoint instead of /nodes - // since /nodes can return data only for tenants - if (path && !isDatabaseEntityType(type)) { - dispatch(getTopComputeNodesByCpu({path})); - } else { - dispatch(getTopNodesByCpu({tenant: path})); - } + dispatch(getTopNodesByCpu({tenant: path})); }, - [dispatch, path, type], + [dispatch, path], ); useAutofetcher(fetchNodes, [fetchNodes], autorefresh); - const renderContent = () => { - if (error) { - return ; - } - - if (loading && !wasLoaded) { - return ; - } - - return ( - - ); - }; - return ( - <> -
Top nodes by pools usage
-
{renderContent()}
- + ); } diff --git a/src/containers/Tenant/Diagnostics/TenantOverview/TenantCpu/TopNodesByLoad.tsx b/src/containers/Tenant/Diagnostics/TenantOverview/TenantCpu/TopNodesByLoad.tsx index 257e756fd..2870f4bb2 100644 --- a/src/containers/Tenant/Diagnostics/TenantOverview/TenantCpu/TopNodesByLoad.tsx +++ b/src/containers/Tenant/Diagnostics/TenantOverview/TenantCpu/TopNodesByLoad.tsx @@ -1,45 +1,30 @@ -import cn from 'bem-cn-lite'; import {useDispatch} from 'react-redux'; import {useCallback} from 'react'; -import DataTable from '@gravity-ui/react-data-table'; - -import { - TENANT_OVERVIEW_TABLES_LIMIT, - TENANT_OVERVIEW_TABLES_SETTINGS, -} from '../../../../../utils/constants'; import {useAutofetcher, useTypedSelector} from '../../../../../utils/hooks'; import { - getTopComputeNodesByLoad, getTopNodesByLoad, selectTopNodesByLoad, setDataWasNotLoaded, } from '../../../../../store/reducers/tenantOverview/topNodesByLoad/topNodesByLoad'; -import type {EPathType} from '../../../../../types/api/schema'; import type {AdditionalNodesProps} from '../../../../../types/additionalProps'; -import {TableSkeleton} from '../../../../../components/TableSkeleton/TableSkeleton'; -import {ResponseError} from '../../../../../components/Errors/ResponseError'; -import {getTopNodesColumns} from '../../../../Nodes/getNodesColumns'; -import {isDatabaseEntityType} from '../../../utils/schema'; +import {getTopNodesByLoadColumns} from '../../../../Nodes/getNodesColumns'; +import {TenantOverviewTableLayout} from '../TenantOverviewTableLayout'; import i18n from '../i18n'; -import './TenantCpu.scss'; - -const b = cn('tenant-overview-cpu'); interface TopNodesByLoadProps { - path?: string; - type?: EPathType; + path: string; additionalNodesProps?: AdditionalNodesProps; } -export function TopNodesByLoad({path, type, additionalNodesProps}: TopNodesByLoadProps) { +export function TopNodesByLoad({path, additionalNodesProps}: TopNodesByLoadProps) { const dispatch = useDispatch(); const {wasLoaded, loading, error} = useTypedSelector((state) => state.topNodesByLoad); const {autorefresh} = useTypedSelector((state) => state.schema); const topNodes = useTypedSelector(selectTopNodesByLoad); - const columns = getTopNodesColumns(additionalNodesProps?.getNodeRef); + const columns = getTopNodesByLoadColumns(additionalNodesProps?.getNodeRef); const fetchNodes = useCallback( (isBackground) => { @@ -47,43 +32,22 @@ export function TopNodesByLoad({path, type, additionalNodesProps}: TopNodesByLoa dispatch(setDataWasNotLoaded()); } - // For not DB entities we always use /compute endpoint instead of /nodes - // since /nodes can return data only for tenants - if (path && !isDatabaseEntityType(type)) { - dispatch(getTopComputeNodesByLoad({path})); - } else { - dispatch(getTopNodesByLoad({tenant: path})); - } + dispatch(getTopNodesByLoad({tenant: path})); }, - [dispatch, path, type], + [dispatch, path], ); useAutofetcher(fetchNodes, [fetchNodes], autorefresh); - const renderContent = () => { - if (error) { - return ; - } - - if (loading && !wasLoaded) { - return ; - } - - return ( - - ); - }; - return ( - <> -
Top nodes by load
-
{renderContent()}
- + ); } diff --git a/src/containers/Tenant/Diagnostics/TenantOverview/TenantCpu/TopQueries.tsx b/src/containers/Tenant/Diagnostics/TenantOverview/TenantCpu/TopQueries.tsx index b16e42be9..254bc697c 100644 --- a/src/containers/Tenant/Diagnostics/TenantOverview/TenantCpu/TopQueries.tsx +++ b/src/containers/Tenant/Diagnostics/TenantOverview/TenantCpu/TopQueries.tsx @@ -1,37 +1,25 @@ -import cn from 'bem-cn-lite'; import qs from 'qs'; import {useDispatch} from 'react-redux'; import {useHistory, useLocation} from 'react-router'; import {useCallback} from 'react'; -import DataTable from '@gravity-ui/react-data-table'; - -import { - TENANT_OVERVIEW_TABLES_LIMIT, - TENANT_OVERVIEW_TABLES_SETTINGS, -} from '../../../../../utils/constants'; import { TENANT_PAGE, TENANT_PAGES_IDS, TENANT_QUERY_TABS_ID, } from '../../../../../store/reducers/tenant/constants'; import { - fetchTopQueries, + fetchTenantOverviewTopQueries, setDataWasNotLoaded, -} from '../../../../../store/reducers/tenantOverview/executeTopQueries/executeTopQueries'; +} from '../../../../../store/reducers/tenantOverview/topQueries/tenantOverviewTopQueries'; import {changeUserInput} from '../../../../../store/reducers/executeQuery'; import {useAutofetcher, useTypedSelector} from '../../../../../utils/hooks'; -import {ResponseError} from '../../../../../components/Errors/ResponseError'; -import {TableSkeleton} from '../../../../../components/TableSkeleton/TableSkeleton'; import {TenantTabsGroups, getTenantPath} from '../../../TenantPages'; import {getTenantOverviewTopQueriesColumns} from '../../TopQueries/getTopQueriesColumns'; - -import './TenantCpu.scss'; - -const b = cn('tenant-overview-cpu'); +import {TenantOverviewTableLayout} from '../TenantOverviewTableLayout'; interface TopQueriesProps { - path?: string; + path: string; } export function TopQueries({path}: TopQueriesProps) { @@ -46,7 +34,7 @@ export function TopQueries({path}: TopQueriesProps) { wasLoaded, error, data: {result: data = undefined} = {}, - } = useTypedSelector((state) => state.tenantOverviewExecuteTopQueries); + } = useTypedSelector((state) => state.tenantOverviewTopQueries); const columns = getTenantOverviewTopQueriesColumns(); useAutofetcher( @@ -55,9 +43,7 @@ export function TopQueries({path}: TopQueriesProps) { dispatch(setDataWasNotLoaded()); } - // @ts-expect-error - // typed dispatch required, remove error expectation after adding it - dispatch(fetchTopQueries(path)); + dispatch(fetchTenantOverviewTopQueries(path)); }, [dispatch, path], autorefresh, @@ -84,30 +70,16 @@ export function TopQueries({path}: TopQueriesProps) { [dispatch, history, location], ); - const renderContent = () => { - if (error) { - return ; - } - - if (loading && !wasLoaded) { - return ; - } - - return ( - - ); - }; - return ( - <> -
Top queries by cpu time
-
{renderContent()}
- + ); } diff --git a/src/containers/Tenant/Diagnostics/TenantOverview/TenantCpu/TopShards.tsx b/src/containers/Tenant/Diagnostics/TenantOverview/TenantCpu/TopShards.tsx index b5d1bb61c..bf01c4bdf 100644 --- a/src/containers/Tenant/Diagnostics/TenantOverview/TenantCpu/TopShards.tsx +++ b/src/containers/Tenant/Diagnostics/TenantOverview/TenantCpu/TopShards.tsx @@ -1,25 +1,16 @@ -import {useMemo} from 'react'; import {useDispatch} from 'react-redux'; -import cn from 'bem-cn-lite'; -import DataTable, {type Column} from '@gravity-ui/react-data-table'; +import {type Column} from '@gravity-ui/react-data-table'; -import { - TENANT_OVERVIEW_TABLES_LIMIT, - TENANT_OVERVIEW_TABLES_SETTINGS, -} from '../../../../../utils/constants'; import {getSchema, setCurrentSchemaPath} from '../../../../../store/reducers/schema/schema'; import {useAutofetcher, useTypedSelector} from '../../../../../utils/hooks'; import type {KeyValueRow} from '../../../../../types/api/query'; import { - sendTopShardsQuery, + sendTenantOverviewTopShardsQuery, setDataWasNotLoaded, -} from '../../../../../store/reducers/tenantOverview/executeTopShards/executeTopShards'; -import {ResponseError} from '../../../../../components/Errors/ResponseError'; -import {TableSkeleton} from '../../../../../components/TableSkeleton/TableSkeleton'; +} from '../../../../../store/reducers/tenantOverview/topShards/tenantOverviewTopShards'; import {getTopShardsColumns} from '../../TopShards/getTopShardsColumns'; - -const b = cn('tenant-overview-cpu'); +import {TenantOverviewTableLayout} from '../TenantOverviewTableLayout'; interface TopShardsProps { path: string; @@ -35,56 +26,38 @@ export const TopShards = ({path}: TopShardsProps) => { data: {result: data = undefined} = {}, error, wasLoaded, - } = useTypedSelector((state) => state.executeTopShards); + } = useTypedSelector((state) => state.tenantOverviewTopShards); useAutofetcher( (isBackground) => { if (!isBackground) { dispatch(setDataWasNotLoaded()); } - dispatch(sendTopShardsQuery(path, currentSchemaPath)); + dispatch(sendTenantOverviewTopShardsQuery(path, currentSchemaPath)); }, [dispatch, path, currentSchemaPath], autorefresh, ); - const columns = useMemo(() => { - const onSchemaClick = (schemaPath: string) => { - return () => { - dispatch(setCurrentSchemaPath(schemaPath)); - dispatch(getSchema({path: schemaPath})); - history.go(0); - }; + const onSchemaClick = (schemaPath: string) => { + return () => { + dispatch(setCurrentSchemaPath(schemaPath)); + dispatch(getSchema({path: schemaPath})); + history.go(0); }; - - const tableColumns: Column[] = getTopShardsColumns(onSchemaClick, path); - - return tableColumns; - }, [dispatch, path]); - - const renderContent = () => { - if (error) { - return ; - } - - if (loading && !wasLoaded) { - return ; - } - - return ( - - ); }; + const columns: Column[] = getTopShardsColumns(onSchemaClick, path); + return ( - <> -
Top shards by cpu usage
-
{renderContent()}
- + ); }; diff --git a/src/containers/Tenant/Diagnostics/TenantOverview/TenantOverview.scss b/src/containers/Tenant/Diagnostics/TenantOverview/TenantOverview.scss index 1290d7ee7..9f1b8f1fb 100644 --- a/src/containers/Tenant/Diagnostics/TenantOverview/TenantOverview.scss +++ b/src/containers/Tenant/Diagnostics/TenantOverview/TenantOverview.scss @@ -1,3 +1,5 @@ +@import '../../../../styles/mixins.scss'; + .tenant-overview { padding-bottom: 20px; @@ -78,4 +80,32 @@ font-weight: 600; line-height: var(--yc-text-body-2-line-height); } + + &__title { + margin-bottom: 10px; + + font-size: var(--yc-text-body-2-font-size); + font-weight: 700; + line-height: var(--yc-text-body-2-line-height); + } + + &__table { + width: var(--diagnostics-section-table-width); + + @include table-styles(); + + &:not(:last-child) { + margin-bottom: var(--diagnostics-section-margin); + } + + th { + height: 40px; + + vertical-align: middle; + } + + &_top-queries tr { + cursor: pointer; + } + } } diff --git a/src/containers/Tenant/Diagnostics/TenantOverview/TenantOverviewTableLayout.tsx b/src/containers/Tenant/Diagnostics/TenantOverview/TenantOverviewTableLayout.tsx new file mode 100644 index 000000000..3c03f0a80 --- /dev/null +++ b/src/containers/Tenant/Diagnostics/TenantOverview/TenantOverviewTableLayout.tsx @@ -0,0 +1,62 @@ +import cn from 'bem-cn-lite'; + +import DataTable from '@gravity-ui/react-data-table'; +import type {DataTableProps, THEMES} from '@gravity-ui/react-data-table'; + +import { + TENANT_OVERVIEW_TABLES_LIMIT, + TENANT_OVERVIEW_TABLES_SETTINGS, +} from '../../../../utils/constants'; +import type {IResponseError} from '../../../../types/api/error'; +import {TableSkeleton} from '../../../../components/TableSkeleton/TableSkeleton'; +import {ResponseError} from '../../../../components/Errors/ResponseError'; + +const b = cn('tenant-overview'); + +interface TenantOverviewTableLayoutProps extends Omit, 'theme'> { + theme?: THEMES | string; + title: string; + loading?: boolean; + wasLoaded?: boolean; + error?: IResponseError; + tableClassNameModifiers?: { + [name: string]: string | boolean | undefined; + }; +} + +export function TenantOverviewTableLayout({ + title, + data, + columns, + error, + loading, + wasLoaded, + emptyDataMessage, + tableClassNameModifiers = {}, +}: TenantOverviewTableLayoutProps) { + const renderContent = () => { + if (error) { + return ; + } + + if (loading && !wasLoaded) { + return ; + } + + return ( + + ); + }; + return ( + <> +
{title}
+
{renderContent()}
+ + ); +} diff --git a/src/containers/Tenant/Diagnostics/TopShards/TopShards.scss b/src/containers/Tenant/Diagnostics/TopShards/TopShards.scss index 4f794d380..c4b433620 100644 --- a/src/containers/Tenant/Diagnostics/TopShards/TopShards.scss +++ b/src/containers/Tenant/Diagnostics/TopShards/TopShards.scss @@ -16,11 +16,4 @@ overflow: auto; flex-grow: 1; } - - &__usage-label { - &_overload { - color: var(--yc-color-text-light-primary); - background-color: var(--yc-color-base-danger-heavy); - } - } } diff --git a/src/containers/Tenant/Diagnostics/TopShards/getTopShardsColumns.tsx b/src/containers/Tenant/Diagnostics/TopShards/getTopShardsColumns.tsx index d9d54c287..c1e5cc80f 100644 --- a/src/containers/Tenant/Diagnostics/TopShards/getTopShardsColumns.tsx +++ b/src/containers/Tenant/Diagnostics/TopShards/getTopShardsColumns.tsx @@ -1,20 +1,19 @@ import cn from 'bem-cn-lite'; import DataTable, {type Column} from '@gravity-ui/react-data-table'; -import {Label} from '@gravity-ui/uikit'; import type {KeyValueRow} from '../../../../types/api/query'; import type {ValueOf} from '../../../../types/common'; import {formatNumber, roundToPrecision} from '../../../../utils/dataFormatters/dataFormatters'; -import {getLoadSeverityForShard} from '../../../../store/reducers/tenantOverview/executeTopShards/utils'; +import {getLoadSeverityForShard} from '../../../../store/reducers/tenantOverview/topShards/utils'; import {InternalLink} from '../../../../components/InternalLink'; import routes, {createHref} from '../../../../routes'; import {getDefaultNodePath} from '../../../Node/NodePages'; +import {UsageLabel} from '../../../../components/UsageLabel/UsageLabel'; import './TopShards.scss'; -const b = cn('top-shards'); -const bLink = cn('yc-link'); +const b = cn('yc-link'); const TOP_SHARDS_COLUMNS_IDS = { TabletId: 'TabletId', @@ -53,10 +52,7 @@ const getPathColumn = ( render: ({row}) => { // row.Path - relative schema path return ( - + {row.Path} ); @@ -115,14 +111,10 @@ const topShardsCpuCoresColumn: Column = { header: tableColumnsNames[TOP_SHARDS_COLUMNS_IDS.CPUCores], render: ({row}) => { return ( - <> - - + ); }, align: DataTable.RIGHT, diff --git a/src/store/reducers/index.ts b/src/store/reducers/index.ts index 41bb568a6..131135630 100644 --- a/src/store/reducers/index.ts +++ b/src/store/reducers/index.ts @@ -1,8 +1,8 @@ import {combineReducers} from 'redux'; import nodes from './nodes/nodes'; -import topNodesByLoad from './tenantOverview/topNodesByLoad/topNodesByLoad'; -import topNodesByCpu from './tenantOverview/topNodesByCpu/topNodesByCpu'; +import {topNodesByLoad} from './tenantOverview/topNodesByLoad/topNodesByLoad'; +import {topNodesByCpu} from './tenantOverview/topNodesByCpu/topNodesByCpu'; import cluster from './cluster/cluster'; import clusterNodes from './clusterNodes/clusterNodes'; import tenant from './tenant/tenant'; @@ -29,11 +29,11 @@ import nodesList from './nodesList'; import describe from './describe'; import schemaAcl from './schemaAcl/schemaAcl'; import executeTopQueries from './executeTopQueries/executeTopQueries'; -import tenantOverviewExecuteTopQueries from './tenantOverview/executeTopQueries/executeTopQueries'; +import {tenantOverviewTopQueries} from './tenantOverview/topQueries/tenantOverviewTopQueries'; import executeTopTables from './tenantOverview/executeTopTables/executeTopTables'; import healthcheckInfo from './healthcheckInfo'; import shardsWorkload from './shardsWorkload/shardsWorkload'; -import executeTopShards from './tenantOverview/executeTopShards/executeTopShards'; +import {tenantOverviewTopShards} from './tenantOverview/topShards/tenantOverviewTopShards'; import hotKeys from './hotKeys'; import olapStats from './olapStats'; import authentication from './authentication/authentication'; @@ -75,10 +75,10 @@ export const rootReducer = { schemaAcl, executeTopQueries, executeTopTables, - tenantOverviewExecuteTopQueries, + tenantOverviewTopQueries, healthcheckInfo, shardsWorkload, - executeTopShards, + tenantOverviewTopShards, hotKeys, authentication, header, diff --git a/src/store/reducers/tenantOverview/executeTopQueries/types.ts b/src/store/reducers/tenantOverview/executeTopQueries/types.ts deleted file mode 100644 index 00adfba5e..000000000 --- a/src/store/reducers/tenantOverview/executeTopQueries/types.ts +++ /dev/null @@ -1,18 +0,0 @@ -import type {IQueryResult, QueryErrorResponse} from '../../../../types/store/query'; -import type {ApiRequestAction} from '../../../utils'; -import {FETCH_TOP_QUERIES, setDataWasNotLoaded} from './executeTopQueries'; - -export interface TopQueriesState { - loading: boolean; - wasLoaded: boolean; - data?: IQueryResult; - error?: QueryErrorResponse; -} - -export type TopQueriesAction = - | ApiRequestAction - | ReturnType; - -export interface TopQueriesSlice { - executeTopQueries: TopQueriesState; -} diff --git a/src/store/reducers/tenantOverview/topNodesByCpu/topNodesByCpu.ts b/src/store/reducers/tenantOverview/topNodesByCpu/topNodesByCpu.ts index c56cfe224..e7dcbb802 100644 --- a/src/store/reducers/tenantOverview/topNodesByCpu/topNodesByCpu.ts +++ b/src/store/reducers/tenantOverview/topNodesByCpu/topNodesByCpu.ts @@ -1,12 +1,10 @@ import type {Reducer} from 'redux'; import {TENANT_OVERVIEW_TABLES_LIMIT} from '../../../../utils/constants'; -import {EVersion} from '../../../../types/api/compute'; import {createApiRequest, createRequestActionTypes} from '../../../utils'; import {prepareNodesData} from '../../nodes/utils'; -import type {ComputeApiRequestParams, NodesApiRequestParams} from '../../nodes/types'; +import type {NodesApiRequestParams} from '../../nodes/types'; import type {TopNodesByCpuAction, TopNodesByCpuState, TopPoolsStateSlice} from './types'; -import {prepareTopComputeNodesData} from './utils'; export const FETCH_TOP_NODES_BY_CPU = createRequestActionTypes( 'topNodesByCpu', @@ -19,7 +17,7 @@ const initialState = { wasLoaded: false, }; -const topNodesByCpu: Reducer = ( +export const topNodesByCpu: Reducer = ( state = initialState, action, ) => { @@ -80,23 +78,6 @@ export function getTopNodesByCpu({ }); } -export function getTopComputeNodesByCpu({ - sortOrder = -1, - sortValue = 'CPU', - limit = TENANT_OVERVIEW_TABLES_LIMIT, - version = EVersion.v2, - ...params -}: ComputeApiRequestParams) { - return createApiRequest({ - request: window.api.getCompute( - {sortOrder, sortValue, limit, version, ...params}, - {concurrentId}, - ), - actions: FETCH_TOP_NODES_BY_CPU, - dataHandler: prepareTopComputeNodesData, - }); -} - export const selectTopNodesByCpu = (state: TopPoolsStateSlice) => state.topNodesByCpu.data; export const setDataWasNotLoaded = () => { @@ -104,5 +85,3 @@ export const setDataWasNotLoaded = () => { type: SET_DATA_WAS_NOT_LOADED, } as const; }; - -export default topNodesByCpu; diff --git a/src/store/reducers/tenantOverview/topNodesByCpu/utils.ts b/src/store/reducers/tenantOverview/topNodesByCpu/utils.ts deleted file mode 100644 index 249601d94..000000000 --- a/src/store/reducers/tenantOverview/topNodesByCpu/utils.ts +++ /dev/null @@ -1,25 +0,0 @@ -import type {TComputeInfo} from '../../../../types/api/compute'; -import {prepareComputeNodes} from '../../nodes/utils'; -import type {TopNodesByCpuHandledResponse} from './types'; - -export const prepareTopComputeNodesData = (data: TComputeInfo): TopNodesByCpuHandledResponse => { - const preparedNodes = prepareComputeNodes(data.Nodes, data.Tenants); - - if (!data.Nodes) { - preparedNodes.sort((a, b) => { - let aMaxPoolUsage = 0; - let bMaxPoolUsage = 0; - if (a.PoolStats) { - aMaxPoolUsage = Math.max(...a.PoolStats.map(({Usage}) => Number(Usage))); - } - if (b.PoolStats) { - bMaxPoolUsage = Math.max(...b.PoolStats.map(({Usage}) => Number(Usage))); - } - return bMaxPoolUsage - aMaxPoolUsage; - }); - } - - return { - Nodes: preparedNodes, - }; -}; diff --git a/src/store/reducers/tenantOverview/topNodesByLoad/topNodesByLoad.ts b/src/store/reducers/tenantOverview/topNodesByLoad/topNodesByLoad.ts index 27591ebf8..16ed110e6 100644 --- a/src/store/reducers/tenantOverview/topNodesByLoad/topNodesByLoad.ts +++ b/src/store/reducers/tenantOverview/topNodesByLoad/topNodesByLoad.ts @@ -1,12 +1,10 @@ import type {Reducer} from 'redux'; import {TENANT_OVERVIEW_TABLES_LIMIT} from '../../../../utils/constants'; -import {EVersion} from '../../../../types/api/compute'; import {createApiRequest, createRequestActionTypes} from '../../../utils'; import {prepareNodesData} from '../../nodes/utils'; -import type {ComputeApiRequestParams, NodesApiRequestParams} from '../../nodes/types'; +import type {NodesApiRequestParams} from '../../nodes/types'; import type {TopNodesByLoadAction, TopNodesByLoadState, TopNodesByLoadStateSlice} from './types'; -import {prepareTopComputeNodesData} from './utils'; export const FETCH_TOP_NODES_BY_LOAD = createRequestActionTypes( 'topNodesByLoad', @@ -19,7 +17,7 @@ const initialState = { wasLoaded: false, }; -const topNodesByLoad: Reducer = ( +export const topNodesByLoad: Reducer = ( state = initialState, action, ) => { @@ -80,23 +78,6 @@ export function getTopNodesByLoad({ }); } -export function getTopComputeNodesByLoad({ - sortOrder = -1, - sortValue = 'LoadAverage', - limit = TENANT_OVERVIEW_TABLES_LIMIT, - version = EVersion.v2, - ...params -}: ComputeApiRequestParams) { - return createApiRequest({ - request: window.api.getCompute( - {sortOrder, sortValue, limit, version, ...params}, - {concurrentId}, - ), - actions: FETCH_TOP_NODES_BY_LOAD, - dataHandler: prepareTopComputeNodesData, - }); -} - export const selectTopNodesByLoad = (state: TopNodesByLoadStateSlice) => state.topNodesByLoad.data; export const setDataWasNotLoaded = () => { @@ -104,5 +85,3 @@ export const setDataWasNotLoaded = () => { type: SET_DATA_WAS_NOT_LOADED, } as const; }; - -export default topNodesByLoad; diff --git a/src/store/reducers/tenantOverview/topNodesByLoad/utils.ts b/src/store/reducers/tenantOverview/topNodesByLoad/utils.ts index 0a59ea5ea..95dfdca4b 100644 --- a/src/store/reducers/tenantOverview/topNodesByLoad/utils.ts +++ b/src/store/reducers/tenantOverview/topNodesByLoad/utils.ts @@ -1,20 +1,3 @@ -import type {TComputeInfo} from '../../../../types/api/compute'; import {generateEvaluator} from '../../../../utils/generateEvaluator'; -import {prepareComputeNodes} from '../../nodes/utils'; -import type {TopNodesByLoadHandledResponse} from './types'; - -export const prepareTopComputeNodesData = (data: TComputeInfo): TopNodesByLoadHandledResponse => { - const preparedNodes = prepareComputeNodes(data.Nodes, data.Tenants); - - let sortedNodes = preparedNodes; - - if (!data.Nodes) { - sortedNodes = preparedNodes.sort((a, b) => Number(b.LoadAverage) - Number(a.LoadAverage)); - } - - return { - Nodes: sortedNodes, - }; -}; export const getLoadSeverityForNode = generateEvaluator(60, 80, ['success', 'warning', 'danger']); diff --git a/src/store/reducers/tenantOverview/executeTopQueries/executeTopQueries.ts b/src/store/reducers/tenantOverview/topQueries/tenantOverviewTopQueries.ts similarity index 71% rename from src/store/reducers/tenantOverview/executeTopQueries/executeTopQueries.ts rename to src/store/reducers/tenantOverview/topQueries/tenantOverviewTopQueries.ts index 0f14793a2..280b84db3 100644 --- a/src/store/reducers/tenantOverview/executeTopQueries/executeTopQueries.ts +++ b/src/store/reducers/tenantOverview/topQueries/tenantOverviewTopQueries.ts @@ -5,10 +5,13 @@ import {TENANT_OVERVIEW_TABLES_LIMIT} from '../../../../utils/constants'; import {parseQueryAPIExecuteResponse} from '../../../../utils/query'; import {createRequestActionTypes, createApiRequest} from '../../../utils'; -import type {TopQueriesAction, TopQueriesState} from './types'; +import type {TenantOverviewTopQueriesAction, TenantOverviewTopQueriesState} from './types'; -export const FETCH_TOP_QUERIES = createRequestActionTypes('topQueries', 'FETCH_TOP_QUERIES'); -const SET_DATA_WAS_NOT_LOADED = 'topQueries/SET_DATA_WAS_NOT_LOADED'; +export const FETCH_TENANT_OVERVIEW_TOP_QUERIES = createRequestActionTypes( + 'tenantOverviewTopQueries', + 'FETCH_TOP_QUERIES', +); +const SET_DATA_WAS_NOT_LOADED = 'tenantOverviewTopQueries/SET_DATA_WAS_NOT_LOADED'; const initialState = { loading: false, @@ -27,19 +30,19 @@ LIMIT ${TENANT_OVERVIEW_TABLES_LIMIT} `; }; -const executeTopQueries: Reducer = ( - state = initialState, - action, -) => { +export const tenantOverviewTopQueries: Reducer< + TenantOverviewTopQueriesState, + TenantOverviewTopQueriesAction +> = (state = initialState, action) => { switch (action.type) { - case FETCH_TOP_QUERIES.REQUEST: { + case FETCH_TENANT_OVERVIEW_TOP_QUERIES.REQUEST: { return { ...state, loading: true, error: undefined, }; } - case FETCH_TOP_QUERIES.SUCCESS: { + case FETCH_TENANT_OVERVIEW_TOP_QUERIES.SUCCESS: { return { ...state, data: action.data, @@ -49,7 +52,7 @@ const executeTopQueries: Reducer = ( }; } // 401 Unauthorized error is handled by GenericAPI - case FETCH_TOP_QUERIES.FAILURE: { + case FETCH_TENANT_OVERVIEW_TOP_QUERIES.FAILURE: { return { ...state, error: action.error || 'Unauthorized', @@ -66,7 +69,7 @@ const executeTopQueries: Reducer = ( } }; -export const fetchTopQueries = (database: string) => +export const fetchTenantOverviewTopQueries = (database: string) => createApiRequest({ request: window.api.sendQuery( { @@ -79,7 +82,7 @@ export const fetchTopQueries = (database: string) => concurrentId: 'executeTopQueries', }, ), - actions: FETCH_TOP_QUERIES, + actions: FETCH_TENANT_OVERVIEW_TOP_QUERIES, dataHandler: parseQueryAPIExecuteResponse, }); @@ -88,5 +91,3 @@ export function setDataWasNotLoaded() { type: SET_DATA_WAS_NOT_LOADED, } as const; } - -export default executeTopQueries; diff --git a/src/store/reducers/tenantOverview/topQueries/types.ts b/src/store/reducers/tenantOverview/topQueries/types.ts new file mode 100644 index 000000000..31764dc42 --- /dev/null +++ b/src/store/reducers/tenantOverview/topQueries/types.ts @@ -0,0 +1,14 @@ +import type {IQueryResult, QueryErrorResponse} from '../../../../types/store/query'; +import type {ApiRequestAction} from '../../../utils'; +import {FETCH_TENANT_OVERVIEW_TOP_QUERIES, setDataWasNotLoaded} from './tenantOverviewTopQueries'; + +export interface TenantOverviewTopQueriesState { + loading: boolean; + wasLoaded: boolean; + data?: IQueryResult; + error?: QueryErrorResponse; +} + +export type TenantOverviewTopQueriesAction = + | ApiRequestAction + | ReturnType; diff --git a/src/store/reducers/tenantOverview/executeTopShards/executeTopShards.ts b/src/store/reducers/tenantOverview/topShards/tenantOverviewTopShards.ts similarity index 73% rename from src/store/reducers/tenantOverview/executeTopShards/executeTopShards.ts rename to src/store/reducers/tenantOverview/topShards/tenantOverviewTopShards.ts index d946b533f..f8ba12c53 100644 --- a/src/store/reducers/tenantOverview/executeTopShards/executeTopShards.ts +++ b/src/store/reducers/tenantOverview/topShards/tenantOverviewTopShards.ts @@ -4,10 +4,13 @@ import '../../../../services/api'; import {TENANT_OVERVIEW_TABLES_LIMIT} from '../../../../utils/constants'; import {parseQueryAPIExecuteResponse} from '../../../../utils/query'; import {createRequestActionTypes, createApiRequest} from '../../../utils'; -import {TopShardsAction, TopShardsState} from './types'; +import {TenantOverviewTopShardsAction, TenantOverviewTopShardsState} from './types'; -export const FETCH_TOP_SHARDS = createRequestActionTypes('topShards', 'FETCH_TOP_SHARDS'); -const SET_DATA_WAS_NOT_LOADED = 'topShards/SET_DATA_WAS_NOT_LOADED'; +export const FETCH_TENANT_OVERVIEW_TOP_SHARDS = createRequestActionTypes( + 'tenantOverviewTopShards', + 'FETCH_TENANT_OVERVIEW_TOP_SHARDS', +); +const SET_DATA_WAS_NOT_LOADED = 'tenantOverviewTopShards/SET_DATA_WAS_NOT_LOADED'; const initialState = { loading: false, @@ -33,19 +36,19 @@ LIMIT ${TENANT_OVERVIEW_TABLES_LIMIT}`; const queryAction = 'execute-scan'; -const executeTopShards: Reducer = ( - state = initialState, - action, -) => { +export const tenantOverviewTopShards: Reducer< + TenantOverviewTopShardsState, + TenantOverviewTopShardsAction +> = (state = initialState, action) => { switch (action.type) { - case FETCH_TOP_SHARDS.REQUEST: { + case FETCH_TENANT_OVERVIEW_TOP_SHARDS.REQUEST: { return { ...state, loading: true, error: undefined, }; } - case FETCH_TOP_SHARDS.SUCCESS: { + case FETCH_TENANT_OVERVIEW_TOP_SHARDS.SUCCESS: { return { ...state, data: action.data, @@ -55,7 +58,7 @@ const executeTopShards: Reducer = ( }; } // 401 Unauthorized error is handled by GenericAPI - case FETCH_TOP_SHARDS.FAILURE: { + case FETCH_TENANT_OVERVIEW_TOP_SHARDS.FAILURE: { if (action.error?.isCancelled) { return state; } @@ -76,7 +79,7 @@ const executeTopShards: Reducer = ( } }; -export const sendTopShardsQuery = (database: string, path = '') => +export const sendTenantOverviewTopShardsQuery = (database: string, path = '') => createApiRequest({ request: window.api.sendQuery( { @@ -89,7 +92,7 @@ export const sendTopShardsQuery = (database: string, path = '') => concurrentId: 'executeTopShards', }, ), - actions: FETCH_TOP_SHARDS, + actions: FETCH_TENANT_OVERVIEW_TOP_SHARDS, dataHandler: parseQueryAPIExecuteResponse, }); @@ -98,5 +101,3 @@ export function setDataWasNotLoaded() { type: SET_DATA_WAS_NOT_LOADED, } as const; } - -export default executeTopShards; diff --git a/src/store/reducers/tenantOverview/executeTopShards/types.ts b/src/store/reducers/tenantOverview/topShards/types.ts similarity index 50% rename from src/store/reducers/tenantOverview/executeTopShards/types.ts rename to src/store/reducers/tenantOverview/topShards/types.ts index 55bf935f2..d679e3109 100644 --- a/src/store/reducers/tenantOverview/executeTopShards/types.ts +++ b/src/store/reducers/tenantOverview/topShards/types.ts @@ -1,18 +1,14 @@ import type {IQueryResult, QueryErrorResponse} from '../../../../types/store/query'; import type {ApiRequestAction} from '../../../utils'; -import {FETCH_TOP_SHARDS, setDataWasNotLoaded} from './executeTopShards'; +import {FETCH_TENANT_OVERVIEW_TOP_SHARDS, setDataWasNotLoaded} from './tenantOverviewTopShards'; -export interface TopShardsState { +export interface TenantOverviewTopShardsState { loading: boolean; wasLoaded: boolean; data?: IQueryResult; error?: QueryErrorResponse; } -export type TopShardsAction = - | ApiRequestAction +export type TenantOverviewTopShardsAction = + | ApiRequestAction | ReturnType; - -export interface TopShardsSlice { - topShards: TopShardsState; -} diff --git a/src/store/reducers/tenantOverview/executeTopShards/utils.ts b/src/store/reducers/tenantOverview/topShards/utils.ts similarity index 100% rename from src/store/reducers/tenantOverview/executeTopShards/utils.ts rename to src/store/reducers/tenantOverview/topShards/utils.ts diff --git a/src/types/additionalProps.ts b/src/types/additionalProps.ts index 59d03477e..724b28f76 100644 --- a/src/types/additionalProps.ts +++ b/src/types/additionalProps.ts @@ -26,6 +26,8 @@ export interface AdditionalTenantsProps { export type NodeAddress = Pick; +export type GetNodeRefFunc = (node?: NodeAddress) => string | null; + export interface AdditionalNodesProps extends Record { - getNodeRef?: (node?: NodeAddress) => string | null; + getNodeRef?: GetNodeRefFunc; }