Skip to content

feat(uptime): Add UptimeResponseCapture model#106341

Merged
wedamija merged 2 commits intomasterfrom
danf/uptime-response-capture-model
Jan 22, 2026
Merged

feat(uptime): Add UptimeResponseCapture model#106341
wedamija merged 2 commits intomasterfrom
danf/uptime-response-capture-model

Conversation

@wedamija
Copy link
Member

@wedamija wedamija commented Jan 15, 2026

Stores HTTP response data captured during uptime check failures. The response body and headers are stored in an associated File to help users debug why their endpoint is failing.

Project Context

This is part of the Uptime Response Capture feature - storing HTTP response data (body + headers) when downtime incidents are created to help users debug failures.

PRs in this series:

  1. sentry-kafka-schemas: Schema changes - feat(uptime): Add response_body and response_headers to CheckResult schema sentry-kafka-schemas#468
  2. uptime-checker: Response capture config and logic - feat(uptime): Add response capture support for failure debugging uptime-checker#464
  3. sentry: Storage model (UptimeResponseCapture) (this PR)
  4. sentry: Toggle producer (send_response_capture_toggle)
  5. sentry: Consumer capture creation
  6. sentry: Incident integration
  7. sentry: API and UI

@wedamija wedamija requested review from a team as code owners January 15, 2026 00:22
@github-actions github-actions bot added the Scope: Backend Automatically applied to PRs that change backend components label Jan 15, 2026
@github-actions
Copy link
Contributor

This PR has a migration; here is the generated SQL for src/sentry/uptime/migrations/0050_uptimeresponsecapture.py

for 0050_uptimeresponsecapture in uptime

--
-- Create model UptimeResponseCapture
--
CREATE TABLE "uptime_uptimeresponsecapture" ("id" bigint NOT NULL PRIMARY KEY GENERATED BY DEFAULT AS IDENTITY, "date_updated" timestamp with time zone NOT NULL, "date_added" timestamp with time zone NOT NULL, "file_id" bigint NOT NULL, "uptime_subscription_id" bigint NOT NULL);
ALTER TABLE "uptime_uptimeresponsecapture" ADD CONSTRAINT "uptime_uptimeresponsecapture_file_id_adfe9aaa_fk_sentry_file_id" FOREIGN KEY ("file_id") REFERENCES "sentry_file" ("id") DEFERRABLE INITIALLY DEFERRED NOT VALID;
ALTER TABLE "uptime_uptimeresponsecapture" VALIDATE CONSTRAINT "uptime_uptimeresponsecapture_file_id_adfe9aaa_fk_sentry_file_id";
ALTER TABLE "uptime_uptimeresponsecapture" ADD CONSTRAINT "uptime_uptimerespons_uptime_subscription__27b6838c_fk_uptime_up" FOREIGN KEY ("uptime_subscription_id") REFERENCES "uptime_uptimesubscription" ("id") DEFERRABLE INITIALLY DEFERRED NOT VALID;
ALTER TABLE "uptime_uptimeresponsecapture" VALIDATE CONSTRAINT "uptime_uptimerespons_uptime_subscription__27b6838c_fk_uptime_up";
CREATE INDEX CONCURRENTLY "uptime_uptimeresponsecapture_file_id_adfe9aaa" ON "uptime_uptimeresponsecapture" ("file_id");
CREATE INDEX CONCURRENTLY "uptime_uptimeresponsecapture_uptime_subscription_id_27b6838c" ON "uptime_uptimeresponsecapture" ("uptime_subscription_id");
CREATE INDEX CONCURRENTLY "uptime_upti_uptime__291edd_idx" ON "uptime_uptimeresponsecapture" ("uptime_subscription_id", "date_added");

@github-actions
Copy link
Contributor

github-actions bot commented Jan 15, 2026

This PR has a migration; here is the generated SQL for src/sentry/uptime/migrations/0050_response_capture.py

for 0050_response_capture in uptime

