Ruby on rails Capistrano与PostgreSQL,错误:其他用户正在访问数据库

Ruby on rails Capistrano与PostgreSQL,错误:其他用户正在访问数据库,ruby-on-rails,postgresql,capistrano,Ruby On Rails,Postgresql,Capistrano,我有一个Rails应用程序,它使用PostgreSQL作为后端,并带有一个尝试模拟生产的cert环境,只是它需要定期重置数据库以进行QA 在部署期间,当我尝试从Capistrano任务执行db:reset时,我得到错误: 错误:其他用户正在访问数据库“database\u name” 并且数据库不能作为重置任务的一部分删除,从而导致部署失败。是否有一种方法可以从Capistrano重置数据库连接,以便成功删除该表?从Capistrano任务将SQL管道化到psql可能会起作用,但我想知道是否有更

我有一个Rails应用程序,它使用PostgreSQL作为后端,并带有一个尝试模拟生产的cert环境,只是它需要定期重置数据库以进行QA

在部署期间,当我尝试从Capistrano任务执行
db:reset
时,我得到错误:

错误:其他用户正在访问数据库“database\u name”


并且数据库不能作为重置任务的一部分删除,从而导致部署失败。是否有一种方法可以从Capistrano重置数据库连接,以便成功删除该表?从Capistrano任务将SQL管道化到psql可能会起作用,但我想知道是否有更好的方法来实现这一点。

对于PostgreSQL,您可以发出以下语句来返回除此之外的所有打开连接的后端PID:

SELECT pid FROM pg_stat_activity where pid <> pg_backend_pid();
将从第一条语句返回的PID绑定到每个pg_terminate_后端执行器

如果其他连接没有使用与您相同的用户,则您必须以超级用户身份连接才能成功发出终止

更新:合并评论并表示为Capistrano任务:

desc "Force disconnect of open backends and drop database"
task :force_close_and_drop_db do
  dbname = 'your_database_name'
  run "psql -U postgres",
      :data => <<-"PSQL"
         REVOKE CONNECT ON DATABASE #{dbname} FROM public;
         ALTER DATABASE #{dbname} CONNECTION LIMIT 0;
         SELECT pg_terminate_backend(pid)
           FROM pg_stat_activity
           WHERE pid <> pg_backend_pid()
           AND datname='#{dbname}';
         DROP DATABASE #{dbname};
      PSQL
end
desc“强制断开开放后端并删除数据库”
任务:强制关闭和丢弃数据库
dbname='您的数据库名称'
运行“psql-U postgres”,
:data=>我已将此Capistrano任务与之结合,以实现我所需的结果,效果非常出色:

desc 'kill pgsql users so database can be dropped'
task :kill_postgres_connections do
  run 'echo "SELECT pg_terminate_backend(procpid) FROM pg_stat_activity WHERE datname=\'database_name\';" | psql -U postgres'
end
这假设在pg_hba.conf中将用户postgre的auth_方法设置为“trust”

然后您可以在
update\u code
之后和
migrate
之前在部署任务中调用它

after 'deploy:update_code', 'kill_postgres_connections'

您可以简单地对执行删除操作的ActiveRecord代码进行monkeypatch

对于Rails 3.x:

# lib/tasks/databases.rake
def drop_database(config)
  raise 'Only for Postgres...' unless config['adapter'] == 'postgresql'
  Rake::Task['environment'].invoke
  ActiveRecord::Base.connection.select_all "select pg_terminate_backend(pg_stat_activity.pid) from pg_stat_activity where datname='#{config['database']}' AND state='idle';"
  ActiveRecord::Base.establish_connection config.merge('database' => 'postgres', 'schema_search_path' => 'public')
  ActiveRecord::Base.connection.drop_database config['database']
end
对于Rails 4.x:

# config/initializers/postgresql_database_tasks.rb
module ActiveRecord
  module Tasks
    class PostgreSQLDatabaseTasks
      def drop
        establish_master_connection
        connection.select_all "select pg_terminate_backend(pg_stat_activity.pid) from pg_stat_activity where datname='#{configuration['database']}' AND state='idle';"
        connection.drop_database configuration['database']
      end
    end
  end
end

(from:)

我建议从public中撤销数据库dbname上的连接然后
从pg_stat_活动中选择pg_terminate_backend(pid),其中pid pg_backend_pid()
后跟
删除数据库dbname。这样就避免了新客户端不断连接的竞争,因此您永远没有足够的时间删除数据库。另一个建议是:首先运行“ALTER DATABASE dbname CONNECTION LIMIT 0”,然后终止查询。如果您使用的是psql,将连接限制设置为0并不是连接数据库的障碍。在我使用的版本(9.1.9)中,pid不是列名,但procpid是:从pg_stat_活动中选择procpid,其中procpid pg_backend_pid();我刚刚得到
错误:没有参数$1
@mkataja
$1
是要终止的后端pid的占位符。如果您只是使用psql执行sql语句,请将pid数字替换为$1。如果您正在编写程序,大多数db接口库都提供了将参数值绑定到包含
$x
占位符的语句的方法,因此使用该方法可以提供一个包含要终止的pid的整数。顺便说一句,您不需要echo和管道将语句发送到psql。只需使用
-c
cmdline选项即可。@dbenhur:您可以使用包含的-c选项更新您的注释吗?
# config/initializers/postgresql_database_tasks.rb
module ActiveRecord
  module Tasks
    class PostgreSQLDatabaseTasks
      def drop
        establish_master_connection
        connection.select_all "select pg_terminate_backend(pg_stat_activity.pid) from pg_stat_activity where datname='#{configuration['database']}' AND state='idle';"
        connection.drop_database configuration['database']
      end
    end
  end
end