ydb-embedded-ui 4.18.0 → 4.19.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (35) hide show
  1. package/CHANGELOG.md +15 -0
  2. package/dist/components/FullNodeViewer/FullNodeViewer.scss +1 -1
  3. package/dist/components/FullNodeViewer/FullNodeViewer.tsx +13 -4
  4. package/dist/components/ProgressViewer/ProgressViewer.tsx +11 -5
  5. package/dist/containers/Nodes/getNodesColumns.tsx +1 -0
  6. package/dist/containers/Storage/PDiskPopup/PDiskPopup.tsx +1 -1
  7. package/dist/containers/Storage/StorageGroups/StorageGroups.tsx +3 -232
  8. package/dist/containers/Storage/StorageGroups/getStorageGroupsColumns.tsx +278 -0
  9. package/dist/containers/Tenant/Diagnostics/TenantOverview/MetricsCards/MetricsCards.tsx +2 -3
  10. package/dist/containers/Tenant/Diagnostics/TenantOverview/TenantOverview.tsx +22 -6
  11. package/dist/containers/Tenant/Diagnostics/TenantOverview/TenantStorage/TenantStorage.scss +41 -0
  12. package/dist/containers/Tenant/Diagnostics/TenantOverview/TenantStorage/TenantStorage.tsx +68 -0
  13. package/dist/containers/Tenant/Diagnostics/TenantOverview/TenantStorage/TopGroups.tsx +76 -0
  14. package/dist/containers/Tenant/Diagnostics/TenantOverview/TenantStorage/TopTables.tsx +105 -0
  15. package/dist/containers/Tenant/Diagnostics/TenantOverview/i18n/en.json +2 -0
  16. package/dist/containers/Tenant/Diagnostics/TenantOverview/i18n/ru.json +2 -0
  17. package/dist/containers/Versions/NodesTable/NodesTable.tsx +1 -0
  18. package/dist/store/reducers/index.ts +4 -0
  19. package/dist/store/reducers/storage/utils.ts +20 -11
  20. package/dist/store/reducers/tenantOverview/executeTopTables/executeTopTables.ts +93 -0
  21. package/dist/store/reducers/tenantOverview/executeTopTables/types.ts +14 -0
  22. package/dist/store/reducers/tenantOverview/topStorageGroups/topStorageGroups.ts +98 -0
  23. package/dist/store/reducers/tenantOverview/topStorageGroups/types.ts +29 -0
  24. package/dist/store/reducers/tenantOverview/topStorageGroups/utils.ts +20 -0
  25. package/dist/store/reducers/tenants/utils.ts +28 -18
  26. package/dist/styles/constants.scss +4 -0
  27. package/dist/types/additionalProps.ts +1 -1
  28. package/dist/types/api/storage.ts +1 -1
  29. package/dist/types/api/tenant.ts +21 -8
  30. package/dist/utils/bytesParsers/__test__/formatBytes.test.ts +10 -1
  31. package/dist/utils/bytesParsers/formatBytes.ts +3 -3
  32. package/dist/utils/constants.ts +8 -0
  33. package/dist/utils/dataFormatters/__test__/roundToSignificant.test.ts +22 -0
  34. package/dist/utils/dataFormatters/dataFormatters.ts +42 -20
  35. package/package.json +1 -1
@@ -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
+ };
@@ -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
  };
@@ -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
+ }
@@ -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 '../dataFormatters/dataFormatters';
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';
@@ -113,6 +115,12 @@ export const DEFAULT_TABLE_SETTINGS = {
113
115
  highlightRows: true,
114
116
  } as const;
115
117
 
118
+ export const TENANT_OVERVIEW_TABLES_SETTINGS = {
119
+ ...DEFAULT_TABLE_SETTINGS,
120
+ stickyHead: 'fixed',
121
+ dynamicRender: false,
122
+ } as const;
123
+
116
124
  export const QUERY_INITIAL_MODE_KEY = 'query_initial_mode';
117
125
  export const LAST_USED_QUERY_ACTION_KEY = 'last_used_query_action';
