Browse Source

feat(cache): add transaction duration to table (#70363)

Adds the the avg transaction duration column to the cache module.

Gotta wait for #70272 to merge as its a dependant to this PR
Dominik Buszowiecki 10 months ago
parent
commit
4d2213e334

+ 51 - 6
static/app/views/performance/cache/cacheLandingPage.tsx

@@ -1,4 +1,5 @@
 import React from 'react';
+import keyBy from 'lodash/keyBy';
 
 import FeatureBadge from 'sentry/components/badge/featureBadge';
 import {Breadcrumbs} from 'sentry/components/breadcrumbs';
@@ -32,7 +33,7 @@ import {
 } from 'sentry/views/performance/cache/tables/transactionsTable';
 import * as ModuleLayout from 'sentry/views/performance/moduleLayout';
 import {ModulePageProviders} from 'sentry/views/performance/modulePageProviders';
-import {useSpanMetrics} from 'sentry/views/starfish/queries/useDiscover';
+import {useMetrics, useSpanMetrics} from 'sentry/views/starfish/queries/useDiscover';
 import {useSpanMetricsSeries} from 'sentry/views/starfish/queries/useDiscoverSeries';
 import {SpanFunction, SpanMetricsField} from 'sentry/views/starfish/types';
 import {QueryParameterNames} from 'sentry/views/starfish/views/queryParameters';
@@ -99,7 +100,32 @@ export function CacheLandingPage() {
     Referrer.LANDING_CACHE_TRANSACTION_LIST
   );
 
-  addCustomMeta(transactionsListMeta);
+  const {
+    data: transactionDurationData,
+    error: transactionDurationError,
+    meta: transactionDurationMeta,
+    isLoading: isTransactionDurationLoading,
+  } = useMetrics(
+    {
+      search: `transaction:[${transactionsList.map(({transaction}) => `"${transaction}"`).join(',')}]`,
+      fields: [`avg(transaction.duration)`, 'transaction'],
+      enabled: !isTransactionsListLoading && transactionsList.length > 0,
+    },
+    Referrer.LANDING_CACHE_TRANSACTION_DURATION
+  );
+
+  const transactionDurationsMap = keyBy(transactionDurationData, 'transaction');
+
+  const transactionsListWithDuration =
+    transactionsList?.map(transaction => ({
+      ...transaction,
+      'avg(transaction.duration)':
+        transactionDurationsMap[transaction.transaction]?.['avg(transaction.duration)'],
+    })) || [];
+
+  const meta = combineMeta(transactionsListMeta, transactionDurationMeta);
+
+  addCustomMeta(meta);
 
   return (
     <React.Fragment>
@@ -156,11 +182,11 @@ export function CacheLandingPage() {
             </ModuleLayout.Half>
             <ModuleLayout.Full>
               <TransactionsTable
-                data={transactionsList}
-                isLoading={isTransactionsListLoading}
+                data={transactionsListWithDuration}
+                isLoading={isTransactionsListLoading || isTransactionDurationLoading}
                 sort={sort}
-                error={transactionsListError}
-                meta={transactionsListMeta}
+                error={transactionsListError || transactionDurationError}
+                meta={meta}
                 pageLinks={transactionsListPageLinks}
               />
             </ModuleLayout.Full>
@@ -184,6 +210,25 @@ export function LandingPageWithProviders() {
   );
 }
 
+const combineMeta = (
+  meta1?: EventsMetaType,
+  meta2?: EventsMetaType
+): EventsMetaType | undefined => {
+  if (!meta1 && !meta2) {
+    return undefined;
+  }
+  if (!meta1) {
+    return meta2;
+  }
+  if (!meta2) {
+    return meta1;
+  }
+  return {
+    fields: {...meta1.fields, ...meta2.fields},
+    units: {...meta1.units, ...meta2.units},
+  };
+};
+
 // TODO - this should come from the backend
 const addCustomMeta = (meta?: EventsMetaType) => {
   if (meta) {

+ 1 - 0
static/app/views/performance/cache/referrers.ts

@@ -2,6 +2,7 @@ export enum Referrer {
   LANDING_CACHE_HIT_MISS_CHART = 'api.performance.cache.landing-cache-hit-miss-chart',
   LANDING_CACHE_THROUGHPUT_CHART = 'api.performance.cache.landing-cache-throughput-chart',
   LANDING_CACHE_TRANSACTION_LIST = 'api.performance.cache.landing-cache-transaction-list',
+  LANDING_CACHE_TRANSACTION_DURATION = 'api.performance.cache.landing-cache-transaction-duration',
 
   SAMPLES_CACHE_METRICS_RIBBON = 'api.performance.cache.samples-cache-metrics-ribbon',
   SAMPLES_CACHE_TRANSACTION_DURATION_CHART = 'api.performance.cache.samples-cache-transaction-duration-chart',

+ 11 - 1
static/app/views/performance/cache/tables/transactionsTable.tsx

@@ -18,6 +18,8 @@ import useOrganization from 'sentry/utils/useOrganization';
 import {TransactionCell} from 'sentry/views/performance/cache/tables/transactionCell';
 import {renderHeadCell} from 'sentry/views/starfish/components/tableCells/renderHeadCell';
 import {
+  MetricsFields,
+  type MetricsResponse,
   SpanFunction,
   SpanMetricsField,
   type SpanMetricsResponse,
@@ -26,6 +28,7 @@ import {QueryParameterNames} from 'sentry/views/starfish/views/queryParameters';
 import {DataTitles} from 'sentry/views/starfish/views/spans/types';
 
 const {CACHE_MISS_RATE, SPM, TIME_SPENT_PERCENTAGE} = SpanFunction;
+const {TRANSACTION_DURATION} = MetricsFields;
 const {CACHE_ITEM_SIZE} = SpanMetricsField;
 
 type Row = Pick<
@@ -38,7 +41,8 @@ type Row = Pick<
   | 'sum(span.self_time)'
   | 'time_spent_percentage()'
   | 'avg(cache.item_size)'
->;
+> &
+  Pick<MetricsResponse, 'avg(transaction.duration)'>;
 
 type Column = GridColumnHeader<
   | 'transaction'
@@ -46,6 +50,7 @@ type Column = GridColumnHeader<
   | 'cache_miss_rate()'
   | 'time_spent_percentage()'
   | 'project'
+  | 'avg(transaction.duration)'
   | 'avg(cache.item_size)'
 >;
 
@@ -70,6 +75,11 @@ const COLUMN_ORDER: Column[] = [
     name: `${t('Requests')} ${RATE_UNIT_TITLE[RateUnit.PER_MINUTE]}`,
     width: COL_WIDTH_UNDEFINED,
   },
+  {
+    key: `avg(${TRANSACTION_DURATION})`,
+    name: DataTitles[`avg(${TRANSACTION_DURATION})`],
+    width: COL_WIDTH_UNDEFINED,
+  },
   {
     key: `${CACHE_MISS_RATE}()`,
     name: DataTitles.cacheMissRate,

+ 6 - 3
static/app/views/starfish/queries/useDiscover.ts

@@ -17,7 +17,8 @@ interface UseMetricsOptions<Fields> {
   enabled?: boolean;
   fields?: Fields;
   limit?: number;
-  search?: MutableSearch;
+  referrer?: string;
+  search?: MutableSearch | string; // TODO - ideally this probably would be only `Mutable Search`, but it doesn't handle some situations well
   sorts?: Sort[];
 }
 
@@ -75,16 +76,18 @@ const useDiscover = <T extends Extract<keyof ResponseType, string>[], ResponseTy
 };
 
 function getEventView(
-  search: MutableSearch | undefined,
+  search: MutableSearch | string | undefined,
   fields: string[] = [],
   sorts: Sort[] = [],
   pageFilters: PageFilters,
   dataset: DiscoverDatasets
 ) {
+  const query = typeof search === 'string' ? search : search?.formatString() ?? '';
+
   const eventView = EventView.fromNewQueryWithPageFilters(
     {
       name: '',
-      query: search?.formatString() ?? '',
+      query,
       fields,
       dataset,
       version: 2,

+ 2 - 0
static/app/views/starfish/types.tsx

@@ -327,6 +327,8 @@ export type MetricsFunctions = (typeof METRICS_FUNCTIONS)[number];
 
 export type MetricsResponse = {
   [Property in MetricsNumberFields as `${Aggregate}(${Property})`]: number;
+} & {
+  [Property in MetricsStringFields as `${Property}`]: string;
 };
 
 export type MetricsProperty = keyof MetricsResponse;