online_migrations 0.34.0 → 0.35.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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: b7f1dd28a4540742f77c2a33d7336f4f6f2b12a4f267a1cc556975f7b7122ba4
4
- data.tar.gz: db448dca841dc39418cd92e5d86108ee0b59b69b72eeda391b0a81a30aa7ce71
3
+ metadata.gz: 5a84bdeb495a3d25432c6ae97d4c46777517ae3bbcf01359020d400ad8f16f30
4
+ data.tar.gz: 935d3b38b096fcdb222a89065d7c10023a8b85a6009065d73a1df9bb92c2b0f8
5
5
  SHA512:
6
- metadata.gz: b423580e44eefc2f1ff71b2ebdcd15e4ff449f869231aaebe38c4598dc364d817eb3b484a82b7a0cc493727f94d956610ced75ac409092227c2e5cc727935eee
7
- data.tar.gz: 3287e23ec07a09a1026790ab4747427eafbc70f675fd2cbf7382f302a86f748c3fde45f5a7546b3e66848cd1e12eb86f073ff3e5461471bb35ba79f5b0a160ec
6
+ metadata.gz: f2c578a25081bb86cafc2851cb818c40e819363d40bdac2a6f4cde634f71a8dc3a90909e4dbf10bf0fb6df61e1d979f4d581982650eec3834715baa8e5888832
7
+ data.tar.gz: 01377b6bcdbe0b601a45b53a52a99746e443d60c5ca513e2312322c81718e4a995fde32ee78a45554fe155878391f6a885fdef50bdea4d4619a4e10799bab295
data/CHANGELOG.md CHANGED
@@ -1,5 +1,11 @@
1
1
  ## master (unreleased)
2
2
 
3
+ ## 0.35.0 (2026-07-01)
4
+
5
+ - Drop support for Ruby < 3.3
6
+ - Defer `tick_total` calculation to the background data migration starting time instead of enqueueing time
7
+ - Make "replacing an index" check independent of the live database state
8
+
3
9
  ## 0.34.0 (2026-05-29)
4
10
 
5
11
  - Drop support for Rails < 7.2
