Browse Source

feat(perf-issues): Add span evidence to Render Blocking Asset (#44956)

George Gritsouk 2 years ago
parent
commit
594d95c61f

+ 1 - 0
bin/load-mocks

@@ -1141,6 +1141,7 @@ def create_mock_transactions(
                     "parent_span_id": parent_span_id,
                     "span_id": uuid4().hex[:16],
                     "hash": "858fea692d4d93e8",
+                    "data": {"Encoded Body Size": 1000001},
                 }
             ]
 

+ 16 - 2
static/app/components/events/interfaces/performance/spanEvidenceKeyValueList.spec.tsx

@@ -384,12 +384,16 @@ describe('SpanEvidenceKeyValueList', () => {
     const builder = new TransactionEventBuilder(
       'a1',
       '/',
-      IssueType.PERFORMANCE_RENDER_BLOCKING_ASSET
+      IssueType.PERFORMANCE_RENDER_BLOCKING_ASSET,
+      {
+        duration: 3,
+        fcp: 2500,
+      }
     );
 
     const offenderSpan = new MockSpan({
       startTimestamp: 0,
-      endTimestamp: 1000,
+      endTimestamp: 1.0,
       op: 'resource.script',
       description: 'https://example.com/resource.js',
       problemSpan: ProblemSpan.OFFENDER,
@@ -409,6 +413,16 @@ describe('SpanEvidenceKeyValueList', () => {
       expect(
         screen.getByTestId('span-evidence-key-value-list.slow-resource-span')
       ).toHaveTextContent('resource.script - https://example.com/resource.js');
+
+      expect(screen.getByRole('cell', {name: 'FCP Delay'})).toBeInTheDocument();
+      expect(
+        screen.getByTestId('span-evidence-key-value-list.fcp-delay')
+      ).toHaveTextContent('1s (40% of 2.50s)');
+
+      expect(screen.getByRole('cell', {name: 'Duration Impact'})).toBeInTheDocument();
+      expect(
+        screen.getByTestId('span-evidence-key-value-list.duration-impact')
+      ).toHaveTextContent('33% (1s/3.00s');
     });
   });
 

+ 35 - 19
static/app/components/events/interfaces/performance/spanEvidenceKeyValueList.tsx

@@ -181,14 +181,23 @@ const SlowDBQueryEvidence = ({event, offendingSpans}: SpanEvidenceKeyValueListPr
 const RenderBlockingAssetSpanEvidence = ({
   event,
   offendingSpans,
-}: SpanEvidenceKeyValueListProps) => (
-  <PresortedKeyValueList
-    data={[
-      makeTransactionNameRow(event),
-      makeRow(t('Slow Resource Span'), getSpanEvidenceValue(offendingSpans[0])),
-    ]}
-  />
-);
+}: SpanEvidenceKeyValueListProps) => {
+  const offendingSpan = offendingSpans[0]; // For render-blocking assets, there is only one offender
+
+  return (
+    <PresortedKeyValueList
+      data={[
+        makeTransactionNameRow(event),
+        makeRow(t('Slow Resource Span'), getSpanEvidenceValue(offendingSpan)),
+        makeRow(
+          t('FCP Delay'),
+          formatDelay(getSpanDuration(offendingSpan), event.measurements?.fcp?.value ?? 0)
+        ),
+        makeRow(t('Duration Impact'), getSingleSpanDurationImpact(event, offendingSpan)),
+      ]}
+    />
+  );
+};
 
 const UncompressedAssetSpanEvidence = ({
   event,
@@ -288,7 +297,7 @@ const sumSpanDurations = (spans: Span[]) => {
 };
 
 const getSpanDuration = ({timestamp, start_timestamp}: Span) => {
-  return timestamp && start_timestamp ? (timestamp - start_timestamp) * 1000 : 0;
+  return ((timestamp ?? 0) - (start_timestamp ?? 0)) * 1000;
 };
 
 function getDurationImpact(event: EventTransaction, durationAdded: number) {
@@ -296,21 +305,28 @@ function getDurationImpact(event: EventTransaction, durationAdded: number) {
   if (!transactionTime) {
     return null;
   }
-  const percent = durationAdded / transactionTime;
+
+  return formatDurationImpact(durationAdded, transactionTime);
+}
+
+function formatDurationImpact(durationAdded: number, totalDuration: number) {
+  const percent = durationAdded / totalDuration;
+
   return `${toRoundedPercent(percent)} (${getPerformanceDuration(
     durationAdded
-  )}/${getPerformanceDuration(transactionTime)})`;
+  )}/${getPerformanceDuration(totalDuration)})`;
+}
+
+function formatDelay(durationAdded: number, totalDuration: number) {
+  const percent = durationAdded / totalDuration;
+
+  return `${getPerformanceDuration(durationAdded)} (${toRoundedPercent(
+    percent
+  )} of ${getPerformanceDuration(totalDuration)})`;
 }
 
 function getSingleSpanDurationImpact(event: EventTransaction, span: Span) {
-  if (
-    typeof span.timestamp === 'undefined' ||
-    typeof span.start_timestamp === 'undefined'
-  ) {
-    return null;
-  }
-  const spanTime = span?.timestamp - span?.start_timestamp;
-  return getDurationImpact(event, spanTime * 1000);
+  return getDurationImpact(event, getSpanDuration(span));
 }
 
 function getSpanDataField(span: Span, field: string) {

+ 7 - 0
tests/js/sentry-test/performance/utils.ts

@@ -21,6 +21,7 @@ type AddSpanOpts = {
 
 interface TransactionSettings {
   duration?: number;
+  fcp?: number;
 }
 export class TransactionEventBuilder {
   TRACE_ID = '8cbbc19c0f54447ab702f00263262726';
@@ -73,6 +74,12 @@ export class TransactionEventBuilder {
       fingerprints: [],
       location: null,
       message: '',
+      measurements: {
+        fcp: {
+          value: transactionSettings?.fcp ?? 0,
+          unit: 'millisecond',
+        },
+      },
       metadata: {
         current_level: undefined,
         current_tree_label: undefined,