Skip to content

Commit 2b9a61a

Browse files
committed
fix: Queries banner fixes
1 parent 437a81c commit 2b9a61a

File tree

10 files changed

+418
-141
lines changed

10 files changed

+418
-141
lines changed
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
@use '../../styles/mixins.scss';
2+
3+
.queries-activity-alert {
4+
$b: &;
5+
6+
border-radius: var(--g-border-radius-s);
7+
8+
&__card {
9+
width: 100%;
10+
padding: 0;
11+
12+
border: 1px solid var(--g-color-line-generic);
13+
border-radius: var(--g-border-radius-s);
14+
}
15+
16+
&__content {
17+
padding: var(--g-spacing-3) var(--g-spacing-4);
18+
}
19+
20+
&__title {
21+
color: var(--g-color-text-primary);
22+
}
23+
24+
&__open-queries-button {
25+
margin-left: var(--g-spacing-1);
26+
27+
&:focus-visible {
28+
outline: 2px solid var(--g-color-line-focus);
29+
outline-offset: 2px;
30+
}
31+
}
32+
}
Lines changed: 98 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,98 @@
1+
import {CirclePlay, Display, Person} from '@gravity-ui/icons';
2+
import {Button, Card, Flex, Icon, Label, Text} from '@gravity-ui/uikit';
3+
import {useHistory, useLocation} from 'react-router-dom';
4+
5+
import {TenantTabsGroups, getTenantPath} from '../../containers/Tenant/TenantPages';
6+
import {parseQuery} from '../../routes';
7+
import {TENANT_DIAGNOSTICS_TABS_IDS} from '../../store/reducers/tenant/constants';
8+
import {cn} from '../../utils/cn';
9+
10+
import i18n from './i18n';
11+
12+
import './QueriesActivityAlert.scss';
13+
14+
const b = cn('queries-activity-alert');
15+
16+
interface QueriesActivityAlertProps {
17+
runningQueriesCount: number;
18+
uniqueApplications: number;
19+
uniqueUsers: number;
20+
}
21+
22+
export function QueriesActivityAlert({
23+
runningQueriesCount,
24+
uniqueApplications,
25+
uniqueUsers,
26+
}: QueriesActivityAlertProps) {
27+
const history = useHistory();
28+
const location = useLocation();
29+
30+
const handleOpenRunningQueries = () => {
31+
const queryParams = parseQuery(location);
32+
const path = getTenantPath({
33+
...queryParams,
34+
[TenantTabsGroups.diagnosticsTab]: TENANT_DIAGNOSTICS_TABS_IDS.topQueries,
35+
queryMode: 'running',
36+
});
37+
history.push(path);
38+
};
39+
40+
return (
41+
<div className={b()}>
42+
<Card className={b('card')} type="container" view="outlined">
43+
<Flex
44+
direction="row"
45+
justifyContent="space-between"
46+
alignItems="center"
47+
gap={4}
48+
className={b('content')}
49+
>
50+
<Flex>
51+
<Text variant="subheader-2" className={b('title')}>
52+
{i18n('title_queries-activity')}
53+
</Text>
54+
</Flex>
55+
<Flex wrap alignItems="center" gap={1} className={b('stats')}>
56+
<Label
57+
theme={runningQueriesCount > 0 ? 'success' : 'unknown'}
58+
icon={<Icon data={CirclePlay} />}
59+
size="s"
60+
value={String(runningQueriesCount)}
61+
>
62+
{i18n('field_running-queries')}
63+
</Label>
64+
65+
<Label
66+
theme="unknown"
67+
icon={<Icon data={Display} />}
68+
size="s"
69+
value={String(uniqueApplications)}
70+
>
71+
{i18n('field_applications')}
72+
</Label>
73+
74+
<Label
75+
theme="unknown"
76+
icon={<Icon data={Person} />}
77+
size="s"
78+
value={String(uniqueUsers)}
79+
>
80+
{i18n('field_users')}
81+
</Label>
82+
83+
{runningQueriesCount > 0 && (
84+
<Button
85+
view="outlined"
86+
size="s"
87+
onClick={handleOpenRunningQueries}
88+
className={b('open-queries-button')}
89+
>
90+
{i18n('action_open-running-queries')}
91+
</Button>
92+
)}
93+
</Flex>
94+
</Flex>
95+
</Card>
96+
</div>
97+
);
98+
}

src/components/QueriesActivityBar/QueriesActivityBar.scss

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,12 @@
5757
gap: var(--g-spacing-1);
5858
}
5959

