Skip to content

Commit e3087ca

Browse files
committed
feat(MetricCards): add network utilization
1 parent 4ef00d6 commit e3087ca

File tree

9 files changed

+107
-8
lines changed

9 files changed

+107
-8
lines changed

src/components/ProgressViewer/ProgressViewer.tsx

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ Props description:
3535
6) inverseColorize - invert the colors of the progress bar
3636
7) warningThreshold - the percentage of fullness at which the color of the progress bar changes to yellow
3737
8) dangerThreshold - the percentage of fullness at which the color of the progress bar changes to red
38+
9) overflow - parcents may be more that 100%
3839
*/
3940

4041
export interface ProgressViewerProps {
@@ -49,13 +50,15 @@ export interface ProgressViewerProps {
4950
warningThreshold?: number;
5051
dangerThreshold?: number;
5152
hideCapacity?: boolean;
53+
overflow?: boolean;
5254
}
5355

5456
export function ProgressViewer({
5557
value,
5658
capacity,
5759
formatValues = defaultFormatValues,
5860
percents,
61+
overflow,
5962
className,
6063
size = 'xs',
6164
colorizeProgress,
@@ -68,12 +71,13 @@ export function ProgressViewer({
6871

6972
let fillWidth =
7073
Math.floor((parseFloat(String(value)) / parseFloat(String(capacity))) * 100) || 0;
74+
const rawFillWidth = fillWidth;
7175
fillWidth = fillWidth > 100 ? 100 : fillWidth;
7276
let valueText: number | string | undefined = value,
7377
capacityText: number | string | undefined = capacity,
7478
divider = '/';
7579
if (percents) {
76-
valueText = fillWidth + '%';
80+
valueText = (overflow ? rawFillWidth : fillWidth) + '%';
7781
capacityText = '';
7882
divider = '';
7983
} else if (formatValues) {

src/containers/Tenant/Diagnostics/TenantOverview/MetricsCards/MetricCard/MetricCard.scss

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,13 @@
1414
margin-bottom: 10px;
1515
}
1616

17+
&__note {
18+
display: flex;
19+
.g-help-mark__button {
20+
display: flex;
21+
}
22+
}
23+
1724
&__label {
1825
font-weight: 600;
1926

src/containers/Tenant/Diagnostics/TenantOverview/MetricsCards/MetricCard/MetricCard.tsx

Lines changed: 22 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
import {Flex, HelpMark} from '@gravity-ui/uikit';
2+
13
import {DiagnosticCard} from '../../../../../../components/DiagnosticCard/DiagnosticCard';
24
import {ProgressViewer} from '../../../../../../components/ProgressViewer/ProgressViewer';
35
import type {ProgressViewerProps} from '../../../../../../components/ProgressViewer/ProgressViewer';
@@ -36,9 +38,11 @@ interface MetricCardProps {
3638
label?: string;
3739
status?: MetricStatus;
3840
metrics: DiagnosticsCardMetric[];
41+
interactive?: boolean;
42+
note?: string;
3943
}
4044

41-
export function MetricCard({active, label, status, metrics}: MetricCardProps) {
45+
export function MetricCard({active, label, status, metrics, interactive, note}: MetricCardProps) {
4246
const renderContent = () => {
4347
return metrics.map(({title, ...progressViewerProps}, index) => {
4448
return (
@@ -49,10 +53,25 @@ export function MetricCard({active, label, status, metrics}: MetricCardProps) {
4953
);
5054
});
5155
};
56+
const renderNote = () => {
57+
if (!note) {
58+
return null;
59+
}
60+
return (
61+
<HelpMark placement="top" className={b('note')}>
62+
{note}
63+
</HelpMark>
64+
);
65+
};
5266
return (
53-
<DiagnosticCard className={b({active})} active={active}>
67+
<DiagnosticCard className={b({active})} active={active} interactive={interactive}>
5468
<div className={b('header')}>
55-
{label && <div className={b('label')}>{label}</div>}
69+
{label && (
70+
<Flex gap={1} alignItems="center" className={b('label')}>
71+
{label}
72+
{renderNote()}
73+
</Flex>
74+
)}
5675
{getStatusIcon(status)}
5776
</div>
5877
<div className={b('content')}>{renderContent()}</div>

src/containers/Tenant/Diagnostics/TenantOverview/MetricsCards/MetricsCards.tsx

Lines changed: 44 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,9 +14,11 @@ import type {
1414
TenantStorageStats,
1515
} from '../../../../../store/reducers/tenants/utils';
1616
import {getMetricStatusFromUsage} from '../../../../../store/reducers/tenants/utils';
17+
import {formatBytes} from '../../../../../utils/bytesParsers';
1718
import {cn} from '../../../../../utils/cn';
19+
import {SHOW_NETWORK_UTILIZATION} from '../../../../../utils/constants';
1820
import {formatStorageValues} from '../../../../../utils/dataFormatters/dataFormatters';
19-
import {useTypedSelector} from '../../../../../utils/hooks';
21+
import {useSetting, useTypedSelector} from '../../../../../utils/hooks';
2022
import {TenantTabsGroups, getTenantPath} from '../../../TenantPages';
2123
import i18n from '../i18n';
2224

@@ -41,13 +43,15 @@ interface MetricsCardsProps {
4143
memoryStats?: TenantMetricStats[];
4244
blobStorageStats?: TenantStorageStats[];
4345
tabletStorageStats?: TenantStorageStats[];
46+
networkStats?: TenantMetricStats[];
4447
}
4548

4649
export function MetricsCards({
4750
poolsCpuStats,
4851
memoryStats,
4952
blobStorageStats,
5053
tabletStorageStats,
54+
networkStats,
5155
}: MetricsCardsProps) {
5256
const location = useLocation();
5357

@@ -100,6 +104,7 @@ export function MetricsCards({
100104
active={metricsTab === TENANT_METRICS_TABS_IDS.memory}
101105
/>
102106
</Link>
107+
<NetworkCard networkStats={networkStats} />
103108
</div>
104109
);
105110
}
@@ -210,3 +215,41 @@ function MemoryCard({active, memoryStats = []}: MemoryCardProps) {
210215
/>
211216
);
212217
}
218+
interface NetworkCardProps {
219+
networkStats?: TenantMetricStats[];
220+
}
221+
222+
function NetworkCard({networkStats}: NetworkCardProps) {
223+
const [showNetworkUtilization] = useSetting<boolean>(SHOW_NETWORK_UTILIZATION);
224+
if (!showNetworkUtilization || !networkStats) {
225+
return null;
226+
}
227+
let status: MetricStatus = METRIC_STATUS.Unspecified;
228+
229+
const metrics: DiagnosticsCardMetric[] = networkStats.map((metric) => {
230+
const {used, limit, usage} = metric;
231+
232+
const metricStatus = getMetricStatusFromUsage(usage);
233+
if (MetricStatusToSeverity[metricStatus] > MetricStatusToSeverity[status]) {
234+
status = metricStatus;
235+
}
236+
237+
return {
238+
title: formatBytes({value: limit, withSpeedLabel: true}),
239+
value: used,
240+
capacity: limit,
241+
percents: true,
242+
overflow: true,
243+
};
244+
});
245+
246+
return (
247+
<MetricCard
248+
interactive={false}
249+
label={i18n('cards.network-label')}
250+
note={i18n('cards.network-note')}
251+
metrics={metrics}
252+
status={status}
253+
/>
254+
);
255+
}

src/containers/Tenant/Diagnostics/TenantOverview/TenantOverview.tsx

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,7 @@ export function TenantOverview({
8686

8787
poolsStats,
8888
memoryStats,
89+
networkStats,
8990
blobStorageStats,
9091
tabletStorageStats,
9192
} = calculateTenantMetrics(tenantData);
@@ -162,6 +163,7 @@ export function TenantOverview({
162163
memoryStats={memoryStats}
163164
blobStorageStats={blobStorageStats}
164165
tabletStorageStats={tabletStorageStats}
166+
networkStats={networkStats}
165167
/>
166168
</Flex>
167169
</div>

src/containers/Tenant/Diagnostics/TenantOverview/i18n/en.json

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,8 @@
2424
"cards.cpu-label": "CPU",
2525
"cards.storage-label": "Storage",
2626
"cards.memory-label": "Memory",
27+
"cards.network-label": "Network",
28+
"cards.network-note": "Network usage is the average outgoing bandwidth usage across all nodes in the database",
2729

2830
"charts.queries-per-second": "Queries per second",
2931
"charts.transaction-latency": "Transactions latencies {{percentile}}",

src/containers/UserSettings/i18n/en.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@
3838

3939
"settings.enableNetworkTable.title": "Enable network table",
4040

41-
"settings.showNetworkUtilization.title": "Show cluster network utilization",
41+
"settings.showNetworkUtilization.title": "Show network utilization",
4242

4343
"settings.useShowPlanToSvg.title": "Execution plan",
4444
"settings.useShowPlanToSvg.description": " Show \"Execution plan\" button in query result widow. Opens svg with execution plan in a new window.",

src/store/reducers/tenants/utils.ts

Lines changed: 19 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,10 @@
1+
import {isNil} from 'lodash';
2+
13
import type {PoolName, TPoolStats} from '../../../types/api/nodes';
24
import type {TTenant} from '../../../types/api/tenant';
35
import {EType} from '../../../types/api/tenant';
46
import {DEFAULT_DANGER_THRESHOLD, DEFAULT_WARNING_THRESHOLD} from '../../../utils/constants';
5-
import {isNumeric} from '../../../utils/utils';
7+
import {isNumeric, safeParseNumber} from '../../../utils/utils';
68

79
import {METRIC_STATUS} from './contants';
810
import type {PreparedTenant} from './types';
@@ -16,7 +18,7 @@ const getControlPlaneValue = (tenant: TTenant) => {
1618
};
1719

1820
export interface TenantMetricStats<T = string> {
19-
name: T;
21+
name?: T;
2022
usage?: number;
2123
limit?: number;
2224
used?: number;
@@ -60,6 +62,8 @@ export const calculateTenantMetrics = (tenant: TTenant = {}) => {
6062
DatabaseQuotas = {},
6163
StorageUsage,
6264
QuotaUsage,
65+
NetworkUtilization,
66+
NetworkWriteThroughput,
6367
} = tenant;
6468

6569
const cpu = Number(CoresUsed) * 1_000_000 || 0;
@@ -140,6 +144,18 @@ export const calculateTenantMetrics = (tenant: TTenant = {}) => {
140144
},
141145
];
142146

147+
const isNetworkStatsAvailabe = !isNil(NetworkUtilization) && !isNil(NetworkWriteThroughput);
148+
const networkThroughput = safeParseNumber(NetworkWriteThroughput);
149+
const usedNetwork = safeParseNumber(NetworkUtilization) * networkThroughput;
150+
const networkStats: TenantMetricStats[] | undefined = isNetworkStatsAvailabe
151+
? [
152+
{
153+
used: usedNetwork,
154+
limit: networkThroughput,
155+
},
156+
]
157+
: undefined;
158+
143159
return {
144160
memory,
145161
blobStorage,
@@ -153,6 +169,7 @@ export const calculateTenantMetrics = (tenant: TTenant = {}) => {
153169
memoryStats,
154170
blobStorageStats,
155171
tabletStorageStats,
172+
networkStats,
156173
};
157174
};
158175

src/types/api/tenant.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,11 @@ export interface TTenant {
6262

6363
StorageUsage?: TStorageUsage[];
6464
QuotaUsage?: TStorageUsage[];
65+
66+
/** value is float */
67+
NetworkUtilization?: number;
68+
/** value is uint64 */
69+
NetworkWriteThroughput?: string;
6570
}
6671

6772
export interface THiveDomainStatsStateCount {

0 commit comments

Comments
 (0)