ydb-embedded-ui 3.3.3 → 3.4.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (90) hide show
  1. package/CHANGELOG.md +28 -0
  2. package/dist/components/Errors/ResponseError/ResponseError.tsx +2 -2
  3. package/dist/components/InfoViewer/formatters/topicStats.tsx +8 -29
  4. package/dist/components/LabelWithPopover/LabelWithPopover.tsx +20 -0
  5. package/dist/components/LabelWithPopover/index.ts +1 -0
  6. package/dist/components/LagImages/LagImages.tsx +205 -0
  7. package/dist/components/LagImages/index.ts +1 -0
  8. package/dist/components/SpeedMultiMeter/SpeedMultiMeter.scss +92 -0
  9. package/dist/components/SpeedMultiMeter/SpeedMultiMeter.tsx +120 -0
  10. package/dist/components/SpeedMultiMeter/i18n/en.json +6 -0
  11. package/dist/components/SpeedMultiMeter/i18n/index.ts +13 -0
  12. package/dist/components/SpeedMultiMeter/i18n/ru.json +6 -0
  13. package/dist/components/SpeedMultiMeter/index.ts +1 -0
  14. package/dist/containers/Storage/StorageGroups/StorageGroups.tsx +26 -14
  15. package/dist/containers/Storage/VDisk/VDisk.tsx +20 -5
  16. package/dist/containers/Storage/VDiskPopup/VDiskPopup.tsx +34 -5
  17. package/dist/containers/Storage/utils/types.ts +5 -0
  18. package/dist/containers/Tenant/Diagnostics/Consumers/Consumers.scss +32 -3
  19. package/dist/containers/Tenant/Diagnostics/Consumers/Consumers.tsx +62 -69
  20. package/dist/containers/Tenant/Diagnostics/Consumers/Headers/Headers.scss +13 -0
  21. package/dist/containers/Tenant/Diagnostics/Consumers/Headers/Headers.tsx +27 -0
  22. package/dist/containers/Tenant/Diagnostics/Consumers/Headers/index.ts +1 -0
  23. package/dist/containers/Tenant/Diagnostics/Consumers/TopicStats/ConsumersTopicStats.scss +32 -0
  24. package/dist/containers/Tenant/Diagnostics/Consumers/TopicStats/ConsumersTopicStats.tsx +43 -0
  25. package/dist/containers/Tenant/Diagnostics/Consumers/TopicStats/index.ts +1 -0
  26. package/dist/containers/Tenant/Diagnostics/Consumers/columns/Columns.scss +5 -0
  27. package/dist/containers/Tenant/Diagnostics/Consumers/columns/columns.tsx +66 -0
  28. package/dist/containers/Tenant/Diagnostics/Consumers/columns/index.ts +1 -0
  29. package/dist/containers/Tenant/Diagnostics/Consumers/i18n/en.json +4 -3
  30. package/dist/containers/Tenant/Diagnostics/Consumers/i18n/ru.json +4 -3
  31. package/dist/containers/Tenant/Diagnostics/Consumers/utils/constants.ts +23 -0
  32. package/dist/containers/Tenant/Diagnostics/Diagnostics.tsx +7 -3
  33. package/dist/containers/Tenant/Diagnostics/DiagnosticsPages.ts +15 -9
  34. package/dist/containers/Tenant/Diagnostics/Overview/TopicStats/TopicStats.scss +9 -1
  35. package/dist/containers/Tenant/Diagnostics/Overview/TopicStats/TopicStats.tsx +6 -8
  36. package/dist/containers/Tenant/Diagnostics/Partitions/Headers/Headers.scss +33 -0
  37. package/dist/containers/Tenant/Diagnostics/Partitions/Headers/Headers.tsx +76 -0
  38. package/dist/containers/Tenant/Diagnostics/Partitions/Headers/index.ts +1 -0
  39. package/dist/containers/Tenant/Diagnostics/Partitions/Partitions.scss +45 -0
  40. package/dist/containers/Tenant/Diagnostics/Partitions/Partitions.tsx +254 -0
  41. package/dist/containers/Tenant/Diagnostics/Partitions/PartitionsWrapper.tsx +79 -0
  42. package/dist/containers/Tenant/Diagnostics/Partitions/columns/Columns.scss +13 -0
  43. package/dist/containers/Tenant/Diagnostics/Partitions/columns/columns.tsx +246 -0
  44. package/dist/containers/Tenant/Diagnostics/Partitions/columns/index.ts +1 -0
  45. package/dist/containers/Tenant/Diagnostics/Partitions/i18n/en.json +13 -0
  46. package/dist/containers/Tenant/Diagnostics/{OverloadedShards → Partitions}/i18n/index.ts +1 -1
  47. package/dist/containers/Tenant/Diagnostics/Partitions/i18n/ru.json +13 -0
  48. package/dist/containers/Tenant/Diagnostics/Partitions/index.ts +1 -0
  49. package/dist/containers/Tenant/Diagnostics/Partitions/utils/constants.ts +74 -0
  50. package/dist/containers/Tenant/Diagnostics/Partitions/utils/types.ts +6 -0
  51. package/dist/containers/Tenant/Diagnostics/TopShards/Filters/Filters.scss +8 -0
  52. package/dist/containers/Tenant/Diagnostics/TopShards/Filters/Filters.tsx +56 -0
  53. package/dist/containers/Tenant/Diagnostics/TopShards/Filters/index.ts +1 -0
  54. package/dist/containers/Tenant/Diagnostics/{OverloadedShards/OverloadedShards.scss → TopShards/TopShards.scss} +2 -10
  55. package/dist/containers/Tenant/Diagnostics/{OverloadedShards/OverloadedShards.tsx → TopShards/TopShards.tsx} +61 -31
  56. package/dist/containers/Tenant/Diagnostics/TopShards/i18n/en.json +6 -0
  57. package/dist/containers/Tenant/Diagnostics/TopShards/i18n/index.ts +11 -0
  58. package/dist/containers/Tenant/Diagnostics/TopShards/i18n/ru.json +6 -0
  59. package/dist/containers/Tenant/Diagnostics/TopShards/index.ts +1 -0
  60. package/dist/containers/Tenant/utils/schema.ts +1 -16
  61. package/dist/services/api.d.ts +4 -0
  62. package/dist/services/api.js +22 -6
  63. package/dist/store/reducers/authentication.js +0 -15
  64. package/dist/store/reducers/consumer.ts +160 -0
  65. package/dist/store/reducers/index.ts +2 -0
  66. package/dist/store/reducers/settings.js +2 -0
  67. package/dist/store/reducers/shardsWorkload.ts +28 -2
  68. package/dist/store/reducers/topic.ts +82 -2
  69. package/dist/store/state-url-mapping.js +3 -0
  70. package/dist/types/store/consumer.ts +55 -0
  71. package/dist/types/store/shardsWorkload.ts +6 -0
  72. package/dist/types/store/topic.ts +23 -6
  73. package/dist/utils/bytesParsers/convertBytesObjectToSpeed.ts +24 -0
  74. package/dist/utils/bytesParsers/formatBytesCustom.ts +57 -0
  75. package/dist/utils/bytesParsers/i18n/en.json +7 -0
  76. package/dist/utils/bytesParsers/i18n/index.ts +11 -0
  77. package/dist/utils/bytesParsers/i18n/ru.json +7 -0
  78. package/dist/utils/bytesParsers/index.ts +2 -0
  79. package/dist/utils/constants.ts +3 -0
  80. package/dist/utils/index.js +6 -0
  81. package/dist/utils/storage.ts +2 -2
  82. package/dist/utils/timeParsers/index.ts +2 -1
  83. package/dist/utils/timeParsers/parsers.ts +18 -0
  84. package/dist/utils/timeParsers/{protobuf.ts → protobufParsers.ts} +0 -0
  85. package/dist/utils/typecheckers.ts +5 -0
  86. package/dist/utils/utils.js +3 -3
  87. package/package.json +2 -2
  88. package/dist/containers/Tenant/Diagnostics/OverloadedShards/i18n/en.json +0 -4
  89. package/dist/containers/Tenant/Diagnostics/OverloadedShards/i18n/ru.json +0 -4
  90. package/dist/containers/Tenant/Diagnostics/OverloadedShards/index.ts +0 -1
@@ -0,0 +1,246 @@
1
+ import DataTable, {Column} from '@gravity-ui/react-data-table';
2
+ import block from 'bem-cn-lite';
3
+
4
+ import {SpeedMultiMeter} from '../../../../../components/SpeedMultiMeter';
5
+ import EntityStatus from '../../../../../components/EntityStatus/EntityStatus';
6
+ import {getDefaultNodePath} from '../../../../Node/NodePages';
7
+ import {formatBytes, formatMsToUptime} from '../../../../../utils';
8
+ import {isNumeric} from '../../../../../utils/utils';
9
+
10
+ import {
11
+ PARTITIONS_COLUMNS_IDS,
12
+ PARTITIONS_COLUMNS_TITILES,
13
+ PARTITIONS_READ_LAGS_SUB_COLUMNS_IDS,
14
+ PARTITIONS_READ_LAGS_SUB_COLUMNS_TITLES,
15
+ PARTITIONS_WRITE_LAGS_SUB_COLUMNS_IDS,
16
+ PARTITIONS_WRITE_LAGS_SUB_COLUMNS_TITLES,
17
+ } from '../utils/constants';
18
+ import type {IPreparedPartitionDataWithHosts} from '../utils/types';
19
+
20
+ import {
21
+ MultilineHeader,
22
+ ReadLagsHeader,
23
+ ReadSessionHeader,
24
+ UncommitedMessagesHeader,
25
+ UnreadMessagesHeader,
26
+ WriteLagsHeader,
27
+ } from '../Headers';
28
+
29
+ import './Columns.scss';
30
+
31
+ const b = block('ydb-diagnostics-partitions-columns');
32
+
33
+ export const columns: Column<IPreparedPartitionDataWithHosts>[] = [
34
+ {
35
+ name: PARTITIONS_COLUMNS_IDS.PARTITION_ID,
36
+ header: (
37
+ <MultilineHeader
38
+ title={PARTITIONS_COLUMNS_TITILES[PARTITIONS_COLUMNS_IDS.PARTITION_ID]}
39
+ />
40
+ ),
41
+ sortAccessor: (row) => isNumeric(row.partitionId) && Number(row.partitionId),
42
+ align: DataTable.LEFT,
43
+ render: ({row}) => row.partitionId,
44
+ },
45
+ {
46
+ name: PARTITIONS_COLUMNS_IDS.STORE_SIZE,
47
+ header: (
48
+ <MultilineHeader
49
+ title={PARTITIONS_COLUMNS_TITILES[PARTITIONS_COLUMNS_IDS.STORE_SIZE]}
50
+ />
51
+ ),
52
+ align: DataTable.RIGHT,
53
+ render: ({row}) => formatBytes(row.storeSize),
54
+ },
55
+ {
56
+ name: PARTITIONS_COLUMNS_IDS.WRITE_SPEED,
57
+ header: PARTITIONS_COLUMNS_TITILES[PARTITIONS_COLUMNS_IDS.WRITE_SPEED],
58
+ align: DataTable.LEFT,
59
+ sortAccessor: (row) => row.writeSpeed.perMinute,
60
+ render: ({row}) => <SpeedMultiMeter data={row.writeSpeed} />,
61
+ },
62
+ {
63
+ name: PARTITIONS_COLUMNS_IDS.READ_SPEED,
64
+ header: PARTITIONS_COLUMNS_TITILES[PARTITIONS_COLUMNS_IDS.READ_SPEED],
65
+ align: DataTable.LEFT,
66
+ sortAccessor: (row) => row.readSpeed.perMinute,
67
+ render: ({row}) => <SpeedMultiMeter data={row.readSpeed} />,
68
+ },
69
+ {
70
+ name: PARTITIONS_COLUMNS_IDS.WRITE_LAGS,
71
+ header: <WriteLagsHeader />,
72
+ className: b('lags-header'),
73
+ sub: [
74
+ {
75
+ name: PARTITIONS_WRITE_LAGS_SUB_COLUMNS_IDS.PARTITION_WRITE_LAG,
76
+ header: PARTITIONS_WRITE_LAGS_SUB_COLUMNS_TITLES[
77
+ PARTITIONS_WRITE_LAGS_SUB_COLUMNS_IDS.PARTITION_WRITE_LAG
78
+ ],
79
+ align: DataTable.RIGHT,
80
+ render: ({row}) => formatMsToUptime(row.partitionWriteLag),
81
+ },
82
+ {
83
+ name: PARTITIONS_WRITE_LAGS_SUB_COLUMNS_IDS.PARTITION_WRITE_IDLE_TIME,
84
+ header: PARTITIONS_WRITE_LAGS_SUB_COLUMNS_TITLES[
85
+ PARTITIONS_WRITE_LAGS_SUB_COLUMNS_IDS.PARTITION_WRITE_IDLE_TIME
86
+ ],
87
+ align: DataTable.RIGHT,
88
+ render: ({row}) => formatMsToUptime(row.partitionWriteIdleTime),
89
+ },
90
+ ],
91
+ },
92
+ {
93
+ name: PARTITIONS_COLUMNS_IDS.READ_LAGS,
94
+ header: <ReadLagsHeader />,
95
+ className: b('lags-header'),
96
+ sub: [
97
+ {
98
+ name: PARTITIONS_READ_LAGS_SUB_COLUMNS_IDS.CONSUMER_WRITE_LAG,
99
+ header: PARTITIONS_READ_LAGS_SUB_COLUMNS_TITLES[
100
+ PARTITIONS_READ_LAGS_SUB_COLUMNS_IDS.CONSUMER_WRITE_LAG
101
+ ],
102
+ align: DataTable.RIGHT,
103
+ render: ({row}) => formatMsToUptime(row.consumerWriteLag),
104
+ },
105
+ {
106
+ name: PARTITIONS_READ_LAGS_SUB_COLUMNS_IDS.CONSUMER_READ_LAG,
107
+ header: PARTITIONS_READ_LAGS_SUB_COLUMNS_TITLES[
108
+ PARTITIONS_READ_LAGS_SUB_COLUMNS_IDS.CONSUMER_READ_LAG
109
+ ],
110
+ align: DataTable.RIGHT,
111
+ render: ({row}) => formatMsToUptime(row.consumerReadLag),
112
+ },
113
+ {
114
+ name: PARTITIONS_READ_LAGS_SUB_COLUMNS_IDS.CONSUMER_READ_IDLE_TIME,
115
+ header: PARTITIONS_READ_LAGS_SUB_COLUMNS_TITLES[
116
+ PARTITIONS_READ_LAGS_SUB_COLUMNS_IDS.CONSUMER_READ_IDLE_TIME
117
+ ],
118
+ align: DataTable.RIGHT,
119
+ render: ({row}) => formatMsToUptime(row.consumerReadIdleTime),
120
+ },
121
+ ],
122
+ },
123
+ {
124
+ name: PARTITIONS_COLUMNS_IDS.UNCOMMITED_MESSAGES,
125
+ header: <UncommitedMessagesHeader />,
126
+ align: DataTable.RIGHT,
127
+ render: ({row}) => row.uncommitedMessages,
128
+ },
129
+ {
130
+ name: PARTITIONS_COLUMNS_IDS.UNREAD_MESSAGES,
131
+ header: <UnreadMessagesHeader />,
132
+ align: DataTable.RIGHT,
133
+ render: ({row}) => row.unreadMessages,
134
+ },
135
+ {
136
+ name: PARTITIONS_COLUMNS_IDS.START_OFFSET,
137
+ header: (
138
+ <MultilineHeader
139
+ title={PARTITIONS_COLUMNS_TITILES[PARTITIONS_COLUMNS_IDS.START_OFFSET]}
140
+ />
141
+ ),
142
+ sortAccessor: (row) => isNumeric(row.startOffset) && Number(row.startOffset),
143
+ align: DataTable.RIGHT,
144
+ render: ({row}) => row.startOffset,
145
+ },
146
+ {
147
+ name: PARTITIONS_COLUMNS_IDS.END_OFFSET,
148
+ header: (
149
+ <MultilineHeader
150
+ title={PARTITIONS_COLUMNS_TITILES[PARTITIONS_COLUMNS_IDS.END_OFFSET]}
151
+ />
152
+ ),
153
+ sortAccessor: (row) => isNumeric(row.endOffset) && Number(row.endOffset),
154
+ align: DataTable.RIGHT,
155
+ render: ({row}) => row.endOffset,
156
+ },
157
+ {
158
+ name: PARTITIONS_COLUMNS_IDS.COMMITED_OFFSET,
159
+ header: (
160
+ <MultilineHeader
161
+ title={PARTITIONS_COLUMNS_TITILES[PARTITIONS_COLUMNS_IDS.COMMITED_OFFSET]}
162
+ />
163
+ ),
164
+ sortAccessor: (row) => isNumeric(row.commitedOffset) && Number(row.commitedOffset),
165
+ align: DataTable.RIGHT,
166
+ render: ({row}) => row.commitedOffset,
167
+ },
168
+ {
169
+ name: PARTITIONS_COLUMNS_IDS.READ_SESSION_ID,
170
+ header: <ReadSessionHeader />,
171
+ align: DataTable.LEFT,
172
+ render: ({row}) =>
173
+ row.readSessionId ? (
174
+ <EntityStatus
175
+ name={row.readSessionId}
176
+ showStatus={false}
177
+ hasClipboardButton
178
+ className={b('string-with-copy')}
179
+ />
180
+ ) : (
181
+ '–'
182
+ ),
183
+ },
184
+ {
185
+ name: PARTITIONS_COLUMNS_IDS.READER_NAME,
186
+ header: (
187
+ <MultilineHeader
188
+ title={PARTITIONS_COLUMNS_TITILES[PARTITIONS_COLUMNS_IDS.READER_NAME]}
189
+ />
190
+ ),
191
+ align: DataTable.LEFT,
192
+ render: ({row}) =>
193
+ row.readerName ? (
194
+ <EntityStatus
195
+ name={row.readerName}
196
+ showStatus={false}
197
+ hasClipboardButton
198
+ className={b('string-with-copy')}
199
+ />
200
+ ) : (
201
+ '–'
202
+ ),
203
+ },
204
+ {
205
+ name: PARTITIONS_COLUMNS_IDS.PARTITION_HOST,
206
+ header: (
207
+ <MultilineHeader
208
+ title={PARTITIONS_COLUMNS_TITILES[PARTITIONS_COLUMNS_IDS.PARTITION_HOST]}
209
+ />
210
+ ),
211
+ align: DataTable.LEFT,
212
+ render: ({row}) =>
213
+ row.partitionHost ? (
214
+ <EntityStatus
215
+ name={row.partitionHost}
216
+ path={getDefaultNodePath(row.partitionHost)}
217
+ showStatus={false}
218
+ hasClipboardButton
219
+ className={b('string-with-copy')}
220
+ />
221
+ ) : (
222
+ '–'
223
+ ),
224
+ },
225
+ {
226
+ name: PARTITIONS_COLUMNS_IDS.CONNECTION_HOST,
227
+ header: (
228
+ <MultilineHeader
229
+ title={PARTITIONS_COLUMNS_TITILES[PARTITIONS_COLUMNS_IDS.CONNECTION_HOST]}
230
+ />
231
+ ),
232
+ align: DataTable.LEFT,
233
+ render: ({row}) =>
234
+ row.connectionHost ? (
235
+ <EntityStatus
236
+ name={row.connectionHost}
237
+ path={getDefaultNodePath(row.connectionNodeId)}
238
+ showStatus={false}
239
+ hasClipboardButton
240
+ className={b('string-with-copy')}
241
+ />
242
+ ) : (
243
+ '–'
244
+ ),
245
+ },
246
+ ];
@@ -0,0 +1 @@
1
+ export * from './columns';
@@ -0,0 +1,13 @@
1
+ {
2
+ "lagsPopover.writeLags": "Write lags statistics (time format dd hh:mm:ss)",
3
+ "lagsPopover.readLags": "Read lags statistics (time format dd hh:mm:ss)",
4
+ "headers.unread": "End offset - Last read offset",
5
+ "headers.uncommited": "End offset - Committed offset",
6
+ "controls.consumerSelector": "Consumer:",
7
+ "controls.consumerSelector.placeholder": "Consumer",
8
+ "controls.partitionSearch": "Partition ID",
9
+ "controls.generalSearch": "Host, Host ID, Reader, Read Session ID",
10
+ "table.emptyDataMessage": "No partitions match the current search",
11
+ "noConsumersMessage.topic": "This topic has no consumers",
12
+ "noConsumersMessage.stream": "This changefeed has no consumers"
13
+ }
@@ -3,7 +3,7 @@ import {i18n, Lang} from '../../../../../utils/i18n';
3
3
  import en from './en.json';
