online_migrations 0.27.1 → 0.29.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: 8a3658cc979ead6b8dea71140f3ba95b0f7a4a4c26a27891f49a5b6925c94e59
4
- data.tar.gz: 7d3ab5e09422e28078543ac67fa0ecae1a7afe9d8e29f67e55d6b772fb708e50
3
+ metadata.gz: babb68ddc337cbc52cb3a10d842dfb427031544fa8b32716b9b0166996f45252
4
+ data.tar.gz: '08e334985d6ecbe603831ad44a9b535d3e39ee1b7f17bb576fc035b9de11c84c'
5
5
  SHA512:
6
- metadata.gz: 43ae3f3ebffa5491d4ac9b99b987b048702b9142e9a4ae49182b705b9c81a562acbea6dc1580ef76e49a9124adf2d662723451b6d03003f10f05c1d39ff4a33e
7
- data.tar.gz: 5793df9ffd71454f2edb251d079439c5dfb0cfe212a30360654cbb5622c23fcd47a63e2a8cdd6b8d4df2bd7d3b39ec4d29eede4a99519264075e65956bdd1b55
6
+ metadata.gz: ca383bfa73dd712076f3ac5ade02767955a7da7ec99306258d70a61b32331226fc22f4a84fea3f37423e7ebce2e805b514077bd13fcee1fcb09ae3ac637ac53f
7
+ data.tar.gz: 470a1d00e16f00b70a248c0ea2e0a3acfcb3e05b165fe5d12fa966c3f03d2a40c953fe4dd9265d210c330fca0b2536143716a47adfb9f1a8749d3348183e4959
data/CHANGELOG.md CHANGED
@@ -1,5 +1,18 @@
1
1
  ## master (unreleased)
2
2
 
3
+ ## 0.29.0 (2025-07-10)
4
+
5
+ - Add `iteration_pause` column to background data migrations
6
+
7
+ Note: Run `bin/rails generate online_migrations:upgrade` if using background data migrations.
8
+ Make sure all data migrations finished before applying this migration or change its timestamp
9
+ to be older than all pending db migrations which will enqueue data migrations.
10
+
11
+ ## 0.28.0 (2025-06-26)
12
+
13
+ - Support renaming columns and tables for tables within custom schemas
14
+ - Include operation name into the background schema migrations names
15
+
3
16
  ## 0.27.1 (2025-05-08)
4
17
 
5
18
  - Fix background data migrations to enumerate using the correct shard
@@ -0,0 +1,7 @@
1
+ class BackgroundDataMigrationsAddIterationPause < <%= migration_parent %>
2
+ def change
3
+ safety_assured("Table is small") do
4
+ add_column :background_data_migrations, :iteration_pause, :float, default: 0.0, null: false
5
+ end
6
+ end
7
+ end
@@ -110,6 +110,9 @@ OnlineMigrations.configure do |config|
110
110
  # The number of seconds that must pass before the cancelling or pausing data migration is considered stuck.
111
111
  config.background_data_migrations.stuck_timeout = 5.minutes
112
112
 
113
+ # The pause interval between each data migration's `process` method execution (in seconds).
114
+ config.background_data_migrations.iteration_pause = Rails.env.production? ? 0.02 : 0
115
+
113
116
  # The callback to perform when an error occurs during the data migration.
114
117
  # config.background_data_migrations.error_handler = ->(error, errored_migration) do
115
118
  # Bugsnag.notify(error) do |notification|
@@ -13,6 +13,7 @@ class InstallOnlineMigrations < <%= migration_parent %>
13
13
  t.bigint :tick_count, default: 0, null: false
14
14
  t.float :time_running, default: 0.0, null: false
15
15
  t.integer :max_attempts, null: false
16
+ t.float :iteration_pause, default: 0.0, null: false
16
17
  t.string :error_class
17
18
  t.string :error_message
18
19
  t.string :backtrace, array: true
@@ -19,33 +19,34 @@ module OnlineMigrations
19
19
  private
20
20
  def migrations_to_apply
21
21
  connection = BackgroundDataMigrations::Migration.connection
22
- data_table = "background_migrations"
23
- schema_table = "background_schema_migrations"
24
- columns = connection.columns(data_table).map(&:name)
25
22
 
26
23
  migrations = []
27
- if connection.table_exists?(data_table) && !columns.include?("shard")
24
+ if connection.table_exists?(:background_migrations) && !connection.column_exists?(:background_migrations, :shard)
28
25
  migrations << "add_sharding_to_online_migrations"
29
26
  end
30
27
 
31
- if !connection.table_exists?(schema_table)
28
+ if !connection.table_exists?(:background_schema_migrations)
32
29
  migrations << "create_background_schema_migrations"
33
30
  end
