online_migrations 0.9.2 → 0.11.0

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 (39) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +41 -0
  3. data/README.md +155 -150
  4. data/docs/background_migrations.md +43 -10
  5. data/docs/configuring.md +23 -18
  6. data/lib/generators/online_migrations/install_generator.rb +3 -7
  7. data/lib/generators/online_migrations/templates/add_sharding_to_online_migrations.rb.tt +18 -0
  8. data/lib/generators/online_migrations/templates/initializer.rb.tt +12 -3
  9. data/lib/generators/online_migrations/templates/migration.rb.tt +8 -3
  10. data/lib/generators/online_migrations/upgrade_generator.rb +33 -0
  11. data/lib/online_migrations/background_migrations/application_record.rb +13 -0
  12. data/lib/online_migrations/background_migrations/backfill_column.rb +1 -1
  13. data/lib/online_migrations/background_migrations/copy_column.rb +11 -19
  14. data/lib/online_migrations/background_migrations/delete_orphaned_records.rb +2 -20
  15. data/lib/online_migrations/background_migrations/migration.rb +123 -34
  16. data/lib/online_migrations/background_migrations/migration_helpers.rb +0 -4
  17. data/lib/online_migrations/background_migrations/migration_job.rb +15 -12
  18. data/lib/online_migrations/background_migrations/migration_job_runner.rb +2 -2
  19. data/lib/online_migrations/background_migrations/migration_runner.rb +56 -11
  20. data/lib/online_migrations/background_migrations/reset_counters.rb +3 -9
  21. data/lib/online_migrations/background_migrations/scheduler.rb +5 -15
  22. data/lib/online_migrations/change_column_type_helpers.rb +71 -86
  23. data/lib/online_migrations/command_checker.rb +50 -46
  24. data/lib/online_migrations/config.rb +19 -15
  25. data/lib/online_migrations/copy_trigger.rb +15 -10
  26. data/lib/online_migrations/error_messages.rb +13 -25
  27. data/lib/online_migrations/foreign_keys_collector.rb +2 -2
  28. data/lib/online_migrations/indexes_collector.rb +3 -3
  29. data/lib/online_migrations/lock_retrier.rb +4 -9
  30. data/lib/online_migrations/schema_cache.rb +0 -6
  31. data/lib/online_migrations/schema_dumper.rb +21 -0
  32. data/lib/online_migrations/schema_statements.rb +80 -256
  33. data/lib/online_migrations/utils.rb +36 -55
  34. data/lib/online_migrations/verbose_sql_logs.rb +3 -2
  35. data/lib/online_migrations/version.rb +1 -1
  36. data/lib/online_migrations.rb +9 -6
  37. metadata +9 -7
  38. data/lib/online_migrations/background_migrations/advisory_lock.rb +0 -62
  39. data/lib/online_migrations/foreign_key_definition.rb +0 -17
@@ -1,7 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require "erb"
4
- require "openssl"
5
4
  require "set"
6
5
 
7
6
  module OnlineMigrations
@@ -53,6 +52,36 @@ module OnlineMigrations
53
52
  end
54
53
 
55
54
  private
