ydb-embedded-ui 1.8.7 → 1.10.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (51) hide show
  1. package/CHANGELOG.md +56 -0
  2. package/dist/components/BasicNodeViewer/BasicNodeViewer.scss +43 -0
  3. package/dist/components/BasicNodeViewer/BasicNodeViewer.tsx +53 -0
  4. package/dist/components/BasicNodeViewer/index.ts +1 -0
  5. package/dist/components/EntityStatus/EntityStatus.js +15 -3
  6. package/dist/components/FullNodeViewer/FullNodeViewer.js +29 -48
  7. package/dist/components/FullNodeViewer/FullNodeViewer.scss +0 -45
  8. package/dist/components/IndexInfoViewer/IndexInfoViewer.tsx +52 -0
  9. package/dist/components/InfoViewer/index.ts +4 -0
  10. package/dist/components/InfoViewer/utils.ts +32 -0
  11. package/dist/components/ProgressViewer/ProgressViewer.js +1 -1
  12. package/dist/components/TabletsOverall/TabletsOverall.tsx +1 -1
  13. package/dist/containers/Node/Node.scss +5 -1
  14. package/dist/containers/Node/Node.tsx +7 -1
  15. package/dist/containers/Node/NodeOverview/NodeOverview.tsx +1 -3
  16. package/dist/containers/Node/NodeStructure/NodeStructure.scss +30 -1
  17. package/dist/containers/Node/NodeStructure/PDiskTitleBadge.tsx +25 -0
  18. package/dist/containers/Node/NodeStructure/Pdisk.tsx +24 -2
  19. package/dist/containers/Nodes/Nodes.js +1 -0
  20. package/dist/containers/ReduxTooltip/ReduxTooltip.js +1 -1
  21. package/dist/containers/Storage/Pdisk/Pdisk.tsx +25 -33
  22. package/dist/containers/Storage/Vdisk/Vdisk.js +2 -0
  23. package/dist/containers/Tablet/Tablet.js +2 -2
  24. package/dist/containers/Tablets/Tablets.js +1 -1
  25. package/dist/containers/Tablets/Tablets.scss +0 -6
  26. package/dist/containers/TabletsFilters/TabletsFilters.scss +0 -7
  27. package/dist/containers/Tenant/Diagnostics/DetailedOverview/DetailedOverview.scss +6 -2
  28. package/dist/containers/Tenant/Diagnostics/DiagnosticsPages.ts +24 -14
  29. package/dist/containers/Tenant/Diagnostics/Healthcheck/Healthcheck.js +6 -2
  30. package/dist/containers/Tenant/Diagnostics/Healthcheck/Healthcheck.scss +6 -2
  31. package/dist/containers/Tenant/Diagnostics/HotKeys/HotKeys.js +3 -3
  32. package/dist/containers/Tenant/Diagnostics/Overview/Overview.tsx +24 -3
  33. package/dist/containers/Tenant/Diagnostics/TenantOverview/TenantOverview.js +15 -13
  34. package/dist/containers/Tenant/Diagnostics/TenantOverview/TenantOverview.scss +22 -6
  35. package/dist/containers/Tenant/Diagnostics/TopShards/TopShards.js +80 -10
  36. package/dist/containers/Tenant/Schema/SchemaInfoViewer/SchemaInfoViewer.js +20 -16
  37. package/dist/containers/Tenant/Schema/SchemaTree/SchemaTree.tsx +1 -0
  38. package/dist/containers/Tenant/utils/schema.ts +73 -28
  39. package/dist/containers/Tenant/utils/schemaActions.ts +42 -32
  40. package/dist/services/api.js +13 -8
  41. package/dist/store/reducers/executeQuery.js +1 -1
  42. package/dist/store/reducers/executeTopQueries.js +1 -1
  43. package/dist/store/reducers/olapStats.js +5 -1
  44. package/dist/store/reducers/preview.js +1 -1
  45. package/dist/store/reducers/shardsWorkload.js +32 -4
  46. package/dist/types/api/schema.ts +43 -1
  47. package/dist/types/api/storage.ts +54 -0
  48. package/dist/utils/getNodesColumns.js +2 -0
  49. package/dist/utils/pdisk.ts +74 -0
  50. package/dist/utils/tooltip.js +27 -0
  51. package/package.json +2 -2
@@ -1,4 +1,4 @@
1
- import {useContext, useEffect, useMemo} from 'react';
1
+ import {useState, useContext, useEffect, useMemo} from 'react';
2
2
  import cn from 'bem-cn-lite';
