summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDouglas Barbosa Alexandre <dbalexandre@gmail.com>2019-09-11 16:23:43 +0000
committerDouglas Barbosa Alexandre <dbalexandre@gmail.com>2019-09-11 16:23:43 +0000
commit70302281d70b65bd0653f9f3732e5dbbbbc98753 (patch)
tree13e059a9af8ddad9d1c5d8e312be17629367c9cb
parent0abc902576a755355b1daf75e19d1f37c6ffb5ff (diff)
parent50c647af51ae733abb96b84a5169d40b3c8c59be (diff)
downloadgitlab-ce-70302281d70b65bd0653f9f3732e5dbbbbc98753.tar.gz
Merge branch 'pl-remove-ignored-columns-task' into 'master'
Add rake task `db:obsolete_ignored_columns` See merge request gitlab-org/gitlab-ce!32904
-rw-r--r--doc/development/rake_tasks.md10
-rw-r--r--lib/gitlab/database/obsolete_ignored_columns.rb38
-rw-r--r--lib/tasks/db_obsolete_ignored_columns.rake21
-rw-r--r--spec/lib/gitlab/database/obsolete_ignored_columns_spec.rb43
4 files changed, 112 insertions, 0 deletions
diff --git a/doc/development/rake_tasks.md b/doc/development/rake_tasks.md
index e9d6cfe00b2..a144bf70a86 100644
--- a/doc/development/rake_tasks.md
+++ b/doc/development/rake_tasks.md
@@ -216,3 +216,13 @@ bundle exec rake routes
Since these take some time to create, it's often helpful to save the output to
a file for quick reference.
+
+## Show obsolete `ignored_columns`
+
+To see a list of all obsolete `ignored_columns` run:
+
+```
+bundle exec rake db:obsolete_ignored_columns
+```
+
+Feel free to remove their definitions from their `ignored_columns` definitions.
diff --git a/lib/gitlab/database/obsolete_ignored_columns.rb b/lib/gitlab/database/obsolete_ignored_columns.rb
new file mode 100644
index 00000000000..6266b6a4b65
--- /dev/null
+++ b/lib/gitlab/database/obsolete_ignored_columns.rb
@@ -0,0 +1,38 @@
+# frozen_string_literal: true
+
+module Gitlab
+ module Database
+ # Checks which `ignored_columns` can be safely removed by scanning
+ # the current schema for all `ApplicationRecord` descendants.
+ class ObsoleteIgnoredColumns
+ def initialize(base = ApplicationRecord)
+ @base = base
+ end
+
+ def execute
+ @base.descendants.map do |klass|
+ next if klass.abstract_class?
+
+ safe_to_remove = ignored_columns_safe_to_remove_for(klass)
+ next if safe_to_remove.empty?
+
+ [klass.name, safe_to_remove]
+ end.compact.sort_by(&:first)
+ end
+
+ private
+
+ def ignored_columns_safe_to_remove_for(klass)
+ ignored = klass.ignored_columns.map(&:to_s)
+
+ return [] if ignored.empty?
+
+ schema = klass.connection.schema_cache.columns_hash(klass.table_name)
+ existing = schema.values.map(&:name)
+
+ used = ignored & existing
+ ignored - used
+ end
+ end
+ end
+end
diff --git a/lib/tasks/db_obsolete_ignored_columns.rake b/lib/tasks/db_obsolete_ignored_columns.rake
new file mode 100644
index 00000000000..184e407f28c
--- /dev/null
+++ b/lib/tasks/db_obsolete_ignored_columns.rake
@@ -0,0 +1,21 @@
+desc 'Show a list of obsolete `ignored_columns`'
+task 'db:obsolete_ignored_columns' => :environment do
+ list = Gitlab::Database::ObsoleteIgnoredColumns.new.execute
+
+ if list.empty?
+ puts 'No obsolete `ignored_columns` found.'
+ else
+ puts 'The following `ignored_columns` are obsolete and can be removed:'
+
+ list.each do |name, ignored_columns|
+ puts "- #{name}: #{ignored_columns.join(', ')}"
+ end
+
+ puts <<~TEXT
+
+ WARNING: Removing columns is tricky because running GitLab processes may still be using the columns.
+
+ See also https://docs.gitlab.com/ee/development/what_requires_downtime.html#dropping-columns
+ TEXT
+ end
+end
diff --git a/spec/lib/gitlab/database/obsolete_ignored_columns_spec.rb b/spec/lib/gitlab/database/obsolete_ignored_columns_spec.rb
new file mode 100644
index 00000000000..6d38f7f1b95
--- /dev/null
+++ b/spec/lib/gitlab/database/obsolete_ignored_columns_spec.rb
@@ -0,0 +1,43 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+describe Gitlab::Database::ObsoleteIgnoredColumns do
+ module Testing
+ class MyBase < ApplicationRecord
+ end
+
+ class SomeAbstract < MyBase
+ self.abstract_class = true
+
+ self.table_name = 'projects'
+
+ self.ignored_columns += %i[unused]
+ end
+
+ class B < MyBase
+ self.table_name = 'issues'
+
+ self.ignored_columns += %i[id other]
+ end
+
+ class A < SomeAbstract
+ self.ignored_columns += %i[id also_unused]
+ end
+
+ class C < MyBase
+ self.table_name = 'users'
+ end
+ end
+
+ subject { described_class.new(Testing::MyBase) }
+
+ describe '#execute' do
+ it 'returns a list of class names and columns pairs' do
+ expect(subject.execute).to eq([
+ ['Testing::A', %w(unused also_unused)],
+ ['Testing::B', %w(other)]
+ ])
+ end
+ end
+end