web-dev-qa-db-ja.com

Capistrano with PostgreSQL、エラー:他のユーザーがデータベースにアクセスしています

私はRails QAのためにデータベースを定期的にリセットする必要があることを除いて、本番環境を模倣しようとする証明書環境でPostgreSQLをバックエンドとして使用するアプリを持っています。

展開中にCapistranoタスクからdb:resetを実行しようとすると、次のエラーが表示されます。

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

また、リセットタスクの一部としてデータベースをドロップすることはできず、デプロイメントが失敗します。テーブルを正常に削除できるように、Capistranoからのデータベース接続をリセットする方法はありますか? CapistranoタスクからSQLをpsqlにパイピングすることはうまくいくかもしれませんが、これについてもっと良い方法があるのではないかと思っていました。

30
Graham Conzett

dbenhur's answer とこの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で「trust」に設定されていることを前提としています

その後、update_codeの後でmigrateの前にデプロイタスクで呼び出すことができます

after 'deploy:update_code', 'kill_postgres_connections'
5
Graham Conzett

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
55
dbenhur

ドロップを行う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

(from: http://www.krautcomputing.com/blog/2014/01/10/how-to-drop-your-postgres-database-with-Rails-4/

1
Manuel Meurer