Browse Source

feat(profiling): Allow users to select the aggregate in the slowest f… (#69777)

…unction widget

This was previously hard coded to p75. Instead, let's allow the user to
select average or any other available percentiles.
Tony Xiao 10 months ago
parent
commit
1459439929

+ 71 - 11
static/app/views/profiling/landing/landingWidgetSelector.tsx

@@ -13,7 +13,11 @@ import {FunctionTrendsWidget} from './functionTrendsWidget';
 import {SlowestFunctionsWidget} from './slowestFunctionsWidget';
 
 export type WidgetOption =
-  | 'slowest functions'
+  | 'slowest functions avg'
+  | 'slowest functions p50'
+  | 'slowest functions' // kept for backwards compatibility as the p75
+  | 'slowest functions p95'
+  | 'slowest functions p99'
   | 'regressed functions'
   | 'improved functions';
 
@@ -67,47 +71,103 @@ export function LandingWidgetSelector({
   );
 
   switch (selectedWidget) {
-    case 'slowest functions':
+    case 'regressed functions':
       return (
-        <SlowestFunctionsWidget
+        <FunctionTrendsWidget
           cursorName={cursorName}
           header={header}
+          trendFunction="p95()"
+          trendType="regression"
           userQuery={functionQuery}
           widgetHeight={widgetHeight}
         />
       );
-    case 'regressed functions':
+    case 'improved functions':
       return (
         <FunctionTrendsWidget
           cursorName={cursorName}
           header={header}
           trendFunction="p95()"
-          trendType="regression"
+          trendType="improvement"
           userQuery={functionQuery}
           widgetHeight={widgetHeight}
         />
       );
-    case 'improved functions':
+    case 'slowest functions avg':
       return (
-        <FunctionTrendsWidget
+        <SlowestFunctionsWidget
+          breakdownFunction="avg()"
+          cursorName={cursorName}
+          header={header}
+          userQuery={functionQuery}
+          widgetHeight={widgetHeight}
+        />
+      );
+    case 'slowest functions p50':
+      return (
+        <SlowestFunctionsWidget
+          breakdownFunction="p50()"
+          cursorName={cursorName}
+          header={header}
+          userQuery={functionQuery}
+          widgetHeight={widgetHeight}
+        />
+      );
+    case 'slowest functions p95':
+      return (
+        <SlowestFunctionsWidget
+          breakdownFunction="p95()"
+          cursorName={cursorName}
+          header={header}
+          userQuery={functionQuery}
+          widgetHeight={widgetHeight}
+        />
+      );
+    case 'slowest functions p99':
+      return (
+        <SlowestFunctionsWidget
+          breakdownFunction="p99()"
           cursorName={cursorName}
           header={header}
-          trendFunction="p95()"
-          trendType="improvement"
           userQuery={functionQuery}
           widgetHeight={widgetHeight}
         />
       );
+    case 'slowest functions':
     default:
-      throw new Error('unknown widget type');
+      return (
+        <SlowestFunctionsWidget
+          breakdownFunction="p75()"
+          cursorName={cursorName}
+          header={header}
+          userQuery={functionQuery}
+          widgetHeight={widgetHeight}
+        />
+      );
   }
 }
 
 const WIDGET_OPTIONS: SelectOption<WidgetOption>[] = [
   {
-    label: t('Slowest Functions'),
+    label: t('Slowest Functions (breakdown by AVG)'),
+    value: 'slowest functions avg' as const,
+  },
+  {
+    label: t('Slowest Functions (breakdown by P50)'),
+    value: 'slowest functions p50' as const,
+  },
+  {
+    label: t('Slowest Functions (breakdown by P75)'),
     value: 'slowest functions' as const,
   },
+  {
+    label: t('Slowest Functions (breakdown by P95)'),
+    value: 'slowest functions p95' as const,
+  },
+  {
+    label: t('Slowest Functions (breakdown by P99)'),
+    value: 'slowest functions p99' as const,
+  },
   {
     label: t('Most Regressed Functions'),
     value: 'regressed functions' as const,

+ 5 - 5
static/app/views/profiling/landing/slowestFunctionsWidget.spec.tsx

@@ -26,7 +26,7 @@ describe('SlowestFunctionsWidget', function () {
       statusCode: 400,
     });
 
-    render(<SlowestFunctionsWidget widgetHeight="100px" />);
+    render(<SlowestFunctionsWidget widgetHeight="100px" breakdownFunction="p75()" />);
 
     // starts by rendering loading
     expect(screen.getByTestId('loading-indicator')).toBeInTheDocument();
@@ -50,7 +50,7 @@ describe('SlowestFunctionsWidget', function () {
       ],
     });
 
-    render(<SlowestFunctionsWidget widgetHeight="100px" />);
+    render(<SlowestFunctionsWidget widgetHeight="100px" breakdownFunction="p75()" />);
 
     // starts by rendering loading
     expect(screen.getByTestId('loading-indicator')).toBeInTheDocument();
@@ -130,7 +130,7 @@ describe('SlowestFunctionsWidget', function () {
         MockApiClient.matchQuery({
           dataset: 'profileFunctions',
           query: 'project.id:1 fingerprint:123',
-          field: ['transaction', 'count()', 'p75()', 'sum()', 'examples()'],
+          field: ['transaction', 'count()', 'sum()', 'examples()', 'p75()'],
         }),
       ],
     });
@@ -163,12 +163,12 @@ describe('SlowestFunctionsWidget', function () {
         MockApiClient.matchQuery({
           dataset: 'profileFunctions',
           query: 'project.id:1 fingerprint:456',
-          field: ['transaction', 'count()', 'p75()', 'sum()', 'examples()'],
+          field: ['transaction', 'count()', 'sum()', 'examples()', 'p75()'],
         }),
       ],
     });
 
