Adding an index to a partitioned table and its children in PostgreSQL and Rails
Published: March 30, 2022
Postgres 10+ made partitioning tables much easier, however adding an index to a table with many partitions and data in them already is not straightforward if you want to avoid long table locking.
With partition tables in PostgreSQL, you can add an index to the parent table and it will cascade to all child tables. However, this operation will lock all child tables until the index is applied. While it is not possible to concurrently add an index to the parent table and have it propagate, you can add an index to the parent table only, apply the index concurrently to each of the child tables, and then attach the index to the parent.
Below is an example migration for Rails 7 using PostgreSQL 13 to do just that:
class AddSomeIndexToAParentTable < ActiveRecord::Migration[7.0] PARENT_TABLE_NAME = "the_parent_partition_table" INDEX_COLUMN_NAME = "column_to_index" PARENT_INDEX_NAME = "index_#{PARENT_TABLE_NAME}_on_#{INDEX_COLUMN_NAME}" disable_ddl_transaction! def up # Create base index on parent table for future tables to get execute "CREATE INDEX #{PARENT_INDEX_NAME} ON ONLY #{PARENT_TABLE_NAME} (#{INDEX_COLUMN_NAME})" child_tables = execute(<<~SQL).pluck("child") SELECT inhrelid::regclass AS child FROM pg_catalog.pg_inherits WHERE inhparent = '#{PARENT_TABLE_NAME}'::regclass SQL child_tables.each do |child_table| # Index name matched up with existing child table index names PostgreSQL automatically generates child_index_name = "#{child_table}_#{INDEX_COLUMN_NAME}_idx" # Add index to child table execute "CREATE INDEX CONCURRENTLY #{child_index_name} ON #{child_table} (#{INDEX_COLUMN_NAME})" # "Attach" child index to parent index so it can be dropped if the parent is dropped execute "ALTER INDEX #{PARENT_INDEX_NAME} ATTACH PARTITION #{child_index_name}" end end def down # If we're running the "down" on this, I hope it's only locally execute "DROP INDEX #{PARENT_INDEX_NAME}" end end
PostgreSQL Partitioning Docs (for version 13): https://www.postgresql.org/docs/13/ddl-partitioning.html