Browse Source

ref(profiling): create useDifferentialFlamegraphQuery (#59943)

Create a data loading hook for differential flamegraph data loading
Jonas 1 year ago
parent
commit
75702fbc6b

+ 3 - 23
static/app/components/events/eventStatisticalDetector/eventAffectedTransactions.tsx

@@ -8,7 +8,6 @@ import {t} from 'sentry/locale';
 import {Event, Group, Project} from 'sentry/types';
 import {defined} from 'sentry/utils';
 import {trackAnalytics} from 'sentry/utils/analytics';
-import {useProfileFunctions} from 'sentry/utils/profiling/hooks/useProfileFunctions';
 import {useProfileTopEventsStats} from 'sentry/utils/profiling/hooks/useProfileTopEventsStats';
 import {useRelativeDateTime} from 'sentry/utils/profiling/hooks/useRelativeDateTime';
 import {
@@ -20,6 +19,7 @@ import useOrganization from 'sentry/utils/useOrganization';
 
 import {RELATIVE_DAYS_WINDOW} from './consts';
 import {EventRegressionTable} from './eventRegressionTable';
+import {useTransactionsDelta} from './transactionsDeltaProvider';
 
 interface EventAffectedTransactionsProps {
   event: Event;
@@ -93,7 +93,6 @@ function EventAffectedTransactionsInner({
   project,
 }: EventAffectedTransactionsInnerProps) {
   const [causeType, setCauseType] = useState<'duration' | 'throughput'>('duration');
-
   const organization = useOrganization();
 
   const datetime = useRelativeDateTime({
@@ -101,31 +100,12 @@ function EventAffectedTransactionsInner({
     relativeDays: RELATIVE_DAYS_WINDOW,
   });
 
+  const transactionsDeltaQuery = useTransactionsDelta();
+
   const percentileBefore = `percentile_before(function.duration, 0.95, ${breakpoint})`;
   const percentileAfter = `percentile_after(function.duration, 0.95, ${breakpoint})`;
   const throughputBefore = `cpm_before(${breakpoint})`;
   const throughputAfter = `cpm_after(${breakpoint})`;
-  const regressionScore = `regression_score(function.duration, 0.95, ${breakpoint})`;
-
-  const transactionsDeltaQuery = useProfileFunctions({
-    datetime,
-    fields: [
-      'transaction',
-      percentileBefore,
-      percentileAfter,
-      throughputBefore,
-      throughputAfter,
-      regressionScore,
-    ],
-    sort: {
-      key: regressionScore,
-      order: 'desc',
-    },
-    query: `fingerprint:${fingerprint} ${regressionScore}:>0`,
-    projects: [project.id],
-    limit: TRANSACTIONS_LIMIT,
-    referrer: 'api.profiling.functions.regression.transactions',
-  });
 
   const query = useMemo(() => {
     const data = transactionsDeltaQuery.data?.data ?? [];

+ 13 - 1
static/app/components/events/eventStatisticalDetector/eventDifferentialFlamegraph.tsx

@@ -2,6 +2,9 @@ import {useEffect} from 'react';
 import * as Sentry from '@sentry/react';
 
 import {Event} from 'sentry/types';
+import {useDifferentialFlamegraphQuery} from 'sentry/utils/profiling/hooks/useDifferentialFlamegraphQuery';
+
+import {useTransactionsDelta} from './transactionsDeltaProvider';
 
 interface EventDifferenialFlamegraphProps {
   event: Event;
@@ -9,7 +12,6 @@ interface EventDifferenialFlamegraphProps {
 
 export function EventDifferenialFlamegraph(props: EventDifferenialFlamegraphProps) {
   const evidenceData = props.event.occurrence?.evidenceData;
-
   const fingerprint = evidenceData?.fingerprint;
   const breakpoint = evidenceData?.breakpoint;
 
@@ -32,6 +34,16 @@ export function EventDifferenialFlamegraph(props: EventDifferenialFlamegraphProp
     });
   }, [isValid, fingerprint, breakpoint]);
 
+  const projectID = parseInt(props.event.projectID, 10);
+  const transactions = useTransactionsDelta();
+
+  useDifferentialFlamegraphQuery({
+    projectID,
+    breakpoint,
+    environments: [],
+    transaction: transactions.data?.data?.[0]?.transaction as string,
+  });
+
   if (!isValid) {
     return null;
   }

+ 78 - 0
static/app/components/events/eventStatisticalDetector/transactionsDeltaProvider.tsx

@@ -0,0 +1,78 @@
+import React, {createContext, useContext} from 'react';
+
+import {RELATIVE_DAYS_WINDOW} from 'sentry/components/events/eventStatisticalDetector/consts';
+import type {Event, Project} from 'sentry/types';
+import {EventsResults} from 'sentry/utils/profiling/hooks/types';
+import {useProfileFunctions} from 'sentry/utils/profiling/hooks/useProfileFunctions';
+import {useRelativeDateTime} from 'sentry/utils/profiling/hooks/useRelativeDateTime';
+import type {UseQueryResult} from 'sentry/utils/queryClient';
+import RequestError from 'sentry/utils/requestError/requestError';
+
+const TransactionsDeltaProviderContext = createContext<UseQueryResult<
+  EventsResults<string>,
+  RequestError
+> | null>(null);
+
+const TRANSACTIONS_LIMIT = 10;
+
+export function useTransactionsDelta(): UseQueryResult<
+  EventsResults<string>,
+  RequestError
+> {
+  const ctx = useContext(TransactionsDeltaProviderContext);
+  if (!ctx) {
+    throw new Error(
+      'useTransactionsDelta called outside of TransactionsDeltaProviderProvider'
+    );
+  }
+  return ctx;
+}
+
+interface TransactionsDeltaProviderProps {
+  children: React.ReactNode;
+  event: Event;
+  project: Project;
+}
+
+export function TransactionsDeltaProvider(props: TransactionsDeltaProviderProps) {
+  const evidenceData = props.event.occurrence?.evidenceData;
+  const fingerprint = evidenceData?.fingerprint;
+  const breakpoint = evidenceData?.breakpoint;
+
+  const datetime = useRelativeDateTime({
+    anchor: breakpoint,
+    relativeDays: RELATIVE_DAYS_WINDOW,
+  });
+
+  const regressionScore = `regression_score(function.duration, 0.95, ${breakpoint})`;
+  const percentileBefore = `percentile_before(function.duration, 0.95, ${breakpoint})`;
+  const percentileAfter = `percentile_after(function.duration, 0.95, ${breakpoint})`;
+  const throughputBefore = `cpm_before(${breakpoint})`;
+  const throughputAfter = `cpm_after(${breakpoint})`;
+
+  const transactionsDeltaQuery = useProfileFunctions({
+    datetime,
+    fields: [
+      'transaction',
+      percentileBefore,
+      percentileAfter,
+      throughputBefore,
+      throughputAfter,
+      regressionScore,
+    ],
+    sort: {
+      key: regressionScore,
+      order: 'desc',
+    },
+    query: `fingerprint:${fingerprint} ${regressionScore}:>0`,
+    projects: [props.project.id],
+    limit: TRANSACTIONS_LIMIT,
+    referrer: 'api.profiling.functions.regression.transactions',
+  });
+
+  return (
+    <TransactionsDeltaProviderContext.Provider value={transactionsDeltaQuery}>
+      {props.children}
+    </TransactionsDeltaProviderContext.Provider>
+  );
+}

+ 8 - 6
static/app/utils/profiling/hooks/useAggregateFlamegraphQuery.ts

@@ -6,17 +6,19 @@ import {useApiQuery} from 'sentry/utils/queryClient';
 import {MutableSearch} from 'sentry/utils/tokenizeSearch';
 import useOrganization from 'sentry/utils/useOrganization';
 
+export type AggregateFlamegraphQueryParameters = {
+  datetime: Partial<PageFilters['datetime']>;
+  environments: string[];
+  projects: number[];
+  transaction: string;
+};
+
 export function useAggregateFlamegraphQuery({
   projects,
   datetime,
   environments,
   transaction,
-}: {
-  datetime: Partial<PageFilters['datetime']>;
-  environments: string[];
-  projects: number[];
-  transaction: string;
-}) {
+}: AggregateFlamegraphQueryParameters) {
   const organization = useOrganization();
   const path = `/organizations/${organization.slug}/profiling/flamegraph/`;
 

+ 68 - 0
static/app/utils/profiling/hooks/useDifferentialFlamegraphQuery.ts

@@ -0,0 +1,68 @@
+import {useMemo} from 'react';
+import {UseQueryResult} from '@tanstack/react-query';
+
+import {RELATIVE_DAYS_WINDOW} from 'sentry/components/events/eventStatisticalDetector/consts';
+import {useRelativeDateTime} from 'sentry/utils/profiling/hooks/useRelativeDateTime';
+import RequestError from 'sentry/utils/requestError/requestError';
+
+import {
+  AggregateFlamegraphQueryParameters,
+  useAggregateFlamegraphQuery,
+} from './useAggregateFlamegraphQuery';
+
+interface DifferentialFlamegraphQueryParameters {
+  breakpoint: number;
+  environments: AggregateFlamegraphQueryParameters['environments'];
+  projectID: number | null;
+  transaction: string;
+}
+
+export function useDifferentialFlamegraphQuery(
+  params: DifferentialFlamegraphQueryParameters
+): {
+  after: UseQueryResult<Profiling.Schema, RequestError>;
+  before: UseQueryResult<Profiling.Schema, RequestError>;
+} {
+  const sharedAggregateQueryParams: AggregateFlamegraphQueryParameters = useMemo(() => {
+    return {
+      transaction: params.transaction,
+      environments: params.environments,
+      projects:
+        params.projectID === null || isNaN(params.projectID) ? [] : [params.projectID],
+      datetime: {},
+    };
+  }, [params.transaction, params.environments, params.projectID]);
+
+  const regressionDateRange = useRelativeDateTime({
+    anchor: params.breakpoint,
+    relativeDays: RELATIVE_DAYS_WINDOW,
+  });
+
+  const beforeAggregateQueryParams: AggregateFlamegraphQueryParameters = useMemo(() => {
+    return {
+      ...sharedAggregateQueryParams,
+      datetime: {
+        start: regressionDateRange.start,
+        end: new Date(params.breakpoint),
+      },
+    };
+  }, [sharedAggregateQueryParams, regressionDateRange.start, params.breakpoint]);
+
+  const afterAggregateQueryParams: AggregateFlamegraphQueryParameters = useMemo(() => {
+    return {
+      ...sharedAggregateQueryParams,
+      datetime: {
+        start: new Date(params.breakpoint),
+        end: regressionDateRange.end,
+      },
+    };
+  }, [sharedAggregateQueryParams, regressionDateRange.end, params.breakpoint]);
+
+  const before = useAggregateFlamegraphQuery(beforeAggregateQueryParams);
+  const after = useAggregateFlamegraphQuery(afterAggregateQueryParams);
+
+  return {
+    before,
+    after,
+  };
+}

+ 23 - 20
static/app/views/issueDetails/groupEventDetails/groupEventDetailsContent.tsx

@@ -21,6 +21,7 @@ import {EventDifferenialFlamegraph} from 'sentry/components/events/eventStatisti
 import {EventFunctionComparisonList} from 'sentry/components/events/eventStatisticalDetector/eventFunctionComparisonList';
 import {EventRegressionSummary} from 'sentry/components/events/eventStatisticalDetector/eventRegressionSummary';
 import {EventFunctionBreakpointChart} from 'sentry/components/events/eventStatisticalDetector/functionBreakpointChart';
+import {TransactionsDeltaProvider} from 'sentry/components/events/eventStatisticalDetector/transactionsDeltaProvider';
 import {EventTagsAndScreenshot} from 'sentry/components/events/eventTagsAndScreenshot';
 import {EventViewHierarchy} from 'sentry/components/events/eventViewHierarchy';
 import {EventGroupingInfo} from 'sentry/components/events/groupingInfo';
@@ -212,28 +213,30 @@ function ProfilingDurationRegressionIssueDetailsContent({
   const organization = useOrganization();
 
   return (
-    <Fragment>
-      <ErrorBoundary mini>
-        <EventRegressionSummary event={event} group={group} />
-      </ErrorBoundary>
-      <ErrorBoundary mini>
-        <EventFunctionBreakpointChart event={event} />
-      </ErrorBoundary>
-      <Feature
-        features={['profiling-differential-flamegraph']}
-        organization={organization}
-      >
+    <TransactionsDeltaProvider event={event} project={project}>
+      <Fragment>
         <ErrorBoundary mini>
-          <EventDifferenialFlamegraph event={event} />
+          <EventRegressionSummary event={event} group={group} />
         </ErrorBoundary>
-      </Feature>
-      <ErrorBoundary mini>
-        <EventAffectedTransactions event={event} group={group} project={project} />
-      </ErrorBoundary>
-      <ErrorBoundary mini>
-        <EventFunctionComparisonList event={event} group={group} project={project} />
-      </ErrorBoundary>
-    </Fragment>
+        <ErrorBoundary mini>
+          <EventFunctionBreakpointChart event={event} />
+        </ErrorBoundary>
+        <Feature
+          features={['profiling-differential-flamegraph']}
+          organization={organization}
+        >
+          <ErrorBoundary mini>
+            <EventDifferenialFlamegraph event={event} />
+          </ErrorBoundary>
+        </Feature>
+        <ErrorBoundary mini>
+          <EventAffectedTransactions event={event} group={group} project={project} />
+        </ErrorBoundary>
+        <ErrorBoundary mini>
+          <EventFunctionComparisonList event={event} group={group} project={project} />
+        </ErrorBoundary>
+      </Fragment>
+    </TransactionsDeltaProvider>
   );
 }