ydb-embedded-ui 4.16.2 → 4.18.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (99) hide show
  1. package/CHANGELOG.md +30 -0
  2. package/dist/components/CircularProgressBar/CircularProgressBar.scss +42 -0
  3. package/dist/components/CircularProgressBar/CircularProgressBar.tsx +59 -0
  4. package/dist/components/DiagnosticCard/DiagnosticCard.scss +20 -3
  5. package/dist/components/DiagnosticCard/DiagnosticCard.tsx +5 -6
  6. package/dist/components/FullNodeViewer/FullNodeViewer.tsx +2 -2
  7. package/dist/components/InfoViewer/formatters/common.ts +1 -1
  8. package/dist/components/InfoViewer/formatters/pqGroup.ts +1 -1
  9. package/dist/components/InfoViewer/formatters/table.ts +6 -1
  10. package/dist/components/ProgressViewer/ProgressViewer.tsx +99 -0
  11. package/dist/components/TooltipsContent/TabletTooltipContent/TabletTooltipContent.tsx +1 -1
  12. package/dist/containers/AsideNavigation/AsideNavigation.tsx +3 -1
  13. package/dist/containers/Authentication/Authentication.tsx +14 -4
  14. package/dist/containers/Cluster/ClusterInfo/ClusterInfo.tsx +2 -2
  15. package/dist/containers/Heatmap/Heatmap.tsx +1 -1
  16. package/dist/containers/Heatmap/Histogram/Histogram.js +1 -1
  17. package/dist/containers/Node/NodeStructure/Pdisk.tsx +2 -2
  18. package/dist/containers/Node/NodeStructure/Vdisk.tsx +5 -2
  19. package/dist/containers/Nodes/getNodesColumns.tsx +2 -2
  20. package/dist/containers/Storage/PDisk/PDisk.tsx +1 -1
  21. package/dist/containers/Storage/PDiskPopup/PDiskPopup.tsx +1 -1
  22. package/dist/containers/Storage/StorageGroups/StorageGroups.tsx +1 -1
  23. package/dist/containers/Storage/VDisk/VDisk.tsx +1 -1
  24. package/dist/containers/Storage/VDiskPopup/VDiskPopup.tsx +1 -1
  25. package/dist/containers/Tablet/TabletInfo/TabletInfo.tsx +1 -1
  26. package/dist/containers/Tablet/TabletTable/TabletTable.tsx +1 -1
  27. package/dist/containers/Tenant/Diagnostics/Consumers/TopicStats/ConsumersTopicStats.tsx +1 -1
  28. package/dist/containers/Tenant/Diagnostics/Consumers/columns/columns.tsx +1 -1
  29. package/dist/containers/Tenant/Diagnostics/DetailedOverview/DetailedOverview.scss +1 -5
  30. package/dist/containers/Tenant/Diagnostics/DetailedOverview/DetailedOverview.tsx +40 -17
  31. package/dist/containers/Tenant/Diagnostics/{Healthcheck → OldHealthcheck}/Details/Details.tsx +3 -3
  32. package/dist/containers/Tenant/Diagnostics/{Healthcheck → OldHealthcheck}/Healthcheck.scss +27 -14
  33. package/dist/containers/Tenant/Diagnostics/{Healthcheck → OldHealthcheck}/Healthcheck.tsx +6 -6
  34. package/dist/containers/Tenant/Diagnostics/{Healthcheck → OldHealthcheck}/Preview/Preview.tsx +15 -16
  35. package/dist/containers/Tenant/Diagnostics/Overview/TableInfo/prepareTableInfo.ts +1 -1
  36. package/dist/containers/Tenant/Diagnostics/Overview/TopicStats/TopicStats.tsx +1 -1
  37. package/dist/containers/Tenant/Diagnostics/Partitions/columns/columns.tsx +1 -1
  38. package/dist/containers/Tenant/Diagnostics/TenantOverview/Healthcheck/Healthcheck.scss +108 -0
  39. package/dist/containers/Tenant/Diagnostics/TenantOverview/Healthcheck/HealthcheckDetails.tsx +45 -0
  40. package/dist/containers/Tenant/Diagnostics/TenantOverview/Healthcheck/HealthcheckPreview.tsx +83 -0
  41. package/dist/containers/Tenant/Diagnostics/{Healthcheck → TenantOverview/Healthcheck}/IssuesViewer/IssueTree.scss +1 -3
  42. package/dist/containers/Tenant/Diagnostics/{Healthcheck → TenantOverview/Healthcheck}/IssuesViewer/IssueTree.tsx +1 -1
  43. package/dist/containers/Tenant/Diagnostics/{Healthcheck → TenantOverview/Healthcheck}/IssuesViewer/IssueTreeItem/IssueTreeItem.tsx +1 -1
  44. package/dist/containers/Tenant/Diagnostics/{Healthcheck → TenantOverview/Healthcheck}/i18n/en.json +2 -1
  45. package/dist/containers/Tenant/Diagnostics/{Healthcheck → TenantOverview/Healthcheck}/i18n/index.ts +1 -1
  46. package/dist/containers/Tenant/Diagnostics/{Healthcheck → TenantOverview/Healthcheck}/i18n/ru.json +2 -1
  47. package/dist/containers/Tenant/Diagnostics/TenantOverview/MetricsCards/MetricCard/MetricCard.scss +52 -0
  48. package/dist/containers/Tenant/Diagnostics/TenantOverview/MetricsCards/MetricCard/MetricCard.tsx +48 -0
  49. package/dist/containers/Tenant/Diagnostics/TenantOverview/MetricsCards/MetricsCards.scss +12 -0
  50. package/dist/containers/Tenant/Diagnostics/TenantOverview/MetricsCards/MetricsCards.tsx +134 -0
  51. package/dist/containers/Tenant/Diagnostics/TenantOverview/OldTenantOverview.tsx +155 -0
  52. package/dist/containers/Tenant/Diagnostics/TenantOverview/TenantOverview.scss +3 -5
  53. package/dist/containers/Tenant/Diagnostics/TenantOverview/TenantOverview.tsx +76 -86
  54. package/dist/containers/Tenant/Diagnostics/TenantOverview/i18n/en.json +3 -1
  55. package/dist/containers/Tenant/Diagnostics/TenantOverview/i18n/ru.json +3 -1
  56. package/dist/containers/Tenant/Diagnostics/TenantOverview/useHealthcheck.ts +53 -0
  57. package/dist/containers/Tenant/Diagnostics/TopQueries/TopQueries.tsx +1 -1
  58. package/dist/containers/Tenant/Diagnostics/TopShards/TopShards.tsx +1 -1
  59. package/dist/containers/Tenant/ObjectSummary/ObjectSummary.tsx +1 -1
  60. package/dist/containers/Tenant/TenantPages.tsx +1 -0
  61. package/dist/containers/Tenants/Tenants.tsx +5 -1
  62. package/dist/containers/UserSettings/i18n/en.json +4 -1
  63. package/dist/containers/UserSettings/i18n/ru.json +4 -1
  64. package/dist/containers/UserSettings/settings.ts +7 -0
  65. package/dist/containers/Versions/NodesTable/NodesTable.tsx +2 -2
  66. package/dist/services/api.ts +6 -4
  67. package/dist/store/reducers/clusterNodes/clusterNodes.tsx +1 -1
  68. package/dist/store/reducers/healthcheckInfo.ts +20 -12
  69. package/dist/store/reducers/node/selectors.ts +1 -1
  70. package/dist/store/reducers/nodes/selectors.ts +1 -1
  71. package/dist/store/reducers/nodes/types.ts +1 -1
  72. package/dist/store/reducers/nodes/utils.ts +1 -1
  73. package/dist/store/reducers/settings/settings.ts +6 -1
  74. package/dist/store/reducers/storage/types.ts +1 -1
  75. package/dist/store/reducers/storage/utils.ts +1 -1
  76. package/dist/store/reducers/tenant/constants.ts +7 -0
  77. package/dist/store/reducers/tenant/tenant.ts +15 -0
  78. package/dist/store/reducers/tenant/types.ts +5 -0
  79. package/dist/store/reducers/tenants/contants.ts +6 -0
  80. package/dist/store/reducers/tenants/types.ts +4 -0
  81. package/dist/store/reducers/tenants/utils.ts +114 -7
  82. package/dist/store/state-url-mapping.js +3 -0
  83. package/dist/styles/constants.scss +2 -0
  84. package/dist/types/api/tenant.ts +3 -0
  85. package/dist/types/api/vdisk.ts +1 -1
  86. package/dist/utils/bytesParsers/formatBytes.ts +1 -1
  87. package/dist/utils/constants.ts +2 -0
  88. package/dist/utils/dataFormatters/dataFormatters.ts +128 -0
  89. package/dist/utils/dataFormatters/i18n/en.json +3 -0
  90. package/dist/utils/dataFormatters/i18n/index.ts +11 -0
  91. package/dist/utils/dataFormatters/i18n/ru.json +3 -0
  92. package/dist/utils/index.js +0 -102
  93. package/package.json +1 -1
  94. package/dist/components/ProgressViewer/ProgressViewer.js +0 -92
  95. /package/dist/containers/Tenant/Diagnostics/{Healthcheck → OldHealthcheck}/Details/index.ts +0 -0
  96. /package/dist/containers/Tenant/Diagnostics/{Healthcheck → OldHealthcheck}/Preview/index.ts +0 -0
  97. /package/dist/containers/Tenant/Diagnostics/{Healthcheck → OldHealthcheck}/index.ts +0 -0
  98. /package/dist/containers/Tenant/Diagnostics/{Healthcheck → TenantOverview/Healthcheck}/IssuesViewer/IssueTreeItem/IssueTreeItem.scss +0 -0
  99. /package/dist/containers/Tenant/Diagnostics/{Healthcheck → TenantOverview/Healthcheck}/IssuesViewer/IssueTreeItem/index.ts +0 -0
@@ -0,0 +1,53 @@
1
+ import {useCallback} from 'react';
2
+ import {useDispatch} from 'react-redux';
3
+
4
+ import {
5
+ getHealthcheckInfo,
6
+ selectIssuesStatistics,
7
+ selectIssuesTrees,
8
+ setDataWasNotLoaded,
9
+ } from '../../../../store/reducers/healthcheckInfo';
10
+ import type {IIssuesTree} from '../../../../types/store/healthcheck';
11
+ import {type StatusFlag, SelfCheckResult} from '../../../../types/api/healthcheck';
12
+ import type {IResponseError} from '../../../../types/api/error';
13
+ import {useTypedSelector} from '../../../../utils/hooks/useTypedSelector';
14
+
15
+ interface HealthcheckParams {
16
+ issueTrees: IIssuesTree[];
17
+ issuesStatistics: [StatusFlag, number][];
18
+ fetchHealthcheck: (isBackground?: boolean) => void;
19
+ loading: boolean;
20
+ wasLoaded: boolean;
21
+ error?: IResponseError;
22
+ selfCheckResult: SelfCheckResult;
23
+ }
24
+
25
+ export const useHealthcheck = (tenantName: string): HealthcheckParams => {
26
+ const dispatch = useDispatch();
27
+
28
+ const {data, loading, wasLoaded, error} = useTypedSelector((state) => state.healthcheckInfo);
29
+ const selfCheckResult = data?.self_check_result || SelfCheckResult.UNSPECIFIED;
30
+ const issuesStatistics = useTypedSelector(selectIssuesStatistics);
31
+ const issueTrees = useTypedSelector(selectIssuesTrees);
32
+
33
+ const fetchHealthcheck = useCallback(
34
+ (isBackground = true) => {
35
+ if (!isBackground) {
36
+ dispatch(setDataWasNotLoaded());
37
+ }
38
+
39
+ dispatch(getHealthcheckInfo(tenantName));
40
+ },
41
+ [dispatch, tenantName],
42
+ );
43
+
44
+ return {
45
+ issueTrees,
46
+ issuesStatistics,
47
+ fetchHealthcheck,
48
+ loading,
49
+ wasLoaded,
50
+ error,
51
+ selfCheckResult,
52
+ };
53
+ };
@@ -28,7 +28,7 @@ import {
28
28
  TENANT_PAGES_IDS,
29
29
  TENANT_QUERY_TABS_ID,
30
30
  } from '../../../../store/reducers/tenant/constants';
