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 Alert from 'app/components/alert';
 import {Panel} from 'app/components/panels';
 import {Panel} from 'app/components/panels';
 import SearchBar from 'app/components/searchBar';
 import SearchBar from 'app/components/searchBar';
-import {ALL_ACCESS_PROJECTS} from 'app/constants/globalSelectionHeader';
 import {IconWarning} from 'app/icons';
 import {IconWarning} from 'app/icons';
 import {t, tn} from 'app/locale';
 import {t, tn} from 'app/locale';
 import space from 'app/styles/space';
 import space from 'app/styles/space';
 import {Organization} from 'app/types';
 import {Organization} from 'app/types';
 import {EventTransaction} from 'app/types/event';
 import {EventTransaction} from 'app/types/event';
 import {objectIsEmpty} from 'app/utils';
 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 * as QuickTraceContext from 'app/utils/performance/quickTrace/quickTraceContext';
-import {QueryResults, stringifyQueryObject} from 'app/utils/tokenizeSearch';
 import withOrganization from 'app/utils/withOrganization';
 import withOrganization from 'app/utils/withOrganization';
 
 
 import * as AnchorLinkManager from './anchorLinkManager';
 import * as AnchorLinkManager from './anchorLinkManager';
@@ -27,7 +23,7 @@ import Filter, {
 } from './filter';
 } from './filter';
 import TraceView from './traceView';
 import TraceView from './traceView';
 import {ParsedTraceType} from './types';
 import {ParsedTraceType} from './types';