55
+ ERROR_MESSAGE_TO_LINK = {
56
+ multiple_foreign_keys: "adding-multiple-foreign-keys",
57
+ create_table: "creating-a-table-with-the-force-option",
58
+ short_primary_key_type: "using-primary-key-with-short-integer-type",
59
+ drop_table_multiple_foreign_keys: "removing-a-table-with-multiple-foreign-keys",
60
+ rename_table: "renaming-a-table",
61
+ add_column_with_default_null: "adding-a-column-with-a-default-value",
62
+ add_column_with_default: "adding-a-column-with-a-default-value",
63
+ add_column_generated_stored: "adding-a-stored-generated-column",
64
+ add_column_json: "adding-a-json-column",
65
+ rename_column: "renaming-a-column",
66
+ change_column: "changing-the-type-of-a-column",
67
+ change_column_default: "changing-the-default-value-of-a-column",
68
+ change_column_null: "setting-not-null-on-an-existing-column",
69
+ remove_column: "removing-a-column",
70
+ add_timestamps_with_default: "adding-a-column-with-a-default-value",
71
+ add_hash_index: "hash-indexes",
72
+ add_reference: "adding-a-reference",
73
+ add_index: "adding-an-index-non-concurrently",
74
+ replace_index: "replacing-an-index",
75
+ remove_index: "removing-an-index-non-concurrently",
76
+ add_foreign_key: "adding-a-foreign-key",
77
+ add_exclusion_constraint: "adding-an-exclusion-constraint",
78
+ add_check_constraint: "adding-a-check-constraint",
79
+ add_unique_constraint: "adding-a-unique-constraint",
80
+ execute: "executing-SQL-directly",
81
+ add_inheritance_column: "adding-a-single-table-inheritance-column",
82
+ mismatched_foreign_key_type: "mismatched-reference-column-types",
83
+ }
84
+
56
85
  def check_database_version
57
86
  return if defined?(@database_version_checked)
58
87
 
@@ -125,13 +154,7 @@ module OnlineMigrations
125
154
  check_columns_removal(command, *args, **options)
126
155
  else
127
156
  if respond_to?(command, true)
128
- if options.any?
129
- # Workaround for Active Record < 5 change_column_default
130
- # not accepting options.
131
- send(command, *args, **options, &block)
132
- else
133
- send(command, *args, &block)
134
- end
157
+ send(command, *args, **options, &block)
135
158
  else
136
159
  # assume it is safe
137
160
  true
@@ -333,9 +356,7 @@ module OnlineMigrations
333
356
  precision = options[:precision] || options[:limit] || 6
334
357
  existing_precision = existing_column.precision || existing_column.limit || 6
335
358
 
336
- # PostgreSQL interval data type was added in https://github.com/rails/rails/pull/16919
337
- (existing_type == :interval || (Utils.ar_version < 6.1 && existing_column.sql_type.start_with?("interval"))) &&
338
- precision >= existing_precision
359
+ existing_type == :interval && precision >= existing_precision
339
360
  when :inet
340
361
  existing_type == :cidr
341
362
  else
@@ -458,7 +479,7 @@ module OnlineMigrations
458
479
 
459
480
  def add_reference(table_name, ref_name, **options)
460
481
  # Always added by default in 5.0+
461
- index = options.fetch(:index) { Utils.ar_version >= 5.0 }
482
+ index = options.fetch(:index, true)
462
483
 
463
484
  if index.is_a?(Hash) && index[:using].to_s == "hash" && postgresql_version < Gem::Version.new("10")
464
485
  raise_error :add_hash_index
@@ -486,7 +507,7 @@ module OnlineMigrations
486
507
  end
487
508
 
488
509
  if !options[:polymorphic]
489
- type = (options[:type] || (Utils.ar_version >= 5.1 ? :bigint : :integer)).to_sym
510
+ type = (options[:type] || :bigint).to_sym
490
511
  column_name = "#{ref_name}_id"
491
512
 
492
513
  foreign_key_options = foreign_key.is_a?(Hash) ? foreign_key : {}
@@ -522,6 +543,11 @@ module OnlineMigrations
522
543
  end
523
544
  end
524
545
  end
546
+
547
+ # Outdated statistics + a new index can hurt performance of existing queries.
548
+ if OnlineMigrations.config.auto_analyze && direction == :up
549
+ connection.execute("ANALYZE #{table_name}")
550
+ end
525
551
  end
526
552
  end
527
553
 
@@ -539,9 +565,9 @@ module OnlineMigrations
539
565
  end
540
566
 
541
567
  if index_def
542
- existing_options = [:name, :columns, :unique, :where, :type, :using, :opclasses].map do |option|
568
+ existing_options = [:name, :columns, :unique, :where, :type, :using, :opclasses].filter_map do |option|
543
569
  [option, index_def.public_send(option)] if index_def.respond_to?(option)
544
- end.compact.to_h
570
+ end.to_h
545
571
 
