online_migrations 0.10.0 → 0.11.0

Sign up to get free protection for your applications and to get access to all the features.
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