Skip to content

Commit 4bdb82e

Browse files
authored
Fully enable reprocessing-v2 (frontend) and update logic showing "Reprocess Event" action (#68412)
This fully enables the feature by simply removing all the checks and early returns for the feature flag. The logic to show the "Reprocess Event" action was updated so that all kinds of events that have debug files are eligible for reprocessing.
1 parent 1c451ae commit 4bdb82e

File tree

12 files changed

+154
-161
lines changed

12 files changed

+154
-161
lines changed

static/app/components/events/interfaces/debugMeta/debugImageDetails/index.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -257,7 +257,7 @@ export function DebugImageDetails({
257257
);
258258
const hasReprocessWarning =
259259
haveCandidatesUnappliedDebugFile &&
260-
displayReprocessEventAction(organization.features, event) &&
260+
displayReprocessEventAction(event) &&
261261
!!onReprocessEvent;
262262

263263
if (isError) {

static/app/components/modals/reprocessEventModal.spec.tsx

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,6 @@ describe('ReprocessEventModal', function () {
2424
organization: {
2525
id: '4660',
2626
slug: 'org',
27-
features: ['reprocessing-v2'],
2827
},
2928
});
3029

@@ -73,7 +72,6 @@ describe('ReprocessEventModal', function () {
7372
organization: {
7473
id: '4660',
7574
slug: 'org',
76-
features: ['reprocessing-v2'],
7775
},
7876
});
7977

static/app/utils/displayReprocessEventAction.spec.tsx

Lines changed: 9 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -6,25 +6,18 @@ import {
66
import {displayReprocessEventAction} from 'sentry/utils/displayReprocessEventAction';
77

88
describe('DisplayReprocessEventAction', function () {
9-
const orgFeatures = ['reprocessing-v2'];
10-
11-
it('returns false in case of no reprocessing-v2 feature', function () {
12-
const event = EventStacktraceMessageFixture();
13-
expect(displayReprocessEventAction([], event)).toBe(false);
14-
});
15-
169
it('returns false in case of no event', function () {
17-
expect(displayReprocessEventAction(orgFeatures)).toBe(false);
10+
expect(displayReprocessEventAction()).toBe(false);
1811
});
1912

2013
it('returns false if no exception entry is found', function () {
2114
const event = EventStacktraceMessageFixture();
22-
expect(displayReprocessEventAction(orgFeatures, event)).toBe(false);
15+
expect(displayReprocessEventAction(event)).toBe(false);
2316
});
2417

2518
it('returns false if the event is not a mini-dump event or an Apple crash report event or a Native event', function () {
2619
const event = EventStacktraceExceptionFixture();
27-
expect(displayReprocessEventAction(orgFeatures, event)).toBe(false);
20+
expect(displayReprocessEventAction(event)).toBe(false);
2821
});
2922

3023
describe('returns true', function () {
@@ -35,15 +28,15 @@ describe('DisplayReprocessEventAction', function () {
3528
platform: 'native',
3629
});
3730

38-
expect(displayReprocessEventAction(orgFeatures, event)).toBe(true);
31+
expect(displayReprocessEventAction(event)).toBe(true);
3932
});
4033

4134
it('cocoa', function () {
4235
const event = EventStacktraceExceptionFixture({
4336
platform: 'cocoa',
4437
});
4538

46-
expect(displayReprocessEventAction(orgFeatures, event)).toBe(true);
39+
expect(displayReprocessEventAction(event)).toBe(true);
4740
});
4841
});
4942

@@ -55,7 +48,7 @@ describe('DisplayReprocessEventAction', function () {
5548

5649
event.entries[0].data.values[0].stacktrace.frames[0].platform = 'native';
5750

58-
expect(displayReprocessEventAction(orgFeatures, event)).toBe(true);
51+
expect(displayReprocessEventAction(event)).toBe(true);
5952
});
6053

6154
it('cocoa', function () {
@@ -65,7 +58,7 @@ describe('DisplayReprocessEventAction', function () {
6558

6659
event.entries[0].data.values[0].stacktrace.frames[0].platform = 'cocoa';
6760

68-
expect(displayReprocessEventAction(orgFeatures, event)).toBe(true);
61+
expect(displayReprocessEventAction(event)).toBe(true);
6962
});
7063
});
7164
});
@@ -82,7 +75,7 @@ describe('DisplayReprocessEventAction', function () {
8275
},
8376
};
8477

85-
expect(displayReprocessEventAction(orgFeatures, event)).toBe(true);
78+
expect(displayReprocessEventAction(event)).toBe(true);
8679
});
8780

8881
it('apple crash report event', function () {
@@ -97,7 +90,7 @@ describe('DisplayReprocessEventAction', function () {
9790
},
9891
};
9992

100-
expect(displayReprocessEventAction(orgFeatures, event)).toBe(true);
93+
expect(displayReprocessEventAction(event)).toBe(true);
10194
});
10295
});
10396
});

static/app/utils/displayReprocessEventAction.tsx

