J'ai une application Rails qui utilise PostgreSQL comme backend avec un environnement cert qui essaie d'imiter la production, sauf qu'il doit avoir la base de données réinitialisée périodiquement pour le contrôle qualité.
Lorsque j'essaie d'exécuter db:reset
à partir d'une tâche Capistrano pendant le déploiement, j'obtiens l'erreur:
ERROR: database "database_name" is being accessed by other users
et la base de données ne peut pas être supprimée dans le cadre de la tâche de réinitialisation, ce qui entraîne l'échec du déploiement. Existe-t-il un moyen de réinitialiser les connexions de base de données à partir de Capistrano afin de pouvoir supprimer correctement la table? Piping le SQL vers psql à partir d'une tâche Capistrano pourrait fonctionner mais je me demandais s'il y avait une meilleure façon de procéder.
J'ai combiné réponse de dbenhur avec cette tâche Capistrano pour obtenir le résultat dont j'avais besoin fonctionne comme un charme:
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
Cela suppose que l'auth_method pour les postgres de l'utilisateur est défini sur 'trust' dans pg_hba.conf
Ensuite, vous pouvez simplement l'appeler dans votre tâche de déploiement après update_code
et avant migrate
after 'deploy:update_code', 'kill_postgres_connections'
Avec PostgreSQL, vous pouvez émettre l'instruction suivante pour renvoyer les pids backend de toutes les connexions ouvertes autres que celle-ci:
SELECT pid FROM pg_stat_activity where pid <> pg_backend_pid();
Ensuite, vous pouvez envoyer une demande de résiliation à chacun de ces backends avec
SELECT pg_terminate_backend($1);
Lier les pids renvoyés par la première instruction à chaque exécutable pg_terminate_backend.
Si les autres connexions n'utilisent pas le même utilisateur que vous, vous devrez vous connecter en tant que superutilisateur pour réussir l'émission des terminaisons.
MISE À JOUR: Incorporation des commentaires et expression en tant que tâche 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
Vous pouvez simplement monkeypatch le code ActiveRecord qui effectue la suppression.
Pour 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
Pour 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
(à partir de: http://www.krautcomputing.com/blog/2014/01/10/how-to-drop-your-postgres-database-with-Rails-4/ )