data/README.md CHANGED
@@ -16,7 +16,7 @@ See [comparison to `strong_migrations`](#comparison-to-strong_migrations)
16
16
 
17
17
  ## Requirements
18
18
 
19
- - Ruby 3.1+
19
+ - Ruby 3.3+
20
20
  - Rails 7.2+
21
21
  - PostgreSQL 12+
22
22
 
@@ -48,7 +48,7 @@ module OnlineMigrations
48
48
  end
49
49
 
50
50
  iteration_pause_column = connection.columns(:background_data_migrations).find { |c| c.name == "iteration_pause" }
51
- if iteration_pause_column && iteration_pause_column.default
51
+ if iteration_pause_column&.default
52
52
  migrations << "background_data_migrations_remove_iteration_pause_default"
53
53
  end
54
54
 
@@ -123,7 +123,13 @@ module OnlineMigrations
123
123
  # @private
124
124
  def start
125
125
  if enqueued?
126
- update!(status: :running, started_at: Time.current)
126
+ # Defer `tick_total` calculation to the migration starting time
127
+ # instead of the enqueueing time to avoid failed deploys.
128
+ self.tick_total ||= safely_calculate_tick_total
129
+ self.status = :running
130
+ self.started_at = Time.current
131
+ save!
132
+
127
133
  data_migration.after_start
128
134
  true
129
135
  else
@@ -309,11 +315,20 @@ module OnlineMigrations
309
315
  def set_defaults
310
316
  config = ::OnlineMigrations.config.background_data_migrations
311
317
  self.max_attempts ||= config.max_attempts
312
- self.tick_total ||= on_shard_if_present do
318
+ self.iteration_pause ||= config.iteration_pause
319
+ end
320
+
321
+ def safely_calculate_tick_total
322
+ on_shard_if_present do
313
323
  data_migration.count
314
324
  end
325
+ rescue ActiveRecord::QueryCanceled => e
326
+ OnlineMigrations.config.background_data_migrations.error_handler.call(e, self)
315
327
 
316
- self.iteration_pause ||= config.iteration_pause
328
+ # `tick_total` is not required and is used only for progress tracking.
329
+ # Probably the `count` method was implemented in a non-efficient way.
330
+ # Better to not track progress than have a failing migration.
331
+ nil
317
332
  end
318
333
 
319
334
  def instrument_status_change
@@ -212,7 +212,14 @@ module OnlineMigrations
212
212
  conversions = column_names.map do |column_name|
213
213
  tmp_column = __change_type_column(column_name)
214
214
 
215
- old_value = Arel::Table.new(table_name)[column_name]
215
+ old_value = begin
216
+ # Delete after supporting only ActiveRecord >= 8.2
217
+ Arel::Table.new(table_name)[column_name]
218
+ rescue ArgumentError
219
+ # https://github.com/rails/rails/commit/b1650993b02497ae7d0d8b984d40bc036e62c681
220
+ Arel::Table.new(name: table_name)[column_name]
221
+ end
222
+
216
223
  if (type_cast_function = type_cast_functions.with_indifferent_access[column_name])
217
224
  old_value =
218
225
  case type_cast_function
@@ -393,10 +400,8 @@ module OnlineMigrations
393
400
  def __options_from_column(column, options)
394
401
  result = {}
395
402
  options.each do |option|
396
- if column.respond_to?(option)
397
- value = column.public_send(option)
398
- result[option] = value if !value.nil?
399
- end
403
+ value = column.public_send(option)
404
+ result[option] = value if !value.nil?
400
405
  end
401
406
  result
402
407
  end
@@ -1,7 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require "erb"
4
- require "set"
5
4
 
6
5
  module OnlineMigrations
7
6
  # @private
@@ -46,7 +45,7 @@ module OnlineMigrations
46
45
 
47
46
  true
48
47
  end
49
- ruby2_keywords(:check) if respond_to?(:ruby2_keywords, true)
48
+ ruby2_keywords(:check)
50
49
 
51
50
  def version_safe?
52
51
  version && version <= OnlineMigrations.config.start_after
@@ -574,13 +573,21 @@ module OnlineMigrations
574
573
  Array(index.columns).map(&:to_s) == Array(options[:column]).map(&:to_s)
575
574
  end
576
575
 
577
- if index_def
578
- existing_options = [:name, :columns, :unique, :where, :type, :using, :opclasses].filter_map do |option|
579
- [option, index_def.public_send(option)] if index_def.respond_to?(option)
580
- end.to_h
576
+ # IndexDefinition expects "columns", but "remove_index" API uses "column".
577
+ options[:columns] = options[:column]
581
578
 
582
- @removed_indexes << IndexDefinition.new(table: table_name, **existing_options)
583
- end
579
+ # Fall back to the declared options when the index isn't present in the database,
580
+ # so an index remove → add is still recorded and caught.
581
+ existing_options =
582
+ [:name, :columns, :unique, :where, :type, :using, :opclasses].index_with do |option|
583
+ if index_def
584
+ index_def.public_send(option)
585
+ else
586
+ options[option]
587
+ end
588
+ end.compact
589
+
590
+ @removed_indexes << IndexDefinition.new(table: table_name, **existing_options)
584
591
  end
585
592
 
586
593
  def add_foreign_key(from_table, to_table, **options)
@@ -844,8 +851,8 @@ module OnlineMigrations
844
851
 
845
852
  def index_include_column?(index, column)
846
853
  index.columns.include?(column) ||
847
- (index.include && index.include.include?(column)) ||
848
- (index.where && index.where.include?(column))
854
+ index.include&.include?(column) ||
855
+ index.where&.include?(column)
849
856
  end
850
857
 
851
858
  def run_custom_checks(method, args)
@@ -48,7 +48,7 @@ module OnlineMigrations
48
48
  record(:"#{method}", args, &block) # record(:create_table, args, &block)
49
49
  end # end
50
50
  RUBY
51
- ruby2_keywords(method) if respond_to?(:ruby2_keywords, true)
51
+ ruby2_keywords(method)
52
52
  end
53
53
 
54
54
  private
@@ -3,10 +3,11 @@
3
3
  module OnlineMigrations
4
4
  # @private
5
5
  class IndexDefinition
6
- attr_reader :table, :columns, :unique, :opclasses, :where, :type, :using
6
+ attr_reader :table, :name, :columns, :unique, :opclasses, :where, :type, :using
7
7
 
8
8
  def initialize(**options)
9
- @table = options[:table]
9
+ @table = options[:table]&.to_s
10
+ @name = options[:name]&.to_s
10
11
  @columns = Array(options[:columns]).map(&:to_s)
11
12
  @unique = options[:unique]
12
13
  @opclasses = options[:opclass] || {}
@@ -20,7 +21,7 @@ module OnlineMigrations
20
21
  # For ActiveRecord::ConnectionAdapters::IndexDefinition is for expression indexes,
21
22
  # `columns` is a string
22
23
  table == other.table &&
23
- columns.intersect?(Array(other.columns))
24
+ ((name && name == other.name) || columns.intersect?(Array(other.columns)))
24
25
  end
25
26
 
26
27
  # @param other [OnlineMigrations::IndexDefinition, ActiveRecord::ConnectionAdapters::IndexDefinition]
@@ -28,7 +29,7 @@ module OnlineMigrations
28
29
  return false if type != other.type
29
30
  return false if using != other.using
30
31
  return false if where != other.where
31
- return false if other.respond_to?(:opclasses) && opclasses != other.opclasses
32
+ return false if opclasses != other.opclasses
32
33
 
33
34
  if unique && !other.unique
34
35
  false
@@ -29,7 +29,7 @@ module OnlineMigrations
29
29
  end
30
30
  end
31
31
  end
32
- ruby2_keywords(:method_missing) if respond_to?(:ruby2_keywords, true)
32
+ ruby2_keywords(:method_missing)
33
33
 
34
34
  # @private
35
35
  def revert(*args)
@@ -111,7 +111,7 @@ module OnlineMigrations
111
111
 
112
112
  relation.update_all(updates)
113
113
 
114
- progress.call(relation) if progress
114
+ progress.call(relation) if progress.present?
115
115
 
116
116
  sleep(pause_ms * 0.001) if pause_ms > 0
117
117
  end
@@ -635,7 +635,7 @@ module OnlineMigrations
635
635
  # @see https://api.rubyonrails.org/v8.1/classes/ActiveRecord/ConnectionAdapters/SchemaStatements.html#method-i-add_column
636
636
  #
637
637
  def add_column(table_name, column_name, type, **options)
638
- if column_exists?(table_name, column_name, type, **options)
638
+ if column_exists?(table_name, column_name)
639
639
  Utils.say("Column was not added because it already exists (this may be due to an aborted migration " \
640
640
  "or similar) table_name: #{table_name}, column_name: #{column_name}")
641
641
  else
@@ -145,7 +145,7 @@ module OnlineMigrations
145
145
 
146
146
  def run_background_migrations_inline?
147
147
  run_inline = OnlineMigrations.config.run_background_migrations_inline
148
- run_inline && run_inline.call
148
+ run_inline.call if run_inline.present?
149
149
  end
150
150
  end
151
151
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module OnlineMigrations
4
- VERSION = "0.34.0"
4
+ VERSION = "0.35.0"
5
5
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: online_migrations
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.34.0
4
+ version: 0.35.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - fatkodima
@@ -108,14 +108,14 @@ required_ruby_version: !ruby/object:Gem::Requirement
108
108
  requirements:
109
109
  - - ">="
110
110
  - !ruby/object:Gem::Version
111
- version: '3.1'
111
+ version: '3.3'
112
112
  required_rubygems_version: !ruby/object:Gem::Requirement
113
113
  requirements:
114
114
  - - ">="
115
115
  - !ruby/object:Gem::Version
116
116
  version: '0'
117
117
  requirements: []
118
- rubygems_version: 4.0.10
118
+ rubygems_version: 4.0.14
119
119
  specification_version: 4
120
120
  summary: Catch unsafe PostgreSQL migrations in development and run them easier in
121
121
  production