--
-- Add field capture_response_on_failure to uptimesubscription
--
ALTER TABLE "uptime_uptimesubscription" ADD COLUMN "capture_response_on_failure" boolean DEFAULT true NOT NULL;
--
-- Create model UptimeResponseCapture
--
CREATE TABLE "uptime_uptimeresponsecapture" ("id" bigint NOT NULL PRIMARY KEY GENERATED BY DEFAULT AS IDENTITY, "date_updated" timestamp with time zone NOT NULL, "date_added" timestamp with time zone NOT NULL, "file_id" bigint NOT NULL, "scheduled_check_time_ms" bigint NOT NULL, "uptime_subscription_id" bigint NOT NULL);
ALTER TABLE "uptime_uptimeresponsecapture" ADD CONSTRAINT "uptime_uptimerespons_uptime_subscription__27b6838c_fk_uptime_up" FOREIGN KEY ("uptime_subscription_id") REFERENCES "uptime_uptimesubscription" ("id") DEFERRABLE INITIALLY DEFERRED NOT VALID;
ALTER TABLE "uptime_uptimeresponsecapture" VALIDATE CONSTRAINT "uptime_uptimerespons_uptime_subscription__27b6838c_fk_uptime_up";
CREATE INDEX CONCURRENTLY "uptime_uptimeresponsecapture_uptime_subscription_id_27b6838c" ON "uptime_uptimeresponsecapture" ("uptime_subscription_id");
CREATE INDEX CONCURRENTLY "uptime_upti_uptime__aa1fbe_idx" ON "uptime_uptimeresponsecapture" ("uptime_subscription_id", "scheduled_check_time_ms");

@wedamija wedamija force-pushed the danf/uptime-response-capture-model branch from 2704aae to dc977c2 Compare January 15, 2026 21:07
@wedamija wedamija force-pushed the danf/uptime-response-capture-model branch 2 times, most recently from c061aa0 to 0141374 Compare January 15, 2026 21:15
Copy link
Member

@markstory markstory left a comment

Choose a reason for hiding this comment

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

Schema looks good to me.

uptime_subscription = FlexibleForeignKey(
"uptime.UptimeSubscription", related_name="response_captures"
)
file_id = BoundedBigIntegerField()
Copy link
Member

Choose a reason for hiding this comment

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

Should this model class override delete() and remove the associated File record?

Stores HTTP response data captured during uptime check failures. The response
body and headers are stored in an associated File to help users debug why
their endpoint is failing.

This is part of the Uptime Response Capture feature - storing HTTP response
data (body + headers) when downtime incidents are created to help users debug
failures.

PRs in this series:
1. sentry-kafka-schemas: Schema changes - <link to PR>
2. uptime-checker: Response capture config and logic - <link to PR>
3. uptime-checker: Toggle handling via set_response_capture action - <link to PR>
4. sentry: Storage model (UptimeResponseCapture) (this PR)
5. sentry: Toggle producer (send_response_capture_toggle)
6. sentry: Consumer capture creation
7. sentry: Incident integration
8. sentry: API and UI
Comment on lines +308 to +310
uptime_subscription = FlexibleForeignKey(
"uptime.UptimeSubscription", related_name="response_captures"
)
Copy link
Contributor

Choose a reason for hiding this comment

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

Bug: Deleting an UptimeSubscription triggers a database CASCADE that bypasses the custom UptimeResponseCapture.delete() method, orphaning associated File objects.
Severity: HIGH

Suggested Fix

Update the UptimeSubscriptionDeletionTask to explicitly handle the deletion of child UptimeResponseCapture objects. This can be done by overriding the get_child_relations() method in the task to include a relation for UptimeResponseCapture, ensuring their custom delete() logic is properly invoked.

Prompt for AI Agent
Review the code at the location below. A potential bug has been identified by an AI
agent.
Verify if this is a real issue. If it is, propose a fix; if not, explain why it's not
valid.

Location: src/sentry/uptime/models.py#L308-L310

Potential issue: When an `UptimeSubscription` is deleted via Sentry's custom deletion
task (`UptimeSubscriptionDeletionTask`), the database will perform a `CASCADE` delete on
related `UptimeResponseCapture` instances. This database-level operation bypasses
Django's model-level `delete()` method. As a result, the custom `delete()` method on
`UptimeResponseCapture`, which is responsible for cleaning up the associated `File`
object, is never called. This will lead to orphaned `File` objects in storage, causing a
persistent data leak.

Did we get this right? 👍 / 👎 to inform future reviews.

@wedamija wedamija merged commit b14d00d into master Jan 22, 2026
68 checks passed
@wedamija wedamija deleted the danf/uptime-response-capture-model branch January 22, 2026 21:56
@github-actions github-actions bot locked and limited conversation to collaborators Feb 7, 2026
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

Scope: Backend Automatically applied to PRs that change backend components

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants