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