Rails + Postgres Drop-Fehler: Auf die Datenbank wird von anderen Benutzern zugegriffen


89

Ich habe eine Rails-Anwendung, die über Postgres läuft.

Ich habe zwei Server: einen zum Testen und einen für die Produktion.

Sehr oft muss ich die Produktionsdatenbank auf dem Testserver klonen.

Der Befehl, den ich über Vlad ausführe, lautet:

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

Das Problem, das ich habe, ist, dass ich den folgenden Fehler erhalte:

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

Dies passiert, wenn jemand kürzlich über das Web auf die Anwendung zugegriffen hat (postgres hält eine "Sitzung" geöffnet)

Gibt es eine Möglichkeit, die Sitzungen in der Postgres-Datenbank zu beenden?

Danke dir.

Bearbeiten

Ich kann die Datenbank über die Schnittstelle von phppgadmin löschen, aber nicht mit der Rake-Task.

Wie kann ich den Drop von phppgadmin mit einer Rake-Aufgabe replizieren?


Stellen Sie sicher, dass Sie keine Verbindungen zur Datenbank haben, da diese sonst nicht gelöscht werden. Weitere Informationen hierzu finden Sie hier .
Nesha Zoric

Antworten:


80

Wenn Sie die laufenden Postgresql-Verbindungen für Ihre Anwendung beenden, können Sie db: drop ganz gut ausführen. Wie kann man diese Verbindungen beenden? Ich benutze die folgende Rechenaufgabe:

# 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

Wenn Sie die Verbindungen unter den Schienen beenden, wird sie manchmal beim nächsten Versuch, eine Seite zu laden, blockiert. Wenn Sie sie jedoch erneut laden, wird die Verbindung wiederhergestellt.


2
Ich musste sudo zu xargs hinzufügen und den Datenbanknamen ändern, aber es funktioniert. TY
lzap

1
Das gleiche gilt für mich ... geändert in "sudo xargs kill" und fest codierter Datenbankname in "my-development-database-name"
Kevin Dewalt

6
task "db:drop" => :kill_postgres_connectionsIch denke, diese Zeile sollte entfernt werden, es besteht aus meiner Sicht die Gefahr, das Verhalten der Systemaufgabe zu erweitern.
msa.im

Anstatt Ihren Datenbanknamen fest zu codieren, verwenden Sie einfach Folgendes:db_name = Rails.configuration.database_configuration[Rails.env]['database']
Tala

38

Einfacher und aktueller ist: 1. Verwenden Sie ps -ef | grep postgresdiese Option , um die Verbindung Nr. 2 zu finden.sudo kill -9 "# of the connection

Hinweis: Möglicherweise ist die PID identisch. Wenn man einen tötet, werden alle getötet.


Welche Zahl im Ergebnis repräsentiert die PID? Ich sehe 3 unbeschriftete Spalten mit Zahlen, die wie PID aussehen.
BradGreens

1
@BradGreens zweite Spalte (ich benutze Mac Terminal)
s2t2

Mit ps wird nichts gefunden, aber es wird immer noch der Fehler auf db: drop angezeigt.
JosephK

17

Hier ist eine schnelle Möglichkeit, alle Verbindungen zu Ihrer Postgres-Datenbank zu beenden.

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

Warnung: Dadurch werden alle laufenden Prozesse beendet, die der postgresBenutzer geöffnet hat. Stellen Sie daher sicher, dass Sie dies zuerst tun möchten.


11
In meinem System verwende ich sudo kill -9 `ps -u postgres -o pid=` stattdessen, damit kein PID- Header gedruckt wird ps, sodass kein String-Argument an übergeben wird und kein killFehler ausgelöst wird. Toller Tipp auf jeden Fall.
Deivid

1
Ich werde immer wieder hoch- und runtergestimmt, was zu einer Bewertung nahe Null führt. Scheint eine kontroverse "schnelle Lösung" zu sein. Lassen Sie mich nur Zustand der Ordnung halber , dass ich habe eine Warnung , dass es gefährlich ist. :)
Jamon Holmgren

3
Verwenden Sie dies, um postgresql erneut zu starten, wenn Sie auf Ubuntu sind:sudo service postgresql start
Frikster

9

Als wir die Methode "Kill-Prozesse" von oben verwendeten, schlug der db: drop fehl (wenn: kill_postgres_connections Voraussetzung war). Ich glaube, das lag daran, dass die Verbindung, die dieser Rechenbefehl verwendete, unterbrochen wurde. Stattdessen verwenden wir einen SQL-Befehl, um die Verbindung zu trennen. Dies ist eine Voraussetzung für db: drop, vermeidet das Risiko, Prozesse über einen recht komplexen Befehl abzubrechen, und sollte auf jedem Betriebssystem funktionieren (Gentoo erfordert eine andere Syntax für kill).

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

