@parca/profile 0.16.465 → 0.16.466

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.
@@ -11,26 +11,19 @@
11
11
  // See the License for the specific language governing permissions and
12
12
  // limitations under the License.
13
13
 
14
- import {useEffect, useMemo} from 'react';
14
+ import {useEffect} from 'react';
15
15
 
16
- import {RpcError} from '@protobuf-ts/runtime-rpc';
17
16
  import {AnimatePresence, motion} from 'framer-motion';
18
17
 
19
- import {Duration, Label, QueryRangeResponse, QueryServiceClient, Timestamp} from '@parca/client';
20
- import {
21
- DateTimeRange,
22
- MetricsGraphSkeleton,
23
- useGrpcMetadata,
24
- useParcaContext,
25
- useURLState,
26
- } from '@parca/components';
18
+ import {Label, QueryServiceClient} from '@parca/client';
19
+ import {DateTimeRange, MetricsGraphSkeleton, useParcaContext} from '@parca/components';
27
20
  import {Query} from '@parca/parser';
28
- import {capitalizeOnlyFirstLetter, getStepDuration} from '@parca/utilities';
21
+ import {capitalizeOnlyFirstLetter} from '@parca/utilities';
29
22
 
30
23
  import {MergedProfileSelection, ProfileSelection} from '..';
31
24
  import MetricsGraph from '../MetricsGraph';
32
25
  import {useMetricsGraphDimensions} from '../MetricsGraph/useMetricsGraphDimensions';
33
- import useGrpcQuery from '../useGrpcQuery';
26
+ import {useQueryRange} from './hooks/useQueryRange';
34
27
 
