ydb-embedded-ui 3.3.2 → 3.3.4

Sign up to get free protection for your applications and to get access to all the features.
Files changed (73) hide show
  1. package/CHANGELOG.md +27 -0
  2. package/dist/components/Errors/ResponseError/ResponseError.tsx +17 -0
  3. package/dist/components/Errors/ResponseError/index.ts +1 -0
  4. package/dist/components/Errors/i18n/en.json +2 -1
  5. package/dist/components/Errors/i18n/ru.json +2 -1
  6. package/dist/components/FullGroupViewer/FullGroupViewer.js +1 -1
  7. package/dist/components/InfoViewer/InfoViewer.scss +1 -1
  8. package/dist/components/InfoViewer/InfoViewer.tsx +29 -21
  9. package/dist/components/InfoViewer/formatters/index.ts +1 -0
  10. package/dist/components/InfoViewer/formatters/table.ts +40 -0
  11. package/dist/components/QueryExecutionStatus/QueryExecutionStatus.tsx +26 -8
  12. package/dist/components/QueryExecutionStatus/index.ts +1 -0
  13. package/dist/components/QueryResultTable/QueryResultTable.tsx +2 -2
  14. package/dist/containers/App/Content.js +12 -5
  15. package/dist/containers/AsideNavigation/AsideNavigation.tsx +10 -13
  16. package/dist/containers/Authentication/Authentication.scss +6 -0
  17. package/dist/containers/Authentication/Authentication.tsx +34 -15
  18. package/dist/containers/Node/NodeStructure/Pdisk.tsx +7 -10
  19. package/dist/containers/Nodes/Nodes.tsx +1 -1
  20. package/dist/containers/Nodes/getNodesColumns.tsx +4 -4
  21. package/dist/containers/Storage/PDisk/PDisk.tsx +25 -17
  22. package/dist/containers/Storage/PDisk/__tests__/colors.tsx +64 -1
  23. package/dist/containers/Storage/Storage.js +1 -1
  24. package/dist/containers/Storage/StorageGroups/StorageGroups.tsx +12 -3
  25. package/dist/containers/Storage/StorageNodes/StorageNodes.tsx +1 -1
  26. package/dist/containers/Storage/utils/index.ts +26 -10
  27. package/dist/containers/Tablet/Tablet.js +1 -1
  28. package/dist/containers/Tenant/Acl/Acl.js +1 -1
  29. package/dist/containers/Tenant/Diagnostics/Consumers/Consumers.tsx +2 -2
  30. package/dist/containers/Tenant/Diagnostics/Diagnostics.tsx +3 -3
  31. package/dist/containers/Tenant/Diagnostics/DiagnosticsPages.ts +7 -7
  32. package/dist/containers/Tenant/Diagnostics/HotKeys/HotKeys.js +1 -1
  33. package/dist/containers/Tenant/Diagnostics/Overview/Overview.tsx +10 -21
  34. package/dist/containers/Tenant/{Schema/SchemaInfoViewer/SchemaInfoViewer.scss → Diagnostics/Overview/TableInfo/TableInfo.scss} +8 -10
  35. package/dist/containers/Tenant/Diagnostics/Overview/TableInfo/TableInfo.tsx +71 -0
  36. package/dist/containers/Tenant/Diagnostics/Overview/TableInfo/i18n/en.json +5 -0
  37. package/dist/containers/Tenant/Diagnostics/Overview/TableInfo/i18n/index.ts +11 -0
  38. package/dist/containers/Tenant/Diagnostics/Overview/TableInfo/i18n/ru.json +5 -0
  39. package/dist/containers/Tenant/Diagnostics/Overview/TableInfo/index.ts +1 -0
  40. package/dist/containers/Tenant/Diagnostics/Overview/TableInfo/prepareTableInfo.ts +96 -0
  41. package/dist/containers/Tenant/Diagnostics/TopQueries/TopQueries.tsx +7 -1
  42. package/dist/containers/Tenant/Diagnostics/TopShards/Filters/Filters.scss +8 -0
  43. package/dist/containers/Tenant/Diagnostics/TopShards/Filters/Filters.tsx +56 -0
  44. package/dist/containers/Tenant/Diagnostics/TopShards/Filters/index.ts +1 -0
  45. package/dist/containers/Tenant/Diagnostics/{OverloadedShards/OverloadedShards.scss → TopShards/TopShards.scss} +2 -10
  46. package/dist/containers/Tenant/Diagnostics/{OverloadedShards/OverloadedShards.tsx → TopShards/TopShards.tsx} +64 -29
  47. package/dist/containers/Tenant/Diagnostics/TopShards/i18n/en.json +6 -0
  48. package/dist/containers/Tenant/Diagnostics/{OverloadedShards → TopShards}/i18n/index.ts +1 -1
  49. package/dist/containers/Tenant/Diagnostics/TopShards/i18n/ru.json +6 -0
  50. package/dist/containers/Tenant/Diagnostics/TopShards/index.ts +1 -0
  51. package/dist/containers/Tenant/ObjectSummary/ObjectSummary.tsx +3 -1
  52. package/dist/containers/Tenant/QueryEditor/QueryExplain/QueryExplain.js +16 -11
  53. package/dist/containers/Tenant/QueryEditor/QueryResult/QueryResult.js +37 -23
  54. package/dist/containers/Tenant/QueryEditor/QueryResult/QueryResult.scss +4 -0
  55. package/dist/containers/Tenant/Schema/SchemaViewer/SchemaViewer.js +1 -1
  56. package/dist/containers/Tenants/Tenants.js +4 -3
  57. package/dist/routes.ts +1 -0
  58. package/dist/services/api.js +4 -1
  59. package/dist/store/reducers/authentication.js +0 -15
  60. package/dist/store/reducers/shardsWorkload.ts +30 -3
  61. package/dist/store/reducers/storage.js +1 -1
  62. package/dist/store/state-url-mapping.js +3 -0
  63. package/dist/types/store/shardsWorkload.ts +6 -0
  64. package/dist/utils/constants.ts +1 -1
  65. package/dist/utils/index.js +3 -1
  66. package/dist/utils/prepareQueryExplain.ts +1 -1
  67. package/dist/utils/typecheckers.ts +5 -0
  68. package/package.json +5 -3
  69. package/dist/containers/Tenant/Diagnostics/OverloadedShards/i18n/en.json +0 -4
  70. package/dist/containers/Tenant/Diagnostics/OverloadedShards/i18n/ru.json +0 -4
  71. package/dist/containers/Tenant/Diagnostics/OverloadedShards/index.ts +0 -1
  72. package/dist/containers/Tenant/Diagnostics/Overview/Overview.scss +0 -13
  73. package/dist/containers/Tenant/Schema/SchemaInfoViewer/SchemaInfoViewer.js +0 -201
