Skip to content

feat(webhooks): Add payload validation during dual-write migration#116040

Merged
Christinarlong merged 16 commits into
masterfrom
crl/webhook-payload-validation-usage
May 26, 2026
Merged

feat(webhooks): Add payload validation during dual-write migration#116040
Christinarlong merged 16 commits into
masterfrom
crl/webhook-payload-validation-usage

Conversation

@Christinarlong
Copy link
Copy Markdown
Contributor

@Christinarlong Christinarlong commented May 21, 2026

Use the ParityChecker on when dual writing to validate the old and new path are outputting the same payload w/o PII exposed

relies on #116038

…param

Move _ParityChecker and _qualify from testutils/helpers/serializer_parity.py
to sentry/utils/payload_comparison.py so it can be used by production code.

Changes from the original:
- Renamed _ParityChecker -> PayloadComparator, _qualify -> qualify
- Added format_value parameter (defaults to repr, preserving current behavior)
- Added describe_value() for PII-safe value formatting in production

serializer_parity.py now imports from the new location; all existing callers
of assert_serializer_parity are unaffected.
@github-actions github-actions Bot added the Scope: Backend Automatically applied to PRs that change backend components label May 21, 2026
Move _ParityChecker and _qualify from testutils/helpers/serializer_parity.py
to sentry/utils/payload_comparison.py so it can be used by production code.

Changes from the original:
- Added format_value parameter (defaults to repr, preserving current behavior)
- Added describe_value() for PII-safe value formatting in production

serializer_parity.py now imports from the new location; all existing callers
of assert_serializer_parity are unaffected.
ParityChecker only recursed into nested structures when known_diffs
or unreliable field paths referenced something inside. When known_diffs
is empty (production validation case), differing nested dicts were
reported as a single flat mismatch instead of drilling into specific
fields. Now always recurses into Mapping and list types.
During the dual-write window (new path on, old path not disabled), build
both old-path and new-path payloads and compare them using
PayloadComparator with PII-safe formatting. Validation runs after both
paths have dispatched and never blocks webhook delivery.

Logs legacy_webhook.validation.match on parity, or
legacy_webhook.validation.payload_mismatch per field difference with
structural metadata only (no actual values).
…om_action

create_rule_instance_from_action calls build_rule_action_blob which
requires an integration_id mapping that webhooks don't have. Use
_get_triggering_rule_name directly — it does the same workflow->rule
label lookup without the integration_id requirement.

Also fixes mypy: dict -> dict[str, Any] in test return types.
TypedDict is not assignable to dict[str, Any] in mypy. Use Mapping[str, Any]
for old_payload (from get_group_data) and LegacyWebhookPayload for new_payload
(from build_legacy_webhook_payload).
@Christinarlong Christinarlong force-pushed the crl/webhook-payload-validation-usage branch from d566f1a to 581d107 Compare May 21, 2026 19:58
Comment on lines +26 to +27
old_payload = WebHooksPlugin().get_group_data(group, event, [rule_name])
new_payload = build_legacy_webhook_payload(invocation)
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

How expensive are these? Are these doing any queries that could add additional latency into our delivery process?

This may be another place we want to have an option to enable/disable these checks.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

I dont think these are expensive at all since we're just extracting fields for old and new payload. The more expensive thing would be the amount of recurring we're doing on the event payload. Yeah option makes sense.

Also this validation would happen after the old and new path webhooks have been sent out so it wouldn't affect webhook delivery

@Christinarlong Christinarlong marked this pull request as ready for review May 21, 2026 21:24
@Christinarlong Christinarlong requested review from a team as code owners May 21, 2026 21:24
@Christinarlong Christinarlong requested review from a team and GabeVillalobos May 21, 2026 21:24
Base automatically changed from crl/webhook-payload-validation to master May 26, 2026 18:27
…n log

Change payload validation option from bool to float-based rollout rate
so we can gradually increase validation coverage. Remove the logger.info
call from the dry run path to avoid logging payload contents.
@Christinarlong Christinarlong requested a review from markstory May 26, 2026 19:00
Copy link
Copy Markdown
Contributor

@cursor cursor Bot left a comment

Choose a reason for hiding this comment

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

Cursor Bugbot has reviewed your changes and found 1 potential issue.

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit a90e775. Configure here.

Copy link
Copy Markdown
Member

@leeandher leeandher left a comment

Choose a reason for hiding this comment

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

few comments, but makes sense!

Comment thread src/sentry/sentry_apps/services/legacy_webhook/validation.py
Comment thread src/sentry/sentry_apps/services/legacy_webhook/validation.py Outdated
Comment thread tests/sentry/sentry_apps/services/legacy_webhook/test_validation.py
…ndling

- Add logging_context variable to avoid redeclaring shared context
- Narrow try/except to only wrap compare_payloads call
- Log all mismatches in a single warning instead of one per mismatch
- Add test for comparison error exception handling
@Christinarlong Christinarlong merged commit f57c72d into master May 26, 2026
115 of 117 checks passed
@Christinarlong Christinarlong deleted the crl/webhook-payload-validation-usage branch May 26, 2026 20:53
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Scope: Backend Automatically applied to PRs that change backend components

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants