Skip to content

feat(models): Recreate CodeReviewEvent with updated schema [3/3]#109424

Merged
vaind merged 3 commits intomasterfrom
feat/recreate-code-review-event
Mar 4, 2026
Merged

feat(models): Recreate CodeReviewEvent with updated schema [3/3]#109424
vaind merged 3 commits intomasterfrom
feat/recreate-code-review-event

Conversation

@vaind
Copy link
Contributor

@vaind vaind commented Feb 26, 2026

Recreate the CodeReviewEvent table with reviewer feedback from PR #108531 applied:

  • DefaultFieldsModel base class for standard date_added/date_updated fields
  • FlexibleForeignKey for organization and repository (cascade deletion, referential integrity)
  • RelocationScope.Global to match Repository's scope (required for org-scoped exports since Repository is Global)
  • DateUpdatedComparator for date_added/date_updated in backup comparators
  • Backup test coverage in create_exhaustive_organization()

Migration is a clean auto-generated CreateModel — no manual edits.

This is PR 3 of 3:

  1. ref(models): Pending-delete CodeReviewEvent table [1/3] #109420SafeDeleteModel with MOVE_TO_PENDING (merged)
  2. ref(models): Delete CodeReviewEvent table [2/3] #109422SafeDeleteModel with DELETE (merged)
  3. This PR — Recreate with updated schema

@github-actions github-actions bot added the Scope: Backend Automatically applied to PRs that change backend components label Feb 26, 2026
@github-actions
Copy link
Contributor

This PR has a migration; here is the generated SQL for src/sentry/migrations/1034_remove_code_review_event.py src/sentry/migrations/1035_delete_code_review_event.py src/sentry/migrations/1036_create_code_review_event.py

for 1034_remove_code_review_event in sentry

--
-- Moved model CodeReviewEvent to pending deletion state
--
-- (no-op)

for 1035_delete_code_review_event in sentry

--
-- Delete model CodeReviewEvent
--
DROP TABLE "sentry_code_review_event" CASCADE;

for 1036_create_code_review_event in sentry