3
3
  import {connect} from 'react-redux';
4
4
  import {Loader} from '@yandex-cloud/uikit';
@@ -14,15 +14,25 @@ import HistoryContext from '../../../../contexts/HistoryContext';
14
14
  import {DEFAULT_TABLE_SETTINGS} from '../../../../utils/constants';
15
15
  import {isColumnEntityType} from '../../utils/schema';
16
16
  import {prepareQueryError} from '../../../../utils';
17
+ import {i18n} from '../../../../utils/i18n';
17
18
 
18
19
  import './TopShards.scss';
19
20
 
20
21
  const b = cn('top-shards');
21
22
  const bLink = cn('yc-link');
22
23
 
24
+ const TABLE_SETTINGS = {
25
+ ...DEFAULT_TABLE_SETTINGS,
26
+ dynamicRender: false, // no more than 20 rows
27
+ externalSort: true,
28
+ disableSortReset: true,
29
+ defaultOrder: DataTable.DESCENDING,
30
+ };
31
+
23
32
  const tableColumnsNames = {
24
33
  TabletId: 'TabletId',
25
34
  CPUCores: 'CPUCores',
35
+ DataSize: 'DataSize',
26
36
  Path: 'Path',
27
37
  };
28
38
 
@@ -32,6 +42,28 @@ function prepareCPUWorkloadValue(value) {
32
42
  return `${(value * 100).toFixed(2)}%`;
33
43
  }
34
44
 
45
+ function prepareDateSizeValue(value) {
46
+ return new Intl.NumberFormat(i18n.lang).format(value);
47
+ }
48
+
49
+ function stringToDataTableSortOrder(value) {
50
+ return value && value.split(',').map((columnId) => ({
51
+ columnId,
52
+ order: DataTable.DESCENDING,
53
+ }));
54
+ }
55
+
56
+ function stringToQuerySortOrder(value) {
57
+ return value && value.split(',').map((columnId) => ({
58
+ columnId,
59
+ order: 'DESC',
60
+ }));
61
+ }
62
+
63
+ function dataTableToStringSortOrder(value = []) {
64
+ return value.map(({columnId}) => columnId).join(',');
65
+ }
66
+
35
67
  function TopShards({
36
68
  sendShardQuery,
37
69
  currentSchemaPath,
@@ -46,25 +78,40 @@ function TopShards({
46
78
  setShardQueryOptions,
47
79
  type,
48
80
  }) {
81
+ const [sortOrder, setSortOrder] = useState(tableColumnsNames.CPUCores);
82
+
49
83
  useEffect(() => {
84
+ autofetcher.stop();
85
+
50
86
  if (autorefresh) {
51
87
  autofetcher.start();
52
- autofetcher.fetch(() => sendShardQuery({database: path, path: currentSchemaPath}));
53
- } else {
54
- autofetcher.stop();
88
+ autofetcher.fetch(() => sendShardQuery({
89
+ database: path,
90
+ path: currentSchemaPath,
91
+ sortOrder: stringToQuerySortOrder(sortOrder),
92
+ }));
55
93
  }
94
+
56
95
  return () => {
57
96
  autofetcher.stop();
58
97
  };
59
- }, [autorefresh]);
98
+ }, [autorefresh, currentSchemaPath, path, sendShardQuery, sortOrder]);
60
99
 
100
+ // don't show loader for requests triggered by table sort, only for path change
61
101
  useEffect(() => {
62
- sendShardQuery({database: path, path: currentSchemaPath});
63
102
  setShardQueryOptions({
64
103
  wasLoaded: false,
65
104
  data: undefined,
66
105
  });
67
- }, [currentSchemaPath]);
106
+ }, [currentSchemaPath, path, setShardQueryOptions]);
107
+
108
+ useEffect(() => {
109
+ sendShardQuery({
110
+ database: path,
111
+ path: currentSchemaPath,
112
+ sortOrder: stringToQuerySortOrder(sortOrder),
113
+ });
114
+ }, [currentSchemaPath, path, sendShardQuery, sortOrder]);
68
115
 
69
116
  const history = useContext(HistoryContext);
70
117
 
@@ -76,6 +123,13 @@ function TopShards({
76
123
  };
77
124
  };
78
125
 