60+
&__header-metrics {
61+
display: flex;
62+
align-items: center;
63+
gap: var(--g-spacing-1);
64+
}
65+
6066
&__content {
6167
display: flex;
6268
flex-direction: column;

src/components/QueriesActivityBar/QueriesActivityBar.tsx

Lines changed: 67 additions & 134 deletions
Original file line numberDiff line numberDiff line change
@@ -6,18 +6,16 @@ import {useHistory, useLocation} from 'react-router-dom';
66

77
import {TenantTabsGroups, getTenantPath} from '../../containers/Tenant/TenantPages';
88
import {parseQuery} from '../../routes';
9-
import {topQueriesApi} from '../../store/reducers/executeTopQueries/executeTopQueries';
109
import {TENANT_DIAGNOSTICS_TABS_IDS} from '../../store/reducers/tenant/constants';
11-
import type {KeyValueRow} from '../../types/api/query';
1210
import {cn} from '../../utils/cn';
13-
import {useAutoRefreshInterval} from '../../utils/hooks';
14-
import type {TimeFrame} from '../../utils/timeframes';
15-
import {chartApi} from '../MetricChart/reducer';
16-
import type {ChartDataStatus} from '../MetricChart/types';
1711

12+
import {QueriesActivityAlert} from './QueriesActivityAlert';
1813
import {QueriesActivityCharts} from './QueriesActivityCharts';
14+
import {QueriesActivitySkeleton} from './QueriesActivitySkeleton';
1915
import i18n from './i18n';
20-
import {calculateLatency, calculateQueriesPerSecond, formatTrendValue} from './utils';
16+
import {useChartAvailability} from './useChartAvailability';
17+
import {useQueriesActivityData} from './useQueriesActivityData';
18+
import {formatTrendValue} from './utils';
2119

2220
import './QueriesActivityBar.scss';
2321

@@ -30,93 +28,13 @@ interface QueriesActivityBarProps {
3028
export function QueriesActivityBar({tenantName}: QueriesActivityBarProps) {
3129
const history = useHistory();
3230
const location = useLocation();
33-
const [autoRefreshInterval] = useAutoRefreshInterval();
3431
const [expanded, setExpanded] = React.useState(false);
35-
const [queriesTimeFrame] = React.useState<TimeFrame>('1h');
36-
const [latenciesTimeFrame] = React.useState<TimeFrame>('1h');
37-
const [isActivityBarHidden, setIsActivityBarHidden] = React.useState<boolean>(true);
38-
39-
// Refetch data only if activity bar successfully loaded
40-
const shouldRefresh = isActivityBarHidden ? 0 : autoRefreshInterval;
41-
42-
/**
43-
* Activity bar should be hidden, if charts are not enabled:
44-
* 1. GraphShard is not enabled
45-
* 2. ydb version does not have /viewer/json/render endpoint (400, 404, CORS error, etc.)
46-
*
47-
* If at least one chart successfully loaded, activity bar should be shown
48-
* @link https://github.com/ydb-platform/ydb-embedded-ui/issues/659
49-
* @todo disable only for specific errors ('GraphShard is not enabled') after ydb-stable-24 is generally used
50-
*/
51-
const handleChartDataStatusChange = React.useCallback((chartStatus: ChartDataStatus) => {
52-
if (chartStatus === 'success') {
53-
setIsActivityBarHidden(false);
54-
}
55-
}, []);
56-
57-
// Fetch running queries
58-
const {data: runningQueriesData} = topQueriesApi.useGetRunningQueriesQuery(
59-
{
60-
database: tenantName,
61-
filters: {},
62-
},
63-
{pollingInterval: shouldRefresh},
64-
);
65-
66-
// Fetch queries per second data for header metrics
67-
const {data: queriesPerSecData} = chartApi.useGetChartDataQuery(
68-
{
69-
database: tenantName,
70-
metrics: [{target: 'queries.requests'}],
71-
timeFrame: queriesTimeFrame,
72-
maxDataPoints: 30,
73-
},
74-
{pollingInterval: shouldRefresh},
75-
);
76-
77-
// Fetch latency data for header metrics
78-
const {data: latencyData} = chartApi.useGetChartDataQuery(
79-
{
80-
database: tenantName,
81-
metrics: [{target: 'queries.latencies.p99'}],
82-
timeFrame: latenciesTimeFrame,
83-
maxDataPoints: 30,
84-
},
85-
{pollingInterval: shouldRefresh},
86-
);
87-
88-
const runningQueriesCount = runningQueriesData?.resultSets?.[0]?.result?.length || 0;
8932

90-
const qps = React.useMemo(
91-
() => calculateQueriesPerSecond(queriesPerSecData?.metrics?.[0]?.data),
92-
[queriesPerSecData?.metrics?.[0]?.data],
93-
);
33+
// Check chart availability without rendering hidden components
34+
const areChartsAvailable = useChartAvailability(tenantName);
9435

95-
const latency = React.useMemo(
96-
() => calculateLatency(latencyData?.metrics?.[0]?.data),
97-
[latencyData?.metrics?.[0]?.data],
98-
);
99-
100-
// Calculate unique applications and users
101-
const uniqueApplications = React.useMemo(() => {
102-
const apps = new Set<string>();
103-
runningQueriesData?.resultSets?.[0]?.result?.forEach((row: KeyValueRow) => {
104-
if (row.ApplicationName) {
105-
apps.add(String(row.ApplicationName));
106-
}
107-
});
108-
return apps.size;
109-
}, [runningQueriesData]);
110-
111-
const uniqueUsers = React.useMemo(() => {
112-
const users = new Set<string>();
113-
runningQueriesData?.resultSets?.[0]?.result?.forEach((row: KeyValueRow) => {
114-
if (row.UserSID) {
115-
users.add(String(row.UserSID));
116-
}
117-
});
118-
return users.size;
119-
}, [runningQueriesData]);
36+
const {runningQueriesCount, uniqueApplications, uniqueUsers, qps, latency} =
37+
useQueriesActivityData(tenantName);
12038

12139
const handleOpenRunningQueries = () => {
12240
const queryParams = parseQuery(location);
@@ -132,8 +50,25 @@ export function QueriesActivityBar({tenantName}: QueriesActivityBarProps) {
13250
setExpanded(!expanded);
13351
};
13452

53+
// Show skeleton while determining chart availability
54+
if (areChartsAvailable === null) {
55+
return <QueriesActivitySkeleton />;
56+
}
57+
58+
// Render compact alert-style mode when charts are not available
59+
if (areChartsAvailable === false) {
60+
return (
61+
<QueriesActivityAlert
62+
runningQueriesCount={runningQueriesCount}
63+
uniqueApplications={uniqueApplications}
64+
uniqueUsers={uniqueUsers}
65+
/>
66+
);
67+
}
68+
69+
// Render expandable mode when charts are available
13570
return (
136-
<div className={b({expanded})} style={{display: isActivityBarHidden ? 'none' : undefined}}>
71+
<div className={b({expanded})}>
13772
<Card className={b('card')} type="container" view={expanded ? 'outlined' : 'raised'}>
13873
<div className={b('header')} onClick={handleToggleExpanded}>
13974
<Flex justifyContent="space-between" className={b('content-wrapper')}>
@@ -146,36 +81,36 @@ export function QueriesActivityBar({tenantName}: QueriesActivityBarProps) {
14681
</Text>
14782
</Flex>
14883

149-
<div className={b('metrics')}>
150-
<Label
151-
theme={runningQueriesCount > 0 ? 'success' : 'unknown'}
152-
size="s"
153-
icon={<Icon data={CirclePlay} size={14} />}
154-
>
155-
{runningQueriesCount}
156-
</Label>
157-
158-
<Label
159-
theme="clear"
160-
size="s"
161-
icon={<Icon data={Rocket} size={14} />}
162-
value={formatTrendValue(qps.trend.value)}
163-
>
164-
{i18n('value_per-sec', {count: qps.value})}
165-
</Label>
166-
167-
<Label
168-
theme="clear"
169-
size="s"
170-
icon={<Icon data={Clock} size={14} />}
171-
value={formatTrendValue(latency.trend.value)}
172-
>
173-
{i18n('value_ms', {time: latency.value})}
174-
</Label>
175-
</div>
84+
<Flex alignItems="center" gap={4} className={b('header-metrics')}>
85+
<div className={b('metrics')}>
86+
<Label
87+
theme={runningQueriesCount > 0 ? 'success' : 'unknown'}
88+
size="s"
89+
icon={<Icon data={CirclePlay} size={14} />}
90+
>
91+
{runningQueriesCount}
92+
</Label>
93+
<Label
94+
theme="unknown"
95+
icon={<Icon data={Rocket} />}
96+
size="s"
97+
value={formatTrendValue(qps?.trend?.value ?? 0)}
98+
>
99+
{i18n('value_per-sec', {count: qps?.value ?? '0'})}
100+
</Label>
101+
<Label
102+
theme="unknown"
103+
icon={<Icon data={Clock} />}
104+
size="s"
105+
value={formatTrendValue(latency?.trend?.value ?? 0)}
106+
>
107+
{i18n('value_ms', {time: latency?.value ?? '0'})}
108+
</Label>
109+
</div>
110+
111+
<ArrowToggle direction={expanded ? 'top' : 'bottom'} size={16} />
112+
</Flex>
176113
</Flex>
177-
178-
<ArrowToggle direction={expanded ? 'top' : 'bottom'} />
179114
</div>
180115

181116
{expanded && (
@@ -208,22 +143,20 @@ export function QueriesActivityBar({tenantName}: QueriesActivityBarProps) {
208143
{i18n('field_users')}
209144
</Label>
210145

211-
<Button
212-
view="outlined"
213-
size="s"
214-
onClick={handleOpenRunningQueries}
215-
className={b('open-queries-button')}
216-
>
217-
{i18n('action_open-running-queries')}
218-
</Button>
146+
{runningQueriesCount > 0 && (
147+
<Button
148+
view="outlined"
149+
size="s"
150+
onClick={handleOpenRunningQueries}
151+
className={b('open-queries-button')}
152+
>
153+
{i18n('action_open-running-queries')}
154+
</Button>
155+
)}
219156
</div>
220157
</div>
221158
)}
222-
<QueriesActivityCharts
223-
tenantName={tenantName}
224-
expanded={expanded}
225-
onChartDataStatusChange={handleChartDataStatusChange}
226-
/>
159+
<QueriesActivityCharts tenantName={tenantName} expanded={expanded} />
227160
</Card>
228161
</div>
229162
);

0 commit comments

Comments
 (0)