34
31
 
35
- indexes = connection.indexes(schema_table)
32
+ indexes = connection.indexes(:background_schema_migrations)
36
33
  unique_index = indexes.find { |i| i.unique && i.columns.sort == ["connection_class_name", "migration_name", "shard"] }
37
34
  if !unique_index
38
35
  migrations << "background_schema_migrations_change_unique_index"
39
36
  end
40
37
 
41
- if connection.table_exists?(data_table) && !connection.column_exists?(data_table, :started_at)
38
+ if connection.table_exists?(:background_migrations) && !connection.column_exists?(:background_migrations, :started_at)
42
39
  migrations << "add_timestamps_to_background_migrations"
43
40
  end
44
41
 
45
- if connection.table_exists?(data_table)
42
+ if connection.table_exists?(:background_migrations)
46
43
  migrations << "change_background_data_migrations"
47
44
  end
48
45
 
46
+ if !connection.column_exists?(:background_data_migrations, :iteration_pause)
47
+ migrations << "background_data_migrations_add_iteration_pause"
48
+ end
49
+
49
50
  migrations
50
51
  end
51
52
 
@@ -250,7 +250,7 @@ module OnlineMigrations
250
250
  def progress
251
251
  if succeeded?
252
252
  100.0
253
- elsif enqueued? || tick_total == 0
253
+ elsif tick_total == 0
254
254
  0.0
255
255
  elsif tick_total
256
256
  ([tick_count.to_f / tick_total, 1.0].min * 100)
@@ -297,6 +297,8 @@ module OnlineMigrations
297
297
  self.tick_total ||= on_shard_if_present do
298
298
  data_migration.count
299
299
  end
300
+
301
+ self.iteration_pause ||= config.iteration_pause
300
302
  end
301
303
 
302
304
  def instrument_status_change
@@ -369,7 +369,7 @@ module OnlineMigrations
369
369
  # in development and test environments
370
370
  #
371
371
  def enqueue_background_data_migration(migration_name, *arguments, **options)
372
- options.assert_valid_keys(:max_attempts, :connection_class_name)
372
+ options.assert_valid_keys(:max_attempts, :iteration_pause, :connection_class_name)
373
373
 
374
374
  migration_name = migration_name.name if migration_name.is_a?(Class)
375
375
  options[:connection_class_name] ||= compute_connection_class_name(migration_name, arguments)
@@ -387,7 +387,7 @@ module OnlineMigrations
387
387
  migration = Migration.where(migration_name: migration_name, shard: shard).where("arguments = ?", arguments.to_json).first
388
388
  migration ||= Migration.create!(**options, migration_name: migration_name, arguments: arguments, shard: shard)
389
389
 
390
- if Utils.run_background_migrations_inline?
390
+ if Utils.run_background_migrations_inline? && !migration.succeeded?
391
391
  job = OnlineMigrations.config.background_data_migrations.job
392
392
  job.constantize.perform_inline(migration.id)
393
393
  end
@@ -128,7 +128,8 @@ module OnlineMigrations
128
128
  @data_migration.around_process do
129
129
  @migration.data_migration.process(item)
130
130
 
131
- pause = OnlineMigrations.config.background_data_migrations.iteration_pause
131
+ # Migration is refreshed regularly by ticker.
132
+ pause = @migration.iteration_pause
132
133
  sleep(pause) if pause > 0
133
134
  end
134
135
  @ticker.tick
@@ -28,7 +28,7 @@ module OnlineMigrations
28
28
  schema_creation = ActiveRecord::ConnectionAdapters::PostgreSQL::SchemaCreation.new(self)
29
29
  definition = schema_creation.accept(create_index)
30
30
 
31
- enqueue_background_schema_migration(index.name, table_name, definition: definition, **migration_options)
31
+ enqueue_background_schema_migration("Add index #{index.name}", table_name, definition: definition, **migration_options)
32
32
  end
33
33
 
34
34
  def remove_index_in_background(table_name, column_name = nil, name:, **options)
@@ -42,7 +42,7 @@ module OnlineMigrations
42
42
  end
43
43
 
44
44
  definition = "DROP INDEX CONCURRENTLY IF EXISTS #{quote_column_name(name)}"
45
- enqueue_background_schema_migration(name, table_name, definition: definition, **migration_options)
45
+ enqueue_background_schema_migration("Remove index #{name}", table_name, definition: definition, **migration_options)
46
46
  end
47
47
 
48
48
  def validate_foreign_key_in_background(from_table, to_table = nil, **options)
@@ -62,7 +62,7 @@ module OnlineMigrations
62
62
  ALTER TABLE #{quote_table_name(table_name)}
63
63
  VALIDATE CONSTRAINT #{quote_table_name(constraint_name)}