35
28
  interface ProfileMetricsEmptyStateProps {
36
29
  message: string;
@@ -76,78 +69,6 @@ interface ProfileMetricsGraphProps {
76
69
  comparing?: boolean;
77
70
  }
78
71
 
79
- export interface IQueryRangeState {
80
- response: QueryRangeResponse | null;
81
- isLoading: boolean;
82
- error: RpcError | null;
83
- }
84
-
85
- const getStepCountFromScreenWidth = (pixelsPerPoint: number): number => {
86
- let width =
87
- // eslint-disable-next-line @typescript-eslint/strict-boolean-expressions
88
- window.innerWidth || document.documentElement.clientWidth || document.body.clientWidth;
89
-
90
- // subtract the padding around the graph
91
- width = width - (20 + 24 + 68) * 2;
92
- return Math.round(width / pixelsPerPoint);
93
- };
94
-
95
- export const useQueryRange = (
96
- client: QueryServiceClient,
97
- queryExpression: string,
98
- start: number,
99
- end: number,
100
- sumBy: string[],
101
- skip = false
102
- ): IQueryRangeState => {
103
- const metadata = useGrpcMetadata();
104
- const [stepCountStr, setStepCount] = useURLState('step_count');
105
-
106
- const defaultStepCount = useMemo(() => {
107
- return getStepCountFromScreenWidth(10);
108
- }, []);
109
-
110
- const stepCount = useMemo(() => {
111
- if (stepCountStr != null) {
112
- return parseInt(stepCountStr as string, 10);
113
- }
114
-
115
- return defaultStepCount;
116
- }, [stepCountStr, defaultStepCount]);
117
-
118
- useEffect(() => {
119
- if (stepCountStr == null) {
120
- setStepCount(defaultStepCount.toString());
121
- }
122
- }, [stepCountStr, defaultStepCount, setStepCount]);
123
-
124
- const {data, isLoading, error} = useGrpcQuery<QueryRangeResponse | undefined>({
125
- key: ['query-range', queryExpression, start, end, (sumBy ?? []).join(','), stepCount, metadata],
126
- queryFn: async () => {
127
- const stepDuration = getStepDuration(start, end, stepCount);
128
- const {response} = await client.queryRange(
129
- {
130
- query: queryExpression,
131
- start: Timestamp.fromDate(new Date(start)),
132
- end: Timestamp.fromDate(new Date(end)),
133
- step: Duration.create(stepDuration),
134
- limit: 0,
135
- sumBy,
136
- },
137
- {meta: metadata}
138
- );
139
- return response;
140
- },
141
- options: {
142
- retry: false,
143
- enabled: !skip,
144
- staleTime: 1000 * 60 * 5, // 5 minutes
145
- },
146
- });
147
-
148
- return {isLoading, error: error as RpcError | null, response: data ?? null};
149
- };
150
-
151
72
  const ProfileMetricsGraph = ({
152
73
  queryClient,
153
74
  queryExpression,
@@ -184,6 +105,7 @@ const ProfileMetricsGraph = ({
184
105
  }, [perf, response]);
185
106
 
186
107
  const series = response?.series;
108
+
187
109
  const dataAvailable = series !== null && series !== undefined && series?.length > 0;
188
110
 
189
111
  const loading = metricsGraphLoading;
@@ -18,8 +18,9 @@ import {DateTimeRange} from '@parca/components';
18
18
  import {Query} from '@parca/parser';
19
19
 
20
20
  import {MergedProfileSelection, ProfileSelection} from '..';
21
+ import UtilizationMetricsGraph from '../MetricsGraph/UtilizationMetrics';
21
22
  import ProfileMetricsGraph, {ProfileMetricsEmptyState} from '../ProfileMetricsGraph';
22
- import {QuerySelection} from './index';
23
+ import {QuerySelection, type UtilizationMetrics as UtilizationMetricsType} from './index';
23
24
 
24
25
  interface MetricsGraphSectionProps {
25
26
  showMetricsGraph: boolean;
@@ -38,6 +39,8 @@ interface MetricsGraphSectionProps {
38
39
  query: Query;
39
40
  setNewQueryExpression: (queryExpression: string) => void;
40
41
  setQueryExpression: (updateTs?: boolean) => void;
42
+ utilizationMetrics?: UtilizationMetricsType[];
43
+ utilizationMetricsLoading?: boolean;
41
44
  }
42
45
 
43
46
  export function MetricsGraphSection({
@@ -56,6 +59,8 @@ export function MetricsGraphSection({
56
59
  selectProfile,
57
60
  query,
58
61
  setNewQueryExpression,
62
+ utilizationMetrics,
63
+ utilizationMetricsLoading,
59
64
  }: MetricsGraphSectionProps): JSX.Element {
60
65
  const handleTimeRangeChange = (range: DateTimeRange): void => {
61
66
  const from = range.getFromMs();
@@ -141,31 +146,46 @@ export function MetricsGraphSection({
141
146
  {showMetricsGraph ? 'Hide' : 'Show'} Metrics Graph
142
147
  </button>
143
148
  {showMetricsGraph && (
144
- <div style={{height: heightStyle}}>
145
- {querySelection.expression !== '' &&
146
- querySelection.from !== undefined &&
147
- querySelection.to !== undefined ? (
148
- <ProfileMetricsGraph
149
- queryClient={queryClient}
150
- queryExpression={querySelection.expression}
151
- from={querySelection.from}
152
- to={querySelection.to}
153
- profile={profileSelection}
154
- comparing={comparing}
155
- sumBy={querySelection.sumBy ?? sumBy ?? []}
156
- sumByLoading={defaultSumByLoading}
157
- setTimeRange={handleTimeRangeChange}
158
- addLabelMatcher={addLabelMatcher}
159
- onPointClick={handlePointClick}
160
- />
161
- ) : (
162
- profileSelection === null && (
163
- <div className="p-2">
164
- <ProfileMetricsEmptyState message="Please select a profile type and click 'Search' to begin." />
165
- </div>
166
- )
167
- )}
168
- </div>
149
+ <>
150
+ <div style={{height: heightStyle}}>
151
+ {querySelection.expression !== '' &&
152
+ querySelection.from !== undefined &&
153
+ querySelection.to !== undefined ? (
154
+ <>
155
+ {utilizationMetrics !== undefined ? (
156
+ <UtilizationMetricsGraph
157
+ data={utilizationMetrics}
158
+ addLabelMatcher={addLabelMatcher}
159
+ setTimeRange={handleTimeRangeChange}
160
+ utilizationMetricsLoading={utilizationMetricsLoading}
161
+ />
162
+ ) : (
163
+ <>
164
+ <ProfileMetricsGraph
165
+ queryClient={queryClient}
166
+ queryExpression={querySelection.expression}
167
+ from={querySelection.from}
168
+ to={querySelection.to}
169
+ profile={profileSelection}
170
+ comparing={comparing}
171
+ sumBy={querySelection.sumBy ?? sumBy ?? []}
172
+ sumByLoading={defaultSumByLoading}
173
+ setTimeRange={handleTimeRangeChange}
174
+ addLabelMatcher={addLabelMatcher}
175
+ onPointClick={handlePointClick}
176
+ />
177
+ </>
178
+ )}
179
+ </>
180
+ ) : (
181
+ profileSelection === null && (
182
+ <div className="p-2">
183
+ <ProfileMetricsEmptyState message="Please select a profile type and click 'Search' to begin." />
184
+ </div>
185
+ )
186
+ )}
187
+ </div>
188
+ </>
169
189
  )}
170
190
  </div>
171
191
  );
@@ -53,6 +53,17 @@ interface ProfileSelectorFeatures {
53
53
  disableProfileTypesDropdown?: boolean;
54
54
  }
55
55
 
56
+ export interface UtilizationMetrics {
57
+ timestamp: number;
58
+ value: number;
59
+ resource: {
60
+ [key: string]: string;
61
+ };
62
+ attributes: {
63
+ [key: string]: string;
64
+ };
65
+ }
66
+
56
67
  interface ProfileSelectorProps extends ProfileSelectorFeatures {
57
68
  queryClient: QueryServiceClient;
58
69
  querySelection: QuerySelection;
@@ -65,6 +76,8 @@ interface ProfileSelectorProps extends ProfileSelectorFeatures {
65
76
  navigateTo: NavigateFunction;
66
77
  setDisplayHideMetricsGraphButton: Dispatch<SetStateAction<boolean>>;
67
78
  suffix?: string;
79
+ utilizationMetrics?: UtilizationMetrics[];
80
+ utilizationMetricsLoading?: boolean;
68
81
  }
69
82
 
70
83
  export interface IProfileTypesResult {
@@ -108,6 +121,8 @@ const ProfileSelector = ({
108
121
  showProfileTypeSelector = true,
109
122
  disableExplorativeQuerying = false,
110
123
  setDisplayHideMetricsGraphButton,
124
+ utilizationMetrics,
125
+ utilizationMetricsLoading,
111
126
  }: ProfileSelectorProps): JSX.Element => {
112
127
  const {
113
128
  loading: profileTypesLoading,
@@ -288,7 +303,6 @@ const ProfileSelector = ({
288
303
  </div>
289
304
  )}
290
305
  </div>
291
-
292
306
  <MetricsGraphSection
293
307
  showMetricsGraph={showMetricsGraph}
294
308
  setDisplayHideMetricsGraphButton={setDisplayHideMetricsGraphButton}
@@ -306,6 +320,8 @@ const ProfileSelector = ({
306
320
  query={query}
307
321
  setQueryExpression={setQueryExpression}
308
322
  setNewQueryExpression={setNewQueryExpression}
323
+ utilizationMetrics={utilizationMetrics}
324
+ utilizationMetricsLoading={utilizationMetricsLoading}
309
325
  />
310
326
  </>
311
327
  );