Skip to content

Commit 5dc4ee4

Browse files
authored
fix(insights): Handle null span.group in Backend Insights widgets (#112379)
A self-hosted user complained in getsentry/self-hosted#4262 that their Backend Insights view breaks when ingesting Otel data. This PR adds some guards to prevent this crash, but it doesn't necessarily guarantee a great experience, e.g., - "Backend Insights" is on its way out, to be replaced with a pre-built dashboard. This hasn't been rolled out to self-hosted yet, so this user will have a somewhat different experience in the next few months if they upgrade - OTel data _should_ go through Sentry enrichment, and get a `span.group` attribute, but decisions around OTel are changing! We might do less enrichment (or different enrichment) there In the meantime, this PR makes two changes - asks for `has:span.group` to be present in a few widgets. This is a common pattern for us, and should work here, too - updates the types for the `span.group` property to mark it as possibly `null` and updates a bunch of usage Fixes DAIN-1467
1 parent b4c5121 commit 5dc4ee4

File tree

9 files changed

+29
-23
lines changed

9 files changed

+29
-23
lines changed

static/app/components/events/eventStatisticalDetector/eventRegressionTable.tsx

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ import {formatPercentage} from 'sentry/utils/number/formatPercentage';
1818
import {unreachable} from 'sentry/utils/unreachable';
1919

2020
export interface EventRegressionTableRow {
21-
group: string;
21+
group: string | null;
2222
operation: string;
2323
percentageChange: number;
2424
description?: string;
@@ -99,8 +99,8 @@ export function EventRegressionTable({
9999
) : data.length === 0 ? (
100100
<SimpleTable.Empty>{t('No results found for your query')}</SimpleTable.Empty>
101101
) : (
102-
data.map(row => (
103-
<SimpleTable.Row key={row.group}>
102+
data.map((row, index) => (
103+
<SimpleTable.Row key={`${row.group ?? index}-${row.operation}`}>
104104
{columns.map(column => (
105105
<SimpleTable.RowCell
106106
key={column.key}

static/app/views/dashboards/widgets/detailsWidget/detailsWidgetVisualization.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,7 @@ export function DetailsWidgetVisualization(props: DetailsWidgetVisualizationProp
6161
(spanDescription.split('?')[0] ?? '').split('.').pop()?.toLowerCase() ?? ''
6262
);
6363

64-
if (isImage) {
64+
if (isImage && spanGroup) {
6565
const projectId = span[SpanFields.PROJECT_ID]
6666
? Number(span[SpanFields.PROJECT_ID])
6767
: undefined;

static/app/views/insights/common/components/fullSpanDescription.tsx

Lines changed: 19 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ const formatter = new SQLishFormatter();
2121
interface Props {
2222
moduleName: ModuleName;
2323
filters?: Record<string, string>;
24-
group?: string;
24+
group?: string | null;
2525
shortDescription?: string;
2626
}
2727

@@ -33,7 +33,10 @@ export function FullSpanDescription({
3333
}: Props) {
3434
const {data: indexedSpans, isFetching: areIndexedSpansLoading} = useSpans(
3535
{
36-
search: MutableSearch.fromQueryObject({'span.group': group, ...filters}),
36+
search: MutableSearch.fromQueryObject({
37+
'span.group': group ?? undefined,
38+
...filters,
39+
}),
3740
limit: 1,
3841
fields: [
3942
SpanFields.PROJECT_ID,
@@ -104,7 +107,7 @@ export function FullSpanDescription({
104107

105108
type TruncatedQueryClipBoxProps = {
106109
children: ReactNode;
107-
group: string | undefined;
110+
group: string | null | undefined;
108111
};
109112

110113
function QueryClippedBox({group, children}: TruncatedQueryClipBoxProps) {
@@ -114,16 +117,20 @@ function QueryClippedBox({group, children}: TruncatedQueryClipBoxProps) {
114117

115118
return (
116119
<StyledClippedBox
117-
btnText={t('View full query')}
120+
btnText={group ? t('View full query') : undefined}
118121
clipHeight={500}
119-
buttonProps={{
120-
icon: <IconOpen />,
121-
onClick: () =>
122-
navigate({
123-
pathname: `${databaseURL}/spans/span/${group}`,
124-
query: {...location.query, isExpanded: true},
125-
}),
126-
}}
122+
buttonProps={
123+
group
124+
? {
125+
icon: <IconOpen />,
126+
onClick: () =>
127+
navigate({
128+
pathname: `${databaseURL}/spans/span/${group}`,
129+
query: {...location.query, isExpanded: true},
130+
}),
131+
}
132+
: undefined
133+
}
127134
>
128135
{children}
129136
</StyledClippedBox>

static/app/views/insights/common/components/spanDescription.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,7 @@ export function DatabaseSpanDescription({
5050

5151
const {data: indexedSpans, isFetching: areIndexedSpansLoading} = useSpans(
5252
{
53-
search: MutableSearch.fromQueryObject({'span.group': groupId}),
53+
search: MutableSearch.fromQueryObject({'span.group': groupId ?? undefined}),
5454
limit: 1,
5555
fields: [
5656
SpanFields.PROJECT_ID,

static/app/views/insights/common/components/spanGroupDetailsLink.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ interface Props {
1717
moduleName: ModuleName.DB | ModuleName.RESOURCE;
1818
projectId: number;
1919
extraLinkQueryParams?: Record<string, string>;
20-
group?: string;
20+
group?: string | null;
2121
spanOp?: string;
2222
}
2323

static/app/views/insights/common/components/tableCells/spanDescriptionCell.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ interface Props {
1919
moduleName: ModuleName.DB | ModuleName.RESOURCE;
2020
projectId: number;
2121
extraLinkQueryParams?: Record<string, string>;
22-
group?: string;
22+
group?: string | null;
2323
spanAction?: string;
2424
spanOp?: string;
2525
system?: string;

static/app/views/insights/common/components/widgets/overviewSlowNextjsSSRWidget.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ export default function OverviewSlowNextjsSSRWidget(props: LoadableChartWidgetPr
3939
const pageFilterChartParams = usePageFilterChartParams();
4040
const {query} = useTransactionNameQuery();
4141

42-
const fullQuery = `span.op:function.nextjs ${query}`;
42+
const fullQuery = `has:${SpanFields.SPAN_GROUP} ${SpanFields.SPAN_OP}:function.nextjs ${query}`;
4343

4444
const spansRequest = useSpans(
4545
{

static/app/views/insights/common/components/widgets/overviewTimeConsumingQueriesWidget.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,7 @@ export default function OverviewTimeConsumingQueriesWidget(
5252
const supportedSystems = Object.values(SupportedDatabaseSystem);
5353

5454
const search = new MutableSearch(
55-
`${SpanFields.DB_SYSTEM}:[${supportedSystems.join(',')}] ${query}`.trim()
55+
`has:${SpanFields.SPAN_GROUP} ${SpanFields.DB_SYSTEM}:[${supportedSystems.join(',')}] ${query}`.trim()
5656
);
5757
const referrer = Referrer.OVERVIEW_TIME_CONSUMING_QUERIES_WIDGET;
5858
const groupBy = SpanFields.NORMALIZED_DESCRIPTION;

static/app/views/insights/types.tsx

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -321,7 +321,6 @@ export type NonNullableStringFields =
321321
| SpanFields.FILE_EXTENSION
322322
| SpanFields.SPAN_OP
323323
| SpanFields.SPAN_DESCRIPTION
324-
| SpanFields.SPAN_GROUP
325324
| SpanFields.SPAN_CATEGORY
326325
| SpanFields.SPAN_SYSTEM
327326
| SpanFields.TIMESTAMP
@@ -338,7 +337,7 @@ export type NonNullableStringFields =
338337
| SpanFields.USER_DISPLAY
339338
| SpanFields.SENTRY_ORIGIN;
340339

341-
type NullableStringFields = SpanFields.NORMALIZED_DESCRIPTION;
340+
type NullableStringFields = SpanFields.NORMALIZED_DESCRIPTION | SpanFields.SPAN_GROUP;
342341

343342
export type SpanStringFields = NullableStringFields | NonNullableStringFields;
344343

0 commit comments

Comments
 (0)