64
64
  SQL
65
- enqueue_background_schema_migration(constraint_name, table_name, definition: definition, **options)
65
+ enqueue_background_schema_migration("Validate #{constraint_name}", table_name, definition: definition, **options)
66
66
  end
67
67
 
68
68
  # Ensures that the background schema migration with the provided migration name succeeded.
@@ -78,7 +78,7 @@ 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
- migrations = Migration.where(migration_name: migration_name).to_a
81
+ migrations = Migration.where("migration_name ILIKE ?", "%#{migration_name}%").to_a
82
82
 
83
83
  if migrations.empty?
84
84
  Utils.raise_in_prod_or_say_in_dev("Could not find background schema migration(s): '#{migration_name}'.")
@@ -97,12 +97,13 @@ module OnlineMigrations
97
97
  if connection_class_name
98
98
  klass = connection_class_name.constantize
99
99
  connection_class = Utils.find_connection_class(klass)
100
- # Normalize to the real connection class name.
101
- connection_class_name = connection_class.name
102
100
  else
103
101
  connection_class = ActiveRecord::Base
104
102
  end
105
103
 
104
+ # Normalize to the real connection class name.
105
+ connection_class_name = connection_class.name
106
+
106
107
  shards = Utils.shard_names(connection_class)
107
108
  shards = [nil] if shards.size == 1
108
109
 
@@ -111,6 +112,9 @@ module OnlineMigrations
111
112
  .find_or_create_by!(migration_name: migration_name, shard: shard, connection_class_name: connection_class_name)
112
113
 
113
114
  if Utils.run_background_migrations_inline?
115
+ # Run migration again in development.
116
+ migration.update_column(:status, :enqueued) if !migration.enqueued?
117
+
114
118
  runner = MigrationRunner.new(migration)
115
119
  runner.run
116
120
  end
@@ -52,15 +52,14 @@ module OnlineMigrations
52
52
  private
53
53
  def renamed_table?(connection, table_name)
54
54
  table_renames = OnlineMigrations.config.table_renames
55
- if table_renames.key?(table_name)
56
- views = connection.views
57
- table_renames[table_name] if views.include?(table_name)
55
+ if table_renames.key?(table_name) && connection.view_exists?(table_name)
56
+ table_renames[table_name]
58
57
  end
59
58
  end
60
59
 
61
60
  def renamed_column?(connection, table_name)
62
61
  column_renames = OnlineMigrations.config.column_renames
63
- column_renames.key?(table_name) && connection.views.include?(table_name)
62
+ column_renames.key?(table_name) && connection.view_exists?(table_name)
64
63
  end
65
64
 
66
65
  def column_rename_table(table_name)
@@ -131,14 +130,21 @@ module OnlineMigrations
131
130
  def renamed_table?(pool, table_name)
132
131
  table_renames = OnlineMigrations.config.table_renames
133
132
  if table_renames.key?(table_name)
134
- views = pool.with_connection(&:views)
135
- table_renames[table_name] if views.include?(table_name)
133
+ view_exists = pool.with_connection do |connection|
134
+ connection.view_exists?(table_name)
135
+ end
136
+
137
+ table_renames[table_name] if view_exists
136
138
  end
137
139
  end
138
140
 
139
141
  def renamed_column?(pool, table_name)
140
142
  column_renames = OnlineMigrations.config.column_renames
141
- column_renames.key?(table_name) && pool.with_connection(&:views).include?(table_name)
143
+ return false if !column_renames.key?(table_name)
144
+
145
+ pool.with_connection do |connection|
146
+ connection.view_exists?(table_name)
147
+ end
142
148
  end
143
149
 
144
150
  def column_rename_table(table_name)
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module OnlineMigrations
4
- VERSION = "0.27.1"
4
+ VERSION = "0.29.0"
5
5
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: online_migrations
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.27.1
4
+ version: 0.29.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - fatkodima
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2025-05-08 00:00:00.000000000 Z
11
+ date: 2025-07-10 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activerecord
@@ -42,6 +42,7 @@ files:
42
42
  - lib/generators/online_migrations/install_generator.rb
43
43
  - lib/generators/online_migrations/templates/add_sharding_to_online_migrations.rb.tt
44
44
  - lib/generators/online_migrations/templates/add_timestamps_to_background_migrations.rb.tt
45
+ - lib/generators/online_migrations/templates/background_data_migrations_add_iteration_pause.rb.tt
45
46
  - lib/generators/online_migrations/templates/background_schema_migrations_change_unique_index.rb.tt
46
47
  - lib/generators/online_migrations/templates/change_background_data_migrations.rb.tt
47
48
  - lib/generators/online_migrations/templates/create_background_schema_migrations.rb.tt