@parca/profile 0.16.466 → 0.16.468

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.
Files changed (32) hide show
  1. package/CHANGELOG.md +8 -0
  2. package/dist/MatchersInput/SuggestionsList.d.ts +2 -1
  3. package/dist/MatchersInput/SuggestionsList.d.ts.map +1 -1
  4. package/dist/MatchersInput/SuggestionsList.js +6 -2
  5. package/dist/MatchersInput/index.d.ts +4 -2
  6. package/dist/MatchersInput/index.d.ts.map +1 -1
  7. package/dist/MatchersInput/index.js +51 -22
  8. package/dist/ProfileIcicleGraph/IcicleGraphArrow/IcicleChartRootNode.js +2 -2
  9. package/dist/ProfileSelector/index.d.ts +7 -1
  10. package/dist/ProfileSelector/index.d.ts.map +1 -1
  11. package/dist/ProfileSelector/index.js +3 -2
  12. package/dist/SimpleMatchers/index.d.ts +4 -2
  13. package/dist/SimpleMatchers/index.d.ts.map +1 -1
  14. package/dist/SimpleMatchers/index.js +46 -32
  15. package/dist/contexts/MatchersInputLabelsContext.d.ts +25 -0
  16. package/dist/contexts/MatchersInputLabelsContext.d.ts.map +1 -0
  17. package/dist/contexts/MatchersInputLabelsContext.js +75 -0
  18. package/dist/contexts/SimpleMatchersLabelContext.d.ts +17 -0
  19. package/dist/contexts/SimpleMatchersLabelContext.d.ts.map +1 -0
  20. package/dist/contexts/SimpleMatchersLabelContext.js +45 -0
  21. package/dist/contexts/UtilizationLabelsContext.d.ts +14 -0
  22. package/dist/contexts/UtilizationLabelsContext.d.ts.map +1 -0
  23. package/dist/contexts/UtilizationLabelsContext.js +25 -0
  24. package/package.json +2 -2
  25. package/src/MatchersInput/SuggestionsList.tsx +9 -2
  26. package/src/MatchersInput/index.tsx +71 -27
  27. package/src/ProfileIcicleGraph/IcicleGraphArrow/IcicleChartRootNode.tsx +2 -2
  28. package/src/ProfileSelector/index.tsx +65 -54
  29. package/src/SimpleMatchers/index.tsx +65 -33
  30. package/src/contexts/MatchersInputLabelsContext.tsx +130 -0
  31. package/src/contexts/SimpleMatchersLabelContext.tsx +78 -0
  32. package/src/contexts/UtilizationLabelsContext.tsx +44 -0
