ydb-embedded-ui 4.18.0 → 4.19.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +15 -0
- package/dist/components/FullNodeViewer/FullNodeViewer.scss +1 -1
- package/dist/components/FullNodeViewer/FullNodeViewer.tsx +13 -4
- package/dist/components/ProgressViewer/ProgressViewer.tsx +11 -5
- package/dist/containers/Nodes/getNodesColumns.tsx +1 -0
- package/dist/containers/Storage/PDiskPopup/PDiskPopup.tsx +1 -1
- package/dist/containers/Storage/StorageGroups/StorageGroups.tsx +3 -232
- package/dist/containers/Storage/StorageGroups/getStorageGroupsColumns.tsx +278 -0
- package/dist/containers/Tenant/Diagnostics/TenantOverview/MetricsCards/MetricsCards.tsx +2 -3
- package/dist/containers/Tenant/Diagnostics/TenantOverview/TenantOverview.tsx +22 -6
- package/dist/containers/Tenant/Diagnostics/TenantOverview/TenantStorage/TenantStorage.scss +41 -0
- package/dist/containers/Tenant/Diagnostics/TenantOverview/TenantStorage/TenantStorage.tsx +68 -0
- package/dist/containers/Tenant/Diagnostics/TenantOverview/TenantStorage/TopGroups.tsx +76 -0
- package/dist/containers/Tenant/Diagnostics/TenantOverview/TenantStorage/TopTables.tsx +105 -0
- package/dist/containers/Tenant/Diagnostics/TenantOverview/i18n/en.json +2 -0
- package/dist/containers/Tenant/Diagnostics/TenantOverview/i18n/ru.json +2 -0
- package/dist/containers/Versions/NodesTable/NodesTable.tsx +1 -0
- package/dist/store/reducers/index.ts +4 -0
- package/dist/store/reducers/storage/utils.ts +20 -11
- package/dist/store/reducers/tenantOverview/executeTopTables/executeTopTables.ts +93 -0
- package/dist/store/reducers/tenantOverview/executeTopTables/types.ts +14 -0
- package/dist/store/reducers/tenantOverview/topStorageGroups/topStorageGroups.ts +98 -0
- package/dist/store/reducers/tenantOverview/topStorageGroups/types.ts +29 -0
- package/dist/store/reducers/tenantOverview/topStorageGroups/utils.ts +20 -0
- package/dist/store/reducers/tenants/utils.ts +28 -18
- package/dist/styles/constants.scss +4 -0
- package/dist/types/additionalProps.ts +1 -1
- package/dist/types/api/storage.ts +1 -1
- package/dist/types/api/tenant.ts +21 -8
- package/dist/utils/bytesParsers/__test__/formatBytes.test.ts +10 -1
- package/dist/utils/bytesParsers/formatBytes.ts +3 -3
- package/dist/utils/constants.ts +8 -0
- package/dist/utils/dataFormatters/__test__/roundToSignificant.test.ts +22 -0
- package/dist/utils/dataFormatters/dataFormatters.ts +42 -20
- 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
|
-
|
30
|
+
StorageAllocatedLimit,
|
31
|
+
PoolStats,
|
32
32
|
Metrics = {},
|
33
|
+
DatabaseQuotas = {},
|
33
34
|
} = tenant || {};
|
34
35
|
|
35
|
-
const
|
36
|
-
const
|
36
|
+
const systemPoolUsage = PoolStats?.find(({Name}) => Name === 'System')?.Usage;
|
37
|
+
const userPoolUsage = PoolStats?.find(({Name}) => Name === 'User')?.Usage;
|
37
38
|
|
38
|
-
const cpu =
|
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
|
-
|
41
|
-
|
42
|
-
const
|
43
|
-
|
44
|
-
|
45
|
-
|
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
|
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
|
-
|
54
|
-
|
61
|
+
blobStorage,
|
62
|
+
tableStorage,
|
63
|
+
cpuUsage,
|
55
64
|
memoryLimit,
|
56
|
-
|
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,
|
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
|
};
|
@@ -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;
|
package/dist/types/api/tenant.ts
CHANGED
@@ -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
|
-
|
62
|
-
|
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('
|
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
|
82
|
+
const result = roundToPrecision(Number(value) / sizes[size].value, precision);
|
83
83
|
|
84
84
|
return formatNumber(result);
|
85
85
|
};
|
package/dist/utils/constants.ts
CHANGED
@@ -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
|
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
|
-
|
60
|
-
|
61
|
-
|
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
|
-
|
65
|
-
|
66
|
-
|
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
|
80
|
-
|
81
|
-
|
82
|
-
|
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 (
|
86
|
-
|
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
|
111
|
+
return roundToPrecision(rawCores, 3);
|
90
112
|
};
|
91
113
|
|
92
114
|
export const formatCPU = (value?: number | string) => {
|