546
572
  @removed_indexes << IndexDefinition.new(table: table_name, **existing_options)
547
573
  end
@@ -586,7 +612,7 @@ module OnlineMigrations
586
612
  def add_unique_constraint(table_name, column_name = nil, **options)
587
613
  return if new_or_small_table?(table_name) || options[:using_index] || !column_name
588
614
 
589
- index_name = index_name(table_name, column_name)
615
+ index_name = Utils.index_name(table_name, column_name)
590
616
 
591
617
  raise_error :add_unique_constraint,
592
618
  add_index_code: command_str(:add_index, table_name, column_name, unique: true, name: index_name, algorithm: :concurrently),
@@ -594,22 +620,6 @@ module OnlineMigrations
594
620
  remove_code: command_str(:remove_unique_constraint, table_name, column_name)
595
621
  end
596
622
 
597
- # Implementation is from Active Record
598
- def index_name(table_name, column_name)
599
- max_index_name_size = 62
600
- name = "index_#{table_name}_on_#{Array(column_name) * '_and_'}"
601
- return name if name.bytesize <= max_index_name_size
602
-
603
- # Fallback to short version, add hash to ensure uniqueness
604
- hashed_identifier = "_#{OpenSSL::Digest::SHA256.hexdigest(name).first(10)}"
605
- name = "idx_on_#{Array(column_name) * '_'}"
606
-
607
- short_limit = max_index_name_size - hashed_identifier.bytesize
608
- short_name = name[0, short_limit]
609
-
610
- "#{short_name}#{hashed_identifier}"
611
- end
612
-
613
623
  def validate_constraint(*)
614
624
  if crud_blocked?
615
625
  raise_error :validate_constraint
@@ -720,19 +730,13 @@ module OnlineMigrations
720
730
  template = OnlineMigrations.config.error_messages.fetch(message_key)
721
731
 
722
732
  vars[:migration_name] = @migration.name
723
- vars[:migration_parent] = Utils.migration_parent_string
724
- vars[:model_parent] = Utils.model_parent_string
733
+ vars[:migration_parent] = "ActiveRecord::Migration[#{Utils.ar_version}]"
725
734
  vars[:ar_version] = Utils.ar_version
726
735
 
727
- if RUBY_VERSION >= "2.6"
728
- message = ERB.new(template, trim_mode: "<>").result_with_hash(vars)
729
- else
730
- # `result_with_hash` was added in ruby 2.5
731
- b = TOPLEVEL_BINDING.dup
732
- vars.each_pair do |key, value|
733
- b.local_variable_set(key, value)
734
- end
735
- message = ERB.new(template, nil, "<>").result(b)
736
+ message = ERB.new(template, trim_mode: "<>").result_with_hash(vars)
737
+
738
+ if (link = ERROR_MESSAGE_TO_LINK[message_key])
739
+ message += "\nFor more details, see https://github.com/fatkodima/online_migrations##{link}"
736
740
  end
737
741
 
738
742
  @migration.stop!(message, header: header || "Dangerous operation detected")
@@ -768,7 +772,7 @@ module OnlineMigrations
768
772
  end
769
773
 
770
774
  def crud_blocked?
771
- locks_query = <<-SQL.strip_heredoc
775
+ locks_query = <<~SQL
772
776
  SELECT relation::regclass::text
773
777
  FROM pg_locks
774
778
  WHERE mode IN ('ShareLock', 'ShareRowExclusiveLock', 'ExclusiveLock', 'AccessExclusiveLock')
@@ -786,7 +790,7 @@ module OnlineMigrations
786
790
  end
787
791
 
788
792
  def check_constraints(table_name)
789
- constraints_query = <<-SQL.strip_heredoc
793
+ constraints_query = <<~SQL
790
794
  SELECT pg_get_constraintdef(oid) AS def
791
795
  FROM pg_constraint
792
796
  WHERE contype = 'c'
@@ -807,7 +811,7 @@ module OnlineMigrations
807
811
 
