ydb-embedded-ui 4.17.0 → 4.19.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (82) hide show
  1. package/CHANGELOG.md +30 -0
  2. package/dist/components/FullNodeViewer/FullNodeViewer.scss +1 -1
  3. package/dist/components/FullNodeViewer/FullNodeViewer.tsx +15 -6
  4. package/dist/components/InfoViewer/formatters/common.ts +1 -1
  5. package/dist/components/InfoViewer/formatters/pqGroup.ts +1 -1
  6. package/dist/components/InfoViewer/formatters/table.ts +6 -1
  7. package/dist/components/ProgressViewer/ProgressViewer.tsx +105 -0
  8. package/dist/components/TooltipsContent/TabletTooltipContent/TabletTooltipContent.tsx +1 -1
  9. package/dist/containers/AsideNavigation/AsideNavigation.tsx +3 -1
  10. package/dist/containers/Authentication/Authentication.tsx +14 -4
  11. package/dist/containers/Cluster/ClusterInfo/ClusterInfo.tsx +2 -2
  12. package/dist/containers/Heatmap/Heatmap.tsx +1 -1
  13. package/dist/containers/Heatmap/Histogram/Histogram.js +1 -1
  14. package/dist/containers/Node/NodeStructure/Pdisk.tsx +2 -2
  15. package/dist/containers/Node/NodeStructure/Vdisk.tsx +5 -2
  16. package/dist/containers/Nodes/getNodesColumns.tsx +3 -2
  17. package/dist/containers/Storage/PDisk/PDisk.tsx +1 -1
  18. package/dist/containers/Storage/PDiskPopup/PDiskPopup.tsx +2 -2
  19. package/dist/containers/Storage/StorageGroups/StorageGroups.tsx +3 -232
  20. package/dist/containers/Storage/StorageGroups/getStorageGroupsColumns.tsx +278 -0
  21. package/dist/containers/Storage/VDisk/VDisk.tsx +1 -1
  22. package/dist/containers/Storage/VDiskPopup/VDiskPopup.tsx +1 -1
  23. package/dist/containers/Tablet/TabletInfo/TabletInfo.tsx +1 -1
  24. package/dist/containers/Tablet/TabletTable/TabletTable.tsx +1 -1
  25. package/dist/containers/Tenant/Diagnostics/Consumers/TopicStats/ConsumersTopicStats.tsx +1 -1
  26. package/dist/containers/Tenant/Diagnostics/Consumers/columns/columns.tsx +1 -1
  27. package/dist/containers/Tenant/Diagnostics/DetailedOverview/DetailedOverview.tsx +3 -3
  28. package/dist/containers/Tenant/Diagnostics/Overview/TableInfo/prepareTableInfo.ts +1 -1
  29. package/dist/containers/Tenant/Diagnostics/Overview/TopicStats/TopicStats.tsx +1 -1
  30. package/dist/containers/Tenant/Diagnostics/Partitions/columns/columns.tsx +1 -1
  31. package/dist/containers/Tenant/Diagnostics/TenantOverview/MetricsCards/MetricsCards.tsx +2 -3
  32. package/dist/containers/Tenant/Diagnostics/TenantOverview/OldTenantOverview.tsx +1 -1
  33. package/dist/containers/Tenant/Diagnostics/TenantOverview/TenantOverview.tsx +22 -6
  34. package/dist/containers/Tenant/Diagnostics/TenantOverview/TenantStorage/TenantStorage.scss +41 -0
  35. package/dist/containers/Tenant/Diagnostics/TenantOverview/TenantStorage/TenantStorage.tsx +68 -0
  36. package/dist/containers/Tenant/Diagnostics/TenantOverview/TenantStorage/TopGroups.tsx +76 -0
  37. package/dist/containers/Tenant/Diagnostics/TenantOverview/TenantStorage/TopTables.tsx +105 -0
  38. package/dist/containers/Tenant/Diagnostics/TenantOverview/i18n/en.json +2 -0
  39. package/dist/containers/Tenant/Diagnostics/TenantOverview/i18n/ru.json +2 -0
  40. package/dist/containers/Tenant/Diagnostics/TopQueries/TopQueries.tsx +1 -1
  41. package/dist/containers/Tenant/Diagnostics/TopShards/TopShards.tsx +1 -1
  42. package/dist/containers/Tenant/ObjectSummary/ObjectSummary.tsx +1 -1
  43. package/dist/containers/Tenants/Tenants.tsx +5 -1
  44. package/dist/containers/UserSettings/i18n/en.json +1 -1
  45. package/dist/containers/UserSettings/i18n/ru.json +1 -1
  46. package/dist/containers/UserSettings/settings.ts +2 -2
  47. package/dist/containers/Versions/NodesTable/NodesTable.tsx +3 -2
  48. package/dist/services/api.ts +1 -1
  49. package/dist/store/reducers/clusterNodes/clusterNodes.tsx +1 -1
  50. package/dist/store/reducers/index.ts +4 -0
  51. package/dist/store/reducers/node/selectors.ts +1 -1
  52. package/dist/store/reducers/nodes/selectors.ts +1 -1
  53. package/dist/store/reducers/nodes/types.ts +1 -1
  54. package/dist/store/reducers/nodes/utils.ts +1 -1
  55. package/dist/store/reducers/settings/settings.ts +4 -4
  56. package/dist/store/reducers/storage/types.ts +1 -1
  57. package/dist/store/reducers/storage/utils.ts +21 -12
  58. package/dist/store/reducers/tenantOverview/executeTopTables/executeTopTables.ts +93 -0
  59. package/dist/store/reducers/tenantOverview/executeTopTables/types.ts +14 -0
  60. package/dist/store/reducers/tenantOverview/topStorageGroups/topStorageGroups.ts +98 -0
  61. package/dist/store/reducers/tenantOverview/topStorageGroups/types.ts +29 -0
  62. package/dist/store/reducers/tenantOverview/topStorageGroups/utils.ts +20 -0
  63. package/dist/store/reducers/tenants/utils.ts +30 -20
  64. package/dist/styles/constants.scss +4 -0
  65. package/dist/types/additionalProps.ts +1 -1
  66. package/dist/types/api/storage.ts +1 -1
  67. package/dist/types/api/tenant.ts +21 -8
  68. package/dist/types/api/vdisk.ts +1 -1
  69. package/dist/utils/bytesParsers/__test__/formatBytes.test.ts +10 -1
  70. package/dist/utils/bytesParsers/formatBytes.ts +3 -3
  71. package/dist/utils/constants.ts +10 -1
  72. package/dist/utils/dataFormatters/__test__/roundToSignificant.test.ts +22 -0
  73. package/dist/utils/dataFormatters/dataFormatters.ts +150 -0
  74. package/dist/utils/dataFormatters/i18n/en.json +3 -0
  75. package/dist/utils/dataFormatters/i18n/ru.json +3 -0
  76. package/dist/utils/index.js +0 -102
  77. package/package.json +1 -1
  78. package/dist/components/ProgressViewer/ProgressViewer.js +0 -92
  79. package/dist/utils/formatCPU/formatCPU.ts +0 -20
  80. package/dist/utils/formatCPU/i18n/en.json +0 -3
  81. package/dist/utils/formatCPU/i18n/ru.json +0 -3
  82. /package/dist/utils/{formatCPU → dataFormatters}/i18n/index.ts +0 -0
