Erreur de suppression de Rails + Postgres: la base de données est en cours d'accès par d'autres utilisateurs

90

J'ai une application de rails fonctionnant sur Postgres.

J'ai deux serveurs: un pour les tests et l'autre pour la production.

Très souvent, j'ai besoin de cloner la base de données de production sur le serveur de test.

La commande que je lance via Vlad est:

rake RAILS_ENV='test_server' db:drop db:create

Le problème que je rencontre est que je reçois l'erreur suivante:

ActiveRecord::StatementInvalid: PGError: ERROR: database <database_name> is being accessed by other users DROP DATABASE IF EXISTS <database_name>

Cela se produit si quelqu'un a récemment accédé à l'application via le Web (postgres garde une "session" ouverte)

Est-il possible que je puisse mettre fin aux sessions sur la base de données postgres?

Je vous remercie.

Éditer

Je peux supprimer la base de données en utilisant l'interface de phppgadmin mais pas avec la tâche rake.

Comment puis-je répliquer le drop de phppgadmin avec une tâche rake?

fjuan
la source
Assurez-vous que vous n'avez pas de connexions à la base de données ou elle ne la supprimera pas. Cliquez ici pour en savoir plus .
Nesha Zoric

Réponses:

81

Si vous supprimez les connexions postgresql en cours d'exécution pour votre application, vous pouvez alors exécuter db: drop très bien. Alors, comment tuer ces connexions? J'utilise la tâche de râteau suivante:

# lib/tasks/kill_postgres_connections.rake
task :kill_postgres_connections => :environment do
  db_name = "#{File.basename(Rails.root)}_#{Rails.env}"
  sh = <<EOF
ps xa \
  | grep postgres: \
  | grep #{db_name} \
  | grep -v grep \
  | awk '{print $1}' \
  | xargs kill
EOF
  puts `#{sh}`
end

task "db:drop" => :kill_postgres_connections

Tuer les connexions sous les rails le fera parfois barf la prochaine fois que vous essayez de charger une page, mais le recharger à nouveau rétablit la connexion.

encodé
la source
2
J'ai dû ajouter sudo à xargs et changer le nom de la base de données, mais cela fonctionne. TY
lzap
1
Même chose pour moi ... changé en "sudo xargs kill" et db_name codé en dur en "my-development-database-name"
Kevin Dewalt
6
task "db:drop" => :kill_postgres_connectionsJe pense que cette ligne devrait être supprimée, il risque d'étendre le comportement de la tâche système, de mon point de vue.
msa.im
Plutôt que de coder en dur le nom de votre base de données, utilisez simplement ce qui suit:db_name = Rails.configuration.database_configuration[Rails.env]['database']
tala
39

Un moyen plus simple et plus mis à jour est: 1. Utilisez ps -ef | grep postgrespour trouver la connexion # 2.sudo kill -9 "# of the connection

Remarque: il peut y avoir un PID identique. Tuer un tue tout.

M. René
la source
Quel nombre dans le résultat représente le PID? Je vois 3 colonnes sans étiquette avec des nombres qui ressemblent à PID.
BradGreens
1
@BradGreens deuxième colonne (j'utilise Mac Terminal)
s2t2
Rien n'est trouvé avec ps, mais obtenez toujours l'erreur sur db: drop.
JosephK
17

Voici un moyen rapide de supprimer toutes les connexions à votre base de données postgres.

sudo kill -9 `ps -u postgres -o pid` 

Attention: cela tuera tous les processus en cours que l' postgresutilisateur a ouverts, alors assurez-vous de vouloir le faire en premier.

Jamon Holmgren
la source
11
Dans mon système, j'utilise à la sudo kill -9 `ps -u postgres -o pid=` place, donc un en- tête PID ne sera pas imprimé par ps, donc un argument de chaîne n'est pas passé à kill, donc une erreur ne sera pas déclenchée. Bon conseil en tout cas.
deivid
1
Je continue de recevoir des votes positifs et négatifs, ce qui me donne une cote proche de zéro. Semble être une «solution miracle» controversée. Permettez - moi de dire officiellement que je l'ai fait un avertissement qu'il est dangereux. :)
Jamon Holmgren
3
Utilisez ceci pour redémarrer postgresql si vous êtes sur Ubuntu:sudo service postgresql start
Frikster
9

Lorsque nous avons utilisé la méthode "kill processus" ci-dessus, le db: drop échouait (si: kill_postgres_connections était un prérequis). Je crois que c'était parce que la connexion que cette commande rake utilisait était en train d'être supprimée. Au lieu de cela, nous utilisons une commande sql pour supprimer la connexion. Cela fonctionne comme un prérequis pour db: drop, évite le risque de tuer des processus via une commande plutôt complexe, et cela devrait fonctionner sur n'importe quel OS (gentoo nécessitait une syntaxe différente pour kill).

cmd = %(psql -c "SELECT pg_terminate_backend(procpid) FROM pg_stat_activity WHERE procpid <> pg_backend_pid();" -d '#{db_name}')

Voici une tâche de râteau qui lit le nom de la base de données à partir de database.yml et exécute une commande améliorée (IMHO). Il ajoute également db: kill_postgres_connections comme condition préalable à db: drop. Il comprend un avertissement qui hurle après la mise à niveau des rails, indiquant que ce correctif n'est peut-être plus nécessaire.

voir: https://gist.github.com/4455341 , références incluses

Matt Scilipoti
la source
8

J'utilise la tâche rake suivante pour remplacer la drop_databaseméthode Rails .

lib/database.rake

