online_migrations 0.26.0 → 0.27.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (51) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +18 -0
  3. data/docs/0.27-upgrade.md +24 -0
  4. data/docs/background_data_migrations.md +200 -101
  5. data/docs/background_schema_migrations.md +2 -2
  6. data/lib/generators/online_migrations/{background_migration_generator.rb → data_migration_generator.rb} +4 -4
  7. data/lib/generators/online_migrations/templates/change_background_data_migrations.rb.tt +34 -0
  8. data/lib/generators/online_migrations/templates/{background_data_migration.rb.tt → data_migration.rb.tt} +8 -9
  9. data/lib/generators/online_migrations/templates/initializer.rb.tt +19 -25
  10. data/lib/generators/online_migrations/templates/install_migration.rb.tt +9 -40
  11. data/lib/generators/online_migrations/upgrade_generator.rb +16 -8
  12. data/lib/online_migrations/active_record_batch_enumerator.rb +8 -0
  13. data/lib/online_migrations/background_data_migrations/backfill_column.rb +50 -0
  14. data/lib/online_migrations/background_data_migrations/config.rb +62 -0
  15. data/lib/online_migrations/{background_migrations → background_data_migrations}/copy_column.rb +15 -28
  16. data/lib/online_migrations/{background_migrations → background_data_migrations}/delete_associated_records.rb +9 -5
  17. data/lib/online_migrations/{background_migrations → background_data_migrations}/delete_orphaned_records.rb +5 -9
  18. data/lib/online_migrations/background_data_migrations/migration.rb +312 -0
  19. data/lib/online_migrations/{background_migrations → background_data_migrations}/migration_helpers.rb +72 -61
  20. data/lib/online_migrations/background_data_migrations/migration_job.rb +160 -0
  21. data/lib/online_migrations/background_data_migrations/migration_status_validator.rb +65 -0
  22. data/lib/online_migrations/{background_migrations → background_data_migrations}/perform_action_on_relation.rb +5 -5
  23. data/lib/online_migrations/{background_migrations → background_data_migrations}/reset_counters.rb +5 -5
  24. data/lib/online_migrations/background_data_migrations/scheduler.rb +78 -0
  25. data/lib/online_migrations/background_data_migrations/ticker.rb +62 -0
  26. data/lib/online_migrations/background_schema_migrations/config.rb +2 -2
  27. data/lib/online_migrations/background_schema_migrations/migration.rb +51 -123
  28. data/lib/online_migrations/background_schema_migrations/migration_helpers.rb +25 -46
  29. data/lib/online_migrations/background_schema_migrations/migration_runner.rb +43 -97
  30. data/lib/online_migrations/background_schema_migrations/scheduler.rb +2 -2
  31. data/lib/online_migrations/change_column_type_helpers.rb +17 -4
  32. data/lib/online_migrations/config.rb +4 -4
  33. data/lib/online_migrations/data_migration.rb +127 -0
  34. data/lib/online_migrations/error_messages.rb +2 -0
  35. data/lib/online_migrations/lock_retrier.rb +5 -2
  36. data/lib/online_migrations/schema_statements.rb +1 -1
  37. data/lib/online_migrations/shard_aware.rb +44 -0
  38. data/lib/online_migrations/version.rb +1 -1
  39. data/lib/online_migrations.rb +18 -11
  40. metadata +22 -21
  41. data/lib/online_migrations/background_migration.rb +0 -64
  42. data/lib/online_migrations/background_migrations/backfill_column.rb +0 -54
  43. data/lib/online_migrations/background_migrations/background_migration_class_validator.rb +0 -29
  44. data/lib/online_migrations/background_migrations/config.rb +0 -74
  45. data/lib/online_migrations/background_migrations/migration.rb +0 -329
  46. data/lib/online_migrations/background_migrations/migration_job.rb +0 -109
  47. data/lib/online_migrations/background_migrations/migration_job_runner.rb +0 -66
  48. data/lib/online_migrations/background_migrations/migration_job_status_validator.rb +0 -29
  49. data/lib/online_migrations/background_migrations/migration_runner.rb +0 -161
  50. data/lib/online_migrations/background_migrations/migration_status_validator.rb +0 -48
  51. data/lib/online_migrations/background_migrations/scheduler.rb +0 -42