@@ -0,0 +1,45 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ // Copyright 2022 The Parca Authors
3
+ // Licensed under the Apache License, Version 2.0 (the "License");
4
+ // you may not use this file except in compliance with the License.
5
+ // You may obtain a copy of the License at
6
+ //
7
+ // http://www.apache.org/licenses/LICENSE-2.0
8
+ //
9
+ // Unless required by applicable law or agreed to in writing, software
10
+ // distributed under the License is distributed on an "AS IS" BASIS,
11
+ // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ // See the License for the specific language governing permissions and
13
+ // limitations under the License.
14
+ import { createContext, useContext, useMemo } from 'react';
15
+ import { useLabelNames } from '../MatchersInput';
16
+ import { transformLabelsForSelect } from '../SimpleMatchers';
17
+ import { useUtilizationLabels } from './UtilizationLabelsContext';
18
+ const LabelContext = createContext(null);
19
+ // With there being the possibility of having utilization labels, we need to be able to determine whether the labels to be used are utilization labels or profiling data labels.
20
+ // This context is used to determine this.
21
+ export function LabelProvider({ children, queryClient, profileType, labelNameFromMatchers, }) {
22
+ const utilizationLabels = useUtilizationLabels();
23
+ const { loading, result } = useLabelNames(queryClient, profileType);
24
+ const value = useMemo(() => {
25
+ const baseLabels = result.error != null ? [] : result.response?.labelNames.filter(e => e !== '__name__') ?? [];
26
+ const labelsToUse = utilizationLabels?.utilizationLabelNames?.length !== undefined
27
+ ? utilizationLabels?.utilizationLabelNames ?? []
28
+ : baseLabels;
29
+ const uniqueLabels = Array.from(new Set([...labelsToUse, ...labelNameFromMatchers]));
30
+ const shouldTrimPrefix = Boolean(utilizationLabels?.utilizationLabelNames);
31
+ return {
32
+ labelNameOptions: transformLabelsForSelect(uniqueLabels, shouldTrimPrefix),
33
+ isLoading: loading,
34
+ error: result.error ?? null,
35
+ };
36
+ }, [result, loading, utilizationLabels, labelNameFromMatchers]);
37
+ return _jsx(LabelContext.Provider, { value: value, children: children });
38
+ }
39
+ export function useLabels() {
40
+ const context = useContext(LabelContext);
41
+ if (context === null) {
42
+ throw new Error('useLabels must be used within a LabelProvider');
43
+ }
44
+ return context;
45
+ }
@@ -0,0 +1,14 @@
1
+ import { ReactNode } from 'react';
2
+ export interface UtilizationLabels {
3
+ utilizationLabelNames?: string[];
4
+ utilizationFetchLabelValues?: (key: string) => Promise<string[]>;
5
+ utilizationLabelValues?: string[];
6
+ }
7
+ interface UtilizationLabelsProviderProps {
8
+ children: ReactNode;
9
+ value: UtilizationLabels | undefined;
10
+ }
11
+ export declare function UtilizationLabelsProvider({ children, value, }: UtilizationLabelsProviderProps): JSX.Element;
12
+ export declare function useUtilizationLabels(): UtilizationLabels | undefined;
13
+ export {};
14
+ //# sourceMappingURL=UtilizationLabelsContext.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"UtilizationLabelsContext.d.ts","sourceRoot":"","sources":["../../src/contexts/UtilizationLabelsContext.tsx"],"names":[],"mappings":"AAaA,OAAO,EAAC,SAAS,EAA4B,MAAM,OAAO,CAAC;AAE3D,MAAM,WAAW,iBAAiB;IAChC,qBAAqB,CAAC,EAAE,MAAM,EAAE,CAAC;IACjC,2BAA2B,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,OAAO,CAAC,MAAM,EAAE,CAAC,CAAC;IACjE,sBAAsB,CAAC,EAAE,MAAM,EAAE,CAAC;CACnC;AAED,UAAU,8BAA8B;IACtC,QAAQ,EAAE,SAAS,CAAC;IACpB,KAAK,EAAE,iBAAiB,GAAG,SAAS,CAAC;CACtC;AAOD,wBAAgB,yBAAyB,CAAC,EACxC,QAAQ,EACR,KAAK,GACN,EAAE,8BAA8B,GAAG,GAAG,CAAC,OAAO,CAI9C;AAED,wBAAgB,oBAAoB,IAAI,iBAAiB,GAAG,SAAS,CAGpE"}
@@ -0,0 +1,25 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ // Copyright 2022 The Parca Authors
3
+ // Licensed under the Apache License, Version 2.0 (the "License");
4
+ // you may not use this file except in compliance with the License.
5
+ // You may obtain a copy of the License at
6
+ //
7
+ // http://www.apache.org/licenses/LICENSE-2.0
8
+ //
9
+ // Unless required by applicable law or agreed to in writing, software
10
+ // distributed under the License is distributed on an "AS IS" BASIS,
11
+ // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ // See the License for the specific language governing permissions and
13
+ // limitations under the License.
14
+ import { createContext, useContext } from 'react';
15
+ // The UtilizationLabelsContext is used to store the utilization label names and values. It also
16
+ // contains the function utilizationFetchLabelValues to fetch the utilization label values.
17
+ // This context was created so as to avoid props drilling.
18
+ const UtilizationLabelsContext = createContext(undefined);
19
+ export function UtilizationLabelsProvider({ children, value, }) {
20
+ return (_jsx(UtilizationLabelsContext.Provider, { value: value, children: children }));
21
+ }
22
+ export function useUtilizationLabels() {
23
+ const context = useContext(UtilizationLabelsContext);
24
+ return context;
25
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@parca/profile",
3
- "version": "0.16.466",
3
+ "version": "0.16.468",
4
4
  "description": "Profile viewing libraries",
5
5
  "dependencies": {
6
6
  "@headlessui/react": "^1.7.19",
@@ -75,5 +75,5 @@
75
75
  "access": "public",
76
76
  "registry": "https://registry.npmjs.org/"
77
77
  },
78
- "gitHead": "52e2c4c6308b962fd0b4d87acc9b0dc4614812f3"
78
+ "gitHead": "1c8eeeff45c162e0c93a59b2cab77f21615e5397"
79
79
  }
