Draft: Add NOT NULL constraints on the placeholder-table columns

What does this MR do and why?

Stacked on !254114 (finalize) → !254095 (sync index) → !253935 (merged) (base). Adds the NOT NULL constraint on the two columns whose backfill BBMs !254114 finalizes:

  • import_source_user_placeholder_references.expires_at
  • import_placeholder_memberships.retention_expires_at

Per not_null_constraints.md's large-table process, the constraint is added after the backfill BBM is finalized, same milestone as the finalize migration in the doc's own worked example (both 16.10) - here that's 19.6.

Validates immediately (add_not_null_constraint's default), not validate: false. That option is explicitly labeled "Optional. For very large tables" in the doc, with its own worked example being merge_request_diffs (over_limit tier, >100GB). These two tables are medium/small tier, and by the time this migration runs the backfill is already confirmed complete via finalization — there's nothing left to violate the constraint, and VALIDATE CONSTRAINT's scan cost is comparable to a plain table scan (~110s measured earlier for the 52M-row table), well inside the post-deploy budget. No separate async-validation follow-up needed — this replaces what was previously planned as a 19.7 task.

Each migration declares DEPENDENT_BATCHED_BACKGROUND_MIGRATIONS referencing its BBM's queued_migration_version, so the Migration::UnfinishedDependencies rubocop cop verifies finalized_by is actually set in the dictionary before this can merge — i.e., it enforces the same ordering this MR's stacking already implies.

Do not merge until !254114 has actually merged and its finalized_by values are live (the cop should catch this anyway, but flagging explicitly).

Full context: #576118

References

Database review data

Both constraints created and validated on a local dev cluster via db:migrate:up (bypassing the still-blocked finalize migrations ahead of them in timestamp order, which correctly can't run until required stop 19.5 passes). Confirmed in the migration log that add_not_null_constraint runs ADD CONSTRAINT ... NOT VALID followed immediately by VALIDATE CONSTRAINT, both in the same migration:

ALTER TABLE import_source_user_placeholder_references
ADD CONSTRAINT check_f6632cc492
CHECK ( expires_at IS NOT NULL )
NOT VALID;
--   -> 0.0015s
ALTER TABLE import_source_user_placeholder_references VALIDATE CONSTRAINT check_f6632cc492;
--   -> 0.0012s
ALTER TABLE import_placeholder_memberships
ADD CONSTRAINT check_cb5199bf23
CHECK ( retention_expires_at IS NOT NULL )
NOT VALID;
--   -> 0.0016s
ALTER TABLE import_placeholder_memberships VALIDATE CONSTRAINT check_cb5199bf23;
--   -> 0.0013s

Trivial locally (tiny local table); at real scale the VALIDATE CONSTRAINT step's cost is bounded by a table scan, not row content — for import_source_user_placeholder_references (~52M rows) that's on the order of the ~110s full-table EXPLAIN measured earlier in !253935 (merged), comfortably inside the post-deploy time budget. No new queries introduced.

Screenshots or screen recordings

Not applicable — database schema change, no UI changes.

How to set up and validate locally

  1. Merge/rebase on top of the latest georgekoltsov/not-null-constraint-placeholder-references base chain.
  2. bundle exec rails db:migrate (regenerates db/structure.sql) — or, if the finalize migrations ahead of these are still blocked by the required-stop check in your environment, test these two in isolation with bundle exec rake db:migrate:up VERSION=<timestamp>.
  3. Run the specs:
    bundle exec rspec spec/migrations/20260908140000_add_not_null_constraint_to_import_source_user_placeholder_references_expires_at_spec.rb
    bundle exec rspec spec/migrations/20260908140100_add_not_null_constraint_to_import_placeholder_memberships_retention_expires_at_spec.rb

MR acceptance checklist

Evaluate this MR against the MR acceptance checklist. It helps you analyze changes to reduce risks in quality, performance, reliability, security, and maintainability.

Edited by George Koltsov

Merge request reports

Loading
Loading