31
- import {formatDateTime, formatNumber} from '../../../../utils';
31
+ import {formatDateTime, formatNumber} from '../../../../utils/dataFormatters/dataFormatters';
32
32
  import {HOUR_IN_SECONDS} from '../../../../utils/constants';
33
33
  import {useAutofetcher, useTypedSelector} from '../../../../utils/hooks';
34
34
  import {prepareQueryError} from '../../../../utils/query';
@@ -22,7 +22,7 @@ import {EShardsWorkloadMode, IShardsWorkloadFilters} from '../../../../types/sto
22
22
  import type {EPathType} from '../../../../types/api/schema';
23
23
  import type {CellValue, KeyValueRow} from '../../../../types/api/query';
24
24
 
25
- import {formatDateTime, formatNumber} from '../../../../utils';
25
+ import {formatDateTime, formatNumber} from '../../../../utils/dataFormatters/dataFormatters';
26
26
  import {DEFAULT_TABLE_SETTINGS, HOUR_IN_SECONDS} from '../../../../utils/constants';
27
27
  import {useAutofetcher, useTypedSelector} from '../../../../utils/hooks';
28
28
  import {prepareQueryError} from '../../../../utils/query';
@@ -24,7 +24,7 @@ import {
24
24
  TColumnTableDescription,
25
25
  } from '../../../types/api/schema';
26
26
  import routes, {createHref} from '../../../routes';
27
- import {formatDateTime} from '../../../utils';
27
+ import {formatDateTime} from '../../../utils/dataFormatters/dataFormatters';
28
28
  import {useTypedSelector} from '../../../utils/hooks';
29
29
  import {
30
30
  DEFAULT_IS_TENANT_COMMON_INFO_COLLAPSED,
@@ -5,6 +5,7 @@ export const TenantTabsGroups = {
5
5
  summaryTab: 'summaryTab',
6
6
  queryTab: 'queryTab',
7
7
  diagnosticsTab: 'diagnosticsTab',
8
+ metricsTab: 'metricsTab',
8
9
  } as const;
9
10
 
10
11
  export const TENANT_INFO_TABS = [
@@ -26,7 +26,11 @@ import {
26
26
  ProblemFilterValues,
27
27
  selectProblemFilter,
28
28
  } from '../../store/reducers/settings/settings';
29
- import {formatCPU, formatBytesToGigabyte, formatNumber} from '../../utils';
29
+ import {
30
+ formatBytesToGigabyte,
31
+ formatCPU,
32
+ formatNumber,
33
+ } from '../../utils/dataFormatters/dataFormatters';
30
34
  import {DEFAULT_TABLE_SETTINGS} from '../../utils/constants';
31
35
  import {useAutofetcher, useTypedSelector} from '../../utils/hooks';
32
36
  import {clusterName} from '../../store';
@@ -23,5 +23,8 @@
23
23
  "settings.useBackendParamsForTables.popover": "Filter and sort Nodes and Storage tables with request params. May increase performance, but could causes additional fetches and longer loading time on older versions",
24
24
 
25
25
  "settings.enableAdditionalQueryModes.title": "Enable additional query modes",
26
- "settings.enableAdditionalQueryModes.popover": "Adds 'Data', 'YQL - QueryService' and 'PostgreSQL' modes. May not work on some versions"
26
+ "settings.enableAdditionalQueryModes.popover": "Adds 'Data', 'YQL - QueryService' and 'PostgreSQL' modes. May not work on some versions",
27
+
28
+ "settings.tenantDiagnostics.title": "Display metrics cards for database diagnostics",
29
+ "settings.tenantDiagnostics.popover": "Adds indicators of database resources usage. Incomplete data may be displayed for some databases"
27
30
  }
@@ -23,5 +23,8 @@
23
23
  "settings.useBackendParamsForTables.popover": "Добавляет фильтрацию и сортировку таблиц Nodes и Storage с использованием параметров запроса. Может улушить производительность, но на старых версиях может привести к дополнительным запросам и большему времени ожидания загрузки",
24
24
 
25
25
  "settings.enableAdditionalQueryModes.title": "Включить дополнительные режимы выполнения запросов",
26
- "settings.enableAdditionalQueryModes.popover": "Добавляет режимы 'Data', 'YQL - QueryService' и 'PostgreSQL'. Может работать некорректно на некоторых версиях"
26
+ "settings.enableAdditionalQueryModes.popover": "Добавляет режимы 'Data', 'YQL - QueryService' и 'PostgreSQL'. Может работать некорректно на некоторых версиях",
27
+
28
+ "settings.tenantDiagnostics.title": "Показывать карточки с метриками в диагностике базы данных",
29
+ "settings.tenantDiagnostics.popover": "Добавляет индикаторы использования ресурсов базы данных. Для некоторых баз могут отображаться неполные данные"
27
30
  }
