ydb-embedded-ui 6.10.3 → 6.11.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -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
- });