@@ -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;
@@ -2,10 +2,9 @@ import {useState, useContext, useEffect, useMemo} from 'react';
2
2
  import {useDispatch} from 'react-redux';
3
3
  import cn from 'bem-cn-lite';
4
4
 
5
- import DataTable, {Column, Settings, SortOrder} from '@yandex-cloud/react-data-table';
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 = {
@@ -53,6 +54,7 @@ const tableColumnsNames = {
53
54
  NodeId: 'NodeId',
54
55
  PeakTime: 'PeakTime',
55
56
  InFlightTxCount: 'InFlightTxCount',
57
+ IntervalEnd: 'IntervalEnd',
56
58
  };
57
59
 
58
60
  function prepareCPUWorkloadValue(value: string) {
@@ -82,12 +84,18 @@ function dataTableToStringSortOrder(value: SortOrder | SortOrder[] = []) {
82
84
  return sortOrders.map(({columnId}) => columnId).join(',');
83
85
  }
84
86
 
85
- 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 {
86
94
  tenantPath: string;
87
95
  type?: EPathType;
88
96
  }
89
97
 
90
- export const OverloadedShards = ({tenantPath, type}: OverloadedShardsProps) => {
98
+ export const TopShards = ({tenantPath, type}: TopShardsProps) => {
91
99
  const dispatch = useDispatch();
92
100
 
93
101
  const {autorefresh, currentSchemaPath} = useTypedSelector((state) => state.schema);
@@ -100,17 +108,20 @@ export const OverloadedShards = ({tenantPath, type}: OverloadedShardsProps) => {
100
108
  wasLoaded,
101
109
  } = useTypedSelector((state) => state.shardsWorkload);
102
110
 
103
- // 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
104
112
  // redux initial value can't be used, as it synchronizes with URL
105
113
  const [filters, setFilters] = useState<IShardsWorkloadFilters>(() => {
106
- if (!storeFilters?.from && !storeFilters?.to) {
107
- return {
108
- from: Date.now() - HOUR_IN_SECONDS * 1000,
109
- to: Date.now(),
110
- };
114
+ const defaultValue = {...storeFilters};
115
+
116
+ if (!defaultValue.mode) {
117
+ defaultValue.mode = EShardsWorkloadMode.Immediate;
111
118
  }
112
119
 
113
- return storeFilters;
120
+ if (!defaultValue.from && !defaultValue.to) {
121
+ fillDateRangeFor(defaultValue);
122
+ }
123
+
124
+ return defaultValue;
114
125
  });
115
126
 
116
127
  const [sortOrder, setSortOrder] = useState(tableColumnsNames.CPUCores);
@@ -143,18 +154,34 @@ export const OverloadedShards = ({tenantPath, type}: OverloadedShardsProps) => {
143
154
  const history = useContext(HistoryContext);
144
155
 
145
156
  const onSort = (newSortOrder?: SortOrder | SortOrder[]) => {
146
- // 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
147
158
  // use a string (and not the DataTable default format) to prevent reference change,
148
159
  // which would cause an excess state change, to avoid repeating requests
149
160
  setSortOrder(dataTableToStringSortOrder(newSortOrder));
150
161
  };
151
162
 
152
- 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
+
153
180
  dispatch(setShardsQueryFilters(value));
154
- setFilters(value);
181
+ setFilters((state) => ({...state, ...newStateValue}));
155
182
  };
156
183
 
157
- const tableColumns: Column<any>[] = useMemo(() => {
184
+ const tableColumns = useMemo(() => {
158
185
  const onSchemaClick = (schemaPath: string) => {
159
186
  return () => {
160
187
  dispatch(setCurrentSchemaPath(schemaPath));
@@ -163,7 +190,7 @@ export const OverloadedShards = ({tenantPath, type}: OverloadedShardsProps) => {
163
190
  };
164
191
  };
165
192
 
166
- return [
193
+ const columns: Column<any>[] = [
167
194
  {
168
195
  name: tableColumnsNames.Path,
169
196
  render: ({value: relativeNodePath}) => {
@@ -216,11 +243,6 @@ export const OverloadedShards = ({tenantPath, type}: OverloadedShardsProps) => {
216
243
  align: DataTable.RIGHT,
217
244
  sortable: false,
218
245
  },
219
- {
220
- name: tableColumnsNames.PeakTime,
221
- render: ({value}) => formatDateTime(new Date(value as string).valueOf()),
222
- sortable: false,
223
- },
224
246
  {
225
247
  name: tableColumnsNames.InFlightTxCount,
226
248
  render: ({value}) => formatNumber(value as number),
@@ -228,7 +250,22 @@ export const OverloadedShards = ({tenantPath, type}: OverloadedShardsProps) => {
228
250
  sortable: false,
229
251
  },
230
252
  ];
231
- }, [dispatch, history, tenantPath]);
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({
262
+ name: tableColumnsNames.IntervalEnd,
263
+ render: ({value}) => formatDateTime(new Date(value as string).getTime()),
264
+ });
265
+ }
266
+
267
+ return columns;
268
+ }, [dispatch, filters.mode, history, tenantPath]);
232
269
 
233
270
  const renderLoader = () => {
234
271
  return (
@@ -267,10 +304,8 @@ export const OverloadedShards = ({tenantPath, type}: OverloadedShardsProps) => {
267
304
 
268
305
  return (
269
306
  <div className={b()}>
270
- <div className={b('controls')}>
271
- {i18n('description')}
272
- <DateRange from={filters.from} to={filters.to} onChange={handleDateRangeChange} />
273
- </div>
307
+ <Filters value={filters} onChange={handleFiltersChange} />
308
+ {filters.mode === EShardsWorkloadMode.History && <div>{i18n('description')}</div>}
274
309
  {renderContent()}
275
310
  </div>
276
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
+ }
@@ -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-top-shards';
7
7
 
8
8
  i18n.registerKeyset(Lang.En, COMPONENT, en);
9
9
  i18n.registerKeyset(Lang.Ru, COMPONENT, ru);
@@ -0,0 +1,6 @@
1
+ {
2
+ "no-data": "Нет данных",
3
+ "filters.mode.immediate": "Мгновенные",
4
+ "filters.mode.history": "Исторические",
5
+ "description": "Исторические данные хранятся только о шардах с загрузкой CPU выше 70%"
6
+ }
@@ -0,0 +1 @@
1
+ export * from './TopShards';
@@ -26,6 +26,8 @@ import {
26
26
  TColumnTableDescription,
27
27
  TDirEntry,
28
28
  } from '../../../types/api/schema';
29
+
30
+ import {formatDateTime} from '../../../utils';
29
31
  import {isColumnEntityType, isIndexTable, isTableType} from '../utils/schema';
30
32
 
31
33
  import {
@@ -201,7 +203,7 @@ function ObjectSummary(props: ObjectSummaryProps) {
201
203
  const startTimeInMilliseconds = Number(currentSchemaData?.CreateStep);
202
204
  let createTime = '';
203
205
  if (startTimeInMilliseconds) {
204
- createTime = new Date(startTimeInMilliseconds).toUTCString();
206
+ createTime = formatDateTime(startTimeInMilliseconds);
205
207
  }
206
208
 
207
209
  component = <InfoViewer info={[{label: 'Create time', value: createTime}]} />;
@@ -1,28 +1,33 @@
1
1
  import React, {useEffect, useRef, useState} from 'react';
2
+ import {useDispatch, useSelector} from 'react-redux';
2
3
  import cn from 'bem-cn-lite';
3
4
  import MonacoEditor from 'react-monaco-editor';
4
- import {Loader, RadioButton} from '@gravity-ui/uikit';
5
5
  import JSONTree from 'react-json-inspector';
6
- import {LANGUAGE_S_EXPRESSION_ID} from '../../../../utils/monaco';
6
+ import 'react-json-inspector/json-inspector.css';
7
+
7
8
  import {
8
9
  TextOverflow,
9
10
  getYdbPlanNodeShape,
10
11
  getCompactTopology,
11
12
  getTopology,
12
- } from '@yandex-cloud/paranoid';
13
- import {renderExplainNode} from '../../../../utils';
14
- import {explainVersions} from '../../../../store/reducers/explainQuery';
15
- import QueryExecutionStatus from '../../../../components/QueryExecutionStatus/QueryExecutionStatus';
13
+ } from '@gravity-ui/paranoid';
14
+ import {Loader, RadioButton} from '@gravity-ui/uikit';
15
+
16
16
  import Divider from '../../../../components/Divider/Divider';
17
17
  import EnableFullscreenButton from '../../../../components/EnableFullscreenButton/EnableFullscreenButton';
18
- import {PaneVisibilityToggleButtons} from '../../utils/paneVisibilityToggleHelpers';
19
18
  import Fullscreen from '../../../../components/Fullscreen/Fullscreen';
19
+ import {QueryExecutionStatus} from '../../../../components/QueryExecutionStatus';
20
20
 
21
- import 'react-json-inspector/json-inspector.css';
22
- import './QueryExplain.scss';
23
- import {useDispatch, useSelector} from 'react-redux';
21
+ import {explainVersions} from '../../../../store/reducers/explainQuery';
24
22
  import {disableFullscreen} from '../../../../store/reducers/fullscreen';
25
23
 
24
+ import {renderExplainNode} from '../../../../utils';
25
+ import {LANGUAGE_S_EXPRESSION_ID} from '../../../../utils/monaco';
26
+
27
+ import {PaneVisibilityToggleButtons} from '../../utils/paneVisibilityToggleHelpers';
28
+
29
+ import './QueryExplain.scss';
30
+
26
31
  const b = cn('kv-query-explain');
27
32
 
28
33
  const EDITOR_OPTIONS = {
@@ -263,7 +268,7 @@ function QueryExplain(props) {
263
268
  {!props.loading && (
264
269
  <React.Fragment>
265
270
  <div className={b('controls-right')}>
266
- <QueryExecutionStatus hasError={Boolean(props.error)} />
271
+ <QueryExecutionStatus error={props.error} />
267
272
  {!props.error && (
268
273
  <React.Fragment>
269
274
  <Divider />
@@ -1,20 +1,26 @@
1
1
  import React, {useEffect, useState} from 'react';
2
2
  import {useDispatch, useSelector} from 'react-redux';
3
3
  import cn from 'bem-cn-lite';
4
- import {RadioButton} from '@gravity-ui/uikit';
5
4
  import JSONTree from 'react-json-inspector';
6
5
 
6
+ import {RadioButton} from '@gravity-ui/uikit';
7
+
7
8
  import CopyToClipboard from '../../../../components/CopyToClipboard/CopyToClipboard';
8
9
  import Divider from '../../../../components/Divider/Divider';
10
+ import EnableFullscreenButton from '../../../../components/EnableFullscreenButton/EnableFullscreenButton';
9
11
  import Fullscreen from '../../../../components/Fullscreen/Fullscreen';
12
+ import {QueryExecutionStatus} from '../../../../components/QueryExecutionStatus';
13
+
10
14
  import {disableFullscreen} from '../../../../store/reducers/fullscreen';
11
15
 
12
- import './QueryResult.scss';
16
+ import {prepareQueryError} from '../../../../utils/query';
17
+
13
18
  import {PaneVisibilityToggleButtons} from '../../utils/paneVisibilityToggleHelpers';
14
- import QueryExecutionStatus from '../../../../components/QueryExecutionStatus/QueryExecutionStatus';
15
- import EnableFullscreenButton from '../../../../components/EnableFullscreenButton/EnableFullscreenButton';
19
+
16
20
  import ResultIssues from '../Issues/Issues';
17
21
 
22
+ import './QueryResult.scss';
23
+
18
24
  const b = cn('kv-query-result');
19
25
 
20
26
  const resultOptionsIds = {
@@ -94,31 +100,39 @@ function QueryResult(props) {
94
100
  };
95
101
 
96
102
  const renderIssues = () => {
97
- const error = props.error?.data;
98
-
99
- const hasIssues = error?.issues && Array.isArray(error.issues);
100
-
101
- return hasIssues ? (
102
- <React.Fragment>
103
- <ResultIssues data={error} />
104
- {isFullscreen && (
105
- <Fullscreen>
106
- <div className={b('result', {fullscreen: true})}>
107
- <ResultIssues data={error} />
108
- </div>
109
- </Fullscreen>
110
- )}
111
- </React.Fragment>
112
- ) : (
113
- <span>{error?.data ?? error}</span>
114
- );
103
+ const error = props.error;
104
+
105
+ const hasIssues = error?.data?.issues && Array.isArray(error.data.issues);
106
+
107
+ if (hasIssues) {
108
+ return (
109
+ <React.Fragment>
110
+ <ResultIssues data={error.data} />
111
+ {isFullscreen && (
112
+ <Fullscreen>
113
+ <div className={b('result', {fullscreen: true})}>
114
+ <ResultIssues data={error.data} />
115
+ </div>
116
+ </Fullscreen>
117
+ )}
118
+ </React.Fragment>
119
+ )
120
+ }
121
+
122
+ if (error) {
123
+ return (
124
+ <div className={b('error')}>
125
+ {prepareQueryError(error)}
126
+ </div>
127
+ );
128
+ }
115
129
  };
116
130
 
117
131
  return (
118
132
  <React.Fragment>
119
133
  <div className={b('controls')}>
120
134
  <div className={b('controls-right')}>
121
- <QueryExecutionStatus hasError={Boolean(props.error)} />
135
+ <QueryExecutionStatus error={props.error} />
122
136
 
123
137
  {props.stats && !props.error && (
124
138
  <React.Fragment>
@@ -20,6 +20,10 @@
20
20
  }
21
21
  }
22
22
 
23
+ &__error {
24
+ padding: 15px 10px;
25
+ }
26
+
23
27
  &__controls {
24
28
  position: sticky;
25
29
  z-index: 2;
@@ -5,7 +5,7 @@ import cn from 'bem-cn-lite';
5
5
  import find from 'lodash/find';
6
6
 
7
7
  import Icon from '../../../../components/Icon/Icon';
8
- import DataTable from '@yandex-cloud/react-data-table';
8
+ import DataTable from '@gravity-ui/react-data-table';
9
9
  import {DEFAULT_TABLE_SETTINGS} from '../../../../utils/constants';
10
10
  import './SchemaViewer.scss';
11
11
 
@@ -5,7 +5,7 @@ import {connect} from 'react-redux';
5
5
  import _ from 'lodash';
6
6
  import {escapeRegExp} from 'lodash/fp';
7
7
 
8
- import DataTable from '@yandex-cloud/react-data-table';
8
+ import DataTable from '@gravity-ui/react-data-table';
9
9
  import {Loader, TextInput, Button} from '@gravity-ui/uikit';
10
10
 
11
11
  import EntityStatus from '../../components/EntityStatus/EntityStatus';
@@ -218,8 +218,9 @@ class Tenants extends React.Component {
218
218
  },
219
219
  accessor: ({Metrics = {}, CoresUsed}) => {
220
220
  if (!isNaN(Number(CoresUsed))) {
221
- const cores = Math.round(Number(CoresUsed) * 100) / 100;
222
- return cores || '—';
221
+ return Number(CoresUsed) * 100 > 1
222
+ ? formatCPU(Number(CoresUsed) * 1_000_000)
223
+ : '—';
223
224
  } else {
224
225
  return Number(Metrics.CPU) ? formatCPU(Number(Metrics.CPU)) : '—';
225
226
  }
package/dist/routes.ts CHANGED
@@ -16,6 +16,7 @@ const routes = {
16
16
  tablet: '/tablet/:id',
17
17
  tabletsFilters: '/tabletsFilters',
18
18
  clusterPage: '/clusters/:name',
19
+ auth: '/auth',
19
20
  };
20
21
 
21
22
  export const CLUSTER_PAGES = {
@@ -185,7 +185,10 @@ export class YdbEmbeddedAPI extends AxiosWrapper {
185
185
  timeout: 600000,
186
186
  },
187
187
  null,
188
- {concurrentId},
188
+ {
189
+ concurrentId,
190
+ timeout: 9 * 60 * 1000,
191
+ },
189
192
  );
190
193
  }
191
194
  getExplainQuery(query, database) {
@@ -34,21 +34,6 @@ const authentication = function (state = initialState, action) {
34
34
  }
35
35
  };
36
36
 
37
- export const setIsNotAuthenticated = () => {
38
- return (dispatch) => {
39
- dispatch({
40
- type: SET_UNAUTHENTICATED.SUCCESS,
41
- });
42
- };
43
- };
44
- export const setIsAuthenticated = () => {
45
- return (dispatch) => {
46
- dispatch({
47
- type: SET_AUTHENTICATED.SUCCESS,
48
- });
49
- };
50
- };
51
-
52
37
  export const authenticate = (user, password) => {
53
38
  return createApiRequest({
54
39
  request: window.api.authenticate(user, password),
@@ -6,6 +6,7 @@ import type {
6
6
  IShardsWorkloadFilters,
7
7
  IShardsWorkloadState,
8
8
  } from '../../types/store/shardsWorkload';
9
+ import {EShardsWorkloadMode} from '../../types/store/shardsWorkload';
9
10
 
10
11
  import {parseQueryAPIExecuteResponse} from '../../utils/query';
11
12
 
@@ -51,7 +52,7 @@ function getFiltersConditions(filters?: IShardsWorkloadFilters) {
51
52
  return conditions.join(' AND ');
52
53
  }
53
54
 
54
- function createShardQuery(
55
+ function createShardQueryHistorical(
55
56
  path: string,
56
57
  filters?: IShardsWorkloadFilters,
57
58
  sortOrder?: SortOrder[],
@@ -77,13 +78,36 @@ function createShardQuery(
77
78
  DataSize,
78
79
  NodeId,
79
80
  PeakTime,
80
- InFlightTxCount
81
+ InFlightTxCount,
82
+ IntervalEnd
81
83
  FROM \`.sys/top_partitions_one_hour\`
82
84
  WHERE ${where}
83
85
  ${orderBy}
84
86
  LIMIT 20`;
85
87
  }
86
88
 
89
+ function createShardQueryImmediate(path: string, sortOrder?: SortOrder[], tenantName?: string) {
90
+ const pathSelect = tenantName
91
+ ? `CAST(SUBSTRING(CAST(Path AS String), ${tenantName.length}) AS Utf8) AS Path`
92
+ : 'Path';
93
+
94
+ const orderBy = sortOrder ? `ORDER BY ${sortOrder.map(formatSortOrder).join(', ')}` : '';
95
+
96
+ return `SELECT
97
+ ${pathSelect},
98
+ TabletId,
99
+ CPUCores,
100
+ DataSize,
101
+ NodeId,
102
+ InFlightTxCount
103
+ FROM \`.sys/partition_stats\`
104
+ WHERE
105
+ Path='${path}'
106
+ OR Path LIKE '${path}/%'
107
+ ${orderBy}
108
+ LIMIT 20`;
109
+ }
110
+
87
111
  const queryAction = 'execute-scan';
88
112
 
89
113
  const shardsWorkload: Reducer<IShardsWorkloadState, IShardsWorkloadAction> = (
@@ -146,7 +170,10 @@ export const sendShardQuery = ({database, path = '', sortOrder, filters}: SendSh
146
170
  request: window.api.sendQuery(
147
171
  {
148
172
  schema: 'modern',
149
- query: createShardQuery(path, filters, sortOrder, database),
173
+ query:
174
+ filters?.mode === EShardsWorkloadMode.Immediate
175
+ ? createShardQueryImmediate(path, sortOrder, database)
176
+ : createShardQueryHistorical(path, filters, sortOrder, database),
150
177
  database,
151
178
  action: queryAction,
152
179
  },
@@ -420,7 +420,7 @@ export const getUsageFilterOptions = createSelector(getVisibleEntitiesList, (ent
420
420
  entities.forEach((entity) => {
421
421
  const usage = getUsage(entity, 5);
422
422
 
423
- if (!Object.hasOwn(items, usage)) {
423
+ if (!Object.prototype.hasOwnProperty.call(items, usage)) {
424
424
  items[usage] = 0;
425
425
  }
426
426
 
@@ -48,6 +48,9 @@ const paramSetup = {
48
48
  generalTab: {
49
49
  stateKey: 'tenant.diagnosticsTab',
50
50
  },
51
+ shardsMode: {
52
+ stateKey: 'shardsWorkload.filters.mode',
53
+ },
51
54
  shardsDateFrom: {
52
55
  stateKey: 'shardsWorkload.filters.from',
53
56
  type: 'number',