Skip to content

Commit

Permalink
refactor(fixtures): Avoid disabling referential integrity (#362)
Browse files Browse the repository at this point in the history
Avoid 87% of the `#disable_referential_integrity` calls made
in fixture setup by just trying first without disabling and
just disabling in case of failure. Also batch foreign keys
removals and addition (for clarity only).
  • Loading branch information
BuonOmo authored Jan 27, 2025
1 parent c02cf48 commit 56a37d8
Show file tree
Hide file tree
Showing 2 changed files with 18 additions and 6 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -24,8 +24,14 @@ def insert_fixtures_set(fixture_set, tables_to_delete = [])
table_deletes = tables_to_delete.map { |table| "DELETE FROM #{quote_table_name(table)}" }
statements = table_deletes + fixture_inserts

disable_referential_integrity do
execute_batch(statements, "Fixtures Load")
begin # much faster without disabling referential integrity, worth trying.
transaction(requires_new: true) do
execute_batch(statements, "Fixtures Load")
end
rescue
disable_referential_integrity do
execute_batch(statements, "Fixtures Load")
end
end
end
end
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,15 +36,16 @@ def check_all_foreign_keys_valid!
def disable_referential_integrity
foreign_keys = all_foreign_keys

foreign_keys.each do |foreign_key|
statements = foreign_keys.map do |foreign_key|
# We do not use the `#remove_foreign_key` method here because it
# checks for foreign keys existance in the schema cache. This method
# is performance critical and we know the foreign key exist.
at = create_alter_table foreign_key.from_table
at.drop_foreign_key foreign_key.name

execute schema_creation.accept(at)
schema_creation.accept(at)
end
execute_batch(statements, "Disable referential integrity -> remove foreign keys")

yield

Expand All @@ -66,11 +67,16 @@ def disable_referential_integrity
# for every key. This method is performance critical for the test suite, hence
# we use the `#all_foreign_keys` method that only make one query to the database.
already_inserted_foreign_keys = all_foreign_keys
foreign_keys.each do |foreign_key|
statements = foreign_keys.map do |foreign_key|
next if already_inserted_foreign_keys.any? { |fk| fk.from_table == foreign_key.from_table && fk.options[:name] == foreign_key.options[:name] }

add_foreign_key(foreign_key.from_table, foreign_key.to_table, **foreign_key.options)
options = foreign_key_options(foreign_key.from_table, foreign_key.to_table, foreign_key.options)
at = create_alter_table foreign_key.from_table
at.add_foreign_key foreign_key.to_table, options

schema_creation.accept(at)
end
execute_batch(statements.compact, "Disable referential integrity -> add foreign keys")
ensure
ActiveRecord::Base.table_name_prefix = old_prefix
ActiveRecord::Base.table_name_suffix = old_suffix
Expand Down

0 comments on commit 56a37d8

Please sign in to comment.