ydb-embedded-ui 4.21.1 → 4.23.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (60) hide show
  1. package/CHANGELOG.md +27 -0
  2. package/dist/components/ProgressViewer/ProgressViewer.tsx +1 -1
  3. package/dist/components/VirtualTable/TableChunk.tsx +2 -1
  4. package/dist/components/VirtualTable/VirtualTable.tsx +1 -1
  5. package/dist/containers/Cluster/Cluster.tsx +2 -0
  6. package/dist/containers/Cluster/ClusterInfo/ClusterInfo.scss +14 -5
  7. package/dist/containers/Cluster/ClusterInfo/ClusterInfo.tsx +104 -24
  8. package/dist/containers/Cluster/ClusterInfoSkeleton/ClusterInfoSkeleton.tsx +1 -1
  9. package/dist/containers/Cluster/i18n/en.json +16 -0
  10. package/dist/containers/Cluster/i18n/index.ts +11 -0
  11. package/dist/containers/Cluster/i18n/ru.json +16 -0
  12. package/dist/containers/Node/NodeStructure/Pdisk.tsx +4 -1
  13. package/dist/containers/Nodes/getNodesColumns.tsx +57 -13
  14. package/dist/containers/Tenant/Diagnostics/Network/Network.js +5 -10
  15. package/dist/containers/Tenant/Diagnostics/Network/utils.ts +6 -0
  16. package/dist/containers/Tenant/Diagnostics/TenantOverview/TenantCpu/TopNodesByCpu.tsx +18 -3
  17. package/dist/containers/Tenant/Diagnostics/TenantOverview/TenantCpu/TopNodesByLoad.tsx +18 -3
  18. package/dist/containers/Tenant/Diagnostics/TenantOverview/TenantCpu/TopQueries.tsx +17 -1
  19. package/dist/containers/Tenant/Diagnostics/TenantOverview/TenantCpu/TopShards.tsx +20 -1
  20. package/dist/containers/Tenant/Diagnostics/TenantOverview/TenantMemory/TopNodesByMemory.tsx +18 -3
  21. package/dist/containers/Tenant/Diagnostics/TenantOverview/TenantOverviewTableLayout.tsx +2 -1
  22. package/dist/containers/Tenant/Diagnostics/TenantOverview/TenantStorage/TopGroups.tsx +19 -2
  23. package/dist/containers/Tenant/Diagnostics/TenantOverview/TenantStorage/TopTables.tsx +8 -1
  24. package/dist/containers/Tenant/Diagnostics/TenantOverview/getSectionTitle.tsx +28 -0
  25. package/dist/containers/Tenant/Diagnostics/TenantOverview/i18n/en.json +17 -1
  26. package/dist/containers/Tenant/Diagnostics/TenantOverview/i18n/ru.json +17 -1
  27. package/dist/containers/Tenant/Query/ExecuteResult/ExecuteResult.scss +13 -5
  28. package/dist/containers/Tenant/Query/ExecuteResult/ExecuteResult.tsx +72 -18
  29. package/dist/containers/Tenant/Query/ExplainResult/ExplainResult.js +2 -1
  30. package/dist/containers/Tenant/Query/ExplainResult/utils.ts +6 -0
  31. package/dist/containers/Tenant/Query/QueryEditor/QueryEditor.js +11 -24
  32. package/dist/containers/Tenant/Query/utils/getPreparedResult.ts +4 -5
  33. package/dist/containers/Tenant/Schema/SchemaViewer/SchemaViewer.tsx +19 -11
  34. package/dist/containers/UserSettings/i18n/en.json +3 -0
  35. package/dist/containers/UserSettings/i18n/ru.json +3 -0
  36. package/dist/containers/UserSettings/settings.ts +7 -0
  37. package/dist/store/reducers/cluster/__test__/parseGroupsStatsQueryResponse.test.ts +121 -0
  38. package/dist/store/reducers/cluster/cluster.ts +46 -2
  39. package/dist/store/reducers/cluster/types.ts +29 -4
  40. package/dist/store/reducers/cluster/utils.ts +88 -0
  41. package/dist/store/reducers/executeQuery.ts +4 -3
  42. package/dist/store/reducers/nodes/types.ts +11 -1
  43. package/dist/store/reducers/nodes/utils.ts +6 -0
  44. package/dist/store/reducers/settings/settings.ts +2 -0
  45. package/dist/types/api/cluster.ts +3 -0
  46. package/dist/types/api/netInfo.ts +1 -1
  47. package/dist/types/api/nodes.ts +24 -0
  48. package/dist/types/api/query.ts +23 -8
  49. package/dist/types/store/query.ts +6 -0
  50. package/dist/utils/constants.ts +3 -0
  51. package/dist/utils/developerUI/__test__/developerUI.test.ts +50 -0
  52. package/dist/utils/developerUI/developerUI.ts +42 -0
  53. package/dist/utils/diagnostics.ts +1 -0
  54. package/dist/utils/hooks/index.ts +1 -0
  55. package/dist/utils/hooks/useSearchQuery.ts +9 -0
  56. package/dist/utils/query.ts +60 -12
  57. package/package.json +1 -1
  58. package/dist/utils/developerUI.ts +0 -32
  59. package/dist/utils/index.js +0 -9
  60. /package/dist/{components/VirtualTable/utils.ts → utils/index.ts} +0 -0