require 'active_record/connection_adapters/postgresql_adapter'
module ActiveRecord
  module ConnectionAdapters
    class PostgreSQLAdapter < AbstractAdapter
      def drop_database(name)
        raise "Nah, I won't drop the production database" if Rails.env.production?
        execute <<-SQL
          UPDATE pg_catalog.pg_database
          SET datallowconn=false WHERE datname='#{name}'
        SQL

        execute <<-SQL
          SELECT pg_terminate_backend(pg_stat_activity.pid)
          FROM pg_stat_activity
          WHERE pg_stat_activity.datname = '#{name}';
        SQL
        execute "DROP DATABASE IF EXISTS #{quote_table_name(name)}"
      end
    end
  end
end
Chris Aitchison
la source
Avez-vous déjà fini par lire cet avertissement de production? Juste curieux: P
Vinicius Brasil
6

Veuillez vérifier si votre console ou serveur rails s'exécute dans un autre onglet, puis

arrêtez le serveur de rails et la console.

puis courir

 rake db:drop
Amol Udage
la source
5

Laissez votre application fermer la connexion lorsqu'elle est terminée. PostgreSQL ne garde pas les connexions ouvertes, c'est l'application qui maintient la connexion.

Frank Heikens
la source
1
Je peux supprimer la base de données en utilisant l'interface de phppgadmin mais pas avec la tâche rake. Comment puis-je répliquer le drop de phppgadmin avec une tâche rake?
fjuan
Désolé, je ne peux pas vous aider, je n'ai aucune expérience avec le rake. Mais l'erreur indique qu'un autre utilisateur utilise toujours la base de données. C'est pourquoi vous ne pouvez pas supprimer la base de données, pas par rake pas par PhpPgAdmin, impossible. D'après le manuel, DROP DATABASE: il ne peut pas être exécuté tant que vous ou quelqu'un d'autre êtes connecté à la base de données cible.
Frank Heikens
3

Rails se connecte probablement à la base de données pour la supprimer, mais lorsque vous vous connectez via phppgadmin, il se connecte via la base de données template1 ou postgres, vous n'êtes donc pas affecté par cela.

Joshua D. Drake
la source
Comment puis-je forcer les rails à supprimer la base de données? Dois-je définir ma propre action de rake à l'aide des commandes SQL postgres?
fjuan
2

J'ai écrit un joyau appelé pgreset qui supprimera automatiquement les connexions à la base de données en question lorsque vous exécutez rake db: drop (ou db: reset, etc.). Tout ce que vous avez à faire est de l'ajouter à votre Gemfile et ce problème devrait disparaître. Au moment d'écrire ces lignes, il fonctionne avec Rails 4 et plus et a été testé sur Postgres 9.x. Le code source est disponible sur github pour toute personne intéressée.

gem 'pgreset'
Dan
la source
Rien d'autre que votre gemme ne le ferait - la connexion n'existait pas (recherche ps grep, etc.), mais les rails pensaient que c'était le cas. Merci beaucoup!!
JosephK
1

Vous pouvez simplement monkeypatch le code ActiveRecord qui effectue la suppression.

Pour les 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 les 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/ )

Manuel Meurer
la source
1

J'ai eu ce même problème lorsque je travaillais avec une application Rails 5.2 et une base de données PostgreSQL en production.

Voici comment je l'ai résolu :

Tout d'abord, déconnectez-vous de chaque connexion au serveur de base de données sur le client PGAdmin, le cas échéant.

Arrêtez chaque session en utilisant la base de données du terminal.

sudo kill -9 `ps -u postgres -o pid=`

Démarrez le serveur PostgreSQL, puisque l'opération de suppression ci-dessus a arrêté le serveur PostgreSQL.

sudo systemctl start postgresql

Déposez la base de données dans l'environnement de production en ajoutant les arguments de production.

rails db:drop RAILS_ENV=production DISABLE_DATABASE_ENVIRONMENT_CHECK=1

C'est tout.

J'espère que ça aide

Promesse Preston
la source
1

Cela a fonctionné pour moi (rails 6): rake db:drop:_unsafe

Je pense que nous avions quelque chose dans notre base de code qui a initié une connexion db avant que la tâche rake ne tente de la supprimer.

cyrilchampier
la source
0

Assurez-vous simplement que vous avez quitté la console des rails sur n'importe quelle fenêtre de terminal ouverte et que vous avez quitté le serveur de rails ... c'est l'une des erreurs les plus courantes commises par les gens.

Mutuma
la source
0

J'ai eu une erreur similaire en disant qu'un utilisateur utilisait la base de données, j'ai réalisé que c'était MOI! J'ai arrêté mon serveur rails, puis j'ai exécuté la commande rake: drop et cela a fonctionné!

ravip0711
la source
0

Après avoir redémarré le serveur ou l'ordinateur, veuillez réessayer.

Cela pourrait être la solution simple.

hobbydev
la source
0

Solution

Script bash

ENV=development

# restart postgresql
brew services restart postgresql

# get name of the db from rails app
RAILS_CONSOLE_COMMAND="bundle exec rails c -e $ENV"
DB_NAME=$(echo 'ActiveRecord::Base.connection_config[:database]' | $RAILS_CONSOLE_COMMAND | tail -2 | tr -d '\"')

# delete all connections to $DB_NAME
for pid in $(ps -ef | grep $DB_NAME | awk {'print$2'})
do
   kill -9 $pid
done

# drop db
DISABLE_DATABASE_ENVIRONMENT_CHECK=1 RAILS_ENV=$ENV bundle exec rails db:drop:_unsafe
Vasily Bodnarchuk
la source