@@ -8,13 +8,15 @@ module OnlineMigrations
8
8
  # `enqueue_background_schema_migration` helper inside migrations.
9
9
  #
10
10
  class Migration < ApplicationRecord
11
+ include ShardAware
12
+
11
13
  STATUSES = [
12
- :enqueued, # The migration has been enqueued by the user.
13
- :running, # The migration is being performed by a migration executor.
14
- :errored, # The migration raised an error during last run.
15
- :failed, # The migration raises an error when running and retry attempts exceeded.
16
- :succeeded, # The migration finished without error.
17
- :cancelled, # The migration was cancelled by the user.
14
+ "enqueued", # The migration has been enqueued by the user.
15
+ "running", # The migration is being performed by a migration executor.
16
+ "errored", # The migration raised an error during last run.
17
+ "failed", # The migration raises an error when running and retry attempts exceeded.
18
+ "succeeded", # The migration finished without error.
19
+ "cancelled", # The migration was cancelled by the user.
18
20
  ]
19
21
 
20
22
  MAX_IDENTIFIER_LENGTH = 63
@@ -22,36 +24,12 @@ module OnlineMigrations
22
24
  self.table_name = :background_schema_migrations
23
25
 
24
26
  scope :queue_order, -> { order(created_at: :asc) }
25
- scope :parents, -> { where(parent_id: nil) }
26
- scope :runnable, -> { where(composite: false) }
27
27
  scope :active, -> { where(status: [:enqueued, :running, :errored]) }
