online_migrations 0.26.0 → 0.27.1

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 (51) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +18 -0
  3. data/docs/0.27-upgrade.md +24 -0
  4. data/docs/background_data_migrations.md +200 -101
  5. data/docs/background_schema_migrations.md +2 -2
  6. data/lib/generators/online_migrations/{background_migration_generator.rb → data_migration_generator.rb} +4 -4
  7. data/lib/generators/online_migrations/templates/change_background_data_migrations.rb.tt +34 -0
  8. data/lib/generators/online_migrations/templates/{background_data_migration.rb.tt → data_migration.rb.tt} +8 -9
  9. data/lib/generators/online_migrations/templates/initializer.rb.tt +19 -25
  10. data/lib/generators/online_migrations/templates/install_migration.rb.tt +9 -40
  11. data/lib/generators/online_migrations/upgrade_generator.rb +16 -8
  12. data/lib/online_migrations/active_record_batch_enumerator.rb +8 -0
  13. data/lib/online_migrations/background_data_migrations/backfill_column.rb +50 -0
  14. data/lib/online_migrations/background_data_migrations/config.rb +62 -0
  15. data/lib/online_migrations/{background_migrations → background_data_migrations}/copy_column.rb +15 -28
  16. data/lib/online_migrations/{background_migrations → background_data_migrations}/delete_associated_records.rb +9 -5
  17. data/lib/online_migrations/{background_migrations → background_data_migrations}/delete_orphaned_records.rb +5 -9
  18. data/lib/online_migrations/background_data_migrations/migration.rb +312 -0
  19. data/lib/online_migrations/{background_migrations → background_data_migrations}/migration_helpers.rb +72 -61
  20. data/lib/online_migrations/background_data_migrations/migration_job.rb +160 -0
  21. data/lib/online_migrations/background_data_migrations/migration_status_validator.rb +65 -0
  22. data/lib/online_migrations/{background_migrations → background_data_migrations}/perform_action_on_relation.rb +5 -5
  23. data/lib/online_migrations/{background_migrations → background_data_migrations}/reset_counters.rb +5 -5
  24. data/lib/online_migrations/background_data_migrations/scheduler.rb +78 -0
  25. data/lib/online_migrations/background_data_migrations/ticker.rb +62 -0
  26. data/lib/online_migrations/background_schema_migrations/config.rb +2 -2
  27. data/lib/online_migrations/background_schema_migrations/migration.rb +51 -123
  28. data/lib/online_migrations/background_schema_migrations/migration_helpers.rb +25 -46
  29. data/lib/online_migrations/background_schema_migrations/migration_runner.rb +43 -97
  30. data/lib/online_migrations/background_schema_migrations/scheduler.rb +2 -2
  31. data/lib/online_migrations/change_column_type_helpers.rb +17 -4
  32. data/lib/online_migrations/config.rb +4 -4
  33. data/lib/online_migrations/data_migration.rb +127 -0
  34. data/lib/online_migrations/error_messages.rb +2 -0
  35. data/lib/online_migrations/lock_retrier.rb +5 -2
  36. data/lib/online_migrations/schema_statements.rb +1 -1
  37. data/lib/online_migrations/shard_aware.rb +44 -0
  38. data/lib/online_migrations/version.rb +1 -1
  39. data/lib/online_migrations.rb +18 -11
  40. metadata +22 -21
  41. data/lib/online_migrations/background_migration.rb +0 -64
  42. data/lib/online_migrations/background_migrations/backfill_column.rb +0 -54
  43. data/lib/online_migrations/background_migrations/background_migration_class_validator.rb +0 -29
  44. data/lib/online_migrations/background_migrations/config.rb +0 -74
  45. data/lib/online_migrations/background_migrations/migration.rb +0 -329
  46. data/lib/online_migrations/background_migrations/migration_job.rb +0 -109
  47. data/lib/online_migrations/background_migrations/migration_job_runner.rb +0 -66
  48. data/lib/online_migrations/background_migrations/migration_job_status_validator.rb +0 -29
  49. data/lib/online_migrations/background_migrations/migration_runner.rb +0 -161
  50. data/lib/online_migrations/background_migrations/migration_status_validator.rb +0 -48
  51. data/lib/online_migrations/background_migrations/scheduler.rb +0 -42