-    render(<SlowestFunctionsWidget widgetHeight="100px" />);
+    render(<SlowestFunctionsWidget widgetHeight="100px" breakdownFunction="p75()" />);
 
     // starts by rendering loading
     expect(screen.getByTestId('loading-indicator')).toBeInTheDocument();

+ 19 - 8
static/app/views/profiling/landing/slowestFunctionsWidget.tsx

@@ -43,7 +43,10 @@ import {
 const MAX_FUNCTIONS = 3;
 const DEFAULT_CURSOR_NAME = 'slowFnCursor';
 
+type BreakdownFunction = 'avg()' | 'p50()' | 'p75()' | 'p95()' | 'p99()';
+
 interface SlowestFunctionsWidgetProps {
+  breakdownFunction: BreakdownFunction;
   cursorName?: string;
   header?: ReactNode;
   userQuery?: string;
@@ -51,6 +54,7 @@ interface SlowestFunctionsWidgetProps {
 }
 
 export function SlowestFunctionsWidget({
+  breakdownFunction,
   cursorName = DEFAULT_CURSOR_NAME,
   header,
   userQuery,
@@ -149,6 +153,7 @@ export function SlowestFunctionsWidget({
               return (
                 <SlowestFunctionEntry
                   key={`${f['project.id']}-${f.package}-${f.function}`}
+                  breakdownFunction={breakdownFunction}
                   isExpanded={i === expandedIndex}
                   setExpanded={() => {
                     const nextIndex = expandedIndex !== i ? i : (i + 1) % l.length;
@@ -168,6 +173,7 @@ export function SlowestFunctionsWidget({
 }
 
 interface SlowestFunctionEntryProps {
+  breakdownFunction: BreakdownFunction;
   func: EventsResultsDataRow<FunctionsField>;
   isExpanded: boolean;
   query: string;
@@ -178,6 +184,7 @@ interface SlowestFunctionEntryProps {
 const BARS = 10;
 
 function SlowestFunctionEntry({
+  breakdownFunction,
   func,
   isExpanded,
   query,
@@ -219,7 +226,7 @@ function SlowestFunctionEntry({
   }, [func, query]);
 
   const functionTransactionsQuery = useProfileFunctions<FunctionTransactionField>({
-    fields: functionTransactionsFields,
+    fields: [...functionTransactionsFields, breakdownFunction],
     referrer: 'api.profiling.suspect-functions.transactions',
     sort: {
       key: 'sum()',
@@ -281,7 +288,7 @@ function SlowestFunctionEntry({
                 <TextOverflow>{t('Count')}</TextOverflow>
               </TransactionsListHeader>
               <TransactionsListHeader align="right">
-                <TextOverflow>{t('P75()')}</TextOverflow>
+                <TextOverflow>{breakdownFunction.toUpperCase()}</TextOverflow>
               </TransactionsListHeader>
               <TransactionsListHeader align="right">
                 <TextOverflow>{t('Time Spent')}</TextOverflow>
@@ -325,7 +332,7 @@ function SlowestFunctionEntry({
                     </TransactionsListCell>
                     <TransactionsListCell align="right">
                       <PerformanceDuration
-                        nanoseconds={transaction['p75()'] as number}
+                        nanoseconds={transaction[breakdownFunction] as number}
                         abbreviation
                       />
                     </TransactionsListCell>
@@ -361,15 +368,19 @@ const totalsFields = ['project.id', 'sum()'] as const;
 
 type TotalsField = (typeof totalsFields)[number];
 
-const functionTransactionsFields = [
+type FunctionTransactionField =
+  | BreakdownFunction
+  | 'transaction'
+  | 'count()'
+  | 'sum()'
+  | 'examples()';
+
+const functionTransactionsFields: FunctionTransactionField[] = [
   'transaction',
   'count()',
-  'p75()',
   'sum()',
   'examples()',
-] as const;
-
-type FunctionTransactionField = (typeof functionTransactionsFields)[number];
+];
 
 const StyledPagination = styled(Pagination)`
   margin: 0;