cartodb/lib/carto/db/migration_helper.rb
2020-06-15 10:58:47 +08:00

56 lines
1.6 KiB
Ruby

module Carto
module Db
module MigrationHelper
LOCK_TIMEOUT_MS = 1000
MAX_RETRIES = 3
WAIT_BETWEEN_RETRIES_S = 2
def migration(up_block, down_block)
Sequel.migration do
# Forces this migration to run under a transaction (controlled by Sequel)
transaction
up do
lock_safe_migration(&up_block)
end
down do
lock_safe_migration(&down_block)
end
end
end
private
def lock_safe_migration(&block)
run "SET lock_timeout TO #{LOCK_TIMEOUT_MS}"
# As the external transaction is controlled by Sequel, we cannot ROLLBACK and BEGIN a new one
# Instead, we use SAVEPOINTs (https://www.postgresql.org/docs/current/static/sql-savepoint.html)
# to start a "sub-transaction" that we can rollback without affecting Sequel
run 'SAVEPOINT before_migration'
(1..MAX_RETRIES).each do
begin
instance_eval &block
return
rescue Sequel::DatabaseError => e
if e.message.include?('lock timeout')
# In case of timeout, we retry by reexecuting the code since the SAVEPOINT
run 'ROLLBACK TO SAVEPOINT before_migration'
sleep WAIT_BETWEEN_RETRIES_S
else
puts e.message
raise e
end
end
end
# Raising an exception forces Sequel to rollback the entire transaction
raise 'Retries exceeded during database migration'
ensure
run "SET lock_timeout TO DEFAULT"
end
end
end
end