ydb-embedded-ui 3.3.4 → 3.4.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 +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
|
}
|