Browse Source

chore(span-view): Remove redundant discover queries (#26012)

With the quick trace results, the discover query for child transactions and
associated errors is no longer necessary. This change removes the redundant
discover query an relies on results from quick trace to find child transactions
and associated errors.
Tony Xiao 3 years ago
parent
commit
4da014f3f4

+ 31 - 132
static/app/components/events/interfaces/spans/index.tsx

@@ -5,17 +5,13 @@ import styled from '@emotion/styled';
 import Alert from 'app/components/alert';
 import {Panel} from 'app/components/panels';
 import SearchBar from 'app/components/searchBar';
-import {ALL_ACCESS_PROJECTS} from 'app/constants/globalSelectionHeader';
 import {IconWarning} from 'app/icons';
 import {t, tn} from 'app/locale';
 import space from 'app/styles/space';
 import {Organization} from 'app/types';
 import {EventTransaction} from 'app/types/event';
 import {objectIsEmpty} from 'app/utils';
-import DiscoverQuery, {TableData} from 'app/utils/discover/discoverQuery';
-import EventView from 'app/utils/discover/eventView';
 import * as QuickTraceContext from 'app/utils/performance/quickTrace/quickTraceContext';
-import {QueryResults, stringifyQueryObject} from 'app/utils/tokenizeSearch';
 import withOrganization from 'app/utils/withOrganization';
 
 import * as AnchorLinkManager from './anchorLinkManager';
@@ -27,7 +23,7 @@ import Filter, {
 } from './filter';
 import TraceView from './traceView';
 import {ParsedTraceType} from './types';
-import {getTraceDateTimeRange, parseTrace} from './utils';
+import {parseTrace} from './utils';
 
 type Props = {
   event: EventTransaction;
@@ -108,104 +104,42 @@ class SpansInterface extends Component<Props, State> {
   };
 
   render() {
-    const {event, location, organization} = this.props;
+    const {event, organization} = this.props;
     const {parsedTrace} = this.state;
 
-    const orgSlug = organization.slug;
-
-    // construct discover query to fetch error events associated with this transaction
-
-    const {start, end} = getTraceDateTimeRange({
-      start: parsedTrace.traceStartTimestamp,
-      end: parsedTrace.traceEndTimestamp,
-    });
-
-    const conditions = new QueryResults([
-      '!event.type:transaction',
-      `trace:${parsedTrace.traceID}`,
-    ]);
-
-    if (typeof event.title === 'string') {
-      conditions.setTagValues('transaction', [event.title]);
-    }
-
-    const orgFeatures = new Set(organization.features);
-
-    const traceErrorsEventView = EventView.fromSavedQuery({
-      id: undefined,
-      name: `Errors related to transaction ${parsedTrace.rootSpanID}`,
-      fields: [
-        'title',
-        'project',
-        'timestamp',
-        'trace',
-        'trace.span',
-        'trace.parent_span',
-      ],
-      orderby: '-timestamp',
-      query: stringifyQueryObject(conditions),
-      // if an org has no global-views, we make an assumption that errors are collected in the same
-      // project as the current transaction event where spans are collected into
-      projects: orgFeatures.has('global-views')
-        ? [ALL_ACCESS_PROJECTS]
-        : [Number(event.projectID)],
-      version: 2,
-      start,
-      end,
-    });
-
     return (
       <Container hasErrors={!objectIsEmpty(event.errors)}>
         <QuickTraceContext.Consumer>
           {quickTrace => (
-            // TODO: remove this extra discover query once quick trace is the default
-            <DiscoverQuery
-              location={location}
-              eventView={traceErrorsEventView}
-              orgSlug={orgSlug}
-              referrer="api.trace-view.errors-view"
-            >
-              {({isLoading, tableData}) => {
-                const spansWithErrors = filterSpansWithErrors(parsedTrace, tableData);
-                const numOfErrors = spansWithErrors?.data.length || 0;
-
-                return (
-                  <AnchorLinkManager.Provider>
-                    {this.renderTraceErrorsAlert({
-                      isLoading: quickTrace ? quickTrace.isLoading : isLoading,
-                      numOfErrors: quickTrace
-                        ? quickTrace?.currentEvent?.errors?.length ?? 0
-                        : numOfErrors,
-                    })}
-                    <Search>
-                      <Filter
-                        parsedTrace={parsedTrace}
-                        operationNameFilter={this.state.operationNameFilters}
-                        toggleOperationNameFilter={this.toggleOperationNameFilter}
-                        toggleAllOperationNameFilters={this.toggleAllOperationNameFilters}
-                      />
-                      <StyledSearchBar
-                        defaultQuery=""
-                        query={this.state.searchQuery || ''}
-                        placeholder={t('Search for spans')}
-                        onSearch={this.handleSpanFilter}
-                      />
-                    </Search>
-                    <Panel>
-                      <TraceView
-                        event={event}
-                        searchQuery={this.state.searchQuery}
-                        orgId={orgSlug}
-                        organization={organization}
-                        parsedTrace={parsedTrace}
-                        spansWithErrors={spansWithErrors}
-                        operationNameFilters={this.state.operationNameFilters}
-                      />
-                    </Panel>
-                  </AnchorLinkManager.Provider>
-                );
-              }}
-            </DiscoverQuery>
+            <AnchorLinkManager.Provider>
+              {this.renderTraceErrorsAlert({
+                isLoading: quickTrace?.isLoading || false,
+                numOfErrors: quickTrace?.currentEvent?.errors?.length ?? 0,
+              })}
+              <Search>
+                <Filter
+                  parsedTrace={parsedTrace}
+                  operationNameFilter={this.state.operationNameFilters}
+                  toggleOperationNameFilter={this.toggleOperationNameFilter}
+                  toggleAllOperationNameFilters={this.toggleAllOperationNameFilters}
+                />
+                <StyledSearchBar
+                  defaultQuery=""
+                  query={this.state.searchQuery || ''}
+                  placeholder={t('Search for spans')}
+                  onSearch={this.handleSpanFilter}
+                />
+              </Search>
+              <Panel>
+                <TraceView
+                  event={event}
+                  searchQuery={this.state.searchQuery}
+                  organization={organization}
+                  parsedTrace={parsedTrace}
+                  operationNameFilters={this.state.operationNameFilters}
+                />
+              </Panel>
+            </AnchorLinkManager.Provider>
           )}
         </QuickTraceContext.Consumer>
       </Container>
@@ -239,39 +173,4 @@ const AlertContainer = styled('div')`
   margin-bottom: ${space(1)};
 `;
 
-function filterSpansWithErrors(
-  parsedTrace: ParsedTraceType,
-  tableData: TableData | null | undefined
-): TableData | null | undefined {
-  if (!tableData) {
-    return undefined;
-  }
-
-  const data = tableData?.data ?? [];
-
-  const filtered = data.filter(row => {
-    const spanId = row['trace.span'] || '';
-
-    if (!spanId) {
-      return false;
-    }
-
-    if (spanId === parsedTrace.rootSpanID) {
-      return true;
-    }
-
-    const hasSpan =
-      parsedTrace.spans.findIndex(span => {
-        return spanId === span.span_id;
-      }) >= 0;
-
-    return hasSpan;
-  });
-
-  return {
-    ...tableData,
-    data: filtered,
-  };
-}
-
 export default ReactRouter.withRouter(withOrganization(SpansInterface));

+ 17 - 31
static/app/components/events/interfaces/spans/spanBar.tsx

@@ -38,7 +38,6 @@ import space from 'app/styles/space';
 import {Organization} from 'app/types';
 import {EventTransaction} from 'app/types/event';
 import {defined} from 'app/utils';
-import {TableDataRow} from 'app/utils/discover/discoverQuery';
 import * as QuickTraceContext from 'app/utils/performance/quickTrace/quickTraceContext';
 import {QuickTraceContextChildrenProps} from 'app/utils/performance/quickTrace/quickTraceContext';
 import {QuickTraceEvent, TraceError} from 'app/utils/performance/quickTrace/types';
@@ -183,7 +182,6 @@ const MARGIN_LEFT = 0;
 
 type SpanBarProps = {
   event: Readonly<EventTransaction>;
-  orgId: string;
   organization: Organization;
   trace: Readonly<ParsedTraceType>;
   span: Readonly<ProcessedSpanType>;
@@ -199,8 +197,6 @@ type SpanBarProps = {
   isRoot?: boolean;
   toggleSpanTree: () => void;
   isCurrentSpanFilteredOut: boolean;
-  totalNumberOfErrors: number;
-  spanErrors: TableDataRow[];
 };
 
 type SpanBarState = {
@@ -251,19 +247,10 @@ class SpanBar extends React.Component<SpanBarProps, SpanBarState> {
     errors,
   }: {
     isVisible: boolean;
-    transactions: QuickTraceEvent[];
-    errors: TraceError[];
+    transactions: QuickTraceEvent[] | null;
+    errors: TraceError[] | null;
   }) {
-    const {
-      span,
-      orgId,
-      organization,
-      isRoot,
-      trace,
-      totalNumberOfErrors,
-      spanErrors,
-      event,
-    } = this.props;
+    const {span, organization, isRoot, trace, event} = this.props;
 
     return (
       <AnchorLinkManager.Consumer>
@@ -279,13 +266,10 @@ class SpanBar extends React.Component<SpanBarProps, SpanBarState> {
           return (
             <SpanDetail
               span={span}
-              orgId={orgId}
               organization={organization}
               event={event}
               isRoot={!!isRoot}
               trace={trace}
-              totalNumberOfErrors={totalNumberOfErrors}
-              spanErrors={spanErrors}
               childTransactions={transactions}
               relatedErrors={errors}
               scrollToHash={scrollToHash}
@@ -510,10 +494,10 @@ class SpanBar extends React.Component<SpanBarProps, SpanBarState> {
 
   renderTitle(
     scrollbarManagerChildrenProps: ScrollbarManager.ScrollbarManagerChildrenProps,
-    errors: TraceError[]
+    errors: TraceError[] | null
   ) {
     const {generateContentSpanBarRef} = scrollbarManagerChildrenProps;
-    const {span, treeDepth, spanErrors} = this.props;
+    const {span, treeDepth} = this.props;
 
     const operationName = getSpanOperation(span) ? (
       <strong>
@@ -526,7 +510,7 @@ class SpanBar extends React.Component<SpanBarProps, SpanBarState> {
     const description = span?.description ?? getSpanID(span);
 
     const left = treeDepth * (TOGGLE_BORDER_BOX / 2) + MARGIN_LEFT;
-    const errored = errors.length + spanErrors.length > 0;
+    const errored = Boolean(errors && errors.length > 0);
 
     return (
       <RowTitleContainer
@@ -788,38 +772,40 @@ class SpanBar extends React.Component<SpanBarProps, SpanBarState> {
     );
   }
 
-  getRelatedErrors(quickTrace: QuickTraceContextChildrenProps): TraceError[] {
+  getRelatedErrors(quickTrace: QuickTraceContextChildrenProps): TraceError[] | null {
     if (!quickTrace) {
-      return [];
+      return null;
     }
 
     const {span} = this.props;
     const {currentEvent} = quickTrace;
 
     if (isGapSpan(span) || !currentEvent || !isTraceFull(currentEvent)) {
-      return [];
+      return null;
     }
 
     return currentEvent.errors.filter(error => error.span === span.span_id);
   }
 
-  getChildTransactions(quickTrace: QuickTraceContextChildrenProps): QuickTraceEvent[] {
+  getChildTransactions(
+    quickTrace: QuickTraceContextChildrenProps
+  ): QuickTraceEvent[] | null {
     if (!quickTrace) {
-      return [];
+      return null;
     }
 
     const {span} = this.props;
     const {trace} = quickTrace;
 
     if (isGapSpan(span) || !trace) {
-      return [];
+      return null;
     }
 
     return trace.filter(({parent_span_id}) => parent_span_id === span.span_id);
   }
 
-  renderErrorBadge(errors: TraceError[]): React.ReactNode {
-    return errors.length ? <ErrorBadge /> : null;
+  renderErrorBadge(errors: TraceError[] | null): React.ReactNode {
+    return errors?.length ? <ErrorBadge /> : null;
   }
 
   renderWarningText({warningText}: {warningText?: string} = {}) {
@@ -841,7 +827,7 @@ class SpanBar extends React.Component<SpanBarProps, SpanBarState> {
   }: {
     dividerHandlerChildrenProps: DividerHandlerManager.DividerHandlerManagerChildrenProps;
     scrollbarManagerChildrenProps: ScrollbarManager.ScrollbarManagerChildrenProps;
-    errors: TraceError[];
+    errors: TraceError[] | null;
   }) {
     const {span, spanBarColour, spanBarHatch, spanNumber} = this.props;
     const startTimestamp: number = span.start_timestamp;

+ 53 - 214
static/app/components/events/interfaces/spans/spanDetail.tsx

@@ -1,7 +1,6 @@
 import * as React from 'react';
 import {browserHistory, withRouter, WithRouterProps} from 'react-router';
 import styled from '@emotion/styled';
-import * as Sentry from '@sentry/react';
 import map from 'lodash/map';
 
 import {Client} from 'app/api';
@@ -13,7 +12,6 @@ import FileSize from 'app/components/fileSize';
 import ExternalLink from 'app/components/links/externalLink';
 import Link from 'app/components/links/link';
 import LoadingIndicator from 'app/components/loadingIndicator';
-import {getParams} from 'app/components/organizations/globalSelectionHeader/getParams';
 import {
   ErrorDot,
   ErrorLevel,
@@ -29,14 +27,13 @@ import {
 } from 'app/components/quickTrace/utils';
 import {ALL_ACCESS_PROJECTS} from 'app/constants/globalSelectionHeader';
 import {IconAnchor, IconChevron, IconWarning} from 'app/icons';
-import {t, tct, tn} from 'app/locale';
+import {t, tn} from 'app/locale';
 import space from 'app/styles/space';
 import {Organization} from 'app/types';
 import {EventTransaction} from 'app/types/event';
 import {assert} from 'app/types/utils';
-import {TableDataRow} from 'app/utils/discover/discoverQuery';
 import EventView from 'app/utils/discover/eventView';
-import {eventDetailsRoute, generateEventSlug} from 'app/utils/discover/urls';
+import {generateEventSlug} from 'app/utils/discover/urls';
 import getDynamicText from 'app/utils/getDynamicText';
 import {QuickTraceEvent, TraceError} from 'app/utils/performance/quickTrace/types';
 import withApi from 'app/utils/withApi';
@@ -57,105 +54,27 @@ type TransactionResult = {
 
 type Props = WithRouterProps & {
   api: Client;
-  orgId: string;
   organization: Organization;
   event: Readonly<EventTransaction>;
   span: Readonly<ProcessedSpanType>;
   isRoot: boolean;
   trace: Readonly<ParsedTraceType>;
-  totalNumberOfErrors: number;
-  spanErrors: TableDataRow[];
-  childTransactions: QuickTraceEvent[];
-  relatedErrors: TraceError[];
+  childTransactions: QuickTraceEvent[] | null;
+  relatedErrors: TraceError[] | null;
   scrollToHash: (hash: string) => void;
 };
 
 type State = {
-  transactionResults?: TransactionResult[];
   errorsOpened: boolean;
 };
 
 class SpanDetail extends React.Component<Props, State> {
   state: State = {
-    transactionResults: undefined,
     errorsOpened: false,
   };
 
-  componentDidMount() {
-    const {span} = this.props;
-
-    if (isGapSpan(span)) {
-      return;
-    }
-
-    this.fetchSpanDescendents(span.span_id, span.trace_id)
-      .then(response => {
-        if (!response.data || !Array.isArray(response.data)) {
-          return;
-        }
-
-        this.setState({
-          transactionResults: response.data,
-        });
-      })
-      .catch(error => {
-        Sentry.captureException(error);
-      });
-  }
-
-  fetchSpanDescendents(
-    spanID: string,
-    traceID: string
-  ): Promise<{data: TransactionResult[]}> {
-    const {api, organization, childTransactions, trace, event} = this.props;
-
-    // Skip doing a request if the results will be behind a disabled button.
-    if (!organization.features.includes('discover-basic')) {
-      return Promise.resolve({data: []});
-    }
-
-    // Quick trace found some results that we can use to link to child
-    // spans without making additional queries.
-    if (childTransactions.length) {
-      return Promise.resolve({
-        data: childTransactions.map(child => ({
-          'project.name': child.project_slug,
-          transaction: child.transaction,
-          'trace.span': child.span_id,
-          id: child.event_id,
-        })),
-      });
-    }
-
-    const url = `/organizations/${organization.slug}/eventsv2/`;
-
-    const {start, end} = getParams(
-      getTraceDateTimeRange({
-        start: trace.traceStartTimestamp,
-        end: trace.traceEndTimestamp,
-      })
-    );
-
-    const query = {
-      field: ['transaction', 'id', 'trace.span'],
-      sort: ['-id'],
-      query: `event.type:transaction trace:${traceID} trace.parent_span:${spanID}`,
-      project: organization.features.includes('global-views')
-        ? [ALL_ACCESS_PROJECTS]
-        : [Number(event.projectID)],
-      referrer: 'api.trace-view.span-detail',
-      start,
-      end,
-    };
-
-    return api.requestPromise(url, {
-      method: 'GET',
-      query,
-    });
-  }
-
   renderTraversalButton(): React.ReactNode {
-    if (!this.state.transactionResults) {
+    if (!this.props.childTransactions) {
       // TODO: Amend size to use theme when we evetually refactor LoadingIndicator
       // 12px is consistent with theme.iconSizes['xs'] but theme returns a string.
       return (
@@ -165,7 +84,7 @@ class SpanDetail extends React.Component<Props, State> {
       );
     }
 
-    if (this.state.transactionResults.length <= 0) {
+    if (this.props.childTransactions.length <= 0) {
       return (
         <StyledDiscoverButton size="xsmall" disabled>
           {t('No Children')}
@@ -173,11 +92,11 @@ class SpanDetail extends React.Component<Props, State> {
       );
     }
 
-    const {span, orgId, trace, event, organization} = this.props;
+    const {span, trace, event, organization} = this.props;
 
     assert(!isGapSpan(span));
 
-    if (this.state.transactionResults.length === 1) {
+    if (this.props.childTransactions.length === 1) {
       // Note: This is rendered by this.renderSpanChild() as a dedicated row
       return null;
     }
@@ -213,7 +132,7 @@ class SpanDetail extends React.Component<Props, State> {
       <StyledDiscoverButton
         data-test-id="view-child-transactions"
         size="xsmall"
-        to={childrenEventView.getResultsViewUrlTarget(orgId)}
+        to={childrenEventView.getResultsViewUrlTarget(organization.slug)}
       >
         {t('View Children')}
       </StyledDiscoverButton>
@@ -221,17 +140,28 @@ class SpanDetail extends React.Component<Props, State> {
   }
 
   renderSpanChild(): React.ReactNode {
-    if (!this.state.transactionResults || this.state.transactionResults.length !== 1) {
+    const {childTransactions} = this.props;
+
+    if (!childTransactions || childTransactions.length !== 1) {
       return null;
     }
 
-    const eventSlug = generateSlug(this.state.transactionResults[0]);
+    const childTransaction = childTransactions[0];
+
+    const transactionResult: TransactionResult = {
+      'project.name': childTransaction.project_slug,
+      transaction: childTransaction.transaction,
+      'trace.span': childTransaction.span_id,
+      id: childTransaction.event_id,
+    };
+
+    const eventSlug = generateSlug(transactionResult);
 
     const viewChildButton = (
       <SpanEntryContext.Consumer>
         {({getViewChildTransactionTarget}) => {
           const to = getViewChildTransactionTarget({
-            ...this.state.transactionResults![0],
+            ...transactionResult,
             eventSlug,
           });
 
@@ -240,23 +170,17 @@ class SpanDetail extends React.Component<Props, State> {
           }
 
           return (
-            <StyledDiscoverButton
-              data-test-id="view-child-transaction"
-              size="xsmall"
-              to={to}
-            >
+            <StyledButton data-test-id="view-child-transaction" size="xsmall" to={to}>
               {t('View Transaction')}
-            </StyledDiscoverButton>
+            </StyledButton>
           );
         }}
       </SpanEntryContext.Consumer>
     );
 
-    const results = this.state.transactionResults[0];
-
     return (
       <Row title="Child Transaction" extra={viewChildButton}>
-        {`${results.transaction} (${results['project.name']})`}
+        {`${transactionResult.transaction} (${transactionResult['project.name']})`}
       </Row>
     );
   }
@@ -296,125 +220,40 @@ class SpanDetail extends React.Component<Props, State> {
   };
 
   renderSpanErrorMessage() {
-    const {
-      orgId,
-      spanErrors,
-      totalNumberOfErrors,
-      span,
-      trace,
-      organization,
-      event,
-      relatedErrors,
-    } = this.props;
+    const {span, organization, relatedErrors} = this.props;
     const {errorsOpened} = this.state;
 
-    /**
-     * Use the related errors as the default and fall back to span errors if this is
-     * empty. This fall back can be removed once trace navigation rollout is complete.
-     */
-    if (relatedErrors.length) {
-      return (
-        <Alert system type="error" icon={<IconWarning size="md" />}>
-          <ErrorMessageTitle>
-            {tn(
-              'An error event occurred in this transaction.',
-              '%s error events occurred in this transaction.',
-              relatedErrors.length
-            )}
-            <Toggle priority="link" onClick={this.toggleErrors}>
-              <IconChevron direction={errorsOpened ? 'up' : 'down'} />
-            </Toggle>
-          </ErrorMessageTitle>
-          {errorsOpened && (
-            <ErrorMessageContent>
-              {relatedErrors.map(error => (
-                <React.Fragment key={error.event_id}>
-                  <ErrorDot level={error.level} />
-                  <ErrorLevel>{error.level}</ErrorLevel>
-                  <ErrorTitle>
-                    <Link to={generateIssueEventTarget(error, organization)}>
-                      {error.title}
-                    </Link>
-                  </ErrorTitle>
-                </React.Fragment>
-              ))}
-            </ErrorMessageContent>
-          )}
-        </Alert>
-      );
-    }
-
-    if (spanErrors.length === 0 || totalNumberOfErrors === 0 || isGapSpan(span)) {
+    if (!relatedErrors || relatedErrors.length <= 0 || isGapSpan(span)) {
       return null;
     }
 
-    // invariant: spanErrors.length <= totalNumberOfErrors
-
-    const eventSlug = generateEventSlug(spanErrors[0]);
-
-    const {start, end} = getTraceDateTimeRange({
-      start: trace.traceStartTimestamp,
-      end: trace.traceEndTimestamp,
-    });
-
-    const orgFeatures = new Set(organization.features);
-
-    const errorsEventView = EventView.fromSavedQuery({
-      id: undefined,
-      name: `Error events associated with span ${span.span_id}`,
-      fields: ['title', 'project', 'issue', 'timestamp'],
-      orderby: '-timestamp',
-      query: `event.type:error trace:${span.trace_id} trace.span:${span.span_id}`,
-      projects: orgFeatures.has('global-views')
-        ? [ALL_ACCESS_PROJECTS]
-        : [Number(event.projectID)],
-      version: 2,
-      start,
-      end,
-    });
-
-    const target =
-      spanErrors.length === 1
-        ? {
-            pathname: eventDetailsRoute({
-              orgSlug: orgId,
-              eventSlug,
-            }),
-          }
-        : errorsEventView.getResultsViewUrlTarget(orgId);
-
-    const message =
-      totalNumberOfErrors === 1 ? (
-        <Link to={target}>
-          <span>{t('An error event occurred in this span.')}</span>
-        </Link>
-      ) : spanErrors.length === totalNumberOfErrors ? (
-        <div>
-          {tct('[link] occurred in this span.', {
-            link: (
-              <Link to={target}>
-                <span>{t('%d error events', totalNumberOfErrors)}</span>
-              </Link>
-            ),
-          })}
-        </div>
-      ) : (
-        <div>
-          {tct('[link] occurred in this span.', {
-            link: (
-              <Link to={target}>
-                <span>
-                  {t('%d out of %d error events', spanErrors.length, totalNumberOfErrors)}
-                </span>
-              </Link>
-            ),
-          })}
-        </div>
-      );
-
     return (
       <Alert system type="error" icon={<IconWarning size="md" />}>
-        {message}
+        <ErrorMessageTitle>
+          {tn(
+            'An error event occurred in this transaction.',
+            '%s error events occurred in this transaction.',
+            relatedErrors.length
+          )}
+          <Toggle priority="link" onClick={this.toggleErrors}>
+            <IconChevron direction={errorsOpened ? 'up' : 'down'} />
+          </Toggle>
+        </ErrorMessageTitle>
+        {errorsOpened && (
+          <ErrorMessageContent>
+            {relatedErrors.map(error => (
+              <React.Fragment key={error.event_id}>
+                <ErrorDot level={error.level} />
+                <ErrorLevel>{error.level}</ErrorLevel>
+                <ErrorTitle>
+                  <Link to={generateIssueEventTarget(error, organization)}>
+                    {error.title}
+                  </Link>
+                </ErrorTitle>
+              </React.Fragment>
+            ))}
+          </ErrorMessageContent>
+        )}
       </Alert>
     );
   }

+ 1 - 34
static/app/components/events/interfaces/spans/spanGroup.tsx

@@ -2,15 +2,13 @@ import {Component, Fragment} from 'react';
 
 import {Organization} from 'app/types';
 import {EventTransaction} from 'app/types/event';
-import {TableData, TableDataRow} from 'app/utils/discover/discoverQuery';
 
 import {ScrollbarManagerChildrenProps, withScrollbarManager} from './scrollbarManager';
 import SpanBar from './spanBar';
 import {ParsedTraceType, ProcessedSpanType, TreeDepthType} from './types';
-import {getSpanID, isGapSpan, SpanBoundsType, SpanGeneratedBoundsType} from './utils';
+import {SpanBoundsType, SpanGeneratedBoundsType} from './utils';
 
 type PropType = ScrollbarManagerChildrenProps & {
-  orgId: string;
   organization: Organization;
   event: Readonly<EventTransaction>;
   span: Readonly<ProcessedSpanType>;
@@ -26,7 +24,6 @@ type PropType = ScrollbarManagerChildrenProps & {
   isLast: boolean;
   isRoot?: boolean;
   isCurrentSpanFilteredOut: boolean;
-  spansWithErrors: TableData | null | undefined;
 };
 
 type State = {
@@ -60,32 +57,6 @@ class SpanGroup extends Component<PropType, State> {
     return this.props.renderedSpanChildren;
   };
 
-  getSpanErrors(): TableDataRow[] {
-    const {span, spansWithErrors} = this.props;
-
-    const spanID = getSpanID(span);
-
-    if (isGapSpan(span) || !spansWithErrors?.data || !spanID) {
-      return [];
-    }
-
-    return spansWithErrors.data.filter(row => {
-      return row['trace.span'] === spanID;
-    });
-  }
-
-  getTotalNumberOfErrors(): number {
-    const {spansWithErrors} = this.props;
-
-    const data = spansWithErrors?.data;
-
-    if (Array.isArray(data)) {
-      return data.length;
-    }
-
-    return 0;
-  }
-
   render() {
     const {
       spanBarColour,
@@ -100,7 +71,6 @@ class SpanGroup extends Component<PropType, State> {
       treeDepth,
       spanNumber,
       isCurrentSpanFilteredOut,
-      orgId,
       organization,
       event,
     } = this.props;
@@ -110,7 +80,6 @@ class SpanGroup extends Component<PropType, State> {
         <SpanBar
           organization={organization}
           event={event}
-          orgId={orgId}
           spanBarColour={spanBarColour}
           spanBarHatch={spanBarHatch}
           span={span}
@@ -125,8 +94,6 @@ class SpanGroup extends Component<PropType, State> {
           isLast={isLast}
           isRoot={isRoot}
           isCurrentSpanFilteredOut={isCurrentSpanFilteredOut}
-          totalNumberOfErrors={this.getTotalNumberOfErrors()}
-          spanErrors={this.getSpanErrors()}
         />
         {this.renderSpanChildren()}
       </Fragment>

+ 1 - 8
static/app/components/events/interfaces/spans/spanTree.tsx

@@ -7,7 +7,6 @@ import {pickBarColour} from 'app/components/performance/waterfall/utils';
 import {t, tct} from 'app/locale';
 import {Organization} from 'app/types';
 import {EventTransaction} from 'app/types/event';
-import {TableData} from 'app/utils/discover/discoverQuery';
 
 import {DragManagerChildrenProps} from './dragManager';
 import {ActiveOperationFilter} from './filter';
@@ -43,13 +42,11 @@ type RenderedSpanTree = {
 };
 
 type PropType = {
-  orgId: string;
   organization: Organization;
   trace: ParsedTraceType;
   dragProps: DragManagerChildrenProps;
   filterSpans: FilterSpans | undefined;
   event: EventTransaction;
-  spansWithErrors: TableData | null | undefined;
   operationNameFilters: ActiveOperationFilter;
   traceViewRef: React.RefObject<HTMLDivElement>;
 };
@@ -182,7 +179,7 @@ class SpanTree extends React.Component<PropType> {
     generateBounds: (bounds: SpanBoundsType) => SpanGeneratedBoundsType;
     previousSiblingEndTimestamp: undefined | number;
   }): RenderedSpanTree => {
-    const {orgId, event, spansWithErrors, organization} = this.props;
+    const {event, organization} = this.props;
 
     const spanBarColour: string = pickBarColour(getSpanOperation(span));
     const spanChildren: Array<RawSpanType> = childSpans?.[getSpanID(span)] ?? [];
@@ -296,7 +293,6 @@ class SpanTree extends React.Component<PropType> {
     const spanGapComponent =
       isValidGap && isSpanDisplayed ? (
         <SpanGroup
-          orgId={orgId}
           organization={organization}
           event={event}
           spanNumber={spanNumber}
@@ -310,7 +306,6 @@ class SpanTree extends React.Component<PropType> {
           numOfSpanChildren={0}
           renderedSpanChildren={[]}
           isCurrentSpanFilteredOut={isCurrentSpanFilteredOut}
-          spansWithErrors={spansWithErrors}
           spanBarHatch
         />
       ) : null;
@@ -324,7 +319,6 @@ class SpanTree extends React.Component<PropType> {
           {infoMessage}
           {spanGapComponent}
           <SpanGroup
-            orgId={orgId}
             organization={organization}
             event={event}
             spanNumber={spanGroupNumber}
@@ -340,7 +334,6 @@ class SpanTree extends React.Component<PropType> {
             spanBarColour={spanBarColour}
             isCurrentSpanFilteredOut={isCurrentSpanFilteredOut}
             spanBarHatch={false}
-            spansWithErrors={spansWithErrors}
           />
         </React.Fragment>
       ),

+ 1 - 6
static/app/components/events/interfaces/spans/traceView.tsx

@@ -6,7 +6,6 @@ import {t} from 'app/locale';
 import {Organization} from 'app/types';
 import {EventTransaction} from 'app/types/event';
 import {createFuzzySearch} from 'app/utils/createFuzzySearch';
-import {TableData} from 'app/utils/discover/discoverQuery';
 
 import * as CursorGuideHandler from './cursorGuideHandler';
 import * as DividerHandlerManager from './dividerHandlerManager';
@@ -38,12 +37,10 @@ export type FilterSpans = {
 };
 
 type Props = {
-  orgId: string;
   organization: Organization;
   event: Readonly<EventTransaction>;
   parsedTrace: ParsedTraceType;
   searchQuery: string | undefined;
-  spansWithErrors: TableData | null | undefined;
   operationNameFilters: ActiveOperationFilter;
 };
 
@@ -193,7 +190,7 @@ class TraceView extends PureComponent<Props, State> {
       );
     }
 
-    const {orgId, organization, spansWithErrors, operationNameFilters} = this.props;
+    const {organization, operationNameFilters} = this.props;
 
     return (
       <DragManager interactiveLayerRef={this.minimapInteractiveRef}>
@@ -219,9 +216,7 @@ class TraceView extends PureComponent<Props, State> {
                         trace={parsedTrace}
                         dragProps={dragProps}
                         filterSpans={this.state.filterSpans}
-                        orgId={orgId}
                         organization={organization}
-                        spansWithErrors={spansWithErrors}
                         operationNameFilters={operationNameFilters}
                       />
                     </ScrollbarManager.Provider>