Browse Source

feat(starfish): db query details find profiles better (#48956)

This is still in a more hacky stage, as this PR basically introduces an
N+1 Api calls. Not ideal, but enough for POC.

Essentially we're just continually checking random events that might
have a stacktrace for the query, until we find one.

New method is as follows
1. Find an event at random for one of the transactions on the
transaction list in the db query details page.
2. Check if that event contains the query from the db query details
page.
3. a) If not we start with a new event at 1
    b) If there is continue
4. Check if there is a stacktrace for that query.
5. a) If not we start with a new event at 1
    b) If there is then we display that stacktrace

We check no more then 10 events
Dominik Buszowiecki 1 year ago
parent
commit
96cd2abde0

+ 9 - 1
static/app/components/events/interfaces/spans/spanProfileDetails.tsx

@@ -32,9 +32,14 @@ const TOP_NODE_MIN_COUNT = 3;
 interface SpanProfileDetailsProps {
   event: Readonly<EventTransaction>;
   span: Readonly<SpanType>;
+  onNoProfileFound?: () => void;
 }
 
-export function SpanProfileDetails({event, span}: SpanProfileDetailsProps) {
+export function SpanProfileDetails({
+  event,
+  span,
+  onNoProfileFound,
+}: SpanProfileDetailsProps) {
   const organization = useOrganization();
   const {projects} = useProjects();
   const project = projects.find(p => p.id === event.projectID);
@@ -141,6 +146,9 @@ export function SpanProfileDetails({event, span}: SpanProfileDetailsProps) {
   }
 
   if (!frames.length) {
+    if (onNoProfileFound) {
+      onNoProfileFound();
+    }
     return null;
   }
 

+ 17 - 6
static/app/views/starfish/modules/databaseModule/panel/profileView.tsx

@@ -1,4 +1,4 @@
-import {Fragment} from 'react';
+import {Fragment, useState} from 'react';
 
 import {SpanProfileDetails} from 'sentry/components/events/interfaces/spans/spanProfileDetails';
 import {RawSpanType} from 'sentry/components/events/interfaces/spans/types';
@@ -19,17 +19,23 @@ type Props = {
 export function ProfileView(props: Props) {
   const {spanHash, transactionNames} = props;
   const organization = useOrganization();
+  const [transactionIdx, setTransactionIdx] = useState<number>(0);
 
-  const result = useQueryGetProfileIds(transactionNames, spanHash);
-  const eventResult = useQueryGetEvent(result.data[0]?.transaction_id);
+  const result = useQueryGetProfileIds(transactionNames);
+  const transactionIds = result?.data?.data?.map(d => d.id) ?? [];
+  const eventResult = useQueryGetEvent(transactionIds[transactionIdx]);
 
-  const isLoading = result.isLoading || eventResult.isLoading;
+  const isLoading = result.isLoading || eventResult.isLoading || eventResult.isRefetching;
 
   if (isLoading) {
     return <LoadingIndicator />;
   }
 
-  if (eventResult.data.id) {
+  const handleNoProfileFound = () => {
+    setTransactionIdx(transactionIdx + 1);
+  };
+
+  if (eventResult.data.id && transactionIdx < transactionIds.length) {
     const event = eventResult.data;
     const spans = event.entries[0].data as RawSpanType[];
 
@@ -49,13 +55,18 @@ export function ProfileView(props: Props) {
                 input={profiles?.type === 'resolved' ? profiles.data : null}
                 traceID={profileId || ''}
               >
-                <SpanProfileDetails event={event} span={currentSpan} />
+                <SpanProfileDetails
+                  onNoProfileFound={handleNoProfileFound}
+                  event={event}
+                  span={currentSpan}
+                />
               </ProfileGroupProvider>
             )}
           </ProfileContext.Consumer>
         </ProfilesProvider>
       );
     }
+    handleNoProfileFound();
   }
 
   return <Fragment>'No profile found'</Fragment>;

+ 3 - 25
static/app/views/starfish/modules/databaseModule/queries.ts

@@ -522,10 +522,7 @@ export const useQueryTransactionByTPMAndP75 = (
   );
 };
 
-export const useQueryGetProfileIds = (
-  transactionNames: string[],
-  spanHash: string
-): DefinedUseQueryResult<{transaction_id: string}[]> => {
+export const useQueryGetProfileIds = (transactionNames: string[]) => {
   const location = useLocation();
   const {slug: orgSlug} = useOrganization();
   const eventView = EventView.fromNewQueryWithLocation(
@@ -535,30 +532,11 @@ export const useQueryGetProfileIds = (
       query: `transaction:[${transactionNames.join(',')}] has:profile.id`,
       projects: [1],
       version: 1,
+      orderby: 'id',
     },
     location
   );
-  const discoverResult = useDiscoverQuery({eventView, location, orgSlug});
-
-  const transactionIds = discoverResult?.data?.data?.map(d => d.id);
-
-  const query = `
-    SELECT
-      transaction_id
-    FROM
-      default.spans_experimental_starfish
-    WHERE
-      group_id = '${spanHash}' AND
-      transaction_id IN ('${transactionIds?.join(`','`)}')
-  `;
-
-  return useQuery({
-    enabled: !!transactionIds?.length,
-    queryKey: ['transactionsWithProfiles', transactionIds?.join(',')],
-    queryFn: () => fetch(`${HOST}/?query=${query}`).then(res => res.json()),
-    retry: false,
-    initialData: [],
-  });
+  return useDiscoverQuery({eventView, location, orgSlug, queryExtras: {per_page: '10'}});
 };
 
 export const useQueryGetEvent = (