@@ -0,0 +1,127 @@
1
+ # frozen_string_literal: true
2
+
3
+ gem "sidekiq", ">= 7.3.3"
4
+ require "sidekiq"
5
+
6
+ module OnlineMigrations
7
+ # Base class that is inherited by the host application's data migration classes.
8
+ class DataMigration
9
+ class NotFoundError < NameError; end
10
+
11
+ class << self
12
+ # Finds a Data Migration with the given name.
13
+ #
14
+ # @param name [String] the name of the Data Migration to be found.
15
+ #
16
+ # @return [DataMigration] the Data Migration with the given name.
17
+ #
18
+ # @raise [NotFoundError] if a Data Migration with the given name does not exist.
19
+ #
20
+ def named(name)
21
+ namespace = OnlineMigrations.config.background_data_migrations.migrations_module.constantize
22
+ internal_namespace = ::OnlineMigrations::BackgroundDataMigrations
23
+
24
+ migration = "#{namespace}::#{name}".safe_constantize ||
25
+ "#{internal_namespace}::#{name}".safe_constantize
26
+
27
+ raise NotFoundError.new("Data Migration #{name} not found", name) if migration.nil?
28
+ if !(migration.is_a?(Class) && migration < self)
29
+ raise NotFoundError.new("#{name} is not a Data Migration", name)
30
+ end
31
+
32
+ migration
33
+ end
34
+
35
+ # @private
36
+ attr_accessor :active_record_enumerator_batch_size
37
+
38
+ # Limit the number of records that will be fetched in a single query when
39
+ # iterating over an Active Record collection migration.
40
+ #
41
+ # @param size [Integer] the number of records to fetch in a single query.
42
+ #
43
+ def collection_batch_size(size)
44
+ self.active_record_enumerator_batch_size = size
45
+ end
46
+ end
47
+
48
+ # A hook to override that will be called when the migration starts running.
49
+ #
50
+ def after_start
51
+ end
52
+
53
+ # A hook to override that will be called around 'process' each time.
54
+ #
55
+ # Can be useful for some metrics collection, performance tracking etc.
56
+ #
57
+ def around_process
58
+ yield
59
+ end
60
+
61
+ # A hook to override that will be called when the migration resumes its work.
62
+ #
63
+ def after_resume
64
+ end
65
+
66
+ # A hook to override that will be called each time the migration is interrupted.
67
+ #
68
+ # This can be due to interruption or sidekiq stopping.
69
+ #
70
+ def after_stop
71
+ end
72
+
73
+ # A hook to override that will be called when the migration finished its work.
74
+ #
75
+ def after_complete
76
+ end
77
+
78
+ # A hook to override that will be called when the migration is paused.
79
+ #
80
+ def after_pause
81
+ end
82
+
83
+ # A hook to override that will be called when the migration is cancelled.
84
+ #
85
+ def after_cancel
86
+ end
87
+
88
+ # The collection to be processed.
89
+ #
90
+ # @return [ActiveRecord::Relation, ActiveRecord::Batches::BatchEnumerator, Array, Enumerator]
91
+ #
92
+ # @raise [NotImplementedError] with a message advising subclasses to override this method.
93
+ #
94
+ def collection
95
+ raise NotImplementedError, "#{self.class.name} must implement a 'collection' method"
96
+ end
97
+
98
+ # The action to be performed on each item from the collection.
99
+ #
100
+ # @param _item the current item from the collection being iterated
101
+ # @raise [NotImplementedError] with a message advising subclasses to override this method.
102
+ #
103
+ def process(_item)
104
+ raise NotImplementedError, "#{self.class.name} must implement a 'process' method"
105
+ end
106
+
107
+ # Total count of iterations to be performed (optional, to be able to show progress).
108
+ #
109
+ # @return [Integer, nil]
110
+ #
111
+ def count
112
+ end
113
+
114
+ # Enumerator builder. You may override this method to return any Enumerator yielding
115
+ # pairs of `[item, item_cursor]`, instead of using `collection`.
116
+ #
117
+ # It is useful when it is not practical or impossible to define an explicit collection
118
+ # in the `collection` method.
119
+ #
120
+ # @param cursor [Object, nil] cursor position to resume from, or nil on initial call.
121
+ #
122
+ # @return [Enumerator]
123
+ #
124
+ def build_enumerator(cursor:)
125
+ end
126
+ end
127
+ end
@@ -230,6 +230,8 @@ during writes works automatically). For most column type changes, this does not
230
230
  7. Finally, if everything works as expected, remove copy trigger and old column:
