online_migrations 0.10.0 → 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 +33 -0
  3. data/README.md +34 -31
  4. data/docs/background_migrations.md +36 -4
  5. data/docs/configuring.md +3 -2
  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 +5 -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 +6 -20
  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 +68 -83
  23. data/lib/online_migrations/command_checker.rb +11 -29
  24. data/lib/online_migrations/config.rb +7 -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 +1 -1
  32. data/lib/online_migrations/schema_statements.rb +64 -256
  33. data/lib/online_migrations/utils.rb +18 -56
  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 +7 -6
  37. metadata +8 -7
  38. data/lib/online_migrations/background_migrations/advisory_lock.rb +0 -62
  39. data/lib/online_migrations/foreign_key_definition.rb +0 -17
@@ -27,51 +27,15 @@ module OnlineMigrations
27
27
  Kernel.warn("[online_migrations] #{message}")
28
28
  end
29
29
 
30
- def supports_multiple_dbs?
31
- # Technically, Active Record 6.0+ supports multiple databases,
32
- # but we can not get the database spec name for this version.
33
- ar_version >= 6.1
34
- end
35
-
36
- def migration_parent
37
- if ar_version <= 4.2
38
- ActiveRecord::Migration
39
- else
40
- ActiveRecord::Migration[ar_version]
41
- end
42
- end
43
-
44
- def migration_parent_string
45
- if ar_version <= 4.2
46
- "ActiveRecord::Migration"
47
- else
48
- "ActiveRecord::Migration[#{ar_version}]"
49
- end
50
- end
51
-
52
- def model_parent_string
53
- if ar_version >= 5.0
54
- "ApplicationRecord"
55
- else
56
- "ActiveRecord::Base"
57
- end
58
- end
59
-
60
- def define_model(table_name, connection = ActiveRecord::Base.connection)
30
+ def define_model(table_name)
61
31
  Class.new(ActiveRecord::Base) do
62
32
  self.table_name = table_name
63
33
  self.inheritance_column = :_type_disabled
64
-
65
- @online_migrations_connection = connection
66
-
67
- def self.connection
68
- @online_migrations_connection
69
- end
70
34
  end
71
35
  end
72
36
 
73
37
  def to_bool(value)
74
- !value.to_s.match(/^(true|t|yes|y|1|on)$/i).nil?
38
+ value.to_s.match?(/^true|t|yes|y|1|on$/i)
75
39
  end
76
40
 
77
41
  def foreign_table_name(ref_name, options)
@@ -81,7 +45,7 @@ module OnlineMigrations
81
45
  end
82
46
 
83
47
  # Implementation is from ActiveRecord.
84
- # This is not needed for ActiveRecord < 7.1 (https://github.com/rails/rails/pull/47753).
48
+ # This is not needed for ActiveRecord >= 7.1 (https://github.com/rails/rails/pull/47753).
85
49
  def index_name(table_name, column_name)
86
50
  max_index_name_size = 62
87
51
  name = "index_#{table_name}_on_#{Array(column_name) * '_and_'}"
@@ -122,7 +86,7 @@ module OnlineMigrations
122
86
  def estimated_count(connection, table_name)
123
87
  quoted_table = connection.quote(table_name)
124
88
 
125
- count = connection.select_value(<<-SQL.strip_heredoc)
89
+ count = connection.select_value(<<~SQL)
126
90
  SELECT
127
91
  (reltuples / COALESCE(NULLIF(relpages, 0), 1)) *
