online_migrations 0.33.2 → 0.34.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 +4 -4
- data/CHANGELOG.md +19 -1
- data/README.md +1 -1
- data/lib/online_migrations/background_data_migrations/migration_helpers.rb +12 -3
- data/lib/online_migrations/background_data_migrations/scheduler.rb +12 -1
- data/lib/online_migrations/background_schema_migrations/migration.rb +2 -6
- data/lib/online_migrations/background_schema_migrations/migration_helpers.rb +13 -6
- data/lib/online_migrations/data_migration.rb +2 -1
- data/lib/online_migrations/schema_cache.rb +0 -77
- data/lib/online_migrations/schema_statements.rb +2 -7
- data/lib/online_migrations/version.rb +1 -1
- data/lib/online_migrations.rb +1 -6
- metadata +4 -4
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: b7f1dd28a4540742f77c2a33d7336f4f6f2b12a4f267a1cc556975f7b7122ba4
|
|
4
|
+
data.tar.gz: db448dca841dc39418cd92e5d86108ee0b59b69b72eeda391b0a81a30aa7ce71
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: b423580e44eefc2f1ff71b2ebdcd15e4ff449f869231aaebe38c4598dc364d817eb3b484a82b7a0cc493727f94d956610ced75ac409092227c2e5cc727935eee
|
|
7
|
+
data.tar.gz: 3287e23ec07a09a1026790ab4747427eafbc70f675fd2cbf7382f302a86f748c3fde45f5a7546b3e66848cd1e12eb86f073ff3e5461471bb35ba79f5b0a160ec
|
data/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,23 @@
|
|
|
1
1
|
## master (unreleased)
|
|
2
2
|
|
|
3
|
+
## 0.34.0 (2026-05-29)
|
|
4
|
+
|
|
5
|
+
- Drop support for Rails < 7.2
|
|
6
|
+
|
|
7
|
+
- Do not schedule background data migrations with non existent data migration classes
|
|
8
|
+
|
|
9
|
+
This prevents a situation when the background data migration was enqueued and started running
|
|
10
|
+
before the deploy completes and the data migration class is available.
|
|
11
|
+
|
|
12
|
+
- Add ability to enqueue background migrations for specific shards (if using sharding)
|
|
13
|
+
|
|
14
|
+
```ruby
|
|
15
|
+
add_index_in_background(:users, :name, connection_class_name: "ApplicationRecord", shard: :shard_one)
|
|
16
|
+
enqueue_background_data_migration("MyMigration", shard: :shard_one)
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
- Fix validating foreign key in `add_reference_concurrently` when multiple foreign keys target the same table
|
|
20
|
+
|
|
3
21
|
## 0.33.2 (2026-03-16)
|
|
4
22
|
|
|
5
23
|
- Fix message for adding to `ignored_columns` when renaming a column
|
|
@@ -22,7 +40,7 @@
|
|
|
22
40
|
- Add ability to create delayed background migrations
|
|
23
41
|
|
|
24
42
|
```ruby
|
|
25
|
-
add_index_in_background(:users, :name, delay: true)
|
|
43
|
+
add_index_in_background(:users, :name, connection_class_name: "ApplicationRecord", delay: true)
|
|
26
44
|
enqueue_background_data_migration("MyMigration", delay: true)
|
|
27
45
|
```
|
|
28
46
|
|
data/README.md
CHANGED
|
@@ -339,6 +339,7 @@ module OnlineMigrations
|
|
|
339
339
|
# @param migration_name [String, Class] Background migration class name
|
|
340
340
|
# @param arguments [Array] Extra arguments to pass to the migration instance when the migration runs
|
|
341
341
|
# @param delay [Boolean] Whether this migration should be delayed and approved by the user to start running.
|
|
342
|
+
# @param shard [String, Symbol] Specific shard this migration will be enqueued for. Defaults to all shards.
|
|
342
343
|
# @option options [Integer] :max_attempts (5) Maximum number of batch run attempts
|
|
343
344
|
# @option options [String, nil] :connection_class_name Class name to use to get connections
|
|
344
345
|
#
|
|
@@ -369,7 +370,7 @@ module OnlineMigrations
|
|
|
369
370
|
# @note For convenience, the enqueued background data migration is run inline
|
|
370
371
|
# in development and test environments
|
|
371
372
|
#
|
|
372
|
-
def enqueue_background_data_migration(migration_name, *arguments, delay: false, **options)
|
|
373
|
+
def enqueue_background_data_migration(migration_name, *arguments, delay: false, shard: nil, **options)
|
|
373
374
|
options.assert_valid_keys(:max_attempts, :iteration_pause, :connection_class_name)
|
|
374
375
|
|
|
375
376
|
migration_name = migration_name.name if migration_name.is_a?(Class)
|
|
@@ -380,8 +381,16 @@ module OnlineMigrations
|
|
|
380
381
|
end
|
|
381
382
|
|
|
382
383
|
connection_class = options[:connection_class_name].constantize
|
|
383
|
-
shards = Utils.shard_names(connection_class)
|
|
384
|
-
|
|
384
|
+
shards = Utils.shard_names(connection_class).map(&:to_s)
|
|
385
|
+
if shards.size == 1
|
|
386
|
+
shards = [nil]
|
|
387
|
+
elsif shard
|
|
388
|
+
shard = shard.to_s
|
|
389
|
+
raise "Unknown shard: #{shard}" if !shards.include?(shard)
|
|
390
|
+
|
|
391
|
+
shards = [shard]
|
|
392
|
+
end
|
|
393
|
+
|
|
385
394
|
status = delay ? :delayed : :pending
|
|
386
395
|
|
|
387
396
|
shards.each do |shard|
|
|
@@ -37,7 +37,7 @@ module OnlineMigrations
|
|
|
37
37
|
|
|
38
38
|
with_lock do
|
|
39
39
|
stuck_migrations, active_migrations = relation.running.partition(&:stuck?)
|
|
40
|
-
runnable_migrations = relation.pending + stuck_migrations
|
|
40
|
+
runnable_migrations = migrations_with_existing_classes(relation.pending) + stuck_migrations
|
|
41
41
|
|
|
42
42
|
# Ensure no more than 'concurrency' migrations are running at the same time.
|
|
43
43
|
remaining_to_enqueue = concurrency - active_migrations.count
|
|
@@ -71,6 +71,17 @@ module OnlineMigrations
|
|
|
71
71
|
end
|
|
72
72
|
end
|
|
73
73
|
|
|
74
|
+
def migrations_with_existing_classes(migrations)
|
|
75
|
+
migrations.select do |migration|
|
|
76
|
+
# Detect if the data migration class exists.
|
|
77
|
+
# It may not yet exist if the data migration was enqueued before the deploy finished.
|
|
78
|
+
migration.data_migration
|
|
79
|
+
true
|
|
80
|
+
rescue DataMigration::NotFoundError
|
|
81
|
+
false
|
|
82
|
+
end
|
|
83
|
+
end
|
|
84
|
+
|
|
74
85
|
def enqueue_migration(migration)
|
|
75
86
|
job = OnlineMigrations.config.background_data_migrations.job
|
|
76
87
|
job_class = job.constantize
|
|
@@ -208,12 +208,8 @@ module OnlineMigrations
|
|
|
208
208
|
|
|
209
209
|
# Extension point, do not remove this method.
|
|
210
210
|
def with_connection(&block)
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
connection_class.with_connection(&block)
|
|
214
|
-
else
|
|
215
|
-
yield connection_class.connection
|
|
216
|
-
end
|
|
211
|
+
# https://github.com/rails/rails/pull/51083
|
|
212
|
+
connection_class.with_connection(&block)
|
|
217
213
|
end
|
|
218
214
|
|
|
219
215
|
def with_statement_timeout(connection, timeout)
|
|
@@ -4,7 +4,7 @@ module OnlineMigrations
|
|
|
4
4
|
module BackgroundSchemaMigrations
|
|
5
5
|
module MigrationHelpers
|
|
6
6
|
def add_index_in_background(table_name, column_name, **options)
|
|
7
|
-
migration_options = options.extract!(:max_attempts, :statement_timeout, :connection_class_name, :delay)
|
|
7
|
+
migration_options = options.extract!(:max_attempts, :statement_timeout, :connection_class_name, :delay, :shard)
|
|
8
8
|
|
|
9
9
|
options[:algorithm] = :concurrently
|
|
10
10
|
index, algorithm, if_not_exists = add_index_options(table_name, column_name, **options)
|
|
@@ -34,7 +34,7 @@ module OnlineMigrations
|
|
|
34
34
|
def remove_index_in_background(table_name, column_name = nil, name:, **options)
|
|
35
35
|
raise ArgumentError, "Index name must be specified" if name.blank?
|
|
36
36
|
|
|
37
|
-
migration_options = options.extract!(:max_attempts, :statement_timeout, :connection_class_name, :delay)
|
|
37
|
+
migration_options = options.extract!(:max_attempts, :statement_timeout, :connection_class_name, :delay, :shard)
|
|
38
38
|
|
|
39
39
|
if !index_exists?(table_name, column_name, **options, name: name)
|
|
40
40
|
Utils.raise_or_say("Index deletion was not enqueued because the index does not exist.")
|
|
@@ -46,7 +46,7 @@ module OnlineMigrations
|
|
|
46
46
|
end
|
|
47
47
|
|
|
48
48
|
def validate_foreign_key_in_background(from_table, to_table = nil, **options)
|
|
49
|
-
migration_options = options.extract!(:max_attempts, :statement_timeout, :connection_class_name, :delay)
|
|
49
|
+
migration_options = options.extract!(:max_attempts, :statement_timeout, :connection_class_name, :delay, :shard)
|
|
50
50
|
|
|
51
51
|
if !foreign_key_exists?(from_table, to_table, **options)
|
|
52
52
|
Utils.raise_or_say("Foreign key validation was not enqueued because the foreign key does not exist.")
|
|
@@ -87,7 +87,7 @@ module OnlineMigrations
|
|
|
87
87
|
end
|
|
88
88
|
end
|
|
89
89
|
|
|
90
|
-
def enqueue_background_schema_migration(migration_name, table_name, connection_class_name: nil, delay: false, **options)
|
|
90
|
+
def enqueue_background_schema_migration(migration_name, table_name, connection_class_name: nil, delay: false, shard: nil, **options)
|
|
91
91
|
options.assert_valid_keys(:definition, :max_attempts, :statement_timeout)
|
|
92
92
|
|
|
93
93
|
if Utils.multiple_databases? && !connection_class_name
|
|
@@ -104,8 +104,15 @@ module OnlineMigrations
|
|
|
104
104
|
# Normalize to the real connection class name.
|
|
105
105
|
connection_class_name = connection_class.name
|
|
106
106
|
|
|
107
|
-
shards = Utils.shard_names(connection_class)
|
|
108
|
-
|
|
107
|
+
shards = Utils.shard_names(connection_class).map(&:to_s)
|
|
108
|
+
if shards.size == 1
|
|
109
|
+
shards = [nil]
|
|
110
|
+
elsif shard
|
|
111
|
+
shard = shard.to_s
|
|
112
|
+
raise "Unknown shard: #{shard}" if !shards.include?(shard)
|
|
113
|
+
|
|
114
|
+
shards = [shard]
|
|
115
|
+
end
|
|
109
116
|
|
|
110
117
|
status = delay ? :delayed : :pending
|
|
111
118
|
|
|
@@ -16,6 +16,7 @@ module OnlineMigrations
|
|
|
16
16
|
# @return [DataMigration] the Data Migration with the given name.
|
|
17
17
|
#
|
|
18
18
|
# @raise [NotFoundError] if a Data Migration with the given name does not exist.
|
|
19
|
+
# @raise [ArgumentError] if a Data Migration with the given name is not a subclass of DataMigration.
|
|
19
20
|
#
|
|
20
21
|
def named(name)
|
|
21
22
|
namespace = OnlineMigrations.config.background_data_migrations.migrations_module.constantize
|
|
@@ -26,7 +27,7 @@ module OnlineMigrations
|
|
|
26
27
|
|
|
27
28
|
raise NotFoundError.new("Data Migration #{name} not found", name) if migration.nil?
|
|
28
29
|
if !(migration.is_a?(Class) && migration < self)
|
|
29
|
-
raise
|
|
30
|
+
raise ArgumentError, "#{name} is not a Data Migration"
|
|
30
31
|
end
|
|
31
32
|
|
|
32
33
|
migration
|
|
@@ -3,83 +3,6 @@
|
|
|
3
3
|
module OnlineMigrations
|
|
4
4
|
# @private
|
|
5
5
|
module SchemaCache
|
|
6
|
-
def primary_keys(connection, table_name)
|
|
7
|
-
if (renamed_table = renamed_table?(connection, table_name))
|
|
8
|
-
super(connection, renamed_table)
|
|
9
|
-
elsif renamed_column?(connection, table_name)
|
|
10
|
-
super(connection, column_rename_table(table_name))
|
|
11
|
-
else
|
|
12
|
-
super
|
|
13
|
-
end
|
|
14
|
-
end
|
|
15
|
-
|
|
16
|
-
def columns(connection, table_name)
|
|
17
|
-
if (renamed_table = renamed_table?(connection, table_name))
|
|
18
|
-
super(connection, renamed_table)
|
|
19
|
-
elsif renamed_column?(connection, table_name)
|
|
20
|
-
columns = super(connection, column_rename_table(table_name))
|
|
21
|
-
OnlineMigrations.config.column_renames[table_name].each do |old_column_name, new_column_name|
|
|
22
|
-
duplicate_column(old_column_name, new_column_name, columns)
|
|
23
|
-
end
|
|
24
|
-
columns
|
|
25
|
-
else
|
|
26
|
-
super.reject { |column| column.name.end_with?("_for_type_change") }
|
|
27
|
-
end
|
|
28
|
-
end
|
|
29
|
-
|
|
30
|
-
def indexes(connection, table_name)
|
|
31
|
-
if (renamed_table = renamed_table?(connection, table_name))
|
|
32
|
-
super(connection, renamed_table)
|
|
33
|
-
elsif renamed_column?(connection, table_name)
|
|
34
|
-
super(connection, column_rename_table(table_name))
|
|
35
|
-
else
|
|
36
|
-
super
|
|
37
|
-
end
|
|
38
|
-
end
|
|
39
|
-
|
|
40
|
-
def clear_data_source_cache!(connection, name)
|
|
41
|
-
if (renamed_table = renamed_table?(connection, name))
|
|
42
|
-
super(connection, renamed_table)
|
|
43
|
-
end
|
|
44
|
-
|
|
45
|
-
if renamed_column?(connection, name)
|
|
46
|
-
super(connection, column_rename_table(name))
|
|
47
|
-
end
|
|
48
|
-
|
|
49
|
-
super
|
|
50
|
-
end
|
|
51
|
-
|
|
52
|
-
private
|
|
53
|
-
def renamed_table?(connection, table_name)
|
|
54
|
-
table_renames = OnlineMigrations.config.table_renames
|
|
55
|
-
if table_renames.key?(table_name) && connection.view_exists?(table_name)
|
|
56
|
-
table_renames[table_name]
|
|
57
|
-
end
|
|
58
|
-
end
|
|
59
|
-
|
|
60
|
-
def renamed_column?(connection, table_name)
|
|
61
|
-
column_renames = OnlineMigrations.config.column_renames
|
|
62
|
-
column_renames.key?(table_name) && connection.view_exists?(table_name)
|
|
63
|
-
end
|
|
64
|
-
|
|
65
|
-
def column_rename_table(table_name)
|
|
66
|
-
"#{table_name}_column_rename"
|
|
67
|
-
end
|
|
68
|
-
|
|
69
|
-
def duplicate_column(old_column_name, new_column_name, columns)
|
|
70
|
-
old_column = columns.find { |column| column.name == old_column_name }
|
|
71
|
-
new_column = old_column.dup
|
|
72
|
-
# Active Record defines only reader for :name
|
|
73
|
-
new_column.instance_variable_set(:@name, new_column_name)
|
|
74
|
-
# Correspond to the Active Record freezing of each column
|
|
75
|
-
columns << new_column.freeze
|
|
76
|
-
end
|
|
77
|
-
end
|
|
78
|
-
|
|
79
|
-
# @private
|
|
80
|
-
module SchemaCache72
|
|
81
|
-
# Active Record >= 7.2 changed signature of the methods,
|
|
82
|
-
# see https://github.com/rails/rails/pull/48716.
|
|
83
6
|
def primary_keys(pool, table_name)
|
|
84
7
|
if (renamed_table = renamed_table?(pool, table_name))
|
|
85
8
|
super(pool, renamed_table)
|
|
@@ -705,7 +705,7 @@ module OnlineMigrations
|
|
|
705
705
|
add_foreign_key(table_name, foreign_table_name, **foreign_key, column: column_name, validate: false)
|
|
706
706
|
|
|
707
707
|
if foreign_key[:validate] != false
|
|
708
|
-
validate_foreign_key(table_name, foreign_table_name, **foreign_key)
|
|
708
|
+
validate_foreign_key(table_name, foreign_table_name, **foreign_key, column: column_name)
|
|
709
709
|
end
|
|
710
710
|
end
|
|
711
711
|
end
|
|
@@ -992,12 +992,7 @@ module OnlineMigrations
|
|
|
992
992
|
|
|
993
993
|
def __tmp_table_name_for_column_rename(table_name)
|
|
994
994
|
suffix = "_column_rename"
|
|
995
|
-
|
|
996
|
-
# On ActiveRecord 7.1 can use table_name_length instead of max_identifier_length,
|
|
997
|
-
# see https://github.com/rails/rails/pull/45136.
|
|
998
|
-
# Also we need to account for "_pkey", because older versions does not correctly rename
|
|
999
|
-
# tables with long names. Remove when supporting newer versions only.
|
|
1000
|
-
prefix_length = max_identifier_length - "_pkey".size - suffix.length
|
|
995
|
+
prefix_length = max_identifier_length - suffix.length
|
|
1001
996
|
table_name[0, prefix_length] + suffix
|
|
1002
997
|
end
|
|
1003
998
|
end
|
data/lib/online_migrations.rb
CHANGED
|
@@ -116,16 +116,11 @@ module OnlineMigrations
|
|
|
116
116
|
ActiveRecord::Migration.prepend(OnlineMigrations::Migration)
|
|
117
117
|
ActiveRecord::Migrator.prepend(OnlineMigrations::Migrator)
|
|
118
118
|
ActiveRecord::SchemaDumper.prepend(OnlineMigrations::SchemaDumper)
|
|
119
|
+
ActiveRecord::ConnectionAdapters::SchemaCache.prepend(OnlineMigrations::SchemaCache)
|
|
119
120
|
|
|
120
121
|
ActiveRecord::Tasks::DatabaseTasks.singleton_class.prepend(OnlineMigrations::DatabaseTasks)
|
|
121
122
|
ActiveRecord::Migration::CommandRecorder.include(OnlineMigrations::CommandRecorder)
|
|
122
123
|
|
|
123
|
-
if OnlineMigrations::Utils.ar_version >= 7.2
|
|
124
|
-
ActiveRecord::ConnectionAdapters::SchemaCache.prepend(OnlineMigrations::SchemaCache72)
|
|
125
|
-
else
|
|
126
|
-
ActiveRecord::ConnectionAdapters::SchemaCache.prepend(OnlineMigrations::SchemaCache)
|
|
127
|
-
end
|
|
128
|
-
|
|
129
124
|
if !ActiveRecord::Batches::BatchEnumerator.method_defined?(:use_ranges)
|
|
130
125
|
ActiveRecord::Batches::BatchEnumerator.include(OnlineMigrations::ActiveRecordBatchEnumerator)
|
|
131
126
|
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.
|
|
4
|
+
version: 0.34.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- fatkodima
|
|
@@ -15,14 +15,14 @@ dependencies:
|
|
|
15
15
|
requirements:
|
|
16
16
|
- - ">="
|
|
17
17
|
- !ruby/object:Gem::Version
|
|
18
|
-
version: '7.
|
|
18
|
+
version: '7.2'
|
|
19
19
|
type: :runtime
|
|
20
20
|
prerelease: false
|
|
21
21
|
version_requirements: !ruby/object:Gem::Requirement
|
|
22
22
|
requirements:
|
|
23
23
|
- - ">="
|
|
24
24
|
- !ruby/object:Gem::Version
|
|
25
|
-
version: '7.
|
|
25
|
+
version: '7.2'
|
|
26
26
|
email:
|
|
27
27
|
- fatkodima123@gmail.com
|
|
28
28
|
executables: []
|
|
@@ -115,7 +115,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
|
115
115
|
- !ruby/object:Gem::Version
|
|
116
116
|
version: '0'
|
|
117
117
|
requirements: []
|
|
118
|
-
rubygems_version: 4.0.
|
|
118
|
+
rubygems_version: 4.0.10
|
|
119
119
|
specification_version: 4
|
|
120
120
|
summary: Catch unsafe PostgreSQL migrations in development and run them easier in
|
|
121
121
|
production
|