808
812
  def check_mismatched_foreign_key_type(table_name, column_name, type, **options)
809
813
  column_name = column_name.to_s
810
- ref_name = column_name.sub(/_id\z/, "")
814
+ ref_name = column_name.delete_suffix("_id")
811
815
 
812
816
  if like_foreign_key?(column_name, type)
813
817
  foreign_table_name = Utils.foreign_table_name(ref_name, options)
@@ -16,7 +16,6 @@ module OnlineMigrations
16
16
  #
17
17
  def start_after=(value)
18
18
  if value.is_a?(Hash)
19
- ensure_supports_multiple_dbs
20
19
  @start_after = value.stringify_keys
21
20
  else
22
21
  @start_after = value
@@ -49,7 +48,6 @@ module OnlineMigrations
49
48
  #
50
49
  def target_version=(value)
51
50
  if value.is_a?(Hash)
52
- ensure_supports_multiple_dbs
53
51
  @target_version = value.stringify_keys
54
52
  else
55
53
  @target_version = value
@@ -84,6 +82,16 @@ module OnlineMigrations
84
82
  #
85
83
  attr_accessor :error_messages
86
84
 
85
+ # Whether to automatically run ANALYZE on the table after the index was added
86
+ # @return [Boolean]
87
+ #
88
+ attr_accessor :auto_analyze
89
+
90
+ # Whether to alphabetize schema
91
+ # @return [Boolean]
92
+ #
93
+ attr_accessor :alphabetize_schema
94
+
87
95
  # Maximum allowed lock timeout value (in seconds)
88
96
  #
89
97
  # If set lock timeout is greater than this value, the migration will fail.
@@ -138,7 +146,7 @@ module OnlineMigrations
138
146
  # Returns a list of enabled checks
139
147
  #
140
148
  # All checks are enabled by default. To disable/enable a check use `disable_check`/`enable_check`.
141
- # For the list of available checks look at `lib/error_messages` folder.
149
+ # For the list of available checks look at the `error_messages.rb` file.
142
150
  #
143
151
  # @return [Array]
144
152
  #
@@ -174,7 +182,7 @@ module OnlineMigrations
174
182
  attempts: 30,
175
183
  base_delay: 0.01.seconds,
176
184
  max_delay: 1.minute,
177
- lock_timeout: 0.05.seconds
185
+ lock_timeout: 0.2.seconds
178
186
  )
179
187
 
180
188
  @background_migrations = BackgroundMigrations::Config.new
@@ -184,8 +192,10 @@ module OnlineMigrations
184
192
  @target_version = nil
185
193
  @small_tables = []
186
194
  @check_down = false
187
- @enabled_checks = @error_messages.keys.map { |k| [k, {}] }.to_h
188
- @verbose_sql_logs = defined?(Rails.env) && Rails.env.production?
195
+ @auto_analyze = false
196
+ @alphabetize_schema = false
197
+ @enabled_checks = @error_messages.keys.index_with({})
198
+ @verbose_sql_logs = defined?(Rails.env) && (Rails.env.production? || Rails.env.staging?)
189
199
  end
190
200
 
191
201
  def lock_retrier=(value)
@@ -198,7 +208,7 @@ module OnlineMigrations
198
208
 
199
209
  # Enables specific check
200
210
  #
201
- # For the list of available checks look at `lib/error_messages` module.
211
+ # For the list of available checks look at the `error_messages.rb` file.
202
212
  #
203
213
  # @param name [Symbol] check name
204
214
  # @param start_after [Integer] migration version from which this check will be performed
@@ -210,7 +220,7 @@ module OnlineMigrations
210
220
 
211
221
  # Disables specific check
212
222
  #
213
- # For the list of available checks look at `lib/error_messages` module.
223
+ # For the list of available checks look at the `error_messages.rb` file.
214
224
  #
215
225
  # @param name [Symbol] check name
216
226
  # @return [void]
@@ -221,7 +231,7 @@ module OnlineMigrations
221
231
 
222
232
  # Test whether specific check is enabled
223
233
  #