@@ -3,13 +3,14 @@ import type {
3
3
  TStorageGroupInfo,
4
4
  TStorageGroupInfoV2,
5
5
  TStorageInfo,
6
+ TStoragePoolInfo,
6
7
  } from '../../../types/api/storage';
7
8
  import {EVDiskState, type TVDiskStateInfo} from '../../../types/api/vdisk';
8
9
  import {TPDiskState} from '../../../types/api/pdisk';
9
10
  import {EFlag} from '../../../types/api/enums';
10
11
  import {getPDiskType} from '../../../utils/pdisk';
11
12
  import {getUsage} from '../../../utils/storage';
12
- import {calcUptime} from '../../../utils';
13
+ import {calcUptime} from '../../../utils/dataFormatters/dataFormatters';
13
14
 
14
15
  import type {PreparedStorageGroup, PreparedStorageNode, PreparedStorageResponse} from './types';
15
16
 
@@ -146,6 +147,24 @@ const prepareStorageGroupDataV2 = (group: TStorageGroupInfoV2): PreparedStorageG
146
147
  };
147
148
  };
148
149
 
150
+ export const prepareStorageGroups = (
151
+ StorageGroups?: TStorageGroupInfoV2[],
152
+ StoragePools?: TStoragePoolInfo[],
153
+ ) => {
154
+ let preparedGroups: PreparedStorageGroup[] = [];
155
+ if (StorageGroups) {
156
+ preparedGroups = StorageGroups.map(prepareStorageGroupDataV2);
157
+ } else {
158
+ StoragePools?.forEach((pool) => {
159
+ pool.Groups?.forEach((group) => {
160
+ preparedGroups.push(prepareStorageGroupData(group, pool.Name));
161
+ });
162
+ });
163
+ }
164
+
165
+ return preparedGroups;
166
+ };
167
+
149
168
  // ==== Prepare nodes ====