118
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
+ });
@@ -1,9 +1,14 @@
1
1
  import {dateTimeParse} from '@gravity-ui/date-utils';
2
2
 
3
3
  import type {TVDiskID, TVSlotId} from '../../types/api/vdisk';
4
- import {DAY_IN_SECONDS, GIGABYTE, TERABYTE} from '../constants';
4
+ import {DAY_IN_SECONDS, GIGABYTE} from '../constants';
5
5
  import {configuredNumeral} from '../numeral';
6
6
  import {isNumeric} from '../utils';
7
+ import {
8
+ type BytesSizes,
9
+ formatBytes as formatBytesCustom,
10
+ getSizeWithSignificantDigits,
11
+ } from '../bytesParsers/formatBytes';
7
12
 
8
13
  import i18n from './i18n';
9
14
 
@@ -55,17 +60,30 @@ export const formatMsToUptime = (ms?: number) => {
55
60
  return ms && formatUptime(ms / 1000);
56
61
  };
57
62
 
58
- export const formatStorageValues = (value?: number, total?: number) => {
59
- return [
60
- value ? String(Math.floor(value / TERABYTE)) : undefined,
61
- total ? `${Math.floor(total / TERABYTE)} TB` : undefined,
62
- ];
63
+ export const formatStorageValues = (value?: number, total?: number, size?: BytesSizes) => {
64
+ let calculatedSize = getSizeWithSignificantDigits(Number(value), 0);
65
+ let valueWithSizeLabel = true;
66
+ let valuePrecision = 0;
67
+
68
+ if (isNumeric(total)) {
69
+ calculatedSize = getSizeWithSignificantDigits(Number(total), 0);
70
+ valueWithSizeLabel = false;
71
+ valuePrecision = 1;
72
+ }
73
+
74
+ const formattedValue = formatBytesCustom({
75
+ value,
76
+ withSizeLabel: valueWithSizeLabel,
77
+ size: size || calculatedSize,
78
+ precision: valuePrecision,
79
+ });
80
+ const formattedTotal = formatBytesCustom({value: total, size: size || calculatedSize});
81
+
82
+ return [formattedValue, formattedTotal];
63
83
  };
64
- export const formatStorageValuesToGb = (value?: number, total?: number): (string | undefined)[] => {
65
- return [
66
- value ? String(Math.floor(value / 1000000000)) : undefined,
67
- total ? `${Math.floor(total / 1000000000)} GB` : undefined,
68
- ];
84
+
85
+ export const formatStorageValuesToGb = (value?: number, total?: number) => {
86
+ return formatStorageValues(value, total, 'gb');
69
87
  };
70
88
 
71
89
  export const formatNumber = (number?: unknown) => {
@@ -73,20 +91,24 @@ export const formatNumber = (number?: unknown) => {
73
91
  return '';
74
92
  }
75
93
 
76
- return configuredNumeral(number).format();
94
+ return configuredNumeral(number).format('0,0.[00000]');
77
95
  };
78
96
 
79
- const normalizeCPU = (value: number | string) => {
80
- const rawCores = Number(value) / 1000000;
81
- let cores = rawCores.toPrecision(3);
82
- if (rawCores >= 1000) {
83
- cores = rawCores.toFixed();
97
+ export const roundToPrecision = (value: number | string, precision = 0) => {
98
+ let [digits] = String(value).split('.');
99
+ if (Number(value) < 1) {
100
+ digits = '';
84
101
  }
85
- if (rawCores < 0.001) {
86
- cores = '0';
102
+ if (digits.length >= precision) {
103
+ return Math.round(Number(value));
87
104
  }
105
+ return Number(Number(value).toFixed(precision - digits.length));
106
+ };
107
+
108
+ const normalizeCPU = (value: number | string) => {
109
+ const rawCores = Number(value) / 1000000;
88
110
 
89
- return Number(cores);
111
+ return roundToPrecision(rawCores, 3);
90
112
  };
91
113
 
92
114
  export const formatCPU = (value?: number | string) => {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ydb-embedded-ui",
3
- "version": "4.18.0",
3
+ "version": "4.19.0",
4
4
  "files": [
5
5
  "dist"
6
6
  ],