Lines changed: 107 additions & 79 deletions
Original file line numberDiff line numberDiff line change
@@ -8,106 +8,134 @@ import type {
88
} from 'sentry/types';
99
import {EntryType} from 'sentry/types/event';
1010

11-
const NATIVE_PLATFORMS: PlatformKey[] = ['cocoa', 'native'];
11+
/** All platforms that always use Debug Files. */
12+
const DEBUG_FILE_PLATFORMS: Set<PlatformKey> = new Set([
13+
'objc',
14+
'cocoa',
15+
'swift',
16+
'native',
17+
'c',
18+
]);
19+
/** Other platforms that may use Debug Files. */
20+
const MAYBE_DEBUG_FILE_PLATFORMS: Set<PlatformKey> = new Set(['csharp', 'java']);
21+
22+
/**
23+
* Returns whether to display the "Reprocess Event" action.
24+
*
25+
* That is the case when we have a "reprocessable" event, which is an event that needs
26+
* Debug Files for proper processing, as those Debug Files could have been uploaded *after*
27+
* the Event was ingested.
28+
*/
29+
export function displayReprocessEventAction(event?: Event): boolean {
30+
if (!event) {
31+
return false;
32+
}
1233

13-
// Finds all frames in a given data blob and returns it's platforms
14-
function getPlatforms(exceptionValue: ExceptionValue | StacktraceType | null) {
15-
const frames = exceptionValue?.frames ?? [];
16-
const stacktraceFrames = (exceptionValue as ExceptionValue)?.stacktrace?.frames ?? [];
34+
const eventPlatforms = getEventPlatform(event);
35+
// Check Events from platforms that always use Debug Files as a fast-path
36+
if (hasIntersection(eventPlatforms, DEBUG_FILE_PLATFORMS)) {
37+
return true;
38+
}
1739

18-
if (!frames.length && !stacktraceFrames.length) {
19-
return [];
40+
const hasDebugImages = (event?.entries ?? []).some(
41+
entry => entry.type === EntryType.DEBUGMETA && entry.data.images.length > 0
42+
);
43+
44+
// Otherwise, check alternative platforms if they actually have Debug Files
45+
if (hasIntersection(eventPlatforms, MAYBE_DEBUG_FILE_PLATFORMS) && hasDebugImages) {
46+
return true;
2047
}
2148

22-
return [...frames, ...stacktraceFrames]
23-
.map(frame => frame.platform)
24-
.filter(platform => !!platform);
25-
}
49+
// Finally, fall back to checking the `platform` of each frame
50+
const exceptionEntry = event.entries.find(
51+
entry => entry.type === EntryType.EXCEPTION
52+
) as EntryException | undefined;
53+
54+
if (!exceptionEntry) {
55+
return false;
56+
}
2657

27-
function getStackTracePlatforms(event: Event, exceptionEntry: EntryException) {
28-
// Fetch platforms in stack traces of an exception entry
29-
const exceptionEntryPlatforms = (exceptionEntry.data.values ?? []).flatMap(
30-
getPlatforms
58+
return hasIntersection(
59+
getStackTracePlatforms(event, exceptionEntry),
60+
DEBUG_FILE_PLATFORMS
3161
);
62+
}
63+
64+
/**
65+
* Returns whether the two Sets have intersecting elements.
66+
*/
67+
function hasIntersection<T>(set1: Set<T>, set2: Set<T>): boolean {
68+
for (const v of set1) {
69+
if (set2.has(v)) {
70+
return true;
71+
}
72+
}
73+
return false;
74+
}
75+
76+
/**
77+
* Returns the event platform as a Set.
78+
*/
79+
function getEventPlatform(event: Event): Set<PlatformKey> {
80+
const platforms = new Set<PlatformKey>();
81+
addPlatforms(platforms, [event]);
82+
return platforms;
83+
}
84+
85+
/**
86+
* Returns a Set of all platforms found in the `event` and `exceptionEntry`.
87+
*/
88+
function getStackTracePlatforms(
89+
event: Event,
90+
exceptionEntry: EntryException
91+
): Set<PlatformKey> {
92+
const platforms = new Set<PlatformKey>();
93+
94+
// Add platforms in stack traces of an exception entry
95+
(exceptionEntry.data.values ?? []).forEach(exc => addFramePlatforms(platforms, exc));
3296

33-
// Fetch platforms in an exception entry
97+
// Add platforms in a stack trace entry
3498
const stackTraceEntry = (event.entries.find(
3599
entry => entry.type === EntryType.STACKTRACE
36100
)?.data ?? {}) as StacktraceType;
37101

38-
// Fetch platforms in an exception entry
39-
const stackTraceEntryPlatforms = Object.keys(stackTraceEntry).flatMap(key =>
40-
getPlatforms(stackTraceEntry[key])
102+
Object.keys(stackTraceEntry).forEach(key =>
103+
addFramePlatforms(platforms, stackTraceEntry[key])
41104
);
42105

43-
// Fetch platforms in an thread entry
106+
// Add platforms in a thread entry
44107
const threadEntry = (event.entries.find(entry => entry.type === EntryType.THREADS)?.data
45108
.values ?? []) as Array<Thread>;
46109

47-
// Fetch platforms in a thread entry
48-
const threadEntryPlatforms = threadEntry.flatMap(({stacktrace}) =>
49-
getPlatforms(stacktrace)
50-
);
110+
threadEntry.forEach(({stacktrace}) => addFramePlatforms(platforms, stacktrace));
51111

52-
return new Set([
53-
...exceptionEntryPlatforms,
54-
...stackTraceEntryPlatforms,
55-
...threadEntryPlatforms,
56-
]);
112+
return platforms;
57113
}
58114

59-
// Checks whether an event indicates that it is a native event.
60-
function isNativeEvent(event: Event, exceptionEntry: EntryException) {
61-
const {platform} = event;
62-
63-
if (platform && NATIVE_PLATFORMS.includes(platform)) {
64-
return true;
65-
}
66-
67-
const stackTracePlatforms = getStackTracePlatforms(event, exceptionEntry);
68-
69-
return NATIVE_PLATFORMS.some(nativePlatform => stackTracePlatforms.has(nativePlatform));
70-
}
71-
72-
// Checks whether an event indicates that it has an associated minidump.
73-
function isMinidumpEvent(exceptionEntry: EntryException) {
74-
const {data} = exceptionEntry;
75-
return (data.values ?? []).some(value => value.mechanism?.type === 'minidump');
76-
}
115+
/**
116+
* Adds all the platforms in the frames of `exceptionValue` to the `platforms` Set.
117+
*/
118+
function addFramePlatforms(
119+
platforms: Set<PlatformKey>,
120+
exceptionValue: ExceptionValue | StacktraceType | null
121+
) {
122+
const frames = exceptionValue?.frames ?? [];
123+
const stacktraceFrames = (exceptionValue as ExceptionValue)?.stacktrace?.frames ?? [];
77124

78-
// Checks whether an event indicates that it has an apple crash report.
79-
function isAppleCrashReportEvent(exceptionEntry: EntryException) {
80-
const {data} = exceptionEntry;
81-
return (data.values ?? []).some(value => value.mechanism?.type === 'applecrashreport');
125+
addPlatforms(platforms, frames);
126+
addPlatforms(platforms, stacktraceFrames);
82127
}
83128

84-
export function displayReprocessEventAction(orgFeatures: Array<string>, event?: Event) {
85-
if (!event || !orgFeatures.includes('reprocessing-v2')) {
86-
return false;
87-
}
88-
89-
const {entries} = event;
90-
const exceptionEntry = entries.find(entry => entry.type === EntryType.EXCEPTION) as
91-
| EntryException
92-
| undefined;
93-
94-
if (!exceptionEntry) {
95-
return false;
129+
/**
130+
* Adds all the `platform` properties found in `iter` to the `platforms` Set.
131+
*/
132+
function addPlatforms(
133+
platforms: Set<PlatformKey>,
134+
iter: Array<{platform?: PlatformKey | null}>
135+
) {
136+
for (const o of iter) {
137+
if (o.platform) {
138+
platforms.add(o.platform);
139+
}
96140
}
97-
98-
// We want to show the reprocessing button if the issue in question is native or contains native frames.
99-
// The logic is taken from the symbolication pipeline in Python, where it is used to determine whether reprocessing
100-
// payloads should be stored:
101-
// https://github.com/getsentry/sentry/blob/cb7baef414890336881d67b7a8433ee47198c701/src/sentry/lang/native/processing.py#L425-L426
102-
// It is still not ideal as one can always merge native and non-native events together into one issue,
103-
// but it's the best approximation we have.
104-
if (
105-
!isMinidumpEvent(exceptionEntry) &&
106-
!isAppleCrashReportEvent(exceptionEntry) &&
107-
!isNativeEvent(event, exceptionEntry)
108-
) {
109-
return false;
110-
}
111-
112-
return true;
113141
}

static/app/views/issueDetails/actions/index.spec.tsx

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,6 @@ const group = GroupFixture({
3737
const organization = OrganizationFixture({
3838
id: '4660',
3939
slug: 'org',
40-
features: ['reprocessing-v2'],
4140
});
4241

4342
describe('GroupActions', function () {
@@ -132,7 +131,7 @@ describe('GroupActions', function () {
132131
});
133132

134133
describe('reprocessing', function () {
135-
it('renders ReprocessAction component if org has feature flag reprocessing-v2 and native exception event', async function () {
134+
it('renders ReprocessAction component if org has native exception event', async function () {
136135
const event = EventStacktraceExceptionFixture({
137136
platform: 'native',
138137
});

static/app/views/issueDetails/actions/index.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -412,7 +412,7 @@ export function Actions(props: Props) {
412412
{
413413
key: 'reprocess',
414414
label: t('Reprocess events'),
415-
hidden: !displayReprocessEventAction(organization.features, event),
415+
hidden: !displayReprocessEventAction(event),
416416
onAction: onReprocessEvent,
417417
},
418418
{

0 commit comments

Comments
 (0)