-import {getTraceDateTimeRange, parseTrace} from './utils';
+import {parseTrace} from './utils';
 
 
 type Props = {
 type Props = {
   event: EventTransaction;
   event: EventTransaction;
@@ -108,104 +104,42 @@ class SpansInterface extends Component<Props, State> {
   };
   };
 
 
   render() {
   render() {
-    const {event, location, organization} = this.props;
+    const {event, organization} = this.props;
     const {parsedTrace} = this.state;
     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 (
     return (
       <Container hasErrors={!objectIsEmpty(event.errors)}>
       <Container hasErrors={!objectIsEmpty(event.errors)}>
         <QuickTraceContext.Consumer>
         <QuickTraceContext.Consumer>
           {quickTrace => (
           {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>
         </QuickTraceContext.Consumer>
       </Container>
       </Container>
@@ -239,39 +173,4 @@ const AlertContainer = styled('div')`
   margin-bottom: ${space(1)};
   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));
 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 {Organization} from 'app/types';
 import {EventTransaction} from 'app/types/event';
 import {EventTransaction} from 'app/types/event';
 import {defined} from 'app/utils';
 import {defined} from 'app/utils';
-import {TableDataRow} from 'app/utils/discover/discoverQuery';
 import * as QuickTraceContext from 'app/utils/performance/quickTrace/quickTraceContext';
 import * as QuickTraceContext from 'app/utils/performance/quickTrace/quickTraceContext';
 import {QuickTraceContextChildrenProps} from 'app/utils/performance/quickTrace/quickTraceContext';
 import {QuickTraceContextChildrenProps} from 'app/utils/performance/quickTrace/quickTraceContext';
 import {QuickTraceEvent, TraceError} from 'app/utils/performance/quickTrace/types';
 import {QuickTraceEvent, TraceError} from 'app/utils/performance/quickTrace/types';
@@ -183,7 +182,6 @@ const MARGIN_LEFT = 0;
 
 
 type SpanBarProps = {
 type SpanBarProps = {
   event: Readonly<EventTransaction>;
   event: Readonly<EventTransaction>;
-  orgId: string;
   organization: Organization;
   organization: Organization;
   trace: Readonly<ParsedTraceType>;
   trace: Readonly<ParsedTraceType>;
   span: Readonly<ProcessedSpanType>;
   span: Readonly<ProcessedSpanType>;
@@ -199,8 +197,6 @@ type SpanBarProps = {
   isRoot?: boolean;
   isRoot?: boolean;
   toggleSpanTree: () => void;
   toggleSpanTree: () => void;
   isCurrentSpanFilteredOut: boolean;
   isCurrentSpanFilteredOut: boolean;
-  totalNumberOfErrors: number;
-  spanErrors: TableDataRow[];
 };
 };
 
 
 type SpanBarState = {
 type SpanBarState = {
@@ -251,19 +247,10 @@ class SpanBar extends React.Component<SpanBarProps, SpanBarState> {
     errors,
     errors,
   }: {
   }: {
     isVisible: boolean;
     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 (
     return (
       <AnchorLinkManager.Consumer>
       <AnchorLinkManager.Consumer>
@@ -279,13 +266,10 @@ class SpanBar extends React.Component<SpanBarProps, SpanBarState> {
           return (
           return (
             <SpanDetail
             <SpanDetail
               span={span}
               span={span}
-              orgId={orgId}
               organization={organization}
               organization={organization}
               event={event}
               event={event}
               isRoot={!!isRoot}
               isRoot={!!isRoot}
               trace={trace}
               trace={trace}
-              totalNumberOfErrors={totalNumberOfErrors}
-              spanErrors={spanErrors}
               childTransactions={transactions}
               childTransactions={transactions}
               relatedErrors={errors}
               relatedErrors={errors}
               scrollToHash={scrollToHash}
               scrollToHash={scrollToHash}
@@ -510,10 +494,10 @@ class SpanBar extends React.Component<SpanBarProps, SpanBarState> {
 
 
   renderTitle(
   renderTitle(
     scrollbarManagerChildrenProps: ScrollbarManager.ScrollbarManagerChildrenProps,
     scrollbarManagerChildrenProps: ScrollbarManager.ScrollbarManagerChildrenProps,
-    errors: TraceError[]
+    errors: TraceError[] | null
   ) {
   ) {
     const {generateContentSpanBarRef} = scrollbarManagerChildrenProps;
     const {generateContentSpanBarRef} = scrollbarManagerChildrenProps;
-    const {span, treeDepth, spanErrors} = this.props;
+    const {span, treeDepth} = this.props;
 
 
     const operationName = getSpanOperation(span) ? (
     const operationName = getSpanOperation(span) ? (
       <strong>
       <strong>
@@ -526,7 +510,7 @@ class SpanBar extends React.Component<SpanBarProps, SpanBarState> {
     const description = span?.description ?? getSpanID(span);
     const description = span?.description ?? getSpanID(span);
 
 
     const left = treeDepth * (TOGGLE_BORDER_BOX / 2) + MARGIN_LEFT;
     const left = treeDepth * (TOGGLE_BORDER_BOX / 2) + MARGIN_LEFT;
-    const errored = errors.length + spanErrors.length > 0;
+    const errored = Boolean(errors && errors.length > 0);
 
 
     return (
     return (
       <RowTitleContainer
       <RowTitleContainer
@@ -788,38 +772,40 @@ class SpanBar extends React.Component<SpanBarProps, SpanBarState> {
     );
     );
   }
   }
 
 
-  getRelatedErrors(quickTrace: QuickTraceContextChildrenProps): TraceError[] {
+  getRelatedErrors(quickTrace: QuickTraceContextChildrenProps): TraceError[] | null {
     if (!quickTrace) {
     if (!quickTrace) {
-      return [];
+      return null;
     }
     }
 
 
     const {span} = this.props;
     const {span} = this.props;
     const {currentEvent} = quickTrace;
     const {currentEvent} = quickTrace;
 
 
     if (isGapSpan(span) || !currentEvent || !isTraceFull(currentEvent)) {
     if (isGapSpan(span) || !currentEvent || !isTraceFull(currentEvent)) {
-      return [];
+      return null;
     }
     }
 
 
     return currentEvent.errors.filter(error => error.span === span.span_id);
     return currentEvent.errors.filter(error => error.span === span.span_id);
   }
   }
 
 
-  getChildTransactions(quickTrace: QuickTraceContextChildrenProps): QuickTraceEvent[] {
+  getChildTransactions(
+    quickTrace: QuickTraceContextChildrenProps
+  ): QuickTraceEvent[] | null {
     if (!quickTrace) {
     if (!quickTrace) {
-      return [];
+      return null;
     }
     }
 
 
     const {span} = this.props;
     const {span} = this.props;
     const {trace} = quickTrace;
     const {trace} = quickTrace;
 
 
     if (isGapSpan(span) || !trace) {
     if (isGapSpan(span) || !trace) {
-      return [];
+      return null;
     }
     }
 
 
     return trace.filter(({parent_span_id}) => parent_span_id === span.span_id);
     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} = {}) {
   renderWarningText({warningText}: {warningText?: string} = {}) {
@@ -841,7 +827,7 @@ class SpanBar extends React.Component<SpanBarProps, SpanBarState> {
   }: {
   }: {
     dividerHandlerChildrenProps: DividerHandlerManager.DividerHandlerManagerChildrenProps;
     dividerHandlerChildrenProps: DividerHandlerManager.DividerHandlerManagerChildrenProps;
     scrollbarManagerChildrenProps: ScrollbarManager.ScrollbarManagerChildrenProps;
     scrollbarManagerChildrenProps: ScrollbarManager.ScrollbarManagerChildrenProps;
-    errors: TraceError[];
+    errors: TraceError[] | null;
   }) {
   }) {
     const {span, spanBarColour, spanBarHatch, spanNumber} = this.props;
     const {span, spanBarColour, spanBarHatch, spanNumber} = this.props;
     const startTimestamp: number = span.start_timestamp;
     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 * as React from 'react';
 import {browserHistory, withRouter, WithRouterProps} from 'react-router';
 import {browserHistory, withRouter, WithRouterProps} from 'react-router';
 import styled from '@emotion/styled';
 import styled from '@emotion/styled';
-import * as Sentry from '@sentry/react';
 import map from 'lodash/map';
 import map from 'lodash/map';
 
 
 import {Client} from 'app/api';
 import {Client} from 'app/api';
@@ -13,7 +12,6 @@ import FileSize from 'app/components/fileSize';
 import ExternalLink from 'app/components/links/externalLink';
 import ExternalLink from 'app/components/links/externalLink';
 import Link from 'app/components/links/link';
 import Link from 'app/components/links/link';
 import LoadingIndicator from 'app/components/loadingIndicator';
 import LoadingIndicator from 'app/components/loadingIndicator';
-import {getParams} from 'app/components/organizations/globalSelectionHeader/getParams';
 import {
 import {
   ErrorDot,
   ErrorDot,
   ErrorLevel,
   ErrorLevel,
@@ -29,14 +27,13 @@ import {
 } from 'app/components/quickTrace/utils';
 } from 'app/components/quickTrace/utils';
 import {ALL_ACCESS_PROJECTS} from 'app/constants/globalSelectionHeader';
 import {ALL_ACCESS_PROJECTS} from 'app/constants/globalSelectionHeader';
 import {IconAnchor, IconChevron, IconWarning} from 'app/icons';
 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 space from 'app/styles/space';
 import {Organization} from 'app/types';
 import {Organization} from 'app/types';
 import {EventTransaction} from 'app/types/event';
 import {EventTransaction} from 'app/types/event';
 import {assert} from 'app/types/utils';
 import {assert} from 'app/types/utils';
-import {TableDataRow} from 'app/utils/discover/discoverQuery';
 import EventView from 'app/utils/discover/eventView';
 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 getDynamicText from 'app/utils/getDynamicText';
 import {QuickTraceEvent, TraceError} from 'app/utils/performance/quickTrace/types';
 import {QuickTraceEvent, TraceError} from 'app/utils/performance/quickTrace/types';
 import withApi from 'app/utils/withApi';
 import withApi from 'app/utils/withApi';
@@ -57,105 +54,27 @@ type TransactionResult = {
 
 
 type Props = WithRouterProps & {
 type Props = WithRouterProps & {
   api: Client;
   api: Client;
-  orgId: string;
   organization: Organization;
   organization: Organization;
   event: Readonly<EventTransaction>;
   event: Readonly<EventTransaction>;
   span: Readonly<ProcessedSpanType>;
   span: Readonly<ProcessedSpanType>;
   isRoot: boolean;
   isRoot: boolean;
   trace: Readonly<ParsedTraceType>;
   trace: Readonly<ParsedTraceType>;
-  totalNumberOfErrors: number;
-  spanErrors: TableDataRow[];
-  childTransactions: QuickTraceEvent[];
-  relatedErrors: TraceError[];
+  childTransactions: QuickTraceEvent[] | null;
+  relatedErrors: TraceError[] | null;
   scrollToHash: (hash: string) => void;
   scrollToHash: (hash: string) => void;
 };
 };
 
 
 type State = {
 type State = {
-  transactionResults?: TransactionResult[];
   errorsOpened: boolean;
   errorsOpened: boolean;
 };
 };
 
 
 class SpanDetail extends React.Component<Props, State> {
 class SpanDetail extends React.Component<Props, State> {
   state: State = {
   state: State = {
-    transactionResults: undefined,
     errorsOpened: false,
     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 {
   renderTraversalButton(): React.ReactNode {
-    if (!this.state.transactionResults) {
+    if (!this.props.childTransactions) {
       // TODO: Amend size to use theme when we evetually refactor LoadingIndicator
       // TODO: Amend size to use theme when we evetually refactor LoadingIndicator
       // 12px is consistent with theme.iconSizes['xs'] but theme returns a string.
       // 12px is consistent with theme.iconSizes['xs'] but theme returns a string.
       return (
       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 (
       return (
         <StyledDiscoverButton size="xsmall" disabled>
         <StyledDiscoverButton size="xsmall" disabled>
           {t('No Children')}
           {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));
     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
       // Note: This is rendered by this.renderSpanChild() as a dedicated row
       return null;
       return null;
     }
     }
@@ -213,7 +132,7 @@ class SpanDetail extends React.Component<Props, State> {
       <StyledDiscoverButton
       <StyledDiscoverButton
         data-test-id="view-child-transactions"
         data-test-id="view-child-transactions"
         size="xsmall"
         size="xsmall"
-        to={childrenEventView.getResultsViewUrlTarget(orgId)}
+        to={childrenEventView.getResultsViewUrlTarget(organization.slug)}
       >
       >
         {t('View Children')}
         {t('View Children')}
       </StyledDiscoverButton>
       </StyledDiscoverButton>
@@ -221,17 +140,28 @@ class SpanDetail extends React.Component<Props, State> {
   }
   }
 
 
   renderSpanChild(): React.ReactNode {
   renderSpanChild(): React.ReactNode {
-    if (!this.state.transactionResults || this.state.transactionResults.length !== 1) {
+    const {childTransactions} = this.props;
+
+    if (!childTransactions || childTransactions.length !== 1) {
       return null;
       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 = (
     const viewChildButton = (
       <SpanEntryContext.Consumer>
       <SpanEntryContext.Consumer>
         {({getViewChildTransactionTarget}) => {
         {({getViewChildTransactionTarget}) => {
           const to = getViewChildTransactionTarget({
           const to = getViewChildTransactionTarget({
-            ...this.state.transactionResults![0],
+            ...transactionResult,
             eventSlug,
             eventSlug,
           });
           });
 
 
@@ -240,23 +170,17 @@ class SpanDetail extends React.Component<Props, State> {
           }
           }
 
 
           return (
           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')}
               {t('View Transaction')}
-            </StyledDiscoverButton>
+            </StyledButton>
           );
           );
         }}
         }}
       </SpanEntryContext.Consumer>
       </SpanEntryContext.Consumer>
     );
     );
 
 
-    const results = this.state.transactionResults[0];
-
     return (
     return (
       <Row title="Child Transaction" extra={viewChildButton}>
       <Row title="Child Transaction" extra={viewChildButton}>
-        {`${results.transaction} (${results['project.name']})`}
+        {`${transactionResult.transaction} (${transactionResult['project.name']})`}
       </Row>
       </Row>
     );
     );
   }
   }
@@ -296,125 +220,40 @@ class SpanDetail extends React.Component<Props, State> {
   };
   };
 
 
   renderSpanErrorMessage() {
   renderSpanErrorMessage() {
-    const {
-      orgId,
-      spanErrors,
-      totalNumberOfErrors,
-      span,
-      trace,
-      organization,
-      event,
-      relatedErrors,
-    } = this.props;
+    const {span, organization, relatedErrors} = this.props;
     const {errorsOpened} = this.state;
     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;
       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 (
     return (
       <Alert system type="error" icon={<IconWarning size="md" />}>
       <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>
       </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 {Organization} from 'app/types';
 import {EventTransaction} from 'app/types/event';
 import {EventTransaction} from 'app/types/event';
-import {TableData, TableDataRow} from 'app/utils/discover/discoverQuery';
 
 
 import {ScrollbarManagerChildrenProps, withScrollbarManager} from './scrollbarManager';
 import {ScrollbarManagerChildrenProps, withScrollbarManager} from './scrollbarManager';
 import SpanBar from './spanBar';
 import SpanBar from './spanBar';
 import {ParsedTraceType, ProcessedSpanType, TreeDepthType} from './types';
 import {ParsedTraceType, ProcessedSpanType, TreeDepthType} from './types';
-import {getSpanID, isGapSpan, SpanBoundsType, SpanGeneratedBoundsType} from './utils';
+import {SpanBoundsType, SpanGeneratedBoundsType} from './utils';
 
 
 type PropType = ScrollbarManagerChildrenProps & {
 type PropType = ScrollbarManagerChildrenProps & {
-  orgId: string;
   organization: Organization;
   organization: Organization;
   event: Readonly<EventTransaction>;
   event: Readonly<EventTransaction>;
   span: Readonly<ProcessedSpanType>;
   span: Readonly<ProcessedSpanType>;
@@ -26,7 +24,6 @@ type PropType = ScrollbarManagerChildrenProps & {
   isLast: boolean;
   isLast: boolean;
   isRoot?: boolean;
   isRoot?: boolean;
   isCurrentSpanFilteredOut: boolean;
   isCurrentSpanFilteredOut: boolean;
-  spansWithErrors: TableData | null | undefined;
 };
 };
 
 
 type State = {
 type State = {
@@ -60,32 +57,6 @@ class SpanGroup extends Component<PropType, State> {
     return this.props.renderedSpanChildren;
     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() {
   render() {
     const {
     const {
       spanBarColour,
       spanBarColour,
@@ -100,7 +71,6 @@ class SpanGroup extends Component<PropType, State> {
       treeDepth,
       treeDepth,
       spanNumber,
       spanNumber,
       isCurrentSpanFilteredOut,
       isCurrentSpanFilteredOut,
-      orgId,
       organization,
       organization,
       event,
       event,
     } = this.props;
     } = this.props;
@@ -110,7 +80,6 @@ class SpanGroup extends Component<PropType, State> {
         <SpanBar
         <SpanBar
           organization={organization}
           organization={organization}
           event={event}
           event={event}
-          orgId={orgId}
           spanBarColour={spanBarColour}
           spanBarColour={spanBarColour}
           spanBarHatch={spanBarHatch}
           spanBarHatch={spanBarHatch}
           span={span}
           span={span}
@@ -125,8 +94,6 @@ class SpanGroup extends Component<PropType, State> {
           isLast={isLast}
           isLast={isLast}
           isRoot={isRoot}
           isRoot={isRoot}
           isCurrentSpanFilteredOut={isCurrentSpanFilteredOut}
           isCurrentSpanFilteredOut={isCurrentSpanFilteredOut}
-          totalNumberOfErrors={this.getTotalNumberOfErrors()}
-          spanErrors={this.getSpanErrors()}
         />
         />
         {this.renderSpanChildren()}
         {this.renderSpanChildren()}
       </Fragment>
       </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 {t, tct} from 'app/locale';
 import {Organization} from 'app/types';
 import {Organization} from 'app/types';
 import {EventTransaction} from 'app/types/event';
 import {EventTransaction} from 'app/types/event';
-import {TableData} from 'app/utils/discover/discoverQuery';
 
 
 import {DragManagerChildrenProps} from './dragManager';
 import {DragManagerChildrenProps} from './dragManager';
 import {ActiveOperationFilter} from './filter';
 import {ActiveOperationFilter} from './filter';
@@ -43,13 +42,11 @@ type RenderedSpanTree = {
 };
 };
 
 
 type PropType = {
 type PropType = {
-  orgId: string;
   organization: Organization;
   organization: Organization;
   trace: ParsedTraceType;
   trace: ParsedTraceType;
   dragProps: DragManagerChildrenProps;
   dragProps: DragManagerChildrenProps;
   filterSpans: FilterSpans | undefined;
   filterSpans: FilterSpans | undefined;
   event: EventTransaction;
   event: EventTransaction;
-  spansWithErrors: TableData | null | undefined;
   operationNameFilters: ActiveOperationFilter;
   operationNameFilters: ActiveOperationFilter;
   traceViewRef: React.RefObject<HTMLDivElement>;
   traceViewRef: React.RefObject<HTMLDivElement>;
 };
 };
@@ -182,7 +179,7 @@ class SpanTree extends React.Component<PropType> {
     generateBounds: (bounds: SpanBoundsType) => SpanGeneratedBoundsType;
     generateBounds: (bounds: SpanBoundsType) => SpanGeneratedBoundsType;
     previousSiblingEndTimestamp: undefined | number;
     previousSiblingEndTimestamp: undefined | number;
   }): RenderedSpanTree => {
   }): RenderedSpanTree => {
-    const {orgId, event, spansWithErrors, organization} = this.props;
+    const {event, organization} = this.props;
 
 
     const spanBarColour: string = pickBarColour(getSpanOperation(span));
     const spanBarColour: string = pickBarColour(getSpanOperation(span));
     const spanChildren: Array<RawSpanType> = childSpans?.[getSpanID(span)] ?? [];
     const spanChildren: Array<RawSpanType> = childSpans?.[getSpanID(span)] ?? [];
@@ -296,7 +293,6 @@ class SpanTree extends React.Component<PropType> {
     const spanGapComponent =
     const spanGapComponent =
       isValidGap && isSpanDisplayed ? (
       isValidGap && isSpanDisplayed ? (
         <SpanGroup
         <SpanGroup
-          orgId={orgId}
           organization={organization}
           organization={organization}
           event={event}
           event={event}
           spanNumber={spanNumber}
           spanNumber={spanNumber}
@@ -310,7 +306,6 @@ class SpanTree extends React.Component<PropType> {
           numOfSpanChildren={0}
           numOfSpanChildren={0}
           renderedSpanChildren={[]}
           renderedSpanChildren={[]}
           isCurrentSpanFilteredOut={isCurrentSpanFilteredOut}
           isCurrentSpanFilteredOut={isCurrentSpanFilteredOut}
-          spansWithErrors={spansWithErrors}
           spanBarHatch
           spanBarHatch
         />
         />
       ) : null;
       ) : null;
@@ -324,7 +319,6 @@ class SpanTree extends React.Component<PropType> {
           {infoMessage}
           {infoMessage}
           {spanGapComponent}
           {spanGapComponent}
           <SpanGroup
           <SpanGroup
-            orgId={orgId}
             organization={organization}
             organization={organization}
             event={event}
             event={event}
             spanNumber={spanGroupNumber}
             spanNumber={spanGroupNumber}
@@ -340,7 +334,6 @@ class SpanTree extends React.Component<PropType> {
             spanBarColour={spanBarColour}
             spanBarColour={spanBarColour}
             isCurrentSpanFilteredOut={isCurrentSpanFilteredOut}
             isCurrentSpanFilteredOut={isCurrentSpanFilteredOut}
             spanBarHatch={false}
             spanBarHatch={false}
-            spansWithErrors={spansWithErrors}
           />
           />
         </React.Fragment>
         </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 {Organization} from 'app/types';
 import {EventTransaction} from 'app/types/event';
 import {EventTransaction} from 'app/types/event';
 import {createFuzzySearch} from 'app/utils/createFuzzySearch';
 import {createFuzzySearch} from 'app/utils/createFuzzySearch';
-import {TableData} from 'app/utils/discover/discoverQuery';
 
 
 import * as CursorGuideHandler from './cursorGuideHandler';
 import * as CursorGuideHandler from './cursorGuideHandler';
 import * as DividerHandlerManager from './dividerHandlerManager';
 import * as DividerHandlerManager from './dividerHandlerManager';
@@ -38,12 +37,10 @@ export type FilterSpans = {
 };
 };
 
 
 type Props = {
 type Props = {
-  orgId: string;
   organization: Organization;
   organization: Organization;
   event: Readonly<EventTransaction>;
   event: Readonly<EventTransaction>;
   parsedTrace: ParsedTraceType;
   parsedTrace: ParsedTraceType;
   searchQuery: string | undefined;
   searchQuery: string | undefined;
-  spansWithErrors: TableData | null | undefined;
   operationNameFilters: ActiveOperationFilter;
   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 (
     return (
       <DragManager interactiveLayerRef={this.minimapInteractiveRef}>
       <DragManager interactiveLayerRef={this.minimapInteractiveRef}>
@@ -219,9 +216,7 @@ class TraceView extends PureComponent<Props, State> {
                         trace={parsedTrace}
                         trace={parsedTrace}
                         dragProps={dragProps}
                         dragProps={dragProps}
                         filterSpans={this.state.filterSpans}
                         filterSpans={this.state.filterSpans}
-                        orgId={orgId}
                         organization={organization}
                         organization={organization}
-                        spansWithErrors={spansWithErrors}
                         operationNameFilters={operationNameFilters}
                         operationNameFilters={operationNameFilters}
                       />
                       />
                     </ScrollbarManager.Provider>
                     </ScrollbarManager.Provider>