ydb-embedded-ui 6.10.3 → 6.11.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,8 @@
1
+ import './DeveloperUiLink.scss';
2
+ interface DeveloperUiLinkProps {
3
+ className?: string;
4
+ visible?: boolean;
5
+ href: string;
6
+ }
7
+ export declare function DeveloperUiLink({ href, visible, className }: DeveloperUiLinkProps): import("react/jsx-runtime").JSX.Element;
8
+ export {};
@@ -0,0 +1,9 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ import { ArrowUpRightFromSquare } from '@gravity-ui/icons';
3
+ import { Button, Icon } from '@gravity-ui/uikit';
4
+ import { cn } from '../../utils/cn';
5
+ import './DeveloperUiLink.scss';
6
+ const b = cn('developer-ui-link');
7
+ export function DeveloperUiLink({ href, visible = false, className }) {
8
+ return (_jsx(Button, { size: "s", href: href, className: b({ visible }, className), target: "_blank", children: _jsx(Icon, { data: ArrowUpRightFromSquare }) }));
9
+ }
@@ -0,0 +1,11 @@
1
+ .developer-ui-link {
2
+ display: none;
3
+
4
+ &_visible {
5
+ display: inline-block;
6
+ }
7
+
8
+ .data-table__row:hover & {
9
+ display: inline-block;
10
+ }
11
+ }
@@ -3,6 +3,7 @@ import { ArrowUpRightFromSquare } from '@gravity-ui/icons';
3
3
  import { Button, Icon, PopoverBehavior } from '@gravity-ui/uikit';
4
4
  import { getDefaultNodePath } from '../../containers/Node/NodePages';
5
5
  import { cn } from '../../utils/cn';
6
+ import { createDeveloperUILinkWithNodeId } from '../../utils/developerUI/developerUI';
6
7
  import { isUnavailableNode } from '../../utils/nodes';
7
8
  import { CellWithPopover } from '../CellWithPopover/CellWithPopover';
8
9
  import { EntityStatus } from '../EntityStatus/EntityStatus';
@@ -14,12 +15,18 @@ export const NodeHostWrapper = ({ node, getNodeRef }) => {
14
15
  return _jsx("span", { children: "\u2014" });
15
16
  }
16
17
  const isNodeAvailable = !isUnavailableNode(node);
17
- const nodeRef = isNodeAvailable && getNodeRef ? getNodeRef(node) + 'internal' : undefined;
18
+ let nodeHref;
19
+ if (getNodeRef) {
20
+ nodeHref = getNodeRef(node) + 'internal';
21
+ }
22
+ else if (node.NodeId) {
23
+ nodeHref = createDeveloperUILinkWithNodeId(node.NodeId) + 'internal';
24
+ }
18
25
  const nodePath = isNodeAvailable
19
26
  ? getDefaultNodePath(node.NodeId, {
20
27
  tenantName: node.TenantName,
21
28
  })
22
29
  : undefined;
23
- const additionalControls = nodeRef ? (_jsx(Button, { size: "s", href: nodeRef, className: b('external-button'), target: "_blank", children: _jsx(Icon, { data: ArrowUpRightFromSquare }) })) : null;
30
+ const additionalControls = nodeHref ? (_jsx(Button, { size: "s", href: nodeHref, className: b('external-button'), target: "_blank", children: _jsx(Icon, { data: ArrowUpRightFromSquare }) })) : null;
24
31
  return (_jsx(CellWithPopover, { disabled: !isNodeAvailable, content: _jsx(NodeEndpointsTooltipContent, { data: node }), placement: ['top', 'bottom'], behavior: PopoverBehavior.Immediate, children: _jsx(EntityStatus, { name: node.Host, status: node.SystemState, path: nodePath, hasClipboardButton: true, additionalControls: additionalControls }) }));
25
32
  };
@@ -3,12 +3,14 @@ import { ArrowsRotateRight } from '@gravity-ui/icons';
3
3
  import { Icon, Label, Text } from '@gravity-ui/uikit';
4
4
  import { skipToken } from '@reduxjs/toolkit/query';
5
5
  import { ButtonWithConfirmDialog } from '../../components/ButtonWithConfirmDialog/ButtonWithConfirmDialog';
6
+ import { DeveloperUiLink } from '../../components/DeveloperUiLink/DeveloperUiLink';
6
7
  import { EntityStatus } from '../../components/EntityStatus/EntityStatus';
7
8
  import { ResponseError } from '../../components/Errors/ResponseError';
8
9
  import { InternalLink } from '../../components/InternalLink';
9
10
  import { ResizeableDataTable } from '../../components/ResizeableDataTable/ResizeableDataTable';
10
11
  import { TableSkeleton } from '../../components/TableSkeleton/TableSkeleton';
11
12
  import routes, { createHref } from '../../routes';
13
+ import { backend } from '../../store';
12
14
  import { selectTabletsWithFqdn, tabletsApi } from '../../store/reducers/tablets';
13
15
  import { ETabletState } from '../../types/api/tablet';
