online_migrations 0.35.0 → 0.36.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: 5a84bdeb495a3d25432c6ae97d4c46777517ae3bbcf01359020d400ad8f16f30
4
- data.tar.gz: 935d3b38b096fcdb222a89065d7c10023a8b85a6009065d73a1df9bb92c2b0f8
3
+ metadata.gz: 6740adaa6d20273e8d148e8e0788a6e27c6c6701187fe60e006ff3ba8e2129d2
4
+ data.tar.gz: 445a3ef72888dd358cc5d917ca31de7499df3368797c9c73324704e0e2d8307a
5
5
  SHA512:
6
- metadata.gz: f2c578a25081bb86cafc2851cb818c40e819363d40bdac2a6f4cde634f71a8dc3a90909e4dbf10bf0fb6df61e1d979f4d581982650eec3834715baa8e5888832
7
- data.tar.gz: 01377b6bcdbe0b601a45b53a52a99746e443d60c5ca513e2312322c81718e4a995fde32ee78a45554fe155878391f6a885fdef50bdea4d4619a4e10799bab295
6
+ metadata.gz: 338cf3a26b64efb827b5691addc67987048e0522f467d89c5ff4a18494f6b0e3cbeb93513e419a01b04be643d9f7e7a36c1aaf1b0a9d7f5c8158ecff1eb315e7
7
+ data.tar.gz: 4a6b9282a87124e037b3f101a5856d4a8756e86f9fb5b8bf3a4e42e7e2790e0edbd78b7263b8e9c653bedc25700d8a3fb8ade671e2f99e1e74dbfd49780c20eb
data/CHANGELOG.md CHANGED
@@ -1,5 +1,14 @@
1
1
  ## master (unreleased)
2
2
 
3
+ ## 0.36.0 (2026-07-20)
4
+
5
+ - Serialize to JSON `cursor` value for background data migrations
6
+
7
+ Note: Run `bin/rails generate online_migrations:upgrade` if using background migrations.
8
+
9
+ - Do not consider running background migrations as stuck
10
+ - Do not automatically retry stuck background migrations
11
+
3
12
  ## 0.35.0 (2026-07-01)
4
13
 
5
14
  - Drop support for Ruby < 3.3
data/README.md CHANGED
@@ -342,6 +342,8 @@ during writes works automatically). For most column type changes, this does not
342
342
 