@@ -5,6 +5,7 @@ import flaskIcon from '../../assets/icons/flask.svg';
5
5
 
6
6
  import {
7
7
  ENABLE_ADDITIONAL_QUERY_MODES,
8
+ DISPLAY_METRICS_CARDS_FOR_TENANT_DIAGNOSTICS,
8
9
  INVERTED_DISKS_KEY,
9
10
  LANGUAGE_KEY,
10
11
  THEME_KEY,
@@ -94,6 +95,11 @@ export const enableQueryModesForExplainSetting: SettingProps = {
94
95
  title: i18n('settings.enableAdditionalQueryModes.title'),
95
96
  helpPopoverContent: i18n('settings.enableAdditionalQueryModes.popover'),
96
97
  };
98
+ export const enableNewTenantDiagnosticsDesign: SettingProps = {
99
+ settingKey: DISPLAY_METRICS_CARDS_FOR_TENANT_DIAGNOSTICS,
100
+ title: i18n('settings.tenantDiagnostics.title'),
101
+ helpPopoverContent: i18n('settings.tenantDiagnostics.popover'),
102
+ };
97
103
 
98
104
  export const generalSection: SettingsSection = {
99
105
  id: 'generalSection',
@@ -108,6 +114,7 @@ export const experimentsSection: SettingsSection = {
108
114
  useNodesEndpointSetting,
109
115
  useBackendParamsForTables,
110
116
  enableQueryModesForExplainSetting,
117
+ enableNewTenantDiagnosticsDesign,
111
118
  ],
112
119
  };
113
120
 
@@ -3,10 +3,10 @@ import DataTable, {Column} from '@gravity-ui/react-data-table';
3
3
  import type {PreparedClusterNode} from '../../../store/reducers/clusterNodes/types';
4
4
  import {isUnavailableNode} from '../../../utils/nodes';
5
5
  import {DEFAULT_TABLE_SETTINGS} from '../../../utils/constants';
6
- import {formatBytes} from '../../../utils';
6
+ import {formatBytes} from '../../../utils/dataFormatters/dataFormatters';
7
7
  import {getDefaultNodePath} from '../../Node/NodePages';
8
8
 
9
- import ProgressViewer from '../../../components/ProgressViewer/ProgressViewer';
9
+ import {ProgressViewer} from '../../../components/ProgressViewer/ProgressViewer';
10
10
  import {PoolsGraph} from '../../../components/PoolsGraph/PoolsGraph';
11
11
  import EntityStatus from '../../../components/EntityStatus/EntityStatus';
12
12
 
@@ -350,10 +350,12 @@ export class YdbEmbeddedAPI extends AxiosWrapper {
350
350
  enable_sampling: enableSampling,
351
351
  });
352
352
  }
353
- getHealthcheckInfo(database: string) {
354
- return this.get<HealthCheckAPIResponse>(this.getPath('/viewer/json/healthcheck'), {
355
- tenant: database,
356
- });
353
+ getHealthcheckInfo(database: string, {concurrentId}: AxiosOptions = {}) {
354
+ return this.get<HealthCheckAPIResponse>(
355
+ this.getPath('/viewer/json/healthcheck?merge_records=true'),
356
+ {tenant: database},
357
+ {concurrentId},
358
+ );
357
359
  }
358
360
  killTablet(id?: string) {
359
361
  return this.get<string>(this.getPath(`/tablets?KillTabletID=${id}`), {});
@@ -5,7 +5,7 @@ import {createRequestActionTypes, createApiRequest} from '../../utils';
5
5
  import type {ClusterNodesAction, ClusterNodesState, PreparedClusterNode} from './types';
6
6
 
7
7
  import '../../../services/api';
8
- import {calcUptime} from '../../../utils';
8
+ import {calcUptime} from '../../../utils/dataFormatters/dataFormatters';
9
9
 
10
10
  export const FETCH_CLUSTER_NODES = createRequestActionTypes('cluster', 'FETCH_CLUSTER_NODES');
11
11
 
@@ -44,6 +44,10 @@ const healthcheckInfo: Reducer<IHealthcheckInfoState, IHealthCheckInfoAction> =
44
44
  };
45
45
  }
