我有一个Rails应用程序,它使用PostgreSQL作为后端,具有试图模仿生产的证书环境,除了它需要定期重置数据库以进行QA。
当我在部署期间尝试从Capistrano任务执行db:reset
时,我收到错误:
ERROR: database "database_name" is being accessed by other users
并且无法将数据库作为重置任务的一部分删除,从而导致部署失败。有没有办法可以从Capistrano重置数据库连接,这样我就可以成功删除表格?从Capistrano任务管理SQL到psql可能会有效,但我想知道是否有更好的方法来解决这个问题。
答案 0 :(得分:55)
使用PostgreSQL,您可以发出以下语句来返回除此之外的所有打开连接的后端pids:
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
答案 1 :(得分:5)
我已将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'
答案 2 :(得分:1)
您可以简单地对执行删除的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
(来自:http://www.krautcomputing.com/blog/2014/01/10/how-to-drop-your-postgres-database-with-rails-4/)