28
- scope :except_succeeded, -> { where.not(status: :succeeded) }
29
-
30
- scope :stuck, -> do
31
- runnable.active.where(<<~SQL)
32
- updated_at <= NOW() - interval '1 second' * (COALESCE(statement_timeout, 60*60*24) + 60*10)
33
- SQL
34
- end
35
-
36
- scope :retriable, -> do
37
- stuck_sql = connection.unprepared_statement { stuck.to_sql }
38
-
39
- from(Arel.sql(<<~SQL))
40
- (
41
- (SELECT * FROM background_schema_migrations WHERE NOT composite AND status = 'errored')
42
- UNION
43
- (#{stuck_sql})
44
- ) AS #{table_name}
45
- SQL
46
- end
47
28
 
48
29
  alias_attribute :name, :migration_name
49
30
 
50
31
  enum :status, STATUSES.index_with(&:to_s)
51
32
 
52
- belongs_to :parent, class_name: name, optional: true, inverse_of: :children
53
- has_many :children, class_name: name, foreign_key: :parent_id, inverse_of: :parent
54
-
55
33
  validates :table_name, presence: true, length: { maximum: MAX_IDENTIFIER_LENGTH }
56
34
  validates :definition, presence: true
57
35
  validates :migration_name, presence: true, uniqueness: {
@@ -65,70 +43,53 @@ module OnlineMigrations
65
43
  end,
66
44
  }
67
45
 
68
- validate :validate_children_statuses, if: -> { composite? && status_changed? }
69
- validate :validate_connection_class, if: :connection_class_name?
70
46
  validate :validate_table_exists
71
47
  validates_with MigrationStatusValidator, on: :update
72
48
 
73
49
  before_validation :set_defaults
74
50
 
51
+ # Returns whether the migration is completed, which is defined as
52
+ # having a status of succeeded, failed, or cancelled.
53
+ #
54
+ # @return [Boolean] whether the migration is completed.
55
+ #
75
56
  def completed?
76
- succeeded? || failed?
57
+ succeeded? || failed? || cancelled?
77
58
  end
78
59
 
79
- # Overwrite enum's generated method to correctly work for composite migrations.
80
- def cancelled!
81
- return super if !composite?
82
-
83
- transaction do
84
- super
85
- children.each { |child| child.cancelled! if !child.succeeded? }
86
- end
60
+ # Returns whether the migration is active, which is defined as
61
+ # having a status of enqueued, or running.
62
+ #
63
+ # @return [Boolean] whether the migration is active.
64
+ #
65
+ def active?
66
+ enqueued? || running?
87
67
  end
68
+
88
69
  alias cancel cancelled!
89
70
 
71
+ # Returns whether this migration is pausable.
72
+ #
90
73
  def pausable?
91
74
  false
92
75
  end
93
76
 
94
- def can_be_paused?
95
- false
96
- end
97
-
98
- def can_be_cancelled?
99
- !succeeded? && !cancelled?
100
- end
101
-
102
- # Returns the progress of the background schema migration.
77
+ # Dummy method to support the same interface as background data migrations.
103
78
  #
104
- # @return [Float] value in range from 0.0 to 100.0
79
+ # @return [nil]
105
80
  #
106
81
  def progress
107
- if succeeded?
108
- 100.0
109
- elsif composite?
110
- progresses = children.map(&:progress)
111
- # There should not be composite migrations without children,
112
- # but children may be deleted for some reason, so we need to
113
- # make a check to avoid 0 division error.
114
- if progresses.any?
115
- (progresses.sum.to_f / progresses.size).round(2)
116
- else
117
- 0.0
118
- end
119
- else
120
- 0.0
121
- end
122
82
  end
123
83
 
124
84
  # Whether the migration is considered stuck (is running for some configured time).
125
85
  #
126
86
  def stuck?
127
- # Composite migrations are not considered stuck.
128
- return false if composite?
129
-
130
- stuck_timeout = (statement_timeout || 1.day) + 10.minutes
131
- running? && updated_at <= stuck_timeout.seconds.ago
87
+ if index_addition?
88
+ running? && !index_build_in_progress?
89
+ else
90
+ stuck_timeout = (statement_timeout || 1.day) + 10.minutes
91
+ running? && updated_at <= stuck_timeout.seconds.ago
92
+ end
132
93
  end
133
94
 
134
95
  # Mark this migration as ready to be processed again.
@@ -136,27 +97,16 @@ module OnlineMigrations
136
97
  # This is used to manually retrying failed migrations.
137
98
  #
138
99
  def retry
139
- if composite? && failed?
140
- transaction do
141
- update!(status: :enqueued, finished_at: nil)
142
- children.failed.each(&:retry)
143
- end
144
-
145
- true
146
- elsif failed?
147
- transaction do
148
- parent.update!(status: :enqueued, finished_at: nil) if parent
149
-
150
- update!(
151
- status: :enqueued,
152
- attempts: 0,
153
- started_at: nil,
154
- finished_at: nil,
155
- error_class: nil,
156
- error_message: nil,
157
- backtrace: nil
158
- )
159
- end
100
+ if failed?
101
+ update!(
102
+ status: :enqueued,
103
+ attempts: 0,
104
+ started_at: nil,
105
+ finished_at: nil,
106
+ error_class: nil,
107
+ error_message: nil,
108
+ backtrace: nil
109
+ )
160
110
 
161
111
  true
162
112
  else
@@ -168,15 +118,6 @@ module OnlineMigrations
168
118
  definition.match?(/create (unique )?index/i)
169
119
  end
170
120
 
171
- # @private
172
- def connection_class
173
- if connection_class_name && (klass = connection_class_name.safe_constantize)
174
- Utils.find_connection_class(klass)
175
- else
176
- ActiveRecord::Base
177
- end
178
- end
179
-
180
121
  # @private
181
122
  def attempts_exceeded?
182
123
  attempts >= max_attempts
@@ -184,7 +125,7 @@ module OnlineMigrations
184
125
 
185
126
  # @private
186
127
  def run
187
- on_shard do
128
+ on_shard_if_present do
188
129
  connection = connection_class.connection
189
130
 
190
131
  connection.with_lock_retries do
@@ -214,28 +155,11 @@ module OnlineMigrations
214
155
  end
215
156
 
216
157
  private
217
- def validate_children_statuses
218
- if composite?
219
- if succeeded? && children.except_succeeded.exists?
220
- errors.add(:base, "all child migrations must be succeeded")
221
- elsif failed? && !children.failed.exists?
222
- errors.add(:base, "at least one child migration must be failed")
223
- end
224
- end
225
- end
226
-
227
- def validate_connection_class
228
- klass = connection_class_name.safe_constantize
229
- if !(klass <= ActiveRecord::Base)
230
- errors.add(:connection_class_name, "is not an ActiveRecord::Base child class")
231
- end
232
- end
233
-
234
158
  def validate_table_exists
235
159
  # Skip this validation if we have invalid connection class name.
236
160
  return if errors.include?(:connection_class_name)
237
161
 
238
- on_shard do
162
+ on_shard_if_present do
239
163
  if !connection_class.connection.table_exists?(table_name)
240
164
  errors.add(:table_name, "'#{table_name}' does not exist")
241
165
  end
@@ -248,9 +172,13 @@ module OnlineMigrations
248
172
  self.statement_timeout ||= config.statement_timeout
249
173
  end
250
174
 
251
- def on_shard(&block)
252
- shard = (self.shard || connection_class.default_shard).to_sym
253
- connection_class.connected_to(shard: shard, role: :writing, &block)
175
+ def index_build_in_progress?
176
+ indexes_in_progress = connection_class.connection.select_values(<<~SQL)
177
+ SELECT index_relid::regclass::text
178
+ FROM pg_stat_progress_create_index
179
+ SQL
180
+
181
+ indexes_in_progress.include?(name)
254
182
  end
255
183
 
256
184
  def with_statement_timeout(connection, timeout)
@@ -78,67 +78,46 @@ module OnlineMigrations
78
78
  # ensure_background_schema_migration_succeeded("index_users_on_email")
79
79
  #
80
80
  def ensure_background_schema_migration_succeeded(migration_name)
81
- migration = Migration.parents.find_by(migration_name: migration_name)
81
+ migrations = Migration.where(migration_name: migration_name).to_a
82
82
 
83
- if migration.nil?
84
- Utils.raise_in_prod_or_say_in_dev("Could not find background schema migration: '#{migration_name}'")
85
- elsif !migration.succeeded?
86
- raise "Expected background schema migration '#{migration_name}' to be marked as 'succeeded', " \
87
- "but it is '#{migration.status}'."
83
+ if migrations.empty?
84
+ Utils.raise_in_prod_or_say_in_dev("Could not find background schema migration(s): '#{migration_name}'.")
85
+ elsif !migrations.all?(&:succeeded?)
86
+ raise "Expected background schema migration(s) '#{migration_name}' to be marked as 'succeeded'."
88
87
  end
89
88
  end
90
89
 
91
- def enqueue_background_schema_migration(name, table_name, **options)
92
- if options[:connection_class_name].nil? && Utils.multiple_databases?
93
- raise ArgumentError, "You must pass a :connection_class_name when using multiple databases."
94
- end
95
-
96
- migration = create_background_schema_migration(name, table_name, **options)
90
+ def enqueue_background_schema_migration(migration_name, table_name, connection_class_name: nil, **options)
91
+ options.assert_valid_keys(:definition, :max_attempts, :statement_timeout)
97
92
 
98
- run_inline = OnlineMigrations.config.run_background_migrations_inline
99
- if run_inline && run_inline.call
100
- runner = MigrationRunner.new(migration)
101
- runner.run
93
+ if Utils.multiple_databases? && !connection_class_name
94
+ raise ArgumentError, "You must pass a :connection_class_name when using multiple databases."
102
95
  end
103
96
 
104
- migration
105
- end
106
-
107
- # @private
108
- def create_background_schema_migration(migration_name, table_name, connection_class_name: nil, **options)
109
- options.assert_valid_keys(:definition, :max_attempts, :statement_timeout)
110
-
111
97
  if connection_class_name
112
- connection_class_name = __normalize_connection_class_name(connection_class_name)
98
+ klass = connection_class_name.constantize
99
+ connection_class = Utils.find_connection_class(klass)
100
+ # Normalize to the real connection class name.
101
+ connection_class_name = connection_class.name
102
+ else
103
+ connection_class = ActiveRecord::Base
113
104
  end
114
105
 
115
- Migration.find_or_create_by!(migration_name: migration_name, shard: nil,
116
- connection_class_name: connection_class_name) do |migration|
117
- migration.assign_attributes(**options, table_name: table_name)
106
+ shards = Utils.shard_names(connection_class)
107
+ shards = [nil] if shards.size == 1
118
108
 
119
- shards = Utils.shard_names(migration.connection_class)
120
- if shards.size > 1
121
- migration.children = shards.map do |shard|
122
- child = migration.dup
123
- child.shard = shard
124
- child
125
- end
109
+ shards.each do |shard|
110
+ migration = Migration.create_with(**options, table_name: table_name)
111
+ .find_or_create_by!(migration_name: migration_name, shard: shard, connection_class_name: connection_class_name)
126
112
 
127
- migration.composite = true
113
+ if Utils.run_background_migrations_inline?
114
+ runner = MigrationRunner.new(migration)
115
+ runner.run
128
116
  end
129
117
  end
130
- end
131
118
 
132
- private
133
- def __normalize_connection_class_name(connection_class_name)
134
- if connection_class_name
135
- klass = connection_class_name.safe_constantize
136
- if klass
137
- connection_class = Utils.find_connection_class(klass)
138
- connection_class.name if connection_class
139
- end
140
- end
141
- end
119
+ true
120
+ end
142
121
  end
143
122
  end
144
123
  end
@@ -13,119 +13,65 @@ module OnlineMigrations
13
13
  def run
14
14
  return if migration.cancelled? || migration.succeeded?
15
15
 
16
- mark_as_running if migration.enqueued? || migration.errored?
16
+ migration.running! if migration.enqueued? || migration.errored?
17
+ migration_payload = { migration: migration }
17
18
 
18
- if migration.composite?
19
- migration.children.each do |child_migration|
20
- runner = self.class.new(child_migration)
21
- runner.run
22
- end
19
+ if migration.attempts == 0
20
+ ActiveSupport::Notifications.instrument("started.background_schema_migrations", migration_payload)
23
21
  else
24
- do_run
22
+ ActiveSupport::Notifications.instrument("retried.background_schema_migrations", migration_payload)
25
23
  end
26
- end
27
-
28
- private
29
- def mark_as_running
30
- Migration.transaction do
31
- migration.running!
32
24
 
33
- if (parent = migration.parent)
34
- if parent.started_at
35
- parent.update!(status: :running, finished_at: nil)
36
- else
37
- parent.update!(status: :running, started_at: Time.current, finished_at: nil)
38
- end
39
- end
40
- end
25
+ if should_throttle?
26
+ ActiveSupport::Notifications.instrument("throttled.background_schema_migrations", migration_payload)
27
+ return
41
28
  end
42
29
 
43
- def do_run
44
- migration_payload = notifications_payload(migration)
45
-
46
- if migration.attempts == 0
47
- ActiveSupport::Notifications.instrument("started.background_schema_migrations", migration_payload)
48
- else
49
- ActiveSupport::Notifications.instrument("retried.background_schema_migrations", migration_payload)
50
- end
51
-
52
- if should_throttle?
53
- ActiveSupport::Notifications.instrument("throttled.background_schema_migrations", migration_payload)
54
- return
55
- end
56
-
57
- migration.update!(
58
- attempts: migration.attempts + 1,
59
- status: :running,
60
- started_at: Time.current,
61
- finished_at: nil,
62
- error_class: nil,
63
- error_message: nil,
64
- backtrace: nil
65
- )
66
-
67
- ActiveSupport::Notifications.instrument("run.background_schema_migrations", migration_payload) do
68
- migration.run
69
- end
70
-
71
- # Background schema migrations could take a while to run. It is possible, that the process
72
- # never reaches this (or the rescue below) line of code. E.g., when it is force quitted
73
- # (SIGKILL etc.) and so the migration will end up in the "running" state and the query is
74
- # still executing (or already finished) in the database. This migration can either be safely
75
- # manually retried or will be picked up in the future by scheduler when it decides that
76
- # this migration is stuck.
77
-
78
- migration.update!(status: :succeeded, finished_at: Time.current)
30
+ migration.update!(
31
+ attempts: migration.attempts + 1,
32
+ status: :running,
33
+ started_at: Time.current,
34
+ finished_at: nil,
35
+ error_class: nil,
36
+ error_message: nil,
37
+ backtrace: nil
38
+ )
39
+
40
+ ActiveSupport::Notifications.instrument("run.background_schema_migrations", migration_payload) do
41
+ migration.run
42
+ end
79
43
 
80
- ActiveSupport::Notifications.instrument("completed.background_schema_migrations", migration_payload)
44
+ # Background schema migrations could take a while to run. It is possible, that the process
45
+ # never reaches this (or the rescue below) line of code. E.g., when it is force quitted
46
+ # (SIGKILL etc.) and so the migration will end up in the "running" state and the query is
47
+ # still executing (or already finished) in the database. This migration can either be safely
48
+ # manually retried or will be picked up in the future by scheduler when it decides that
49
+ # this migration is stuck.
81
50
 
82
- complete_parent_if_needed(migration) if migration.parent.present?
83
- rescue Exception => e # rubocop:disable Lint/RescueException
84
- backtrace_cleaner = ::OnlineMigrations.config.backtrace_cleaner
51
+ migration.update!(status: :succeeded, finished_at: Time.current)
85
52
 
86
- status = migration.attempts_exceeded? ? :failed : :errored
53
+ ActiveSupport::Notifications.instrument("completed.background_schema_migrations", migration_payload)
54
+ rescue Exception => e # rubocop:disable Lint/RescueException
55
+ backtrace_cleaner = ::OnlineMigrations.config.backtrace_cleaner
87
56
 
88
- migration.update!(
89
- status: status,
90
- finished_at: Time.current,
91
- error_class: e.class.name,
92
- error_message: e.message,
93
- backtrace: backtrace_cleaner ? backtrace_cleaner.clean(e.backtrace) : e.backtrace
94
- )
57
+ status = migration.attempts_exceeded? ? :failed : :errored
95
58
 
96
- complete_parent_if_needed(migration) if migration.parent.present?
59
+ migration.update!(
60
+ status: status,
61
+ finished_at: Time.current,
62
+ error_class: e.class.name,
63
+ error_message: e.message,
64
+ backtrace: backtrace_cleaner ? backtrace_cleaner.clean(e.backtrace) : e.backtrace
65
+ )
97
66
 
98
- ::OnlineMigrations.config.background_schema_migrations.error_handler.call(e, migration)
99
- raise if Utils.run_background_migrations_inline?
100
- end
67
+ ::OnlineMigrations.config.background_schema_migrations.error_handler.call(e, migration)
68
+ raise if Utils.run_background_migrations_inline?
69
+ end
101
70
 
71
+ private
102
72
  def should_throttle?
103
73
  ::OnlineMigrations.config.throttler.call
104
74
  end
105
-
106
- def complete_parent_if_needed(migration)
107
- parent = migration.parent
108
- completed = false
109
-
110
- parent.with_lock do
111
- children = parent.children.select(:status)
112
- if children.all?(&:succeeded?)
113
- parent.update!(status: :succeeded, finished_at: Time.current)
114
- completed = true
115
- elsif children.any?(&:failed?)
116
- parent.update!(status: :failed, finished_at: Time.current)
117
- completed = true
118
- end
119
- end
120
-
121
- if completed
122
- ActiveSupport::Notifications.instrument("completed.background_migrations", notifications_payload(migration))
123
- end
124
- end
125
-
126
- def notifications_payload(migration)
127
- { background_schema_migration: migration }
128
- end
129
75
  end
130
76
  end
131
77
  end
@@ -35,8 +35,8 @@ module OnlineMigrations
35
35
 
36
36
  private
37
37
  def find_migration(**options)
38
- active_migrations = Migration.running.reject(&:stuck?)
39
- runnable_migrations = Migration.runnable.enqueued.queue_order.to_a + Migration.retriable.queue_order.to_a
38
+ stuck_migrations, active_migrations = Migration.running.partition(&:stuck?)
39
+ runnable_migrations = (Migration.enqueued + Migration.errored + stuck_migrations).sort_by(&:created_at)
40
40
 
41
41
  if options.key?(:shard)
42
42
  runnable_migrations = runnable_migrations.select { |migration| migration.shard.to_s == options[:shard].to_s }
@@ -152,7 +152,11 @@ module OnlineMigrations
152
152
  # revert_initialize_column_type_change(:files, :size)
153
153
  #
154
154
  def revert_initialize_column_type_change(table_name, column_name, _new_type = nil, **_options)
155
- cleanup_column_type_change(table_name, column_name)
155
+ tmp_column_name = __change_type_column(column_name)
156
+ transaction do
157
+ __remove_copy_triggers(table_name, column_name, tmp_column_name)
158
+ remove_column(table_name, tmp_column_name)
159
+ end
156
160
  end
157
161
 
158
162
  # Same as `revert_initialize_column_type_change` but for multiple columns.
@@ -160,7 +164,12 @@ module OnlineMigrations
160
164
  #
161
165
  def revert_initialize_columns_type_change(table_name, columns_and_types, **_options)
162
166
  column_names = columns_and_types.map(&:first)
163
- cleanup_columns_type_change(table_name, *column_names)
167
+ tmp_column_names = column_names.map { |column_name| __change_type_column(column_name) }
168
+
169
+ transaction do
170
+ __remove_copy_triggers(table_name, column_names, tmp_column_names)
171
+ remove_columns(table_name, *tmp_column_names)
172
+ end
164
173
  end
165
174
 
166
175
  # Backfills data from the old column to the new column.
@@ -347,6 +356,7 @@ module OnlineMigrations
347
356
  # the original column type to be able to revert.
348
357
  #
349
358
  def cleanup_column_type_change(table_name, column_name)
359
+ __ensure_not_in_transaction!
350
360
  cleanup_columns_type_change(table_name, column_name)
351
361
  end
352
362
 
@@ -354,6 +364,8 @@ module OnlineMigrations
354
364
  # @see #cleanup_column_type_change
355
365
  #
356
366
  def cleanup_columns_type_change(table_name, *column_names)
367
+ __ensure_not_in_transaction!
368
+
357
369
  tmp_column_names = column_names.map { |column_name| __change_type_column(column_name) }
358
370
 
359
371
  # Safely remove existing indexes and foreign keys first, if any.
@@ -477,10 +489,11 @@ module OnlineMigrations
477
489
  end
478
490
 
479
491
  def __copy_check_constraints(table_name, from_column, to_column)
480
- check_constraints = check_constraints(table_name).select { |c| c.expression.include?(from_column) }
492
+ from_column_re = /\b#{from_column}\b/
493
+ check_constraints = check_constraints(table_name).select { |c| c.expression.match?(from_column_re) }
481
494
 
482
495
  check_constraints.each do |check|
483
- new_expression = check.expression.gsub(from_column, to_column)
496
+ new_expression = check.expression.gsub(from_column_re, to_column)
484
497
 
485
498
  add_check_constraint(table_name, new_expression, validate: false)
486
499
 
@@ -210,10 +210,10 @@ module OnlineMigrations
210
210
 
211
211
  # Configuration object to configure background migrations
212
212
  #
213
- # @return [BackgroundMigrationsConfig]
214
- # @see BackgroundMigrationsConfig
213
+ # @return [BackgroundDataMigrations::Config]
214
+ # @see BackgroundDataMigrations::Config
215
215
  #
216
- attr_reader :background_migrations
216
+ attr_reader :background_data_migrations
217
217
 
218
218
  attr_reader :background_schema_migrations
219
219
 
@@ -230,7 +230,7 @@ module OnlineMigrations
230
230
  lock_timeout: 0.2.seconds
231
231
  )
232
232
 
233
- @background_migrations = BackgroundMigrations::Config.new
233
+ @background_data_migrations = BackgroundDataMigrations::Config.new
234
234
  @background_schema_migrations = BackgroundSchemaMigrations::Config.new
235
235
 
236
236
  @checks = []