150
169
 
151
170
  const prepareStorageNodeData = (node: TNodeInfo): PreparedStorageNode => {
@@ -187,17 +206,7 @@ export const prepareStorageNodesResponse = (data: TNodesInfo): PreparedStorageRe
187
206
  export const prepareStorageGroupsResponse = (data: TStorageInfo): PreparedStorageResponse => {
188
207
  const {StoragePools, StorageGroups, TotalGroups, FoundGroups} = data;
189
208
 
190
- let preparedGroups: PreparedStorageGroup[] = [];
191
-
192
- if (StorageGroups) {
193
- preparedGroups = StorageGroups.map(prepareStorageGroupDataV2);
194
- } else {
195
- StoragePools?.forEach((pool) => {
196
- pool.Groups?.forEach((group) => {
197
- preparedGroups.push(prepareStorageGroupData(group, pool.Name));
198
- });
199
- });
200
- }
209
+ const preparedGroups = prepareStorageGroups(StorageGroups, StoragePools);
201
210
 
202
211
  return {
203
212
  groups: preparedGroups,
@@ -0,0 +1,93 @@
1
+ import type {Reducer} from 'redux';
2
+
3
+ import {TENANT_OVERVIEW_TABLES_LIMIT} from '../../../../utils/constants';
4
+ import {parseQueryAPIExecuteResponse} from '../../../../utils/query';
5
+ import {createApiRequest, createRequestActionTypes} from '../../../utils';
6
+ import type {TopTablesAction, TopTablesState} from './types';
7
+
8
+ export const FETCH_TOP_TABLES = createRequestActionTypes('top-tables', 'FETCH_TOP_TABLES');
9
+ const SET_DATA_WAS_NOT_LOADED = 'top-tables/SET_DATA_WAS_NOT_LOADED';
10
+
11
+ const initialState = {
12
+ loading: false,
13
+ wasLoaded: false,
14
+ };
15
+
16
+ const getQueryText = (path: string) => {
17
+ return `
18
+ SELECT
19
+ Path, SUM(DataSize) as Size
20
+ FROM \`${path}/.sys/partition_stats\`
21
+ GROUP BY Path
22
+ ORDER BY Size DESC
23
+ LIMIT ${TENANT_OVERVIEW_TABLES_LIMIT}
24
+ `;
25
+ };
26
+
27
+ const executeTopTables: Reducer<TopTablesState, TopTablesAction> = (
28
+ state = initialState,
29
+ action,
30
+ ) => {
31
+ switch (action.type) {
32
+ case FETCH_TOP_TABLES.REQUEST: {
33
+ return {
34
+ ...state,
35
+ loading: true,
36
+ error: undefined,
37
+ };
38
+ }
39
+ case FETCH_TOP_TABLES.SUCCESS: {
40
+ return {
41
+ ...state,
42
+ data: action.data,
43
+ loading: false,
44
+ error: undefined,
45
+ wasLoaded: true,
46
+ };
47
+ }
48
+ // 401 Unauthorized error is handled by GenericAPI
49
+ case FETCH_TOP_TABLES.FAILURE: {
50
+ if (action.error?.isCancelled) {
51
+ return state;
52
+ }
53
+ return {
54
+ ...state,
55
+ error: action.error || 'Unauthorized',
56
+ loading: false,
57
+ };
58
+ }
59
+ case SET_DATA_WAS_NOT_LOADED:
60
+ return {
61
+ ...state,
62
+ wasLoaded: false,
63
+ };
64
+ default:
65
+ return state;
66
+ }
67
+ };
68
+
69
+ export const fetchTopTables = (database: string) => {
70
+ return createApiRequest({
71
+ request: window.api.sendQuery(
72
+ {
73
+ schema: 'modern',
74
+ query: getQueryText(database),
75
+ database,
76
+ action: 'execute-scan',
77
+ },
78
+ {
79
+ concurrentId: 'executeTopTables',
80
+ },
81
+ ),
82
+ actions: FETCH_TOP_TABLES,
83
+ dataHandler: parseQueryAPIExecuteResponse,
84
+ });
85
+ };
86
+
87
+ export function setDataWasNotLoaded() {
88
+ return {
89
+ type: SET_DATA_WAS_NOT_LOADED,
90
+ } as const;
91
+ }
92
+
93
+ export default executeTopTables;
@@ -0,0 +1,14 @@
1
+ import type {IQueryResult, QueryErrorResponse} from '../../../../types/store/query';
2
+ import type {ApiRequestAction} from '../../../utils';
3
+ import {FETCH_TOP_TABLES, setDataWasNotLoaded} from './executeTopTables';
4
+
5
+ export interface TopTablesState {
6
+ loading: boolean;
7
+ wasLoaded: boolean;
8
+ data?: IQueryResult;
9
+ error?: QueryErrorResponse;
10
+ }
11
+
12
+ export type TopTablesAction =
13
+ | ApiRequestAction<typeof FETCH_TOP_TABLES, IQueryResult, QueryErrorResponse>
14
+ | ReturnType<typeof setDataWasNotLoaded>;
@@ -0,0 +1,98 @@
1
+ import type {Reducer} from 'redux';
2
+
3
+ import {TENANT_OVERVIEW_TABLES_LIMIT} from '../../../../utils/constants';
4
+ import {EVersion} from '../../../../types/api/storage';
5
+ import {createApiRequest, createRequestActionTypes} from '../../../utils';
6
+ import type {StorageApiRequestParams} from '../../storage/types';
7
+ import type {
8
+ TopStorageGroupsAction,
9
+ TopStorageGroupsState,
10
+ TopStorageGroupsStateSlice,
11
+ } from './types';
12
+ import {prepareTopStorageGroupsResponse} from './utils';
13
+
14
+ export const FETCH_TOP_STORAGE_GROUPS = createRequestActionTypes(
15
+ 'topStorageGroups',
16
+ 'FETCH_TOP_STORAGE_GROUPS',
17
+ );
18
+
19
+ const SET_DATA_WAS_NOT_LOADED = 'topStorageGroups/SET_DATA_WAS_NOT_LOADED';
20
+
21
+ const initialState = {
22
+ loading: true,
23
+ wasLoaded: false,
24
+ };
25
+
26
+ const topStorageGroups: Reducer<TopStorageGroupsState, TopStorageGroupsAction> = (
27
+ state = initialState,
28
+ action,
29
+ ) => {
30
+ switch (action.type) {
31
+ case FETCH_TOP_STORAGE_GROUPS.REQUEST: {
32
+ return {
33
+ ...state,
34
+ loading: true,
35
+ };
36
+ }
37
+ case FETCH_TOP_STORAGE_GROUPS.SUCCESS: {
38
+ return {
39
+ ...state,
40
+ data: action.data.groups,
41
+ loading: false,
42
+ wasLoaded: true,
43
+ error: undefined,
44
+ };
45
+ }
46
+ case FETCH_TOP_STORAGE_GROUPS.FAILURE: {
47
+ if (action.error?.isCancelled) {
48
+ return state;
49
+ }
50
+
51
+ return {
52
+ ...state,
53
+ error: action.error,
54
+ loading: false,
55
+ wasLoaded: true,
56
+ };
57
+ }
58
+ case SET_DATA_WAS_NOT_LOADED: {
59
+ return {
60
+ ...state,
61
+ wasLoaded: false,
62
+ };
63
+ }
64
+ default:
65
+ return state;
66
+ }
67
+ };
68
+
69
+ export const getTopStorageGroups = ({
70
+ tenant,
71
+ visibleEntities = 'all',
72
+ nodeId,
73
+ sortOrder = -1,
74
+ sortValue = 'Usage',
75
+ limit = TENANT_OVERVIEW_TABLES_LIMIT,
76
+ version = EVersion.v2,
77
+ ...params
78
+ }: StorageApiRequestParams) => {
79
+ return createApiRequest({
80
+ request: window.api.getStorageInfo(
81
+ {tenant, visibleEntities, nodeId, version, sortOrder, sortValue, limit, ...params},
82
+ {concurrentId: 'getTopStorageGroups'},
83
+ ),
84
+ actions: FETCH_TOP_STORAGE_GROUPS,
85
+ dataHandler: prepareTopStorageGroupsResponse,
86
+ });
87
+ };
88
+
89
+ export const selectTopStorageGroups = (state: TopStorageGroupsStateSlice) =>
90
+ state.topStorageGroups.data;
91
+
92
+ export const setDataWasNotLoaded = () => {
93
+ return {
94
+ type: SET_DATA_WAS_NOT_LOADED,
95
+ } as const;
96
+ };
97
+
98
+ export default topStorageGroups;
@@ -0,0 +1,29 @@
1
+ import type {IResponseError} from '../../../../types/api/error';
2
+ import type {ApiRequestAction} from '../../../utils';
3
+ import type {PreparedStorageGroup} from '../../storage/types';
4
+ import {FETCH_TOP_STORAGE_GROUPS, setDataWasNotLoaded} from './topStorageGroups';
5
+
6
+ export interface TopStorageGroupsState {
7
+ loading: boolean;
8
+ wasLoaded: boolean;
9
+ data?: PreparedStorageGroup[];
10
+ error?: IResponseError;
11
+ }
12
+
13
+ export interface PreparedTopStorageGroupsResponse {
14
+ groups?: PreparedStorageGroup[];
15
+ }
16
+
17
+ type GetTopStorageGroupApiRequestAction = ApiRequestAction<
18
+ typeof FETCH_TOP_STORAGE_GROUPS,
19
+ PreparedTopStorageGroupsResponse,
20
+ IResponseError
21
+ >;
22
+
23
+ export type TopStorageGroupsAction =
24
+ | GetTopStorageGroupApiRequestAction
25
+ | ReturnType<typeof setDataWasNotLoaded>;
26
+
27
+ export interface TopStorageGroupsStateSlice {
28
+ topStorageGroups: TopStorageGroupsState;
29
+ }
@@ -0,0 +1,20 @@
1
+ import {TENANT_OVERVIEW_TABLES_LIMIT} from '../../../../utils/constants';
2
+ import type {TStorageInfo} from '../../../../types/api/storage';
3
+ import {prepareStorageGroups} from '../../storage/utils';
4
+ import type {PreparedTopStorageGroupsResponse} from './types';
5
+
6
+ export const prepareTopStorageGroupsResponse = (
7
+ data: TStorageInfo,
8
+ ): PreparedTopStorageGroupsResponse => {
9
+ const {StoragePools, StorageGroups} = data;
10
+
11
+ const preparedGroups = prepareStorageGroups(StorageGroups, StoragePools);
12
+
13
+ if (StoragePools) {
14
+ preparedGroups.sort((a, b) => b.Usage - a.Usage);
15
+ }
16
+
17
+ return {
18
+ groups: preparedGroups.slice(0, TENANT_OVERVIEW_TABLES_LIMIT),
19
+ };
20
+ };
@@ -1,6 +1,6 @@
1
1
  import type {TTenant} from '../../../types/api/tenant';
2
2
  import {formatBytes} from '../../../utils/bytesParsers';
3
- import {formatCPU} from '../../../utils/formatCPU/formatCPU';
3
+ import {formatCPUWithLabel} from '../../../utils/dataFormatters/dataFormatters';
4
4
  import {isNumeric} from '../../../utils/utils';
5
5
  import {METRIC_STATUS} from './contants';
6
6
 
@@ -26,34 +26,44 @@ export const calculateTenantMetrics = (tenant?: TTenant) => {
26
26
  CoresUsed,
27
27
  MemoryUsed,
28
28
  StorageAllocatedSize,
29
- CoresLimit,
30
29
  MemoryLimit,
31
- StorageLimit,
30
+ StorageAllocatedLimit,
31
+ PoolStats,
32
32
  Metrics = {},
33
+ DatabaseQuotas = {},
33
34
  } = tenant || {};
34
35
 
35
- const cpuFromCores = isNumeric(CoresUsed) ? Number(CoresUsed) * 1_000_000 : undefined;
36
- const cpuFromMetrics = isNumeric(Metrics.CPU) ? Number(Metrics.CPU) : undefined;
36
+ const systemPoolUsage = PoolStats?.find(({Name}) => Name === 'System')?.Usage;
37
+ const userPoolUsage = PoolStats?.find(({Name}) => Name === 'User')?.Usage;
37
38
 
38
- const cpu = cpuFromCores ?? cpuFromMetrics ?? undefined;
39
+ const cpu = isNumeric(CoresUsed) ? Number(CoresUsed) * 1_000_000 : undefined;
40
+ const memory = isNumeric(MemoryUsed) ? Number(MemoryUsed) : undefined;
41
+ const blobStorage = isNumeric(StorageAllocatedSize) ? Number(StorageAllocatedSize) : undefined;
42
+ const tableStorage = isNumeric(Metrics.Storage) ? Number(Metrics.Storage) : undefined;
39
43
 
40
- const rawMemory = MemoryUsed ?? Metrics.Memory;
41
-
42
- const memory = isNumeric(rawMemory) ? Number(rawMemory) : undefined;
43
-
44
- // Blob storage - actual database size
45
- const storage = isNumeric(StorageAllocatedSize) ? Number(StorageAllocatedSize) : undefined;
46
- const cpuLimit = isNumeric(CoresLimit) ? Number(CoresLimit) : undefined;
44
+ // We use system pool usage and user pool usage to calculate cpu usage because
45
+ // only these pools directly indicate resources available to perform user queries
46
+ const cpuUsage =
47
+ isNumeric(systemPoolUsage) || isNumeric(userPoolUsage)
48
+ ? Math.max(Number(systemPoolUsage), Number(userPoolUsage)) * 100
49
+ : undefined;
47
50
  const memoryLimit = isNumeric(MemoryLimit) ? Number(MemoryLimit) : undefined;
48
- const storageLimit = isNumeric(StorageLimit) ? Number(StorageLimit) : undefined;
51
+ const blobStorageLimit = isNumeric(StorageAllocatedLimit)
52
+ ? Number(StorageAllocatedLimit)
53
+ : undefined;
54
+ const tableStorageLimit = isNumeric(DatabaseQuotas.data_size_hard_quota)
55
+ ? Number(DatabaseQuotas.data_size_hard_quota)
56
+ : undefined;
49
57
 
50
58
  return {
51
59
  cpu,
52
60
  memory,
53
- storage,
54
- cpuLimit,
61
+ blobStorage,
62
+ tableStorage,
63
+ cpuUsage,
55
64
  memoryLimit,
56
- storageLimit,
65
+ blobStorageLimit,
66
+ tableStorageLimit,
57
67
  };
58
68
  };
59
69
 
@@ -71,7 +81,7 @@ export const prepareTenants = (tenants: TTenant[], useNodeAsBackend: boolean) =>
71
81
  const backend = useNodeAsBackend ? getTenantBackend(tenant) : undefined;
72
82
  const sharedTenantName = tenants.find((item) => item.Id === tenant.ResourceId)?.Name;
73
83
  const controlPlaneName = getControlPlaneValue(tenant);
74
- const {cpu, memory, storage} = calculateTenantMetrics(tenant);
84
+ const {cpu, memory, blobStorage} = calculateTenantMetrics(tenant);
75
85
  const {nodesCount, groupsCount} = calculateTenantEntities(tenant);
76
86
 
77
87
  return {
@@ -82,7 +92,7 @@ export const prepareTenants = (tenants: TTenant[], useNodeAsBackend: boolean) =>
82
92
  controlPlaneName,
83
93
  cpu,
84
94
  memory,
85
- storage,
95
+ storage: blobStorage,
86
96
  nodesCount,
87
97
  groupsCount,
88
98
  };
@@ -158,7 +168,7 @@ export const formatTenantMetrics = ({
158
168
  storage?: number;
159
169
  memory?: number;
160
170
  }) => ({
161
- cpu: formatCPU(cpu),
171
+ cpu: formatCPUWithLabel(cpu),
162
172
  storage: formatBytes({value: storage, significantDigits: 2}) || undefined,
163
173
  memory: formatBytes({value: memory, significantDigits: 2}) || undefined,
164
174
  });
@@ -2,4 +2,8 @@
2
2
  --tenant-object-info-max-value-width: 300px;
3
3
 
4
4
  --diagnostics-section-title-margin: 20px;
5
+
6
+ --diagnostics-section-margin: 16px;
7
+
8
+ --diagnostics-section-table-width: 872px;
5
9
  }
@@ -24,7 +24,7 @@ export interface AdditionalTenantsProps {
24
24
  getMonitoringLink?: (name?: string, type?: ETenantType) => ReactNode;
25
25
  }
26
26
 
27
- export type NodeAddress = Pick<TSystemStateInfo, 'Host' | 'Endpoints'>;
27
+ export type NodeAddress = Pick<TSystemStateInfo, 'Host' | 'Endpoints' | 'NodeId'>;
28
28
 
29
29
  export interface AdditionalNodesProps extends Record<string, unknown> {
30
30
  getNodeRef?: (node?: NodeAddress) => string | null;
@@ -16,7 +16,7 @@ export interface TStorageInfo {
16
16
  FoundGroups?: string;
17
17
  }
18
18
 
19
- interface TStoragePoolInfo {
19
+ export interface TStoragePoolInfo {
20
20
  Overall?: EFlag;
21
21
  Name?: string;
22
22
  Kind?: string;
@@ -42,24 +42,24 @@ export interface TTenant {
42
42
  ResourceId?: string;
43
43
  Tablets?: TTabletStateInfo[];
44
44
  /** uint64 */
45
- StorageAllocatedSize?: string;
45
+ StorageAllocatedSize?: string; // Actual database size
46
46
  /** uint64 */
47
47
  StorageMinAvailableSize?: string;
48
48
  Nodes?: TSystemStateInfo[];
49
49
  /** uint64 */
50
- MemoryUsed?: string;
50
+ MemoryUsed?: string; // Actual memory consumption
51
51
  /** uint64 */
52
52
  MemoryLimit?: string;
53
53
  /** double */
54
- CoresUsed?: number;
54
+ CoresUsed?: number; // Actual cpu consumption
55
55
  /** uint64 */
56
56
  StorageGroups?: string;
57
57
 
58
58
  MonitoringEndpoint?: string; // additional
59
59
  ControlPlane?: ControlPlane; // additional
60
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
61
+ StorageAllocatedLimit?: string;
62
+ DatabaseQuotas?: DatabaseQuotas;
63
63
  }
64
64
 
65
65
  interface THiveDomainStatsStateCount {
@@ -69,15 +69,15 @@ interface THiveDomainStatsStateCount {
69
69
 
70
70
  export interface TMetrics {
71
71
  /** uint64 */
72
- CPU?: string;
72
+ CPU?: string; // Logical cpu consumption
73
73
  /** uint64 */
74
- Memory?: string;
74
+ Memory?: string; // Logical memory consumption
75
75
  /** uint64 */
76
76
  Network?: string;
77
77
  /** uint64 */
78
78
  Counter?: string;
79
79
  /** uint64 */
80
- Storage?: string;
80
+ Storage?: string; // Logical database size
81
81
  /** uint64 */
82
82
  ReadThroughput?: string;
83
83
  /** uint64 */
@@ -149,3 +149,16 @@ export enum ETabletVolatileState {
149
149
  'TABLET_VOLATILE_STATE_STARTING' = 'TABLET_VOLATILE_STATE_STARTING',
150
150
  'TABLET_VOLATILE_STATE_RUNNING' = 'TABLET_VOLATILE_STATE_RUNNING',
151
151
  }
152
+
153
+ interface DatabaseQuotas {
154
+ /** uint64 */
155
+ data_size_hard_quota?: string;
156
+ /** uint64 */
157
+ data_size_soft_quota?: string;
158
+ /** uint64 */
159
+ data_stream_shards_quota?: string;
160
+ /** uint64 */
161
+ data_stream_reserved_storage_quota?: string;
162
+ /** uint32 */
163
+ ttl_min_run_internal_seconds?: string;
164
+ }
@@ -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;
@@ -28,11 +28,20 @@ describe('formatBytes', () => {
28
28
  expect(formatBytes({value: 99_000_000_000_000, significantDigits: 2})).toEqual('99,000 GB');
29
29
  expect(formatBytes({value: 100_000_000_000_000, significantDigits: 2})).toEqual('100 TB');
30
30
  });
31
- it('shoudl return empty string on invalid data', () => {
31
+ it('should return empty string on invalid data', () => {
32
32
  expect(formatBytes({value: undefined})).toEqual('');
33
33
  expect(formatBytes({value: null})).toEqual('');
34
34
  expect(formatBytes({value: ''})).toEqual('');
35
35
  expect(formatBytes({value: 'false'})).toEqual('');
36
36
  expect(formatBytes({value: '123qwe'})).toEqual('');
37
37
  });
38
+ it('should work with precision', () => {
39
+ expect(formatBytes({value: 123.123, precision: 2})).toBe('123 B');
40
+ expect(formatBytes({value: 12.123, precision: 2})).toBe('12 B');
41
+ expect(formatBytes({value: 1.123, precision: 2})).toBe('1.1 B');
42
+ expect(formatBytes({value: 0.123, precision: 2})).toBe('0.12 B');
43
+ expect(formatBytes({value: 0.012, precision: 2})).toBe('0.01 B');
44
+ expect(formatBytes({value: 0.001, precision: 2})).toBe('0 B');
45
+ expect(formatBytes({value: 0, precision: 2})).toBe('0 B');
46
+ });
38
47
  });
@@ -1,4 +1,4 @@
1
- import {formatNumber} from '..';
1
+ import {formatNumber, roundToPrecision} from '../dataFormatters/dataFormatters';
2
2
  import {GIGABYTE, KILOBYTE, MEGABYTE, TERABYTE} from '../constants';
3
3
  import {isNumeric} from '../utils';
4
4
 
@@ -46,7 +46,7 @@ export type BytesSizes = keyof typeof sizes;
46
46
  *
47
47
  * significantDigits = 3 - 900 000 mb and 1000 gb
48
48
  */
49
- const getSizeWithSignificantDigits = (value: number, significantDigits: number) => {
49
+ export const getSizeWithSignificantDigits = (value: number, significantDigits: number) => {
50
50
  const multiplier = 10 ** significantDigits;
51
51
 
52
52
  const tbLevel = sizes.tb.value * multiplier;
@@ -79,7 +79,7 @@ interface FormatToSizeArgs {
79
79
  }
80
80
 
81
81
  const formatToSize = ({value, size = 'mb', precision = 0}: FormatToSizeArgs) => {
82
- const result = (Number(value) / sizes[size].value).toFixed(precision);
82
+ const result = roundToPrecision(Number(value) / sizes[size].value, precision);
83
83
 
84
84
  return formatNumber(result);
85
85
  };
@@ -77,6 +77,8 @@ export const COLORS_PRIORITY = {
77
77
  grey: 1,
78
78
  };
79
79
 
80
+ export const TENANT_OVERVIEW_TABLES_LIMIT = 5;
81
+
80
82
  // ==== Titles ====
81
83
  export const DEVELOPER_UI_TITLE = 'Developer UI';
82
84
  export const CLUSTER_DEFAULT_TITLE = 'Cluster';
@@ -88,7 +90,8 @@ export const LANGUAGE_KEY = 'language';
88
90
  export const INVERTED_DISKS_KEY = 'invertedDisks';
89
91
  export const USE_NODES_ENDPOINT_IN_DIAGNOSTICS_KEY = 'useNodesEndpointInDiagnostics';
90
92
  export const ENABLE_ADDITIONAL_QUERY_MODES = 'enableAdditionalQueryModes';
91
- export const ENABLE_NEW_TENANT_DIAGNOSTICS_DESIGN = 'enableNewTenantDiagnosticsDesign';
93
+ // Remain key name "enableNewTenantDiagnosticsDesign" for backward compatibility
94
+ export const DISPLAY_METRICS_CARDS_FOR_TENANT_DIAGNOSTICS = 'enableNewTenantDiagnosticsDesign';
92
95
  export const SAVED_QUERIES_KEY = 'saved_queries';
93
96
  export const ASIDE_HEADER_COMPACT_KEY = 'asideHeaderCompact';
94
97
  export const QUERIES_HISTORY_KEY = 'queries_history';
@@ -112,6 +115,12 @@ export const DEFAULT_TABLE_SETTINGS = {
112
115
  highlightRows: true,
113
116
  } as const;
114
117
 
118
+ export const TENANT_OVERVIEW_TABLES_SETTINGS = {
119
+ ...DEFAULT_TABLE_SETTINGS,
120
+ stickyHead: 'fixed',
121
+ dynamicRender: false,
122
+ } as const;
123
+
115
124
  export const QUERY_INITIAL_MODE_KEY = 'query_initial_mode';
116
125
  export const LAST_USED_QUERY_ACTION_KEY = 'last_used_query_action';
117
126
 
@@ -0,0 +1,22 @@
1
+ import {roundToPrecision} from '../dataFormatters';
2
+
3
+ describe('roundToSignificant', () => {
4
+ it('should work with only value', () => {
5
+ expect(roundToPrecision(123)).toBe(123);
6
+ expect(roundToPrecision(123.123)).toBe(123);
7
+ expect(roundToPrecision(12.123)).toBe(12);
8
+ expect(roundToPrecision(1.123)).toBe(1);
9
+ expect(roundToPrecision(0.123)).toBe(0);
10
+ expect(roundToPrecision(0)).toBe(0);
11
+ });
12
+ it('should work with precision', () => {
13
+ expect(roundToPrecision(123, 2)).toBe(123);
14
+ expect(roundToPrecision(123.123, 2)).toBe(123);
15
+ expect(roundToPrecision(12.123, 2)).toBe(12);
16
+ expect(roundToPrecision(1.123, 2)).toBe(1.1);
17
+ expect(roundToPrecision(0.123, 2)).toBe(0.12);
18
+ expect(roundToPrecision(0.012, 2)).toBe(0.01);
19
+ expect(roundToPrecision(0.001, 2)).toBe(0);
20
+ expect(roundToPrecision(0, 2)).toBe(0);
21
+ });
22
+ });