343
343
  ```ruby
344
344
  class CleanupChangeFilesSizeType < ActiveRecord::Migration[8.0]
345
+ disable_ddl_transaction!
346
+
345
347
  def up
346
348
  cleanup_column_type_change :files, :size
347
349
  end
@@ -0,0 +1,9 @@
1
+ class BackgroundDataMigrationsConvertCursor < <%= migration_parent %>
2
+ def change
3
+ up_only do
4
+ OnlineMigrations::DataMigrations::Migration.find_each do |migration|
5
+ OnlineMigrations::DataMigrations::Migration.where(id: migration.id).update_all(cursor: migration.cursor)
6
+ end
7
+ end
8
+ end
9
+ end
@@ -57,6 +57,8 @@ module OnlineMigrations
57
57
  migrations << "background_migrations_change_status_default"
58
58
  end
59
59
 
60
+ migrations << "background_data_migrations_convert_cursor"
61
+
60
62
  migrations
61
63
  end
62
64
 
@@ -52,6 +52,8 @@ module OnlineMigrations
52
52
 
53
53
  enum :status, STATUSES.index_with(&:to_s)
54
54
 
55
+ serialize :cursor, coder: JSON
56
+
55
57
  validates :migration_name, presence: true
56
58
  validates :arguments, uniqueness: { scope: [:migration_name, :shard] }
57
59
 
@@ -66,6 +68,13 @@ module OnlineMigrations
66
68
  migration_name.sub(/^(::)?#{namespace}::/, "")
67
69
  end
68
70
 
71
+ # TODO: delete in some future version
72
+ def cursor
73
+ self[:cursor]
74
+ rescue JSON::ParserError
75
+ cursor_before_type_cast
76
+ end
77
+
69
78
  def migration_name=(class_name)
70
79
  class_name = class_name.name if class_name.is_a?(Class)
71
80
  write_attribute(:migration_name, self.class.normalize_migration_name(class_name))
@@ -111,13 +120,13 @@ module OnlineMigrations
111
120
  end
112
121
 
113
122
  # Returns whether a migration is stuck, which is defined as having a status of
114
- # running, cancelling or pausing, and not having been updated in the last 5 minutes.
123
+ # cancelling or pausing, and not having been updated in the last 5 minutes.
115
124
  #
116
125
  # @return [Boolean] whether the migration is stuck.
117
126
  #
118
127
  def stuck?
119
128
  stuck_timeout = OnlineMigrations.config.background_data_migrations.stuck_timeout
120
- (running? || cancelling? || pausing?) && updated_at <= stuck_timeout.ago
129
+ (cancelling? || pausing?) && updated_at <= stuck_timeout.ago
121
130
  end
122
131
 
123
132
  # @private
@@ -72,6 +72,7 @@ module OnlineMigrations
72
72
  # Job was manually cancelled.
73
73
  @migration.cancel if cancelled?
74
74
 
75
+ @migration.update!(jid: nil)
75
76
  @migration.complete
76
77
  end
77
78
 
@@ -13,14 +13,12 @@ module OnlineMigrations
13
13
  # enqueued -> failed occurs when the migration job fails to be enqueued, or
14
14
  # if the migration is deleted before is starts running.
15
15
  "enqueued" => ["running", "paused", "cancelled", "failed"],
16
- # running -> enqueued occurs when the migration is stuck and rescheduled by the scheduler.
17
16
  # running -> succeeded occurs when the migration completes successfully.
18
17
  # running -> pausing occurs when a user pauses the migration as it's performing.
19
18
  # running -> cancelling occurs when a user cancels the migration as it's performing.
20
19
  # running -> errored occurs when the migration raised an error during the last run.
21
20
  # running -> failed occurs when the migration raises an error when running and retry attempts exceeded.
22
21
  "running" => [
23
- "enqueued",
24
22
  "succeeded",
25
23
  "pausing",
26
24
  "cancelling",
@@ -36,11 +36,10 @@ module OnlineMigrations
36
36
  migrations_to_enqueue = []
37
37
 
38
38
  with_lock do
39
- stuck_migrations, active_migrations = relation.running.partition(&:stuck?)
40
- runnable_migrations = migrations_with_existing_classes(relation.pending) + stuck_migrations
39
+ runnable_migrations = migrations_with_existing_classes(relation.pending)
41
40
 
42
41
  # Ensure no more than 'concurrency' migrations are running at the same time.
43
- remaining_to_enqueue = concurrency - active_migrations.count
42
+ remaining_to_enqueue = concurrency - relation.running.count
44
43
  if remaining_to_enqueue > 0
45
44
  runnable_migrations.take(remaining_to_enqueue).each do |migration|
46
45
  migration.update!(status: :enqueued)
@@ -23,6 +23,7 @@ module OnlineMigrations
23
23
  MAX_IDENTIFIER_LENGTH = 63
24
24
 
25
25
  self.table_name = :background_schema_migrations
26
+ self.ignored_columns += ["parent_id", "composite"]
26
27
 
27
28
  scope :queue_order, -> { order(created_at: :asc) }
28
29
  scope :active, -> { where(status: [:pending, :running, :errored]) }
@@ -80,15 +81,10 @@ module OnlineMigrations
80
81
  def progress
81
82
  end
82
83
 
83
- # Whether the migration is considered stuck (is running for some configured time).
84
+ # Whether the migration is considered stuck.
84
85
  #
85
86
  def stuck?
86
- if index_addition?
87
- running? && !index_build_in_progress?
88
- else
89
- stuck_timeout = (statement_timeout || 1.day) + 10.minutes
90
- running? && updated_at <= stuck_timeout.seconds.ago
91
- end
87
+ index_addition? && running? && !index_build_in_progress?
92
88
  end
93
89
 
94
90
  # Mark this migration as ready to be processed again.
@@ -44,9 +44,8 @@ module OnlineMigrations
44
44
  # Background schema migrations could take a while to run. It is possible, that the process
45
45
  # never reaches this (or the rescue below) line of code. E.g., when it is force quitted
46
46
  # (SIGKILL etc.) and so the migration will end up in the "running" state and the query is
47
- # still executing (or already finished) in the database. This migration can either be safely
48
- # manually retried or will be picked up in the future by scheduler when it decides that
49
- # this migration is stuck.
47
+ # still executing (or already finished) in the database. This migration can be safely
48
+ # manually retried.
50
49
 
51
50
  migration.update!(status: :succeeded, finished_at: Time.current)
52
51
 
@@ -35,8 +35,8 @@ module OnlineMigrations
35
35
 
36
36
  private
37
37
  def find_migration(**options)
38
- stuck_migrations, active_migrations = Migration.running.partition(&:stuck?)
39
- runnable_migrations = (Migration.pending + Migration.errored + stuck_migrations).sort_by(&:created_at)
38
+ active_migrations = Migration.running.to_a
39
+ runnable_migrations = (Migration.pending + Migration.errored).sort_by(&:created_at)
40
40
 
41
41
  if options.key?(:shard)
42
42
  runnable_migrations = runnable_migrations.select { |migration| migration.shard.to_s == options[:shard].to_s }
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module OnlineMigrations
4
- VERSION = "0.35.0"
4
+ VERSION = "0.36.0"
5
5
  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.35.0
4
+ version: 0.36.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - fatkodima
@@ -41,6 +41,7 @@ files:
41
41
  - lib/generators/online_migrations/templates/add_sharding_to_online_migrations.rb.tt
42
42
  - lib/generators/online_migrations/templates/add_timestamps_to_background_migrations.rb.tt
43
43
  - lib/generators/online_migrations/templates/background_data_migrations_add_iteration_pause.rb.tt
44
+ - lib/generators/online_migrations/templates/background_data_migrations_convert_cursor.rb.tt
44
45
  - lib/generators/online_migrations/templates/background_data_migrations_remove_iteration_pause_default.rb.tt
45
46
  - lib/generators/online_migrations/templates/background_migrations_change_status_default.rb.tt
46
47
  - lib/generators/online_migrations/templates/background_schema_migrations_change_unique_index.rb.tt