ydb-embedded-ui 4.21.0 → 4.22.0
Sign up to get free protection for your applications and to get access to all the features.
- package/CHANGELOG.md +27 -0
- package/dist/components/NodeHostWrapper/NodeHostWrapper.scss +2 -1
- package/dist/components/QueryResultTable/QueryResultTable.tsx +7 -3
- package/dist/components/VirtualTable/TableChunk.tsx +2 -1
- package/dist/components/VirtualTable/VirtualTable.tsx +1 -1
- package/dist/containers/Node/Node.tsx +9 -17
- package/dist/containers/Node/NodeStructure/NodeStructure.tsx +8 -30
- package/dist/containers/Node/NodeStructure/Pdisk.tsx +32 -18
- package/dist/containers/Node/i18n/en.json +4 -0
- package/dist/containers/Node/i18n/index.ts +11 -0
- package/dist/containers/Node/i18n/ru.json +4 -0
- package/dist/containers/Nodes/Nodes.tsx +5 -10
- package/dist/containers/Nodes/getNodesColumns.tsx +57 -13
- package/dist/containers/Tablets/Tablets.tsx +3 -8
- package/dist/containers/Tenant/Diagnostics/Diagnostics.tsx +0 -1
- package/dist/containers/Tenant/Diagnostics/Network/Network.js +5 -10
- package/dist/containers/Tenant/Diagnostics/Network/utils.ts +6 -0
- package/dist/containers/Tenant/Query/ExecuteResult/ExecuteResult.scss +13 -5
- package/dist/containers/Tenant/Query/ExecuteResult/ExecuteResult.tsx +72 -18
- package/dist/containers/Tenant/Query/ExplainResult/ExplainResult.js +2 -1
- package/dist/containers/Tenant/Query/ExplainResult/utils.ts +6 -0
- package/dist/containers/Tenant/Query/QueryEditor/QueryEditor.js +11 -24
- package/dist/containers/Tenant/Query/utils/getPreparedResult.ts +4 -5
- package/dist/containers/UserSettings/i18n/en.json +5 -2
- package/dist/containers/UserSettings/i18n/ru.json +5 -2
- package/dist/containers/UserSettings/settings.ts +12 -6
- package/dist/store/reducers/executeQuery.ts +4 -3
- package/dist/store/reducers/nodes/nodes.ts +2 -2
- package/dist/store/reducers/nodes/types.ts +12 -1
- package/dist/store/reducers/nodes/utils.ts +6 -0
- package/dist/store/reducers/settings/settings.ts +5 -3
- package/dist/types/api/netInfo.ts +1 -1
- package/dist/types/api/nodes.ts +24 -0
- package/dist/types/api/query.ts +23 -8
- package/dist/types/store/query.ts +6 -0
- package/dist/utils/constants.ts +3 -0
- package/dist/utils/developerUI/__test__/developerUI.test.ts +50 -0
- package/dist/utils/developerUI/developerUI.ts +42 -0
- package/dist/utils/diagnostics.ts +1 -0
- package/dist/utils/query.ts +68 -13
- package/package.json +1 -1
- package/dist/utils/index.js +0 -9
- /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.22.0](https://github.com/ydb-platform/ydb-embedded-ui/compare/v4.21.1...v4.22.0) (2023-11-27)
|
4
|
+
|
5
|
+
|
6
|
+
### Features
|
7
|
+
|
8
|
+
* **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))
|
9
|
+
* **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))
|
10
|
+
|
11
|
+
|
12
|
+
### Bug Fixes
|
13
|
+
|
14
|
+
* 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))
|
15
|
+
* **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))
|
16
|
+
|
17
|
+
## [4.21.1](https://github.com/ydb-platform/ydb-embedded-ui/compare/v4.21.0...v4.21.1) (2023-11-17)
|
18
|
+
|
19
|
+
|
20
|
+
### Bug Fixes
|
21
|
+
|
22
|
+
* move inverted disk space setting to general page ([#589](https://github.com/ydb-platform/ydb-embedded-ui/issues/589)) ([b09345e](https://github.com/ydb-platform/ydb-embedded-ui/commit/b09345e1ebe9e7a47beea5ab2dac4257790232cc))
|
23
|
+
* **Nodes:** always use nodes when flag is enabled ([#584](https://github.com/ydb-platform/ydb-embedded-ui/issues/584)) ([6ac6ee2](https://github.com/ydb-platform/ydb-embedded-ui/commit/6ac6ee2516bb2612cc7832e67ffa0bf92615a36c))
|
24
|
+
* **QueryResultTable:** fix table error on null cell sort ([#590](https://github.com/ydb-platform/ydb-embedded-ui/issues/590)) ([805a339](https://github.com/ydb-platform/ydb-embedded-ui/commit/805a339b0bba34412bf8e854cf6d24ae7c080539))
|
25
|
+
* **Tablets:** reduce rerenders ([#585](https://github.com/ydb-platform/ydb-embedded-ui/issues/585)) ([f1767a1](https://github.com/ydb-platform/ydb-embedded-ui/commit/f1767a143b139de4cd7c0df5c8c243c0224ebd3c))
|
26
|
+
* turn on query modes and metrics cards by default ([#588](https://github.com/ydb-platform/ydb-embedded-ui/issues/588)) ([c2f0d74](https://github.com/ydb-platform/ydb-embedded-ui/commit/c2f0d7424cb3182926f125a1e8c16cd4a2d422b9))
|
27
|
+
* update links to VDisk and PDisk Developer UI ([#582](https://github.com/ydb-platform/ydb-embedded-ui/issues/582)) ([97dda88](https://github.com/ydb-platform/ydb-embedded-ui/commit/97dda88bd595295eefaed4c0cbcd333e84b047f0))
|
28
|
+
* **VirtualNodes:** display developerUI link on hover ([#587](https://github.com/ydb-platform/ydb-embedded-ui/issues/587)) ([ba6c249](https://github.com/ydb-platform/ydb-embedded-ui/commit/ba6c249a9793b0bac45607b0b36f284dea4e0a7a))
|
29
|
+
|
3
30
|
## [4.21.0](https://github.com/ydb-platform/ydb-embedded-ui/compare/v4.20.4...v4.21.0) (2023-10-27)
|
4
31
|
|
5
32
|
|
@@ -36,10 +36,14 @@ const prepareTypedColumns = (columns: ColumnType[]) => {
|
|
36
36
|
align: columnType === 'number' ? DataTable.RIGHT : DataTable.LEFT,
|
37
37
|
sortAccessor: (row) => {
|
38
38
|
const value = row[name];
|
39
|
-
|
39
|
+
|
40
|
+
if (value === undefined || value === null) {
|
41
|
+
return null;
|
42
|
+
}
|
43
|
+
|
40
44
|
return columnType === 'number' ? BigInt(value) : value;
|
41
45
|
},
|
42
|
-
render: ({
|
46
|
+
render: ({row}) => <Cell value={String(row[name])} />,
|
43
47
|
};
|
44
48
|
|
45
49
|
return column;
|
@@ -56,7 +60,7 @@ const prepareGenericColumns = (data: KeyValueRow[]) => {
|
|
56
60
|
name,
|
57
61
|
align: isNumeric(data[0][name]) ? DataTable.RIGHT : DataTable.LEFT,
|
58
62
|
sortAccessor: (row) => (isNumeric(row[name]) ? Number(row[name]) : row[name]),
|
59
|
-
render: ({
|
63
|
+
render: ({row}) => <Cell value={String(row[name])} />,
|
60
64
|
};
|
61
65
|
|
62
66
|
return column;
|
@@ -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
|
|
@@ -2,7 +2,6 @@ import * as React from 'react';
|
|
2
2
|
import {useLocation, useRouteMatch} from 'react-router';
|
3
3
|
import cn from 'bem-cn-lite';
|
4
4
|
import {useDispatch} from 'react-redux';
|
5
|
-
import _ from 'lodash';
|
6
5
|
|
7
6
|
import {Tabs} from '@gravity-ui/uikit';
|
8
7
|
import {Link} from 'react-router-dom';
|
@@ -52,21 +51,22 @@ function Node(props: NodeProps) {
|
|
52
51
|
const {tenantName: tenantNameFromQuery} = parseQuery(location);
|
53
52
|
|
54
53
|
const {activeTabVerified, nodeTabs} = React.useMemo(() => {
|
55
|
-
const hasStorage =
|
56
|
-
|
54
|
+
const hasStorage = node?.Roles?.find((el) => el === STORAGE_ROLE);
|
55
|
+
|
56
|
+
let actualActiveTab = activeTab;
|
57
57
|
if (!hasStorage && activeTab === STORAGE) {
|
58
|
-
|
58
|
+
actualActiveTab = OVERVIEW;
|
59
59
|
}
|
60
60
|
const nodePages = hasStorage ? NODE_PAGES : NODE_PAGES.filter((el) => el.id !== STORAGE);
|
61
61
|
|
62
|
-
const
|
62
|
+
const actualNodeTabs = nodePages.map((page) => {
|
63
63
|
return {
|
64
64
|
...page,
|
65
65
|
title: page.name,
|
66
66
|
};
|
67
67
|
});
|
68
68
|
|
69
|
-
return {activeTabVerified, nodeTabs};
|
69
|
+
return {activeTabVerified: actualActiveTab, nodeTabs: actualNodeTabs};
|
70
70
|
}, [activeTab, node]);
|
71
71
|
|
72
72
|
React.useEffect(() => {
|
@@ -100,13 +100,13 @@ function Node(props: NodeProps) {
|
|
100
100
|
size="l"
|
101
101
|
items={nodeTabs}
|
102
102
|
activeTab={activeTabVerified}
|
103
|
-
wrapTo={({id},
|
103
|
+
wrapTo={({id}, tabNode) => (
|
104
104
|
<Link
|
105
105
|
to={createHref(routes.node, {id: nodeId, activeTab: id})}
|
106
106
|
key={id}
|
107
107
|
className={b('tab')}
|
108
108
|
>
|
109
|
-
{
|
109
|
+
{tabNode}
|
110
110
|
</Link>
|
111
111
|
)}
|
112
112
|
allowNotSelected={true}
|
@@ -115,8 +115,6 @@ function Node(props: NodeProps) {
|
|
115
115
|
);
|
116
116
|
};
|
117
117
|
const renderTabContent = () => {
|
118
|
-
const {additionalNodesProps} = props;
|
119
|
-
|
120
118
|
switch (activeTab) {
|
121
119
|
case STORAGE: {
|
122
120
|
return (
|
@@ -134,13 +132,7 @@ function Node(props: NodeProps) {
|
|
134
132
|
}
|
135
133
|
|
136
134
|
case STRUCTURE: {
|
137
|
-
return (
|
138
|
-
<NodeStructure
|
139
|
-
className={b('node-page-wrapper')}
|
140
|
-
nodeId={nodeId}
|
141
|
-
additionalNodesProps={additionalNodesProps}
|
142
|
-
/>
|
143
|
-
);
|
135
|
+
return <NodeStructure className={b('node-page-wrapper')} nodeId={nodeId} />;
|
144
136
|
}
|
145
137
|
default:
|
146
138
|
return false;
|
@@ -1,7 +1,7 @@
|
|
1
|
-
import {useEffect, useRef
|
1
|
+
import {useEffect, useRef} from 'react';
|
2
2
|
import {useDispatch} from 'react-redux';
|
3
3
|
import url from 'url';
|
4
|
-
import
|
4
|
+
import {isEmpty} from 'lodash/fp';
|
5
5
|
|
6
6
|
import cn from 'bem-cn-lite';
|
7
7
|
|
@@ -13,15 +13,13 @@ import {selectNodeStructure} from '../../../store/reducers/node/selectors';
|
|
13
13
|
import {AutoFetcher} from '../../../utils/autofetcher';
|
14
14
|
import {useTypedSelector} from '../../../utils/hooks';
|
15
15
|
|
16
|
-
import type {AdditionalNodesProps} from '../../../types/additionalProps';
|
17
|
-
|
18
16
|
import {PDisk} from './Pdisk';
|
19
17
|
|
20
18
|
import './NodeStructure.scss';
|
21
19
|
|
22
20
|
const b = cn('kv-node-structure');
|
23
21
|
|
24
|
-
export function valueIsDefined(value:
|
22
|
+
export function valueIsDefined<T>(value: T | null | undefined): value is T {
|
25
23
|
return value !== null && value !== undefined;
|
26
24
|
}
|
27
25
|
|
@@ -32,24 +30,16 @@ function generateId({type, id}: {type: 'pdisk' | 'vdisk'; id: string}) {
|
|
32
30
|
interface NodeStructureProps {
|
33
31
|
nodeId: string;
|
34
32
|
className?: string;
|
35
|
-
additionalNodesProps?: AdditionalNodesProps;
|
36
33
|
}
|
37
34
|
|
38
35
|
const autofetcher = new AutoFetcher();
|
39
36
|
|
40
|
-
function NodeStructure({nodeId, className
|
37
|
+
function NodeStructure({nodeId, className}: NodeStructureProps) {
|
41
38
|
const dispatch = useDispatch();
|
42
39
|
|
43
40
|
const nodeStructure = useTypedSelector(selectNodeStructure);
|
44
41
|
|
45
42
|
const {loadingStructure, wasLoadedStructure} = useTypedSelector((state) => state.node);
|
46
|
-
const nodeData = useTypedSelector((state) => state.node?.data?.SystemStateInfo?.[0]);
|
47
|
-
|
48
|
-
const nodeHref = useMemo(() => {
|
49
|
-
return additionalNodesProps?.getNodeRef
|
50
|
-
? additionalNodesProps.getNodeRef(nodeData)
|
51
|
-
: undefined;
|
52
|
-
}, [nodeData, additionalNodesProps]);
|
53
43
|
|
54
44
|
const {pdiskId: pdiskIdFromUrl, vdiskId: vdiskIdFromUrl} = url.parse(
|
55
45
|
window.location.href,
|
@@ -57,23 +47,11 @@ function NodeStructure({nodeId, className, additionalNodesProps}: NodeStructureP
|
|
57
47
|
).query;
|
58
48
|
|
59
49
|
const scrollContainerRef = useRef<HTMLDivElement>(null);
|
60
|
-
const scrollContainer = scrollContainerRef.current;
|
61
50
|
|
62
51
|
const isReady = useRef(false);
|
63
52
|
|
64
53
|
const scrolled = useRef(false);
|
65
54
|
|
66
|
-
useEffect(() => {
|
67
|
-
return () => {
|
68
|
-
if (scrollContainer) {
|
69
|
-
scrollContainer.scrollTo({
|
70
|
-
behavior: 'smooth',
|
71
|
-
top: 0,
|
72
|
-
});
|
73
|
-
}
|
74
|
-
};
|
75
|
-
}, []);
|
76
|
-
|
77
55
|
useEffect(() => {
|
78
56
|
dispatch(getNodeStructure(nodeId));
|
79
57
|
autofetcher.start();
|
@@ -87,13 +65,13 @@ function NodeStructure({nodeId, className, additionalNodesProps}: NodeStructureP
|
|
87
65
|
}, [nodeId, dispatch]);
|
88
66
|
|
89
67
|
useEffect(() => {
|
90
|
-
if (!
|
68
|
+
if (!isEmpty(nodeStructure) && scrollContainerRef.current) {
|
91
69
|
isReady.current = true;
|
92
70
|
}
|
93
71
|
}, [nodeStructure]);
|
94
72
|
|
95
73
|
useEffect(() => {
|
96
|
-
if (isReady.current && !scrolled.current &&
|
74
|
+
if (isReady.current && !scrolled.current && scrollContainerRef.current) {
|
97
75
|
const element = document.getElementById(
|
98
76
|
generateId({type: 'pdisk', id: pdiskIdFromUrl as string}),
|
99
77
|
);
|
@@ -112,7 +90,7 @@ function NodeStructure({nodeId, className, additionalNodesProps}: NodeStructureP
|
|
112
90
|
}
|
113
91
|
|
114
92
|
if (element) {
|
115
|
-
|
93
|
+
scrollContainerRef.current.scrollTo({
|
116
94
|
behavior: 'smooth',
|
117
95
|
// should subtract 20 to avoid sticking the element to tabs
|
118
96
|
top: scrollToVdisk ? scrollToVdisk : element.offsetTop,
|
@@ -136,7 +114,7 @@ function NodeStructure({nodeId, className, additionalNodesProps}: NodeStructureP
|
|
136
114
|
id={generateId({type: 'pdisk', id: pDiskId})}
|
137
115
|
unfolded={pdiskIdFromUrl === pDiskId}
|
138
116
|
selectedVdiskId={vdiskIdFromUrl as string}
|
139
|
-
|
117
|
+
nodeId={nodeId}
|
140
118
|
/>
|
141
119
|
))
|
142
120
|
: renderStub();
|
@@ -12,15 +12,20 @@ import type {
|
|
12
12
|
PreparedStructureVDisk,
|
13
13
|
} from '../../../store/reducers/node/types';
|
14
14
|
import {EVDiskState} from '../../../types/api/vdisk';
|
15
|
-
import {bytesToGB
|
15
|
+
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 {
|
20
|
+
createPDiskDeveloperUILink,
|
21
|
+
createVDiskDeveloperUILink,
|
22
|
+
} from '../../../utils/developerUI/developerUI';
|
19
23
|
import EntityStatus from '../../../components/EntityStatus/EntityStatus';
|
20
24
|
import InfoViewer, {type InfoViewerItem} from '../../../components/InfoViewer/InfoViewer';
|
21
25
|
import {ProgressViewer} from '../../../components/ProgressViewer/ProgressViewer';
|
22
26
|
import {Icon} from '../../../components/Icon';
|
23
27
|
|
28
|
+
import i18n from '../i18n';
|
24
29
|
import {Vdisk} from './Vdisk';
|
25
30
|
import {valueIsDefined} from './NodeStructure';
|
26
31
|
import {PDiskTitleBadge} from './PDiskTitleBadge';
|
@@ -32,7 +37,7 @@ interface PDiskProps {
|
|
32
37
|
unfolded?: boolean;
|
33
38
|
id: string;
|
34
39
|
selectedVdiskId?: string;
|
35
|
-
|
40
|
+
nodeId: string | number;
|
36
41
|
}
|
37
42
|
|
38
43
|
enum VDiskTableColumnsIds {
|
@@ -54,11 +59,11 @@ const vDiskTableColumnsNames: Record<VDiskTableColumnsIdsValues, string> = {
|
|
54
59
|
function getColumns({
|
55
60
|
pDiskId,
|
56
61
|
selectedVdiskId,
|
57
|
-
|
62
|
+
nodeId,
|
58
63
|
}: {
|
59
64
|
pDiskId: number | undefined;
|
60
65
|
selectedVdiskId?: string;
|
61
|
-
|
66
|
+
nodeId?: string | number;
|
62
67
|
}) {
|
63
68
|
const columns: Column<PreparedStructureVDisk>[] = [
|
64
69
|
{
|
@@ -66,26 +71,31 @@ function getColumns({
|
|
66
71
|
header: vDiskTableColumnsNames[VDiskTableColumnsIds.slotId],
|
67
72
|
width: 100,
|
68
73
|
render: ({row}) => {
|
69
|
-
|
74
|
+
const vDiskSlotId = row.VDiskSlotId;
|
75
|
+
let vdiskInternalViewerLink = null;
|
70
76
|
|
71
|
-
if (
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
77
|
+
if (
|
78
|
+
valueIsDefined(nodeId) &&
|
79
|
+
valueIsDefined(pDiskId) &&
|
80
|
+
valueIsDefined(vDiskSlotId)
|
81
|
+
) {
|
82
|
+
vdiskInternalViewerLink = createVDiskDeveloperUILink({
|
83
|
+
nodeId,
|
84
|
+
pDiskId,
|
85
|
+
vDiskSlotId,
|
86
|
+
});
|
78
87
|
}
|
79
88
|
|
80
89
|
return (
|
81
90
|
<div className={b('vdisk-id', {selected: row.id === selectedVdiskId})}>
|
82
|
-
<span>{
|
91
|
+
<span>{vDiskSlotId}</span>
|
83
92
|
{vdiskInternalViewerLink && (
|
84
93
|
<Button
|
85
94
|
size="s"
|
86
95
|
className={b('external-button', {hidden: true})}
|
87
96
|
href={vdiskInternalViewerLink}
|
88
97
|
target="_blank"
|
98
|
+
title={i18n('vdisk.developer-ui-button-title')}
|
89
99
|
>
|
90
100
|
<Icon name="external" />
|
91
101
|
</Button>
|
@@ -156,7 +166,7 @@ export function PDisk({
|
|
156
166
|
id,
|
157
167
|
data,
|
158
168
|
selectedVdiskId,
|
159
|
-
|
169
|
+
nodeId,
|
160
170
|
unfolded: unfoldedFromProps,
|
161
171
|
}: PDiskProps) {
|
162
172
|
const [unfolded, setUnfolded] = useState(unfoldedFromProps ?? false);
|
@@ -190,7 +200,7 @@ export function PDisk({
|
|
190
200
|
<DataTable
|
191
201
|
theme="yandex-cloud"
|
192
202
|
data={vDisks}
|
193
|
-
columns={getColumns({
|
203
|
+
columns={getColumns({nodeId, pDiskId: PDiskId, selectedVdiskId})}
|
194
204
|
settings={{...DEFAULT_TABLE_SETTINGS, dynamicRender: false}}
|
195
205
|
rowClassName={(row) => {
|
196
206
|
return row.id === selectedVdiskId ? b('selected-vdisk') : '';
|
@@ -203,10 +213,13 @@ export function PDisk({
|
|
203
213
|
if (isEmpty(data)) {
|
204
214
|
return <div>No information about PDisk</div>;
|
205
215
|
}
|
206
|
-
let pDiskInternalViewerLink =
|
216
|
+
let pDiskInternalViewerLink = null;
|
207
217
|
|
208
|
-
if (
|
209
|
-
pDiskInternalViewerLink
|
218
|
+
if (valueIsDefined(PDiskId) && valueIsDefined(nodeId)) {
|
219
|
+
pDiskInternalViewerLink = createPDiskDeveloperUILink({
|
220
|
+
nodeId,
|
221
|
+
pDiskId: PDiskId,
|
222
|
+
});
|
210
223
|
}
|
211
224
|
|
212
225
|
const pdiskInfo: InfoViewerItem[] = [
|
@@ -222,6 +235,7 @@ export function PDisk({
|
|
222
235
|
href={pDiskInternalViewerLink}
|
223
236
|
target="_blank"
|
224
237
|
view="flat-secondary"
|
238
|
+
title={i18n('pdisk.developer-ui-button-title')}
|
225
239
|
>
|
226
240
|
<Icon name="external" />
|
227
241
|
</Button>
|
@@ -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-node-page';
|
7
|
+
|
8
|
+
i18n.registerKeyset(Lang.En, COMPONENT, en);
|
9
|
+
i18n.registerKeyset(Lang.Ru, COMPONENT, ru);
|
10
|
+
|
11
|
+
export default i18n.keyset(COMPONENT);
|
@@ -5,7 +5,6 @@ import {useDispatch} from 'react-redux';
|
|
5
5
|
import DataTable from '@gravity-ui/react-data-table';
|
6
6
|
import {ASCENDING} from '@gravity-ui/react-data-table/build/esm/lib/constants';
|
7
7
|
|
8
|
-
import type {EPathType} from '../../types/api/schema';
|
9
8
|
import type {ProblemFilterValue} from '../../store/reducers/settings/types';
|
10
9
|
import type {NodesSortParams} from '../../store/reducers/nodes/types';
|
11
10
|
|
@@ -39,8 +38,6 @@ import {selectFilteredNodes} from '../../store/reducers/nodes/selectors';
|
|
39
38
|
import {changeFilter, ProblemFilterValues} from '../../store/reducers/settings/settings';
|
40
39
|
import type {AdditionalNodesProps} from '../../types/additionalProps';
|
41
40
|
|
42
|
-
import {isDatabaseEntityType} from '../Tenant/utils/schema';
|
43
|
-
|
44
41
|
import {getNodesColumns} from './getNodesColumns';
|
45
42
|
|
46
43
|
import './Nodes.scss';
|
@@ -51,11 +48,10 @@ const b = cn('ydb-nodes');
|
|
51
48
|
|
52
49
|
interface NodesProps {
|
53
50
|
path?: string;
|
54
|
-
type?: EPathType;
|
55
51
|
additionalNodesProps?: AdditionalNodesProps;
|
56
52
|
}
|
57
53
|
|
58
|
-
export const Nodes = ({path,
|
54
|
+
export const Nodes = ({path, additionalNodesProps = {}}: NodesProps) => {
|
59
55
|
const dispatch = useDispatch();
|
60
56
|
|
61
57
|
const isClusterNodes = !path;
|
@@ -90,15 +86,14 @@ export const Nodes = ({path, type, additionalNodesProps = {}}: NodesProps) => {
|
|
90
86
|
dispatch(setDataWasNotLoaded());
|
91
87
|
}
|
92
88
|
|
93
|
-
//
|
94
|
-
|
95
|
-
if (path && (!useNodesEndpoint || !isDatabaseEntityType(type))) {
|
89
|
+
// If there is no path, it's cluster Nodes tab
|
90
|
+
if (path && !useNodesEndpoint) {
|
96
91
|
dispatch(getComputeNodes({path}));
|
97
92
|
} else {
|
98
|
-
dispatch(getNodes({
|
93
|
+
dispatch(getNodes({path}));
|
99
94
|
}
|
100
95
|
},
|
101
|
-
[dispatch, path,
|
96
|
+
[dispatch, path, useNodesEndpoint],
|
102
97
|
);
|
103
98
|
|
104
99
|
useAutofetcher(fetchNodes, [fetchNodes], isClusterNodes ? true : autorefresh);
|
@@ -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: '
|
188
|
-
render: ({row}) =>
|
189
|
-
|
190
|
-
|
191
|
-
|
192
|
-
|
193
|
-
|
194
|
-
|
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
|
}
|
@@ -34,14 +34,9 @@ interface TabletsProps {
|
|
34
34
|
export const Tablets = ({path, nodeId, className}: TabletsProps) => {
|
35
35
|
const dispatch = useDispatch();
|
36
36
|
|
37
|
-
const {
|
38
|
-
|
39
|
-
|
40
|
-
loading,
|
41
|
-
error,
|
42
|
-
stateFilter,
|
43
|
-
typeFilter,
|
44
|
-
} = useTypedSelector((state) => state.tablets);
|
37
|
+
const {data, wasLoaded, loading, error, stateFilter, typeFilter} = useTypedSelector(
|
38
|
+
(state) => state.tablets,
|
39
|
+
);
|
45
40
|
const {autorefresh} = useTypedSelector((state) => state.schema);
|
46
41
|
|
47
42
|
const tablets = useMemo(() => data?.TabletStateInfo || [], [data]);
|
@@ -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 =
|
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 =
|
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
|
-
|
232
|
+
getConnectedNodesCount(nodeInfo?.Peers)
|
238
233
|
}
|
239
234
|
onMouseEnter={showTooltip}
|
240
235
|
onMouseLeave={hideTooltip}
|