231
231
 
232
232
  class Cleanup<%= migration_name %> < <%= migration_parent %>
233
+ disable_ddl_transaction!
234
+
233
235
  def up
234
236
  <%= cleanup_code %>
235
237
  end
@@ -91,10 +91,13 @@ module OnlineMigrations
91
91
  else
92
92
  yield
93
93
  end
94
- rescue ActiveRecord::LockWaitTimeout
94
+ rescue ActiveRecord::LockWaitTimeout, ActiveRecord::Deadlocked => e
95
95
  if current_attempt <= attempts
96
96
  current_delay = delay(current_attempt)
97
- Utils.say("Lock timeout. Retrying in #{current_delay} seconds...")
97
+
98
+ problem = e.is_a?(ActiveRecord::Deadlocked) ? "Deadlock detected." : "Lock timeout."
99
+ Utils.say("#{problem} Retrying in #{current_delay} seconds...")
100
+
98
101
  sleep(current_delay)
99
102
  retry
100
103
  end
@@ -3,7 +3,7 @@
3
3
  module OnlineMigrations
4
4
  module SchemaStatements
5
5
  include ChangeColumnTypeHelpers
6
- include BackgroundMigrations::MigrationHelpers
6
+ include BackgroundDataMigrations::MigrationHelpers
7
7
  include BackgroundSchemaMigrations::MigrationHelpers
8
8
 
9
9
  # Updates the value of a column in batches.
@@ -0,0 +1,44 @@
1
+ # frozen_string_literal: true
2
+
3
+ module OnlineMigrations
4
+ module ShardAware
5
+ extend ActiveSupport::Concern
6
+
7
+ included do
8
+ before_validation :set_connection_class_name
9
+ end
10
+
11
+ def on_shard_if_present(&block)
12
+ if shard
13
+ connection_class.connected_to(shard: shard.to_sym, role: :writing, &block)
14
+ else
15
+ yield
16
+ end
17
+ end
18
+
19
+ def connection_class_name=(value)
20
+ if value && (klass = value.safe_constantize)
21
+ if !(klass <= ActiveRecord::Base)
22
+ raise ArgumentError, "connection_class_name is not an ActiveRecord::Base child class"
23
+ end
24
+
25
+ connection_class = Utils.find_connection_class(klass)
26
+ super(connection_class.name)
27
+ end
28
+ end
29
+
30
+ # @private
31
+ def connection_class
32
+ if connection_class_name && (klass = connection_class_name.safe_constantize)
33
+ Utils.find_connection_class(klass)
34
+ else
35
+ ActiveRecord::Base
36
+ end
37
+ end
38
+
39
+ private
40
+ def set_connection_class_name
41
+ self.connection_class_name ||= "ActiveRecord::Base"
42
+ end
43
+ end
44
+ end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module OnlineMigrations
4
- VERSION = "0.26.0"
4
+ VERSION = "0.27.1"
5
5
  end
@@ -6,7 +6,8 @@ require "online_migrations/version"
6
6
  require "online_migrations/utils"
7
7
  require "online_migrations/background_schema_migrations/migration_helpers"
8
8
  require "online_migrations/change_column_type_helpers"
