ydb-embedded-ui 4.21.0 → 4.22.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 +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}
|