ydb-embedded-ui 3.3.2 → 3.3.4

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 (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',