Просмотр исходного кода

fix(suspect-spans): Bad example txns without the span (#31552)

There are some cases where we get a bad response where an example doesn't
actually contain the specified span. This is a hot fix to make sure the page
doesn't error, and an actual fix to follow.
Tony Xiao 3 лет назад
Родитель
Сommit
b18b4678a6

+ 19 - 14
static/app/views/performance/transactionSummary/transactionSpans/spanDetails/spanDetailsTable.tsx

@@ -67,20 +67,25 @@ export default function SpanTable(props: Props) {
     return null;
   }
 
-  const data = examples.map(example => ({
-    id: example.id,
-    project: project?.slug,
-    // timestamps are in seconds but want them in milliseconds
-    timestamp: example.finishTimestamp * 1000,
-    transactionDuration: (example.finishTimestamp - example.startTimestamp) * 1000,
-    spanDuration: example.nonOverlappingExclusiveTime,
-    occurrences: example.spans.length,
-    cumulativeDuration: example.spans.reduce(
-      (duration, span) => duration + span.exclusiveTime,
-      0
-    ),
-    spans: example.spans,
-  }));
+  const data = examples
+    // we assume that the span appears in each example at least once,
+    // if this assumption is broken, nothing onwards will work so
+    // filter out such examples
+    .filter(example => example.spans.length > 0)
+    .map(example => ({
+      id: example.id,
+      project: project?.slug,
+      // timestamps are in seconds but want them in milliseconds
+      timestamp: example.finishTimestamp * 1000,
+      transactionDuration: (example.finishTimestamp - example.startTimestamp) * 1000,
+      spanDuration: example.nonOverlappingExclusiveTime,
+      occurrences: example.spans.length,
+      cumulativeDuration: example.spans.reduce(
+        (duration, span) => duration + span.exclusiveTime,
+        0
+      ),
+      spans: example.spans,
+    }));
 
   return (
     <Fragment>

+ 70 - 0
tests/js/spec/views/performance/transactionSpans/spanDetails.spec.tsx

@@ -119,6 +119,76 @@ describe('Performance > Transaction Spans > Span Details', function () {
     });
   });
 
+  describe('With Bad Span Data', function () {
+    it('filters examples missing spans', async function () {
+      MockApiClient.addMockResponse({
+        url: '/organizations/org-slug/events-spans-performance/',
+        body: generateSuspectSpansResponse(),
+      });
+
+      // just want to get one span in the response
+      const badExamples = [generateSuspectSpansResponse({examplesOnly: true})[0]];
+      for (const example of badExamples[0].examples) {
+        // make sure that the spans array is empty
+        example.spans = [];
+      }
+
+      MockApiClient.addMockResponse({
+        url: '/organizations/org-slug/events-spans/',
+        body: badExamples,
+      });
+
+      MockApiClient.addMockResponse({
+        url: '/organizations/org-slug/events-spans-stats/',
+        body: {
+          'percentileArray(spans_exclusive_time, 0.75)': {
+            data: [
+              [0, [{count: 0}]],
+              [10, [{count: 0}]],
+            ],
+            order: 2,
+            start: 0,
+            end: 10,
+          },
+          'percentileArray(spans_exclusive_time, 0.95)': {
+            data: [
+              [0, [{count: 0}]],
+              [10, [{count: 0}]],
+            ],
+            order: 2,
+            start: 0,
+            end: 10,
+          },
+          'percentileArray(spans_exclusive_time, 0.99)': {
+            data: [
+              [0, [{count: 0}]],
+              [10, [{count: 0}]],
+            ],
+            order: 2,
+            start: 0,
+            end: 10,
+          },
+        },
+      });
+
+      const data = initializeData({
+        features: ['performance-view', 'performance-suspect-spans-view'],
+        query: {project: '1', transaction: 'transaction'},
+      });
+
+      mountWithTheme(<SpanDetails params={{spanSlug: 'op:aaaaaaaa'}} {...data} />, {
+        context: data.routerContext,
+        organization: data.organization,
+      });
+
+      expect(await screen.findByText('Event ID')).toBeInTheDocument();
+      expect(await screen.findByText('Timestamp')).toBeInTheDocument();
+      expect(await screen.findByText('Span Duration')).toBeInTheDocument();
+      expect(await screen.findByText('Count')).toBeInTheDocument();
+      expect(await screen.findByText('Cumulative Duration')).toBeInTheDocument();
+    });
+  });
+
   describe('With Span Data', function () {
     beforeEach(function () {
       MockApiClient.addMockResponse({