Capistrano с PostgreSQL, ошибка: к базе данных обращаются другие пользователи
У меня есть приложение Rails, которое использует PostgreSQL в качестве бэкэнд с средой cert, которая пытается имитировать производство, за исключением того, что для QA периодически требуется база данных reset.
Когда я пытаюсь выполнить db:reset
из задачи Capistrano во время развертывания, я получаю ошибку:
ERROR: database "database_name" is being accessed by other users
и база данных не может быть удалена как часть задачи reset, что приведет к сбою развертывания. Есть ли способ, которым я могу подключиться к базе данных reset из Capistrano, чтобы успешно удалить таблицу? Трубопровод SQL к psql из задачи Capistrano может работать, но мне было интересно, есть ли лучший способ сделать это.
Ответы
Ответ 1
Я объединил dbenhur 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
Это предполагает, что auth_method для postgres пользователя установлен в 'trust' в pg_hba.conf
Затем вы можете просто вызвать его в своей задаче развертывания после update_code
и до migrate
after 'deploy:update_code', 'kill_postgres_connections'
Ответ 2
В PostgreSQL вы можете выпустить следующий оператор для возврата бэкэндов всех открытых соединений, кроме этого:
SELECT pid FROM pg_stat_activity where pid <> pg_backend_pid();
Затем вы можете выдать запрос завершения для каждого из этих бэкэндов с помощью
SELECT pg_terminate_backend($1);
Связывание pids, возвращаемых из первого оператора, в каждый pg_terminate_backend exec.
Если другие соединения не используют того же пользователя, что и вы, вам нужно будет подключиться как суперпользователь, чтобы успешно выполнить завершение.
UPDATE: включение комментариев и выражение в качестве задачи 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
Ответ 3
Вы можете просто обезвредить код 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/)