Change unique constraints to prepare ci_job_artifact_states for partitioning
What does this MR do and why?
Adds partition_id to the primary key, making it a composite key, to prepare ci_job_artifact_states for partitioning.
References
Related to #525449 (closed)
This is step 3 and 5 of this guide https://docs.gitlab.com/development/database/partitioning/list/#step-3---enforce-unique-constraint. Step 4 is already covered so it should be ok to merge both steps in this MR.
Screenshots or screen recordings
Running the migration
gitlab|525449-change-unique-constraints-ci_job_artifact_states⚡ ⇒ bin/rails db:migrate:main VERSION=20250428232147 RAILS_ENV=development
DEPRECATION WARNING: Support for `config.active_support.cache_format_version = 6.1` has been deprecated and will be removed in Rails 7.2.
Check the Rails upgrade guide at https://guides.rubyonrails.org/upgrading_ruby_on_rails.html#new-activesupport-cache-serialization-format
for more information on how to upgrade.
(called from initialize at /Users/natanaelsilva/dev/gdk/gitlab/lib/gitlab/patch/redis_cache_store.rb:16)
main: == [advisory_lock_connection] object_id: 129520, pg_backend_pid: 12080
main: == 20250428232147 ChangeUniqueContraintsCiJobArtifactStates: migrating ========
main: -- indexes(:ci_job_artifact_states)
main: -> 0.0191s
main: -- transaction_open?(nil)
main: -> 0.0000s
main: -- view_exists?(:postgres_partitions)
main: -> 0.0003s
main: -- indexes(:ci_job_artifact_states)
main: -> 0.0015s
main: -- execute("SET statement_timeout TO 0")
main: -> 0.0003s
main: -- remove_index(:ci_job_artifact_states, {:algorithm=>:concurrently, :name=>:index_ci_job_artifact_states_on_job_artifact_id_partition_id})
main: -> 0.0013s
main: -- execute("RESET statement_timeout")
main: -> 0.0003s
main: -- transaction_open?(nil)
main: -> 0.0000s
main: -- view_exists?(:postgres_partitions)
main: -> 0.0003s
main: -- index_exists?(:ci_job_artifact_states, [:job_artifact_id, :partition_id], {:unique=>true, :name=>:index_ci_job_artifact_states_on_job_artifact_id_partition_id, :algorithm=>:concurrently})
main: -> 0.0014s
main: -- add_index(:ci_job_artifact_states, [:job_artifact_id, :partition_id], {:unique=>true, :name=>:index_ci_job_artifact_states_on_job_artifact_id_partition_id, :algorithm=>:concurrently})
main: -> 0.0016s
main: -- transaction_open?(nil)
main: -> 0.0000s
main: -- quote_table_name(:ci_job_artifact_states)
main: -> 0.0000s
main: -- quote_column_name(:ci_job_artifact_states_pkey)
main: -> 0.0000s
main: -- execute("ALTER TABLE \"ci_job_artifact_states\" DROP CONSTRAINT \"ci_job_artifact_states_pkey\" CASCADE\n")
main: -> 0.0021s
main: -- quote_table_name(:ci_job_artifact_states)
main: -> 0.0000s
main: -- quote_table_name(:ci_job_artifact_states_pkey)
main: -> 0.0000s
main: -- quote_table_name(:index_ci_job_artifact_states_on_job_artifact_id_partition_id)
main: -> 0.0000s
main: -- execute("ALTER TABLE \"ci_job_artifact_states\" ADD CONSTRAINT \"ci_job_artifact_states_pkey\" PRIMARY KEY USING INDEX \"index_ci_job_artifact_states_on_job_artifact_id_partition_id\"\n")
main: -> 0.0005s
main: == 20250428232147 ChangeUniqueContraintsCiJobArtifactStates: migrated (0.0689s)
main: == [advisory_lock_connection] object_id: 129520, pg_backend_pid: 12080
Table state:
gitlabhq_development=# \d ci_job_artifact_states
Table "public.ci_job_artifact_states"
Column | Type | Collation | Nullable | Default
--------------------------+--------------------------+-----------+----------+---------
verification_started_at | timestamp with time zone | | |
verification_retry_at | timestamp with time zone | | |
verified_at | timestamp with time zone | | |
job_artifact_id | bigint | | not null |
verification_state | smallint | | not null | 0
verification_retry_count | smallint | | |
verification_checksum | bytea | | |
verification_failure | text | | |
partition_id | bigint | | not null |
project_id | bigint | | |
Indexes:
"ci_job_artifact_states_pkey" PRIMARY KEY, btree (job_artifact_id, partition_id)
"index_job_artifact_states_failed_verification" btree (verification_retry_at NULLS FIRST) WHERE verification_state = 3
"index_job_artifact_states_needs_verification" btree (verification_state) WHERE verification_state = 0 OR verification_state = 3
"index_job_artifact_states_on_verification_state" btree (verification_state)
"index_job_artifact_states_pending_verification" btree (verified_at NULLS FIRST) WHERE verification_state = 0
"index_on_job_artifact_id_partition_id_verification_state" btree (verification_state, job_artifact_id, partition_id)
Check constraints:
"check_df832b66ea" CHECK (char_length(verification_failure) <= 255)
Foreign-key constraints:
"fk_rails_80a9cba3b2_p" FOREIGN KEY (partition_id, job_artifact_id) REFERENCES p_ci_job_artifacts(partition_id, id) ON UPDATE CASCADE ON DELETE CASCADE
Triggers:
gitlab_schema_write_trigger_for_ci_job_artifact_states BEFORE INSERT OR DELETE OR UPDATE OR TRUNCATE ON ci_job_artifact_states FOR EACH STATEMENT EXECUTE FUNCTION gitlab_schema_prevent_write()
trigger_a465de38164e BEFORE INSERT OR UPDATE ON ci_job_artifact_states FOR EACH ROW EXECUTE FUNCTION trigger_a465de38164e()
Reverting changes:
gitlab|525449-change-unique-constraints-ci_job_artifact_states⚡ ⇒ bin/rails db:migrate:down:main VERSION=20250428232147 RAILS_ENV=development
DEPRECATION WARNING: Support for `config.active_support.cache_format_version = 6.1` has been deprecated and will be removed in Rails 7.2.
Check the Rails upgrade guide at https://guides.rubyonrails.org/upgrading_ruby_on_rails.html#new-activesupport-cache-serialization-format
for more information on how to upgrade.
(called from initialize at /Users/natanaelsilva/dev/gdk/gitlab/lib/gitlab/patch/redis_cache_store.rb:16)
main: == [advisory_lock_connection] object_id: 129280, pg_backend_pid: 12008
main: == 20250428232147 ChangeUniqueContraintsCiJobArtifactStates: reverting ========
main: -- transaction_open?(nil)
main: -> 0.0000s
main: -- view_exists?(:postgres_partitions)
main: -> 0.0182s
main: -- index_exists?(:ci_job_artifact_states, :job_artifact_id, {:unique=>true, :name=>:temp_ci_job_artifact_states_job_artifact_id_idx, :algorithm=>:concurrently})
main: -> 0.0021s
main: -- execute("SET statement_timeout TO 0")
main: -> 0.0003s
main: -- add_index(:ci_job_artifact_states, :job_artifact_id, {:unique=>true, :name=>:temp_ci_job_artifact_states_job_artifact_id_idx, :algorithm=>:concurrently})
main: -> 0.0021s
main: -- execute("RESET statement_timeout")
main: -> 0.0003s
main: -- transaction_open?(nil)
main: -> 0.0000s
main: -- quote_table_name(:ci_job_artifact_states)
main: -> 0.0000s
main: -- quote_column_name(:ci_job_artifact_states_pkey)
main: -> 0.0000s
main: -- execute("ALTER TABLE \"ci_job_artifact_states\" DROP CONSTRAINT \"ci_job_artifact_states_pkey\" CASCADE\n")
main: -> 0.0024s
main: -- quote_table_name(:ci_job_artifact_states)
main: -> 0.0000s
main: -- quote_table_name(:ci_job_artifact_states_pkey)
main: -> 0.0000s
main: -- quote_table_name(:temp_ci_job_artifact_states_job_artifact_id_idx)
main: -> 0.0000s
main: -- execute("ALTER TABLE \"ci_job_artifact_states\" ADD CONSTRAINT \"ci_job_artifact_states_pkey\" PRIMARY KEY USING INDEX \"temp_ci_job_artifact_states_job_artifact_id_idx\"\n")
main: -> 0.0005s
main: -- indexes(:ci_job_artifact_states)
main: -> 0.0015s
main: -- current_schema(nil)
main: -> 0.0001s
main: -- transaction_open?(nil)
main: -> 0.0000s
main: -- view_exists?(:postgres_partitions)
main: -> 0.0002s
main: -- index_exists?(:ci_job_artifact_states, [:job_artifact_id, :partition_id], {:unique=>false, :name=>:index_ci_job_artifact_states_on_job_artifact_id_partition_id, :algorithm=>:concurrently})
main: -> 0.0012s
main: -- add_index(:ci_job_artifact_states, [:job_artifact_id, :partition_id], {:unique=>false, :name=>:index_ci_job_artifact_states_on_job_artifact_id_partition_id, :algorithm=>:concurrently})
main: -> 0.0012s
main: == 20250428232147 ChangeUniqueContraintsCiJobArtifactStates: reverted (0.0601s)
main: == [advisory_lock_connection] object_id: 129280, pg_backend_pid: 12008
Table restored:
gitlabhq_development=# \d ci_job_artifact_states
Table "public.ci_job_artifact_states"
Column | Type | Collation | Nullable | Default
--------------------------+--------------------------+-----------+----------+---------
verification_started_at | timestamp with time zone | | |
verification_retry_at | timestamp with time zone | | |
verified_at | timestamp with time zone | | |
job_artifact_id | bigint | | not null |
verification_state | smallint | | not null | 0
verification_retry_count | smallint | | |
verification_checksum | bytea | | |
verification_failure | text | | |
partition_id | bigint | | not null |
project_id | bigint | | |
Indexes:
"ci_job_artifact_states_pkey" PRIMARY KEY, btree (job_artifact_id)
"index_ci_job_artifact_states_on_job_artifact_id_partition_id" btree (job_artifact_id, partition_id)
"index_job_artifact_states_failed_verification" btree (verification_retry_at NULLS FIRST) WHERE verification_state = 3
"index_job_artifact_states_needs_verification" btree (verification_state) WHERE verification_state = 0 OR verification_state = 3
"index_job_artifact_states_on_verification_state" btree (verification_state)
"index_job_artifact_states_pending_verification" btree (verified_at NULLS FIRST) WHERE verification_state = 0
"index_on_job_artifact_id_partition_id_verification_state" btree (verification_state, job_artifact_id, partition_id)
Check constraints:
"check_df832b66ea" CHECK (char_length(verification_failure) <= 255)
Foreign-key constraints:
"fk_rails_80a9cba3b2_p" FOREIGN KEY (partition_id, job_artifact_id) REFERENCES p_ci_job_artifacts(partition_id, id) ON UPDATE CASCADE ON DELETE CASCADE
Triggers:
gitlab_schema_write_trigger_for_ci_job_artifact_states BEFORE INSERT OR DELETE OR UPDATE OR TRUNCATE ON ci_job_artifact_states FOR EACH STATEMENT EXECUTE FUNCTION gitlab_schema_prevent_write()
trigger_a465de38164e BEFORE INSERT OR UPDATE ON ci_job_artifact_states FOR EACH ROW EXECUTE FUNCTION trigger_a465de38164e()
How to set up and validate locally
The primary key should be (job_artifact_id, partition_id), no longer just job_artifact_id. Just check on with \d ci_job_artifact_states .
Edited by Natanael Silva