package/CHANGELOG.md CHANGED
@@ -1,5 +1,32 @@
1
1
  # Changelog
2
2
 
3
+ ## [4.23.0](https://github.com/ydb-platform/ydb-embedded-ui/compare/v4.22.0...v4.23.0) (2023-12-06)
4
+
5
+
6
+ ### Features
7
+
8
+ * **ClusterInfo:** display groups stats ([#598](https://github.com/ydb-platform/ydb-embedded-ui/issues/598)) ([c31d048](https://github.com/ydb-platform/ydb-embedded-ui/commit/c31d0480a1b91cf01a660fd1d9726c6708f7c252))
9
+ * **TenantOverview:** add links to sections titles ([#599](https://github.com/ydb-platform/ydb-embedded-ui/issues/599)) ([30401fa](https://github.com/ydb-platform/ydb-embedded-ui/commit/30401fa354d90943bc4af4ddbf65466ce10381f9))
10
+
11
+
12
+ ### Bug Fixes
13
+
14
+ * **Schema:** display keys in right order ([#596](https://github.com/ydb-platform/ydb-embedded-ui/issues/596)) ([c99b7e2](https://github.com/ydb-platform/ydb-embedded-ui/commit/c99b7e2e97acffc1cab450dfbf758c38b8b6e4d5))
15
+
16
+ ## [4.22.0](https://github.com/ydb-platform/ydb-embedded-ui/compare/v4.21.1...v4.22.0) (2023-11-27)
17
+
18
+
19
+ ### Features
20
+
21
+ * **Query:** enable queries with multiple resultsets ([#595](https://github.com/ydb-platform/ydb-embedded-ui/issues/595)) ([2eedfb6](https://github.com/ydb-platform/ydb-embedded-ui/commit/2eedfb6ec3be932c7399bb67de901798c0b31b50))
22
+ * **TenantOverview:** add columns to memory table ([#593](https://github.com/ydb-platform/ydb-embedded-ui/issues/593)) ([6379577](https://github.com/ydb-platform/ydb-embedded-ui/commit/6379577782cfa69de9fb39640d2a143f1670be39))
23
+
24
+
25
+ ### Bug Fixes
26
+
27
+ * fix disks developer UI links for paths with nodeId ([#594](https://github.com/ydb-platform/ydb-embedded-ui/issues/594)) ([7f5a783](https://github.com/ydb-platform/ydb-embedded-ui/commit/7f5a78393d0c23e584ad73040fd0e73d404e5d01))
28
+ * **TopShards:** sort by InFlightTxCount ([#591](https://github.com/ydb-platform/ydb-embedded-ui/issues/591)) ([eb3592d](https://github.com/ydb-platform/ydb-embedded-ui/commit/eb3592d69a465814de27e8b1e368b34cc60fed2f))
29
+
3
30
  ## [4.21.1](https://github.com/ydb-platform/ydb-embedded-ui/compare/v4.21.0...v4.21.1) (2023-11-17)
4
31
 
5
32
 
@@ -44,7 +44,7 @@ Props description:
44
44
  interface ProgressViewerProps {
45
45
  value?: number | string;
46
46
  capacity?: number | string;
47
- formatValues?: (value?: number, capacity?: number) => (string | undefined)[];
47
+ formatValues?: (value?: number, capacity?: number) => (string | number | undefined)[];
48
48
  percents?: boolean;
49
49
  className?: string;
50
50
  size?: ProgressViewerSize;
@@ -1,8 +1,9 @@
1
1
  import {useEffect, useRef, memo} from 'react';
2
2
 
3
+ import {getArray} from '../../utils';
4
+
3
5
  import type {Column, Chunk, GetRowClassName} from './types';
4
6
  import {LoadingTableRow, TableRow} from './TableRow';
5
- import {getArray} from './utils';
6
7
 
7
8
  // With original memo generic types are lost
8
9
  const typedMemo: <T>(Component: T) => T = memo;
@@ -1,6 +1,7 @@
1
1
  import {useState, useReducer, useRef, useCallback, useEffect} from 'react';
2
2
 
3
3
  import type {IResponseError} from '../../types/api/error';
4
+ import {getArray} from '../../utils';
4
5
 
5
6
  import {TableWithControlsLayout} from '../TableWithControlsLayout/TableWithControlsLayout';
6
7
  import {ResponseError} from '../Errors/ResponseError';
@@ -31,7 +32,6 @@ import {TableHead} from './TableHead';
31
32
  import {TableChunk} from './TableChunk';
32
33
  import {EmptyTableRow} from './TableRow';
33
34
  import {useIntersectionObserver} from './useIntersectionObserver';
34
- import {getArray} from './utils';
35
35
  import i18n from './i18n';
36
36
  import {b} from './shared';
37
37
 
@@ -68,6 +68,7 @@ function Cluster({
68
68
  loading: clusterLoading,
69
69
  wasLoaded: clusterWasLoaded,
70
70
  error: clusterError,
71
+ groupsStats,
71
72
  } = useTypedSelector((state) => state.cluster);
72
73
  const {
73
74
  nodes,
@@ -135,6 +136,7 @@ function Cluster({
135
136
  <div className={b()} ref={container}>
136
137
  <ClusterInfo
137
138
  cluster={cluster}
139
+ groupsStats={groupsStats}
138
140
  versionsValues={versionsValues}
139
141
  loading={infoLoading}
140
142
  error={clusterError}
@@ -49,10 +49,6 @@
49
49
  }
50
50
  }
51
51
 
52
- &__metric-field {
53
- margin-top: -8px;
54
- }
55
-
56
52
  &__system-tablets {
57
53
  display: flex;
58
54
  flex-wrap: wrap;
@@ -83,7 +79,6 @@
83
79
  margin-left: 15px;
84
80
  padding: 0 !important;
85
81
  }
86
-
87
82
  &__links {
88
83
  display: flex;
89
84
  flex-flow: row wrap;
@@ -91,6 +86,20 @@
91
86
  gap: 12px;
92
87
  }
93
88
 
89
+ &__storage-groups-stats {
90
+ display: flex;
91
+ flex-direction: column;
92
+ gap: 11px;
93
+ }
94
+
95
+ &__groups-stats-bar {
96
+ cursor: pointer;
97
+ }
98
+
99
+ &__groups-stats-popup-content {
100
+ padding: 12px;
101
+ }
102
+
94
103
  &__clipboard-button {
95
104
  display: flex;
96
105
  align-items: center;
@@ -10,6 +10,7 @@ import {Tablet} from '../../../components/Tablet';
10
10
  import {ResponseError} from '../../../components/Errors/ResponseError';
11
11
  import {ExternalLinkWithIcon} from '../../../components/ExternalLinkWithIcon/ExternalLinkWithIcon';
12
12
  import {IconWrapper as Icon} from '../../../components/Icon/Icon';
13
+ import {ContentWithPopup} from '../../../components/ContentWithPopup/ContentWithPopup';
13
14
 
14
15
  import type {IResponseError} from '../../../types/api/error';
15
16
  import type {AdditionalClusterProps, ClusterLink} from '../../../types/additionalProps';
@@ -18,14 +19,21 @@ import type {TClusterInfo} from '../../../types/api/cluster';
18
19
  import {backend, customBackend} from '../../../store';
19
20
  import {formatStorageValues} from '../../../utils/dataFormatters/dataFormatters';
20
21
  import {useSetting, useTypedSelector} from '../../../utils/hooks';
22
+ import {formatBytes, getSizeWithSignificantDigits} from '../../../utils/bytesParsers';
21
23
  import {
22
24
  CLUSTER_DEFAULT_TITLE,
23
25
  CLUSTER_INFO_HIDDEN_KEY,
24
26
  DEVELOPER_UI_TITLE,
25
27
  } from '../../../utils/constants';
28
+ import type {
29
+ ClusterGroupsStats,
30
+ DiskErasureGroupsStats,
31
+ DiskGroupsStats,
32
+ } from '../../../store/reducers/cluster/types';
26
33
 
27
34
  import {VersionsBar} from '../VersionsBar/VersionsBar';
28
35
  import {ClusterInfoSkeleton} from '../ClusterInfoSkeleton/ClusterInfoSkeleton';
36
+ import i18n from '../i18n';
29
37
 
30
38
  import {compareTablets} from './utils';
31
39
 
@@ -33,9 +41,85 @@ import './ClusterInfo.scss';
33
41
 
34
42
  const b = block('cluster-info');
35
43
 
44
+ interface GroupsStatsPopupContentProps {
45
+ stats: DiskErasureGroupsStats;
46
+ }
47
+
48
+ const GroupsStatsPopupContent = ({stats}: GroupsStatsPopupContentProps) => {
49
+ const {diskType, erasure, allocatedSize, availableSize} = stats;
50
+
51
+ const sizeToConvert = getSizeWithSignificantDigits(Math.max(allocatedSize, availableSize), 2);
52
+
53
+ const convertedAllocatedSize = formatBytes({value: allocatedSize, size: sizeToConvert});
54
+ const convertedAvailableSize = formatBytes({value: availableSize, size: sizeToConvert});
55
+
56
+ const usage = Math.round((allocatedSize / (allocatedSize + availableSize)) * 100);
57
+
58
+ const info = [
59
+ {
60
+ label: i18n('disk-type'),
61
+ value: diskType,
62
+ },
63
+ {
64
+ label: i18n('erasure'),
65
+ value: erasure,
66
+ },
67
+ {
68
+ label: i18n('allocated'),
69
+ value: convertedAllocatedSize,
70
+ },
71
+ {
72
+ label: i18n('available'),
73
+ value: convertedAvailableSize,
74
+ },
75
+ {
76
+ label: i18n('usage'),
77
+ value: usage + '%',
78
+ },
79
+ ];
80
+
81
+ return (
82
+ <InfoViewer dots={true} info={info} className={b('groups-stats-popup-content')} size="s" />
83
+ );
84
+ };
85
+
86
+ interface DiskGroupsStatsProps {
87
+ stats: DiskGroupsStats;
88
+ }
89
+
90
+ const DiskGroupsStatsBars = ({stats}: DiskGroupsStatsProps) => {
91
+ return (
92
+ <div className={b('storage-groups-stats')}>
93
+ {Object.values(stats).map((erasureStats) => (
94
+ <ContentWithPopup
95
+ placement={['right']}
96
+ key={erasureStats.erasure}
97
+ content={<GroupsStatsPopupContent stats={erasureStats} />}
98
+ >
99
+ <ProgressViewer
100
+ className={b('groups-stats-bar')}
101
+ value={erasureStats.createdGroups}
102
+ capacity={erasureStats.totalGroups}
103
+ />
104
+ </ContentWithPopup>
105
+ ))}
106
+ </div>
107
+ );
108
+ };
109
+
110
+ const getGroupsStatsFields = (groupsStats: ClusterGroupsStats) => {
111
+ return Object.keys(groupsStats).map((diskType) => {
112
+ return {
113
+ label: i18n('storage-groups', {diskType}),
114
+ value: <DiskGroupsStatsBars stats={groupsStats[diskType]} />,
115
+ };
116
+ });
117
+ };
118
+
36
119
  const getInfo = (
37
120
  cluster: TClusterInfo,
38
121
  versionsValues: VersionValue[],
122
+ groupsStats: ClusterGroupsStats,
39
123
  additionalInfo: InfoViewerItem[],
40
124
  links: ClusterLink[],
41
125
  ) => {
@@ -43,14 +127,14 @@ const getInfo = (
43
127
 
44
128
  if (cluster.DataCenters) {
45
129
  info.push({
46
- label: 'DC',
130
+ label: i18n('dc'),
47
131
  value: <Tags tags={cluster.DataCenters} />,
48
132
  });
49
133
  }
50
134
 
51
135
  if (cluster.SystemTablets) {
52
136
  info.push({
53
- label: 'Tablets',
137
+ label: i18n('tablets'),
54
138
  value: (
55
139
  <div className={b('system-tablets')}>
56
140
  {cluster.SystemTablets.sort(compareTablets).map((tablet, tabletIndex) => (
@@ -63,46 +147,40 @@ const getInfo = (
63
147
 
64
148
  if (cluster.Tenants) {
65
149
  info.push({
66
- label: 'Databases',
150
+ label: i18n('databases'),
67
151
  value: cluster.Tenants,
68
152
  });
69
153
  }
70
154
 
71
155
  info.push(
72
156
  {
73
- label: 'Nodes',
74
- value: (
75
- <ProgressViewer
76
- className={b('metric-field')}
77
- value={cluster?.NodesAlive}
78
- capacity={cluster?.NodesTotal}
79
- />
80
- ),
157
+ label: i18n('nodes'),
158
+ value: <ProgressViewer value={cluster?.NodesAlive} capacity={cluster?.NodesTotal} />,
81
159
  },
82
160
  {
83
- label: 'Load',
84
- value: (
85
- <ProgressViewer
86
- className={b('metric-field')}
87
- value={cluster?.LoadAverage}
88
- capacity={cluster?.NumberOfCpus}
89
- />
90
- ),
161
+ label: i18n('load'),
162
+ value: <ProgressViewer value={cluster?.LoadAverage} capacity={cluster?.NumberOfCpus} />,
91
163
  },
92
164
  {
93
- label: 'Storage',
165
+ label: i18n('storage-size'),
94
166
  value: (
95
167
  <ProgressViewer
96
- className={b('metric-field')}
97
168
  value={cluster?.StorageUsed}
98
169
  capacity={cluster?.StorageTotal}
99
170
  formatValues={formatStorageValues}
100
171
  />
101
172
  ),
102
173
  },
174
+ );
175
+
176
+ if (Object.keys(groupsStats).length) {
177
+ info.push(...getGroupsStatsFields(groupsStats));
178
+ }
179
+
180
+ info.push(
103
181
  ...additionalInfo,
104
182
  {
105
- label: 'Links',
183
+ label: i18n('links'),
106
184
  value: (
107
185
  <div className={b('links')}>
108
186
  {links.map(({title, url}) => (
@@ -112,7 +190,7 @@ const getInfo = (
112
190
  ),
113
191
  },
114
192
  {
115
- label: 'Versions',
193
+ label: i18n('versions'),
116
194
  value: <VersionsBar versionsValues={versionsValues} />,
117
195
  },
118
196
  );
@@ -123,6 +201,7 @@ const getInfo = (
123
201
  interface ClusterInfoProps {
124
202
  cluster?: TClusterInfo;
125
203
  versionsValues?: VersionValue[];
204
+ groupsStats?: ClusterGroupsStats;
126
205
  loading?: boolean;
127
206
  error?: IResponseError;
128
207
  additionalClusterProps?: AdditionalClusterProps;
@@ -131,6 +210,7 @@ interface ClusterInfoProps {
131
210
  export const ClusterInfo = ({
132
211
  cluster = {},
133
212
  versionsValues = [],
213
+ groupsStats = {},
134
214
  loading,
135
215
  error,
136
216
  additionalClusterProps = {},
@@ -151,7 +231,7 @@ export const ClusterInfo = ({
151
231
 
152
232
  const {info = [], links = []} = additionalClusterProps;
153
233
 
154
- const clusterInfo = getInfo(cluster, versionsValues, info, [
234
+ const clusterInfo = getInfo(cluster, versionsValues, groupsStats, info, [
155
235
  {title: DEVELOPER_UI_TITLE, url: internalLink},
156
236
  ...links,
157
237
  ]);
@@ -18,7 +18,7 @@ interface ClusterInfoSkeletonProps {
18
18
  rows?: number;
19
19
  }
20
20
 
21
- export const ClusterInfoSkeleton = ({rows = 7, className}: ClusterInfoSkeletonProps) => (
21
+ export const ClusterInfoSkeleton = ({rows = 8, className}: ClusterInfoSkeletonProps) => (
22
22
  <div className={b(null, className)}>
23
23
  {[...new Array(rows)].map((_, index) => (
24
24
  <div className={b('row')} key={`skeleton-row-${index}`}>
@@ -0,0 +1,16 @@
1
+ {
2
+ "disk-type": "Disk Type",
3
+ "erasure": "Erasure",
4
+ "allocated": "Allocated",
5
+ "available": "Available",
6
+ "usage": "Usage",
7
+ "dc": "DC",
8
+ "tablets": "Tablets",
9
+ "databases": "Databases",
10
+ "nodes": "Nodes",
11
+ "load": "Load",
12
+ "storage-size": "Storage size",
13
+ "storage-groups": "Storage groups, {{diskType}}",
14
+ "links": "Links",
15
+ "versions": "Versions"
16
+ }
@@ -0,0 +1,11 @@
1
+ import {i18n, Lang} from '../../../utils/i18n';
2
+
3
+ import en from './en.json';
4
+ import ru from './ru.json';
5
+
6
+ const COMPONENT = 'ydb-cluster';
7
+
8
+ i18n.registerKeyset(Lang.En, COMPONENT, en);
9
+ i18n.registerKeyset(Lang.Ru, COMPONENT, ru);
10
+
11
+ export default i18n.keyset(COMPONENT);
@@ -0,0 +1,16 @@
1
+ {
2
+ "disk-type": "Тип диска",
3
+ "erasure": "Режим",
4
+ "allocated": "Использовано",
5
+ "available": "Доступно",
6
+ "usage": "Потребление",
7
+ "dc": "ДЦ",
8
+ "tablets": "Таблетки",
9
+ "databases": "Базы данных",
10
+ "nodes": "Узлы",
11
+ "load": "Нагрузка",
12
+ "storage-size": "Размер хранилища",
13
+ "storage-groups": "Группы хранения, {{diskType}}",
14
+ "links": "Ссылки",
15
+ "versions": "Версии"
16
+ }
@@ -16,7 +16,10 @@ import {bytesToGB} from '../../../utils/utils';
16
16
  import {formatStorageValuesToGb} from '../../../utils/dataFormatters/dataFormatters';
17
17
  import {getPDiskType} from '../../../utils/pdisk';
18
18
  import {DEFAULT_TABLE_SETTINGS} from '../../../utils/constants';
19
- import {createPDiskDeveloperUILink, createVDiskDeveloperUILink} from '../../../utils/developerUI';
19
+ import {
20
+ createPDiskDeveloperUILink,
21
+ createVDiskDeveloperUILink,
22
+ } from '../../../utils/developerUI/developerUI';
20
23
  import EntityStatus from '../../../components/EntityStatus/EntityStatus';
21
24
  import InfoViewer, {type InfoViewerItem} from '../../../components/InfoViewer/InfoViewer';
22
25
  import {ProgressViewer} from '../../../components/ProgressViewer/ProgressViewer';
@@ -28,6 +28,9 @@ const NODES_COLUMNS_IDS = {
28
28
  Tablets: 'Tablets',
29
29
  TopNodesLoadAverage: 'TopNodesLoadAverage',
30
30
  TopNodesMemory: 'TopNodesMemory',
31
+ SharedCacheUsage: 'SharedCacheUsage',
32
+ MemoryUsedInAlloc: 'MemoryUsedInAlloc',
33
+ TotalSessions: 'TotalSessions',
31
34
  };
32
35
 
33
36
  interface GetNodesColumnsProps {
@@ -184,23 +187,61 @@ const topNodesLoadAverageColumn: NodesColumn = {
184
187
 
185
188
  const topNodesMemoryColumn: NodesColumn = {
186
189
  name: NODES_COLUMNS_IDS.TopNodesMemory,
187
- header: 'Memory',
188
- render: ({row}) =>
189
- row.MemoryUsed ? (
190
- <ProgressViewer
191
- value={row.MemoryUsed}
192
- capacity={row.MemoryLimit}
193
- formatValues={formatStorageValuesToGb}
194
- colorizeProgress={true}
195
- />
196
- ) : (
197
- '—'
198
- ),
190
+ header: 'Process',
191
+ render: ({row}) => (
192
+ <ProgressViewer
193
+ value={row.MemoryUsed}
194
+ capacity={row.MemoryLimit}
195
+ formatValues={formatStorageValuesToGb}
196
+ colorizeProgress={true}
197
+ />
198
+ ),
199
+ align: DataTable.LEFT,
200
+ width: 140,
201
+ sortable: false,
202
+ };
203
+
204
+ const sharedCacheUsageColumn: NodesColumn = {
205
+ name: NODES_COLUMNS_IDS.SharedCacheUsage,
206
+ header: 'Tablet Cache',
207
+ render: ({row}) => (
208
+ <ProgressViewer
209
+ value={row.SharedCacheUsed}
210
+ capacity={row.SharedCacheLimit}
211
+ formatValues={formatStorageValuesToGb}
212
+ colorizeProgress={true}
213
+ />
214
+ ),
215
+ align: DataTable.LEFT,
216
+ width: 140,
217
+ sortable: false,
218
+ };
219
+
220
+ const memoryUsedInAllocColumn: NodesColumn = {
221
+ name: NODES_COLUMNS_IDS.MemoryUsedInAlloc,
222
+ header: 'Query Runtime',
223
+ render: ({row}) => (
224
+ <ProgressViewer
225
+ value={row.MemoryUsedInAlloc}
226
+ capacity={row.MemoryLimit}
227
+ formatValues={formatStorageValuesToGb}
228
+ colorizeProgress={true}
229
+ />
230
+ ),
199
231
  align: DataTable.LEFT,
200
232
  width: 140,
201
233
  sortable: false,
202
234
  };
203
235
 
236
+ const sessionsColumn: NodesColumn = {
237
+ name: NODES_COLUMNS_IDS.TotalSessions,
238
+ header: 'Sessions',
239
+ render: ({row}) => row.TotalSessions ?? '—',
240
+ align: DataTable.RIGHT,
241
+ width: 100,
242
+ sortable: false,
243
+ };
244
+
204
245
  export function getNodesColumns({tabletsPath, getNodeRef}: GetNodesColumnsProps): NodesColumn[] {
205
246
  return [
206
247
  nodeIdColumn,
@@ -241,8 +282,11 @@ export function getTopNodesByMemoryColumns({
241
282
  nodeIdColumn,
242
283
  getHostColumn(getNodeRef),
243
284
  uptimeColumn,
244
- topNodesMemoryColumn,
245
285
  topNodesLoadAverageColumn,
286
+ topNodesMemoryColumn,
287
+ sharedCacheUsageColumn,
288
+ memoryUsedInAllocColumn,
289
+ sessionsColumn,
246
290
  getTabletsColumn(tabletsPath),
247
291
  ];
248
292
  }
@@ -18,6 +18,8 @@ import {changeFilter, ProblemFilterValues} from '../../../../store/reducers/sett
18
18
  import {AutoFetcher} from '../../../../utils/autofetcher';
19
19
  import {getDefaultNodePath} from '../../../Node/NodePages';
20
20
 
21
+ import {getConnectedNodesCount} from './utils';
22
+
21
23
  import './Network.scss';
22
24
 
23
25
  const b = cn('network');
@@ -145,11 +147,6 @@ class Network extends React.Component {
145
147
  );
146
148
  };
147
149
 
148
- getConnectedNodesCount = (peers) => {
149
- const res = peers?.reduce((acc, item) => (item.Connected ? acc + 1 : acc), 0);
150
- return res;
151
- };
152
-
153
150
  renderNodes = (nodes, isRight) => {
154
151
  const {showId, showRacks, clickedNode} = this.state;
155
152
  let problemNodesCount = 0;
@@ -171,9 +168,7 @@ class Network extends React.Component {
171
168
  let capacity, connected;
172
169
  if (!isRight && nodeInfo?.Peers) {
173
170
  capacity = Object.keys(nodeInfo?.Peers).length;
174
- connected = this.getConnectedNodesCount(
175
- nodeInfo?.Peers,
176
- );
171
+ connected = getConnectedNodesCount(nodeInfo?.Peers);
177
172
  }
178
173
 
179
174
  if (
@@ -214,7 +209,7 @@ class Network extends React.Component {
214
209
  let capacity, connected;
215
210
  if (!isRight) {
216
211
  capacity = nodeInfo?.Peers?.length;
217
- connected = this.getConnectedNodesCount(nodeInfo?.Peers);
212
+ connected = getConnectedNodesCount(nodeInfo?.Peers);
218
213
  }
219
214
 
220
215
  if (
@@ -234,7 +229,7 @@ class Network extends React.Component {
234
229
  capacity={nodeInfo?.Peers && nodeInfo?.Peers.length}
235
230
  connected={
236
231
  nodeInfo?.Peers &&
237
- this.getConnectedNodesCount(nodeInfo?.Peers)
232
+ getConnectedNodesCount(nodeInfo?.Peers)
238
233
  }
239
234
  onMouseEnter={showTooltip}
240
235
  onMouseLeave={hideTooltip}
@@ -0,0 +1,6 @@
1
+ import type {TNetNodePeerInfo} from '../../../../types/api/netInfo';
2
+
3
+ // determine how many nodes have status Connected "true"
4
+ export const getConnectedNodesCount = (peers: TNetNodePeerInfo[] | undefined) => {
5
+ return peers?.reduce((acc, item) => (item.Connected ? acc + 1 : acc), 0);
6
+ };
@@ -1,16 +1,20 @@
1
1
  import {useDispatch} from 'react-redux';
2
2
  import {useCallback} from 'react';
3
3
 
4
- import {useAutofetcher, useTypedSelector} from '../../../../../utils/hooks';
4
+ import {useAutofetcher, useSearchQuery, useTypedSelector} from '../../../../../utils/hooks';
5
5
  import {
6
6
  getTopNodesByCpu,
7
7
  selectTopNodesByCpu,
8
8
  setDataWasNotLoaded,
9
9
  } from '../../../../../store/reducers/tenantOverview/topNodesByCpu/topNodesByCpu';
10
+ import {TENANT_DIAGNOSTICS_TABS_IDS} from '../../../../../store/reducers/tenant/constants';
10
11
  import type {AdditionalNodesProps} from '../../../../../types/additionalProps';
11
12
  import {getTopNodesByCpuColumns} from '../../../../Nodes/getNodesColumns';
12
- import {TenantOverviewTableLayout} from '../TenantOverviewTableLayout';
13
13
 
14
+ import {TenantTabsGroups, getTenantPath} from '../../../TenantPages';
15
+
16
+ import {TenantOverviewTableLayout} from '../TenantOverviewTableLayout';
17
+ import {getSectionTitle} from '../getSectionTitle';
14
18
  import i18n from '../i18n';
15
19
 
16
20
  interface TopNodesByCpuProps {
@@ -21,6 +25,8 @@ interface TopNodesByCpuProps {
21
25
  export function TopNodesByCpu({path, additionalNodesProps}: TopNodesByCpuProps) {
22
26
  const dispatch = useDispatch();
23
27
 
28
+ const query = useSearchQuery();
29
+
24
30
  const {wasLoaded, loading, error} = useTypedSelector((state) => state.topNodesByCpu);
25
31
  const {autorefresh} = useTypedSelector((state) => state.schema);
26
32
  const topNodes = useTypedSelector(selectTopNodesByCpu);
@@ -39,11 +45,20 @@ export function TopNodesByCpu({path, additionalNodesProps}: TopNodesByCpuProps)
39
45
 
40
46
  useAutofetcher(fetchNodes, [fetchNodes], autorefresh);
41
47
 
48
+ const title = getSectionTitle({
49
+ entity: i18n('nodes'),
50
+ postfix: i18n('by-pools-usage'),
51
+ link: getTenantPath({
52
+ ...query,
53
+ [TenantTabsGroups.diagnosticsTab]: TENANT_DIAGNOSTICS_TABS_IDS.nodes,
54
+ }),
55
+ });
56
+
42
57
  return (
43
58
  <TenantOverviewTableLayout
44
59
  data={topNodes || []}
45
60
  columns={columns}
46
- title="Top nodes by pools usage"
61
+ title={title}
47
62
  loading={loading}
48
63
  wasLoaded={wasLoaded}
49
64
  error={error}