@@ -52,6 +52,7 @@ interface Props {
52
52
  focusedInput: boolean;
53
53
  isLabelNamesLoading: boolean;
54
54
  isLabelValuesLoading: boolean;
55
+ shouldTrimPrefix: boolean;
55
56
  }
56
57
 
57
58
  const LoadingSpinner = (): JSX.Element => {
@@ -60,6 +61,11 @@ const LoadingSpinner = (): JSX.Element => {
60
61
  return <div className="pt-2 pb-4">{Spinner}</div>;
61
62
  };
62
63
 
64
+ const transformLabelsForSuggestions = (labelNames: string, shouldTrimPrefix = false): string => {
65
+ const trimmedLabel = shouldTrimPrefix ? labelNames.split('.').pop() ?? labelNames : labelNames;
66
+ return trimmedLabel;
67
+ };
68
+
63
69
  const SuggestionsList = ({
64
70
  suggestions,
65
71
  applySuggestion,
@@ -68,6 +74,7 @@ const SuggestionsList = ({
68
74
  focusedInput,
69
75
  isLabelNamesLoading,
70
76
  isLabelValuesLoading,
77
+ shouldTrimPrefix = false,
71
78
  }: Props): JSX.Element => {
72
79
  const [popperElement, setPopperElement] = useState<HTMLDivElement | null>(null);
73
80
  const {styles, attributes} = usePopper(inputRef, popperElement, {
@@ -234,8 +241,8 @@ const SuggestionsList = ({
234
241
  onHighlight={() => setHighlightedSuggestionIndex(i)}
235
242
  onApplySuggestion={() => applySuggestionWithIndex(i)}
236
243
  onResetHighlight={() => resetHighlight()}
237
- value={l.value}
238
- key={l.value}
244
+ value={transformLabelsForSuggestions(l.value, shouldTrimPrefix)}
245
+ key={transformLabelsForSuggestions(l.value, shouldTrimPrefix)}
239
246
  />
240
247
  ))}
241
248
  </>
@@ -13,6 +13,7 @@
13
13
 
14
14
  import React, {useMemo, useRef, useState} from 'react';
15
15
 
16
+ import {useQuery} from '@tanstack/react-query';
16
17
  import cx from 'classnames';
17
18
  import TextareaAutosize from 'react-textarea-autosize';
18
19
 
@@ -21,6 +22,8 @@ import {useGrpcMetadata} from '@parca/components';
21
22
  import {Query} from '@parca/parser';
22
23
  import {millisToProtoTimestamp, sanitizeLabelValue} from '@parca/utilities';
23
24
 
25
+ import {UtilizationLabels} from '../ProfileSelector';
26
+ import {LabelsProvider, useLabels} from '../contexts/MatchersInputLabelsContext';
24
27
  import useGrpcQuery from '../useGrpcQuery';
25
28
  import SuggestionsList, {Suggestion, Suggestions} from './SuggestionsList';
26
29
 
@@ -111,33 +114,39 @@ export const useLabelValues = (
111
114
  return {result: {response: data ?? [], error: error as Error}, loading: isLoading};
112
115
  };
113
116
 
117
+ export const useFetchUtilizationLabelValues = (
118
+ labelName: string,
119
+ utilizationLabels?: UtilizationLabels
120
+ ): string[] => {
121
+ const {data} = useQuery({
122
+ queryKey: ['utilizationLabelValues', labelName],
123
+ queryFn: async () => {
124
+ return await utilizationLabels?.utilizationFetchLabelValues?.(labelName);
125
+ },
126
+ });
127
+
128
+ return data ?? [];
129
+ };
130
+
114
131
  const MatchersInput = ({
115
- queryClient,
116
132
  setMatchersString,
117
133
  runQuery,
118
134
  currentQuery,
119
- profileType,
120
135
  }: MatchersInputProps): JSX.Element => {
121
136
  const inputRef = useRef<HTMLTextAreaElement | null>(null);
122
137
  const [focusedInput, setFocusedInput] = useState(false);
123
138
  const [lastCompleted, setLastCompleted] = useState<Suggestion>(new Suggestion('', '', ''));
124
- const [currentLabelName, setCurrentLabelName] = useState<string | null>(null);
125
-
126
- const {loading: labelNamesLoading, result} = useLabelNames(queryClient, profileType);
127
- const {response: labelNamesResponse, error: labelNamesError} = result;
128
139
 
129
140
  const {
130
- loading: labelValuesLoading,
131
- result: {response: labelValues},
132
- } = useLabelValues(queryClient, currentLabelName ?? '', profileType);
133
-
134
- const labelNames = useMemo(() => {
135
- return (labelNamesError === undefined || labelNamesError == null) &&
136
- labelNamesResponse !== undefined &&
137
- labelNamesResponse != null
138
- ? labelNamesResponse.labelNames.filter(e => e !== '__name__')
139
- : [];
140
- }, [labelNamesError, labelNamesResponse]);
141
+ labelNames,
142
+ labelValues,
143
+ labelNameMappings,
144
+ isLabelNamesLoading,
145
+ isLabelValuesLoading,
146
+ currentLabelName,
147
+ setCurrentLabelName,
148
+ shouldHandlePrefixes,
149
+ } = useLabels();
141
150
 
142
151
  const value = currentQuery.matchersString();
143
152
 
@@ -168,18 +177,36 @@ const MatchersInput = ({
168
177
  return label.toLowerCase().slice(0, inputLength) === inputValue;
169
178
  });
170
179
 
171
- matches.forEach(m =>
172
- suggestionSections.labelNames.push({
180
+ matches.forEach(m => {
181
+ const suggestion = {
173
182
  type: s.type,
174
183
  typeahead: s.typeahead,
175
184
  value: m,
176
- })
177
- );
185
+ };
186
+
187
+ if (shouldHandlePrefixes) {
188
+ const mapping = labelNameMappings.find(l => l.displayName === m);
189
+ if (mapping != null) {
190
+ (suggestion as any).fullName = mapping.fullName;
191
+ }
192
+ }
193
+
194
+ suggestionSections.labelNames.push(suggestion);
195
+ });
178
196
  }
179
197
 
180
198
  if (s.type === 'labelValue') {
181
- if (currentLabelName === null || s.labelName !== currentLabelName) {
182
- setCurrentLabelName(s.labelName);
199
+ let labelNameForQuery = s.labelName;
200
+
201
+ if (shouldHandlePrefixes) {
202
+ const mapping = labelNameMappings.find(l => l.displayName === s.labelName);
203
+ if (mapping != null) {
204
+ labelNameForQuery = mapping.fullName;
205
+ }
206
+ }
207
+
208
+ if (currentLabelName === null || labelNameForQuery !== currentLabelName) {
209
+ setCurrentLabelName(labelNameForQuery);
183
210
  return;
184
211
  }
185
212
 
@@ -197,7 +224,17 @@ const MatchersInput = ({
197
224
  }
198
225
  });
199
226
  return suggestionSections;
200
- }, [currentQuery, lastCompleted, labelNames, labelValues, currentLabelName, value]);
227
+ }, [
228
+ currentQuery,
229
+ lastCompleted,
230
+ labelNames,
231
+ labelValues,
232
+ currentLabelName,
233
+ value,
234
+ shouldHandlePrefixes,
235
+ labelNameMappings,
236
+ setCurrentLabelName,
237
+ ]);
201
238
 
202
239
  const resetLastCompleted = (): void => setLastCompleted(new Suggestion('', '', ''));
203
240
 
@@ -269,16 +306,23 @@ const MatchersInput = ({
269
306
  id="matchers-input"
270
307
  />
271
308
  <SuggestionsList
272
- isLabelNamesLoading={labelNamesLoading}
309
+ isLabelNamesLoading={isLabelNamesLoading}
273
310
  suggestions={suggestionSections}
274
311
  applySuggestion={applySuggestion}
275
312
  inputRef={inputRef.current}
276
313
  runQuery={runQuery}
277
314
  focusedInput={focusedInput}
278
- isLabelValuesLoading={labelValuesLoading && lastCompleted.type === 'literal'}
315
+ isLabelValuesLoading={isLabelValuesLoading && lastCompleted.type === 'literal'}
316
+ shouldTrimPrefix={shouldHandlePrefixes}
279
317
  />
280
318
  </div>
281
319
  );
282
320
  };
283
321
 
284
- export default MatchersInput;
322
+ export default function MatchersInputWithProvider(props: MatchersInputProps): JSX.Element {
323
+ return (
324
+ <LabelsProvider queryClient={props.queryClient} profileType={props.profileType}>
325
+ <MatchersInput {...props} />
326
+ </LabelsProvider>
327
+ );
328
+ }
@@ -87,9 +87,9 @@ export const IcicleChartRootNode = React.memo(function IcicleChartRootNodeNonMem
87
87
  groupByMetadata?.get(row) as StructRow<Record<string, Binary>>
88
88
  ).toJSON();
89
89
 
90
- const tsStr = arrowToString(groupByFields.timestamp) as string;
90
+ const tsStr = arrowToString(groupByFields.time_nanos) as string;
91
91
 
92
- const tsNanos = BigInt(parseInt(tsStr, 10)) * 1000000n;
92
+ const tsNanos = BigInt(parseInt(tsStr, 10));
93
93
  const durationStr = arrowToString(groupByFields.duration) as string;
94
94
  const duration = parseInt(durationStr, 10);
95
95
 
@@ -30,6 +30,7 @@ import {type NavigateFunction} from '@parca/utilities';
30
30
  import {ProfileSelection} from '..';
31
31
  import {useLabelNames} from '../MatchersInput/index';
32
32
  import {useMetricsGraphDimensions} from '../MetricsGraph/useMetricsGraphDimensions';
33
+ import {UtilizationLabelsProvider} from '../contexts/UtilizationLabelsContext';
33
34
  import {useDefaultSumBy, useSumBySelection} from '../useSumBy';
34
35
  import {MetricsGraphSection} from './MetricsGraphSection';
35
36
  import {QueryControls} from './QueryControls';
@@ -64,6 +65,12 @@ export interface UtilizationMetrics {
64
65
  };
65
66
  }
