@parca/profile 0.16.398 → 0.16.400

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,14 @@
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.400](https://github.com/parca-dev/parca/compare/@parca/profile@0.16.399...@parca/profile@0.16.400) (2024-07-02)
7
+
8
+ **Note:** Version bump only for package @parca/profile
9
+
10
+ ## [0.16.399](https://github.com/parca-dev/parca/compare/@parca/profile@0.16.398...@parca/profile@0.16.399) (2024-07-02)
11
+
12
+ **Note:** Version bump only for package @parca/profile
13
+
6
14
  ## [0.16.398](https://github.com/parca-dev/parca/compare/@parca/profile@0.16.397...@parca/profile@0.16.398) (2024-07-01)
7
15
 
8
16
  **Note:** Version bump only for package @parca/profile
@@ -12,6 +12,7 @@ interface ProfileMetricsGraphProps {
12
12
  profile: ProfileSelection | null;
13
13
  from: number;
14
14
  to: number;
15
+ timeRange: DateTimeRange;
15
16
  setTimeRange: (range: DateTimeRange) => void;
16
17
  addLabelMatcher: (labels: {
17
18
  key: string;
@@ -26,8 +27,9 @@ interface ProfileMetricsGraphProps {
26
27
  export interface IQueryRangeState {
27
28
  response: QueryRangeResponse | null;
28
29
  isLoading: boolean;
30
+ isRefreshing?: boolean;
29
31
  error: RpcError | null;
30
32
  }
31
- export declare const useQueryRange: (client: QueryServiceClient, queryExpression: string, start: number, end: number, sumBy?: string[], skip?: boolean) => IQueryRangeState;
32
- declare const ProfileMetricsGraph: ({ queryClient, queryExpression, profile, from, to, setTimeRange, addLabelMatcher, onPointClick, comparing, }: ProfileMetricsGraphProps) => JSX.Element;
33
+ export declare const useQueryRange: (client: QueryServiceClient, queryExpression: string, start: number, end: number, timeRange: DateTimeRange, sumBy?: string[], skip?: boolean) => IQueryRangeState;
34
+ declare const ProfileMetricsGraph: ({ queryClient, queryExpression, profile, from, to, setTimeRange, addLabelMatcher, onPointClick, comparing, timeRange, }: ProfileMetricsGraphProps) => JSX.Element;
33
35
  export default ProfileMetricsGraph;
@@ -11,7 +11,7 @@ import { jsx as _jsx } from "react/jsx-runtime";
11
11
  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
12
  // See the License for the specific language governing permissions and
13
13
  // limitations under the License.
14
- import { useEffect, useState } from 'react';
14
+ import { useEffect, useRef, useState } from 'react';
15
15
  import { AnimatePresence, motion } from 'framer-motion';
16
16
  import { Duration, Timestamp } from '@parca/client';
17
17
  import { MetricsGraphSkeleton, useGrpcMetadata, useParcaContext, } from '@parca/components';
@@ -19,7 +19,6 @@ import { Query } from '@parca/parser';
19
19
  import { capitalizeOnlyFirstLetter, getStepDuration } from '@parca/utilities';
20
20
  import MetricsGraph from '../MetricsGraph';
21
21
  import { useMetricsGraphDimensions } from '../MetricsGraph/useMetricsGraphDimensions';
22
- import useDelayedLoader from '../useDelayedLoader';
23
22
  const ErrorContent = ({ errorMessage }) => {
24
23
  return (_jsx("div", { className: "relative rounded border border-red-400 bg-red-100 px-4 py-3 text-red-700", role: "alert", children: _jsx("span", { className: "block sm:inline", children: errorMessage }) }));
25
24
  };
@@ -27,7 +26,8 @@ export const ProfileMetricsEmptyState = ({ message }) => {
27
26
  return (_jsx("div", { className: "flex h-full w-full flex-col items-center justify-center", children: _jsx("p", { children: message }) }));
28
27
  };
29
28
  const EMPTY_SUM_BY = [];
30
- export const useQueryRange = (client, queryExpression, start, end, sumBy = EMPTY_SUM_BY, skip = false) => {
29
+ export const useQueryRange = (client, queryExpression, start, end, timeRange, sumBy = EMPTY_SUM_BY, skip = false) => {
30
+ const previousQueryParams = useRef({});
31
31
  const [isLoading, setLoading] = useState(!skip);
32
32
  const [state, setState] = useState({
33
33
  response: null,
@@ -40,6 +40,15 @@ export const useQueryRange = (client, queryExpression, start, end, sumBy = EMPTY
40
40
  if (skip) {
41
41
  return;
42
42
  }
43
+ if (previousQueryParams.current.queryExpression !== queryExpression ||
44
+ previousQueryParams.current.timeRange !== timeRange) {
45
+ previousQueryParams.current.queryExpression = queryExpression;
46
+ previousQueryParams.current.timeRange = timeRange;
47
+ previousQueryParams.current.isRefresh = undefined;
48
+ }
49
+ else {
50
+ previousQueryParams.current.isRefresh = true;
51
+ }
43
52
  setLoading(true);
44
53
  const stepDuration = getStepDuration(start, end);
45
54
  const call = client.queryRange({
@@ -59,14 +68,18 @@ export const useQueryRange = (client, queryExpression, start, end, sumBy = EMPTY
59
68
  .catch(error => {
60
69
  setState({ response: null, isLoading: false, error });
61
70
  setLoading(false);
71
+ })
72
+ .finally(() => {
73
+ if (previousQueryParams.current.isRefresh !== undefined) {
74
+ previousQueryParams.current.isRefresh = undefined;
75
+ }
62
76
  });
63
77
  })();
64
- }, [client, queryExpression, start, end, metadata, sumBy, skip]);
65
- return { ...state, isLoading };
78
+ }, [client, queryExpression, start, end, metadata, timeRange, sumBy, skip]);
79
+ return { ...state, isLoading, isRefreshing: previousQueryParams.current.isRefresh };
66
80
  };
67
- const ProfileMetricsGraph = ({ queryClient, queryExpression, profile, from, to, setTimeRange, addLabelMatcher, onPointClick, comparing = false, }) => {
68
- const { isLoading, response, error } = useQueryRange(queryClient, queryExpression, from, to);
69
- const isLoaderVisible = useDelayedLoader(isLoading);
81
+ const ProfileMetricsGraph = ({ queryClient, queryExpression, profile, from, to, setTimeRange, addLabelMatcher, onPointClick, comparing = false, timeRange, }) => {
82
+ const { isLoading: metricsGraphLoading, response, error, } = useQueryRange(queryClient, queryExpression, from, to, timeRange);
70
83
  const { onError, perf, authenticationErrorMessage, isDarkMode } = useParcaContext();
71
84
  const { width, height, margin, heightStyle } = useMetricsGraphDimensions(comparing);
72
85
  useEffect(() => {
@@ -82,7 +95,6 @@ const ProfileMetricsGraph = ({ queryClient, queryExpression, profile, from, to,
82
95
  }, [perf, response]);
83
96
  const series = response?.series;
84
97
  const dataAvailable = series !== null && series !== undefined && series?.length > 0;
85
- const metricsGraphLoading = isLoaderVisible || (isLoading && !dataAvailable);
86
98
  if (metricsGraphLoading) {
87
99
  return _jsx(MetricsGraphSkeleton, { heightStyle: heightStyle, isDarkMode: isDarkMode });
88
100
  }
@@ -149,7 +149,7 @@ const ProfileSelector = ({ queryClient, querySelection, selectProfile, selectQue
149
149
  }, 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 &&
150
150
  querySelection.expression.length > 0 &&
151
151
  querySelection.from !== undefined &&
152
- 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, setTimeRange: (range) => {
152
+ 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, timeRange: timeRangeSelection, setTimeRange: (range) => {
153
153
  const from = range.getFromMs();
154
154
  const to = range.getToMs();
155
155
  let mergedProfileParams = {};
package/package.json CHANGED
@@ -1,18 +1,18 @@
1
1
  {
2
2
  "name": "@parca/profile",
3
- "version": "0.16.398",
3
+ "version": "0.16.400",
4
4
  "description": "Profile viewing libraries",
5
5
  "dependencies": {
6
6
  "@headlessui/react": "^1.7.19",
7
7
  "@iconify/react": "^4.0.0",
8
- "@parca/client": "0.16.118",
9
- "@parca/components": "0.16.288",
8
+ "@parca/client": "0.16.119",
9
+ "@parca/components": "0.16.289",
10
10
  "@parca/dynamicsize": "0.16.65",
11
- "@parca/hooks": "0.0.63",
11
+ "@parca/hooks": "0.0.64",
12
12
  "@parca/icons": "0.16.70",
13
13
  "@parca/parser": "0.16.77",
14
- "@parca/store": "0.16.152",
15
- "@parca/utilities": "0.0.79",
14
+ "@parca/store": "0.16.153",
15
+ "@parca/utilities": "0.0.80",
16
16
  "@popperjs/core": "^2.11.8",
17
17
  "@protobuf-ts/runtime-rpc": "^2.5.0",
18
18
  "@tanstack/react-query": "^4.0.5",
@@ -73,5 +73,5 @@
73
73
  "access": "public",
74
74
  "registry": "https://registry.npmjs.org/"
75
75
  },
76
- "gitHead": "cf52d4ef66a1bb394cb5e0b4da16f84935a34784"
76
+ "gitHead": "9d13687d6648e70412369d67999b77633d384199"
77
77
  }
@@ -11,7 +11,7 @@
11
11
  // See the License for the specific language governing permissions and
12
12
  // limitations under the License.
13
13
 
14
- import {useEffect, useState} from 'react';
14
+ import {useEffect, useRef, useState} from 'react';
15
15
 
16
16
  import {RpcError} from '@protobuf-ts/runtime-rpc';
17
17
  import {AnimatePresence, motion} from 'framer-motion';
@@ -29,7 +29,6 @@ import {capitalizeOnlyFirstLetter, getStepDuration} from '@parca/utilities';
29
29
  import {MergedProfileSelection, ProfileSelection} from '..';
30
30
  import MetricsGraph from '../MetricsGraph';
31
31
  import {useMetricsGraphDimensions} from '../MetricsGraph/useMetricsGraphDimensions';
32
- import useDelayedLoader from '../useDelayedLoader';
33
32
 
34
33
  interface ProfileMetricsEmptyStateProps {
35
34
  message: string;
@@ -60,6 +59,7 @@ interface ProfileMetricsGraphProps {
60
59
  profile: ProfileSelection | null;
61
60
  from: number;
62
61
  to: number;
62
+ timeRange: DateTimeRange;
63
63
  setTimeRange: (range: DateTimeRange) => void;
64
64
  addLabelMatcher: (
65
65
  labels: {key: string; value: string} | Array<{key: string; value: string}>
@@ -76,6 +76,7 @@ interface ProfileMetricsGraphProps {
76
76
  export interface IQueryRangeState {
77
77
  response: QueryRangeResponse | null;
78
78
  isLoading: boolean;
79
+ isRefreshing?: boolean;
79
80
  error: RpcError | null;
80
81
  }
81
82
 
@@ -86,9 +87,15 @@ export const useQueryRange = (
86
87
  queryExpression: string,
87
88
  start: number,
88
89
  end: number,
90
+ timeRange: DateTimeRange,
89
91
  sumBy: string[] = EMPTY_SUM_BY,
90
92
  skip = false
91
93
  ): IQueryRangeState => {
94
+ const previousQueryParams = useRef<{
95
+ queryExpression?: string;
96
+ timeRange?: DateTimeRange;
97
+ isRefresh?: boolean;
98
+ }>({});
92
99
  const [isLoading, setLoading] = useState<boolean>(!skip);
93
100
  const [state, setState] = useState<IQueryRangeState>({
94
101
  response: null,
@@ -102,6 +109,18 @@ export const useQueryRange = (
102
109
  if (skip) {
103
110
  return;
104
111
  }
112
+
113
+ if (
114
+ previousQueryParams.current.queryExpression !== queryExpression ||
115
+ previousQueryParams.current.timeRange !== timeRange
116
+ ) {
117
+ previousQueryParams.current.queryExpression = queryExpression;
118
+ previousQueryParams.current.timeRange = timeRange;
119
+ previousQueryParams.current.isRefresh = undefined;
120
+ } else {
121
+ previousQueryParams.current.isRefresh = true;
122
+ }
123
+
105
124
  setLoading(true);
106
125
 
107
126
  const stepDuration = getStepDuration(start, end);
@@ -126,11 +145,16 @@ export const useQueryRange = (
126
145
  .catch(error => {
127
146
  setState({response: null, isLoading: false, error});
128
147
  setLoading(false);
148
+ })
149
+ .finally(() => {
150
+ if (previousQueryParams.current.isRefresh !== undefined) {
151
+ previousQueryParams.current.isRefresh = undefined;
152
+ }
129
153
  });
130
154
  })();
131
- }, [client, queryExpression, start, end, metadata, sumBy, skip]);
155
+ }, [client, queryExpression, start, end, metadata, timeRange, sumBy, skip]);
132
156
 
133
- return {...state, isLoading};
157
+ return {...state, isLoading, isRefreshing: previousQueryParams.current.isRefresh};
134
158
  };
135
159
 
136
160
  const ProfileMetricsGraph = ({
@@ -143,9 +167,13 @@ const ProfileMetricsGraph = ({
143
167
  addLabelMatcher,
144
168
  onPointClick,
145
169
  comparing = false,
170
+ timeRange,
146
171
  }: ProfileMetricsGraphProps): JSX.Element => {
147
- const {isLoading, response, error} = useQueryRange(queryClient, queryExpression, from, to);
148
- const isLoaderVisible = useDelayedLoader(isLoading);
172
+ const {
173
+ isLoading: metricsGraphLoading,
174
+ response,
175
+ error,
176
+ } = useQueryRange(queryClient, queryExpression, from, to, timeRange);
149
177
  const {onError, perf, authenticationErrorMessage, isDarkMode} = useParcaContext();
150
178
  const {width, height, margin, heightStyle} = useMetricsGraphDimensions(comparing);
151
179
 
@@ -166,8 +194,6 @@ const ProfileMetricsGraph = ({
166
194
  const series = response?.series;
167
195
  const dataAvailable = series !== null && series !== undefined && series?.length > 0;
168
196
 
169
- const metricsGraphLoading = isLoaderVisible || (isLoading && !dataAvailable);
170
-
171
197
  if (metricsGraphLoading) {
172
198
  return <MetricsGraphSkeleton heightStyle={heightStyle} isDarkMode={isDarkMode} />;
173
199
  }
@@ -287,6 +287,7 @@ const ProfileSelector = ({
287
287
  to={querySelection.to}
288
288
  profile={profileSelection}
289
289
  comparing={comparing}
290
+ timeRange={timeRangeSelection}
290
291
  setTimeRange={(range: DateTimeRange) => {
291
292
  const from = range.getFromMs();
292
293
  const to = range.getToMs();