-
Notifications
You must be signed in to change notification settings - Fork 362
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Tasks in a non-terminal state have to be deleted individually due to an 'after_destroy' hook that creates an app usage event. The other tasks are bulk-deleted. - Builds in state 'STAGING' have to be canceled; then all builds can be bulk-deleted. - All deployments are bulk-deleted. - All revisions are bulk-deleted. - All sidecars are bulk-deleted. - Bulk deletion is achieved by adding DELETE CASCADE to multiple foreign keys.
- Loading branch information
1 parent
c511c4c
commit 80d2436
Showing
21 changed files
with
388 additions
and
220 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,26 @@ | ||
ForeignKey = Struct.new(:table, :name, :column, :referenced_table, :referenced_column, :new_constraint) do | ||
def initialize(table, name, column, referenced_table, referenced_column, new_constraint: false) | ||
super(table, name, column, referenced_table, referenced_column, new_constraint) | ||
end | ||
end | ||
|
||
def foreign_key_exists?(db, table, name) | ||
db.foreign_key_list(table).detect { |fk| fk[:name] == name }.present? | ||
end | ||
|
||
def recreate_foreign_key_with_delete_cascade(db, fk) | ||
# Remove orphaned entries. | ||
db[fk.table].exclude(fk.column => db[fk.referenced_table].select(fk.referenced_column)).delete if fk.new_constraint | ||
|
||
alter_table fk.table do | ||
drop_constraint fk.name, type: :foreign_key if foreign_key_exists?(db, fk.table, fk.name) | ||
add_foreign_key [fk.column], fk.referenced_table, key: fk.referenced_column, name: fk.name, on_delete: :cascade | ||
end | ||
end | ||
|
||
def recreate_foreign_key_without_delete_cascade(db, fk) | ||
alter_table fk.table do | ||
drop_constraint fk.name, type: :foreign_key if foreign_key_exists?(db, fk.table, fk.name) | ||
add_foreign_key [fk.column], fk.referenced_table, key: fk.referenced_column, name: fk.name unless fk.new_constraint | ||
end | ||
end |
40 changes: 40 additions & 0 deletions
40
db/migrations/20240115163000_add_delete_cascade_to_foreign_keys.rb
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,40 @@ | ||
require File.expand_path('../../helpers/add_delete_cascade_to_foreign_key', __FILE__) | ||
|
||
Sequel.migration do | ||
foreign_keys = [ | ||
ForeignKey.new(:deployment_processes, :fk_deployment_processes_deployment_guid, :deployment_guid, :deployments, :guid), | ||
ForeignKey.new(:deployment_labels, :fk_deployment_labels_resource_guid, :resource_guid, :deployments, :guid), | ||
ForeignKey.new(:deployment_annotations, :fk_deployment_annotations_resource_guid, :resource_guid, :deployments, :guid), | ||
ForeignKey.new(:build_labels, :fk_build_labels_resource_guid, :resource_guid, :builds, :guid), | ||
ForeignKey.new(:build_annotations, :fk_build_annotations_resource_guid, :resource_guid, :builds, :guid), | ||
ForeignKey.new(:kpack_lifecycle_data, :fk_kpack_lifecycle_build_guid, :build_guid, :builds, :guid), | ||
ForeignKey.new(:buildpack_lifecycle_data, :fk_buildpack_lifecycle_build_guid, :build_guid, :builds, :guid, new_constraint: true), | ||
ForeignKey.new(:buildpack_lifecycle_buildpacks, :fk_blbuildpack_bldata_guid, :buildpack_lifecycle_data_guid, :buildpack_lifecycle_data, :guid), | ||
ForeignKey.new(:task_labels, :fk_task_labels_resource_guid, :resource_guid, :tasks, :guid), | ||
ForeignKey.new(:task_annotations, :fk_task_annotations_resource_guid, :resource_guid, :tasks, :guid), | ||
ForeignKey.new(:revision_labels, :fk_revision_labels_resource_guid, :resource_guid, :revisions, :guid), | ||
ForeignKey.new(:revision_annotations, :fk_revision_annotations_resource_guid, :resource_guid, :revisions, :guid), | ||
ForeignKey.new(:revision_process_commands, :rev_commands_revision_guid_fkey, :revision_guid, :revisions, :guid), | ||
ForeignKey.new(:revision_sidecars, :fk_sidecar_revision_guid, :revision_guid, :revisions, :guid), | ||
ForeignKey.new(:revision_sidecar_process_types, :fk_revision_sidecar_proc_type_sidecar_guid, :revision_sidecar_guid, :revision_sidecars, :guid), | ||
ForeignKey.new(:sidecar_process_types, :fk_sidecar_proc_type_sidecar_guid, :sidecar_guid, :sidecars, :guid), | ||
] | ||
|
||
no_transaction | ||
|
||
up do | ||
db = self | ||
|
||
foreign_keys.each do |fk| | ||
transaction { recreate_foreign_key_with_delete_cascade(db, fk) } | ||
end | ||
end | ||
|
||
down do | ||
db = self | ||
|
||
foreign_keys.each do |fk| | ||
transaction { recreate_foreign_key_without_delete_cascade(db, fk) } | ||
end | ||
end | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
40 changes: 40 additions & 0 deletions
40
spec/migrations/20240115163000_add_delete_cascade_to_foreign_keys_spec.rb
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,40 @@ | ||
require 'spec_helper' | ||
require 'migrations/helpers/migration_shared_context' | ||
|
||
RSpec.describe 'migration to add delete cascade to foreign keys', isolation: :truncation, type: :migration do | ||
include_context 'migration' do | ||
let(:migration_filename) { '20240115163000_add_delete_cascade_to_foreign_keys.rb' } | ||
end | ||
|
||
describe 'buildpack_lifecycle_data table' do | ||
after do | ||
db[:buildpack_lifecycle_data].delete | ||
db[:builds].delete | ||
end | ||
|
||
context 'before adding the foreign key' do | ||
it 'allows inserts with a build_guid that does not exist' do | ||
expect { db[:buildpack_lifecycle_data].insert(guid: 'bld_guid', build_guid: 'not_exists') }.not_to raise_error | ||
end | ||
end | ||
|
||
context 'after adding the foreign key' do | ||
it 'prevents inserts with a build_guid that does not exist' do | ||
Sequel::Migrator.run(db, migration_to_test, allow_missing_migration_files: true) | ||
|
||
expect { db[:buildpack_lifecycle_data].insert(guid: 'bld_guid', build_guid: 'not_exists') }.to raise_error(Sequel::ForeignKeyConstraintViolation) | ||
end | ||
|
||
it 'deleted orphaned buildpack_lifecycle_data entries but kept valid ones' do | ||
db[:builds].insert(guid: 'build_guid') | ||
db[:buildpack_lifecycle_data].insert(guid: 'bld_guid', build_guid: 'build_guid') | ||
db[:buildpack_lifecycle_data].insert(guid: 'another_bld_guid', build_guid: 'not_exists') | ||
|
||
Sequel::Migrator.run(db, migration_to_test, allow_missing_migration_files: true) | ||
|
||
expect(db[:buildpack_lifecycle_data].where(guid: 'bld_guid').count).to eq(1) | ||
expect(db[:buildpack_lifecycle_data].where(guid: 'another_bld_guid').count).to eq(0) | ||
end | ||
end | ||
end | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,31 +1,39 @@ | ||
def mktmpsubdir(tmpdir, name) | ||
dir = File.join(tmpdir, name) | ||
Dir.mkdir(dir) | ||
dir | ||
end | ||
|
||
RSpec.shared_context 'migration' do | ||
let(:all_migrations) { Dir.mktmpdir } | ||
let(:down_migrations) { Dir.mktmpdir } | ||
let(:migration_to_test) { Dir.mktmpdir } | ||
let(:tmpdir) { Dir.mktmpdir } | ||
let(:down_migrations) { mktmpsubdir(tmpdir, 'down_migrations') } | ||
let(:migration_to_test) { mktmpsubdir(tmpdir, 'migration_to_test') } | ||
let(:db) { Sequel::Model.db } | ||
|
||
before do | ||
Sequel.extension :migration | ||
|
||
# Find all migrations | ||
migration_files = Dir.glob(sprintf('%s/*.rb', DBMigrator::SEQUEL_MIGRATIONS)) | ||
all_migration_files = Dir.glob(sprintf('%s/*.rb', DBMigrator::SEQUEL_MIGRATIONS)) | ||
# Calculate the index of our migration file we`d like to test | ||
migration_index = migration_files.find_index { |file| file.end_with?(migration_filename) } | ||
migration_index = all_migration_files.find_index { |file| file.end_with?(migration_filename) } | ||
# Make a file list of the migration file we like to test plus all migrations after the one we want to test | ||
migration_files_after_test = migration_files[migration_index...] | ||
migration_files_after_test = all_migration_files[migration_index...] | ||
|
||
# Copy them to a temp directory | ||
FileUtils.cp(migration_files, all_migrations) | ||
FileUtils.cp(migration_files_after_test, down_migrations) | ||
FileUtils.cp(File.join(DBMigrator::SEQUEL_MIGRATIONS, migration_filename), migration_to_test) | ||
|
||
# Copy migration helper files to temp directory | ||
FileUtils.cp_r(File.join(DBMigrator::MIGRATIONS_DIR, 'helpers'), tmpdir) | ||
|
||
# Revert the given migration and everything newer so we are at the database version exactly before our migration we want to test. | ||
Sequel::Migrator.run(db, down_migrations, target: 0, allow_missing_migration_files: true) | ||
end | ||
|
||
after do | ||
FileUtils.rm_rf(migration_to_test) | ||
FileUtils.rm_rf(down_migrations) | ||
|
||
# Complete the migration to not leave the test database half migrated and following tests fail due to this | ||
Sequel::Migrator.run(db, all_migrations, allow_missing_migration_files: true) | ||
FileUtils.rm_rf(all_migrations) | ||
Sequel::Migrator.run(db, down_migrations, allow_missing_migration_files: true) | ||
FileUtils.rm_rf(tmpdir) | ||
end | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.