66
67
 
68
+ export interface UtilizationLabels {
69
+ utilizationLabelNames?: string[];
70
+ utilizationFetchLabelValues?: (key: string) => Promise<string[]>;
71
+ utilizationLabelValues?: string[];
72
+ }
73
+
67
74
  interface ProfileSelectorProps extends ProfileSelectorFeatures {
68
75
  queryClient: QueryServiceClient;
69
76
  querySelection: QuerySelection;
@@ -78,6 +85,7 @@ interface ProfileSelectorProps extends ProfileSelectorFeatures {
78
85
  suffix?: string;
79
86
  utilizationMetrics?: UtilizationMetrics[];
80
87
  utilizationMetricsLoading?: boolean;
88
+ utilizationLabels?: UtilizationLabels;
81
89
  }
82
90
 
83
91
  export interface IProfileTypesResult {
@@ -123,6 +131,7 @@ const ProfileSelector = ({
123
131
  setDisplayHideMetricsGraphButton,
124
132
  utilizationMetrics,
125
133
  utilizationMetricsLoading,
134
+ utilizationLabels,
126
135
  }: ProfileSelectorProps): JSX.Element => {
127
136
  const {
128
137
  loading: profileTypesLoading,
@@ -268,62 +277,64 @@ const ProfileSelector = ({
268
277
  const sumByRef = useRef(null);
269
278
 
270
279
  return (
271
- <>
272
- <div className="mb-2 flex">
273
- <QueryControls
274
- showProfileTypeSelector={showProfileTypeSelector}
275
- showSumBySelector={showSumBySelector}
276
- disableExplorativeQuerying={disableExplorativeQuerying}
277
- profileTypesData={profileTypesData}
278
- profileTypesLoading={profileTypesLoading}
279
- selectedProfileName={selectedProfileName}
280
- setProfileName={setProfileName}
281
- setMatchersString={setMatchersString}
282
- setQueryExpression={setQueryExpression}
283
- query={query}
284
- queryBrowserRef={queryBrowserRef}
285
- timeRangeSelection={timeRangeSelection}
286
- setTimeRangeSelection={setTimeRangeSelection}
287
- searchDisabled={searchDisabled}
288
- queryBrowserMode={queryBrowserMode as string}
289
- setQueryBrowserMode={setQueryBrowserMode}
290
- advancedModeForQueryBrowser={advancedModeForQueryBrowser}
291
- setAdvancedModeForQueryBrowser={setAdvancedModeForQueryBrowser}
280
+ <UtilizationLabelsProvider value={utilizationLabels}>
281
+ <>
282
+ <div className="mb-2 flex">
283
+ <QueryControls
284
+ showProfileTypeSelector={showProfileTypeSelector}
285
+ showSumBySelector={showSumBySelector}
286
+ disableExplorativeQuerying={disableExplorativeQuerying}
287
+ profileTypesData={profileTypesData}
288
+ profileTypesLoading={profileTypesLoading}
289
+ selectedProfileName={selectedProfileName}
290
+ setProfileName={setProfileName}
291
+ setMatchersString={setMatchersString}
292
+ setQueryExpression={setQueryExpression}
293
+ query={query}
294
+ queryBrowserRef={queryBrowserRef}
295
+ timeRangeSelection={timeRangeSelection}
296
+ setTimeRangeSelection={setTimeRangeSelection}
297
+ searchDisabled={searchDisabled}
298
+ queryBrowserMode={queryBrowserMode as string}
299
+ setQueryBrowserMode={setQueryBrowserMode}
300
+ advancedModeForQueryBrowser={advancedModeForQueryBrowser}
301
+ setAdvancedModeForQueryBrowser={setAdvancedModeForQueryBrowser}
302
+ queryClient={queryClient}
303
+ sumByRef={sumByRef}
304
+ labels={labels}
305
+ sumBySelection={sumBySelection ?? []}
306
+ setUserSumBySelection={setUserSumBySelection}
307
+ profileType={profileType}
308
+ profileTypesError={error}
309
+ />
310
+ {comparing && (
311
+ <div>
312
+ <IconButton onClick={() => closeProfile()} icon={<CloseIcon />} />
313
+ </div>
314
+ )}
315
+ </div>
316
+ <MetricsGraphSection
317
+ showMetricsGraph={showMetricsGraph}
318
+ setDisplayHideMetricsGraphButton={setDisplayHideMetricsGraphButton}
319
+ heightStyle={heightStyle}
320
+ querySelection={querySelection}
321
+ profileSelection={profileSelection}
322
+ comparing={comparing}
323
+ sumBy={querySelection.sumBy ?? defaultSumBy ?? []}
324
+ defaultSumByLoading={defaultSumByLoading}
292
325
  queryClient={queryClient}
293
- sumByRef={sumByRef}
294
- labels={labels}
295
- sumBySelection={sumBySelection ?? []}
296
- setUserSumBySelection={setUserSumBySelection}
297
- profileType={profileType}
298
- profileTypesError={error}
326
+ queryExpressionString={queryExpressionString}
327
+ setTimeRangeSelection={setTimeRangeSelection}
328
+ selectQuery={selectQuery}
329
+ selectProfile={selectProfile}
330
+ query={query}
331
+ setQueryExpression={setQueryExpression}
332
+ setNewQueryExpression={setNewQueryExpression}
333
+ utilizationMetrics={utilizationMetrics}
334
+ utilizationMetricsLoading={utilizationMetricsLoading}
299
335
  />
300
- {comparing && (
301
- <div>
302
- <IconButton onClick={() => closeProfile()} icon={<CloseIcon />} />
303
- </div>
304
- )}
305
- </div>
306
- <MetricsGraphSection
307
- showMetricsGraph={showMetricsGraph}
308
- setDisplayHideMetricsGraphButton={setDisplayHideMetricsGraphButton}
309
- heightStyle={heightStyle}
310
- querySelection={querySelection}
311
- profileSelection={profileSelection}
312
- comparing={comparing}
313
- sumBy={querySelection.sumBy ?? defaultSumBy ?? []}
314
- defaultSumByLoading={defaultSumByLoading}
315
- queryClient={queryClient}
316
- queryExpressionString={queryExpressionString}
317
- setTimeRangeSelection={setTimeRangeSelection}
318
- selectQuery={selectQuery}
319
- selectProfile={selectProfile}
320
- query={query}
321
- setQueryExpression={setQueryExpression}
322
- setNewQueryExpression={setNewQueryExpression}
323
- utilizationMetrics={utilizationMetrics}
324
- utilizationMetricsLoading={utilizationMetricsLoading}
325
- />
326
- </>
336
+ </>
337
+ </UtilizationLabelsProvider>
327
338
  );
328
339
  };
329
340
 
@@ -22,7 +22,8 @@ import {useGrpcMetadata} from '@parca/components';
22
22
  import {Query} from '@parca/parser';
23
23
  import {sanitizeLabelValue} from '@parca/utilities';
24
24
 
25
- import {useLabelNames} from '../MatchersInput';
25
+ import {LabelProvider, useLabels} from '../contexts/SimpleMatchersLabelContext';
26
+ import {useUtilizationLabels} from '../contexts/UtilizationLabelsContext';
26
27
  import Select, {type SelectItem} from './Select';
27
28
 
28
29
  interface Props {
@@ -42,10 +43,16 @@ interface QueryRow {
42
43
  isLoading: boolean;
43
44
  }
44
45
 
45
- const transformLabelsForSelect = (labelNames: string[]): SelectItem[] => {
46
+ export const transformLabelsForSelect = (
47
+ labelNames: string[],
48
+ shouldTrimPrefix = false
49
+ ): SelectItem[] => {
46
50
  return labelNames.map(labelName => ({
47
51
  key: labelName,
48
- element: {active: <>{labelName}</>, expanded: <>{labelName}</>},
52
+ element: {
53
+ active: <>{shouldTrimPrefix ? labelName.split('.').pop() : labelName}</>,
54
+ expanded: <>{shouldTrimPrefix ? labelName.split('.').pop() : labelName}</>,
55
+ },
49
56
  }));
50
57
  };
51
58
 
@@ -99,19 +106,17 @@ const operatorOptions = [
99
106
  const SimpleMatchers = ({
100
107
  queryClient,
101
108
  setMatchersString,
102
- // runQuery,
103
109
  currentQuery,
104
110
  profileType,
105
111
  queryBrowserRef,
106
112
  }: Props): JSX.Element => {
113
+ const utilizationLabels = useUtilizationLabels();
107
114
  const [queryRows, setQueryRows] = useState<QueryRow[]>([
108
115
  {labelName: '', operator: '=', labelValue: '', labelValues: [], isLoading: false},
109
116
  ]);
110
117
  const reactQueryClient = useQueryClient();
111
118
  const metadata = useGrpcMetadata();
112
119
 
113
- const {loading: labelNamesLoading, result} = useLabelNames(queryClient, profileType);
114
- const {response: labelNamesResponse, error: labelNamesError} = result;
115
120
  const [showAll, setShowAll] = useState(false);
116
121
 
117
122
  const visibleRows = showAll ? queryRows : queryRows.slice(0, 3);
@@ -121,14 +126,6 @@ const SimpleMatchers = ({
121
126
 
122
127
  const currentMatchers = currentQuery.matchersString();
123
128
 
124
- const labelNameFromMatchers = useMemo(() => {
125
- if (currentQuery === undefined) return [];
126
-
127
- const matchers = currentQuery.matchers;
128
-
129
- return matchers.map(matcher => matcher.key);
130
- }, [currentQuery]);
131
-
132
129
  const fetchLabelValues = useCallback(
133
130
  async (labelName: string): Promise<string[]> => {
134
131
  if (labelName == null || labelName === '' || profileType == null || profileType === '') {
@@ -158,6 +155,13 @@ const SimpleMatchers = ({
158
155
  [queryClient, metadata, profileType, reactQueryClient]
159
156
  );
160
157
 
158
+ const fetchLabelValuesUtilization = useCallback(
159
+ async (labelName: string): Promise<string[]> => {
160
+ return (await utilizationLabels?.utilizationFetchLabelValues?.(labelName)) ?? [];
161
+ },
162
+ [utilizationLabels]
163
+ );
164
+
161
165
  const updateMatchersString = useCallback(
162
166
  (rows: QueryRow[]) => {
163
167
  const matcherString = rows
@@ -184,7 +188,10 @@ const SimpleMatchers = ({
184
188
  const trimmedLabelName = labelName.trim();
185
189
  if (trimmedLabelName === '') return null;
186
190
 
187
- const labelValues = await fetchLabelValues(trimmedLabelName);
191
+ const labelValues =
192
+ utilizationLabels?.utilizationFetchLabelValues !== undefined
193
+ ? await fetchLabelValuesUtilization(trimmedLabelName)
194
+ : await fetchLabelValues(trimmedLabelName);
188
195
  const sanitizedLabelValue =
189
196
  labelValue.startsWith('"') && labelValue.endsWith('"')
190
197
  ? labelValue.slice(1, -1)
@@ -205,20 +212,15 @@ const SimpleMatchers = ({
205
212
  };
206
213
 
207
214
  void fetchAndSetQueryRows();
208
- }, [currentMatchers, fetchLabelValues, updateMatchersString]);
209
-
210
- const labelNames = useMemo(() => {
211
- return (labelNamesError === undefined || labelNamesError == null) &&
212
- labelNamesResponse !== undefined &&
213
- labelNamesResponse != null
214
- ? labelNamesResponse.labelNames.filter(e => e !== '__name__')
215
- : [];
216
- }, [labelNamesError, labelNamesResponse]);
215
+ }, [
216
+ currentMatchers,
217
+ fetchLabelValues,
218
+ updateMatchersString,
219
+ fetchLabelValuesUtilization,
220
+ utilizationLabels,
221
+ ]);
217
222
 
218
- const labelNameOptions = useMemo(() => {
219
- const uniqueLabelNames = Array.from(new Set([...labelNames, ...labelNameFromMatchers]));
220
- return transformLabelsForSelect(uniqueLabelNames);
221
- }, [labelNames, labelNameFromMatchers]);
223
+ const {labelNameOptions, isLoading: labelNamesLoading} = useLabels();
222
224
 
223
225
  const updateRow = useCallback(
224
226
  async (index: number, field: keyof QueryRow, value: string): Promise<void> => {
@@ -232,7 +234,10 @@ const SimpleMatchers = ({
232
234
  updatedRows[index].isLoading = true;
233
235
  setQueryRows([...updatedRows]);
234
236
 
235
- const labelValues = await fetchLabelValues(value);
237
+ const labelValues =
238
+ utilizationLabels?.utilizationFetchLabelValues !== undefined
239
+ ? await fetchLabelValuesUtilization(value)
240
+ : await fetchLabelValues(value);
236
241
  updatedRows[index].labelValues = labelValues;
237
242
  updatedRows[index].isLoading = false;
238
243
  }
@@ -240,7 +245,13 @@ const SimpleMatchers = ({
240
245
  setQueryRows([...updatedRows]);
241
246
  updateMatchersString(updatedRows);
242
247
  },
243
- [queryRows, fetchLabelValues, updateMatchersString]
248
+ [
249
+ queryRows,
250
+ fetchLabelValues,
251
+ updateMatchersString,
252
+ fetchLabelValuesUtilization,
253
+ utilizationLabels,
254
+ ]
244
255
  );
245
256
 
246
257
  const handleUpdateRow = useCallback(
@@ -290,7 +301,10 @@ const SimpleMatchers = ({
290
301
  setQueryRows([...updatedRows]);
291
302
 
292
303
  try {
293
- const labelValues = await fetchLabelValues(updatedRows[index].labelName);
304
+ const labelValues =
305
+ utilizationLabels?.utilizationFetchLabelValues !== undefined
306
+ ? await fetchLabelValuesUtilization(updatedRows[index].labelName)
307
+ : await fetchLabelValues(updatedRows[index].labelName);
294
308
  updatedRows[index].labelValues = labelValues;
295
309
  } catch (error) {
296
310
  console.error('Error fetching label values:', error);
@@ -303,7 +317,7 @@ const SimpleMatchers = ({
303
317
  }
304
318
  };
305
319
  },
306
- [queryRows, fetchLabelValues]
320
+ [queryRows, fetchLabelValues, fetchLabelValuesUtilization, utilizationLabels]
307
321
  );
308
322
 
309
323
  const isRowRegex = (row: QueryRow): boolean => row.operator === '=~' || row.operator === '!~';
@@ -375,4 +389,22 @@ const SimpleMatchers = ({
375
389
  );
376
390
  };
377
391
 
378
- export default SimpleMatchers;
392
+ export default function SimpleMathersWithProvider(props: Props): JSX.Element {
393
+ const labelNameFromMatchers = useMemo(() => {
394
+ if (props.currentQuery === undefined) return [];
395
+
396
+ const matchers = props.currentQuery.matchers;
397
+
398
+ return matchers.map(matcher => matcher.key);
399
+ }, [props.currentQuery]);
400
+
401
+ return (
402
+ <LabelProvider
403
+ queryClient={props.queryClient}
404
+ profileType={props.profileType}
405
+ labelNameFromMatchers={labelNameFromMatchers}
406
+ >
407
+ <SimpleMatchers {...props} />
408
+ </LabelProvider>
409
+ );
410
+ }