@parca/profile 0.16.414 → 0.16.415

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.
package/CHANGELOG.md CHANGED
@@ -3,6 +3,10 @@
3
3
  All notable changes to this project will be documented in this file.
4
4
  See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
5
5
 
6
+ ## [0.16.415](https://github.com/parca-dev/parca/compare/@parca/profile@0.16.414...@parca/profile@0.16.415) (2024-07-23)
7
+
8
+ **Note:** Version bump only for package @parca/profile
9
+
6
10
  ## [0.16.414](https://github.com/parca-dev/parca/compare/@parca/profile@0.16.413...@parca/profile@0.16.414) (2024-07-22)
7
11
 
8
12
  **Note:** Version bump only for package @parca/profile
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/ProfileExplorer/index.tsx"],"names":[],"mappings":"AAiBA,OAAO,EAAC,kBAAkB,EAAC,MAAM,eAAe,CAAC;AAGjD,OAAO,EAA4B,KAAK,gBAAgB,EAAC,MAAM,kBAAkB,CAAC;AAOlF,UAAU,oBAAoB;IAC5B,WAAW,EAAE,kBAAkB,CAAC;IAChC,WAAW,EAAE,GAAG,CAAC;IACjB,UAAU,EAAE,gBAAgB,CAAC;CAC9B;AAaD,eAAO,MAAM,sBAAsB,eAAgB,MAAM,GAAG,EAAE,KAAG,MAGhE,CAAC;AAgVF,QAAA,MAAM,eAAe,8CAIlB,oBAAoB,KAAG,GAAG,CAAC,OAkB7B,CAAC;AAEF,eAAe,eAAe,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/ProfileExplorer/index.tsx"],"names":[],"mappings":"AAiBA,OAAO,EAAC,kBAAkB,EAAC,MAAM,eAAe,CAAC;AAGjD,OAAO,EAA4B,KAAK,gBAAgB,EAAC,MAAM,kBAAkB,CAAC;AAQlF,UAAU,oBAAoB;IAC5B,WAAW,EAAE,kBAAkB,CAAC;IAChC,WAAW,EAAE,GAAG,CAAC;IACjB,UAAU,EAAE,gBAAgB,CAAC;CAC9B;AAaD,eAAO,MAAM,sBAAsB,eAAgB,MAAM,GAAG,EAAE,KAAG,MAGhE,CAAC;AAyWF,QAAA,MAAM,eAAe,8CAIlB,oBAAoB,KAAG,GAAG,CAAC,OAkB7B,CAAC;AAEF,eAAe,eAAe,CAAC"}
@@ -18,6 +18,7 @@ import { createStore } from '@parca/store';
18
18
  import { capitalizeOnlyFirstLetter } from '@parca/utilities';
19
19
  import { ProfileSelectionFromParams, SuffixParams } from '..';
20
20
  import { useProfileTypes } from '../ProfileSelector';
21
+ import { sumByToParam, useSumByFromParams } from '../useSumBy';
21
22
  import ProfileExplorerCompare from './ProfileExplorerCompare';
22
23
  import ProfileExplorerSingle from './ProfileExplorerSingle';
23
24
  const ErrorContent = ({ errorMessage }) => {
@@ -38,6 +39,19 @@ const sanitizeDateRange = (time_selection_a, from_a, to_a) => {
38
39
  return { time_selection_a: range.getRangeKey(), from_a, to_a };
39
40
  };
40
41
  /* eslint-enable @typescript-eslint/naming-convention */
42
+ const filterEmptyParams = (o) => {
43
+ return Object.fromEntries(Object.entries(o)
44
+ .filter(([_, value]) => value !== '' && value !== undefined && (Array.isArray(value) ? value.length > 0 : true))
45
+ .map(([key, value]) => {
46
+ if (typeof value === 'string') {
47
+ return [key, value];
48
+ }
49
+ if (Array.isArray(value)) {
50
+ return [key, value];
51
+ }
52
+ return [key, value];
53
+ }));
54
+ };
41
55
  const filterSuffix = (o, suffix) => Object.fromEntries(Object.entries(o)
42
56
  .filter(([key]) => !key.endsWith(suffix))
43
57
  .map(([key, value]) => {
@@ -78,6 +92,8 @@ const ProfileExplorerApp = ({ queryClient, queryParams, navigateTo, }) => {
78
92
  /* eslint-enable @typescript-eslint/naming-convention */
79
93
  const [profileA, setProfileA] = useState(null);
80
94
  const [profileB, setProfileB] = useState(null);
95
+ const sumByA = useSumByFromParams(sum_by_a);
96
+ const sumByB = useSumByFromParams(sum_by_b);
81
97
  useEffect(() => {
82
98
  const mergeFrom = merge_from_a ?? undefined;
83
99
  const mergeTo = merge_to_a ?? undefined;
@@ -134,7 +150,7 @@ const ProfileExplorerApp = ({ queryClient, queryParams, navigateTo, }) => {
134
150
  from: parseInt(from_a),
135
151
  to: parseInt(to_a),
136
152
  timeSelection: time_selection_a,
137
- sumBy: sum_by_a,
153
+ sumBy: sumByA,
138
154
  };
139
155
  // Show the SingleProfileExplorer when not comparing
140
156
  if (compare_a !== 'true' && compare_b !== 'true') {
@@ -149,17 +165,18 @@ const ProfileExplorerApp = ({ queryClient, queryParams, navigateTo, }) => {
149
165
  return navigateTo('/',
150
166
  // Filtering the _a suffix causes us to reset potential profile
151
167
  // selection when running a new query.
152
- {
168
+ filterEmptyParams({
153
169
  ...filterSuffix(queryParams, '_a'),
154
170
  ...{
155
171
  expression_a: encodeURIComponent(q.expression),
156
172
  from_a: q.from.toString(),
157
173
  to_a: q.to.toString(),
158
174
  time_selection_a: q.timeSelection,
175
+ sum_by_a: sumByToParam(q.sumBy),
159
176
  dashboard_items: dashboard_items ?? DEFAULT_DASHBOARD_ITEMS,
160
177
  ...mergeParams,
161
178
  },
162
- });
179
+ }));
163
180
  };
164
181
  const selectProfile = (p) => {
165
182
  queryParams.expression_a = encodeURIComponent(queryParams.expression_a);
@@ -176,7 +193,7 @@ const ProfileExplorerApp = ({ queryClient, queryParams, navigateTo, }) => {
176
193
  from: parseInt(from_b),
177
194
  to: parseInt(to_b),
178
195
  timeSelection: time_selection_b,
179
- sumBy: sum_by_b,
196
+ sumBy: sumByB,
180
197
  };
181
198
  const selectQueryA = (q) => {
182
199
  const mergeParams = q.mergeFrom !== undefined && q.mergeTo !== undefined
@@ -189,7 +206,7 @@ const ProfileExplorerApp = ({ queryClient, queryParams, navigateTo, }) => {
189
206
  return navigateTo('/',
190
207
  // Filtering the _a suffix causes us to reset potential profile
191
208
  // selection when running a new query.
192
- {
209
+ filterEmptyParams({
193
210
  ...filterSuffix(queryParams, '_a'),
194
211
  ...{
195
212
  compare_a: 'true',
@@ -199,11 +216,12 @@ const ProfileExplorerApp = ({ queryClient, queryParams, navigateTo, }) => {
199
216
  from_a: q.from.toString(),
200
217
  to_a: q.to.toString(),
201
218
  time_selection_a: q.timeSelection,
219
+ sum_by_a: sumByToParam(q.sumBy),
202
220
  filter_by_function: filter_by_function ?? '',
203
221
  dashboard_items: dashboard_items ?? DEFAULT_DASHBOARD_ITEMS,
204
222
  ...mergeParams,
205
223
  },
206
- });
224
+ }));
207
225
  };
208
226
  const selectQueryB = (q) => {
209
227
  const mergeParams = q.mergeFrom !== undefined && q.mergeTo !== undefined
@@ -216,7 +234,7 @@ const ProfileExplorerApp = ({ queryClient, queryParams, navigateTo, }) => {
216
234
  return navigateTo('/',
217
235
  // Filtering the _b suffix causes us to reset potential profile
218
236
  // selection when running a new query.
219
- {
237
+ filterEmptyParams({
220
238
  ...filterSuffix(queryParams, '_b'),
221
239
  ...{
222
240
  compare_b: 'true',
@@ -226,11 +244,12 @@ const ProfileExplorerApp = ({ queryClient, queryParams, navigateTo, }) => {
226
244
  from_b: q.from.toString(),
227
245
  to_b: q.to.toString(),
228
246
  time_selection_b: q.timeSelection,
247
+ sum_by_b: sumByToParam(q.sumBy),
229
248
  filter_by_function: filter_by_function ?? '',
230
249
  dashboard_items: dashboard_items ?? DEFAULT_DASHBOARD_ITEMS,
231
250
  ...mergeParams,
232
251
  },
233
- });
252
+ }));
234
253
  };
235
254
  const closeProfile = (card) => {
236
255
  let newQueryParameters = queryParams;
@@ -53,7 +53,7 @@ export const useQueryRange = (client, queryExpression, start, end, sumBy, skip =
53
53
  }
54
54
  }, [stepCountStr, defaultStepCount, setStepCount]);
55
55
  const { data, isLoading, error } = useGrpcQuery({
56
- key: ['query-range', queryExpression, start, end, sumBy.join(','), stepCount, metadata],
56
+ key: ['query-range', queryExpression, start, end, (sumBy ?? []).join(','), stepCount, metadata],
57
57
  queryFn: async () => {
58
58
  const stepDuration = getStepDuration(start, end, stepCount);
59
59
  const { response } = await client.queryRange({
@@ -29,6 +29,6 @@ export interface IProfileTypesResult {
29
29
  error?: RpcError;
30
30
  }
31
31
  export declare const useProfileTypes: (client: QueryServiceClient) => IProfileTypesResult;
32
- declare const ProfileSelector: ({ queryClient, querySelection, selectProfile, selectQuery, closeProfile, enforcedProfileName, profileSelection, comparing, navigateTo, suffix, }: ProfileSelectorProps) => JSX.Element;
32
+ declare const ProfileSelector: ({ queryClient, querySelection, selectProfile, selectQuery, closeProfile, enforcedProfileName, profileSelection, comparing, navigateTo, }: ProfileSelectorProps) => JSX.Element;
33
33
  export default ProfileSelector;
34
34
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/ProfileSelector/index.tsx"],"names":[],"mappings":"AAeA,OAAO,EAAC,QAAQ,EAAC,MAAM,0BAA0B,CAAC;AAGlD,OAAO,EAAQ,oBAAoB,EAAE,kBAAkB,EAAC,MAAM,eAAe,CAAC;AAY9E,OAAO,EAAC,KAAK,gBAAgB,EAAC,MAAM,kBAAkB,CAAC;AAEvD,OAAO,EAAyB,gBAAgB,EAAC,MAAM,IAAI,CAAC;AAQ5D,MAAM,WAAW,cAAc;IAC7B,UAAU,EAAE,MAAM,CAAC;IACnB,IAAI,EAAE,MAAM,CAAC;IACb,EAAE,EAAE,MAAM,CAAC;IACX,aAAa,EAAE,MAAM,CAAC;IACtB,KAAK,CAAC,EAAE,MAAM,EAAE,CAAC;IACjB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAED,UAAU,oBAAoB;IAC5B,WAAW,EAAE,kBAAkB,CAAC;IAChC,cAAc,EAAE,cAAc,CAAC;IAC/B,aAAa,EAAE,CAAC,MAAM,EAAE,gBAAgB,KAAK,IAAI,CAAC;IAClD,WAAW,EAAE,CAAC,KAAK,EAAE,cAAc,KAAK,IAAI,CAAC;IAC7C,YAAY,EAAE,MAAM,IAAI,CAAC;IACzB,mBAAmB,EAAE,MAAM,CAAC;IAC5B,gBAAgB,EAAE,gBAAgB,GAAG,IAAI,CAAC;IAC1C,SAAS,EAAE,OAAO,CAAC;IACnB,UAAU,EAAE,gBAAgB,CAAC;IAC7B,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,WAAW,mBAAmB;IAClC,OAAO,EAAE,OAAO,CAAC;IACjB,IAAI,CAAC,EAAE,oBAAoB,CAAC;IAC5B,KAAK,CAAC,EAAE,QAAQ,CAAC;CAClB;AAED,eAAO,MAAM,eAAe,WAAY,kBAAkB,KAAG,mBAkB5D,CAAC;AAEF,QAAA,MAAM,eAAe,qJAWlB,oBAAoB,KAAG,GAAG,CAAC,OAiU7B,CAAC;AAEF,eAAe,eAAe,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/ProfileSelector/index.tsx"],"names":[],"mappings":"AAeA,OAAO,EAAC,QAAQ,EAAC,MAAM,0BAA0B,CAAC;AAGlD,OAAO,EAAQ,oBAAoB,EAAE,kBAAkB,EAAC,MAAM,eAAe,CAAC;AAY9E,OAAO,EAAC,KAAK,gBAAgB,EAAC,MAAM,kBAAkB,CAAC;AAEvD,OAAO,EAAyB,gBAAgB,EAAC,MAAM,IAAI,CAAC;AAQ5D,MAAM,WAAW,cAAc;IAC7B,UAAU,EAAE,MAAM,CAAC;IACnB,IAAI,EAAE,MAAM,CAAC;IACb,EAAE,EAAE,MAAM,CAAC;IACX,aAAa,EAAE,MAAM,CAAC;IACtB,KAAK,CAAC,EAAE,MAAM,EAAE,CAAC;IACjB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAED,UAAU,oBAAoB;IAC5B,WAAW,EAAE,kBAAkB,CAAC;IAChC,cAAc,EAAE,cAAc,CAAC;IAC/B,aAAa,EAAE,CAAC,MAAM,EAAE,gBAAgB,KAAK,IAAI,CAAC;IAClD,WAAW,EAAE,CAAC,KAAK,EAAE,cAAc,KAAK,IAAI,CAAC;IAC7C,YAAY,EAAE,MAAM,IAAI,CAAC;IACzB,mBAAmB,EAAE,MAAM,CAAC;IAC5B,gBAAgB,EAAE,gBAAgB,GAAG,IAAI,CAAC;IAC1C,SAAS,EAAE,OAAO,CAAC;IACnB,UAAU,EAAE,gBAAgB,CAAC;IAC7B,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,WAAW,mBAAmB;IAClC,OAAO,EAAE,OAAO,CAAC;IACjB,IAAI,CAAC,EAAE,oBAAoB,CAAC;IAC5B,KAAK,CAAC,EAAE,QAAQ,CAAC;CAClB;AAED,eAAO,MAAM,eAAe,WAAY,kBAAkB,KAAG,mBAkB5D,CAAC;AAEF,QAAA,MAAM,eAAe,6IAUlB,oBAAoB,KAAG,GAAG,CAAC,OAwT7B,CAAC;AAEF,eAAe,eAAe,CAAC"}
@@ -21,7 +21,7 @@ import MatchersInput, { useLabelNames } from '../MatchersInput/index';
21
21
  import { useMetricsGraphDimensions } from '../MetricsGraph/useMetricsGraphDimensions';
22
22
  import ProfileMetricsGraph, { ProfileMetricsEmptyState } from '../ProfileMetricsGraph';
23
23
  import ProfileTypeSelector from '../ProfileTypeSelector/index';
24
- import { useSumBy } from '../useSumBy';
24
+ import { useDefaultSumBy, useSumBySelection } from '../useSumBy';
25
25
  import { useAutoQuerySelector } from './useAutoQuerySelector';
26
26
  export const useProfileTypes = (client) => {
27
27
  const [result, setResult] = useState(undefined);
@@ -40,7 +40,7 @@ export const useProfileTypes = (client) => {
40
40
  }, [client, metadata, loading]);
41
41
  return { loading, data: result, error };
42
42
  };
43
- const ProfileSelector = ({ queryClient, querySelection, selectProfile, selectQuery, closeProfile, enforcedProfileName, profileSelection, comparing, navigateTo, suffix = '', }) => {
43
+ const ProfileSelector = ({ queryClient, querySelection, selectProfile, selectQuery, closeProfile, enforcedProfileName, profileSelection, comparing, navigateTo, }) => {
44
44
  const { loading: profileTypesLoading, data: profileTypesData, error, } = useProfileTypes(queryClient);
45
45
  const { heightStyle } = useMetricsGraphDimensions(comparing);
46
46
  const { viewComponent } = useParcaContext();
@@ -62,14 +62,10 @@ const ProfileSelector = ({ queryClient, querySelection, selectProfile, selectQue
62
62
  ? []
63
63
  : selectedLabelNamesResult.response.labelNames;
64
64
  }, [selectedLabelNamesResult]);
65
- const [sumBy, setSumBy, { userSelectedSumBy, isLoading: isSumByLoading }] = useSumBy(selectedProfileType, selectedLabelNamesLoading, selectedLabels, {
66
- urlParamKey: 'sum_by' + suffix,
67
- });
68
- const [sumBySelection, setSumBySelection, { isLoading: isSumBySelectionLoading }] = useSumBy(profileType, labelNamesLoading, labels, {
69
- urlParamKey: 'sum_by_selection' + suffix,
70
- withURLUpdate: false,
71
- defaultValue: userSelectedSumBy,
65
+ const [sumBySelection, setUserSumBySelection, { isLoading: sumBySelectionLoading }] = useSumBySelection(profileType, labelNamesLoading, labels, {
66
+ defaultValue: querySelection.sumBy,
72
67
  });
68
+ const { defaultSumBy, isLoading: defaultSumByLoading } = useDefaultSumBy(selectedProfileType, selectedLabelNamesLoading, selectedLabels);
73
69
  useEffect(() => {
74
70
  if (enforcedProfileName !== '') {
75
71
  const [q, changed] = Query.parse(querySelection.expression).setProfileName(enforcedProfileName);
@@ -88,9 +84,6 @@ const ProfileSelector = ({ queryClient, querySelection, selectProfile, selectQue
88
84
  const query = enforcedProfileName !== '' ? enforcedProfileNameQuery() : Query.parse(queryExpressionString);
89
85
  const selectedProfileName = query.profileName();
90
86
  const setNewQueryExpression = (expr, updateTs = false) => {
91
- if (!isSumBySelectionLoading) {
92
- setSumBy(sumBySelection);
93
- }
94
87
  const query = enforcedProfileName !== '' ? enforcedProfileNameQuery() : Query.parse(expr);
95
88
  const delta = query.profileType().delta;
96
89
  const from = timeRangeSelection.getFromMs(updateTs);
@@ -106,6 +99,7 @@ const ProfileSelector = ({ queryClient, querySelection, selectProfile, selectQue
106
99
  from,
107
100
  to,
108
101
  timeSelection: timeRangeSelection.getRangeKey(),
102
+ sumBy: sumBySelection,
109
103
  ...mergeParams,
110
104
  });
111
105
  };
@@ -165,24 +159,25 @@ const ProfileSelector = ({ queryClient, querySelection, selectProfile, selectQue
165
159
  profileTypesData,
166
160
  setProfileName,
167
161
  setQueryExpression,
168
- querySelection: { ...querySelection, sumBy },
162
+ querySelection: { ...querySelection, sumBy: sumBySelection },
169
163
  navigateTo,
164
+ loading: sumBySelectionLoading,
170
165
  });
171
166
  const searchDisabled = queryExpressionString === undefined ||
172
167
  queryExpressionString === '' ||
173
168
  queryExpressionString === '{}';
174
169
  return (_jsxs(_Fragment, { children: [_jsxs("div", { className: "mb-2 flex gap-2", children: [_jsxs("div", { className: "flex w-full flex-wrap content-start items-center gap-2", children: [_jsxs("div", { className: "pb-6", children: [_jsx("label", { className: "text-xs", children: "Profile type" }), _jsx(ProfileTypeSelector, { profileTypesData: profileTypesData, loading: profileTypesLoading, selectedKey: selectedProfileName, onSelection: setProfileName, error: error, disabled: viewComponent?.disableProfileTypesDropdown })] }), _jsxs("div", { className: "w-full flex-1 pb-6", children: [_jsxs("div", { className: "mb-0.5 mt-1.5 flex items-center justify-between", children: [_jsx("label", { className: "text-xs", children: "Query" }), (query.matchers.length > 0 || query.inputMatcherString.length > 0) &&
175
- viewComponent !== undefined && _jsx("div", { children: viewComponent?.createViewComponent })] }), _jsx(MatchersInput, { queryClient: queryClient, setMatchersString: setMatchersString, runQuery: setQueryExpression, currentQuery: query, profileType: selectedProfileName })] }), _jsxs("div", { className: "pb-6", children: [_jsx("div", { className: "mb-0.5 mt-1.5 flex items-center justify-between", children: _jsx("label", { className: "text-xs", children: "Sum by" }) }), _jsx(Select, { defaultValue: [], isMulti: true, name: "colors", options: labels.map(label => ({ label, value: label })), className: "parca-select-container text-sm w-80", classNamePrefix: "parca-select", value: sumBySelection.map(sumBy => ({ label: sumBy, value: sumBy })), onChange: selectedOptions => {
176
- setSumBySelection(selectedOptions.map(option => option.value));
170
+ viewComponent !== undefined && _jsx("div", { children: viewComponent?.createViewComponent })] }), _jsx(MatchersInput, { queryClient: queryClient, setMatchersString: setMatchersString, runQuery: setQueryExpression, currentQuery: query, profileType: selectedProfileName })] }), _jsxs("div", { className: "pb-6", children: [_jsx("div", { className: "mb-0.5 mt-1.5 flex items-center justify-between", children: _jsx("label", { className: "text-xs", children: "Sum by" }) }), _jsx(Select, { defaultValue: [], isMulti: true, name: "colors", options: labels.map(label => ({ label, value: label })), className: "parca-select-container text-sm w-80", classNamePrefix: "parca-select", value: (sumBySelection ?? []).map(sumBy => ({ label: sumBy, value: sumBy })), onChange: selectedOptions => {
171
+ setUserSumBySelection(selectedOptions.map(option => option.value));
177
172
  }, placeholder: "Labels...", styles: {
178
173
  indicatorSeparator: () => ({ display: 'none' }),
179
- } })] }), _jsx(DateTimeRangePicker, { onRangeSelection: setTimeRangeSelection, range: timeRangeSelection }), _jsx(ButtonGroup, { children: _jsx(Button, { disabled: searchDisabled, onClick: (e) => {
174
+ }, isDisabled: !profileType.delta })] }), _jsx(DateTimeRangePicker, { onRangeSelection: setTimeRangeSelection, range: timeRangeSelection }), _jsx(ButtonGroup, { children: _jsx(Button, { disabled: searchDisabled, onClick: (e) => {
180
175
  e.preventDefault();
181
176
  setQueryExpression(true);
182
177
  }, id: "h-matcher-search-button", children: "Search" }) })] }), _jsx("div", { children: comparing && _jsx(IconButton, { onClick: () => closeProfile(), icon: _jsx(CloseIcon, {}) }) })] }), _jsx("div", { className: "rounded bg-white shadow dark:border-gray-500 dark:bg-gray-700", children: _jsx("div", { style: { height: heightStyle }, children: querySelection.expression !== undefined &&
183
178
  querySelection.expression.length > 0 &&
184
179
  querySelection.from !== undefined &&
185
- querySelection.to !== undefined ? (_jsx("div", { className: "p-2", children: _jsx(ProfileMetricsGraph, { queryClient: queryClient, queryExpression: querySelection.expression, from: querySelection.from, to: querySelection.to, profile: profileSelection, comparing: comparing, sumBy: sumBy, sumByLoading: isSumByLoading, setTimeRange: (range) => {
180
+ querySelection.to !== undefined ? (_jsx("div", { className: "p-2", children: _jsx(ProfileMetricsGraph, { queryClient: queryClient, queryExpression: querySelection.expression, from: querySelection.from, to: querySelection.to, profile: profileSelection, comparing: comparing, sumBy: querySelection.sumBy ?? defaultSumBy ?? [], sumByLoading: defaultSumByLoading, setTimeRange: (range) => {
186
181
  const from = range.getFromMs();
187
182
  const to = range.getToMs();
188
183
  let mergedProfileParams = {};
@@ -8,7 +8,8 @@ interface Props {
8
8
  setQueryExpression: () => void;
9
9
  querySelection: QuerySelection;
10
10
  navigateTo: NavigateFunction;
11
+ loading: boolean;
11
12
  }
12
- export declare const useAutoQuerySelector: ({ selectedProfileName, profileTypesData, setProfileName, setQueryExpression, querySelection, navigateTo, }: Props) => void;
13
+ export declare const useAutoQuerySelector: ({ selectedProfileName, profileTypesData, setProfileName, setQueryExpression, querySelection, navigateTo, loading, }: Props) => void;
13
14
  export {};
14
15
  //# sourceMappingURL=useAutoQuerySelector.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"useAutoQuerySelector.d.ts","sourceRoot":"","sources":["../../src/ProfileSelector/useAutoQuerySelector.ts"],"names":[],"mappings":"AAeA,OAAO,EAAC,oBAAoB,EAAC,MAAM,eAAe,CAAC;AAEnD,OAAO,EAAC,KAAK,gBAAgB,EAAC,MAAM,kBAAkB,CAAC;AAGvD,OAAO,EAAC,cAAc,EAAC,MAAM,oBAAoB,CAAC;AAGlD,UAAU,KAAK;IACb,mBAAmB,EAAE,MAAM,CAAC;IAC5B,gBAAgB,EAAE,oBAAoB,GAAG,SAAS,CAAC;IACnD,cAAc,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;IACvC,kBAAkB,EAAE,MAAM,IAAI,CAAC;IAC/B,cAAc,EAAE,cAAc,CAAC;IAC/B,UAAU,EAAE,gBAAgB,CAAC;CAC9B;AAED,eAAO,MAAM,oBAAoB,+GAO9B,KAAK,KAAG,IAkIV,CAAC"}
1
+ {"version":3,"file":"useAutoQuerySelector.d.ts","sourceRoot":"","sources":["../../src/ProfileSelector/useAutoQuerySelector.ts"],"names":[],"mappings":"AAeA,OAAO,EAAC,oBAAoB,EAAC,MAAM,eAAe,CAAC;AAEnD,OAAO,EAAC,KAAK,gBAAgB,EAAC,MAAM,kBAAkB,CAAC;AAGvD,OAAO,EAAC,cAAc,EAAC,MAAM,oBAAoB,CAAC;AAGlD,UAAU,KAAK;IACb,mBAAmB,EAAE,MAAM,CAAC;IAC5B,gBAAgB,EAAE,oBAAoB,GAAG,SAAS,CAAC;IACnD,cAAc,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;IACvC,kBAAkB,EAAE,MAAM,IAAI,CAAC;IAC/B,cAAc,EAAE,cAAc,CAAC;IAC/B,UAAU,EAAE,gBAAgB,CAAC;IAC7B,OAAO,EAAE,OAAO,CAAC;CAClB;AAED,eAAO,MAAM,oBAAoB,wHAQ9B,KAAK,KAAG,IAuIV,CAAC"}
@@ -14,13 +14,16 @@ import { useEffect } from 'react';
14
14
  import { selectAutoQuery, setAutoQuery, useAppDispatch, useAppSelector } from '@parca/store';
15
15
  import { ProfileSelectionFromParams, SuffixParams } from '..';
16
16
  import { constructProfileName } from '../ProfileTypeSelector';
17
- export const useAutoQuerySelector = ({ selectedProfileName, profileTypesData, setProfileName, setQueryExpression, querySelection, navigateTo, }) => {
17
+ export const useAutoQuerySelector = ({ selectedProfileName, profileTypesData, setProfileName, setQueryExpression, querySelection, navigateTo, loading, }) => {
18
18
  const autoQuery = useAppSelector(selectAutoQuery);
19
19
  const dispatch = useAppDispatch();
20
20
  const queryParams = new URLSearchParams(location.search);
21
21
  const comparing = queryParams.get('comparing') === 'true';
22
22
  const expressionA = queryParams.get('expression_a');
23
23
  useEffect(() => {
24
+ if (loading) {
25
+ return;
26
+ }
24
27
  if (comparing && expressionA !== null && expressionA !== undefined) {
25
28
  if (querySelection.expression === undefined) {
26
29
  return;
@@ -62,7 +65,7 @@ export const useAutoQuerySelector = ({ selectedProfileName, profileTypesData, se
62
65
  dashboard_items: ['icicle'],
63
66
  });
64
67
  }
65
- }, [comparing, querySelection, navigateTo, expressionA, dispatch]);
68
+ }, [comparing, querySelection, navigateTo, expressionA, dispatch, loading]);
66
69
  // Effect to load some initial data on load when is no selection
67
70
  useEffect(() => {
68
71
  void (async () => {
@@ -105,7 +108,8 @@ export const useAutoQuerySelector = ({ selectedProfileName, profileTypesData, se
105
108
  if (autoQuery !== 'true' ||
106
109
  profileTypesData?.types == null ||
107
110
  profileTypesData.types.length < 1 ||
108
- selectedProfileName.length === 0) {
111
+ selectedProfileName.length === 0 ||
112
+ loading) {
109
113
  return;
110
114
  }
111
115
  setQueryExpression();
@@ -118,5 +122,6 @@ export const useAutoQuerySelector = ({ selectedProfileName, profileTypesData, se
118
122
  setProfileName,
119
123
  dispatch,
120
124
  selectedProfileName,
125
+ loading,
121
126
  ]);
122
127
  };
@@ -1,11 +1,14 @@
1
1
  import { ProfileType } from '@parca/parser';
2
2
  export declare const DEFAULT_EMPTY_SUM_BY: string[];
3
- export declare const useSumBy: (profileType: ProfileType | undefined, labelNamesLoading: boolean, labels: string[] | undefined, { urlParamKey, withURLUpdate, defaultValue, }?: {
4
- urlParamKey?: string;
5
- withURLUpdate?: boolean;
3
+ export declare const useSumBySelection: (profileType: ProfileType | undefined, labelNamesLoading: boolean, labels: string[] | undefined, { defaultValue, }?: {
6
4
  defaultValue?: string[];
7
- }) => [string[], (labels: string[]) => void, {
8
- userSelectedSumBy: string[] | undefined;
5
+ }) => [string[] | undefined, (labels: string[]) => void, {
9
6
  isLoading: boolean;
10
7
  }];
8
+ export declare const useDefaultSumBy: (profileType: ProfileType | undefined, labelNamesLoading: boolean, labels: string[] | undefined) => {
9
+ defaultSumBy: string[] | undefined;
10
+ isLoading: boolean;
11
+ };
12
+ export declare const useSumByFromParams: (param: string | string[] | undefined) => string[] | undefined;
13
+ export declare const sumByToParam: (sumBy: string[] | undefined) => string | string[] | undefined;
11
14
  //# sourceMappingURL=useSumBy.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"useSumBy.d.ts","sourceRoot":"","sources":["../src/useSumBy.ts"],"names":[],"mappings":"AAgBA,OAAO,EAAC,WAAW,EAAC,MAAM,eAAe,CAAC;AAE1C,eAAO,MAAM,oBAAoB,EAAE,MAAM,EAAO,CAAC;AAyBjD,eAAO,MAAM,QAAQ,gBACN,WAAW,GAAG,SAAS,qBACjB,OAAO,UAClB,MAAM,EAAE,GAAG,SAAS,kDAKzB;IACD,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,aAAa,CAAC,EAAE,OAAO,CAAC;IACxB,YAAY,CAAC,EAAE,MAAM,EAAE,CAAC;CACzB,KACA,CACD,MAAM,EAAE,EACR,CAAC,MAAM,EAAE,MAAM,EAAE,KAAK,IAAI,EAC1B;IAAC,iBAAiB,EAAE,MAAM,EAAE,GAAG,SAAS,CAAC;IAAC,SAAS,EAAE,OAAO,CAAA;CAAC,CA4D9D,CAAC"}
1
+ {"version":3,"file":"useSumBy.d.ts","sourceRoot":"","sources":["../src/useSumBy.ts"],"names":[],"mappings":"AAeA,OAAO,EAAC,WAAW,EAAC,MAAM,eAAe,CAAC;AAE1C,eAAO,MAAM,oBAAoB,EAAE,MAAM,EAAO,CAAC;AA6BjD,eAAO,MAAM,iBAAiB,gBACf,WAAW,GAAG,SAAS,qBACjB,OAAO,UAClB,MAAM,EAAE,GAAG,SAAS,sBAGzB;IACD,YAAY,CAAC,EAAE,MAAM,EAAE,CAAC;CACzB,KACA,CACD,MAAM,EAAE,GAAG,SAAS,EACpB,CAAC,MAAM,EAAE,MAAM,EAAE,KAAK,IAAI,EAC1B;IACE,SAAS,EAAE,OAAO,CAAC;CACpB,CAsCF,CAAC;AAEF,eAAO,MAAM,eAAe,gBACb,WAAW,GAAG,SAAS,qBACjB,OAAO,UAClB,MAAM,EAAE,GAAG,SAAS,KAC3B;IAAC,YAAY,EAAE,MAAM,EAAE,GAAG,SAAS,CAAC;IAAC,SAAS,EAAE,OAAO,CAAA;CAMzD,CAAC;AAkBF,eAAO,MAAM,kBAAkB,UAAW,MAAM,GAAG,MAAM,EAAE,GAAG,SAAS,KAAG,MAAM,EAAE,GAAG,SAMpF,CAAC;AAEF,eAAO,MAAM,YAAY,UAAW,MAAM,EAAE,GAAG,SAAS,KAAG,MAAM,GAAG,MAAM,EAAE,GAAG,SAU9E,CAAC"}
package/dist/useSumBy.js CHANGED
@@ -10,13 +10,15 @@
10
10
  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
11
11
  // See the License for the specific language governing permissions and
12
12
  // limitations under the License.
13
- import { useCallback, useMemo } from 'react';
14
- import { useParcaContext, useURLState } from '@parca/components';
13
+ import { useCallback, useMemo, useState } from 'react';
15
14
  export const DEFAULT_EMPTY_SUM_BY = [];
16
15
  const getDefaultSumBy = (profile, labels) => {
17
16
  if (profile === undefined || labels === undefined) {
18
17
  return undefined;
19
18
  }
19
+ if (!profile.delta) {
20
+ return DEFAULT_EMPTY_SUM_BY;
21
+ }
20
22
  if (labels.includes('comm')) {
21
23
  return ['comm'];
22
24
  }
@@ -28,47 +30,62 @@ const getDefaultSumBy = (profile, labels) => {
28
30
  }
29
31
  return undefined;
30
32
  };
31
- export const useSumBy = (profileType, labelNamesLoading, labels, { urlParamKey = 'sum_by', withURLUpdate = true, defaultValue, } = {}) => {
32
- const { navigateTo } = useParcaContext();
33
- const [userSelectedSumByParam, setUserSelectedSumByParam] = useURLState({
34
- param: urlParamKey,
35
- navigateTo,
36
- withURLUpdate,
37
- });
38
- const userSelectedSumBy = useMemo(() => {
39
- if (userSelectedSumByParam?.length === 0) {
40
- return undefined;
41
- }
42
- if (userSelectedSumByParam === '__none__') {
43
- return [];
44
- }
45
- if (userSelectedSumByParam === undefined && defaultValue !== undefined) {
46
- return defaultValue;
47
- }
48
- if (typeof userSelectedSumByParam === 'string') {
49
- return [userSelectedSumByParam];
50
- }
51
- return userSelectedSumByParam;
52
- // eslint-disable-next-line react-hooks/exhaustive-deps
53
- }, [userSelectedSumByParam]);
54
- const setUserSelectedSumBy = useCallback((sumBy) => {
55
- if (sumBy.length === 0) {
56
- setUserSelectedSumByParam('__none__');
57
- return;
58
- }
59
- if (sumBy.length === 1) {
60
- // Handle this separately to take care of the empty string scenario
61
- setUserSelectedSumByParam(sumBy[0]);
62
- return;
63
- }
64
- setUserSelectedSumByParam(sumBy);
65
- }, [setUserSelectedSumByParam]);
33
+ export const useSumBySelection = (profileType, labelNamesLoading, labels, { defaultValue, } = {}) => {
34
+ const [userSelectedSumBy, setUserSelectedSumBy] = useState(profileType != null ? { [profileType.toString()]: defaultValue } : {});
35
+ const setSumBy = useCallback((sumBy) => {
36
+ setUserSelectedSumBy(prev => {
37
+ if (profileType == null) {
38
+ return prev;
39
+ }
40
+ return {
41
+ ...prev,
42
+ [profileType.toString()]: sumBy,
43
+ };
44
+ });
45
+ }, [setUserSelectedSumBy, profileType]);
46
+ const { defaultSumBy } = useDefaultSumBy(profileType, labelNamesLoading, labels);
47
+ let sumBy = userSelectedSumBy[profileType?.toString() ?? ''] ?? defaultSumBy ?? DEFAULT_EMPTY_SUM_BY;
48
+ if (profileType?.delta !== true) {
49
+ sumBy = DEFAULT_EMPTY_SUM_BY;
50
+ }
51
+ return [
52
+ labelNamesLoading ? undefined : sumBy,
53
+ setSumBy,
54
+ {
55
+ isLoading: labelNamesLoading,
56
+ },
57
+ ];
58
+ };
59
+ export const useDefaultSumBy = (profileType, labelNamesLoading, labels) => {
66
60
  const defaultSumBy = useMemo(() => {
67
61
  return getDefaultSumBy(profileType, labels);
68
62
  }, [profileType, labels]);
69
- let sumBy = userSelectedSumBy ?? defaultSumBy ?? DEFAULT_EMPTY_SUM_BY;
70
- if (profileType?.delta !== true) {
71
- sumBy = DEFAULT_EMPTY_SUM_BY;
63
+ return { defaultSumBy, isLoading: labelNamesLoading };
64
+ };
65
+ const getSumByFromParam = (param) => {
66
+ if (param?.length === 0) {
67
+ return undefined;
68
+ }
69
+ if (param === '__none__') {
70
+ return [];
71
+ }
72
+ if (typeof param === 'string') {
73
+ return [param];
74
+ }
75
+ return param;
76
+ };
77
+ export const useSumByFromParams = (param) => {
78
+ const sumBy = useMemo(() => {
79
+ return getSumByFromParam(param);
80
+ }, [param]);
81
+ return sumBy;
82
+ };
83
+ export const sumByToParam = (sumBy) => {
84
+ if (sumBy === undefined) {
85
+ return undefined;
86
+ }
87
+ if (sumBy.length === 0) {
88
+ return '__none__';
72
89
  }
73
- return [sumBy, setUserSelectedSumBy, { userSelectedSumBy, isLoading: labelNamesLoading }];
90
+ return sumBy;
74
91
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@parca/profile",
3
- "version": "0.16.414",
3
+ "version": "0.16.415",
4
4
  "description": "Profile viewing libraries",
5
5
  "dependencies": {
6
6
  "@headlessui/react": "^1.7.19",
@@ -73,5 +73,5 @@
73
73
  "access": "public",
74
74
  "registry": "https://registry.npmjs.org/"
75
75
  },
76
- "gitHead": "7344e3c5687b4d9a5c93b6c44494592726fcba23"
76
+ "gitHead": "4e0b35d45ffe7a449f875b54a8921dc05f2070e7"
77
77
  }
@@ -22,6 +22,7 @@ import {capitalizeOnlyFirstLetter, type NavigateFunction} from '@parca/utilities
22
22
 
23
23
  import {ProfileSelection, ProfileSelectionFromParams, SuffixParams} from '..';
24
24
  import {QuerySelection, useProfileTypes} from '../ProfileSelector';
25
+ import {sumByToParam, useSumByFromParams} from '../useSumBy';
25
26
  import ProfileExplorerCompare from './ProfileExplorerCompare';
26
27
  import ProfileExplorerSingle from './ProfileExplorerSingle';
27
28
 
@@ -64,6 +65,25 @@ const sanitizeDateRange = (
64
65
  };
65
66
  /* eslint-enable @typescript-eslint/naming-convention */
66
67
 
68
+ const filterEmptyParams = (o: Record<string, any>): Record<string, any> => {
69
+ return Object.fromEntries(
70
+ Object.entries(o)
71
+ .filter(
72
+ ([_, value]) =>
73
+ value !== '' && value !== undefined && (Array.isArray(value) ? value.length > 0 : true)
74
+ )
75
+ .map(([key, value]) => {
76
+ if (typeof value === 'string') {
77
+ return [key, value];
78
+ }
79
+ if (Array.isArray(value)) {
80
+ return [key, value];
81
+ }
82
+ return [key, value];
83
+ })
84
+ );
85
+ };
86
+
67
87
  const filterSuffix = (
68
88
  o: {[key: string]: string | string[] | undefined},
69
89
  suffix: string
@@ -148,6 +168,9 @@ const ProfileExplorerApp = ({
148
168
  const [profileA, setProfileA] = useState<ProfileSelection | null>(null);
149
169
  const [profileB, setProfileB] = useState<ProfileSelection | null>(null);
150
170
 
171
+ const sumByA = useSumByFromParams(sum_by_a);
172
+ const sumByB = useSumByFromParams(sum_by_b);
173
+
151
174
  useEffect(() => {
152
175
  const mergeFrom = merge_from_a ?? undefined;
153
176
  const mergeTo = merge_to_a ?? undefined;
@@ -225,7 +248,7 @@ const ProfileExplorerApp = ({
225
248
  from: parseInt(from_a as string),
226
249
  to: parseInt(to_a as string),
227
250
  timeSelection: time_selection_a as string,
228
- sumBy: sum_by_a as string[],
251
+ sumBy: sumByA,
229
252
  };
230
253
 
231
254
  // Show the SingleProfileExplorer when not comparing
@@ -243,17 +266,18 @@ const ProfileExplorerApp = ({
243
266
  '/',
244
267
  // Filtering the _a suffix causes us to reset potential profile
245
268
  // selection when running a new query.
246
- {
269
+ filterEmptyParams({
247
270
  ...filterSuffix(queryParams, '_a'),
248
271
  ...{
249
272
  expression_a: encodeURIComponent(q.expression),
250
273
  from_a: q.from.toString(),
251
274
  to_a: q.to.toString(),
252
275
  time_selection_a: q.timeSelection,
276
+ sum_by_a: sumByToParam(q.sumBy),
253
277
  dashboard_items: dashboard_items ?? DEFAULT_DASHBOARD_ITEMS,
254
278
  ...mergeParams,
255
279
  },
256
- }
280
+ })
257
281
  );
258
282
  };
259
283
 
@@ -283,7 +307,7 @@ const ProfileExplorerApp = ({
283
307
  from: parseInt(from_b as string),
284
308
  to: parseInt(to_b as string),
285
309
  timeSelection: time_selection_b as string,
286
- sumBy: sum_by_b as string[],
310
+ sumBy: sumByB,
287
311
  };
288
312
 
289
313
  const selectQueryA = (q: QuerySelection): void => {
@@ -299,7 +323,7 @@ const ProfileExplorerApp = ({
299
323
  '/',
300
324
  // Filtering the _a suffix causes us to reset potential profile
301
325
  // selection when running a new query.
302
- {
326
+ filterEmptyParams({
303
327
  ...filterSuffix(queryParams, '_a'),
304
328
  ...{
305
329
  compare_a: 'true',
@@ -309,11 +333,12 @@ const ProfileExplorerApp = ({
309
333
  from_a: q.from.toString(),
310
334
  to_a: q.to.toString(),
311
335
  time_selection_a: q.timeSelection,
336
+ sum_by_a: sumByToParam(q.sumBy),
312
337
  filter_by_function: filter_by_function ?? '',
313
338
  dashboard_items: dashboard_items ?? DEFAULT_DASHBOARD_ITEMS,
314
339
  ...mergeParams,
315
340
  },
316
- }
341
+ })
317
342
  );
318
343
  };
319
344
 
@@ -330,7 +355,7 @@ const ProfileExplorerApp = ({
330
355
  '/',
331
356
  // Filtering the _b suffix causes us to reset potential profile
332
357
  // selection when running a new query.
333
- {
358
+ filterEmptyParams({
334
359
  ...filterSuffix(queryParams, '_b'),
335
360
  ...{
336
361
  compare_b: 'true',
@@ -340,11 +365,12 @@ const ProfileExplorerApp = ({
340
365
  from_b: q.from.toString(),
341
366
  to_b: q.to.toString(),
342
367
  time_selection_b: q.timeSelection,
368
+ sum_by_b: sumByToParam(q.sumBy),
343
369
  filter_by_function: filter_by_function ?? '',
344
370
  dashboard_items: dashboard_items ?? DEFAULT_DASHBOARD_ITEMS,
345
371
  ...mergeParams,
346
372
  },
347
- }
373
+ })
348
374
  );
349
375
  };
350
376
 
@@ -122,7 +122,7 @@ export const useQueryRange = (
122
122
  }, [stepCountStr, defaultStepCount, setStepCount]);
123
123
 
124
124
  const {data, isLoading, error} = useGrpcQuery<QueryRangeResponse | undefined>({
125
- key: ['query-range', queryExpression, start, end, sumBy.join(','), stepCount, metadata],
125
+ key: ['query-range', queryExpression, start, end, (sumBy ?? []).join(','), stepCount, metadata],
126
126
  queryFn: async () => {
127
127
  const stepDuration = getStepDuration(start, end, stepCount);
128
128
  const {response} = await client.queryRange(
@@ -35,7 +35,7 @@ import MatchersInput, {useLabelNames} from '../MatchersInput/index';
35
35
  import {useMetricsGraphDimensions} from '../MetricsGraph/useMetricsGraphDimensions';
36
36
  import ProfileMetricsGraph, {ProfileMetricsEmptyState} from '../ProfileMetricsGraph';
37
37
  import ProfileTypeSelector from '../ProfileTypeSelector/index';
38
- import {useSumBy} from '../useSumBy';
38
+ import {useDefaultSumBy, useSumBySelection} from '../useSumBy';
39
39
  import {useAutoQuerySelector} from './useAutoQuerySelector';
40
40
 
41
41
  export interface QuerySelection {
@@ -97,7 +97,6 @@ const ProfileSelector = ({
97
97
  profileSelection,
98
98
  comparing,
99
99
  navigateTo,
100
- suffix = '',
101
100
  }: ProfileSelectorProps): JSX.Element => {
102
101
  const {
103
102
  loading: profileTypesLoading,
@@ -137,24 +136,15 @@ const ProfileSelector = ({
137
136
  : selectedLabelNamesResult.response.labelNames;
138
137
  }, [selectedLabelNamesResult]);
139
138
 
140
- const [sumBy, setSumBy, {userSelectedSumBy, isLoading: isSumByLoading}] = useSumBy(
139
+ const [sumBySelection, setUserSumBySelection, {isLoading: sumBySelectionLoading}] =
140
+ useSumBySelection(profileType, labelNamesLoading, labels, {
141
+ defaultValue: querySelection.sumBy,
142
+ });
143
+
144
+ const {defaultSumBy, isLoading: defaultSumByLoading} = useDefaultSumBy(
141
145
  selectedProfileType,
142
146
  selectedLabelNamesLoading,
143
- selectedLabels,
144
- {
145
- urlParamKey: 'sum_by' + suffix,
146
- }
147
- );
148
-
149
- const [sumBySelection, setSumBySelection, {isLoading: isSumBySelectionLoading}] = useSumBy(
150
- profileType,
151
- labelNamesLoading,
152
- labels,
153
- {
154
- urlParamKey: 'sum_by_selection' + suffix,
155
- withURLUpdate: false,
156
- defaultValue: userSelectedSumBy,
157
- }
147
+ selectedLabels
158
148
  );
159
149
 
160
150
  useEffect(() => {
@@ -181,9 +171,6 @@ const ProfileSelector = ({
181
171
  const selectedProfileName = query.profileName();
182
172
 
183
173
  const setNewQueryExpression = (expr: string, updateTs = false): void => {
184
- if (!isSumBySelectionLoading) {
185
- setSumBy(sumBySelection);
186
- }
187
174
  const query = enforcedProfileName !== '' ? enforcedProfileNameQuery() : Query.parse(expr);
188
175
  const delta = query.profileType().delta;
189
176
  const from = timeRangeSelection.getFromMs(updateTs);
@@ -200,6 +187,7 @@ const ProfileSelector = ({
200
187
  from,
201
188
  to,
202
189
  timeSelection: timeRangeSelection.getRangeKey(),
190
+ sumBy: sumBySelection,
203
191
  ...mergeParams,
204
192
  });
205
193
  };
@@ -269,8 +257,9 @@ const ProfileSelector = ({
269
257
  profileTypesData,
270
258
  setProfileName,
271
259
  setQueryExpression,
272
- querySelection: {...querySelection, sumBy},
260
+ querySelection: {...querySelection, sumBy: sumBySelection},
273
261
  navigateTo,
262
+ loading: sumBySelectionLoading,
274
263
  });
275
264
 
276
265
  const searchDisabled =
@@ -318,14 +307,15 @@ const ProfileSelector = ({
318
307
  options={labels.map(label => ({label, value: label}))}
319
308
  className="parca-select-container text-sm w-80"
320
309
  classNamePrefix="parca-select"
321
- value={sumBySelection.map(sumBy => ({label: sumBy, value: sumBy}))}
310
+ value={(sumBySelection ?? []).map(sumBy => ({label: sumBy, value: sumBy}))}
322
311
  onChange={selectedOptions => {
323
- setSumBySelection(selectedOptions.map(option => option.value));
312
+ setUserSumBySelection(selectedOptions.map(option => option.value));
324
313
  }}
325
314
  placeholder="Labels..."
326
315
  styles={{
327
316
  indicatorSeparator: () => ({display: 'none'}),
328
317
  }}
318
+ isDisabled={!profileType.delta}
329
319
  />
330
320
  </div>
331
321
  <DateTimeRangePicker
@@ -361,8 +351,8 @@ const ProfileSelector = ({
361
351
  to={querySelection.to}
362
352
  profile={profileSelection}
363
353
  comparing={comparing}
364
- sumBy={sumBy}
365
- sumByLoading={isSumByLoading}
354
+ sumBy={querySelection.sumBy ?? defaultSumBy ?? []}
355
+ sumByLoading={defaultSumByLoading}
366
356
  setTimeRange={(range: DateTimeRange) => {
367
357
  const from = range.getFromMs();
368
358
  const to = range.getToMs();
@@ -28,6 +28,7 @@ interface Props {
28
28
  setQueryExpression: () => void;
29
29
  querySelection: QuerySelection;
30
30
  navigateTo: NavigateFunction;
31
+ loading: boolean;
31
32
  }
32
33
 
33
34
  export const useAutoQuerySelector = ({
@@ -37,6 +38,7 @@ export const useAutoQuerySelector = ({
37
38
  setQueryExpression,
38
39
  querySelection,
39
40
  navigateTo,
41
+ loading,
40
42
  }: Props): void => {
41
43
  const autoQuery = useAppSelector(selectAutoQuery);
42
44
  const dispatch = useAppDispatch();
@@ -46,6 +48,9 @@ export const useAutoQuerySelector = ({
46
48
  const expressionA = queryParams.get('expression_a');
47
49
 
48
50
  useEffect(() => {
51
+ if (loading) {
52
+ return;
53
+ }
49
54
  if (comparing && expressionA !== null && expressionA !== undefined) {
50
55
  if (querySelection.expression === undefined) {
51
56
  return;
@@ -98,7 +103,7 @@ export const useAutoQuerySelector = ({
98
103
  dashboard_items: ['icicle'],
99
104
  });
100
105
  }
101
- }, [comparing, querySelection, navigateTo, expressionA, dispatch]);
106
+ }, [comparing, querySelection, navigateTo, expressionA, dispatch, loading]);
102
107
 
103
108
  // Effect to load some initial data on load when is no selection
104
109
  useEffect(() => {
@@ -152,7 +157,8 @@ export const useAutoQuerySelector = ({
152
157
  autoQuery !== 'true' ||
153
158
  profileTypesData?.types == null ||
154
159
  profileTypesData.types.length < 1 ||
155
- selectedProfileName.length === 0
160
+ selectedProfileName.length === 0 ||
161
+ loading
156
162
  ) {
157
163
  return;
158
164
  }
@@ -166,5 +172,6 @@ export const useAutoQuerySelector = ({
166
172
  setProfileName,
167
173
  dispatch,
168
174
  selectedProfileName,
175
+ loading,
169
176
  ]);
170
177
  };
package/src/useSumBy.ts CHANGED
@@ -11,9 +11,8 @@
11
11
  // See the License for the specific language governing permissions and
12
12
  // limitations under the License.
13
13
 
14
- import {useCallback, useMemo} from 'react';
14
+ import {useCallback, useMemo, useState} from 'react';
15
15
 
16
- import {useParcaContext, useURLState} from '@parca/components';
17
16
  import {ProfileType} from '@parca/parser';
18
17
 
19
18
  export const DEFAULT_EMPTY_SUM_BY: string[] = [];
@@ -26,6 +25,10 @@ const getDefaultSumBy = (
26
25
  return undefined;
27
26
  }
28
27
 
28
+ if (!profile.delta) {
29
+ return DEFAULT_EMPTY_SUM_BY;
30
+ }
31
+
29
32
  if (labels.includes('comm')) {
30
33
  return ['comm'];
31
34
  }
@@ -41,80 +44,104 @@ const getDefaultSumBy = (
41
44
  return undefined;
42
45
  };
43
46
 
44
- export const useSumBy = (
47
+ export const useSumBySelection = (
45
48
  profileType: ProfileType | undefined,
46
49
  labelNamesLoading: boolean,
47
50
  labels: string[] | undefined,
48
51
  {
49
- urlParamKey = 'sum_by',
50
- withURLUpdate = true,
51
52
  defaultValue,
52
53
  }: {
53
- urlParamKey?: string;
54
- withURLUpdate?: boolean;
55
54
  defaultValue?: string[];
56
55
  } = {}
57
56
  ): [
58
- string[],
57
+ string[] | undefined,
59
58
  (labels: string[]) => void,
60
- {userSelectedSumBy: string[] | undefined; isLoading: boolean}
59
+ {
60
+ isLoading: boolean;
61
+ }
61
62
  ] => {
62
- const {navigateTo} = useParcaContext();
63
- const [userSelectedSumByParam, setUserSelectedSumByParam] = useURLState({
64
- param: urlParamKey,
65
- navigateTo,
66
- withURLUpdate,
67
- });
68
-
69
- const userSelectedSumBy = useMemo<string[] | undefined>(() => {
70
- if (userSelectedSumByParam?.length === 0) {
71
- return undefined;
72
- }
73
-
74
- if (userSelectedSumByParam === '__none__') {
75
- return [];
76
- }
63
+ const [userSelectedSumBy, setUserSelectedSumBy] = useState<Record<string, string[] | undefined>>(
64
+ profileType != null ? {[profileType.toString()]: defaultValue} : {}
65
+ );
77
66
 
78
- if (userSelectedSumByParam === undefined && defaultValue !== undefined) {
79
- return defaultValue;
80
- }
67
+ const setSumBy = useCallback(
68
+ (sumBy: string[]) => {
69
+ setUserSelectedSumBy(prev => {
70
+ if (profileType == null) {
71
+ return prev;
72
+ }
73
+
74
+ return {
75
+ ...prev,
76
+ [profileType.toString()]: sumBy,
77
+ };
78
+ });
79
+ },
80
+ [setUserSelectedSumBy, profileType]
81
+ );
81
82
 
82
- if (typeof userSelectedSumByParam === 'string') {
83
- return [userSelectedSumByParam];
84
- }
83
+ const {defaultSumBy} = useDefaultSumBy(profileType, labelNamesLoading, labels);
85
84
 
86
- return userSelectedSumByParam;
85
+ let sumBy =
86
+ userSelectedSumBy[profileType?.toString() ?? ''] ?? defaultSumBy ?? DEFAULT_EMPTY_SUM_BY;
87
87
 
88
- // eslint-disable-next-line react-hooks/exhaustive-deps
89
- }, [userSelectedSumByParam]);
88
+ if (profileType?.delta !== true) {
89
+ sumBy = DEFAULT_EMPTY_SUM_BY;
90
+ }
90
91
 
91
- const setUserSelectedSumBy = useCallback(
92
- (sumBy: string[]) => {
93
- if (sumBy.length === 0) {
94
- setUserSelectedSumByParam('__none__');
95
- return;
96
- }
97
-
98
- if (sumBy.length === 1) {
99
- // Handle this separately to take care of the empty string scenario
100
- setUserSelectedSumByParam(sumBy[0]);
101
- return;
102
- }
103
-
104
- setUserSelectedSumByParam(sumBy);
92
+ return [
93
+ labelNamesLoading ? undefined : sumBy,
94
+ setSumBy,
95
+ {
96
+ isLoading: labelNamesLoading,
105
97
  },
106
- [setUserSelectedSumByParam]
107
- );
98
+ ];
99
+ };
108
100
 
101
+ export const useDefaultSumBy = (
102
+ profileType: ProfileType | undefined,
103
+ labelNamesLoading: boolean,
104
+ labels: string[] | undefined
105
+ ): {defaultSumBy: string[] | undefined; isLoading: boolean} => {
109
106
  const defaultSumBy = useMemo(() => {
110
107
  return getDefaultSumBy(profileType, labels);
111
108
  }, [profileType, labels]);
112
109
 
113
- let sumBy = userSelectedSumBy ?? defaultSumBy ?? DEFAULT_EMPTY_SUM_BY;
110
+ return {defaultSumBy, isLoading: labelNamesLoading};
111
+ };
114
112
 
115
- if (profileType?.delta !== true) {
116
- sumBy = DEFAULT_EMPTY_SUM_BY;
113
+ const getSumByFromParam = (param: string | string[] | undefined): string[] | undefined => {
114
+ if (param?.length === 0) {
115
+ return undefined;
116
+ }
117
+
118
+ if (param === '__none__') {
119
+ return [];
120
+ }
121
+
122
+ if (typeof param === 'string') {
123
+ return [param];
124
+ }
125
+
126
+ return param;
127
+ };
128
+
129
+ export const useSumByFromParams = (param: string | string[] | undefined): string[] | undefined => {
130
+ const sumBy = useMemo(() => {
131
+ return getSumByFromParam(param);
132
+ }, [param]);
133
+
134
+ return sumBy;
135
+ };
136
+
137
+ export const sumByToParam = (sumBy: string[] | undefined): string | string[] | undefined => {
138
+ if (sumBy === undefined) {
139
+ return undefined;
140
+ }
141
+
142
+ if (sumBy.length === 0) {
143
+ return '__none__';
117
144
  }
118
145
 
119
- return [sumBy, setUserSelectedSumBy, {userSelectedSumBy, isLoading: labelNamesLoading}];
146
+ return sumBy;
120
147
  };