Skip to content

Instantly share code, notes, and snippets.

@Loschcode
Created April 25, 2022 20:49
Show Gist options
  • Save Loschcode/d26b34446d56bb0f09bdbe2cfa375bd4 to your computer and use it in GitHub Desktop.
Save Loschcode/d26b34446d56bb0f09bdbe2cfa375bd4 to your computer and use it in GitHub Desktop.
# concurrent migration strategy
# when migrating through this script
# you can launch it on multiple processes
# and they will deal with the concurrency by
# themselves without interrupting or crashing
class MigrateConcurrently
class Error < StandardError; end
DELAY_BETWEEN_CYCLES_IN_SECONDS = 3
MAX_CYCLES = 100
def initialize; end
def perform
return true unless concurrency_error? { run_migrate }
wait_and_try_again
end
private
def wait_and_try_again
log_cycle
wait
next_cycle
perform
end
def run_migrate
ActiveRecord::Base.connection.migration_context.migrate
end
def concurrency_error?(&block)
yield
false
rescue ActiveRecord::ConcurrentMigrationError
true
end
def log_cycle
Logger.info(
message: "Migration is already running, waiting to try again",
type: "infrastructure_run_migrate",
delay_between_cycles_in_seconds: DELAY_BETWEEN_CYCLES_IN_SECONDS,
cycle: cycles.current,
max_cycles: cycles.max,
)
end
def wait
sleep DELAY_BETWEEN_CYCLES_IN_SECONDS
end
def next_cycle
raise_if_max_cycles_reached
cycles.next
end
def raise_if_max_cycles_reached
return unless cycles.max?
raise Error, """
Max cycles reached after trying #{cycles.current} times.
Something might be wrong with the migration process.
"""
end
def cycles
@cycles ||= Cycles.new(max: MAX_CYCLES)
end
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment