Skip to content

Commit 1a35cfc

Browse files
committed
fix(Overview): format & group table info in overview
1 parent 5d58fcf commit 1a35cfc

File tree

5 files changed

+246
-59
lines changed

5 files changed

+246
-59
lines changed

src/containers/Tenant/Diagnostics/DetailedOverview/DetailedOverview.tsx

Lines changed: 15 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -60,20 +60,21 @@ function DetailedOverview(props: DetailedOverviewProps) {
6060
const isTenant = tenantName === currentSchemaPath;
6161
return (
6262
<div className={b()}>
63-
<div className={b('section')}>
64-
{!isTenant && (
65-
<Overview type={type} tenantName={tenantName} />
66-
)}
67-
{isTenant && <TenantOverview tenantName={tenantName} additionalTenantInfo={additionalTenantInfo}/>}
68-
</div>
69-
{isTenant && (
70-
<div className={b('section')}>
71-
<Healthcheck
72-
tenant={tenantName}
73-
preview={true}
74-
showMoreHandler={openModalHandler}
75-
/>
76-
</div>
63+
{isTenant ? (
64+
<>
65+
<div className={b('section')}>
66+
<TenantOverview tenantName={tenantName} additionalTenantInfo={additionalTenantInfo} />
67+
</div>
68+
<div className={b('section')}>
69+
<Healthcheck
70+
tenant={tenantName}
71+
preview={true}
72+
showMoreHandler={openModalHandler}
73+
/>
74+
</div>
75+
</>
76+
) : (
77+
<Overview type={type} tenantName={tenantName} />
7778
)}
7879
</div>
7980
);

src/containers/Tenant/Schema/SchemaInfoViewer/SchemaInfoViewer.js

Lines changed: 132 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -3,63 +3,153 @@ import PropTypes from 'prop-types';
33
import cn from 'bem-cn-lite';
44
import './SchemaInfoViewer.scss';
55

6-
import {formatCPU, formatBytes} from '../../../../utils';
6+
import {formatCPU, formatBytes, formatNumber, formatBps} from '../../../../utils';
77

8-
import InfoViewer from '../../../../components/InfoViewer/InfoViewer';
8+
import {InfoViewer, createInfoFormatter} from '../../../../components/InfoViewer';
99

1010
const b = cn('schema-info-viewer');
1111

12+
const formatTabletMetricsItem = createInfoFormatter({
13+
values: {
14+
CPU: formatCPU,
15+
Memory: formatBytes,
16+
Storage: formatBytes,
17+
Network: formatBps,
18+
ReadThroughput: formatBps,
19+
WriteThroughput: formatBps,
20+
},
21+
defaultValueFormatter: formatNumber,
22+
});
23+
24+
const formatTableStatsItem = createInfoFormatter({
25+
values: {
26+
DataSize: formatBytes,
27+
IndexSize: formatBytes,
28+
LastAccessTime: (value) => value > 0 ? new Date(Number(value)).toUTCString() : 'N/A',
29+
LastUpdateTime: (value) => value > 0 ? new Date(Number(value)).toUTCString() : 'N/A',
30+
},
31+
defaultValueFormatter: formatNumber,
32+
});
33+
34+
const formatTableStats = (fields) => Object.entries(fields)
35+
.map(([label, value]) => formatTableStatsItem(label, value))
36+
.filter(({value}) => Boolean(value));
37+
1238
class SchemaInfoViewer extends React.Component {
1339
static propTypes = {
1440
data: PropTypes.object.isRequired,
1541
};
16-
formatTabletMetricsValue = (key, value) => {
17-
if (key === 'CPU') {
18-
return formatCPU(value);
19-
} else if (key === 'Memory' || key === 'Storage') {
20-
return formatBytes(value);
21-
} else {
22-
return value;
42+
43+
renderItem(itemData, title) {
44+
if (!Array.isArray(itemData) || !itemData.length) {
45+
return null;
2346
}
24-
};
47+
48+
return (
49+
<div className={b('item')}>
50+
<InfoViewer
51+
title={title}
52+
info={itemData}
53+
/>
54+
</div>
55+
);
56+
}
57+
58+
renderContent(data) {
59+
const {PathDescription = {}} = data;
60+
const {TableStats = {}, TabletMetrics = {}} = PathDescription;
61+
const {
62+
PartCount,
63+
RowCount,
64+
DataSize,
65+
IndexSize,
66+
67+
LastAccessTime,
68+
LastUpdateTime,
69+
70+
ImmediateTxCompleted,
71+
PlannedTxCompleted,
72+
TxRejectedByOverload,
73+
TxRejectedBySpace,
74+
TxCompleteLagMsec,
75+
InFlightTxCount,
76+
77+
RowUpdates,
78+
RowDeletes,
79+
RowReads,
80+
RangeReads,
81+
RangeReadRows,
82+
83+
...restTableStats
84+
} = TableStats;
85+
86+
const tableStatsInfo = [
87+
formatTableStats({
88+
PartCount,
89+
RowCount,
90+
DataSize,
91+
IndexSize,
92+
}),
93+
formatTableStats({
94+
LastAccessTime,
95+
LastUpdateTime,
96+
}),
97+
formatTableStats({
98+
ImmediateTxCompleted,
99+
PlannedTxCompleted,
100+
TxRejectedByOverload,
101+
TxRejectedBySpace,
102+
TxCompleteLagMsec,
103+
InFlightTxCount,
104+
}),
105+
formatTableStats({
106+
RowUpdates,
107+
RowDeletes,
108+
RowReads,
109+
RangeReads,
110+
RangeReadRows,
111+
}),
112+
formatTableStats(restTableStats),
113+
];
114+
115+
const tabletMetricsInfo = Object.keys(TabletMetrics).map((key) =>
116+
formatTabletMetricsItem(key, TabletMetrics[key])
117+
);
118+
119+
if ([
120+
tabletMetricsInfo,
121+
tableStatsInfo.flat(),
122+
].flat().length === 0) {
123+
return (
124+
<div className={b('item')}>Empty</div>
125+
);
126+
}
127+
128+
return (
129+
<div className={b('row')}>
130+
{tabletMetricsInfo.length > 0 ? (
131+
<div className={b('col')}>
132+
{this.renderItem(tabletMetricsInfo, 'Tablet Metrics')}
133+
</div>
134+
) : null}
135+
<div className={b('col')}>
136+
{tableStatsInfo.map((info, index) => (
137+
<React.Fragment key={index}>
138+
{this.renderItem(info, index === 0 ? 'Table Stats' : undefined)}
139+
</React.Fragment>
140+
))}
141+
</div>
142+
</div>
143+
);
144+
}
145+
25146
render() {
26147
const {data} = this.props;
27148

28149
if (data) {
29-
const {PathDescription = {}} = data;
30-
const {TableStats = {}, TabletMetrics = {}} = PathDescription;
31-
const {PartCount, ...restTableStats} = TableStats;
32-
33-
const priorityInfo = [{
34-
label: 'PartCount',
35-
value: PartCount,
36-
}].filter(({value}) => value !== undefined);
37-
38-
const tableStatsInfo = Object.keys(restTableStats).map((key) => ({
39-
label: key,
40-
value: TableStats[key].toString(),
41-
}));
42-
43-
const tabletMetricsInfo = Object.keys(TabletMetrics).map((key) => ({
44-
label: key,
45-
value: this.formatTabletMetricsValue(key, TabletMetrics[key].toString()),
46-
}));
47-
48-
const generalInfo = [
49-
...priorityInfo,
50-
...tabletMetricsInfo,
51-
...tableStatsInfo,
52-
];
53-
54150
return (
55151
<div className={b()}>
56-
<div className={b('item')}>
57-
{generalInfo.length ? (
58-
<InfoViewer info={generalInfo}></InfoViewer>
59-
) : (
60-
<div>Empty</div>
61-
)}
62-
</div>
152+
{this.renderContent(data)}
63153
</div>
64154
);
65155
} else {

src/containers/Tenant/Schema/SchemaInfoViewer/SchemaInfoViewer.scss

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,24 @@
11
.schema-info-viewer {
22
overflow: auto;
33

4+
&__row {
5+
display: flex;
6+
flex-wrap: wrap;
7+
justify-content: flex-start;
8+
align-items: flex-start;
9+
}
10+
11+
&__col {
12+
display: flex;
13+
flex-direction: column;
14+
justify-content: flex-start;
15+
align-items: flex-start;
16+
17+
& + & {
18+
margin-left: 50px;
19+
}
20+
}
21+
422
&__item {
523
margin-bottom: 20px;
624

src/types/api/schema.ts

Lines changed: 79 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ export interface TEvDescribeSchemeResult {
1616
PathOwnerId?: string;
1717
}
1818

19-
enum EStatus {
19+
enum EStatus {
2020
StatusSuccess = 'StatusSuccess',
2121
StatusAccepted = 'StatusAccepted',
2222
StatusPathDoesNotExist = 'StatusPathDoesNotExist',
@@ -48,7 +48,7 @@ interface TPathDescription {
4848

4949
// for table
5050
Table?: unknown;
51-
TableStats?: unknown;
51+
TableStats?: TTableStats;
5252
TabletMetrics?: unknown;
5353
TablePartitions?: unknown[];
5454

@@ -82,6 +82,82 @@ interface TDirEntry {
8282
Version?: TPathVersion;
8383
}
8484

85+
interface TTableStats {
86+
/** uint64 */
87+
DataSize?: string;
88+
/** uint64 */
89+
RowCount?: string;
90+
/** uint64 */
91+
IndexSize?: string;
92+
/** uint64 */
93+
InMemSize?: string;
94+
95+
/**
96+
* uint64
97+
* unix time in millisec
98+
*/
99+
LastAccessTime?: string;
100+
/**
101+
* uint64
102+
* unix time in millisec
103+
*/
104+
LastUpdateTime?: string;
105+
106+
RowCountHistogram?: THistogram;
107+
DataSizeHistogram?: THistogram;
108+
109+
/** uint64 */
110+
ImmediateTxCompleted?: string;
111+
/** uint64 */
112+
PlannedTxCompleted?: string;
113+
/** uint64 */
114+
TxRejectedByOverload?: string;
115+
/** uint64 */
116+
TxRejectedBySpace?: string;
117+
/** uint64 */
118+
TxCompleteLagMsec?: string;
119+
/** uint64 */
120+
InFlightTxCount?: string;
121+
122+
/** uint64 */
123+
RowUpdates?: string;
124+
/** uint64 */
125+
RowDeletes?: string;
126+
/** uint64 */
127+
RowReads?: string;
128+
/** uint64 */
129+
RangeReads?: string;
130+
/** uint64 */
131+
RangeReadRows?: string;
132+
133+
/** uint64 */
134+
PartCount?: string;
135+
136+
KeyAccessSample?: THistogram;
137+
138+
/** uint64 */
139+
SearchHeight?: string;
140+
141+
/**
142+
* uint64
143+
* seconds since epoch
144+
*/
145+
LastFullCompactionTs?: string;
146+
147+
// i.e. this shard lent to other shards
148+
HasLoanedParts?: boolean;
149+
}
150+
151+
interface THistogram {
152+
Buckets?: THistogramBucket[];
153+
}
154+
155+
interface THistogramBucket {
156+
Key?: string;
157+
/** uint64 */
158+
Value?: string;
159+
}
160+
85161
export interface TIndexDescription {
86162
Name?: string;
87163
/** uint64 */
@@ -111,7 +187,7 @@ export enum EPathType {
111187

112188
EPathTypeSubDomain = 'EPathTypeSubDomain',
113189

114-
EPathTypeTableIndex = 'EPathTypeTableIndex',
190+
EPathTypeTableIndex = 'EPathTypeTableIndex',
115191
EPathTypeExtSubDomain = 'EPathTypeExtSubDomain',
116192

117193
EPathTypeColumnStore = 'EPathTypeColumnStore',

src/utils/index.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,8 @@ export const formatBytes = (bytes) => {
1212
return numeral(bytes).format('0 ib').replace('i', '');
1313
};
1414

15+
export const formatBps = (bytes) => formatBytes(bytes) + '/s';
16+
1517
export const formatBytesToGigabyte = (bytes) => {
1618
return `${Math.floor(bytes / GIGABYTE)} GB`;
1719
};

0 commit comments

Comments
 (0)