Remove obsolete push_rules organizations loose foreign key

What does this MR do and why?

Removes the loose foreign key (LFK) definition push_rules -> organizations (column: organization_id, on_delete: async_delete) from config/gitlab_loose_foreign_keys.yml, and adds push_rules: %w[organization_id] to the ignored_fk_columns_map in spec/db/schema_spec.rb.

The spec addition is required because spec/db/schema_spec.rb requires every *_id column to be covered by a real FK, a loose FK, or an ignore-map entry. push_rules.organization_id has no real Postgres FK, and this LFK entry was its only coverage.

No database migrations are needed; this is a config + spec change only.

Why this is safe

The LFK is dead config:

  • Organization-level push rules were migrated to the dedicated organization_push_rules table, which has a real Postgres foreign key on organization_id with ON DELETE CASCADE, so organization deletions are handled at the database level.
  • New organization-level rows in push_rules are impossible: PushRule has an application-level validates :project_id, presence: true (added in !246324 (merged)), and on GitLab.com the NOT NULL constraint on push_rules.project_id is validated.
  • No runtime code reads push_rules.organization_id (the read_organization_push_rules feature flag paths were removed in !219558 (merged)).
  • The organizations deletion-tracking trigger is not removed: many other LFK definitions still reference organizations as parent, so per the loose foreign keys development docs the trigger must stay.

Self-managed instances

Removing this LFK is safe on self-managed even though the NOT NULL constraint on push_rules.project_id is not yet validated and DeleteNullProjectIdPushRules is not finalized:

  • No new organization-level rows can be created anywhere: migration 20260715040848 (19.3, unguarded) added check_1d23f0a102 CHECK (project_id IS NOT NULL) NOT VALID, and PostgreSQL enforces NOT VALID constraints on all new INSERTs/UPDATEs — only existing rows are unchecked. PushRule also has validates :project_id, presence: true.
  • Legacy rows with NULL project_id may still exist until the requeued DeleteNullProjectIdPushRules BBM is finalized (#607954, 19.6). If an organization is deleted before then, its legacy rows become orphans instead of being async-deleted — benign, since no code path reads push_rules by organization_id.
  • The BBM's scope is relation.where(project_id: nil), so it deletes those orphans whether or not the organization still exists.
  • Sequencing: finalization (#607954) runs before the self-managed constraint validation (#607955), so orphaned rows can never cause the NOT NULL validation to fail.

Verification

  • bundle exec rspec spec/lib/gitlab/database/loose_foreign_keys_spec.rb — passed (17 examples, 0 failures, 1 pending)
  • bundle exec rspec spec/db/schema_spec.rb — passed except one pre-existing quarantined failure unrelated to this change (the "jsonb columns uses json schema validator" example, quarantined via https://gitlab.com/gitlab-org/quality/test-failure-issues/-/issues/9460, failing about jsonb schema validators on ~97 models)

Out of scope

Dropping the push_rules.organization_id and is_sample columns themselves is tracked in #623433, blocked by #607954 and #607955.

Closes #606845 (closed)

Edited by Emma Park

Merge request reports

Loading
Loading