128
92
  (pg_relation_size(#{quoted_table}) / (current_setting('block_size')::integer))
@@ -135,25 +99,11 @@ module OnlineMigrations
135
99
  count = count.to_i
136
100
  # If the table has never yet been vacuumed or analyzed, reltuples contains -1
137
101
  # indicating that the row count is unknown.
138
- count = 0 if count == -1
102
+ count = 0 if count < 0
139
103
  count
140
104
  end
141
105
  end
142
106
 
143
- def ar_where_not_multiple_conditions(relation, conditions)
144
- if Utils.ar_version >= 6.1
145
- relation.where.not(conditions)
146
- else
147
- # In Active Record < 6.1, NOT with multiple conditions behaves as NOR,
148
- # which should really behave as NAND.
149
- # https://www.bigbinary.com/blog/rails-6-deprecates-where-not-working-as-nor-and-will-change-to-nand-in-rails-6-1
150
- arel_table = relation.arel_table
151
- conditions = conditions.map { |column, value| arel_table[column].not_eq(value) }
152
- conditions = conditions.inject(:or)
153
- relation.where(conditions)
154
- end
155
- end
156
-
157
107
  FUNCTION_CALL_RE = /(\w+)\s*\(/
158
108
  private_constant :FUNCTION_CALL_RE
159
109
 
@@ -167,7 +117,7 @@ module OnlineMigrations
167
117
  end
168
118
 
169
119
  def volatile_function?(connection, function_name)
170
- query = <<-SQL.strip_heredoc
120
+ query = <<~SQL
171
121
  SELECT provolatile
172
122
  FROM pg_catalog.pg_proc
173
123
  WHERE proname = #{connection.quote(function_name)}
@@ -175,6 +125,18 @@ module OnlineMigrations
175
125
 
176
126
  connection.select_value(query) == "v"
177
127
  end
128
+
129
+ def shard_names(model)
130
+ model.ancestors.each do |ancestor|
131
+ # There is no official method to get shard names from the model.
132
+ # This is the way that currently is used in ActiveRecord tests themselves.
133
+ pool_manager = ActiveRecord::Base.connection_handler.send(:get_pool_manager, ancestor.name)
134
+
135
+ # .uniq call is not needed for Active Record 7.1+
136
+ # See https://github.com/rails/rails/pull/49284.
137
+ return pool_manager.shard_names.uniq if pool_manager
138
+ end
139
+ end
178
140
  end
179
141
  end
180
142
  end
@@ -7,6 +7,7 @@ module OnlineMigrations
7
7
  def enable
8
8
  @activerecord_logger_was = ActiveRecord::Base.logger
9
9
  @verbose_query_logs_was = verbose_query_logs
10
+ return if @activerecord_logger_was.nil?
10
11
 
11
12
  stdout_logger = ActiveSupport::Logger.new($stdout)
12
13
  stdout_logger.formatter = @activerecord_logger_was.formatter
@@ -34,7 +35,7 @@ module OnlineMigrations
34
35
  def verbose_query_logs
35
36
  if Utils.ar_version >= 7.0
36
37
  ActiveRecord.verbose_query_logs
37
- elsif Utils.ar_version >= 5.2
38
+ else
38
39
  ActiveRecord::Base.verbose_query_logs
39
40
  end
40
41
  end
@@ -42,7 +43,7 @@ module OnlineMigrations
42
43
  def set_verbose_query_logs(value) # rubocop:disable Naming/AccessorMethodName
43
44
  if Utils.ar_version >= 7.0
44
45
  ActiveRecord.verbose_query_logs = value
45
- elsif Utils.ar_version >= 5.2
46
+ else
46
47
  ActiveRecord::Base.verbose_query_logs = value
47
48
  end
48
49
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module OnlineMigrations
4
- VERSION = "0.10.0"
4
+ VERSION = "0.11.0"
5
5
  end
@@ -18,7 +18,6 @@ module OnlineMigrations
18
18
  autoload :Migrator
19
19
  autoload :SchemaDumper
20
20
  autoload :DatabaseTasks
21
- autoload :ForeignKeyDefinition
22
21
  autoload :ForeignKeysCollector
23
22
  autoload :IndexDefinition
24
23
  autoload :IndexesCollector
@@ -55,12 +54,12 @@ module OnlineMigrations
55
54
  autoload :DeleteOrphanedRecords
56
55
  autoload :PerformActionOnRelation
57
56
  autoload :ResetCounters
57
+ autoload :ApplicationRecord
58
58
  autoload :MigrationJob
59
59
  autoload :Migration
60
60
  autoload :MigrationJobRunner
61
61
  autoload :MigrationRunner
62
62
  autoload :MigrationHelpers
63
- autoload :AdvisoryLock
64
63
  autoload :Scheduler
65
64
  end
66
65
 
@@ -76,6 +75,12 @@ module OnlineMigrations
76
75
  @config ||= Config.new
77
76
  end
78
77
 
78
+ # Run background migrations
79
+ def run_background_migrations
80
+ BackgroundMigrations::Scheduler.run
81
+ end
82
+
83
+ # @private
79
84
  def load
80
85
  require "active_record/connection_adapters/postgresql_adapter"
81
86
  ActiveRecord::ConnectionAdapters::PostgreSQLAdapter.prepend(OnlineMigrations::SchemaStatements)
@@ -92,10 +97,6 @@ module OnlineMigrations
92
97
  else
93
98
  ActiveRecord::ConnectionAdapters::SchemaCache.prepend(OnlineMigrations::SchemaCache)
94
99
  end
95
-
96
- if OnlineMigrations::Utils.ar_version <= 5.1
97
- ActiveRecord::ConnectionAdapters::ForeignKeyDefinition.prepend(OnlineMigrations::ForeignKeyDefinition)
98
- end
99
100
  end
100
101
  end
101
102
  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.10.0
4
+ version: 0.11.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - fatkodima
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2023-12-12 00:00:00.000000000 Z
11
+ date: 2024-01-08 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activerecord
@@ -16,14 +16,14 @@ dependencies:
16
16
  requirements:
17
17
  - - ">="
18
18
  - !ruby/object:Gem::Version
19
- version: '4.2'
19
+ version: '6.1'
20
20
  type: :runtime
21
21
  prerelease: false
22
22
  version_requirements: !ruby/object:Gem::Requirement
23
23
  requirements:
24
24
  - - ">="
25
25
  - !ruby/object:Gem::Version
26
- version: '4.2'
26
+ version: '6.1'
27
27
  description:
28
28
  email:
29
29
  - fatkodima123@gmail.com
@@ -38,12 +38,14 @@ files:
38
38
  - docs/configuring.md
39
39
  - lib/generators/online_migrations/background_migration_generator.rb
40
40
  - lib/generators/online_migrations/install_generator.rb
41
+ - lib/generators/online_migrations/templates/add_sharding_to_online_migrations.rb.tt
41
42
  - lib/generators/online_migrations/templates/background_migration.rb.tt
42
43
  - lib/generators/online_migrations/templates/initializer.rb.tt
43
44
  - lib/generators/online_migrations/templates/migration.rb.tt
45
+ - lib/generators/online_migrations/upgrade_generator.rb
44
46
  - lib/online_migrations.rb
45
47
  - lib/online_migrations/background_migration.rb
46
- - lib/online_migrations/background_migrations/advisory_lock.rb
48
+ - lib/online_migrations/background_migrations/application_record.rb
47
49
  - lib/online_migrations/background_migrations/backfill_column.rb
48
50
  - lib/online_migrations/background_migrations/background_migration_class_validator.rb
49
51
  - lib/online_migrations/background_migrations/config.rb
@@ -68,7 +70,6 @@ files:
68
70
  - lib/online_migrations/copy_trigger.rb
69
71
  - lib/online_migrations/database_tasks.rb
70
72
  - lib/online_migrations/error_messages.rb
71
- - lib/online_migrations/foreign_key_definition.rb
72
73
  - lib/online_migrations/foreign_keys_collector.rb
73
74
  - lib/online_migrations/index_definition.rb
74
75
  - lib/online_migrations/indexes_collector.rb
@@ -96,7 +97,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
96
97
  requirements:
97
98
  - - ">="
98
99
  - !ruby/object:Gem::Version
99
- version: 2.1.0
100
+ version: '2.7'
100
101
  required_rubygems_version: !ruby/object:Gem::Requirement
101
102
  requirements:
102
103
  - - ">="
@@ -1,62 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require "zlib"
4
-
5
- module OnlineMigrations
6
- module BackgroundMigrations
7
- # @private
8
- class AdvisoryLock
9
- attr_reader :name, :connection
10
-
11
- def initialize(name:, connection: ActiveRecord::Base.connection)
12
- @name = name
13
- @connection = connection
14
- end
15
-
16
- def try_lock
17
- locked = connection.select_value("SELECT pg_try_advisory_lock(#{lock_key})")
18
- Utils.to_bool(locked)
19
- end
20
-
21
- def unlock
22
- connection.select_value("SELECT pg_advisory_unlock(#{lock_key})")
23
- end
24
-
25
- # Runs the given block if an advisory lock is able to be acquired.
26
- def with_lock
27
- if try_lock
28
- begin
29
- yield
30
- ensure
31
- unlock
32
- end
33
- end
34
- end
35
-
36
- def active?
37
- objid = lock_key & 0xffffffff
38
- classid = (lock_key & (0xffffffff << 32)) >> 32
39
-
40
- active = connection.select_value(<<-SQL.strip_heredoc)
41
- SELECT granted
42
- FROM pg_locks
43
- WHERE locktype = 'advisory'
44
- AND pid = pg_backend_pid()
45
- AND mode = 'ExclusiveLock'
46
- AND classid = #{classid}
47
- AND objid = #{objid}
48
- SQL
49
-
50
- Utils.to_bool(active)
51
- end
52
-
53
- private
54
- SALT = 936723412
55
-
56
- def lock_key
57
- name_hash = Zlib.crc32(name)
58
- SALT * name_hash
59
- end
60
- end
61
- end
62
- end
@@ -1,17 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module OnlineMigrations
4
- # @private
5
- module ForeignKeyDefinition
6
- if Utils.ar_version <= 4.2
7
- def defined_for?(to_table: nil, **options)
8
- (to_table.nil? || to_table.to_s == self.to_table) &&
9
- options.all? { |k, v| self.options[k].to_s == v.to_s }
10
- end
11
- elsif Utils.ar_version <= 5.1
12
- def defined_for?(*args, **options)
13
- super(*args, **options.except(:validate))
14
- end
15
- end
16
- end
17
- end