224
- # For the list of available checks look at `lib/error_messages` module.
234
+ # For the list of available checks look at the `error_messages.rb` file.
225
235
  #
226
236
  # @param name [Symbol] check name
227
237
  # @param version [Integer] migration version
@@ -260,12 +270,6 @@ module OnlineMigrations
260
270
  end
261
271
 
262
272
  private
263
- def ensure_supports_multiple_dbs
264
- if !Utils.supports_multiple_dbs?
265
- raise "OnlineMigrations does not support multiple databases for Active Record < 6.1"
266
- end
267
- end
268
-
269
273
  def db_config_name
270
274
  connection = OnlineMigrations.current_migration.connection
271
275
  connection.pool.db_config.name
@@ -18,12 +18,12 @@ module OnlineMigrations
18
18
  "trigger_#{hashed_identifier}"
19
19
  end
20
20
 
21
- def create(from_columns, to_columns)
21
+ def create(from_columns, to_columns, type_cast_functions: {})
22
22
  from_columns, to_columns = normalize_column_names(from_columns, to_columns)
23
23
  trigger_name = name(from_columns, to_columns)
24
- assignment_clauses = assignment_clauses_for_columns(from_columns, to_columns)
24
+ assignment_clauses = assignment_clauses_for_columns(from_columns, to_columns, type_cast_functions)
25
25
 
26
- connection.execute(<<-SQL.strip_heredoc)
26
+ connection.execute(<<~SQL)
27
27
  CREATE OR REPLACE FUNCTION #{trigger_name}() RETURNS TRIGGER AS $$
28
28
  BEGIN
29
29
  #{assignment_clauses};
@@ -32,11 +32,11 @@ module OnlineMigrations
32
32
  $$ LANGUAGE plpgsql;
33
33
  SQL
34
34
 
35
- connection.execute(<<-SQL.strip_heredoc)
35
+ connection.execute(<<~SQL)
36
36
  DROP TRIGGER IF EXISTS #{trigger_name} ON #{quoted_table_name}
37
37
  SQL
38
38
 
39
- connection.execute(<<-SQL.strip_heredoc)
39
+ connection.execute(<<~SQL)
40
40
  CREATE TRIGGER #{trigger_name}
41
41
  BEFORE INSERT OR UPDATE
42
42
  ON #{quoted_table_name}
@@ -75,14 +75,19 @@ module OnlineMigrations
75
75
  [from_columns, to_columns]
76
76
  end
77
77
 
78
- def assignment_clauses_for_columns(from_columns, to_columns)
78
+ def assignment_clauses_for_columns(from_columns, to_columns, type_cast_functions)
79
79
  combined_column_names = to_columns.zip(from_columns)
80
80
 
81
81
  assignment_clauses = combined_column_names.map do |(new_name, old_name)|
82
- new_name = connection.quote_column_name(new_name)
83
- old_name = connection.quote_column_name(old_name)
84
-
85
- "NEW.#{new_name} := NEW.#{old_name}"
82
+ quoted_new_name = connection.quote_column_name(new_name)
83
+ quoted_old_name = connection.quote_column_name(old_name)
84
+ type_cast_function = type_cast_functions[old_name]
85
+
86
+ if type_cast_function
87
+ "NEW.#{quoted_new_name} := #{type_cast_function.gsub(old_name.to_s, "NEW.#{quoted_old_name}")}"
88
+ else
89
+ "NEW.#{quoted_new_name} := NEW.#{quoted_old_name}"
90
+ end
86
91
  end
87
92
 
88
93
  assignment_clauses.join(";\n ")
@@ -109,14 +109,8 @@ A safer approach is to:
109
109
 
110
110
  1. ignore the column:
111
111
 