--
-- Create model CodeReviewEvent
--
CREATE TABLE "sentry_code_review_event" ("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, "pr_number" integer NULL, "pr_title" text NULL, "pr_author" text NULL, "pr_url" text NULL, "pr_state" varchar(16) NULL, "raw_event_type" varchar(64) NOT NULL, "raw_event_action" varchar(64) NOT NULL, "trigger_id" varchar(64) NULL, "trigger" varchar(64) NULL, "trigger_user" text NULL, "trigger_at" timestamp with time zone NOT NULL, "target_commit_sha" varchar(64) NULL, "status" varchar(32) NOT NULL, "denial_reason" text NULL, "webhook_received_at" timestamp with time zone NULL, "preflight_completed_at" timestamp with time zone NULL, "task_enqueued_at" timestamp with time zone NULL, "sent_to_seer_at" timestamp with time zone NULL, "review_started_at" timestamp with time zone NULL, "review_completed_at" timestamp with time zone NULL, "seer_run_id" varchar(64) NULL, "comments_posted" integer NULL CHECK ("comments_posted" >= 0), "review_result" jsonb NULL, "organization_id" bigint NOT NULL, "repository_id" bigint NOT NULL);
ALTER TABLE "sentry_code_review_event" ADD CONSTRAINT "sentry_code_review_e_organization_id_1ce9fe63_fk_sentry_or" FOREIGN KEY ("organization_id") REFERENCES "sentry_organization" ("id") DEFERRABLE INITIALLY DEFERRED NOT VALID;
ALTER TABLE "sentry_code_review_event" VALIDATE CONSTRAINT "sentry_code_review_e_organization_id_1ce9fe63_fk_sentry_or";
ALTER TABLE "sentry_code_review_event" ADD CONSTRAINT "sentry_code_review_e_repository_id_a5be62a4_fk_sentry_re" FOREIGN KEY ("repository_id") REFERENCES "sentry_repository" ("id") DEFERRABLE INITIALLY DEFERRED NOT VALID;
ALTER TABLE "sentry_code_review_event" VALIDATE CONSTRAINT "sentry_code_review_e_repository_id_a5be62a4_fk_sentry_re";
CREATE UNIQUE INDEX CONCURRENTLY "unique_org_repo_trigger_id" ON "sentry_code_review_event" ("organization_id", "repository_id", "trigger_id") WHERE "trigger_id" IS NOT NULL;
CREATE INDEX CONCURRENTLY "sentry_code_review_event_organization_id_1ce9fe63" ON "sentry_code_review_event" ("organization_id");
CREATE INDEX CONCURRENTLY "sentry_code_review_event_repository_id_a5be62a4" ON "sentry_code_review_event" ("repository_id");
CREATE INDEX CONCURRENTLY "sentry_code_date_ad_a2451c_idx" ON "sentry_code_review_event" ("date_added");
CREATE INDEX CONCURRENTLY "sentry_code_organiz_4f4b09_idx" ON "sentry_code_review_event" ("organization_id", "trigger_at");
CREATE INDEX CONCURRENTLY "sentry_code_organiz_7ba32c_idx" ON "sentry_code_review_event" ("organization_id", "repository_id", "trigger_at");
CREATE INDEX CONCURRENTLY "sentry_code_organiz_76bbd1_idx" ON "sentry_code_review_event" ("organization_id", "repository_id", "pr_number");

@github-actions
Copy link
Contributor

This PR has a migration; here is the generated SQL for src/sentry/migrations/1035_delete_code_review_event.py src/sentry/migrations/1036_create_code_review_event.py

for 1035_delete_code_review_event in sentry

--
-- Delete model CodeReviewEvent
--
DROP TABLE "sentry_code_review_event" CASCADE;

for 1036_create_code_review_event in sentry

--
-- Create model CodeReviewEvent
--
CREATE TABLE "sentry_code_review_event" ("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, "pr_number" integer NULL, "pr_title" text NULL, "pr_author" text NULL, "pr_url" text NULL, "pr_state" varchar(16) NULL, "raw_event_type" varchar(64) NOT NULL, "raw_event_action" varchar(64) NOT NULL, "trigger_id" varchar(64) NULL, "trigger" varchar(64) NULL, "trigger_user" text NULL, "trigger_at" timestamp with time zone NOT NULL, "target_commit_sha" varchar(64) NULL, "status" varchar(32) NOT NULL, "denial_reason" text NULL, "webhook_received_at" timestamp with time zone NULL, "preflight_completed_at" timestamp with time zone NULL, "task_enqueued_at" timestamp with time zone NULL, "sent_to_seer_at" timestamp with time zone NULL, "review_started_at" timestamp with time zone NULL, "review_completed_at" timestamp with time zone NULL, "seer_run_id" varchar(64) NULL, "comments_posted" integer NULL CHECK ("comments_posted" >= 0), "review_result" jsonb NULL, "organization_id" bigint NOT NULL, "repository_id" bigint NOT NULL);
ALTER TABLE "sentry_code_review_event" ADD CONSTRAINT "sentry_code_review_e_organization_id_1ce9fe63_fk_sentry_or" FOREIGN KEY ("organization_id") REFERENCES "sentry_organization" ("id") DEFERRABLE INITIALLY DEFERRED NOT VALID;
ALTER TABLE "sentry_code_review_event" VALIDATE CONSTRAINT "sentry_code_review_e_organization_id_1ce9fe63_fk_sentry_or";
ALTER TABLE "sentry_code_review_event" ADD CONSTRAINT "sentry_code_review_e_repository_id_a5be62a4_fk_sentry_re" FOREIGN KEY ("repository_id") REFERENCES "sentry_repository" ("id") DEFERRABLE INITIALLY DEFERRED NOT VALID;
ALTER TABLE "sentry_code_review_event" VALIDATE CONSTRAINT "sentry_code_review_e_repository_id_a5be62a4_fk_sentry_re";
CREATE UNIQUE INDEX CONCURRENTLY "unique_org_repo_trigger_id" ON "sentry_code_review_event" ("organization_id", "repository_id", "trigger_id") WHERE "trigger_id" IS NOT NULL;
CREATE INDEX CONCURRENTLY "sentry_code_review_event_organization_id_1ce9fe63" ON "sentry_code_review_event" ("organization_id");
CREATE INDEX CONCURRENTLY "sentry_code_review_event_repository_id_a5be62a4" ON "sentry_code_review_event" ("repository_id");
CREATE INDEX CONCURRENTLY "sentry_code_date_ad_a2451c_idx" ON "sentry_code_review_event" ("date_added");
CREATE INDEX CONCURRENTLY "sentry_code_organiz_4f4b09_idx" ON "sentry_code_review_event" ("organization_id", "trigger_at");
CREATE INDEX CONCURRENTLY "sentry_code_organiz_7ba32c_idx" ON "sentry_code_review_event" ("organization_id", "repository_id", "trigger_at");
CREATE INDEX CONCURRENTLY "sentry_code_organiz_76bbd1_idx" ON "sentry_code_review_event" ("organization_id", "repository_id", "pr_number");

Base automatically changed from ref/delete-code-review-event to master February 27, 2026 15:07
Comment on lines +65 to +69
status = models.CharField(
max_length=32,
choices=CodeReviewEventStatus.as_choices(),
default=CodeReviewEventStatus.WEBHOOK_RECEIVED,
)
Copy link
Member

Choose a reason for hiding this comment

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

I'm wondering if the status can just be derived by looking at the dates, but maybe it's easier to just keep it as a column

Copy link
Contributor Author

Choose a reason for hiding this comment

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

We can't really distinguish based on timestamp fields alone as these are stored for success or failure states both. Plus the stats endpoint (in subsequent PR) filters based on status directly.

@github-actions
Copy link
Contributor

github-actions bot commented Mar 2, 2026

This PR has a migration; here is the generated SQL for src/sentry/migrations/1038_create_code_review_event.py

for 1038_create_code_review_event in sentry

--
-- Create model CodeReviewEvent
--
CREATE TABLE "sentry_code_review_event" ("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, "pr_number" integer NULL, "pr_title" text NULL, "pr_author" text NULL, "pr_url" text NULL, "pr_state" varchar(16) NULL, "raw_event_type" varchar(64) NOT NULL, "raw_event_action" varchar(64) NOT NULL, "trigger_id" varchar(64) NULL, "trigger" varchar(64) NULL, "trigger_user" text NULL, "trigger_at" timestamp with time zone NOT NULL, "target_commit_sha" varchar(64) NULL, "status" varchar(32) NOT NULL, "denial_reason" text NULL, "webhook_received_at" timestamp with time zone NULL, "preflight_completed_at" timestamp with time zone NULL, "task_enqueued_at" timestamp with time zone NULL, "sent_to_seer_at" timestamp with time zone NULL, "review_started_at" timestamp with time zone NULL, "review_completed_at" timestamp with time zone NULL, "seer_run_id" varchar(64) NULL, "comments_posted" integer NULL CHECK ("comments_posted" >= 0), "review_result" jsonb NULL, "organization_id" bigint NOT NULL, "repository_id" bigint NOT NULL);
ALTER TABLE "sentry_code_review_event" ADD CONSTRAINT "sentry_code_review_e_organization_id_1ce9fe63_fk_sentry_or" FOREIGN KEY ("organization_id") REFERENCES "sentry_organization" ("id") DEFERRABLE INITIALLY DEFERRED NOT VALID;
ALTER TABLE "sentry_code_review_event" VALIDATE CONSTRAINT "sentry_code_review_e_organization_id_1ce9fe63_fk_sentry_or";
ALTER TABLE "sentry_code_review_event" ADD CONSTRAINT "sentry_code_review_e_repository_id_a5be62a4_fk_sentry_re" FOREIGN KEY ("repository_id") REFERENCES "sentry_repository" ("id") DEFERRABLE INITIALLY DEFERRED NOT VALID;
ALTER TABLE "sentry_code_review_event" VALIDATE CONSTRAINT "sentry_code_review_e_repository_id_a5be62a4_fk_sentry_re";
CREATE UNIQUE INDEX CONCURRENTLY "unique_org_repo_trigger_id" ON "sentry_code_review_event" ("organization_id", "repository_id", "trigger_id") WHERE "trigger_id" IS NOT NULL;
CREATE INDEX CONCURRENTLY "sentry_code_review_event_organization_id_1ce9fe63" ON "sentry_code_review_event" ("organization_id");
CREATE INDEX CONCURRENTLY "sentry_code_review_event_repository_id_a5be62a4" ON "sentry_code_review_event" ("repository_id");
CREATE INDEX CONCURRENTLY "sentry_code_date_ad_a2451c_idx" ON "sentry_code_review_event" ("date_added");
CREATE INDEX CONCURRENTLY "sentry_code_organiz_4f4b09_idx" ON "sentry_code_review_event" ("organization_id", "trigger_at");
CREATE INDEX CONCURRENTLY "sentry_code_organiz_7ba32c_idx" ON "sentry_code_review_event" ("organization_id", "repository_id", "trigger_at");
CREATE INDEX CONCURRENTLY "sentry_code_organiz_76bbd1_idx" ON "sentry_code_review_event" ("organization_id", "repository_id", "pr_number");

@github-actions
Copy link
Contributor

github-actions bot commented Mar 2, 2026

Backend Test Failures

Failures on ca389a1 in this run:

tests/sentry/backup/test_exhaustive.py::ExhaustiveTests::test_exhaustive_dirty_pkslog
tests/sentry/backup/test_exhaustive.py:38: in test_exhaustive_dirty_pks
    actual = self.import_export_then_validate(self._testMethodName, reset_pks=False)
src/sentry/testutils/helpers/backups.py:976: in import_export_then_validate
    return import_export_then_validate(out_name, reset_pks=reset_pks)
src/sentry/testutils/helpers/backups.py:348: in import_export_then_validate
    raise ValidationError(res)
E   sentry.testutils.helpers.backups.ValidationError: ComparatorFinding(
E       kind: UnequalJSON,
E       on: InstanceID(model: 'sentry.codereviewevent', ordinal: 1),
E       left_pk: 2,
E       right_pk: 1,
E       reason: 
E       --- 
E   
E       +++ 
E   
E       @@ -1,19 +1,19 @@
E   
E        {
E          "comments_posted": 2,
E       -  "date_added": "2026-03-02T21:16:37.497Z",
E       -  "date_updated": "2026-03-02T21:16:37.497Z",
E       +  "date_added": "2026-03-02T21:16:40.200Z",
E       +  "date_updated": "2026-03-02T21:16:40.200Z",
E          "denial_reason": null,
E          "pr_author": "test-author",
E          "pr_number": 1,
E          "pr_state": "open",
E          "pr_title": "Test PR for test-org",
E          "pr_url": "https://github.com/getsentry/getsentry/pull/1",
E          "preflight_completed_at": null,
E          "raw_event_action": "opened",
E          "raw_event_type": "pull_request",
E          "review_completed_at": null,
E          "review_result": null,
E          "review_started_at": null,
E          "seer_run_id": "seer-run-test-org",
E          "sent_to_seer_at": null,
E          "status": "review_completed",
E   )
tests/sentry/backup/test_exports.py::ScopingTests::test_organization_export_scopinglog
tests/sentry/backup/test_exports.py:263: in test_organization_export_scoping
    self.verify_model_inclusion(unencrypted, ExportScope.Organization)
tests/sentry/backup/test_exports.py:182: in verify_model_inclusion
    raise AssertionError(
E   AssertionError: The following models were not included in the export: ${<class 'sentry.models.code_review_event.CodeReviewEvent'>}; this is despite it being included in at least one of the following relocation scopes: {<RelocationScope.User: 2>, <RelocationScope.Organization: 3>}
tests/sentry/backup/test_imports.py::ScopingTests::test_organization_import_scopinglog
tests/sentry/backup/test_imports.py:845: in test_organization_import_scoping
    self.verify_model_inclusion(ImportScope.Organization)
tests/sentry/backup/test_imports.py:813: in verify_model_inclusion
    assert model.objects.count() > 0
E   AssertionError: assert 0 > 0
E    +  where 0 = <bound method QuerySet.count of <sentry.db.models.manager.base.BaseManager object at 0x7f82b19ad6a0>>()
E    +    where <bound method QuerySet.count of <sentry.db.models.manager.base.BaseManager object at 0x7f82b19ad6a0>> = <sentry.db.models.manager.base.BaseManager object at 0x7f82b19ad6a0>.count
E    +      where <sentry.db.models.manager.base.BaseManager object at 0x7f82b19ad6a0> = <class 'sentry.models.code_review_event.CodeReviewEvent'>.objects

@vaind vaind marked this pull request as ready for review March 2, 2026 22:07
@vaind vaind requested review from a team as code owners March 2, 2026 22:07
@vaind vaind marked this pull request as draft March 2, 2026 22:51
@vaind vaind force-pushed the feat/recreate-code-review-event branch from c509b64 to dccf3f6 Compare March 2, 2026 23:00
@github-actions
Copy link
Contributor

github-actions bot commented Mar 2, 2026

This PR has a migration; here is the generated SQL for src/sentry/migrations/1039_create_code_review_event.py

for 1039_create_code_review_event in sentry

--
-- Create model CodeReviewEvent
--
CREATE TABLE "sentry_code_review_event" ("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, "pr_number" integer NULL, "pr_title" text NULL, "pr_author" text NULL, "pr_url" text NULL, "pr_state" varchar(16) NULL, "raw_event_type" varchar(64) NOT NULL, "raw_event_action" varchar(64) NOT NULL, "trigger_id" varchar(64) NULL, "trigger" varchar(64) NULL, "trigger_user" text NULL, "trigger_at" timestamp with time zone NOT NULL, "target_commit_sha" varchar(64) NULL, "status" varchar(32) NOT NULL, "denial_reason" text NULL, "webhook_received_at" timestamp with time zone NULL, "preflight_completed_at" timestamp with time zone NULL, "task_enqueued_at" timestamp with time zone NULL, "sent_to_seer_at" timestamp with time zone NULL, "review_started_at" timestamp with time zone NULL, "review_completed_at" timestamp with time zone NULL, "seer_run_id" varchar(64) NULL, "comments_posted" integer NULL CHECK ("comments_posted" >= 0), "review_result" jsonb NULL, "organization_id" bigint NOT NULL, "repository_id" bigint NOT NULL);
ALTER TABLE "sentry_code_review_event" ADD CONSTRAINT "sentry_code_review_e_organization_id_1ce9fe63_fk_sentry_or" FOREIGN KEY ("organization_id") REFERENCES "sentry_organization" ("id") DEFERRABLE INITIALLY DEFERRED NOT VALID;
ALTER TABLE "sentry_code_review_event" VALIDATE CONSTRAINT "sentry_code_review_e_organization_id_1ce9fe63_fk_sentry_or";
ALTER TABLE "sentry_code_review_event" ADD CONSTRAINT "sentry_code_review_e_repository_id_a5be62a4_fk_sentry_re" FOREIGN KEY ("repository_id") REFERENCES "sentry_repository" ("id") DEFERRABLE INITIALLY DEFERRED NOT VALID;
ALTER TABLE "sentry_code_review_event" VALIDATE CONSTRAINT "sentry_code_review_e_repository_id_a5be62a4_fk_sentry_re";
CREATE UNIQUE INDEX CONCURRENTLY "unique_org_repo_trigger_id" ON "sentry_code_review_event" ("organization_id", "repository_id", "trigger_id") WHERE "trigger_id" IS NOT NULL;
CREATE INDEX CONCURRENTLY "sentry_code_review_event_organization_id_1ce9fe63" ON "sentry_code_review_event" ("organization_id");
CREATE INDEX CONCURRENTLY "sentry_code_review_event_repository_id_a5be62a4" ON "sentry_code_review_event" ("repository_id");
CREATE INDEX CONCURRENTLY "sentry_code_date_ad_a2451c_idx" ON "sentry_code_review_event" ("date_added");
CREATE INDEX CONCURRENTLY "sentry_code_organiz_4f4b09_idx" ON "sentry_code_review_event" ("organization_id", "trigger_at");
CREATE INDEX CONCURRENTLY "sentry_code_organiz_7ba32c_idx" ON "sentry_code_review_event" ("organization_id", "repository_id", "trigger_at");
CREATE INDEX CONCURRENTLY "sentry_code_organiz_76bbd1_idx" ON "sentry_code_review_event" ("organization_id", "repository_id", "pr_number");

@vaind vaind marked this pull request as ready for review March 3, 2026 00:20
denial_reason = models.TextField(null=True)

# Timestamps for pipeline stages
webhook_received_at = models.DateTimeField(null=True)
Copy link
Member

Choose a reason for hiding this comment

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

Is this the control timestamp? or when the region receives it?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

region, i'll add a note

# Seer callback data
seer_run_id = models.CharField(max_length=64, null=True)
comments_posted = BoundedPositiveIntegerField(null=True)
review_result = models.JSONField(null=True)
Copy link
Member

Choose a reason for hiding this comment

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

What will this represent?

Copy link
Contributor Author

@vaind vaind Mar 3, 2026

Choose a reason for hiding this comment

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

the structured fields (seer_run_id, comments_posted, timestamps) capture the key data, while review_result stores the raw/full Seer response payload as JSON for debugging and future use or for stuff where don't really need filtering/queries

adding a comment

)
constraints = [
models.UniqueConstraint(
fields=["organization", "repository", "trigger_id"],
Copy link
Member

Choose a reason for hiding this comment

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

What will happen when two rows have a trigger_id of null? (e.g. two events for the same PR come in before we have a trigger_id)

Copy link
Contributor Author

Choose a reason for hiding this comment

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

it's a partial unique index with condition=models.Q(trigger_id__isnull=False), meaning it only enforces uniqueness when trigger_id is non-null

models.Index(fields=("date_added",)),
models.Index(fields=("organization", "trigger_at")),
models.Index(fields=("organization", "repository", "trigger_at")),
models.Index(fields=("organization", "repository", "pr_number")),
Copy link
Member

Choose a reason for hiding this comment

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

We can always expand the indexes as we need to. We can start with none or a smaller subset and expand afterward.

I don't know how to measure the impact of including multiple indexes but it's preferred to add them as needed.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

all 4 indexes have concrete query patterns in PR #108533

  • (org, trigger_at) — stats endpoint date-range filters
  • (org, repo, trigger_at) — events list endpoint with repo + date filters
  • (org, repo, pr_number) — PR grouping/lookup in events endpoint
  • (date_added,) — cleanup task purging old rows

@github-actions
Copy link
Contributor

github-actions bot commented Mar 4, 2026

This PR has a migration; here is the generated SQL for src/sentry/migrations/1041_create_code_review_event.py

for 1041_create_code_review_event in sentry

--
-- Create model CodeReviewEvent
--
CREATE TABLE "sentry_code_review_event" ("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, "pr_number" integer NULL, "pr_title" text NULL, "pr_author" text NULL, "pr_url" text NULL, "pr_state" varchar(16) NULL, "raw_event_type" varchar(64) NOT NULL, "raw_event_action" varchar(64) NOT NULL, "trigger_id" varchar(64) NULL, "trigger" varchar(64) NULL, "trigger_user" text NULL, "trigger_at" timestamp with time zone NOT NULL, "target_commit_sha" varchar(64) NULL, "status" varchar(32) NOT NULL, "denial_reason" text NULL, "webhook_received_at" timestamp with time zone NULL, "preflight_completed_at" timestamp with time zone NULL, "task_enqueued_at" timestamp with time zone NULL, "sent_to_seer_at" timestamp with time zone NULL, "review_started_at" timestamp with time zone NULL, "review_completed_at" timestamp with time zone NULL, "seer_run_id" varchar(64) NULL, "comments_posted" integer NULL CHECK ("comments_posted" >= 0), "review_result" jsonb NULL, "organization_id" bigint NOT NULL, "repository_id" bigint NOT NULL);
ALTER TABLE "sentry_code_review_event" ADD CONSTRAINT "sentry_code_review_e_organization_id_1ce9fe63_fk_sentry_or" FOREIGN KEY ("organization_id") REFERENCES "sentry_organization" ("id") DEFERRABLE INITIALLY DEFERRED NOT VALID;
ALTER TABLE "sentry_code_review_event" VALIDATE CONSTRAINT "sentry_code_review_e_organization_id_1ce9fe63_fk_sentry_or";
ALTER TABLE "sentry_code_review_event" ADD CONSTRAINT "sentry_code_review_e_repository_id_a5be62a4_fk_sentry_re" FOREIGN KEY ("repository_id") REFERENCES "sentry_repository" ("id") DEFERRABLE INITIALLY DEFERRED NOT VALID;
ALTER TABLE "sentry_code_review_event" VALIDATE CONSTRAINT "sentry_code_review_e_repository_id_a5be62a4_fk_sentry_re";
CREATE UNIQUE INDEX CONCURRENTLY "unique_org_repo_trigger_id" ON "sentry_code_review_event" ("organization_id", "repository_id", "trigger_id") WHERE "trigger_id" IS NOT NULL;
CREATE INDEX CONCURRENTLY "sentry_code_review_event_organization_id_1ce9fe63" ON "sentry_code_review_event" ("organization_id");
CREATE INDEX CONCURRENTLY "sentry_code_review_event_repository_id_a5be62a4" ON "sentry_code_review_event" ("repository_id");
CREATE INDEX CONCURRENTLY "sentry_code_date_ad_a2451c_idx" ON "sentry_code_review_event" ("date_added");
CREATE INDEX CONCURRENTLY "sentry_code_organiz_4f4b09_idx" ON "sentry_code_review_event" ("organization_id", "trigger_at");
CREATE INDEX CONCURRENTLY "sentry_code_organiz_7ba32c_idx" ON "sentry_code_review_event" ("organization_id", "repository_id", "trigger_at");
CREATE INDEX CONCURRENTLY "sentry_code_organiz_76bbd1_idx" ON "sentry_code_review_event" ("organization_id", "repository_id", "pr_number");

vaind and others added 3 commits March 4, 2026 13:43
Recreate the CodeReviewEvent table with reviewer feedback applied:

- DefaultFieldsModel base class for date_added/date_updated
- FlexibleForeignKey for organization and repository (cascade deletion)
- RelocationScope.Organization (data preserved during org relocation)
- Backup test coverage in create_exhaustive_organization()

Migration is a clean auto-generated CreateModel — no manual edits.

Co-Authored-By: Claude <noreply@anthropic.com>
Two issues:
1. Change relocation scope from Organization to Global. The model has
   a FK to Repository which is Global scope — Organization-scoped
   exports don't include Repository PKs in the pk_map, so
   CodeReviewEvent instances were filtered out during export.

2. Register DateUpdatedComparator for date_added/date_updated fields.
   DefaultFieldsModel's auto_now/auto_now_add fields get new timestamps
   on import, so the comparator must allow timestamp differences.

Co-Authored-By: Claude <noreply@anthropic.com>
@vaind vaind force-pushed the feat/recreate-code-review-event branch from daf3e1d to 25f9603 Compare March 4, 2026 21:43
Comment on lines +106 to +116
fields=["organization", "repository", "pr_number"],
name="sentry_code_organiz_76bbd1_idx",
),
],
"constraints": [
models.UniqueConstraint(
condition=models.Q(("trigger_id__isnull", False)),
fields=("organization", "repository", "trigger_id"),
name="unique_org_repo_trigger_id",
)
],
Copy link
Contributor

Choose a reason for hiding this comment

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

Bug: Migration 1042_create_code_review_event is missing a dependency on 1035_delete_code_review_event, which can cause migration failures due to pre-existing table indexes and constraints.
Severity: CRITICAL

Suggested Fix

Update the dependencies in 1042_create_code_review_event.py to depend on ("sentry", "1035_delete_code_review_event") instead of ("sentry", "1041_projectkeymapping"). This ensures the old table is dropped before the new one is created.

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/migrations/1042_create_code_review_event.py#L96-L116

Potential issue: The migration `1042_create_code_review_event` recreates the
`CodeReviewEvent` table, which was deleted by a previous migration,
`1035_delete_code_review_event`. However, migration `1042` incorrectly depends on
`1041_projectkeymapping` instead of `1035`. This lack of an explicit dependency means
the Django migration framework doesn't guarantee the deletion migration runs before the
creation migration. If the ordering is incorrect during deployment, the migration will
fail when attempting to create a table and indexes with names that already exist,
blocking the deployment process.

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

@github-actions
Copy link
Contributor

github-actions bot commented Mar 4, 2026

This PR has a migration; here is the generated SQL for src/sentry/migrations/1042_create_code_review_event.py

for 1042_create_code_review_event in sentry

--
-- Create model CodeReviewEvent
--
CREATE TABLE "sentry_code_review_event" ("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, "pr_number" integer NULL, "pr_title" text NULL, "pr_author" text NULL, "pr_url" text NULL, "pr_state" varchar(16) NULL, "raw_event_type" varchar(64) NOT NULL, "raw_event_action" varchar(64) NOT NULL, "trigger_id" varchar(64) NULL, "trigger" varchar(64) NULL, "trigger_user" text NULL, "trigger_at" timestamp with time zone NOT NULL, "target_commit_sha" varchar(64) NULL, "status" varchar(32) NOT NULL, "denial_reason" text NULL, "webhook_received_at" timestamp with time zone NULL, "preflight_completed_at" timestamp with time zone NULL, "task_enqueued_at" timestamp with time zone NULL, "sent_to_seer_at" timestamp with time zone NULL, "review_started_at" timestamp with time zone NULL, "review_completed_at" timestamp with time zone NULL, "seer_run_id" varchar(64) NULL, "comments_posted" integer NULL CHECK ("comments_posted" >= 0), "review_result" jsonb NULL, "organization_id" bigint NOT NULL, "repository_id" bigint NOT NULL);
ALTER TABLE "sentry_code_review_event" ADD CONSTRAINT "sentry_code_review_e_organization_id_1ce9fe63_fk_sentry_or" FOREIGN KEY ("organization_id") REFERENCES "sentry_organization" ("id") DEFERRABLE INITIALLY DEFERRED NOT VALID;
ALTER TABLE "sentry_code_review_event" VALIDATE CONSTRAINT "sentry_code_review_e_organization_id_1ce9fe63_fk_sentry_or";
ALTER TABLE "sentry_code_review_event" ADD CONSTRAINT "sentry_code_review_e_repository_id_a5be62a4_fk_sentry_re" FOREIGN KEY ("repository_id") REFERENCES "sentry_repository" ("id") DEFERRABLE INITIALLY DEFERRED NOT VALID;
ALTER TABLE "sentry_code_review_event" VALIDATE CONSTRAINT "sentry_code_review_e_repository_id_a5be62a4_fk_sentry_re";
CREATE UNIQUE INDEX CONCURRENTLY "unique_org_repo_trigger_id" ON "sentry_code_review_event" ("organization_id", "repository_id", "trigger_id") WHERE "trigger_id" IS NOT NULL;
CREATE INDEX CONCURRENTLY "sentry_code_review_event_organization_id_1ce9fe63" ON "sentry_code_review_event" ("organization_id");
CREATE INDEX CONCURRENTLY "sentry_code_review_event_repository_id_a5be62a4" ON "sentry_code_review_event" ("repository_id");
CREATE INDEX CONCURRENTLY "sentry_code_date_ad_a2451c_idx" ON "sentry_code_review_event" ("date_added");
CREATE INDEX CONCURRENTLY "sentry_code_organiz_4f4b09_idx" ON "sentry_code_review_event" ("organization_id", "trigger_at");
CREATE INDEX CONCURRENTLY "sentry_code_organiz_7ba32c_idx" ON "sentry_code_review_event" ("organization_id", "repository_id", "trigger_at");
CREATE INDEX CONCURRENTLY "sentry_code_organiz_76bbd1_idx" ON "sentry_code_review_event" ("organization_id", "repository_id", "pr_number");

@wedamija
Copy link
Member

wedamija commented Mar 4, 2026

Btw, you can ignore the migrations-drift ci job

@vaind vaind merged commit fa972ad into master Mar 4, 2026
76 of 77 checks passed
@vaind vaind deleted the feat/recreate-code-review-event branch March 4, 2026 22:17
JonasBa pushed a commit that referenced this pull request Mar 5, 2026
…9424)

Recreate the `CodeReviewEvent` table with reviewer feedback from PR
#108531 applied:

- `DefaultFieldsModel` base class for standard
`date_added`/`date_updated` fields
- `FlexibleForeignKey` for organization and repository (cascade
deletion, referential integrity)
- `RelocationScope.Global` to match `Repository`'s scope (required for
org-scoped exports since Repository is Global)
- `DateUpdatedComparator` for `date_added`/`date_updated` in backup
comparators
- Backup test coverage in `create_exhaustive_organization()`

Migration is a clean auto-generated `CreateModel` — no manual edits.

**This is PR 3 of 3:**
1. #109420 — `SafeDeleteModel` with `MOVE_TO_PENDING` (merged)
2. #109422 — `SafeDeleteModel` with `DELETE` (merged)
3. **This PR** — Recreate with updated schema

---------

Co-authored-by: Claude <noreply@anthropic.com>
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.

3 participants