Class: OnlineMigrations::BackgroundSchemaMigrations::Migration

Inherits:
ApplicationRecord
  • Object
show all
Defined in:
lib/online_migrations/background_schema_migrations/migration.rb

Overview

Note:

The records of this class should not be created manually, but via ‘enqueue_background_schema_migration` helper inside migrations.

Class representing background schema migration.

Constant Summary collapse

STATUSES =
[
  :enqueued,    # The migration has been enqueued by the user.
  :running,     # The migration is being performed by a migration executor.
  :failed,      # The migration raises an exception when running.
  :succeeded,   # The migration finished without error.
  :cancelled,   # The migration was cancelled by the user.
]
MAX_IDENTIFIER_LENGTH =
63

Instance Method Summary collapse

Instance Method Details

#attempts_exceeded?Boolean

Returns:

  • (Boolean)


171
172
173
# File 'lib/online_migrations/background_schema_migrations/migration.rb', line 171

def attempts_exceeded?
  attempts >= max_attempts
end

#cancelled!Object Also known as: cancel

Overwrite enum’s generated method to correctly work for composite migrations.



87
88
89
90
91
92
93
94
# File 'lib/online_migrations/background_schema_migrations/migration.rb', line 87

def cancelled!
  return super if !composite?

  transaction do
    super
    children.each { |child| child.cancelled! if !child.succeeded? }
  end
end

#completed?Boolean

Returns:

  • (Boolean)


82
83
84
# File 'lib/online_migrations/background_schema_migrations/migration.rb', line 82

def completed?
  succeeded? || failed?
end

#connection_classObject



162
163
164
165
166
167
168
# File 'lib/online_migrations/background_schema_migrations/migration.rb', line 162

def connection_class
  if connection_class_name && (klass = connection_class_name.safe_constantize)
    Utils.find_connection_class(klass)
  else
    ActiveRecord::Base
  end
end

#index_addition?Boolean

Returns:

  • (Boolean)


157
158
159
# File 'lib/online_migrations/background_schema_migrations/migration.rb', line 157

def index_addition?
  definition.match?(/create (unique )?index/i)
end

#progressFloat

Returns the progress of the background schema migration.

Returns:

  • (Float)

    value in range from 0.0 to 100.0



101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
# File 'lib/online_migrations/background_schema_migrations/migration.rb', line 101

def progress
  if succeeded?
    100.0
  elsif composite?
    progresses = children.map(&:progress)
    # There should not be composite migrations without children,
    # but children may be deleted for some reason, so we need to
    # make a check to avoid 0 division error.
    if progresses.any?
      (progresses.sum.to_f / progresses.size).round(2)
    else
      0.0
    end
  else
    0.0
  end
end

#retryObject

Mark this migration as ready to be processed again.

This is used to manually retrying failed migrations.



133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
# File 'lib/online_migrations/background_schema_migrations/migration.rb', line 133

def retry
  if composite? && failed?
    children.failed.each(&:retry)
    update!(
      status: self.class.statuses[:enqueued],
      finished_at: nil
    )
    true
  elsif failed?
    update!(
      status: self.class.statuses[:enqueued],
      attempts: 0,
      started_at: nil,
      finished_at: nil,
      error_class: nil,
      error_message: nil,
      backtrace: nil
    )
    true
  else
    false
  end
end

#runObject



176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
# File 'lib/online_migrations/background_schema_migrations/migration.rb', line 176

def run
  on_shard do
    connection = connection_class.connection

    connection.with_lock_retries do
      statement_timeout = self.statement_timeout || OnlineMigrations.config.statement_timeout

      with_statement_timeout(connection, statement_timeout) do
        if index_addition?
          index = connection.indexes(table_name).find { |i| i.name == name }
          if index
            # Use index validity from https://github.com/rails/rails/pull/45160
            # when switching to ActiveRecord >= 7.1.
            schema = connection.send(:__schema_for_table, table_name)
            if connection.send(:__index_valid?, name, schema: schema)
              return
            else
              connection.remove_index(table_name, name: name, algorithm: :concurrently)
            end
          end
        end

        connection.execute(definition)
      end
    end
  end
end

#stuck?Boolean

Whether the migration is considered stuck (is running for some configured time).

Returns:

  • (Boolean)


121
122
123
124
125
126
127
# File 'lib/online_migrations/background_schema_migrations/migration.rb', line 121

def stuck?
  # Composite migrations are not considered stuck.
  return false if composite?

  stuck_timeout = (statement_timeout || 1.day) + 10.minutes
  running? && updated_at <= stuck_timeout.seconds.ago
end