112
- class <%= model %> < <%= model_parent %>
113
- <% if ar_version >= 5 %>
112
+ class <%= model %> < ApplicationRecord
114
113
  self.ignored_columns = [\"<%= column_name %>\"]
115
- <% else %>
116
- def self.columns
117
- super.reject { |c| c.name == \"<%= column_name %>\" }
118
- end
119
- <% end %>
120
114
  end
121
115
 
122
116
  2. deploy
@@ -157,13 +151,7 @@ It will use a combination of a VIEW and column aliasing to work with both column
157
151
  <% if enumerate_columns_in_select_statements %>
158
152
  5. Ignore old column
159
153
 
160
- <% if ar_version >= 5 %>
161
154
  self.ignored_columns = [:<%= column_name %>]
162
- <% else %>
163
- def self.columns
164
- super.reject { |c| c.name == \"<%= column_name %>\" }
165
- end
166
- <% end %>
167
155
 
168
156
  6. Deploy
169
157
  7. Remove the column rename config from step 1
@@ -216,6 +204,8 @@ which will be passed to `add_column` when creating a new column, so you can over
216
204
 
217
205
  def up
218
206
  <%= backfill_code %>
207
+ # You can use `backfill_column_for_type_change_in_background` if want to
208
+ # backfill using background migrations.
219
209
  end
220
210
 
221
211
  def down
@@ -223,7 +213,10 @@ which will be passed to `add_column` when creating a new column, so you can over
223
213
  end
224
214
  end
225
215
 
226
- 3. Copy indexes, foreign keys, check constraints, NOT NULL constraint, swap new column in place:
216
+ 3. Make sure your application works with values in both formats (when read from the database, converting
217
+ during writes works automatically). For most column type changes, this does not need any updates in the app.
218
+ 4. Deploy
219
+ 5. Copy indexes, foreign keys, check constraints, NOT NULL constraint, swap new column in place:
227
220
 
228
221
  class Finalize<%= migration_name %> < <%= migration_parent %>
229
222
  disable_ddl_transaction!
@@ -233,8 +226,8 @@ which will be passed to `add_column` when creating a new column, so you can over
233
226
  end
234
227
  end
235
228
 
236
- 4. Deploy
237
- 5. Finally, if everything is working as expected, remove copy trigger and old column:
229
+ 6. Deploy
230
+ 7. Finally, if everything works as expected, remove copy trigger and old column:
238
231
 
239
232
  class Cleanup<%= migration_name %> < <%= migration_parent %>
240
233
  def up
@@ -246,7 +239,8 @@ which will be passed to `add_column` when creating a new column, so you can over
246
239
  end
247
240
  end
248
241
 
249
- 6. Deploy",
242
+ 8. Remove changes from step 3, if any
243
+ 9. Deploy",
250
244
 
251
245
  change_column_default:
252
246
  "Partial writes are enabled, which can cause incorrect values
@@ -303,14 +297,8 @@ A safer approach is to:
303
297
 
304
298
  1. Ignore the column(s):
305
299
 
306
- class <%= model %> < <%= model_parent %>
307
- <% if ar_version >= 5 %>
300
+ class <%= model %> < ApplicationRecord
308
301
  self.ignored_columns = <%= columns %>
309
- <% else %>
310
- def self.columns
311
- super.reject { |c| <%= columns %>.include?(c.name) }
312
- end
313
- <% end %>
314
302
  end
315
303
 
316
304
  2. Deploy
@@ -504,7 +492,7 @@ execute call, so cannot help you here. Make really sure that what
504
492
  you're doing is safe before proceeding, then wrap it in a safety_assured { ... } block.",
505
493
 
506
494
  multiple_foreign_keys:
507
- "Adding multiple foreign keys in a single migration blocks reads and writes on all involved tables until migration is completed.
495
+ "Adding multiple foreign keys in a single migration blocks writes on all involved tables until migration is completed.
508
496
  Avoid adding foreign key more than once per migration file, unless the source and target tables are identical.",
509
497
 
510
498
  drop_table_multiple_foreign_keys:
@@ -9,8 +9,8 @@ module OnlineMigrations
9
9
  @referenced_tables = Set.new
10
10
  end
11
11
 
12
- def collect(&table_definition)
13
- table_definition.call(self)
12
+ def collect
13
+ yield self
14
14
  end
15
15
 
16
16
  def foreign_key(to_table, **_options)
@@ -12,8 +12,8 @@ module OnlineMigrations
12
12
  @indexes = []
13
13
  end
14
14
 
15
- def collect(&table_definition)
16
- table_definition.call(self)
15
+ def collect
16
+ yield self
17
17
  end
18
18
 
19
19
  def index(_column_name, **options)
@@ -21,7 +21,7 @@ module OnlineMigrations
21
21
  end
22
22
 
23
23
  def references(*_ref_names, **options)
24
- index = options.fetch(:index) { Utils.ar_version >= 5.0 }
24
+ index = options.fetch(:index, true)
25
25
 
26
26
  if index
27
27
  using = index.is_a?(Hash) ? index[:using].to_s : nil
@@ -96,9 +96,8 @@ module OnlineMigrations
96
96
  else
97
97
  yield
98
98
  end
99
- # ActiveRecord::LockWaitTimeout can be used for Active Record 5.2+
100
- rescue ActiveRecord::StatementInvalid => e
101
- if lock_timeout_error?(e) && current_attempt <= attempts
99
+ rescue ActiveRecord::LockWaitTimeout
100
+ if current_attempt <= attempts
102
101
  current_delay = delay(current_attempt)
103
102
  Utils.say("Lock timeout. Retrying in #{current_delay} seconds...")
104
103
  sleep(current_delay)
@@ -122,10 +121,6 @@ module OnlineMigrations
122
121
  ensure
123
122
  connection.execute("SET lock_timeout TO #{connection.quote(prev_value)}")
124
123
  end
125
-
126
- def lock_timeout_error?(error)
127
- error.message.include?("canceling statement due to lock timeout")
128
- end
129
124
  end
130
125
 
131
126
  # `LockRetrier` implementation that has a constant delay between tries
@@ -181,10 +176,10 @@ module OnlineMigrations
181
176
  #
182
177
  # @example
183
178
  # # This will attempt 30 retries starting with delay of 10ms between each unsuccessful try, increasing exponentially
184
- # # up to the maximum delay of 1 minute and 50ms set as lock timeout for each try:
179
+ # # up to the maximum delay of 1 minute and 200ms set as lock timeout for each try:
185
180
  #
186
181
  # config.retrier = OnlineMigrations::ConstantLockRetrier.new(attempts: 30,
187
- # base_delay: 0.01.seconds, max_delay: 1.minute, lock_timeout: 0.05.seconds)
182
+ # base_delay: 0.01.seconds, max_delay: 1.minute, lock_timeout: 0.2.seconds)
188
183
  #
189
184
  class ExponentialLockRetrier < LockRetrier
190
185
  # LockRetrier API implementation
@@ -28,9 +28,6 @@ module OnlineMigrations
28
28
  end
29
29
 
30
30
  def indexes(table_name)
31
- # Available only in Active Record 6.0+
32
- return if !defined?(super)
33
-
34
31
  if (renamed_table = renamed_table?(table_name))
35
32
  super(renamed_table)
36
33
  elsif renamed_column?(table_name)
@@ -109,9 +106,6 @@ module OnlineMigrations
109
106
  end
110
107
 
111
108
  def indexes(connection, table_name)
112
- # Available only in Active Record 6.0+
113
- return if !defined?(super)
114
-
115
109
  if (renamed_table = renamed_table?(connection, table_name))
116
110
  super(connection, renamed_table)
117
111
  elsif renamed_column?(connection, table_name)
@@ -0,0 +1,21 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "delegate"
4
+
5
+ module OnlineMigrations
6
+ module SchemaDumper
7
+ def initialize(connection, options = {})
8
+ if OnlineMigrations.config.alphabetize_schema
9
+ connection = WrappedConnection.new(connection)
10
+ end
11
+
12
+ super
13
+ end
14
+ end
15
+
16
+ class WrappedConnection < SimpleDelegator
17
+ def columns(table_name)
18
+ super.sort_by(&:name)
19
+ end
20
+ end
21
+ end