9
- require "online_migrations/background_migrations/migration_helpers"
9
+ require "online_migrations/background_data_migrations/migration_helpers"
10
+ require "online_migrations/active_record_batch_enumerator"
10
11
  require "online_migrations/schema_statements"
11
12
  require "online_migrations/schema_cache"
12
13
  require "online_migrations/migration"
@@ -29,7 +30,8 @@ module OnlineMigrations
29
30
  autoload :ForeignKeysCollector
30
31
  autoload :IndexDefinition
31
32
  autoload :CommandChecker
32
- autoload :BackgroundMigration
33
+ autoload :DataMigration
34
+ autoload :ShardAware
33
35
 
34
36
  autoload_at "online_migrations/lock_retrier" do
35
37
  autoload :LockRetrier
@@ -40,24 +42,21 @@ module OnlineMigrations
40
42
 
41
43
  autoload :CopyTrigger
42
44
 
43
- module BackgroundMigrations
45
+ module BackgroundDataMigrations
44
46
  extend ActiveSupport::Autoload
45
47
 
46
48
  autoload :Config
47
49
  autoload :MigrationStatusValidator
48
- autoload :MigrationJobStatusValidator
49
- autoload :BackgroundMigrationClassValidator
50
50
  autoload :BackfillColumn
51
51
  autoload :CopyColumn
52
52
  autoload :DeleteAssociatedRecords
53
53
  autoload :DeleteOrphanedRecords
54
54
  autoload :PerformActionOnRelation
55
55
  autoload :ResetCounters
56
- autoload :MigrationJob
57
56
  autoload :Migration
58
- autoload :MigrationJobRunner
59
- autoload :MigrationRunner
57
+ autoload :MigrationJob
60
58
  autoload :Scheduler
59
+ autoload :Ticker
61
60
  end
62
61
 
63
62
  module BackgroundSchemaMigrations
@@ -70,6 +69,10 @@ module OnlineMigrations
70
69
  autoload :Scheduler
71
70
  end
72
71
 
72
+ # Make aliases for less typing.
73
+ DataMigrations = BackgroundDataMigrations
74
+ SchemaMigrations = BackgroundSchemaMigrations
75
+
73
76
  class << self
74
77
  # @private
75
78
  attr_accessor :current_migration
@@ -87,10 +90,10 @@ module OnlineMigrations
87
90
  # @option options [String, Symbol, nil] :shard The name of the shard to run
88
91
  # background data migrations on. By default runs on all shards.
89
92
  #
90
- def run_background_migrations(**options)
91
- BackgroundMigrations::Scheduler.run(**options)
93
+ def run_background_data_migrations(**options)
94
+ BackgroundDataMigrations::Scheduler.run(**options)
92
95
  end
93
- alias run_background_data_migrations run_background_migrations
96
+ alias run_background_migrations run_background_data_migrations
94
97
 
95
98
  # Run background schema migrations
96
99
  #
@@ -122,6 +125,10 @@ module OnlineMigrations
122
125
  else
123
126
  ActiveRecord::ConnectionAdapters::SchemaCache.prepend(OnlineMigrations::SchemaCache)
124
127
  end
128
+
129
+ if !ActiveRecord::Batches::BatchEnumerator.method_defined?(:use_ranges)
130
+ ActiveRecord::Batches::BatchEnumerator.include(OnlineMigrations::ActiveRecordBatchEnumerator)
131
+ end
125
132
  end
126
133
  end
127
134
  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.26.0
4
+ version: 0.27.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - fatkodima
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2025-04-27 00:00:00.000000000 Z
11
+ date: 2025-05-08 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activerecord
@@ -34,39 +34,38 @@ files:
34
34
  - CHANGELOG.md
35
35
  - LICENSE.txt
36
36
  - README.md
37
+ - docs/0.27-upgrade.md
37
38
  - docs/background_data_migrations.md
38
39
  - docs/background_schema_migrations.md
39
40
  - docs/configuring.md
40
- - lib/generators/online_migrations/background_migration_generator.rb
41
+ - lib/generators/online_migrations/data_migration_generator.rb
41
42
  - lib/generators/online_migrations/install_generator.rb
42
43
  - lib/generators/online_migrations/templates/add_sharding_to_online_migrations.rb.tt
43
44
  - lib/generators/online_migrations/templates/add_timestamps_to_background_migrations.rb.tt
44
- - lib/generators/online_migrations/templates/background_data_migration.rb.tt
45
45
  - lib/generators/online_migrations/templates/background_schema_migrations_change_unique_index.rb.tt
46
+ - lib/generators/online_migrations/templates/change_background_data_migrations.rb.tt
46
47
  - lib/generators/online_migrations/templates/create_background_schema_migrations.rb.tt
48
+ - lib/generators/online_migrations/templates/data_migration.rb.tt
47
49
  - lib/generators/online_migrations/templates/initializer.rb.tt
48
50
  - lib/generators/online_migrations/templates/install_migration.rb.tt
49
51
  - lib/generators/online_migrations/templates/migration.rb.tt
50
52
  - lib/generators/online_migrations/upgrade_generator.rb
51
53
  - lib/online_migrations.rb
54
+ - lib/online_migrations/active_record_batch_enumerator.rb
52
55
  - lib/online_migrations/application_record.rb
53
- - lib/online_migrations/background_migration.rb
54
- - lib/online_migrations/background_migrations/backfill_column.rb
55
- - lib/online_migrations/background_migrations/background_migration_class_validator.rb
56
- - lib/online_migrations/background_migrations/config.rb
57
- - lib/online_migrations/background_migrations/copy_column.rb
58
- - lib/online_migrations/background_migrations/delete_associated_records.rb
59
- - lib/online_migrations/background_migrations/delete_orphaned_records.rb
60
- - lib/online_migrations/background_migrations/migration.rb
61
- - lib/online_migrations/background_migrations/migration_helpers.rb
62
- - lib/online_migrations/background_migrations/migration_job.rb
63
- - lib/online_migrations/background_migrations/migration_job_runner.rb
64
- - lib/online_migrations/background_migrations/migration_job_status_validator.rb
65
- - lib/online_migrations/background_migrations/migration_runner.rb
66
- - lib/online_migrations/background_migrations/migration_status_validator.rb
67
- - lib/online_migrations/background_migrations/perform_action_on_relation.rb
68
- - lib/online_migrations/background_migrations/reset_counters.rb
69
- - lib/online_migrations/background_migrations/scheduler.rb
56
+ - lib/online_migrations/background_data_migrations/backfill_column.rb
57
+ - lib/online_migrations/background_data_migrations/config.rb
58
+ - lib/online_migrations/background_data_migrations/copy_column.rb
59
+ - lib/online_migrations/background_data_migrations/delete_associated_records.rb
60
+ - lib/online_migrations/background_data_migrations/delete_orphaned_records.rb
61
+ - lib/online_migrations/background_data_migrations/migration.rb
62
+ - lib/online_migrations/background_data_migrations/migration_helpers.rb
63
+ - lib/online_migrations/background_data_migrations/migration_job.rb
64
+ - lib/online_migrations/background_data_migrations/migration_status_validator.rb
65
+ - lib/online_migrations/background_data_migrations/perform_action_on_relation.rb
66
+ - lib/online_migrations/background_data_migrations/reset_counters.rb
67
+ - lib/online_migrations/background_data_migrations/scheduler.rb
68
+ - lib/online_migrations/background_data_migrations/ticker.rb
70
69
  - lib/online_migrations/background_schema_migrations/config.rb
71
70
  - lib/online_migrations/background_schema_migrations/migration.rb
72
71
  - lib/online_migrations/background_schema_migrations/migration_helpers.rb
@@ -79,6 +78,7 @@ files:
79
78
  - lib/online_migrations/command_recorder.rb
80
79
  - lib/online_migrations/config.rb
81
80
  - lib/online_migrations/copy_trigger.rb
81
+ - lib/online_migrations/data_migration.rb
82
82
  - lib/online_migrations/database_tasks.rb
83
83
  - lib/online_migrations/error_messages.rb
84
84
  - lib/online_migrations/foreign_keys_collector.rb
@@ -89,6 +89,7 @@ files:
89
89
  - lib/online_migrations/schema_cache.rb
90
90
  - lib/online_migrations/schema_dumper.rb
91
91
  - lib/online_migrations/schema_statements.rb
92
+ - lib/online_migrations/shard_aware.rb
92
93
  - lib/online_migrations/utils.rb
93
94
  - lib/online_migrations/verbose_sql_logs.rb
94
95
  - lib/online_migrations/version.rb
@@ -1,64 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module OnlineMigrations
4
- # Base class that is inherited by the host application's background migration classes.
5
- class BackgroundMigration
6
- class NotFoundError < NameError; end
7
-
8
- class << self
9
- # Finds a Background Migration with the given name.
10
- #
11
- # @param name [String] the name of the Background Migration to be found.
12
- #
13
- # @return [BackgroundMigration] the Background Migration with the given name.
14
- #
15
- # @raise [NotFoundError] if a Background Migration with the given name does not exist.
16
- #
17
- def named(name)
18
- namespace = OnlineMigrations.config.background_migrations.migrations_module.constantize
19
- internal_namespace = ::OnlineMigrations::BackgroundMigrations
20
-
21
- migration = "#{namespace}::#{name}".safe_constantize ||
22
- "#{internal_namespace}::#{name}".safe_constantize
23
-
24
- raise NotFoundError.new("Background Migration #{name} not found", name) if migration.nil?
25
- if !(migration.is_a?(Class) && migration < self)
26
- raise NotFoundError.new("#{name} is not a Background Migration", name)
27
- end
28
-
29
- migration
30
- end
31
- end
32
-
33
- # The relation to be iterated over.
34
- #
35
- # @return [ActiveRecord::Relation]
36
- #
37
- # @raise [NotImplementedError] with a message advising subclasses to
38
- # implement an override for this method.
39
- #
40
- def relation
41
- raise NotImplementedError, "#{self.class.name} must implement a 'relation' method"
42
- end
43
-
44
- # Processes one batch.
45
- #
46
- # @param _relation [ActiveRecord::Relation] the current batch from the enumerator being iterated
47
- # @return [void]
48
- #
49
- # @raise [NotImplementedError] with a message advising subclasses to
50
- # implement an override for this method.
51
- #
52
- def process_batch(_relation)
53
- raise NotImplementedError, "#{self.class.name} must implement a 'process_batch' method"
54
- end
55
-
56
- # Returns the count of rows that will be iterated over (optional, to be able to show progress).
57
- #
58
- # @return [Integer, nil, :no_count]
59
- #
60
- def count
61
- :no_count
62
- end
63
- end
64
- end
@@ -1,54 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module OnlineMigrations
4
- module BackgroundMigrations
5
- # @private
6
- class BackfillColumn < BackgroundMigration
7
- attr_reader :table_name, :updates, :model_name
8
-
9
- def initialize(table_name, updates, model_name = nil)
10
- @table_name = table_name
11
- @updates = updates
12
- @model_name = model_name
13
- end
14
-
15
- def relation
16
- column, value = updates.first
17
-
18
- if updates.size == 1 && !value.nil?
19
- # If value is nil, the generated SQL is correct (`WHERE column IS NOT NULL`).
20
- # Otherwise, the SQL is `WHERE column != value`. This condition ignores column
21
- # with NULLs in it, so we need to also manually check for NULLs.
22
- quoted_column = connection.quote_column_name(column)
23
- model.unscoped.where("#{quoted_column} != ? OR #{quoted_column} IS NULL", value)
24
- else
25
- model.unscoped.where.not(updates)
26
- end
27
- end
28
-
29
- def process_batch(relation)
30
- relation.update_all(updates)
31
- end
32
-
33
- def count
34
- # Exact counts are expensive on large tables, since PostgreSQL
35
- # needs to do a full scan. An estimated count should give a pretty decent
36
- # approximation of rows count in this case.
37
- Utils.estimated_count(connection, table_name)
38
- end
39
-
40
- private
41
- def model
42
- @model ||= if model_name.present?
43
- Object.const_get(model_name, false)
44
- else
45
- Utils.define_model(table_name)
46
- end
47
- end
48
-
49
- def connection
50
- model.connection
51
- end
52
- end
53
- end
54
- end
@@ -1,29 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module OnlineMigrations
4
- module BackgroundMigrations
5
- # @private
6
- class BackgroundMigrationClassValidator < ActiveModel::Validator
7
- def validate(record)
8
- relation = record.migration_relation
9
- migration_name = record.migration_name
10
-
11
- if !relation.is_a?(ActiveRecord::Relation)
12
- record.errors.add(
13
- :migration_name,
14
- "#{migration_name}#relation must return an ActiveRecord::Relation object"
15
- )
16
- return
17
- end
18
-
19
- if relation.arel.orders.present? || relation.arel.taken.present?
20
- record.errors.add(
21
- :migration_name,
22
- "#{migration_name}#relation cannot use ORDER BY or LIMIT due to the way how iteration with a cursor is designed. " \
23
- "You can use other ways to limit the number of rows, e.g. a WHERE condition on the primary key column."
24
- )
25
- end
26
- end
27
- end
28
- end
29
- end
@@ -1,74 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module OnlineMigrations
4
- module BackgroundMigrations
5
- # Class representing configuration options for background migrations.
6
- class Config
7
- # The path where generated background migrations will be placed
8
- # @return [String] defaults to "lib"
9
- attr_accessor :migrations_path
10
-
11
- # The module in which background migrations will be placed
12
- # @return [String] defaults to "OnlineMigrations::BackgroundMigrations"
13
- attr_accessor :migrations_module
14
-
15
- # The number of rows to process in a single background migration run
16
- # @return [Integer] defaults to 1_000
17
- #
18
- attr_accessor :batch_size
19
-
20
- # The smaller batches size that the batches will be divided into
21
- # @return [Integer] defaults to 100
22
- #
23
- attr_accessor :sub_batch_size
24
-
25
- # The pause interval between each background migration job's execution (in seconds)
26
- # @return [Integer] defaults to 0
27
- #
28
- attr_accessor :batch_pause
29
-
30
- # The number of milliseconds to sleep between each sub_batch execution
31
- # @return [Integer] defaults to 100 milliseconds
32
- #
33
- attr_accessor :sub_batch_pause_ms
34
-
35
- # Maximum number of batch run attempts
36
- #
37
- # When attempts are exhausted, the individual batch is marked as failed.
38
- # @return [Integer] defaults to 5
39
- #
40
- attr_accessor :batch_max_attempts
41
-
42
- # The number of seconds that must pass before the running job is considered stuck
43
- #
44
- # @return [Integer] defaults to 1 hour
45
- #
46
- attr_accessor :stuck_jobs_timeout
47
-
48
- # The callback to perform when an error occurs in the migration job.
49
- #
50
- # @example
51
- # OnlineMigrations.config.background_migrations.error_handler = ->(error, errored_job) do
52
- # Bugsnag.notify(error) do |notification|
53
- # notification.add_metadata(:background_migration, { name: errored_job.migration_name })
54
- # end
55
- # end
56
- #
57
- # @return [Proc] the callback to perform when an error occurs in the migration job
58
- #
59
- attr_accessor :error_handler
60
-
61
- def initialize
62
- @migrations_path = "lib"
63
- @migrations_module = "OnlineMigrations::BackgroundMigrations"
64
- @batch_size = 1_000
65
- @sub_batch_size = 100
66
- @batch_pause = 0.seconds
67
- @sub_batch_pause_ms = 100
68
- @batch_max_attempts = 5
69
- @stuck_jobs_timeout = 1.hour
70
- @error_handler = ->(error, errored_job) {}
71
- end
72
- end
73
- end
74
- end