Dump/Load FK data for partitioned tables
What does this MR do?
This MR is the third step of implementing a new set of migration helpers, which will handle management of foreign key-like behavior for partitioned tables, which do not support some types of foreign keys natively in PG11. This issue implements the process of dumping and loading the foreign key data from the table in the database, so normal Rails operations like db:structure:dump
and db:structure:load
will work seamlessly with the FK data as it would with builtin PG constraints.
The foreign key data existing in the partitioned_foreign_keys
table will be dumped (when migrations are run, or on a db:structure:dump
) to a new file similar to structure.sql
. When executing a db:structure:load
, the data from the file will be reloaded into the table after the standard rails logic executes.
Related issue: #201872 (closed)
MR for part 1: !29064 (merged)
MR for part 2: !29510 (merged)
Overview
Postgres 11 does not support foreign keys that reference partitioned tables, so a similar solution needs to be provided by other means. This MR implements part of the features that are given by foreign keys, which is automatic cleanup when a record pointed to be a foreign key is deleted. The most common action taken in that scenario is a cascading delete, but also it's not unusual to set the nullify the foreign key.
A new set of migration helpers are added, which create a function in the database that performs the cleanup operation, and an AFTER DELETE
trigger that will call the function. The foreign key definitions are tracked in a new table, so that when a key is added or removed by a migration, the database has the information to rebuild the cleanup function with the correct operations.
A sample migration might look something like:
class AddIssueToProjectForeignKey < ActiveRecord::Migration[6.0]
include Gitlab::Database::PartitioningMigrationHelpers
DOWNTIME = false
def up
add_foreign_key :issues, :projects
end
def down
remove_foreign_key :issues, :projects
end
end
Then, when the migration is run, it will do the following:
- Read the current foreign keys that reference projects from the
partitioned_foreign_keys
table - Verify that the added foreign key is valid and doesn't already exist
- Create or replace the database function
- Re-create a trigger on
projects
to call the function
An example function for the above migration would look like:
CREATE OR REPLACE FUNCTION fk_cascade_function_b4db0d065d()
RETURNS TRIGGER AS
$$
BEGIN
DELETE FROM issues WHERE project_id = OLD.id;
RETURN OLD;
END
$$ LANGUAGE PLPGSQL
While the created trigger would look like:
CREATE TRIGGER fk_cascade_trigger_e59525315e
AFTER DELETE ON projects
FOR EACH ROW
EXECUTE PROCEDURE fk_cascade_function_b4db0d065d()
Limitations
- At some point in the future we would likely add support for multi-column foreign keys, since we would want to be able to include the partitioning key in the foreign key.
- Currently only two options for
ON DELETE
are allowed:CASCADE
andSET NULL
. It was mentioned here: !29525 (comment 325426412) about needing support for other options, which is a valid point. Given that this is already a sizable change of several MR in total, I think it makes sense to try what we have now in a real scenario, and add in those additional options later once we have tested the approach.
Does this MR meet the acceptance criteria?
Conformity
-
Changelog entry -
Documentation (if required) -
Code review guidelines -
Merge request performance guidelines -
Style guides -
Database guides -
Separation of EE specific content
Availability and Testing
-
Review and add/update tests for this feature/bug. Consider all test levels. See the Test Planning Process. -
Informed Infrastructure department of a default or new setting change, if applicable per definition of done