Skip to content

Replays Self-Serve Bulk Delete System - #1834

Open
kaihao-zhao wants to merge 33 commits into
replays-delete-vulnerablefrom
replays-delete-stable-hesol61-r1b
Open

kaihao-zhao wants to merge 33 commits into
replays-delete-vulnerablefrom
replays-delete-stable-hesol61-r1b

Conversation

@kaihao-zhao

Copy link
Copy Markdown

See title.

armenzg and others added 30 commits June 20, 2025 12:49
…o 'low' (#93927)"

This reverts commit 8d04522.

Co-authored-by: roaga <47861399+roaga@users.noreply.github.com>
Missed in the initial commit, leading to some relevant logs being
unannotated.
We have had a few tasks get killed at 10% rollout.
Also add a test, so that this doesn't happen again
Fixes DE-129 and DE-156

---------

Co-authored-by: Cursor Agent <cursoragent@cursor.com>
These transitions should be matching
…` (#93946)

Use `project_id` on the replay record instead of the URL (where it does
not always exist).

---------

Co-authored-by: Cursor Agent <cursoragent@cursor.com>
Co-authored-by: getsantry[bot] <66042841+getsantry[bot]@users.noreply.github.com>
Also fixed `replay.view_html` -> `replay.view-html`

---------

Co-authored-by: Michelle Zhang <56095982+michellewzhang@users.noreply.github.com>
…948)

gets `npx @typescript/native-preview` passing again
The conditions associated with a DCG can change over time, and it's good
if we can be completely confident that they're consistent within a given
task execution.
This is unused and most regex experiments have required broader changes
to ensure that regexes are evaluated in a specific order (ex:
traceparent). Removing this for now to simplify the code and very
slightly improve runtime performance.
From some testing (on feedback lists of all different lengths), this
prompt seems to work better. It doesn't write overly long sentences and
also does a better job at "summarizing" versus just mentioning a few
specific topics and leaving out others.
Just remove a couple custom Flex* classes in favor of the Flex primitive
This has been killed a few times.

Refs SENTRY-42M7
…n table (#93892)

<!-- Describe your PR here. -->

[ticket](https://linear.app/getsentry/issue/ID-156/grouping-info-remove-type-field-from-ui)
The Type field in the Grouping Info section of the issue details page
was redundant.
This removes the Type row from all variant types while keeping the
underlying data structure intact.

before
![Screenshot 2025-06-20 at 12 00
54 PM](https://github.com/user-attachments/assets/97ca72da-0a52-4446-9825-cd4fcb505adf)

after
![Screenshot 2025-06-20 at 11 59
29 AM](https://github.com/user-attachments/assets/a4284d2b-c9f5-442f-b010-7fe72a598e39)
### Changes
Related to this PR: getsentry/sentry#93810. This
is part 1 of the change, which is pulling out the new component and just
adding it to the repo. Also includes some simplification of the logic in
the base component.

Part 2 will be replacing tables in widgets.

### Before/After

There is no UI change as the table is not being used yet. There is a new
story page for the component.
…93943)

to prevent this issue from becoming too noisy, add a noise config
Unfortunately, 'event_data' went from being the variable for current
event context to being the complete parsed data from Redis, and we
continued logging it per group.
That's more data than we should be logging even arguably once, let alone
per group.
Co-authored-by: Abdullah Khan <abdullahkhan@PG9Y57YDXQ.local>
Adds some simple analytics to our endpoint so we can begin building a
dashboard in Amplitude.
Previously, explore supported multiple y axis per chart, so each
visualize supported multiple y axis. That functionality has since been
removed for simplicity so update the types here to match. Keep in mind
that saved queries still store them as an array so when
serializing/deserializing, we still need to treat it as an array.
We'll need the `useGetTraceItemAttributeKeys` hook in other places so
refactoring it so that it can exported.
mrduncan and others added 3 commits June 20, 2025 13:20
When the max segment ID is null the process fails. We should exit early
since if there aren't any segments to delete there's nothing to do.

@unblocked-local-kaihao unblocked-local-kaihao Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

28 issues found.

About Unblocked

Unblocked has been set up to automatically review your team's pull requests to identify genuine bugs and issues.

📖 Documentation — Learn more in our docs.

💬 Ask questions — Mention @unblocked-local-kaihao to request a review or summary, or ask follow-up questions.

👍 Give feedback — React to comments with 👍 or 👎 to help us improve.

⚙️ Customize — Adjust settings in your preferences.

Comment on lines +166 to +172
columns={[]}
tableData={{
data: [],
meta: {
fields: {},
units: {},
},

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

When use-table-widget-visualization is enabled, every table receives empty columns, rows, and metadata regardless of the fetched result. Existing dashboard tables therefore become empty. Adapt result.data, result.meta, and the configured fields into the new component instead of supplying placeholders.

Comment on lines +153 to +155
while error_idx < len(error_events) and error_events[error_idx][
"timestamp"
] < event.get("timestamp", 0):

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nodestore event timestamps are Unix seconds, while rrweb event timestamps are milliseconds. Comparing them directly places essentially every error before the first recording event, even when the error happened later. Normalize errors and recording events to the same unit before comparison and log-message formatting.

Comment on lines +39 to +42
const queryKey = useMemo(
() => ['use-trace-item-attribute-keys', queryOptions],
[queryOptions]
);

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The request depends on organization.slug, but the cache key only contains filter options. Switching organizations with the same relative date range and an all-project selection reuses the previous organization's cached attribute collection under this key. Include the organization identifier so cached results remain scoped to the endpoint that produced them.

...normalizeDateTimeParams(datetime),
};

// environment left out intentionally as it's not supported

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Environment filtering is supported here: the attributes endpoint calls SearchResolver.resolve_query, which injects snuba_params.environments into its attribute-name filter. The previous hook sent selection.environments; this helper now omits it, so suggestions include attributes from other environments. Pass the selected environments through both the request options and cache key, and correct this comment.

id=event_id,
title=data.get("title", ""),
timestamp=data.get("timestamp", 0.0),
message=data.get("message", ""),

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Normalized event messages live in logentry.formatted or logentry.message, as used by Event.message, rather than the top-level message key. This lookup silently produces an empty message for those events and omits useful error context from the summary. Use the eventstore message accessor or its normalized-field fallback.

def get_merged_pr_single_issue_template(title: str, url: str, environment: str) -> str:
truncated_title = PRCommentWorkflow._truncate_title(title)
return MERGED_PR_SINGLE_ISSUE_TEMPLATE.format(
title=truncated_title,

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The formatter copies event-title brackets directly into the Markdown link-label syntax. Truncation can also retain an opening bracket while removing its matching closing bracket. Thus the generated comment contains unescaped title characters in the link delimiters rather than an encoded literal label. Escape the label after truncation and cover bracket-containing titles; provider-specific rendering consequences are not established by this checkout.

if recommended_event:
environment = recommended_event.get_environment()
if environment and environment.name:
return f" in `{environment.name}`"

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A valid environment name such as prodtest` is inserted between single-backtick delimiters without encoding its embedded backtick. The resulting comment contains three competing backtick delimiters instead of a code span safely enclosing the literal name. Use delimiters that accommodate embedded backticks and add a fixture for this input; exact provider rendering or notification behavior is not established here.

Comment on lines +125 to +129
const SummaryListContainer = styled('div')`
display: flex;
flex-direction: column;
gap: ${space(1)};
`;

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The new summary/list wrapper has an automatic content-based minimum instead of the previous direct FluidHeight item's overflow constraint. Its intrinsic height includes the summary, list header, and existing 300px list minimum. When the desktop viewport allocates less space than that combined minimum, the grid row can exceed its available space inside the overflow-hidden layout. Set min-height: 0 on the outer item and constrain the list to the remaining space; the exact browser-visible clipping was not reproduced.

Comment on lines +106 to +109
# Validate each report in the array
validated_reports = []
for report in raw_data:
browser_report = BrowserReport(**report)
serializer = BrowserReportSerializer(data=report)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

When the collector option is enabled, the public endpoint validates every report and retains every validated dictionary until the complete batch succeeds. There is no batch-count limit on this path, so serializer work and additional retained memory grow with caller-controlled batch size. DATA_UPLOAD_MAX_MEMORY_SIZE is also disabled in the application configuration. Add batch and report-size bounds before this processing; deployment-level limits and actual worker exhaustion were not verified.

Comment on lines +107 to +108
node_ids = [Event.generate_node_id(project_id, event_id=id) for id in error_ids]
events = nodestore.backend.get_multi(node_ids)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Every error ID returned for the replay is passed to one get_multi call before recording-segment pagination. Nodestore decodes and retains all corresponding payloads before the endpoint extracts the small error-context fields. Consequently, a small segment page does not bound the number or total size of event payloads loaded for its summary. Cap the context count and size and fetch bounded batches; a DSN-driven worker-exhaustion outcome was not verified.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.