ruby Rails 5数据库迁移:如何修复活动记录::并发迁移错误

vc6uscn9  于 2023-03-08  发布在  Ruby
关注(0)|答案(5)|浏览(279)

先前的rake db:rollback已停止。现在,当尝试新迁移时,我们收到以下错误:

rake aborted!
ActiveRecord::ConcurrentMigrationError: 

Cannot run migrations because another migration process is currently running.

/home/me/.rvm/gems/ruby-2.4.1@global/gems/activerecord-    5.1.4/lib/active_record/migration.rb:1315:in `with_advisory_lock'
/home/me/.rvm/gems/ruby-2.4.1@global/gems/activerecord-5.1.4/lib/active_record/migration.rb:1148:in `migrate'
/home/me/.rvm/gems/ruby-2.4.1@global/gems/activerecord-5.1.4/lib/active_record/migration.rb:1007:in `up'
/home/me/.rvm/gems/ruby-2.4.1@global/gems/activerecord-5.1.4/lib/active_record/migration.rb:985:in `migrate'
/home/me/.rvm/gems/ruby-2.4.1@global/gems/activerecord-5.1.4/lib/active_record/tasks/database_tasks.rb:171:in `migrate'
/home/me/.rvm/gems/ruby-2.4.1@global/gems/activerecord-5.1.4/lib/active_record/railties/databases.rake:58:in `block (2 levels) in <top (required)>'
/home/me/.rvm/gems/ruby-2.4.1/gems/rake-12.1.0/exe/rake:27:in `<top (required)>'
/home/me/.rvm/gems/ruby-2.4.1/bin/ruby_executable_hooks:15:in `eval'
/home/me/.rvm/gems/ruby-2.4.1/bin/ruby_executable_hooks:15:in `<main>'
Tasks: TOP => db:migrate
(See full trace by running task with --trace)

我们正在使用Postresql

db2dz4w8

db2dz4w81#

对我来说,它是这样解决的:
选择建议锁定:

SELECT pid, locktype, mode FROM pg_locks WHERE locktype = 'advisory';
SELECT pg_terminate_backend(<PID>);
ego6inou

ego6inou2#

在Rails5中添加了建议锁定,以防止迁移过程中出现意外的并发错误。修复方法是清除保留的DB锁定。
通过对DB运行以下SQL来查看锁:

SELECT DISTINCT age(now(), query_start) AS age, pg_stat_activity.pid,pg_locks.granted,pg_stat_activity.application_name,pg_stat_activity.backend_start, pg_stat_activity.xact_start, pg_stat_activity.state_change, pg_stat_activity.waiting, pg_stat_activity.state, pg_stat_activity.query_start, left(pg_stat_activity.query, 60)
FROM pg_stat_activity, pg_locks
WHERE pg_locks.pid = pg_stat_activity.pid

要清除锁,请对您的DB运行以下SQL:

select pg_advisory_unlock({the pid of the lock you want to release})
af7jpaap

af7jpaap3#

所以在我的例子中,查询是不同的

SELECT DISTINCT age(now(), query_start) AS age, pg_stat_activity.pid,pg_locks.granted,pg_stat_activity.application_name,pg_stat_activity.backend_start, pg_stat_activity.xact_start, pg_stat_activity.state_change, pg_stat_activity.state, pg_stat_activity.query_start, left(pg_stat_activity.query, 60)
    FROM pg_stat_activity, pg_locks
    WHERE pg_locks.pid = pg_stat_activity.pid

它基本上会告诉你

0 years 0 mons 0 days 0 hours 0 mins -0.01005 secs    360    true    PostgreSQL JDBC Driver    2019-04-03 16:57:16.873609    2019-04-03 16:58:00.531675    2019-04-03 16:58:00.541727    active    2019-04-03 16:58:00.541725    SELECT DISTINCT age(now(), query_start) AS age, pg_stat_acti
    17272    true    ""                        <insufficient privilege>
    22640    true    ""                        <insufficient privilege>
    29466    true    ""                        <insufficient privilege>

然后你可以简单地用下面的命令解锁pid:select pg_advisory_unlock(#{target_pid})
例如:

select pg_advisory_unlock(17272)
select pg_advisory_unlock(22640)
select pg_advisory_unlock(22640)
select pg_advisory_unlock(360)

干杯!

k97glaaz

k97glaaz4#

当使用PostgreSQL的连接池时(例如PgBouncer或Yandex Cloud Odyssey中的连接池),
如果连接池模式为“事务”,则添加到database.yml:

prepared_statements: false
advisory_locks: false

如果连接管理器模式为“会话”,则添加到database.yml:

prepared_statements: true
advisory_locks: false
rfbsl7qr

rfbsl7qr5#

我来这里是为了寻找ActiveRecord::ConcurrentMigrationError的解决方案,但在我的情况下,没有任何迁移停止。事实上,我有许多EC2示例运行我的Rails应用,每当我部署新代码时,一些服务器成功完成,而其他服务器则因为上述错误而无法完成
如果您像我一样,出于同样的原因来到这里,请参见下面我为完成迁移而创建的monkey补丁。

# Base: https://github.com/rails/rails/blob/v7.0.1/activerecord/lib/active_record/migration.rb#L1424-L1439

# Everytime we merge our code, it gets deployed to some different servers.
# During deployment, the steps described in scripts/dependencies_install.sh
# get executed.
#
# Do notice that part of what that script does is running migrations. In our
# context (i.e., deploying the same code to multiple servers), it's problematic
# because if we had migrations running concurrently, we'd end-up with
# unexpected behavior.
#
# That's why Rails puts a "lock" within the database and raises an error if we
# try to run migrations concurrently.
#
# Below monkey patch was designed to bypass the
# ActiveRecord::ConcurrentMigrationError exception (raised when concurrent
# migrations are run).
#
# It works by retrying the migration during 120 seconds in case the
# ActiveRecord::ConcurrentMigrationError error gets raised.
#
# By retrying (instead of simply ignoring the error), we keep the old
# version of the system running while the new one will have to wait
# until the other server finishes running the migration. Once it
# happens, this task will realize it doesn't have to run migrations
# anymore - and thus the deployment script will simply proceed to restarting
# the server.
#
# This approach is important because, if we simply ignored the error,
# we could get in a situation in which the new version of the system
# would be incompatible with the DB schema - which would lead to breaks
# until the other server finished running migrations.

module ActiveRecord
  class Migrator # :nodoc:
    MAX_RETRIES = 120 # 2 minutes

    private

    def with_advisory_lock
      lock_id = generate_migrator_advisory_lock_id
      retry_count = 0

      with_advisory_lock_connection do |connection|
        got_lock = connection.get_advisory_lock(lock_id)
        raise ConcurrentMigrationError unless got_lock
        load_migrated # reload schema_migrations to be sure it wasn't changed by another process before we got the lock
        yield
      rescue ActiveRecord::ConcurrentMigrationError => e # main addition by the monkey patch
        sleep 1
        retry unless (retry_count += 1) >= MAX_RETRIES
        raise e
      ensure
        if got_lock && !connection.release_advisory_lock(lock_id)
          raise ConcurrentMigrationError.new(
            ConcurrentMigrationError::RELEASE_LOCK_FAILED_MESSAGE
          )
        end
      end
    end
  end
end

相关问题