Skip to content

Commit 0ba2eee

Browse files
ui: Query state utilization metrics support (#6053)
* Query State utilization metrics support * Linter fixes * [pre-commit.ci lite] apply automatic fixes --------- Co-authored-by: pre-commit-ci-lite[bot] <117423508+pre-commit-ci-lite[bot]@users.noreply.github.com>
1 parent bedae13 commit 0ba2eee

File tree

6 files changed

+56
-41
lines changed

6 files changed

+56
-41
lines changed

ui/packages/shared/profile/src/MetricsGraph/UtilizationMetrics/index.tsx

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -201,7 +201,10 @@ const RawUtilizationMetrics = ({
201201

202202
return (
203203
<MetricsGraph
204-
data={data}
204+
data={data.map((val, idx) => ({
205+
...val,
206+
highlighted: originalData?.[idx]?.isSelected ?? false,
207+
}))}
205208
from={from}
206209
to={to}
207210
setTimeRange={setTimeRange}

ui/packages/shared/profile/src/MetricsGraph/index.tsx

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,7 @@ export interface HighlightedSeries {
6363
export interface Series {
6464
id: string; // opaque string used to determine line color
6565
values: Array<[number, number]>; // [timestamp_ms, value]
66+
highlighted?: boolean;
6667
}
6768

6869
const MetricsGraph = ({
@@ -200,7 +201,10 @@ export const RawMetricsGraph = ({
200201
);
201202

202203
const closestPoint = useMemo(() => {
203-
// Return the closest point as the highlighted point
204+
// Guard against empty series
205+
if (series.length === 0) {
206+
return null;
207+
}
204208

205209
const closestPointPerSeries = series.map(function (s) {
206210
const distances = s.values.map(d => {
@@ -530,7 +534,8 @@ export const RawMetricsGraph = ({
530534
line={l}
531535
color={color(s.id)}
532536
strokeWidth={
533-
hovering && highlighted != null && i === highlighted.seriesIndex
537+
(hovering && highlighted != null && i === highlighted.seriesIndex) ||
538+
s.highlighted === true
534539
? lineStrokeHover
535540
: lineStroke
536541
}

ui/packages/shared/profile/src/ProfileSelector/MetricsGraphSection.tsx

Lines changed: 17 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -174,25 +174,23 @@ export function MetricsGraphSection({
174174
name !== 'gpu_pcie_throughput_receive_bytes'
175175
) {
176176
return (
177-
<>
178-
<UtilizationMetricsGraph
179-
key={name}
180-
data={data}
181-
setTimeRange={handleTimeRangeChange}
182-
utilizationMetricsLoading={utilizationMetricsLoading}
183-
humanReadableName={humanReadableName}
184-
from={querySelection.from}
185-
to={querySelection.to}
186-
yAxisUnit="percentage"
187-
addLabelMatcher={addLabelMatcher}
188-
onSeriesClick={seriesIndex => {
189-
// For generic UtilizationMetrics, just pass the series index
190-
if (onUtilizationSeriesSelect != null) {
191-
onUtilizationSeriesSelect(seriesIndex);
192-
}
193-
}}
194-
/>
195-
</>
177+
<UtilizationMetricsGraph
178+
key={name}
179+
data={data}
180+
setTimeRange={handleTimeRangeChange}
181+
utilizationMetricsLoading={utilizationMetricsLoading}
182+
humanReadableName={humanReadableName}
183+
from={querySelection.from}
184+
to={querySelection.to}
185+
yAxisUnit="percentage"
186+
addLabelMatcher={addLabelMatcher}
187+
onSeriesClick={seriesIndex => {
188+
// For generic UtilizationMetrics, just pass the series index
189+
if (onUtilizationSeriesSelect != null) {
190+
onUtilizationSeriesSelect(seriesIndex);
191+
}
192+
}}
193+
/>
196194
);
197195
}
198196
return null;

ui/packages/shared/profile/src/ProfileSelector/index.tsx

Lines changed: 25 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ import {
2222
useGrpcMetadata,
2323
useParcaContext,
2424
useURLState,
25+
useURLStateBatch,
2526
} from '@parca/components';
2627
import {CloseIcon} from '@parca/icons';
2728
import {Query} from '@parca/parser';
@@ -92,6 +93,7 @@ interface ProfileSelectorProps extends ProfileSelectorFeatures {
9293
utilizationMetricsLoading?: boolean;
9394
utilizationLabels?: UtilizationLabels;
9495
onUtilizationSeriesSelect?: (seriesIndex: number) => void;
96+
onSearchHook?: () => void;
9597
}
9698

9799
export interface IProfileTypesResult {
@@ -144,10 +146,12 @@ const ProfileSelector = ({
144146
utilizationMetricsLoading,
145147
utilizationLabels,
146148
onUtilizationSeriesSelect,
149+
onSearchHook,
147150
}: ProfileSelectorProps): JSX.Element => {
148151
const {heightStyle} = useMetricsGraphDimensions(comparing, utilizationMetrics != null);
149152
const {viewComponent} = useParcaContext();
150153
const [queryBrowserMode, setQueryBrowserMode] = useURLState('query_browser_mode');
154+
const batchUpdates = useURLStateBatch();
151155

152156
// Use the new useQueryState hook - reads directly from URL params
153157
const {
@@ -227,22 +231,27 @@ const ProfileSelector = ({
227231
const selectedProfileName = query.profileName();
228232

229233
const setQueryExpression = (updateTs = false): void => {
230-
// When updateTs is true, re-evaluate the time range to current values
231-
if (updateTs) {
232-
// Force re-evaluation of time range (important for relative ranges like "last 15 minutes")
233-
const currentFrom = timeRangeSelection.getFromMs(true);
234-
const currentTo = timeRangeSelection.getToMs(true);
235-
const currentRangeKey = timeRangeSelection.getRangeKey();
236-
// Commit with refreshed time range
237-
commitDraft({
238-
from: currentFrom,
239-
to: currentTo,
240-
timeSelection: currentRangeKey,
241-
});
242-
} else {
243-
// Commit the draft with existing values
244-
commitDraft();
245-
}
234+
batchUpdates(() => {
235+
if (onSearchHook != null) {
236+
onSearchHook();
237+
}
238+
// When updateTs is true, re-evaluate the time range to current values
239+
if (updateTs) {
240+
// Force re-evaluation of time range (important for relative ranges like "last 15 minutes")
241+
const currentFrom = timeRangeSelection.getFromMs(true);
242+
const currentTo = timeRangeSelection.getToMs(true);
243+
const currentRangeKey = timeRangeSelection.getRangeKey();
244+
// Commit with refreshed time range
245+
commitDraft({
246+
from: currentFrom,
247+
to: currentTo,
248+
timeSelection: currentRangeKey,
249+
});
250+
} else {
251+
// Commit the draft with existing values
252+
commitDraft();
253+
}
254+
});
246255
};
247256

248257
const setMatchersString = (matchers: string): void => {

ui/packages/shared/profile/src/hooks/useQueryState.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -150,7 +150,6 @@ export const useQueryState = (options: UseQueryStateOptions = {}): UseQueryState
150150
draftTo !== '' ? parseInt(draftTo) : defaultTo
151151
);
152152
}, [draftTimeSelection, draftFrom, draftTo, defaultTimeSelection, defaultFrom, defaultTo]);
153-
154153
// Use combined sumBy hook for fetching labels and computing defaults (based on committed state)
155154
const {sumBy: computedSumByFromURL, isLoading: sumBySelectionLoading} = useSumBy(
156155
queryClient,
@@ -201,12 +200,13 @@ export const useQueryState = (options: UseQueryStateOptions = {}): UseQueryState
201200
? (BigInt(draftTimeRange.getToMs()) * 1_000_000n).toString()
202201
: undefined;
203202

203+
const finalSumBy = draftSumBy ?? computedSumByFromURL;
204204
return {
205205
expression: draftExpression ?? defaultExpression,
206206
from: draftTimeRange.getFromMs(),
207207
to: draftTimeRange.getToMs(),
208208
timeSelection: draftTimeRange.getRangeKey(),
209-
sumBy: draftSumBy ?? computedSumByFromURL, // Use draft if set, otherwise fallback to computed
209+
sumBy: finalSumBy, // Use draft if set, otherwise fallback to computed
210210
...(draftMergeFrom !== undefined &&
211211
draftMergeFrom !== '' &&
212212
draftMergeTo !== undefined &&

ui/packages/shared/profile/src/useSumBy.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -145,7 +145,7 @@ const getSumByFromParam = (param: string | string[] | undefined): string[] | und
145145
}
146146

147147
if (param === '__none__') {
148-
return [];
148+
return DEFAULT_EMPTY_SUM_BY;
149149
}
150150

151151
if (typeof param === 'string') {

0 commit comments

Comments
 (0)