30

我有一个使用 PostgreSQL 作为后端的 Rails 应用程序,它带有一个尝试模仿生产的证书环境,除了它需要定期重置数据库以进行质量检查。

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

ERROR: database "database_name" is being accessed by other users

并且数据库不能作为重置任务的一部分被删除,从而导致部署失败。有没有办法可以从 Capistrano 重置数据库连接,以便成功删除表?从 Capistrano 任务将 SQL 通过管道传输到 psql 可能有效,但我想知道是否有更好的方法来解决这个问题。

4

3 回答 3

55

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

SELECT pid FROM pg_stat_activity where pid <> pg_backend_pid();

然后您可以向每个后端发出终止请求

SELECT pg_terminate_backend($1);

将从第一条语句返回的 pid 绑定到每个 pg_terminate_backend exec。

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

更新:合并评论并表达为 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
于 2012-10-16T22:59:03.783 回答
5

我将dbenhur 的答案与 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

这假定用户 postgres 的 auth_method 在 pg_hba.conf 中设置为“信任”

然后你可以在你的部署任务之后update_code和之前调用它migrate

after 'deploy:update_code', 'kill_postgres_connections'
于 2012-10-17T16:45:07.973 回答
1

您可以简单地对执行删除的 ActiveRecord 代码进行猴子补丁。

对于 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

(来自:http ://www.krautcomputing.com/blog/2014/01/10/how-to-drop-your-postgres-database-with-rails-4/ )

于 2014-01-10T14:49:46.903 回答