ydb-embedded-ui 3.3.4 → 3.4.0
Sign up to get free protection for your applications and to get access to all the features.
- package/CHANGELOG.md +19 -0
- package/dist/components/Errors/ResponseError/ResponseError.tsx +2 -2
- package/dist/components/InfoViewer/formatters/topicStats.tsx +8 -29
- package/dist/components/LabelWithPopover/LabelWithPopover.tsx +20 -0
- package/dist/components/LabelWithPopover/index.ts +1 -0
- package/dist/components/LagImages/LagImages.tsx +205 -0
- package/dist/components/LagImages/index.ts +1 -0
- package/dist/components/SpeedMultiMeter/SpeedMultiMeter.scss +92 -0
- package/dist/components/SpeedMultiMeter/SpeedMultiMeter.tsx +120 -0
- package/dist/components/SpeedMultiMeter/i18n/en.json +6 -0
- package/dist/components/SpeedMultiMeter/i18n/index.ts +13 -0
- package/dist/components/SpeedMultiMeter/i18n/ru.json +6 -0
- package/dist/components/SpeedMultiMeter/index.ts +1 -0
- package/dist/containers/Storage/StorageGroups/StorageGroups.tsx +18 -14
- package/dist/containers/Storage/VDisk/VDisk.tsx +20 -5
- package/dist/containers/Storage/VDiskPopup/VDiskPopup.tsx +34 -5
- package/dist/containers/Storage/utils/types.ts +5 -0
- package/dist/containers/Tenant/Diagnostics/Consumers/Consumers.scss +32 -3
- package/dist/containers/Tenant/Diagnostics/Consumers/Consumers.tsx +62 -69
- package/dist/containers/Tenant/Diagnostics/Consumers/Headers/Headers.scss +13 -0
- package/dist/containers/Tenant/Diagnostics/Consumers/Headers/Headers.tsx +27 -0
- package/dist/containers/Tenant/Diagnostics/Consumers/Headers/index.ts +1 -0
- package/dist/containers/Tenant/Diagnostics/Consumers/TopicStats/ConsumersTopicStats.scss +32 -0
- package/dist/containers/Tenant/Diagnostics/Consumers/TopicStats/ConsumersTopicStats.tsx +43 -0
- package/dist/containers/Tenant/Diagnostics/Consumers/TopicStats/index.ts +1 -0
- package/dist/containers/Tenant/Diagnostics/Consumers/columns/Columns.scss +5 -0
- package/dist/containers/Tenant/Diagnostics/Consumers/columns/columns.tsx +66 -0
- package/dist/containers/Tenant/Diagnostics/Consumers/columns/index.ts +1 -0
- package/dist/containers/Tenant/Diagnostics/Consumers/i18n/en.json +4 -3
- package/dist/containers/Tenant/Diagnostics/Consumers/i18n/ru.json +4 -3
- package/dist/containers/Tenant/Diagnostics/Consumers/utils/constants.ts +23 -0
- package/dist/containers/Tenant/Diagnostics/Diagnostics.tsx +4 -0
- package/dist/containers/Tenant/Diagnostics/DiagnosticsPages.ts +8 -2
- package/dist/containers/Tenant/Diagnostics/Overview/TopicStats/TopicStats.scss +9 -1
- package/dist/containers/Tenant/Diagnostics/Overview/TopicStats/TopicStats.tsx +6 -8
- package/dist/containers/Tenant/Diagnostics/Partitions/Headers/Headers.scss +33 -0
- package/dist/containers/Tenant/Diagnostics/Partitions/Headers/Headers.tsx +76 -0
- package/dist/containers/Tenant/Diagnostics/Partitions/Headers/index.ts +1 -0
- package/dist/containers/Tenant/Diagnostics/Partitions/Partitions.scss +45 -0
- package/dist/containers/Tenant/Diagnostics/Partitions/Partitions.tsx +254 -0
- package/dist/containers/Tenant/Diagnostics/Partitions/PartitionsWrapper.tsx +79 -0
- package/dist/containers/Tenant/Diagnostics/Partitions/columns/Columns.scss +13 -0
- package/dist/containers/Tenant/Diagnostics/Partitions/columns/columns.tsx +246 -0
- package/dist/containers/Tenant/Diagnostics/Partitions/columns/index.ts +1 -0
- package/dist/containers/Tenant/Diagnostics/Partitions/i18n/en.json +13 -0
- package/dist/containers/Tenant/Diagnostics/Partitions/i18n/index.ts +11 -0
- package/dist/containers/Tenant/Diagnostics/Partitions/i18n/ru.json +13 -0
- package/dist/containers/Tenant/Diagnostics/Partitions/index.ts +1 -0
- package/dist/containers/Tenant/Diagnostics/Partitions/utils/constants.ts +74 -0
- package/dist/containers/Tenant/Diagnostics/Partitions/utils/types.ts +6 -0
- package/dist/containers/Tenant/utils/schema.ts +1 -16
- package/dist/services/api.d.ts +4 -0
- package/dist/services/api.js +22 -6
- package/dist/store/reducers/consumer.ts +160 -0
- package/dist/store/reducers/index.ts +2 -0
- package/dist/store/reducers/settings.js +2 -0
- package/dist/store/reducers/topic.ts +82 -2
- package/dist/types/store/consumer.ts +55 -0
- package/dist/types/store/topic.ts +23 -6
- package/dist/utils/bytesParsers/convertBytesObjectToSpeed.ts +24 -0
- package/dist/utils/bytesParsers/formatBytesCustom.ts +57 -0
- package/dist/utils/bytesParsers/i18n/en.json +7 -0
- package/dist/utils/bytesParsers/i18n/index.ts +11 -0
- package/dist/utils/bytesParsers/i18n/ru.json +7 -0
- package/dist/utils/bytesParsers/index.ts +2 -0
- package/dist/utils/constants.ts +3 -0
- package/dist/utils/index.js +6 -0
- package/dist/utils/storage.ts +2 -2
- package/dist/utils/timeParsers/index.ts +2 -1
- package/dist/utils/timeParsers/parsers.ts +18 -0
- package/dist/utils/timeParsers/{protobuf.ts → protobufParsers.ts} +0 -0
- package/dist/utils/utils.js +3 -3
- package/package.json +2 -2
@@ -7,6 +7,7 @@ import routes, {createHref} from '../../../routes';
|
|
7
7
|
import {EFlag} from '../../../types/api/enums';
|
8
8
|
import {EVDiskState, TVDiskStateInfo} from '../../../types/api/vdisk';
|
9
9
|
import {stringifyVdiskId} from '../../../utils';
|
10
|
+
import {isFullVDiksData} from '../../../utils/storage';
|
10
11
|
|
11
12
|
import {STRUCTURE} from '../../Node/NodePages';
|
12
13
|
|
@@ -14,6 +15,7 @@ import {DiskStateProgressBar, EDiskStateSeverity} from '../DiskStateProgressBar'
|
|
14
15
|
import type {NodesHosts} from '../PDiskPopup';
|
15
16
|
import {VDiskPopup} from '../VDiskPopup';
|
16
17
|
|
18
|
+
import type {IUnavailableDonor} from '../utils/types';
|
17
19
|
import {NOT_AVAILABLE_SEVERITY} from '../utils';
|
18
20
|
|
19
21
|
import './VDisk.scss';
|
@@ -46,20 +48,29 @@ const getColorSeverity = (color?: EFlag) => {
|
|
46
48
|
};
|
47
49
|
|
48
50
|
interface VDiskProps {
|
49
|
-
data?: TVDiskStateInfo;
|
51
|
+
data?: TVDiskStateInfo | IUnavailableDonor;
|
50
52
|
poolName?: string;
|
51
53
|
nodes?: NodesHosts;
|
52
54
|
compact?: boolean;
|
53
55
|
}
|
54
56
|
|
55
57
|
export const VDisk = ({data = {}, poolName, nodes, compact}: VDiskProps) => {
|
56
|
-
const
|
58
|
+
const isFullData = isFullVDiksData(data);
|
59
|
+
|
60
|
+
const [severity, setSeverity] = useState(
|
61
|
+
getStateSeverity(isFullData ? data.VDiskState : undefined),
|
62
|
+
);
|
57
63
|
const [isPopupVisible, setIsPopupVisible] = useState(false);
|
58
64
|
|
59
65
|
const anchor = useRef(null);
|
60
66
|
|
61
67
|
// determine disk status severity
|
62
68
|
useEffect(() => {
|
69
|
+
if (!isFullData) {
|
70
|
+
setSeverity(NOT_AVAILABLE_SEVERITY);
|
71
|
+
return;
|
72
|
+
}
|
73
|
+
|
63
74
|
const {DiskSpace, VDiskState, FrontQueues, Replicated, DonorMode} = data;
|
64
75
|
|
65
76
|
// if the disk is not available, this determines its status severity regardless of other features
|
@@ -84,7 +95,7 @@ export const VDisk = ({data = {}, poolName, nodes, compact}: VDiskProps) => {
|
|
84
95
|
}
|
85
96
|
|
86
97
|
setSeverity(newSeverity);
|
87
|
-
}, [data]);
|
98
|
+
}, [data, isFullData]);
|
88
99
|
|
89
100
|
const showPopup = () => {
|
90
101
|
setIsPopupVisible(true);
|
@@ -95,6 +106,10 @@ export const VDisk = ({data = {}, poolName, nodes, compact}: VDiskProps) => {
|
|
95
106
|
};
|
96
107
|
|
97
108
|
const vdiskAllocatedPercent = useMemo(() => {
|
109
|
+
if (!isFullData) {
|
110
|
+
return undefined;
|
111
|
+
}
|
112
|
+
|
98
113
|
const {AvailableSize, AllocatedSize, PDisk} = data;
|
99
114
|
const available = AvailableSize ? AvailableSize : PDisk?.AvailableSize;
|
100
115
|
|
@@ -105,7 +120,7 @@ export const VDisk = ({data = {}, poolName, nodes, compact}: VDiskProps) => {
|
|
105
120
|
return isNaN(Number(AllocatedSize))
|
106
121
|
? undefined
|
107
122
|
: (Number(AllocatedSize) * 100) / (Number(available) + Number(AllocatedSize));
|
108
|
-
}, [data]);
|
123
|
+
}, [data, isFullData]);
|
109
124
|
|
110
125
|
return (
|
111
126
|
<React.Fragment>
|
@@ -117,7 +132,7 @@ export const VDisk = ({data = {}, poolName, nodes, compact}: VDiskProps) => {
|
|
117
132
|
open={isPopupVisible}
|
118
133
|
/>
|
119
134
|
<div className={b()} ref={anchor} onMouseEnter={showPopup} onMouseLeave={hidePopup}>
|
120
|
-
{data.NodeId ? (
|
135
|
+
{data.NodeId && isFullData ? (
|
121
136
|
<InternalLink
|
122
137
|
to={createHref(
|
123
138
|
routes.node,
|
@@ -6,9 +6,12 @@ import {Label, Popup, PopupProps} from '@gravity-ui/uikit';
|
|
6
6
|
import {InfoViewer, InfoViewerItem} from '../../../components/InfoViewer';
|
7
7
|
|
8
8
|
import {EFlag} from '../../../types/api/enums';
|
9
|
-
import {TVDiskStateInfo} from '../../../types/api/vdisk';
|
9
|
+
import type {TVDiskStateInfo} from '../../../types/api/vdisk';
|
10
10
|
import {stringifyVdiskId} from '../../../utils';
|
11
11
|
import {bytesToGB, bytesToSpeed} from '../../../utils/utils';
|
12
|
+
import {isFullVDiksData} from '../../../utils/storage';
|
13
|
+
|
14
|
+
import type {IUnavailableDonor} from '../utils/types';
|
12
15
|
|
13
16
|
import {NodesHosts, preparePDiskData} from '../PDiskPopup';
|
14
17
|
|
@@ -16,6 +19,24 @@ import './VDiskPopup.scss';
|
|
16
19
|
|
17
20
|
const b = cn('vdisk-storage-popup');
|
18
21
|
|
22
|
+
const prepareUnavailableVDiskData = (data: IUnavailableDonor, poolName?: string) => {
|
23
|
+
const {NodeId, PDiskId, VSlotId} = data;
|
24
|
+
|
25
|
+
const vdiskData: InfoViewerItem[] = [{label: 'State', value: 'not available'}];
|
26
|
+
|
27
|
+
if (poolName) {
|
28
|
+
vdiskData.push({label: 'StoragePool', value: poolName});
|
29
|
+
}
|
30
|
+
|
31
|
+
vdiskData.push(
|
32
|
+
{label: 'NodeId', value: NodeId ?? '–'},
|
33
|
+
{label: 'PDiskId', value: PDiskId ?? '–'},
|
34
|
+
{label: 'VSlotId', value: VSlotId ?? '–'},
|
35
|
+
);
|
36
|
+
|
37
|
+
return vdiskData;
|
38
|
+
};
|
39
|
+
|
19
40
|
const prepareVDiskData = (data: TVDiskStateInfo, poolName?: string) => {
|
20
41
|
const {
|
21
42
|
VDiskId,
|
@@ -105,16 +126,24 @@ const prepareVDiskData = (data: TVDiskStateInfo, poolName?: string) => {
|
|
105
126
|
};
|
106
127
|
|
107
128
|
interface VDiskPopupProps extends PopupProps {
|
108
|
-
data: TVDiskStateInfo;
|
129
|
+
data: TVDiskStateInfo | IUnavailableDonor;
|
109
130
|
poolName?: string;
|
110
131
|
nodes?: NodesHosts;
|
111
132
|
}
|
112
133
|
|
113
134
|
export const VDiskPopup = ({data, poolName, nodes, ...props}: VDiskPopupProps) => {
|
114
|
-
const
|
135
|
+
const isFullData = isFullVDiksData(data);
|
136
|
+
|
137
|
+
const vdiskInfo = useMemo(
|
138
|
+
() =>
|
139
|
+
isFullData
|
140
|
+
? prepareVDiskData(data, poolName)
|
141
|
+
: prepareUnavailableVDiskData(data, poolName),
|
142
|
+
[data, poolName, isFullData],
|
143
|
+
);
|
115
144
|
const pdiskInfo = useMemo(
|
116
|
-
() => data.PDisk && preparePDiskData(data.PDisk, nodes),
|
117
|
-
[data
|
145
|
+
() => isFullData && data.PDisk && preparePDiskData(data.PDisk, nodes),
|
146
|
+
[data, nodes, isFullData],
|
118
147
|
);
|
119
148
|
|
120
149
|
return (
|
@@ -1,6 +1,35 @@
|
|
1
|
-
.
|
1
|
+
@import '../../../../styles/mixins.scss';
|
2
|
+
|
3
|
+
.ydb-diagnostics-consumers {
|
4
|
+
overflow: auto;
|
5
|
+
flex-grow: 1;
|
6
|
+
|
7
|
+
height: 100%;
|
8
|
+
|
9
|
+
@include flex-container();
|
10
|
+
|
11
|
+
&__controls {
|
12
|
+
@include controls();
|
13
|
+
}
|
14
|
+
|
2
15
|
&__search {
|
3
|
-
|
4
|
-
|
16
|
+
@include search();
|
17
|
+
}
|
18
|
+
|
19
|
+
&__table-settings .yc-icon {
|
20
|
+
width: 20px;
|
21
|
+
}
|
22
|
+
|
23
|
+
&__table-wrapper {
|
24
|
+
overflow: auto;
|
25
|
+
@include flex-container();
|
26
|
+
}
|
27
|
+
|
28
|
+
&__table-content {
|
29
|
+
overflow: auto;
|
30
|
+
|
31
|
+
height: 100%;
|
32
|
+
|
33
|
+
@include freeze-nth-column(1);
|
5
34
|
}
|
6
35
|
}
|
@@ -1,31 +1,36 @@
|
|
1
|
-
import {useCallback,
|
1
|
+
import {useCallback, useMemo, useState} from 'react';
|
2
2
|
import {useDispatch} from 'react-redux';
|
3
3
|
import block from 'bem-cn-lite';
|
4
4
|
import {escapeRegExp} from 'lodash/fp';
|
5
5
|
|
6
|
-
import DataTable
|
6
|
+
import DataTable from '@gravity-ui/react-data-table';
|
7
7
|
|
8
8
|
import type {EPathType} from '../../../../types/api/schema';
|
9
|
+
|
9
10
|
import {Loader} from '../../../../components/Loader';
|
10
|
-
import {prepareQueryError} from '../../../../utils/query';
|
11
|
-
import {DEFAULT_TABLE_SETTINGS} from '../../../../utils/constants';
|
12
|
-
import {useAutofetcher, useTypedSelector} from '../../../../utils/hooks';
|
13
11
|
import {Search} from '../../../../components/Search';
|
12
|
+
import {ResponseError} from '../../../../components/Errors/ResponseError';
|
13
|
+
|
14
|
+
import {useAutofetcher, useTypedSelector} from '../../../../utils/hooks';
|
15
|
+
import {DEFAULT_TABLE_SETTINGS} from '../../../../utils/constants';
|
16
|
+
|
14
17
|
import {
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
+
selectPreparedConsumersData,
|
19
|
+
selectPreparedTopicStats,
|
20
|
+
getTopic,
|
18
21
|
setDataWasNotLoaded,
|
19
|
-
} from '../../../../store/reducers/
|
20
|
-
import {selectSchemaMergedChildrenPaths} from '../../../../store/reducers/schema';
|
22
|
+
} from '../../../../store/reducers/topic';
|
21
23
|
|
22
24
|
import {isCdcStreamEntityType} from '../../utils/schema';
|
23
25
|
|
26
|
+
import {ConsumersTopicStats} from './TopicStats';
|
27
|
+
import {columns} from './columns';
|
28
|
+
|
24
29
|
import i18n from './i18n';
|
25
30
|
|
26
31
|
import './Consumers.scss';
|
27
32
|
|
28
|
-
const b = block('ydb-consumers');
|
33
|
+
const b = block('ydb-diagnostics-consumers');
|
29
34
|
|
30
35
|
interface ConsumersProps {
|
31
36
|
path: string;
|
@@ -33,93 +38,81 @@ interface ConsumersProps {
|
|
33
38
|
}
|
34
39
|
|
35
40
|
export const Consumers = ({path, type}: ConsumersProps) => {
|
41
|
+
const isCdcStream = isCdcStreamEntityType(type);
|
42
|
+
|
36
43
|
const dispatch = useDispatch();
|
37
44
|
|
38
|
-
const
|
45
|
+
const [searchValue, setSearchValue] = useState('');
|
39
46
|
|
40
|
-
const
|
41
|
-
|
42
|
-
);
|
47
|
+
const {autorefresh} = useTypedSelector((state) => state.schema);
|
48
|
+
const {loading, wasLoaded, error} = useTypedSelector((state) => state.topic);
|
43
49
|
|
44
|
-
const
|
50
|
+
const consumers = useTypedSelector((state) => selectPreparedConsumersData(state));
|
51
|
+
const topic = useTypedSelector((state) => selectPreparedTopicStats(state));
|
45
52
|
|
46
53
|
const fetchData = useCallback(
|
47
|
-
(isBackground
|
54
|
+
(isBackground) => {
|
48
55
|
if (!isBackground) {
|
49
|
-
dispatch(setDataWasNotLoaded
|
56
|
+
dispatch(setDataWasNotLoaded);
|
50
57
|
}
|
51
58
|
|
52
|
-
|
53
|
-
dispatch(setCurrentDescribePath(dataPath));
|
54
|
-
dispatch(getDescribe({path: dataPath}));
|
55
|
-
}
|
59
|
+
dispatch(getTopic(path));
|
56
60
|
},
|
57
|
-
|
58
|
-
[dispatch, dataPath],
|
61
|
+
[dispatch, path],
|
59
62
|
);
|
60
63
|
|
61
|
-
const {autorefresh} = useTypedSelector((state) => state.schema);
|
62
|
-
|
63
64
|
useAutofetcher(fetchData, [fetchData], autorefresh);
|
64
65
|
|
65
|
-
const
|
66
|
-
|
66
|
+
const dataToRender = useMemo(() => {
|
67
|
+
if (!consumers) {
|
68
|
+
return [];
|
69
|
+
}
|
67
70
|
|
68
|
-
|
71
|
+
const searchRe = new RegExp(escapeRegExp(searchValue), 'i');
|
69
72
|
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
+
return consumers.filter((consumer) => {
|
74
|
+
return searchRe.test(String(consumer.name));
|
75
|
+
});
|
76
|
+
}, [consumers, searchValue]);
|
73
77
|
|
74
|
-
const
|
75
|
-
|
76
|
-
? consumers.filter((consumer) => {
|
77
|
-
const re = new RegExp(escapeRegExp(search), 'i');
|
78
|
-
return re.test(consumer.name);
|
79
|
-
})
|
80
|
-
: consumers;
|
81
|
-
|
82
|
-
setConsumersToRender(filteredConsumers);
|
83
|
-
};
|
84
|
-
|
85
|
-
const handleSearch = (value: string) => {
|
86
|
-
filterConsumersByName(value);
|
78
|
+
const handleSearchChange = (value: string) => {
|
79
|
+
setSearchValue(value);
|
87
80
|
};
|
88
81
|
|
89
|
-
const columns: Column<any>[] = [
|
90
|
-
{
|
91
|
-
name: 'name',
|
92
|
-
header: i18n('table.columns.consumerName'),
|
93
|
-
width: 200,
|
94
|
-
},
|
95
|
-
];
|
96
|
-
|
97
82
|
if (loading && !wasLoaded) {
|
98
83
|
return <Loader size="m" />;
|
99
84
|
}
|
100
85
|
|
101
|
-
if (
|
102
|
-
return <
|
86
|
+
if (error) {
|
87
|
+
return <ResponseError error={error} />;
|
103
88
|
}
|
104
89
|
|
105
|
-
if (consumers.length
|
106
|
-
return <div
|
90
|
+
if (!consumers || !consumers.length) {
|
91
|
+
return <div>{i18n(`noConsumersMessage.${isCdcStream ? 'stream' : 'topic'}`)}</div>;
|
107
92
|
}
|
108
93
|
|
109
94
|
return (
|
110
95
|
<div className={b()}>
|
111
|
-
<
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
96
|
+
<div className={b('controls')}>
|
97
|
+
<Search
|
98
|
+
onChange={handleSearchChange}
|
99
|
+
placeholder={i18n('controls.search')}
|
100
|
+
className={b('search')}
|
101
|
+
value={searchValue}
|
102
|
+
/>
|
103
|
+
{topic && <ConsumersTopicStats data={topic} />}
|
104
|
+
</div>
|
105
|
+
<div className={b('table-wrapper')}>
|
106
|
+
<div className={b('table-content')}>
|
107
|
+
<DataTable
|
108
|
+
theme="yandex-cloud"
|
109
|
+
data={dataToRender}
|
110
|
+
columns={columns}
|
111
|
+
settings={DEFAULT_TABLE_SETTINGS}
|
112
|
+
emptyDataMessage={i18n('table.emptyDataMessage')}
|
113
|
+
/>
|
114
|
+
</div>
|
115
|
+
</div>
|
123
116
|
</div>
|
124
117
|
);
|
125
118
|
};
|
@@ -0,0 +1,27 @@
|
|
1
|
+
import block from 'bem-cn-lite';
|
2
|
+
|
3
|
+
import {ReadLagImage} from '../../../../../components/LagImages';
|
4
|
+
import {LabelWithPopover} from '../../../../../components/LabelWithPopover';
|
5
|
+
|
6
|
+
import {CONSUMERS_COLUMNS_IDS, CONSUMERS_COLUMNS_TITILES} from '../utils/constants';
|
7
|
+
|
8
|
+
import i18n from '../i18n';
|
9
|
+
|
10
|
+
import './Headers.scss';
|
11
|
+
|
12
|
+
const b = block('ydb-diagnostics-consumers-columns-header');
|
13
|
+
|
14
|
+
export const ReadLagsHeader = () => (
|
15
|
+
<LabelWithPopover
|
16
|
+
className={b('lags')}
|
17
|
+
headerText={CONSUMERS_COLUMNS_TITILES[CONSUMERS_COLUMNS_IDS.READ_LAGS]}
|
18
|
+
popoverContent={
|
19
|
+
<div className={b('lags-popover-content')}>
|
20
|
+
<div>{i18n('lagsPopover.readLags')}</div>
|
21
|
+
<div>
|
22
|
+
<ReadLagImage />
|
23
|
+
</div>
|
24
|
+
</div>
|
25
|
+
}
|
26
|
+
/>
|
27
|
+
);
|
@@ -0,0 +1 @@
|
|
1
|
+
export * from './Headers';
|
@@ -0,0 +1,32 @@
|
|
1
|
+
.ydb-diagnostics-consumers-topic-stats {
|
2
|
+
font-size: var(--yc-text-body-2-font-size);
|
3
|
+
line-height: var(--yc-text-body-2-line-height);
|
4
|
+
|
5
|
+
&__wrapper {
|
6
|
+
display: flex;
|
7
|
+
flex-direction: row;
|
8
|
+
|
9
|
+
padding-left: 16px;
|
10
|
+
|
11
|
+
border-left: 1px solid var(--yc-color-line-generic);
|
12
|
+
}
|
13
|
+
|
14
|
+
&__item {
|
15
|
+
display: flex;
|
16
|
+
flex-direction: column;
|
17
|
+
|
18
|
+
margin-right: 20px;
|
19
|
+
}
|
20
|
+
&__label {
|
21
|
+
margin-bottom: 4px;
|
22
|
+
|
23
|
+
color: var(--yc-color-text-secondary);
|
24
|
+
}
|
25
|
+
&__value {
|
26
|
+
display: flex;
|
27
|
+
justify-content: flex-end;
|
28
|
+
align-items: center;
|
29
|
+
|
30
|
+
height: 30px;
|
31
|
+
}
|
32
|
+
}
|
@@ -0,0 +1,43 @@
|
|
1
|
+
import block from 'bem-cn-lite';
|
2
|
+
|
3
|
+
import type {IPreparedTopicStats} from '../../../../../types/store/topic';
|
4
|
+
import {formatMsToUptime} from '../../../../../utils';
|
5
|
+
import {SpeedMultiMeter} from '../../../../../components/SpeedMultiMeter';
|
6
|
+
|
7
|
+
import './ConsumersTopicStats.scss';
|
8
|
+
|
9
|
+
const b = block('ydb-diagnostics-consumers-topic-stats');
|
10
|
+
|
11
|
+
interface ConsumersTopicStatsProps {
|
12
|
+
data?: IPreparedTopicStats;
|
13
|
+
}
|
14
|
+
|
15
|
+
export const ConsumersTopicStats = ({data}: ConsumersTopicStatsProps) => {
|
16
|
+
const {writeSpeed, partitionsWriteLag, partitionsIdleTime} = data || {};
|
17
|
+
|
18
|
+
const values = [
|
19
|
+
{
|
20
|
+
label: 'Write speed',
|
21
|
+
value: <SpeedMultiMeter data={writeSpeed} />,
|
22
|
+
},
|
23
|
+
{
|
24
|
+
label: 'Write lag',
|
25
|
+
value: formatMsToUptime(partitionsWriteLag || 0),
|
26
|
+
},
|
27
|
+
{
|
28
|
+
label: 'Write idle time',
|
29
|
+
value: formatMsToUptime(partitionsIdleTime || 0),
|
30
|
+
},
|
31
|
+
];
|
32
|
+
|
33
|
+
return (
|
34
|
+
<div className={b('wrapper')}>
|
35
|
+
{values.map((value, index) => (
|
36
|
+
<div key={index} className={b('item')}>
|
37
|
+
<div className={b('label')}>{value.label}</div>
|
38
|
+
<div className={b('value')}>{value.value}</div>
|
39
|
+
</div>
|
40
|
+
))}
|
41
|
+
</div>
|
42
|
+
);
|
43
|
+
};
|
@@ -0,0 +1 @@
|
|
1
|
+
export * from './ConsumersTopicStats';
|
@@ -0,0 +1,66 @@
|
|
1
|
+
import DataTable, {Column} from '@gravity-ui/react-data-table';
|
2
|
+
import block from 'bem-cn-lite';
|
3
|
+
|
4
|
+
import type {IPreparedConsumerData} from '../../../../../types/store/topic';
|
5
|
+
import {SpeedMultiMeter} from '../../../../../components/SpeedMultiMeter';
|
6
|
+
import {formatMsToUptime} from '../../../../../utils';
|
7
|
+
|
8
|
+
import {
|
9
|
+
CONSUMERS_COLUMNS_IDS,
|
10
|
+
CONSUMERS_COLUMNS_TITILES,
|
11
|
+
CONSUMERS_READ_LAGS_SUB_COLUMNS_IDS,
|
12
|
+
CONSUMERS_READ_LAGS_SUB_COLUMNS_TITLES,
|
13
|
+
} from '../utils/constants';
|
14
|
+
|
15
|
+
import {ReadLagsHeader} from '../Headers';
|
16
|
+
|
17
|
+
import './Columns.scss';
|
18
|
+
|
19
|
+
const b = block('ydb-diagnostics-consumers-columns');
|
20
|
+
|
21
|
+
export const columns: Column<IPreparedConsumerData>[] = [
|
22
|
+
{
|
23
|
+
name: CONSUMERS_COLUMNS_IDS.CONSUMER,
|
24
|
+
header: CONSUMERS_COLUMNS_TITILES[CONSUMERS_COLUMNS_IDS.CONSUMER],
|
25
|
+
align: DataTable.LEFT,
|
26
|
+
render: ({row}) => row.name || '-',
|
27
|
+
},
|
28
|
+
{
|
29
|
+
name: CONSUMERS_COLUMNS_IDS.READ_SPEED,
|
30
|
+
header: CONSUMERS_COLUMNS_TITILES[CONSUMERS_COLUMNS_IDS.READ_SPEED],
|
31
|
+
align: DataTable.RIGHT,
|
32
|
+
sortAccessor: (row) => row.readSpeed.perMinute,
|
33
|
+
render: ({row}) => <SpeedMultiMeter data={row.readSpeed} />,
|
34
|
+
},
|
35
|
+
{
|
36
|
+
name: CONSUMERS_COLUMNS_IDS.READ_LAGS,
|
37
|
+
header: <ReadLagsHeader />,
|
38
|
+
className: b('lags-header'),
|
39
|
+
sub: [
|
40
|
+
{
|
41
|
+
name: CONSUMERS_READ_LAGS_SUB_COLUMNS_IDS.WRITE_LAG,
|
42
|
+
header: CONSUMERS_READ_LAGS_SUB_COLUMNS_TITLES[
|
43
|
+
CONSUMERS_READ_LAGS_SUB_COLUMNS_IDS.WRITE_LAG
|
44
|
+
],
|
45
|
+
align: DataTable.RIGHT,
|
46
|
+
render: ({row}) => formatMsToUptime(row.writeLag),
|
47
|
+
},
|
48
|
+
{
|
49
|
+
name: CONSUMERS_READ_LAGS_SUB_COLUMNS_IDS.READ_LAG,
|
50
|
+
header: CONSUMERS_READ_LAGS_SUB_COLUMNS_TITLES[
|
51
|
+
CONSUMERS_READ_LAGS_SUB_COLUMNS_IDS.READ_LAG
|
52
|
+
],
|
53
|
+
align: DataTable.RIGHT,
|
54
|
+
render: ({row}) => formatMsToUptime(row.readLag),
|
55
|
+
},
|
56
|
+
{
|
57
|
+
name: CONSUMERS_READ_LAGS_SUB_COLUMNS_IDS.READ_IDLE_TIME,
|
58
|
+
header: CONSUMERS_READ_LAGS_SUB_COLUMNS_TITLES[
|
59
|
+
CONSUMERS_READ_LAGS_SUB_COLUMNS_IDS.READ_IDLE_TIME
|
60
|
+
],
|
61
|
+
align: DataTable.RIGHT,
|
62
|
+
render: ({row}) => formatMsToUptime(row.readIdleTime),
|
63
|
+
},
|
64
|
+
],
|
65
|
+
},
|
66
|
+
];
|
@@ -0,0 +1 @@
|
|
1
|
+
export * from './columns';
|
@@ -1,6 +1,7 @@
|
|
1
1
|
{
|
2
|
-
"
|
2
|
+
"noConsumersMessage.topic": "This topic has no consumers",
|
3
|
+
"noConsumersMessage.stream": "This changefeed has no consumers",
|
4
|
+
"lagsPopover.readLags": "Read lags statistics, maximum among all consumer partitions (time format dd hh:mm:ss)",
|
3
5
|
"table.emptyDataMessage": "No consumers match the current search",
|
4
|
-
"
|
5
|
-
"noConsumersMessage": "This topic has no consumers"
|
6
|
+
"controls.search": "Consumer"
|
6
7
|
}
|
@@ -1,6 +1,7 @@
|
|
1
1
|
{
|
2
|
-
"
|
2
|
+
"noConsumersMessage.topic": "У этого топика нет читателей",
|
3
|
+
"noConsumersMessage.stream": "У этого стрима нет читателей",
|
4
|
+
"lagsPopover.readLags": "Статистика лагов чтения, максимальной значение среди всех партиций читателя (формат времени дд чч:мм:сс)",
|
3
5
|
"table.emptyDataMessage": "По заданному поиску нет читателей",
|
4
|
-
"
|
5
|
-
"noConsumersMessage": "У этого топика нет читателей"
|
6
|
+
"controls.search": "Consumer"
|
6
7
|
}
|
@@ -0,0 +1,23 @@
|
|
1
|
+
export const CONSUMERS_COLUMNS_IDS = {
|
2
|
+
CONSUMER: 'consumer',
|
3
|
+
READ_SPEED: 'readSpeed',
|
4
|
+
READ_LAGS: 'readLags',
|
5
|
+
} as const;
|
6
|
+
|
7
|
+
export const CONSUMERS_COLUMNS_TITILES = {
|
8
|
+
[CONSUMERS_COLUMNS_IDS.CONSUMER]: 'Consumer',
|
9
|
+
[CONSUMERS_COLUMNS_IDS.READ_SPEED]: 'Read speed',
|
10
|
+
[CONSUMERS_COLUMNS_IDS.READ_LAGS]: 'Read lags, duration',
|
11
|
+
} as const;
|
12
|
+
|
13
|
+
export const CONSUMERS_READ_LAGS_SUB_COLUMNS_IDS = {
|
14
|
+
WRITE_LAG: 'writeLag',
|
15
|
+
READ_LAG: 'readLag',
|
16
|
+
READ_IDLE_TIME: 'readIdleTime',
|
17
|
+
} as const;
|
18
|
+
|
19
|
+
export const CONSUMERS_READ_LAGS_SUB_COLUMNS_TITLES = {
|
20
|
+
[CONSUMERS_READ_LAGS_SUB_COLUMNS_IDS.WRITE_LAG]: 'write lag',
|
21
|
+
[CONSUMERS_READ_LAGS_SUB_COLUMNS_IDS.READ_LAG]: 'read lag',
|
22
|
+
[CONSUMERS_READ_LAGS_SUB_COLUMNS_IDS.READ_IDLE_TIME]: 'read idle time',
|
23
|
+
} as const;
|
@@ -27,6 +27,7 @@ import {Nodes} from '../../Nodes';
|
|
27
27
|
//@ts-ignore
|
28
28
|
import {Tablets} from '../../Tablets';
|
29
29
|
import {Consumers} from './Consumers';
|
30
|
+
import {PartitionsWrapper} from './Partitions';
|
30
31
|
|
31
32
|
import routes, {createHref} from '../../../routes';
|
32
33
|
import type {EPathType} from '../../../types/api/schema';
|
@@ -156,6 +157,9 @@ function Diagnostics(props: DiagnosticsProps) {
|
|
156
157
|
case GeneralPagesIds.consumers: {
|
157
158
|
return <Consumers path={currentSchemaPath} type={type} />;
|
158
159
|
}
|
160
|
+
case GeneralPagesIds.partitions: {
|
161
|
+
return <PartitionsWrapper path={currentSchemaPath} type={type} />;
|
162
|
+
}
|
159
163
|
default: {
|
160
164
|
return <div>No data...</div>;
|
161
165
|
}
|