46
46
  case FETCH_HEALTHCHECK.FAILURE: {
47
+ if (action.error?.isCancelled) {
48
+ return state;
49
+ }
50
+
47
51
  return {
48
52
  ...state,
49
53
  error: action.error,
@@ -72,21 +76,25 @@ const mapStatusToPriority: Partial<Record<StatusFlag, number>> = {
72
76
  GREEN: 4,
73
77
  };
74
78
 
75
- const getReasonsForIssue = ({issue, data}: {issue: IssueLog; data: IssueLog[]}) => {
76
- return data.filter((item) => issue.reason && issue.reason.indexOf(item.id) !== -1);
77
- };
79
+ const sortIssues = (data: IssueLog[]): IssueLog[] => {
80
+ return data.sort((a, b) => {
81
+ const aPriority = mapStatusToPriority[a.status] || 0;
82
+ const bPriority = mapStatusToPriority[b.status] || 0;
78
83
 
79
- const getRoots = (data: IssueLog[]): IssueLog[] => {
80
- let roots = data.filter((item) => {
81
- return !data.find((issue) => issue.reason && issue.reason.indexOf(item.id) !== -1);
84
+ return aPriority - bPriority;
82
85
  });
86
+ };
83
87
 
84
- roots = _flow([
85
- _uniqBy((item: IssueLog) => item.id),
86
- _sortBy(({status}: {status: StatusFlag}) => mapStatusToPriority[status]),
87
- ])(roots);
88
+ const getReasonsForIssue = ({issue, data}: {issue: IssueLog; data: IssueLog[]}) => {
89
+ return sortIssues(data.filter((item) => issue.reason && issue.reason.indexOf(item.id) !== -1));
90
+ };
88
91
 
89
- return roots;
92
+ const getRoots = (data: IssueLog[]): IssueLog[] => {
93
+ return sortIssues(
94
+ data.filter((item) => {
95
+ return !data.find((issue) => issue.reason && issue.reason.indexOf(item.id) !== -1);
96
+ }),
97
+ );
90
98
  };
91
99
 
92
100
  const getInvertedConsequencesTree = ({
@@ -147,7 +155,7 @@ export const selectIssuesStatistics: Selector<
147
155
 
148
156
  export function getHealthcheckInfo(database: string) {
149
157
  return createApiRequest({
150
- request: window.api.getHealthcheckInfo(database),
158
+ request: window.api.getHealthcheckInfo(database, {concurrentId: 'getHealthcheckInfo'}),
151
159
  actions: FETCH_HEALTHCHECK,
152
160
  });
153
161
  }
@@ -1,7 +1,7 @@
1
1
  import type {Selector} from 'reselect';
2
2
  import {createSelector} from 'reselect';
3
3
 
4
- import {stringifyVdiskId} from '../../../utils';
4
+ import {stringifyVdiskId} from '../../../utils/dataFormatters/dataFormatters';
5
5
 
6
6
  import type {
7
7
  NodeStateSlice,
@@ -1,7 +1,7 @@
1
1
  import {Selector, createSelector} from 'reselect';
2
2
 
3
3
  import {EFlag} from '../../../types/api/enums';
4
- import {calcUptimeInSeconds} from '../../../utils';
4
+ import {calcUptimeInSeconds} from '../../../utils/dataFormatters/dataFormatters';
5
5
  import {HOUR_IN_SECONDS} from '../../../utils/constants';
6
6
  import {NodesUptimeFilterValues} from '../../../utils/nodes';
7
7
  import {prepareSearchValue} from '../../../utils/filters';
@@ -64,7 +64,7 @@ export interface NodesGeneralRequestParams extends NodesSortParams {
64
64
  uptime?: number; // return nodes with less uptime in seconds
65
65
  problems_only?: boolean; // return nodes with SystemState !== EFlag.Green
66
66
 
67
- offser?: number;
67
+ offset?: number;
68
68
  limit?: number;
69
69
  }
70
70
 
@@ -1,6 +1,6 @@
1
1
  import type {TComputeInfo, TComputeNodeInfo} from '../../../types/api/compute';
2
2
  import type {TNodesInfo} from '../../../types/api/nodes';
3
- import {calcUptime} from '../../../utils';
3
+ import {calcUptime} from '../../../utils/dataFormatters/dataFormatters';
4
4
 
5
5
  import type {NodesHandledResponse, NodesPreparedEntity} from './types';
6
6
 
@@ -15,6 +15,7 @@ import {
15
15
  LAST_USED_QUERY_ACTION_KEY,
16
16
  USE_BACKEND_PARAMS_FOR_TABLES_KEY,
17
17
  LANGUAGE_KEY,
18
+ DISPLAY_METRICS_CARDS_FOR_TENANT_DIAGNOSTICS,
18
19
  } from '../../../utils/constants';
19
20
  import '../../../services/api';
20
21
  import {parseJson} from '../../../utils/utils';
@@ -49,12 +50,16 @@ export const initialState = {
49
50
  [INVERTED_DISKS_KEY]: readSavedSettingsValue(INVERTED_DISKS_KEY, 'false'),
50
51
  [USE_NODES_ENDPOINT_IN_DIAGNOSTICS_KEY]: readSavedSettingsValue(
51
52
  USE_NODES_ENDPOINT_IN_DIAGNOSTICS_KEY,
52
- 'false',
53
+ 'true',
53
54
  ),
54
55
  [ENABLE_ADDITIONAL_QUERY_MODES]: readSavedSettingsValue(
55
56
  ENABLE_ADDITIONAL_QUERY_MODES,
56
57
  'false',
57
58
  ),
59
+ [DISPLAY_METRICS_CARDS_FOR_TENANT_DIAGNOSTICS]: readSavedSettingsValue(
60
+ DISPLAY_METRICS_CARDS_FOR_TENANT_DIAGNOSTICS,
61
+ 'false',
62
+ ),
58
63
  [SAVED_QUERIES_KEY]: readSavedSettingsValue(SAVED_QUERIES_KEY, '[]'),
59
64
  [TENANT_INITIAL_PAGE_KEY]: readSavedSettingsValue(
60
65
  TENANT_INITIAL_PAGE_KEY,
@@ -63,7 +63,7 @@ export interface StorageSortParams {
63
63
  export interface StorageSortAndFilterParams extends StorageSortParams {
64
64
  filter?: string; // PoolName or GroupId
65
65
 
66
- offser?: number;
66
+ offset?: number;
67
67
  limit?: number;
68
68
  }
69
69
 
@@ -9,7 +9,7 @@ import {TPDiskState} from '../../../types/api/pdisk';
9
9
  import {EFlag} from '../../../types/api/enums';
10
10
  import {getPDiskType} from '../../../utils/pdisk';
11
11
  import {getUsage} from '../../../utils/storage';
12
- import {calcUptime} from '../../../utils';
12
+ import {calcUptime} from '../../../utils/dataFormatters/dataFormatters';
13
13
 
14
14
  import type {PreparedStorageGroup, PreparedStorageNode, PreparedStorageResponse} from './types';
15
15
 
@@ -31,3 +31,10 @@ export const TENANT_SUMMARY_TABS_IDS = {
31
31
  acl: 'acl',
32
32
  schema: 'schema',
33
33
  } as const;
34
+
35
+ export const TENANT_METRICS_TABS_IDS = {
36
+ cpu: 'cpu',
37
+ storage: 'storage',
38
+ memory: 'memory',
39
+ healthcheck: 'healthcheck',
40
+ } as const;
@@ -4,6 +4,7 @@ import type {TTenant} from '../../../types/api/tenant';
4
4
  import type {
5
5
  TenantAction,
6
6
  TenantDiagnosticsTab,
7
+ TenantMetricsTab,
7
8
  TenantPage,
8
9
  TenantQueryTab,
9
10
  TenantState,
@@ -19,6 +20,7 @@ const SET_TOP_LEVEL_TAB = 'tenant/SET_TOP_LEVEL_TAB';
19
20
  const SET_QUERY_TAB = 'tenant/SET_QUERY_TAB';
20
21
  const SET_DIAGNOSTICS_TAB = 'tenant/SET_DIAGNOSTICS_TAB';
21
22
  const SET_SUMMARY_TAB = 'tenant/SET_SUMMARY_TAB';
23
+ const SET_METRICS_TAB = 'tenant/SET_METRICS_TAB';
22
24
  const CLEAR_TENANT = 'tenant/CLEAR_TENANT';
23
25
  const SET_DATA_WAS_NOT_LOADED = 'tenant/SET_DATA_WAS_NOT_LOADED';
24
26
 
@@ -88,6 +90,12 @@ const tenantReducer: Reducer<TenantState, TenantAction> = (state = initialState,
88
90
  summaryTab: action.data,
89
91
  };
90
92
  }
93
+ case SET_METRICS_TAB: {
94
+ return {
95
+ ...state,
96
+ metricsTab: action.data,
97
+ };
98
+ }
91
99
 
92
100
  case SET_DATA_WAS_NOT_LOADED: {
93
101
  return {
@@ -143,6 +151,13 @@ export function setSummaryTab(tab: TenantSummaryTab) {
143
151
  } as const;
144
152
  }
145
153
 
154
+ export function setMetricsTab(tab: TenantMetricsTab) {
155
+ return {
156
+ type: SET_METRICS_TAB,
157
+ data: tab,
158
+ } as const;
159
+ }
160
+
146
161
  export const setDataWasNotLoaded = () => {
147
162
  return {
148
163
  type: SET_DATA_WAS_NOT_LOADED,
@@ -8,6 +8,7 @@ import {
8
8
  TENANT_DIAGNOSTICS_TABS_IDS,
9
9
  TENANT_PAGES_IDS,
10
10
  TENANT_SUMMARY_TABS_IDS,
11
+ TENANT_METRICS_TABS_IDS,
11
12
  } from './constants';
12
13
  import {
13
14
  FETCH_TENANT,
@@ -15,6 +16,7 @@ import {
15
16
  setDiagnosticsTab,
16
17
  setQueryTab,
17
18
  setSummaryTab,
19
+ setMetricsTab,
18
20
  setTenantPage,
19
21
  setDataWasNotLoaded,
20
22
  } from './tenant';
@@ -24,6 +26,7 @@ export type TenantPage = ValueOf<typeof TENANT_PAGES_IDS>;
24
26
  export type TenantQueryTab = ValueOf<typeof TENANT_QUERY_TABS_ID>;
25
27
  export type TenantDiagnosticsTab = ValueOf<typeof TENANT_DIAGNOSTICS_TABS_IDS>;
26
28
  export type TenantSummaryTab = ValueOf<typeof TENANT_SUMMARY_TABS_IDS>;
29
+ export type TenantMetricsTab = ValueOf<typeof TENANT_METRICS_TABS_IDS>;
27
30
 
28
31
  export interface TenantState {
29
32
  loading: boolean;
@@ -32,6 +35,7 @@ export interface TenantState {
32
35
  queryTab?: TenantQueryTab;
33
36
  diagnosticsTab?: TenantDiagnosticsTab;
34
37
  summaryTab?: TenantSummaryTab;
38
+ metricsTab?: TenantMetricsTab;
35
39
  tenant?: TTenant;
36
40
  error?: IResponseError;
37
41
  }
@@ -43,4 +47,5 @@ export type TenantAction =
43
47
  | ReturnType<typeof setQueryTab>
44
48
  | ReturnType<typeof setDiagnosticsTab>
45
49
  | ReturnType<typeof setSummaryTab>
50
+ | ReturnType<typeof setMetricsTab>
46
51
  | ReturnType<typeof setDataWasNotLoaded>;
@@ -0,0 +1,6 @@
1
+ export const METRIC_STATUS = {
2
+ Unspecified: 'Unspecified',
3
+ Good: 'Good',
4
+ Warning: 'Warning',
5
+ Danger: 'Danger',
6
+ } as const;
@@ -1,8 +1,10 @@
1
1
  import {FETCH_TENANTS, setSearchValue} from './tenants';
2
2
 
3
+ import {ValueOf} from '../../../types/common';
3
4
  import type {TTenant} from '../../../types/api/tenant';
4
5
  import type {IResponseError} from '../../../types/api/error';
5
6
  import type {ApiRequestAction} from '../../utils';
7
+ import {METRIC_STATUS} from './contants';
6
8
 
7
9
  export interface PreparedTenant extends TTenant {
8
10
  backend: string | undefined;
@@ -30,3 +32,5 @@ export type TenantsAction =
30
32
  export interface TenantsStateSlice {
31
33
  tenants: TenantsState;
32
34
  }
35
+
36
+ export type MetricStatus = ValueOf<typeof METRIC_STATUS>;
@@ -1,5 +1,8 @@
1
1
  import type {TTenant} from '../../../types/api/tenant';
2
+ import {formatBytes} from '../../../utils/bytesParsers';
3
+ import {formatCPUWithLabel} from '../../../utils/dataFormatters/dataFormatters';
2
4
  import {isNumeric} from '../../../utils/utils';
5
+ import {METRIC_STATUS} from './contants';
3
6
 
4
7
  const getControlPlaneValue = (tenant: TTenant) => {
5
8
  const parts = tenant.Name?.split('/');
@@ -18,21 +21,40 @@ const getTenantBackend = (tenant: TTenant) => {
18
21
  return node.Host ? `${node.Host}${address ? address : ''}` : undefined;
19
22
  };
20
23
 
21
- const calculateTenantMetrics = (tenant: TTenant) => {
22
- const {CoresUsed, MemoryUsed, StorageAllocatedSize, Metrics = {}} = tenant;
24
+ export const calculateTenantMetrics = (tenant?: TTenant) => {
25
+ const {
26
+ CoresUsed,
27
+ MemoryUsed,
28
+ StorageAllocatedSize,
29
+ CoresLimit,
30
+ MemoryLimit,
31
+ StorageLimit,
32
+ Metrics = {},
33
+ } = tenant || {};
23
34
 
24
35
  const cpuFromCores = isNumeric(CoresUsed) ? Number(CoresUsed) * 1_000_000 : undefined;
25
36
  const cpuFromMetrics = isNumeric(Metrics.CPU) ? Number(Metrics.CPU) : undefined;
26
37
 
27
- const cpu = cpuFromCores ?? cpuFromMetrics ?? 0;
38
+ const cpu = cpuFromCores ?? cpuFromMetrics ?? undefined;
28
39
 
29
40
  const rawMemory = MemoryUsed ?? Metrics.Memory;
30
- const rawStorage = StorageAllocatedSize ?? Metrics.Storage;
31
41
 
32
- const memory = isNumeric(rawMemory) ? Number(rawMemory) : 0;
33
- const storage = isNumeric(rawStorage) ? Number(rawStorage) : 0;
42
+ const memory = isNumeric(rawMemory) ? Number(rawMemory) : undefined;
34
43
 
35
- return {cpu, memory, storage};
44
+ // Blob storage - actual database size
45
+ const storage = isNumeric(StorageAllocatedSize) ? Number(StorageAllocatedSize) : undefined;
46
+ const cpuLimit = isNumeric(CoresLimit) ? Number(CoresLimit) : undefined;
47
+ const memoryLimit = isNumeric(MemoryLimit) ? Number(MemoryLimit) : undefined;
48
+ const storageLimit = isNumeric(StorageLimit) ? Number(StorageLimit) : undefined;
49
+
50
+ return {
51
+ cpu,
52
+ memory,
53
+ storage,
54
+ cpuLimit,
55
+ memoryLimit,
56
+ storageLimit,
57
+ };
36
58
  };
37
59
 
38
60
  const calculateTenantEntities = (tenant: TTenant) => {
@@ -66,3 +88,88 @@ export const prepareTenants = (tenants: TTenant[], useNodeAsBackend: boolean) =>
66
88
  };
67
89
  });
68
90
  };
91
+
92
+ export const calculateUsage = (valueUsed?: number, valueLimit?: number): number | undefined => {
93
+ if (valueUsed && valueLimit) {
94
+ return (valueUsed * 100) / valueLimit;
95
+ }
96
+
97
+ return undefined;
98
+ };
99
+
100
+ export const formatUsage = (usage?: number) => {
101
+ if (usage) {
102
+ return `${usage.toFixed()}%`;
103
+ }
104
+
105
+ return undefined;
106
+ };
107
+
108
+ export const cpuUsageToStatus = (usage?: number) => {
109
+ if (!usage) {
110
+ return METRIC_STATUS.Unspecified;
111
+ }
112
+
113
+ if (usage > 70) {
114
+ return METRIC_STATUS.Danger;
115
+ }
116
+ if (usage > 60) {
117
+ return METRIC_STATUS.Warning;
118
+ }
119
+
120
+ return METRIC_STATUS.Good;
121
+ };
122
+ export const storageUsageToStatus = (usage?: number) => {
123
+ if (!usage) {
124
+ return METRIC_STATUS.Unspecified;
125
+ }
126
+
127
+ if (usage > 85) {
128
+ return METRIC_STATUS.Danger;
129
+ }
130
+ if (usage > 75) {
131
+ return METRIC_STATUS.Warning;
132
+ }
133
+
134
+ return METRIC_STATUS.Good;
135
+ };
136
+
137
+ export const memoryUsageToStatus = (usage?: number) => {
138
+ if (!usage) {
139
+ return METRIC_STATUS.Unspecified;
140
+ }
141
+
142
+ if (usage > 70) {
143
+ return METRIC_STATUS.Danger;
144
+ }
145
+ if (usage > 60) {
146
+ return METRIC_STATUS.Warning;
147
+ }
148
+
149
+ return METRIC_STATUS.Good;
150
+ };
151
+
152
+ export const formatTenantMetrics = ({
153
+ cpu,
154
+ storage,
155
+ memory,
156
+ }: {
157
+ cpu?: number;
158
+ storage?: number;
159
+ memory?: number;
160
+ }) => ({
161
+ cpu: formatCPUWithLabel(cpu),
162
+ storage: formatBytes({value: storage, significantDigits: 2}) || undefined,
163
+ memory: formatBytes({value: memory, significantDigits: 2}) || undefined,
164
+ });
165
+
166
+ export const normalizeProgress = (progress: number) => {
167
+ if (progress >= 100) {
168
+ return 100;
169
+ }
170
+ if (progress <= 0) {
171
+ return 0;
172
+ }
173
+
174
+ return progress;
175
+ };
@@ -54,6 +54,9 @@ const paramSetup = {
54
54
  summaryTab: {
55
55
  stateKey: 'tenant.summaryTab',
56
56
  },
57
+ metricsTab: {
58
+ stateKey: 'tenant.metricsTab',
59
+ },
57
60
  shardsMode: {
58
61
  stateKey: 'shardsWorkload.filters.mode',
59
62
  },
@@ -1,3 +1,5 @@
1
1
  :root {
2
2
  --tenant-object-info-max-value-width: 300px;
3
+
4
+ --diagnostics-section-title-margin: 20px;
3
5
  }
@@ -57,6 +57,9 @@ export interface TTenant {
57
57
 
58
58
  MonitoringEndpoint?: string; // additional
59
59
  ControlPlane?: ControlPlane; // additional
60
+
61
+ CoresLimit?: string; // TODO: check correctness in backend protos when fully supported
62
+ StorageLimit?: string; // TODO: check correctness in backend protos when fully supported
60
63
  }
61
64
 
62
65
  interface THiveDomainStatsStateCount {
@@ -99,7 +99,7 @@ interface TVDiskSatisfactionRank {
99
99
  LevelRank?: TRank;
100
100
  }
101
101
 
102
- interface TVDiskID {
102
+ export interface TVDiskID {
103
103
  GroupID?: number;
104
104
  GroupGeneration?: number;
105
105
  Ring?: number;
@@ -1,4 +1,4 @@
1
- import {formatNumber} from '..';
1
+ import {formatNumber} from '../dataFormatters/dataFormatters';
2
2
  import {GIGABYTE, KILOBYTE, MEGABYTE, TERABYTE} from '../constants';
3
3
  import {isNumeric} from '../utils';
4
4