Hier ist eine Rake-Task, die den Datenbanknamen aus database.yml liest und einen verbesserten Befehl (IMHO) ausführt. Außerdem wird db: kill_postgres_connections als Voraussetzung für db: drop hinzugefügt. Es enthält eine Warnung, die nach dem Upgrade der Schienen ertönt und darauf hinweist, dass dieser Patch möglicherweise nicht mehr benötigt wird.

Siehe: https://gist.github.com/4455341 , einschließlich Referenzen


8

Ich verwende die folgende Rechenaufgabe, um die Rails- drop_databaseMethode zu überschreiben .

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

Haben Sie jemals diese Produktionswarnung gelesen? Nur neugierig: P
Vinicius Brasil

6

Bitte überprüfen Sie, ob Ihre Rails-Konsole oder Ihr Rails-Server auf einer anderen Registerkarte ausgeführt wird

Stoppen Sie den Rails-Server und die Konsole.

dann renne

 rake db:drop

5

Lassen Sie Ihre Anwendung die Verbindung schließen, wenn sie fertig ist. PostgreSQL hält keine Verbindungen offen, sondern die Anwendung, die die Verbindung aufrechterhält.


1
Ich kann die Datenbank über die Schnittstelle von phppgadmin löschen, aber nicht mit der Rake-Task. Wie kann ich den Drop von phppgadmin mit einer Rake-Aufgabe replizieren?
Fjuan

Entschuldigung, kann Ihnen da nicht helfen, ich habe keine Erfahrung mit Rechen. Der Fehler zeigt jedoch an, dass ein anderer Benutzer die Datenbank noch verwendet. Deshalb können Sie die Datenbank nicht löschen, nicht durch Rake, nicht durch PhpPgAdmin, unmöglich. Aus dem Handbuch DROP DATABASE: Es kann nicht ausgeführt werden, während Sie oder andere Personen mit der Zieldatenbank verbunden sind.
Frank Heikens

3

Rails stellt wahrscheinlich eine Verbindung zur Datenbank her, um sie zu löschen. Wenn Sie sich jedoch über phppgadmin anmelden, wird sie über die Datenbank template1 oder postgres angemeldet, sodass Sie davon nicht betroffen sind.


Wie kann ich Rails zwingen, die Datenbank zu löschen? Sollte ich meine eigene Rake-Aktion mit Postgres-SQL-Befehlen definieren?
Fjuan

2

Ich habe ein Juwel namens pgreset geschrieben , das Verbindungen zur fraglichen Datenbank automatisch beendet, wenn Sie rake db: drop (oder db: reset usw.) ausführen. Alles was Sie tun müssen, ist es zu Ihrer Gemfile hinzuzufügen und dieses Problem sollte verschwinden. Zum Zeitpunkt dieses Schreibens funktioniert es mit Rails 4 und höher und wurde auf Postgres 9.x getestet. Der Quellcode ist auf Github für alle Interessierten verfügbar .

gem 'pgreset'

Nichts als Ihr Juwel würde es tun - die Verbindung existierte nicht (ps grep search etc), aber Rails glaubte es. Vielen Dank!!
JosephK

1

Sie können den ActiveRecord-Code, der das Löschen ausführt, einfach monkeypatchen.

Für Schienen 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

Für Schienen 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

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


1

Ich hatte das gleiche Problem bei der Arbeit mit einer Rails 5.2-Anwendung und einer PostgreSQL-Datenbank in der Produktion.

So habe ich es gelöst :

Melden Sie zunächst jede Verbindung zum Datenbankserver auf dem PGAdmin ab Client ab, falls vorhanden.

Stoppen Sie jede Sitzung mit der Datenbank vom Terminal aus.

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

Starten Sie den PostgreSQL-Server, da der obige Kill-Vorgang den PostgreSQL-Server gestoppt hat.

sudo systemctl start postgresql

Löschen Sie die Datenbank in der Produktionsumgebung, indem Sie die Produktionsargumente anhängen.

rails db:drop RAILS_ENV=production DISABLE_DATABASE_ENVIRONMENT_CHECK=1

Das ist alles.

ich hoffe das hilft


1

Das hat bei mir funktioniert (Schienen 6): rake db:drop:_unsafe

Ich denke, wir hatten etwas in unserer Codebasis, das eine Datenbankverbindung initiierte, bevor die Rake-Task versuchte, sie zu löschen.


0

Stellen Sie einfach sicher, dass Sie die Rails-Konsole in einem geöffneten Terminalfenster verlassen und den Rails-Server verlassen haben. Dies ist einer der häufigsten Fehler, die von Personen begangen wurden


0

Ich hatte einen ähnlichen Fehler, der besagte, dass 1 Benutzer die Datenbank benutzte. Ich erkannte, dass es ICH war! Ich habe meinen Rails-Server heruntergefahren und dann den Befehl rake: drop ausgeführt und es hat funktioniert!


0

Versuchen Sie es nach dem Neustart des Servers oder Computers erneut.

Es könnte die einfache Lösung sein.


0

Lösung

Bash-Skript

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
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.