4
4
  import ru from './ru.json';
5
5
 
6
- const COMPONENT = 'ydb-diagnostics-overloaded-shards';
6
+ const COMPONENT = 'ydb-diagnostics-partitions';
7
7
 
8
8
  i18n.registerKeyset(Lang.En, COMPONENT, en);
9
9
  i18n.registerKeyset(Lang.Ru, COMPONENT, ru);
@@ -0,0 +1,13 @@
1
+ {
2
+ "lagsPopover.writeLags": "Статистика лагов записи (формат времени дд чч:мм:сс)",
3
+ "lagsPopover.readLags": "Статистика лагов чтения (формат времени дд чч:мм:сс)",
4
+ "headers.unread": "End offset - Last read offset",
5
+ "headers.uncommited": "End offset - Committed offset",
6
+ "controls.consumerSelector": "Читатель:",
7
+ "controls.consumerSelector.placeholder": "Читатель",
8
+ "controls.partitionSearch": "Partition ID",
9
+ "controls.generalSearch": "Host, Host ID, Reader, Read Session ID",
10
+ "table.emptyDataMessage": "По заданному поиску нет партиций",
11
+ "noConsumersMessage.topic": "У этого топика нет читателей",
12
+ "noConsumersMessage.stream": "У этого стрима нет читателей"
13
+ }
@@ -0,0 +1 @@
1
+ export * from './PartitionsWrapper';
@@ -0,0 +1,74 @@
1
+ export const PARTITIONS_COLUMNS_IDS = {
2
+ PARTITION_ID: 'partitionId',
3
+
4
+ STORE_SIZE: 'storeSize',
5
+
6
+ WRITE_SPEED: 'writeSpeed',
7
+ READ_SPEED: 'readSpeed',
8
+
9
+ WRITE_LAGS: 'writeLags',
10
+ READ_LAGS: 'readLags',
11
+
12
+ UNCOMMITED_MESSAGES: 'uncommitedMessages',
13
+ UNREAD_MESSAGES: 'unreadMessages',
14
+
15
+ START_OFFSET: 'startOffset',
16
+ END_OFFSET: 'endOffset',
17
+ COMMITED_OFFSET: 'commitedOffset',
18
+
19
+ READ_SESSION_ID: 'readSessionId',
20
+ READER_NAME: 'readerName',
21
+
22
+ PARTITION_HOST: 'partitionHost',
23
+ CONNECTION_HOST: 'connectionHost',
24
+ } as const;
25
+
26
+ export const PARTITIONS_COLUMNS_TITILES = {
27
+ [PARTITIONS_COLUMNS_IDS.PARTITION_ID]: 'Partition ID',
28
+
29
+ [PARTITIONS_COLUMNS_IDS.STORE_SIZE]: 'Store size',
30
+
31
+ [PARTITIONS_COLUMNS_IDS.WRITE_SPEED]: 'Write speed',
32
+ [PARTITIONS_COLUMNS_IDS.READ_SPEED]: 'Read speed',
33
+
34
+ [PARTITIONS_COLUMNS_IDS.WRITE_LAGS]: 'Write lags, duration',
35
+
36
+ [PARTITIONS_COLUMNS_IDS.READ_LAGS]: 'Read lags, duration',
37
+
38
+ [PARTITIONS_COLUMNS_IDS.UNCOMMITED_MESSAGES]: 'Uncommited messages',
39
+ [PARTITIONS_COLUMNS_IDS.UNREAD_MESSAGES]: 'Unread messages',
40
+
41
+ [PARTITIONS_COLUMNS_IDS.START_OFFSET]: 'Start offset',
42
+ [PARTITIONS_COLUMNS_IDS.END_OFFSET]: 'End offset',
43
+ [PARTITIONS_COLUMNS_IDS.COMMITED_OFFSET]: 'Commited offset',
44
+
45
+ [PARTITIONS_COLUMNS_IDS.READ_SESSION_ID]: 'Read session ID',
46
+ [PARTITIONS_COLUMNS_IDS.READER_NAME]: 'Reader name',
47
+
48
+ [PARTITIONS_COLUMNS_IDS.PARTITION_HOST]: 'Partition host',
49
+ [PARTITIONS_COLUMNS_IDS.CONNECTION_HOST]: 'Connection host',
50
+ } as const;
51
+
52
+ export const PARTITIONS_WRITE_LAGS_SUB_COLUMNS_IDS = {
53
+ PARTITION_WRITE_LAG: 'partitionWriteLag',
54
+ PARTITION_WRITE_IDLE_TIME: 'partitionWriteIdleTime',
55
+ } as const;
56
+
57
+ export const PARTITIONS_WRITE_LAGS_SUB_COLUMNS_TITLES = {
58
+ [PARTITIONS_WRITE_LAGS_SUB_COLUMNS_IDS.PARTITION_WRITE_LAG]: 'write lag',
59
+ [PARTITIONS_WRITE_LAGS_SUB_COLUMNS_IDS.PARTITION_WRITE_IDLE_TIME]: 'write idle time',
60
+ } as const;
61
+
62
+ export const PARTITIONS_READ_LAGS_SUB_COLUMNS_IDS = {
63
+ CONSUMER_WRITE_LAG: 'consumerWriteLag',
64
+ CONSUMER_READ_LAG: 'consumerReadLag',
65
+ CONSUMER_READ_IDLE_TIME: 'consumerReadIdleTime',
66
+ } as const;
67
+
68
+ export const PARTITIONS_READ_LAGS_SUB_COLUMNS_TITLES = {
69
+ [PARTITIONS_READ_LAGS_SUB_COLUMNS_IDS.CONSUMER_WRITE_LAG]: 'write lag',
70
+ [PARTITIONS_READ_LAGS_SUB_COLUMNS_IDS.CONSUMER_READ_LAG]: 'read lag',
71
+ [PARTITIONS_READ_LAGS_SUB_COLUMNS_IDS.CONSUMER_READ_IDLE_TIME]: 'read idle time',
72
+ } as const;
73
+
74
+ export const PARTITIONS_DEFAULT_SELECTED_COLUMNS = Object.values(PARTITIONS_COLUMNS_IDS);
@@ -0,0 +1,6 @@
1
+ import type {IPreparedPartitionData} from '../../../../../types/store/consumer';
2
+
3
+ export interface IPreparedPartitionDataWithHosts extends IPreparedPartitionData {
4
+ partitionHost: string | undefined;
5
+ connectionHost: string | undefined;
6
+ }
@@ -0,0 +1,8 @@
1
+ .top-shards {
2
+ &__filters {
3
+ display: flex;
4
+ flex-wrap: wrap;
5
+ align-items: baseline;
6
+ gap: 16px;
7
+ }
8
+ }
@@ -0,0 +1,56 @@
1
+ import {RadioButton} from '@gravity-ui/uikit';
2
+
3
+ import {DateRange, DateRangeValues} from '../../../../../components/DateRange';
4
+
5
+ import {
6
+ EShardsWorkloadMode,
7
+ IShardsWorkloadFilters,
8
+ } from '../../../../../types/store/shardsWorkload';
9
+
10
+ import {isEnumMember} from '../../../../../utils/typecheckers';
11
+
12
+ import i18n from '../i18n';
13
+ import {b} from '../TopShards';
14
+
15
+ import './Filters.scss';
16
+
17
+ interface FiltersProps {
18
+ value: IShardsWorkloadFilters;
19
+ onChange: (value: Partial<IShardsWorkloadFilters>) => void;
20
+ className?: string;
21
+ }
22
+
23
+ export const Filters = ({value, onChange, className}: FiltersProps) => {
24
+ const handleModeChange = (mode: string) => {
25
+ if (!isEnumMember(EShardsWorkloadMode, mode)) {
26
+ const values = Object.values(EShardsWorkloadMode).join(', ');
27
+ throw new Error(`Unexpected TopShards mode "${mode}". Should be one of: ${values}`);
28
+ }
29
+
30
+ onChange({mode});
31
+ };
32
+
33
+ const handleDateRangeChange = (dateRange: DateRangeValues) => {
34
+ onChange({
35
+ mode: EShardsWorkloadMode.History,
36
+ ...dateRange,
37
+ });
38
+ };
39
+
40
+ const from = value.mode === EShardsWorkloadMode.Immediate ? undefined : value.from;
41
+ const to = value.mode === EShardsWorkloadMode.Immediate ? undefined : value.to;
42
+
43
+ return (
44
+ <div className={b('filters', className)}>
45
+ <RadioButton value={value.mode} onUpdate={handleModeChange}>
46
+ <RadioButton.Option value={EShardsWorkloadMode.Immediate}>
47
+ {i18n('filters.mode.immediate')}
48
+ </RadioButton.Option>
49
+ <RadioButton.Option value={EShardsWorkloadMode.History}>
50
+ {i18n('filters.mode.history')}
51
+ </RadioButton.Option>
52
+ </RadioButton>
53
+ <DateRange from={from} to={to} onChange={handleDateRangeChange} />
54
+ </div>
55
+ );
56
+ };
@@ -0,0 +1 @@
1
+ export * from './Filters';
@@ -1,6 +1,7 @@
1
- .overloaded-shards {
1
+ .top-shards {
2
2
  display: flex;
3
3
  flex-direction: column;
4
+ gap: 10px;
4
5
 
5
6
  height: 100%;
6
7
 
@@ -11,15 +12,6 @@
11
12
  justify-content: center;
12
13
  }
13
14
 
14
- &__controls {
15
- display: flex;
16
- flex-wrap: wrap;
17
- align-items: baseline;
18
- gap: 16px;
19
-
20
- margin-bottom: 10px;
21
- }
22
-
23
15
  &__table {
24
16
  overflow: auto;
25
17
  flex-grow: 1;
@@ -5,7 +5,6 @@ import cn from 'bem-cn-lite';
5
5
  import DataTable, {Column, Settings, SortOrder} from '@gravity-ui/react-data-table';
6
6
  import {Loader} from '@gravity-ui/uikit';
7
7
 
8
- import {DateRange, DateRangeValues} from '../../../../components/DateRange';
9
8
  import {InternalLink} from '../../../../components/InternalLink';
10
9
 
11
10
  import HistoryContext from '../../../../contexts/HistoryContext';
@@ -18,7 +17,7 @@ import {
18
17
  setShardsQueryFilters,
19
18
  } from '../../../../store/reducers/shardsWorkload';
20
19
  import {setCurrentSchemaPath, getSchema} from '../../../../store/reducers/schema';
21
- import type {IShardsWorkloadFilters} from '../../../../types/store/shardsWorkload';
20
+ import {EShardsWorkloadMode, IShardsWorkloadFilters} from '../../../../types/store/shardsWorkload';
22
21
 
23
22
  import type {EPathType} from '../../../../types/api/schema';
24
23
 
@@ -31,10 +30,12 @@ import {getDefaultNodePath} from '../../../Node/NodePages';
31
30
 
32
31
  import {isColumnEntityType} from '../../utils/schema';
33
32
 
33
+ import {Filters} from './Filters';
34
+
34
35
  import i18n from './i18n';
35
- import './OverloadedShards.scss';
36
+ import './TopShards.scss';
36
37
 
37
- const b = cn('overloaded-shards');
38
+ export const b = cn('top-shards');
38
39
  const bLink = cn('yc-link');
39
40
 
40
41
  const TABLE_SETTINGS: Settings = {
@@ -83,12 +84,18 @@ function dataTableToStringSortOrder(value: SortOrder | SortOrder[] = []) {
83
84
  return sortOrders.map(({columnId}) => columnId).join(',');
84
85
  }
85
86
 
86
- interface OverloadedShardsProps {
87
+ function fillDateRangeFor(value: IShardsWorkloadFilters) {
88
+ value.to = Date.now();
89
+ value.from = value.to - HOUR_IN_SECONDS * 1000;
90
+ return value;
91
+ }
92
+
93
+ interface TopShardsProps {
87
94
  tenantPath: string;
88
95
  type?: EPathType;
89
96
  }
90
97
 
91
- export const OverloadedShards = ({tenantPath, type}: OverloadedShardsProps) => {
98
+ export const TopShards = ({tenantPath, type}: TopShardsProps) => {
92
99
  const dispatch = useDispatch();
93
100
 
94
101
  const {autorefresh, currentSchemaPath} = useTypedSelector((state) => state.schema);
@@ -101,17 +108,20 @@ export const OverloadedShards = ({tenantPath, type}: OverloadedShardsProps) => {
101
108
  wasLoaded,
102
109
  } = useTypedSelector((state) => state.shardsWorkload);
103
110
 
104
- // default date range should be the last hour, but shouldn't propagate into URL until user interacts with the control
111
+ // default filters shouldn't propagate into URL until user interacts with the control
105
112
  // redux initial value can't be used, as it synchronizes with URL
106
113
  const [filters, setFilters] = useState<IShardsWorkloadFilters>(() => {
107
- if (!storeFilters?.from && !storeFilters?.to) {
108
- return {
109
- from: Date.now() - HOUR_IN_SECONDS * 1000,
110
- to: Date.now(),
111
- };
114
+ const defaultValue = {...storeFilters};
115
+
116
+ if (!defaultValue.mode) {
117
+ defaultValue.mode = EShardsWorkloadMode.Immediate;
118
+ }
119
+
120
+ if (!defaultValue.from && !defaultValue.to) {
121
+ fillDateRangeFor(defaultValue);
112
122
  }
113
123
 
114
- return storeFilters;
124
+ return defaultValue;
115
125
  });
116
126
 
117
127
  const [sortOrder, setSortOrder] = useState(tableColumnsNames.CPUCores);
@@ -144,18 +154,34 @@ export const OverloadedShards = ({tenantPath, type}: OverloadedShardsProps) => {
144
154
  const history = useContext(HistoryContext);
145
155
 
146
156
  const onSort = (newSortOrder?: SortOrder | SortOrder[]) => {
147
- // omit information about sort order to disable ASC order, only DESC makes sense for overloaded shards
157
+ // omit information about sort order to disable ASC order, only DESC makes sense for top shards
148
158
  // use a string (and not the DataTable default format) to prevent reference change,
149
159
  // which would cause an excess state change, to avoid repeating requests
150
160
  setSortOrder(dataTableToStringSortOrder(newSortOrder));
151
161
  };
152
162
 
153
- const handleDateRangeChange = (value: DateRangeValues) => {
163
+ const handleFiltersChange = (value: Partial<IShardsWorkloadFilters>) => {
164
+ const newStateValue = {...value};
165
+ const isDateRangePristine =
166
+ !storeFilters.from && !storeFilters.to && !value.from && !value.to;
167
+
168
+ if (isDateRangePristine) {
169
+ switch (value.mode) {
170
+ case EShardsWorkloadMode.Immediate:
171
+ newStateValue.from = newStateValue.to = undefined;
172
+ break;
173
+ case EShardsWorkloadMode.History:
174
+ // should default to the current datetime every time history mode activates
175
+ fillDateRangeFor(newStateValue);
176
+ break;
177
+ }
178
+ }
179
+
154
180
  dispatch(setShardsQueryFilters(value));
155
- setFilters(value);
181
+ setFilters((state) => ({...state, ...newStateValue}));
156
182
  };
157
183
 
158
- const tableColumns: Column<any>[] = useMemo(() => {
184
+ const tableColumns = useMemo(() => {
159
185
  const onSchemaClick = (schemaPath: string) => {
160
186
  return () => {
161
187
  dispatch(setCurrentSchemaPath(schemaPath));
@@ -164,7 +190,7 @@ export const OverloadedShards = ({tenantPath, type}: OverloadedShardsProps) => {
164
190
  };
165
191
  };
166
192
 
167
- return [
193
+ const columns: Column<any>[] = [
168
194
  {
169
195
  name: tableColumnsNames.Path,
170
196
  render: ({value: relativeNodePath}) => {
@@ -217,23 +243,29 @@ export const OverloadedShards = ({tenantPath, type}: OverloadedShardsProps) => {
217
243
  align: DataTable.RIGHT,
218
244
  sortable: false,
219
245
  },
220
- {
221
- name: tableColumnsNames.PeakTime,
222
- render: ({value}) => formatDateTime(new Date(value as string).valueOf()),
223
- sortable: false,
224
- },
225
246
  {
226
247
  name: tableColumnsNames.InFlightTxCount,
227
248
  render: ({value}) => formatNumber(value as number),
228
249
  align: DataTable.RIGHT,
229
250
  sortable: false,
230
251
  },
231
- {
252
+ ];
253
+
254
+ if (filters.mode === EShardsWorkloadMode.History) {
255
+ // after NodeId
256
+ columns.splice(5, 0, {
257
+ name: tableColumnsNames.PeakTime,
258
+ render: ({value}) => formatDateTime(new Date(value as string).valueOf()),
259
+ sortable: false,
260
+ });
261
+ columns.push({
232
262
  name: tableColumnsNames.IntervalEnd,
233
263
  render: ({value}) => formatDateTime(new Date(value as string).getTime()),
234
- }
235
- ];
236
- }, [dispatch, history, tenantPath]);
264
+ });
265
+ }
266
+
267
+ return columns;
268
+ }, [dispatch, filters.mode, history, tenantPath]);
237
269
 
238
270
  const renderLoader = () => {
239
271
  return (
@@ -272,10 +304,8 @@ export const OverloadedShards = ({tenantPath, type}: OverloadedShardsProps) => {
272
304
 
273
305
  return (
274
306
  <div className={b()}>
275
- <div className={b('controls')}>
276
- {i18n('description')}
277
- <DateRange from={filters.from} to={filters.to} onChange={handleDateRangeChange} />
278
- </div>
307
+ <Filters value={filters} onChange={handleFiltersChange} />
308
+ {filters.mode === EShardsWorkloadMode.History && <div>{i18n('description')}</div>}
279
309
  {renderContent()}
280
310
  </div>
281
311
  );
@@ -0,0 +1,6 @@
1
+ {
2
+ "no-data": "No data",
3
+ "filters.mode.immediate": "Immediate",
4
+ "filters.mode.history": "Historical",
5
+ "description": "Historical data only tracks shards with CPU load over 70%"
6
+ }
@@ -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-diagnostics-top-shards';
7
+
8
+ i18n.registerKeyset(Lang.En, COMPONENT, en);
9
+ i18n.registerKeyset(Lang.Ru, COMPONENT, ru);
10
+
11
+ export default i18n.keyset(COMPONENT);
@@ -0,0 +1,6 @@
1
+ {
2
+ "no-data": "Нет данных",
3
+ "filters.mode.immediate": "Мгновенные",
4
+ "filters.mode.history": "Исторические",
5
+ "description": "Исторические данные хранятся только о шардах с загрузкой CPU выше 70%"
6
+ }