14
16
  import { cn } from '../../utils/cn';
@@ -31,13 +33,15 @@ const columns = [
31
33
  },
32
34
  {
33
35
  name: 'TabletId',
36
+ width: 230,
34
37
  get header() {
35
38
  return i18n('Tablet');
36
39
  },
37
40
  render: ({ row }) => {
41
+ var _a;
38
42
  const tabletPath = row.TabletId &&
39
43
  createHref(routes.tablet, { id: row.TabletId }, { nodeId: row.NodeId, type: row.Type });
40
- return _jsx(InternalLink, { to: tabletPath, children: row.TabletId });
44
+ return (_jsx(EntityStatus, { name: (_a = row.TabletId) === null || _a === void 0 ? void 0 : _a.toString(), path: tabletPath, hasClipboardButton: true, showStatus: false, additionalControls: _jsx(DeveloperUiLink, { href: `${backend}/tablets?TabletID=${row.TabletId}` }) }));
41
45
  },
42
46
  },
43
47
  {
@@ -18,17 +18,6 @@ import { TableInfo } from './TableInfo';
18
18
  import { TopicInfo } from './TopicInfo';
19
19
  function Overview({ type, path }) {
20
20
  const [autoRefreshInterval] = useAutoRefreshInterval();
21
- // FIXME: The request is too heavy, stats table may have millions of items
22
- // Disabled until fixed
23
- // https://github.com/ydb-platform/ydb-embedded-ui/issues/907
24
- // https://github.com/ydb-platform/ydb-embedded-ui/issues/908
25
- // const olapParams = isTableType(type) && isColumnEntityType(type) ? {path} : skipToken;
26
- // const {currentData: olapData, isFetching: olapIsFetching} = olapApi.useGetOlapStatsQuery(
27
- // olapParams,
28
- // {pollingInterval: autoRefreshInterval},
29
- // );
30
- // const olapStatsLoading = olapIsFetching && olapData === undefined;
31
- // const {result: olapStats} = olapData || {result: undefined};
32
21
  const isEntityWithMergedImpl = isEntityWithMergedImplementation(type);
33
22
  // shallowEqual prevents rerenders when new schema data is loaded
34
23
  const mergedChildrenPaths = useTypedSelector((state) => selectSchemaMergedChildrenPaths(state, path, type), shallowEqual);
@@ -45,7 +34,6 @@ function Overview({ type, path }) {
45
34
  const overviewLoading = isFetching && currentData === undefined;
46
35
  const { data: rawData, additionalData } = currentData || {};
47
36
  const { error: schemaError } = useGetSchemaQuery({ path });
48
- // overviewLoading || olapStatsLoading
49
37
  const entityLoading = overviewLoading;
50
38
  const entityNotReady = isEntityWithMergedImpl && !mergedChildrenPaths;
51
39
  const renderContent = () => {
@@ -72,7 +60,7 @@ function Overview({ type, path }) {
72
60
  [EPathType.EPathTypeView]: () => _jsx(ViewInfo, { data: data }),
73
61
  [EPathType.EPathTypeReplication]: () => _jsx(AsyncReplicationInfo, { data: data }),
74
62
  };
75
- return ((type && ((_a = pathTypeToComponent[type]) === null || _a === void 0 ? void 0 : _a.call(pathTypeToComponent))) || (_jsx(TableInfo, { data: data, type: type })));
63
+ return (type && ((_a = pathTypeToComponent[type]) === null || _a === void 0 ? void 0 : _a.call(pathTypeToComponent))) || _jsx(TableInfo, { data: data, type: type });
76
64
  };
77
65
  if (entityLoading || entityNotReady) {
78
66
  return _jsx(Loader, { size: "m" });
@@ -1,10 +1,8 @@
1
- import type { KeyValueRow } from '../../../../../types/api/query';
2
1
  import type { EPathType, TEvDescribeSchemeResult } from '../../../../../types/api/schema';
3
2
  import './TableInfo.scss';
4
3
  interface TableInfoProps {
5
4
  data?: TEvDescribeSchemeResult;
6
5
  type?: EPathType;
7
- olapStats?: KeyValueRow[];
8
6
  }
9
- export declare const TableInfo: ({ data, type, olapStats }: TableInfoProps) => import("react/jsx-runtime").JSX.Element;
7
+ export declare const TableInfo: ({ data, type }: TableInfoProps) => import("react/jsx-runtime").JSX.Element;
10
8
  export {};
@@ -7,8 +7,8 @@ import i18n from './i18n';
7
7
  import { prepareTableInfo } from './prepareTableInfo';
8
8
  import './TableInfo.scss';
9
9
  const b = cn('ydb-diagnostics-table-info');
10
- export const TableInfo = ({ data, type, olapStats }) => {
10
+ export const TableInfo = ({ data, type }) => {
11
11
  const title = _jsx(EntityTitle, { data: data === null || data === void 0 ? void 0 : data.PathDescription });
12
- const { generalInfo, tableStatsInfo, tabletMetricsInfo = [], partitionConfigInfo = [], } = React.useMemo(() => prepareTableInfo(data, type, olapStats), [data, type, olapStats]);
12
+ const { generalInfo, tableStatsInfo, tabletMetricsInfo = [], partitionConfigInfo = [], } = React.useMemo(() => prepareTableInfo(data, type), [data, type]);
13
13
  return (_jsxs("div", { className: b(), children: [_jsx(InfoViewer, { info: generalInfo, title: title, className: b('info-block'), renderEmptyState: () => _jsx("div", { className: b('title'), children: title }) }), _jsxs("div", { className: b('row'), children: [tableStatsInfo ? (_jsx("div", { className: b('col'), children: tableStatsInfo.map((info, index) => (_jsx(InfoViewer, { info: info, title: index === 0 ? i18n('tableStats') : undefined, className: b('info-block'), renderEmptyState: () => null }, index))) })) : null, tabletMetricsInfo.length > 0 || partitionConfigInfo.length > 0 ? (_jsxs("div", { className: b('col'), children: [_jsx(InfoViewer, { info: tabletMetricsInfo, title: i18n('tabletMetrics'), className: b('info-block'), renderEmptyState: () => null }), _jsx(InfoViewer, { info: partitionConfigInfo, title: i18n('partitionConfig'), className: b('info-block'), renderEmptyState: () => null })] })) : null] })] }));
14
14
  };
@@ -1,16 +1,15 @@
1
1
  import type { InfoViewerItem } from '../../../../../components/InfoViewer';
2
- import type { KeyValueRow } from '../../../../../types/api/query';
3
2
  import type { TEvDescribeSchemeResult } from '../../../../../types/api/schema';
4
3
  import { EPathType } from '../../../../../types/api/schema';
5
4
  /** Prepares data for Table, ColumnTable and ColumnStore */
6
- export declare const prepareTableInfo: (data?: TEvDescribeSchemeResult, type?: EPathType, olapStats?: KeyValueRow[]) => {
5
+ export declare const prepareTableInfo: (data?: TEvDescribeSchemeResult, type?: EPathType) => {
7
6
  generalInfo?: undefined;
8
7
  tableStatsInfo?: undefined;
9
8
  tabletMetricsInfo?: undefined;
10
9
  partitionConfigInfo?: undefined;
11
10
  } | {
12
11
  generalInfo: InfoViewerItem[];
13
- tableStatsInfo: InfoViewerItem[][] | undefined;
12
+ tableStatsInfo: InfoViewerItem[][];
14
13
  tabletMetricsInfo: InfoViewerItem[];
15
14
  partitionConfigInfo: InfoViewerItem[];
16
15
  };
@@ -3,31 +3,10 @@ import { formatFollowerGroupItem, formatPartitionConfigItem, formatTableStatsIte
3
3
  import { EPathType } from '../../../../../types/api/schema';
4
4
  import { formatBytes, formatNumber } from '../../../../../utils/dataFormatters/dataFormatters';
5
5
  import { formatDurationToShortTimeFormat } from '../../../../../utils/timeParsers';
6
- import { isNumeric } from '../../../../../utils/utils';
7
6
  const isInStoreColumnTable = (table) => {
8
7
  // SchemaPresetId could be 0
9
8
  return table.SchemaPresetName && table.SchemaPresetId !== undefined;
10
9
  };
11
- const prepareOlapStats = (olapStats) => {
12
- var _a, _b, _c;
13
- const Bytes = olapStats === null || olapStats === void 0 ? void 0 : olapStats.reduce((acc, el) => {
14
- const value = isNumeric(el.Bytes) ? Number(el.Bytes) : 0;
15
- return acc + value;
16
- }, 0);
17
- const Rows = olapStats === null || olapStats === void 0 ? void 0 : olapStats.reduce((acc, el) => {
18
- const value = isNumeric(el.Rows) ? Number(el.Rows) : 0;
19
- return acc + value;
20
- }, 0);
21
- const tabletIds = olapStats === null || olapStats === void 0 ? void 0 : olapStats.reduce((acc, el) => {
22
- acc.add(el.TabletId);
23
- return acc;
24
- }, new Set());
25
- return [
26
- { label: 'PartCount', value: (_a = tabletIds === null || tabletIds === void 0 ? void 0 : tabletIds.size) !== null && _a !== void 0 ? _a : 0 },
27
- { label: 'RowCount', value: (_b = formatNumber(Rows)) !== null && _b !== void 0 ? _b : 0 },
28
- { label: 'DataSize', value: (_c = formatBytes(Bytes)) !== null && _c !== void 0 ? _c : 0 },
29
- ];
30
- };
31
10
  const prepareTTL = (ttl) => {
32
11
  // ExpireAfterSeconds could be 0
33
12
  if (ttl.Enabled && ttl.Enabled.ColumnName && ttl.Enabled.ExpireAfterSeconds !== undefined) {
@@ -97,7 +76,7 @@ const prepareTableGeneralInfo = (PartitionConfig, TTLSettings) => {
97
76
  return generalTableInfo;
98
77
  };
99
78
  /** Prepares data for Table, ColumnTable and ColumnStore */
100
- export const prepareTableInfo = (data, type, olapStats) => {
79
+ export const prepareTableInfo = (data, type) => {
101
80
  if (!data) {
102
81
  return {};
103
82
  }
@@ -116,43 +95,33 @@ export const prepareTableInfo = (data, type, olapStats) => {
116
95
  break;
117
96
  }
118
97
  }
119
- let tableStatsInfo;
120
- // There is no TableStats and TabletMetrics for ColumnTables inside ColumnStore
121
- // Therefore we parse olapStats
122
- if (type === EPathType.EPathTypeColumnTable && isInStoreColumnTable(ColumnTableDescription)) {
123
- if (olapStats) {
124
- tableStatsInfo = [prepareOlapStats(olapStats)];
125
- }
126
- }
127
- else {
128
- tableStatsInfo = [
129
- formatObject(formatTableStatsItem, {
130
- PartCount,
131
- RowCount,
132
- DataSize,
133
- IndexSize,
134
- }),
135
- formatObject(formatTableStatsItem, {
136
- LastAccessTime,
137
- LastUpdateTime,
138
- }),
139
- formatObject(formatTableStatsItem, {
140
- ImmediateTxCompleted,
141
- PlannedTxCompleted,
142
- TxRejectedByOverload,
143
- TxRejectedBySpace,
144
- TxCompleteLagMsec,
145
- InFlightTxCount,
146
- }),
147
- formatObject(formatTableStatsItem, {
148
- RowUpdates,
149
- RowDeletes,
150
- RowReads,
151
- RangeReads,
152
- RangeReadRows,
153
- }),
154
- ];
155
- }
98
+ const tableStatsInfo = [
99
+ formatObject(formatTableStatsItem, {
100
+ PartCount,
101
+ RowCount,
102
+ DataSize,
103
+ IndexSize,
104
+ }),
105
+ formatObject(formatTableStatsItem, {
106
+ LastAccessTime,
107
+ LastUpdateTime,
108
+ }),
109
+ formatObject(formatTableStatsItem, {
110
+ ImmediateTxCompleted,
111
+ PlannedTxCompleted,
112
+ TxRejectedByOverload,
113
+ TxRejectedBySpace,
114
+ TxCompleteLagMsec,
115
+ InFlightTxCount,
116
+ }),
117
+ formatObject(formatTableStatsItem, {
118
+ RowUpdates,
119
+ RowDeletes,
120
+ RowReads,
121
+ RangeReads,
122
+ RangeReadRows,
123
+ }),
124
+ ];
156
125
  //@ts-expect-error
157
126
  const tabletMetricsInfo = formatObject(formatTabletMetricsItem, TabletMetrics);
158
127
  let partitionConfigInfo = [];
@@ -1,6 +1,7 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
2
  import { Loader } from '@gravity-ui/uikit';
3
3
  import { EntityStatus } from '../../../../components/EntityStatus/EntityStatus';
4
+ import { useGetSchemaQuery } from '../../../../store/reducers/schema/schema';
4
5
  import { TENANT_METRICS_TABS_IDS } from '../../../../store/reducers/tenant/constants';
5
6
  import { tenantApi } from '../../../../store/reducers/tenant/tenant';
6
7
  import { calculateTenantMetrics } from '../../../../store/reducers/tenants/utils';
@@ -16,7 +17,7 @@ import { TenantStorage } from './TenantStorage/TenantStorage';
16
17
  import { b } from './utils';
17
18
  import './TenantOverview.scss';
18
19
  export function TenantOverview({ tenantName, additionalTenantProps, additionalNodesProps, }) {
19
- var _a;
20
+ var _a, _b, _c;
20
21
  const { metricsTab } = useTypedSelector((state) => state.tenant);
21
22
  const [autoRefreshInterval] = useAutoRefreshInterval();
22
23
  const { currentData: tenant, isFetching } = tenantApi.useGetTenantInfoQuery({ path: tenantName }, {
@@ -25,7 +26,31 @@ export function TenantOverview({ tenantName, additionalTenantProps, additionalNo
25
26
  const tenantLoading = isFetching && tenant === undefined;
26
27
  const { Name, Type, Overall } = tenant || {};
27
28
  const tenantType = mapDatabaseTypeToDBName(Type);
28
- const { blobStorage, tabletStorage, blobStorageLimit, tabletStorageLimit, poolsStats, memoryStats, blobStorageStats, tabletStorageStats, } = calculateTenantMetrics(tenant !== null && tenant !== void 0 ? tenant : undefined);
29
+ // FIXME: remove after correct data is added to tenantInfo
30
+ const { data: tenantSchemaData } = useGetSchemaQuery({ path: tenantName });
31
+ const { Tables, Topics } = ((_b = (_a = tenantSchemaData === null || tenantSchemaData === void 0 ? void 0 : tenantSchemaData.PathDescription) === null || _a === void 0 ? void 0 : _a.DomainDescription) === null || _b === void 0 ? void 0 : _b.DiskSpaceUsage) || {};
32
+ const usedTabletStorage = [
33
+ Tables === null || Tables === void 0 ? void 0 : Tables.TotalSize,
34
+ Topics === null || Topics === void 0 ? void 0 : Topics.AccountSize,
35
+ Topics === null || Topics === void 0 ? void 0 : Topics.DataSize,
36
+ Topics === null || Topics === void 0 ? void 0 : Topics.ReserveSize,
37
+ Topics === null || Topics === void 0 ? void 0 : Topics.UsedReserveSize,
38
+ ].reduce((sum, current) => {
39
+ if (current) {
40
+ return sum + Number(current);
41
+ }
42
+ return sum;
43
+ }, 0);
44
+ const tenantData = {
45
+ ...tenant,
46
+ Metrics: {
47
+ ...tenant === null || tenant === void 0 ? void 0 : tenant.Metrics,
48
+ // Replace incorrect tenant metric with correct value
49
+ Storage: String(usedTabletStorage),
50
+ },
51
+ };
52
+ // === === ===
53
+ const { blobStorage, tabletStorage, blobStorageLimit, tabletStorageLimit, poolsStats, memoryStats, blobStorageStats, tabletStorageStats, } = calculateTenantMetrics(tenantData);
29
54
  const storageMetrics = {
30
55
  blobStorageUsed: blobStorage,
31
56
  blobStorageLimit,
@@ -57,5 +82,5 @@ export function TenantOverview({ tenantName, additionalTenantProps, additionalNo
57
82
  if (tenantLoading) {
58
83
  return (_jsx("div", { className: b('loader'), children: _jsx(Loader, { size: "m" }) }));
59
84
  }
60
- return (_jsxs("div", { className: b(), children: [_jsxs("div", { className: b('info'), children: [_jsx("div", { className: b('top-label'), children: tenantType }), _jsxs("div", { className: b('top'), children: [renderName(), (_a = additionalTenantProps === null || additionalTenantProps === void 0 ? void 0 : additionalTenantProps.getMonitoringLink) === null || _a === void 0 ? void 0 : _a.call(additionalTenantProps, Name, Type)] }), _jsx(MetricsCards, { poolsCpuStats: poolsStats, memoryStats: memoryStats, blobStorageStats: blobStorageStats, tabletStorageStats: tabletStorageStats, tenantName: tenantName })] }), renderTabContent()] }));
85
+ return (_jsxs("div", { className: b(), children: [_jsxs("div", { className: b('info'), children: [_jsx("div", { className: b('top-label'), children: tenantType }), _jsxs("div", { className: b('top'), children: [renderName(), (_c = additionalTenantProps === null || additionalTenantProps === void 0 ? void 0 : additionalTenantProps.getMonitoringLink) === null || _c === void 0 ? void 0 : _c.call(additionalTenantProps, Name, Type)] }), _jsx(MetricsCards, { poolsCpuStats: poolsStats, memoryStats: memoryStats, blobStorageStats: blobStorageStats, tabletStorageStats: tabletStorageStats, tenantName: tenantName })] }), renderTabContent()] }));
61
86
  }
@@ -3,26 +3,17 @@ import DataTable from '@gravity-ui/react-data-table';
3
3
  import { OneLineQueryWithPopover, TruncatedQuery, } from '../../../../components/TruncatedQuery/TruncatedQuery';
4
4
  import { cn } from '../../../../utils/cn';
5
5
  import { formatDateTime, formatNumber } from '../../../../utils/dataFormatters/dataFormatters';
6
+ import { TOP_QUERIES_COLUMNS_IDS } from '../../../../utils/diagnostics';
6
7
  import { generateHash } from '../../../../utils/generateHash';
7
- import { parseUsToMs } from '../../../../utils/timeParsers';
8
+ import { formatToMs, parseUsToMs } from '../../../../utils/timeParsers';
8
9
  import { MAX_QUERY_HEIGHT } from '../../utils/constants';
9
10
  import './TopQueries.scss';
10
11
  const b = cn('kv-top-queries');
11
12
  export const TOP_QUERIES_COLUMNS_WIDTH_LS_KEY = 'topQueriesColumnsWidth';
12
- const TOP_QUERIES_COLUMNS_IDS = {
13
- CPUTimeUs: 'CPUTimeUs',
14
- QueryText: 'QueryText',
15
- EndTime: 'EndTime',
16
- ReadRows: 'ReadRows',
17
- ReadBytes: 'ReadBytes',
18
- UserSID: 'UserSID',
19
- OneLineQueryText: 'OneLineQueryText',
20
- QueryHash: 'QueryHash',
21
- Duration: 'Duration',
22
- };
23
13
  const cpuTimeUsColumn = {
24
14
  name: TOP_QUERIES_COLUMNS_IDS.CPUTimeUs,
25
15
  sortAccessor: (row) => Number(row.CPUTimeUs),
16
+ render: ({ row }) => { var _a; return formatToMs(parseUsToMs((_a = row.CPUTimeUs) !== null && _a !== void 0 ? _a : undefined)); },
26
17
  width: 120,
27
18
  align: DataTable.RIGHT,
28
19
  sortable: false,
@@ -78,8 +69,8 @@ const queryHashColumn = {
78
69
  };
79
70
  const durationColumn = {
80
71
  name: TOP_QUERIES_COLUMNS_IDS.Duration,
81
- header: 'Duration, ms',
82
- render: ({ row }) => { var _a; return formatNumber(parseUsToMs((_a = row.Duration) !== null && _a !== void 0 ? _a : undefined)); },
72
+ header: 'Duration',
73
+ render: ({ row }) => { var _a; return formatToMs(parseUsToMs((_a = row.Duration) !== null && _a !== void 0 ? _a : undefined)); },
83
74
  sortAccessor: (row) => Number(row.Duration),
84
75
  align: DataTable.RIGHT,
85
76
  width: 150,
@@ -9,7 +9,7 @@ import { EShardsWorkloadMode } from '../../../../store/reducers/shardsWorkload/t
9
9
  import { cn } from '../../../../utils/cn';
10
10
  import { DEFAULT_TABLE_SETTINGS, HOUR_IN_SECONDS } from '../../../../utils/constants';
11
11
  import { formatDateTime } from '../../../../utils/dataFormatters/dataFormatters';
12
- import { isSortableTopShardsProperty } from '../../../../utils/diagnostics';
12
+ import { TOP_SHARD_COLUMNS_IDS, isSortableTopShardsProperty } from '../../../../utils/diagnostics';
13
13
  import { useAutoRefreshInterval, useTypedDispatch, useTypedSelector } from '../../../../utils/hooks';
14
14
  import { parseQueryErrorToString } from '../../../../utils/query';
15
15
  import { isColumnEntityType } from '../../utils/schema';
@@ -25,16 +25,6 @@ const TABLE_SETTINGS = {
25
25
  disableSortReset: true,
26
26
  defaultOrder: DataTable.DESCENDING,
27
27
  };
28
- const tableColumnsNames = {
29
- TabletId: 'TabletId',
30
- CPUCores: 'CPUCores',
31
- DataSize: 'DataSize',
32
- Path: 'Path',
33
- NodeId: 'NodeId',
34
- PeakTime: 'PeakTime',
35
- InFlightTxCount: 'InFlightTxCount',
36
- IntervalEnd: 'IntervalEnd',
37
- };
38
28
  function prepareDateTimeValue(value) {
39
29
  if (!value) {
40
30
  return '–';
@@ -83,7 +73,7 @@ export const TopShards = ({ tenantName, path, type }) => {
83
73
  }
84
74
  return defaultValue;
85
75
  });
86
- const [sortOrder, setSortOrder] = React.useState(tableColumnsNames.CPUCores);
76
+ const [sortOrder, setSortOrder] = React.useState(TOP_SHARD_COLUMNS_IDS.CPUCores);
87
77
  const { data: result, isFetching, error, } = shardApi.useSendShardQueryQuery({
88
78
  database: tenantName,
89
79
  path: path,
@@ -124,14 +114,14 @@ export const TopShards = ({ tenantName, path, type }) => {
124
114
  if (filters.mode === EShardsWorkloadMode.History) {
125
115
  // after NodeId
126
116
  columns.splice(5, 0, {
127
- name: tableColumnsNames.PeakTime,
117
+ name: TOP_SHARD_COLUMNS_IDS.PeakTime,
128
118
  render: ({ row }) => {
129
119
  return prepareDateTimeValue(row.PeakTime);
130
120
  },
131
121
  sortable: false,
132
122
  });
133
123
  columns.push({
134
- name: tableColumnsNames.IntervalEnd,
124
+ name: TOP_SHARD_COLUMNS_IDS.IntervalEnd,
135
125
  render: ({ row }) => {
136
126
  return prepareDateTimeValue(row.IntervalEnd);
137
127
  },
@@ -1,9 +1,12 @@
1
1
  import { jsx as _jsx } from "react/jsx-runtime";
2
2
  import DataTable from '@gravity-ui/react-data-table';
3
+ import { DeveloperUiLink } from '../../../../components/DeveloperUiLink/DeveloperUiLink';
4
+ import { EntityStatus } from '../../../../components/EntityStatus/EntityStatus';
3
5
  import { InternalLink } from '../../../../components/InternalLink';
4
6
  import { LinkToSchemaObject } from '../../../../components/LinkToSchemaObject/LinkToSchemaObject';
5
7
  import { UsageLabel } from '../../../../components/UsageLabel/UsageLabel';
6
8
  import routes, { createHref } from '../../../../routes';
9
+ import { backend } from '../../../../store';
7
10
  import { getLoadSeverityForShard } from '../../../../store/reducers/tenantOverview/topShards/utils';
8
11
  import { formatNumber, roundToPrecision } from '../../../../utils/dataFormatters/dataFormatters';
9
12
  import { getDefaultNodePath } from '../../../Node/NodePages';
@@ -61,10 +64,11 @@ const tabletIdColumn = {
61
64
  name: TOP_SHARDS_COLUMNS_IDS.TabletId,
62
65
  header: tableColumnsNames[TOP_SHARDS_COLUMNS_IDS.TabletId],
63
66
  render: ({ row }) => {
67
+ var _a;
64
68
  if (!row.TabletId) {
65
69
  return '–';
66
70
  }
67
- return (_jsx(InternalLink, { to: createHref(routes.tablet, { id: row.TabletId }), children: row.TabletId }));
71
+ return (_jsx(EntityStatus, { name: (_a = row.TabletId) === null || _a === void 0 ? void 0 : _a.toString(), path: createHref(routes.tablet, { id: row.TabletId }), hasClipboardButton: true, showStatus: false, additionalControls: _jsx(DeveloperUiLink, { href: `${backend}/tablets?TabletID=${row.TabletId}` }) }));
68
72
  },
69
73
  sortable: false,
70
74
  width: 190,
@@ -29,6 +29,7 @@ SELECT
29
29
  Duration
30
30
  FROM \`${path}/.sys/top_queries_by_cpu_time_one_hour\`
31
31
  WHERE ${filterConditions || 'true'}
32
+ ORDER BY CPUTimeUs DESC
32
33
  `;
33
34
  };
34
35
  export const topQueriesApi = api.injectEndpoints({
@@ -148,6 +148,8 @@ interface TDomainState {
148
148
  }
149
149
  interface TDiskSpaceUsage {
150
150
  Tables?: TTables;
151
+ Topics?: TTopics;
152
+ TStoragePoolUsage?: TStoragePoolUsage[];
151
153
  }
152
154
  interface TTables {
153
155
  /** uint64 */
@@ -157,6 +159,25 @@ interface TTables {
157
159
  /** uint64 */
158
160
  IndexSize?: string;
159
161
  }
162
+ interface TTopics {
163
+ /** uint64 */
164
+ ReserveSize?: string;
165
+ /** uint64 */
166
+ AccountSize?: string;
167
+ /** uint64 */
168
+ DataSize?: string;
169
+ /** uint64 */
170
+ UsedReserveSize?: string;
171
+ }
172
+ interface TStoragePoolUsage {
173
+ PoolKind?: string;
174
+ /** uint64 */
175
+ TotalSize?: string;
176
+ /** uint64 */
177
+ DataSize?: string;
178
+ /** uint64 */
179
+ IndexSize?: string;
180
+ }
160
181
  interface TStoragePool {
161
182
  Name?: string;
162
183
  Kind?: string;
@@ -1,19 +1,23 @@
1
- import type { ValueOf } from '../types/common';
2
- declare const TOP_SHARDS_SORT_VALUES: {
3
- readonly CPUCores: "CPUCores";
4
- readonly DataSize: "DataSize";
5
- readonly InFlightTxCount: "InFlightTxCount";
1
+ export declare const TOP_QUERIES_COLUMNS_IDS: {
2
+ CPUTimeUs: string;
3
+ QueryText: string;
4
+ EndTime: string;
5
+ ReadRows: string;
6
+ ReadBytes: string;
7
+ UserSID: string;
8
+ OneLineQueryText: string;
9
+ QueryHash: string;
10
+ Duration: string;
6
11
  };
7
- declare const TOP_QUERIES_SORT_VALUES: {
8
- readonly CPUTimeUs: "CPUTimeUs";
9
- readonly EndTime: "EndTime";
10
- readonly ReadRows: "ReadRows";
11
- readonly ReadBytes: "ReadBytes";
12
- readonly UserSID: "UserSID";
13
- readonly Duration: "Duration";
12
+ export declare const TOP_SHARD_COLUMNS_IDS: {
13
+ TabletId: string;
14
+ CPUCores: string;
15
+ DataSize: string;
16
+ Path: string;
17
+ NodeId: string;
18
+ PeakTime: string;
19
+ InFlightTxCount: string;
20
+ IntervalEnd: string;
14
21
  };
15
- type TopShardsSortValue = ValueOf<typeof TOP_SHARDS_SORT_VALUES>;
16
- type TopQueriesSortValue = ValueOf<typeof TOP_QUERIES_SORT_VALUES>;
17
- export declare const isSortableTopShardsProperty: (value: string) => value is TopShardsSortValue;
18
- export declare const isSortableTopQueriesProperty: (value: string) => value is TopQueriesSortValue;
19
- export {};
22
+ export declare const isSortableTopShardsProperty: (value: string) => boolean;
23
+ export declare const isSortableTopQueriesProperty: (value: string) => boolean;
@@ -1,15 +1,36 @@
1
- const TOP_SHARDS_SORT_VALUES = {
2
- CPUCores: 'CPUCores',
3
- DataSize: 'DataSize',
4
- InFlightTxCount: 'InFlightTxCount',
5
- };
6
- const TOP_QUERIES_SORT_VALUES = {
7
- CPUTimeUs: 'CPUTimeUs',
1
+ export const TOP_QUERIES_COLUMNS_IDS = {
2
+ CPUTimeUs: 'CPUTime',
3
+ QueryText: 'QueryText',
8
4
  EndTime: 'EndTime',
9
5
  ReadRows: 'ReadRows',
10
6
  ReadBytes: 'ReadBytes',
11
7
  UserSID: 'UserSID',
8
+ OneLineQueryText: 'OneLineQueryText',
9
+ QueryHash: 'QueryHash',
12
10
  Duration: 'Duration',
13
11
  };
12
+ export const TOP_SHARD_COLUMNS_IDS = {
13
+ TabletId: 'TabletId',
14
+ CPUCores: 'CPUCores',
15
+ DataSize: 'DataSize',
16
+ Path: 'Path',
17
+ NodeId: 'NodeId',
18
+ PeakTime: 'PeakTime',
19
+ InFlightTxCount: 'InFlightTxCount',
20
+ IntervalEnd: 'IntervalEnd',
21
+ };
22
+ const TOP_SHARDS_SORT_VALUES = [
23
+ TOP_SHARD_COLUMNS_IDS.CPUCores,
24
+ TOP_SHARD_COLUMNS_IDS.DataSize,
25
+ TOP_SHARD_COLUMNS_IDS.InFlightTxCount,
26
+ ];
27
+ const TOP_QUERIES_SORT_VALUES = [
28
+ TOP_QUERIES_COLUMNS_IDS.CPUTimeUs,
29
+ TOP_QUERIES_COLUMNS_IDS.EndTime,
30
+ TOP_QUERIES_COLUMNS_IDS.ReadRows,
31
+ TOP_QUERIES_COLUMNS_IDS.ReadBytes,
32
+ TOP_QUERIES_COLUMNS_IDS.UserSID,
33
+ TOP_QUERIES_COLUMNS_IDS.Duration,
34
+ ];
14
35
  export const isSortableTopShardsProperty = (value) => Object.values(TOP_SHARDS_SORT_VALUES).includes(value);
15
36
  export const isSortableTopQueriesProperty = (value) => Object.values(TOP_QUERIES_SORT_VALUES).includes(value);
@@ -10,7 +10,7 @@ function disableCodeSuggestions() {
10
10
  export function registerYQLCompletionItemProvider(database) {
11
11
  disableCodeSuggestions();
12
12
  completionProvider = monaco.languages.registerCompletionItemProvider(LANGUAGE_YQL_ID, {
13
- triggerCharacters: [' ', '', ',', '.', '`', '(', '/'],
13
+ triggerCharacters: [' ', '.', '`', '(', '/'],
14
14
  provideCompletionItems: createProvideSuggestionsFunction(database),
15
15
  });
16
16
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ydb-embedded-ui",
3
- "version": "6.10.3",
3
+ "version": "6.11.0",
4
4
  "files": [
5
5
  "dist"
6
6
  ],
@@ -1,5 +0,0 @@
1
- export declare const olapApi: import("@reduxjs/toolkit/query").Api<import("@reduxjs/toolkit/query").BaseQueryFn<void, typeof import("./api")._NEVER, unknown, {}>, {
2
- getOlapStats: import("@reduxjs/toolkit/query").QueryDefinition<{
3
- path?: string;
4
- } | undefined, import("@reduxjs/toolkit/query").BaseQueryFn<void, typeof import("./api")._NEVER, unknown, {}>, "All", import("../../types/store/query").IQueryResult | undefined, "api">;
5
- }, "api", "All", typeof import("@reduxjs/toolkit/query").coreModuleName | typeof import("@reduxjs/toolkit/query/react").reactHooksModuleName>;
@@ -1,31 +0,0 @@
1
- import { isQueryErrorResponse, parseQueryAPIExecuteResponse } from '../../utils/query';
2
- import { api } from './api';
3
- function createOlatStatsQuery(path) {
4
- return `SELECT * FROM \`${path}/.sys/primary_index_stats\``;
5
- }
6
- const queryAction = 'execute-scan';
7
- export const olapApi = api.injectEndpoints({
8
- endpoints: (build) => ({
9
- getOlapStats: build.query({
10
- queryFn: async ({ path = '' } = {}, { signal }) => {
11
- try {
12
- const response = await window.api.sendQuery({
13
- schema: 'modern',
14
- query: createOlatStatsQuery(path),
15
- database: path,
16
- action: queryAction,
17
- }, { signal });
18
- if (isQueryErrorResponse(response)) {
19
- return { error: response };
20
- }
21
- return { data: parseQueryAPIExecuteResponse(response) };
22
- }
23
- catch (error) {
24
- return { error: error || new Error('Unauthorized') };
25
- }
26
- },
27
- providesTags: ['All'],
28
- }),
29
- }),
30
- overrideExisting: 'throw',
31
- });