126
+ const onSort = (newSortOrder) => {
127
+ // omit information about sort order to disable ASC order, only DESC makes sense for top shards
128
+ // use a string (and not the DataTable default format) to prevent reference change,
129
+ // which would cause an excess state change, to avoid repeating requests
130
+ setSortOrder(dataTableToStringSortOrder(newSortOrder));
131
+ };
132
+
79
133
  const tableColumns = useMemo(() => {
80
134
  return [
81
135
  {
@@ -83,11 +137,16 @@ function TopShards({
83
137
  // eslint-disable-next-line
84
138
  render: ({value}) => {
85
139
  return (
86
- <span onClick={onSchemaClick(value)} className={bLink({view: 'normal'})}>
140
+ <span
141
+ // tenant name is substringed out in sql query but is needed here
142
+ onClick={onSchemaClick(path + value)}
143
+ className={bLink({view: 'normal'})}
144
+ >
87
145
  {value}
88
146
  </span>
89
147
  );
90
148
  },
149
+ sortable: false,
91
150
  },
92
151
  {
93
152
  name: tableColumnsNames.CPUCores,
@@ -97,6 +156,14 @@ function TopShards({
97
156
  },
98
157
  align: DataTable.RIGHT,
99
158
  },
159
+ {
160
+ name: tableColumnsNames.DataSize,
161
+ header: 'DataSize (B)',
162
+ render: ({value}) => {
163
+ return prepareDateSizeValue(value);
164
+ },
165
+ align: DataTable.RIGHT,
166
+ },
100
167
  {
101
168
  name: tableColumnsNames.TabletId,
102
169
  // eslint-disable-next-line
@@ -107,6 +174,7 @@ function TopShards({
107
174
  </InternalLink>
108
175
  );
109
176
  },
177
+ sortable: false,
110
178
  },
111
179
  ];
112
180
  }, []);
@@ -123,7 +191,7 @@ function TopShards({
123
191
  if (isColumnEntityType(type)) {
124
192
  return 'No data';
125
193
  }
126
- if (error) {
194
+ if (error && !error.isCancelled) {
127
195
  return prepareQueryError(error);
128
196
  }
129
197
 
@@ -132,9 +200,11 @@ function TopShards({
132
200
  <DataTable
133
201
  columns={tableColumns}
134
202
  data={data}
135
- settings={DEFAULT_TABLE_SETTINGS}
203
+ settings={TABLE_SETTINGS}
136
204
  className={b('table')}
137
205
  theme="yandex-cloud"
206
+ onSort={onSort}
207
+ sortOrder={stringToDataTableSortOrder(sortOrder)}
138
208
  />
139
209
  </div>
140
210
  ) : (
@@ -28,29 +28,33 @@ class SchemaInfoViewer extends React.Component {
28
28
  if (data) {
29
29
  const {PathDescription = {}} = data;
30
30
  const {TableStats = {}, TabletMetrics = {}} = PathDescription;
31
- const tableStatsInfo =
32
- TableStats &&
33
- Object.keys(TableStats).map((key) => ({
34
- label: key,
35
- value: TableStats[key].toString(),
36
- }));
31
+ const {PartCount, ...restTableStats} = TableStats;
37
32
 
38
- const tabletMetricsInfo =
39
- TableStats &&
40
- Object.keys(TabletMetrics).map((key) => ({
41
- label: key,
42
- value: this.formatTabletMetricsValue(key, TabletMetrics[key].toString()),
43
- }));
33
+ const priorityInfo = [{
34
+ label: 'PartCount',
35
+ value: PartCount,
36
+ }].filter(({value}) => value !== undefined);
44
37
 
45
- let generalInfo = Object.assign(tableStatsInfo, tabletMetricsInfo);
46
- generalInfo = Object.assign(generalInfo);
38
+ const tableStatsInfo = Object.keys(restTableStats).map((key) => ({
39
+ label: key,
40
+ value: TableStats[key].toString(),
41
+ }));
47
42
 
48
- const infoLength = Object.keys(generalInfo).length;
43
+ const tabletMetricsInfo = Object.keys(TabletMetrics).map((key) => ({
44
+ label: key,
45
+ value: this.formatTabletMetricsValue(key, TabletMetrics[key].toString()),
46
+ }));
49
47
 
48
+ const generalInfo = [
49
+ ...priorityInfo,
50
+ ...tabletMetricsInfo,
51
+ ...tableStatsInfo,
52
+ ];
53
+
50
54
  return (
51
55
  <div className={b()}>
52
56
  <div className={b('item')}>
53
- {infoLength ? (
57
+ {generalInfo.length ? (
54
58
  <InfoViewer info={generalInfo}></InfoViewer>
55
59
  ) : (
56
60
  <div>Empty</div>
@@ -61,6 +61,7 @@ export function SchemaTree(props: SchemaTreeProps) {
61
61
  activePath={currentPath}
62
62
  onActivePathUpdate={handleActivePathUpdate}
63
63
  cache={false}
64
+ virtualize
64
65
  />
65
66
  );
66
67
  }
@@ -1,43 +1,88 @@
1
1
  import type {NavigationTreeNodeType} from 'ydb-ui-components';
2
2
  import {EPathSubType, EPathType} from '../../../types/api/schema';
3
3
 
4
- const mapTablePathSubTypeToNavigationTreeType = (subType?: EPathSubType) => {
5
- switch (subType) {
6
- case EPathSubType.EPathSubTypeSyncIndexImplTable:
7
- case EPathSubType.EPathSubTypeAsyncIndexImplTable:
8
- return 'index_table';
9
- default:
10
- return 'table';
11
- }
4
+ // this file contains verbose mappings that are typed in a way that ensures
5
+ // correctness when a new node type or a new path type is added
6
+ // TS will error if a new entity is added but not mapped here
7
+
8
+ const pathSubTypeToNodeType: Record<EPathSubType, NavigationTreeNodeType | undefined> = {
9
+ [EPathSubType.EPathSubTypeSyncIndexImplTable]: 'index_table',
10
+ [EPathSubType.EPathSubTypeAsyncIndexImplTable]: 'index_table',
11
+
12
+ [EPathSubType.EPathSubTypeStreamImpl]: undefined,
13
+ [EPathSubType.EPathSubTypeEmpty]: undefined,
14
+ };
15
+
16
+ const pathTypeToNodeType: Record<EPathType, NavigationTreeNodeType | undefined> = {
17
+ [EPathType.EPathTypeInvalid]: undefined,
18
+
19
+ [EPathType.EPathTypeSubDomain]: 'database',
20
+ [EPathType.EPathTypeExtSubDomain]: 'database',
21
+
22
+ [EPathType.EPathTypeDir]: 'directory',
23
+ [EPathType.EPathTypeColumnStore]: 'directory',
24
+
25
+ [EPathType.EPathTypeTable]: 'table',
26
+
27
+ [EPathType.EPathTypeTableIndex]: 'index',
28
+
29
+ [EPathType.EPathTypeColumnTable]: 'column_table',
30
+
31
+ [EPathType.EPathTypeCdcStream]: 'topic',
12
32
  };
13
33
 
14
34
  export const mapPathTypeToNavigationTreeType = (
15
35
  type: EPathType = EPathType.EPathTypeDir,
16
36
  subType?: EPathSubType,
17
37
  defaultType: NavigationTreeNodeType = 'directory'
18
- ): NavigationTreeNodeType => {
19
- switch (type) {
20
- case EPathType.EPathTypeSubDomain:
21
- return 'database';
22
- case EPathType.EPathTypeTable:
23
- case EPathType.EPathTypeColumnTable:
24
- return mapTablePathSubTypeToNavigationTreeType(subType);
25
- case EPathType.EPathTypeDir:
26
- case EPathType.EPathTypeColumnStore:
27
- return 'directory';
28
- case EPathType.EPathTypeTableIndex:
29
- return 'index';
30
- default:
31
- return defaultType;
32
- }
38
+ ): NavigationTreeNodeType =>
39
+ (subType && pathSubTypeToNodeType[subType]) || pathTypeToNodeType[type] || defaultType;
40
+
41
+ // ====================
42
+
43
+ const pathTypeToIsTable: Record<EPathType, boolean> = {
44
+ [EPathType.EPathTypeTable]: true,
45
+ [EPathType.EPathTypeColumnTable]: true,
46
+
47
+ [EPathType.EPathTypeInvalid]: false,
48
+ [EPathType.EPathTypeDir]: false,
49
+ [EPathType.EPathTypeSubDomain]: false,
50
+ [EPathType.EPathTypeTableIndex]: false,
51
+ [EPathType.EPathTypeExtSubDomain]: false,
52
+ [EPathType.EPathTypeColumnStore]: false,
53
+ [EPathType.EPathTypeCdcStream]: false,
33
54
  };
34
55
 
35
- export const isTableType = (type?: EPathType) =>
36
- mapPathTypeToNavigationTreeType(type) === 'table';
56
+ export const isTableType = (pathType?: EPathType) =>
57
+ (pathType && pathTypeToIsTable[pathType]) ?? false;
58
+
59
+ // ====================
60
+
61
+ const pathSubTypeToIsIndexImpl: Record<EPathSubType, boolean> = {
62
+ [EPathSubType.EPathSubTypeSyncIndexImplTable]: true,
63
+ [EPathSubType.EPathSubTypeAsyncIndexImplTable]: true,
64
+
65
+ [EPathSubType.EPathSubTypeStreamImpl]: false,
66
+ [EPathSubType.EPathSubTypeEmpty]: false,
67
+ };
37
68
 
38
69
  export const isIndexTable = (subType?: EPathSubType) =>
39
- mapTablePathSubTypeToNavigationTreeType(subType) === 'index_table';
70
+ (subType && pathSubTypeToIsIndexImpl[subType]) ?? false;
71
+
72
+ // ====================
73
+
74
+ const pathTypeToIsColumn: Record<EPathType, boolean> = {
75
+ [EPathType.EPathTypeColumnStore]: true,
76
+ [EPathType.EPathTypeColumnTable]: true,
77
+
78
+ [EPathType.EPathTypeInvalid]: false,
79
+ [EPathType.EPathTypeDir]: false,
80
+ [EPathType.EPathTypeTable]: false,
81
+ [EPathType.EPathTypeSubDomain]: false,
82
+ [EPathType.EPathTypeTableIndex]: false,
83
+ [EPathType.EPathTypeExtSubDomain]: false,
84
+ [EPathType.EPathTypeCdcStream]: false,
85
+ };
40
86
 
41
87
  export const isColumnEntityType = (type?: EPathType) =>
42
- type === EPathType.EPathTypeColumnStore ||
43
- type === EPathType.EPathTypeColumnTable;
88
+ (type && pathTypeToIsColumn[type]) ?? false;
@@ -1,5 +1,5 @@
1
1
  import {Dispatch} from 'react';
2
- import type {NavigationTreeNodeType} from 'ydb-ui-components';
2
+ import type {NavigationTreeNodeType, NavigationTreeProps} from 'ydb-ui-components';
3
3
 
4
4
  import {changeUserInput} from '../../../store/reducers/executeQuery';
5
5
  import {setShowPreview} from '../../../store/reducers/schema';
@@ -73,6 +73,8 @@ const bindActions = (
73
73
  };
74
74
  };
75
75
 
76
+ type ActionsSet = ReturnType<Required<NavigationTreeProps>['getActions']>;
77
+
76
78
  export const getActions = (
77
79
  dispatch: Dispatch<any>,
78
80
  setActivePath: (path: string) => void,
@@ -81,35 +83,43 @@ export const getActions = (
81
83
  const actions = bindActions(path, dispatch, setActivePath);
82
84
  const copyItem = {text: 'Copy path', action: actions.copyPath};
83
85
 
84
- switch (type) {
85
- case 'database':
86
- case 'directory':
87
- return [
88
- [
89
- copyItem,
90
- ],
91
- [
92
- {text: 'Create table...', action: actions.createTable},
93
- ],
94
- ];
95
- case 'table':
96
- return [
97
- [
98
- {text: 'Open preview', action: actions.openPreview},
99
- copyItem,
100
- ],
101
- [
102
- {text: 'Alter table...', action: actions.alterTable},
103
- {text: 'Select query...', action: actions.selectQuery},
104
- {text: 'Upsert query...', action: actions.upsertQuery},
105
- ],
106
- ];
107
- case 'index_table':
108
- return [
109
- copyItem,
110
- ];
111
- case 'index':
112
- default:
113
- return [];
114
- }
86
+ const DIR_SET: ActionsSet = [
87
+ [
88
+ copyItem,
89
+ ],
90
+ [
91
+ {text: 'Create table...', action: actions.createTable},
92
+ ],
93
+ ];
94
+ const TABLE_SET: ActionsSet = [
95
+ [
96
+ {text: 'Open preview', action: actions.openPreview},
97
+ copyItem,
98
+ ],
99
+ [
100
+ {text: 'Alter table...', action: actions.alterTable},
101
+ {text: 'Select query...', action: actions.selectQuery},
102
+ {text: 'Upsert query...', action: actions.upsertQuery},
103
+ ],
104
+ ];
105
+
106
+ const JUST_COPY: ActionsSet = [
107
+ copyItem,
108
+ ];
109
+
110
+ const EMPTY_SET: ActionsSet = [];
111
+
112
+ // verbose mapping to guarantee a correct actions set for new node types
113
+ // TS will error when a new type is added in the lib but is not mapped here
114
+ const nodeTypeToActions: Record<NavigationTreeNodeType, ActionsSet> = {
115
+ database: DIR_SET,
116
+ directory: DIR_SET,
117
+ table: TABLE_SET,
118
+ column_table: TABLE_SET,
119
+ index_table: JUST_COPY,
120
+ index: EMPTY_SET,
121
+ topic: DIR_SET,
122
+ };
123
+
124
+ return nodeTypeToActions[type];
115
125
  };
@@ -147,14 +147,19 @@ export class YdbEmbeddedAPI extends AxiosWrapper {
147
147
  state: 0,
148
148
  });
149
149
  }
150
- sendQuery(query, database, action, stats) {
151
- return this.post(this.getPath('/viewer/json/query'), {
152
- query,
153
- database,
154
- action,
155
- stats,
156
- timeout: 600000,
157
- });
150
+ sendQuery({query, database, action, stats}, {concurrentId} = {}) {
151
+ return this.post(
152
+ this.getPath('/viewer/json/query'),
153
+ {
154
+ query,
155
+ database,
156
+ action,
157
+ stats,
158
+ timeout: 600000,
159
+ },
160
+ null,
161
+ {concurrentId},
162
+ );
158
163
  }
159
164
  getExplainQuery(query, database) {
160
165
  return this.post(this.getPath('/viewer/json/query'), {
@@ -141,7 +141,7 @@ const executeQuery = (state = initialState, action) => {
141
141
 
142
142
  export const sendQuery = ({query, database, action}) => {
143
143
  return createApiRequest({
144
- request: window.api.sendQuery(query, database, action, 'profile'),
144
+ request: window.api.sendQuery({query, database, action, stats: 'profile'}),
145
145
  actions: SEND_QUERY,
146
146
  dataHandler: (result) => {
147
147
  const resultData = result.result ?? result;
@@ -47,7 +47,7 @@ const executeTopQueries = (state = initialState, action) => {
47
47
 
48
48
  export const sendQuery = ({query, database, action}) => {
49
49
  return createApiRequest({
50
- request: window.api.sendQuery(query, database, action),
50
+ request: window.api.sendQuery({query, database, action}),
51
51
  actions: SEND_QUERY,
52
52
  dataHandler: (result) => {
53
53
  if (result && typeof result === 'string') {
@@ -52,7 +52,11 @@ const olapStats = (state = initialState, action) => {
52
52
 
53
53
  export const getOlapStats = ({path = ''}) => {
54
54
  return createApiRequest({
55
- request: window.api.sendQuery(createOlatStatsQuery(path), path, queryAction),
55
+ request: window.api.sendQuery({
56
+ query: createOlatStatsQuery(path),
57
+ database: path,
58
+ action: queryAction,
59
+ }),
56
60
  actions: FETCH_OLAP_STATS,
57
61
  dataHandler: (result) => {
58
62
  if (result && typeof result === 'string') {
@@ -47,7 +47,7 @@ const preview = (state = initialState, action) => {
47
47
 
48
48
  export const sendQuery = ({query, database, action}) => {
49
49
  return createApiRequest({
50
- request: window.api.sendQuery(query, database, action),
50
+ request: window.api.sendQuery({query, database, action}),
51
51
  actions: SEND_QUERY,
52
52
  dataHandler: (data) => {
53
53
  if (!Array.isArray(data)) {
@@ -9,8 +9,30 @@ const initialState = {
9
9
  wasLoaded: false,
10
10
  };
11
11
 
12
- function createShardQuery(path) {
13
- return `SELECT Path, TabletId, CPUCores FROM \`.sys/partition_stats\` WHERE Path='${path}' OR Path LIKE '${path}/%' ORDER BY CPUCores DESC LIMIT 20`;
12
+ function formatSortOrder({columnId, order}) {
13
+ return `${columnId} ${order}`;
14
+ }
15
+
16
+ function createShardQuery(path, sortOrder, tenantName) {
17
+ const orderBy = Array.isArray(sortOrder) ?
18
+ `ORDER BY ${sortOrder.map(formatSortOrder).join(', ')}` :
19
+ '';
20
+
21
+ const pathSelect = tenantName ?
22
+ `CAST(SUBSTRING(CAST(Path AS String), ${tenantName.length}) AS Utf8) AS Path` :
23
+ 'Path';
24
+
25
+ return `SELECT
26
+ ${pathSelect},
27
+ TabletId,
28
+ CPUCores,
29
+ DataSize
30
+ FROM \`.sys/partition_stats\`
31
+ WHERE
32
+ Path='${path}'
33
+ OR Path LIKE '${path}/%'
34
+ ${orderBy}
35
+ LIMIT 20`;
14
36
  }
15
37
 
16
38
  const queryAction = 'execute-scan';
@@ -51,9 +73,15 @@ const shardsWorkload = (state = initialState, action) => {
51
73
  }
52
74
  };
53
75
 
54
- export const sendShardQuery = ({database, path = ''}) => {
76
+ export const sendShardQuery = ({database, path = '', sortOrder}) => {
55
77
  return createApiRequest({
56
- request: window.api.sendQuery(createShardQuery(path), database, queryAction),
78
+ request: window.api.sendQuery({
79
+ query: createShardQuery(path, sortOrder, database),
80
+ database,
81
+ action: queryAction,
82
+ }, {
83
+ concurrentId: 'topShards',
84
+ }),
57
85
  actions: SEND_SHARD_QUERY,
58
86
  dataHandler: (result) => {
59
87
  if (result && typeof result === 'string') {
@@ -54,6 +54,8 @@ interface TPathDescription {
54
54
 
55
55
  ColumnStoreDescription?: unknown;
56
56
  ColumnTableDescription?: unknown;
57
+
58
+ TableIndex?: TIndexDescription;
57
59
  }
58
60
 
59
61
  interface TDirEntry {
@@ -80,15 +82,42 @@ interface TDirEntry {
80
82
  Version?: TPathVersion;
81
83
  }
82
84
 
85
+ export interface TIndexDescription {
86
+ Name?: string;
87
+ /** uint64 */
88
+ LocalPathId?: string;
89
+
90
+ Type?: EIndexType;
91
+ State?: EIndexState;
92
+
93
+ KeyColumnNames?: string[];
94
+
95
+ /** uint64 */
96
+ SchemaVersion?: string;
97
+
98
+ /** uint64 */
99
+ PathOwnerId?: string;
100
+
101
+ DataColumnNames?: string[];
102
+ /** uint64 */
103
+ DataSize?: string;
104
+ }
105
+
83
106
  // incomplete
84
107
  export enum EPathType {
85
108
  EPathTypeInvalid = 'EPathTypeInvalid',
86
109
  EPathTypeDir = 'EPathTypeDir',
87
110
  EPathTypeTable = 'EPathTypeTable',
111
+
88
112
  EPathTypeSubDomain = 'EPathTypeSubDomain',
113
+
114
+ EPathTypeTableIndex = 'EPathTypeTableIndex',
115
+ EPathTypeExtSubDomain = 'EPathTypeExtSubDomain',
116
+
89
117
  EPathTypeColumnStore = 'EPathTypeColumnStore',
90
118
  EPathTypeColumnTable = 'EPathTypeColumnTable',
91
- EPathTypeTableIndex = 'EPathTypeTableIndex',
119
+ EPathTypeCdcStream = 'EPathTypeCdcStream',
120
+
92
121
  }
93
122
 
94
123
  export enum EPathSubType {
@@ -112,6 +141,19 @@ enum EPathState {
112
141
  EPathStateMoving = 'EPathStateMoving',
113
142
  }
114
143
 
144
+ enum EIndexType {
145
+ EIndexTypeInvalid = 'EIndexTypeInvalid',
146
+ EIndexTypeGlobal = 'EIndexTypeGlobal',
147
+ EIndexTypeGlobalAsync = 'EIndexTypeGlobalAsync',
148
+ }
149
+
150
+ enum EIndexState {
151
+ EIndexStateInvalid = 'EIndexStateInvalid',
152
+ EIndexStateReady = 'EIndexStateReady',
153
+ EIndexStateNotReady = 'EIndexStateNotReady',
154
+ EIndexStateWriteOnly = 'EIndexStateWriteOnly',
155
+ }
156
+
115
157
  // incomplete
116
158
  interface TPathVersion {
117
159
  /** uint64 */