Errore di rilascio di Rails + Postgres: altri utenti accedono al database


90

Ho un'applicazione rails in esecuzione su Postgres.

Ho due server: uno per il test e l'altro per la produzione.

Molto spesso ho bisogno di clonare il DB di produzione sul server di prova.

Il comando che sto eseguendo tramite Vlad è:

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

Il problema che sto riscontrando è che ricevo il seguente errore:

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

Questo accade se qualcuno ha recentemente eseguito l'accesso all'applicazione via web (postgres mantiene aperta una "sessione")

C'è un modo per terminare le sessioni sul DB Postgres?

Grazie.

modificare

Posso eliminare il database utilizzando l'interfaccia di phppgadmin ma non con l'attività rake.

Come posso replicare il rilascio di phppgadmin con un'attività di rake?


Assicurati di non avere connessioni al database o non lo lascerà cadere. Controlla di più su questo qui .
Nesha Zoric

Risposte:


81

Se interrompi le connessioni postgresql in esecuzione per la tua applicazione, puoi eseguire db: drop senza problemi. Quindi come eliminare quelle connessioni? Uso la seguente attività di rake:

# 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

Interrompere le connessioni da sotto i binari a volte farà sì che si sbarrasse la prossima volta che provi a caricare una pagina, ma ricaricarla di nuovo ristabilisce la connessione.


2
Ho dovuto aggiungere sudo a xargs e cambiare il nome del database, ma funziona. TY
lzap

1
Lo stesso per me ... cambiato in "sudo xargs kill" e db_name hardcoded in "my-development-database-name"
Kevin Dewalt

6
task "db:drop" => :kill_postgres_connectionsPenso che questa linea dovrebbe essere rimossa, dal mio punto di vista è pericoloso estendere il comportamento dell'attività di sistema.
msa.im

Anziché codificare il nome del database, utilizzare semplicemente quanto segue:db_name = Rails.configuration.database_configuration[Rails.env]['database']
tala

39

Il modo più semplice e aggiornato è: 1. Utilizzare ps -ef | grep postgresper trovare la connessione n. 2.sudo kill -9 "# of the connection

Nota: potrebbe esserci un PID identico. Uccidere uno uccide tutti.


Quale numero nel risultato rappresenta il PID? Vedo 3 colonne senza etichetta con numeri che sembrano PID.
BradGreens

1
@BradGreens seconda colonna (sto usando Mac Terminal)
s2t2

Non viene trovato nulla con ps, ma viene comunque visualizzato l'errore su db: drop.
JosephK

17

Ecco un modo rapido per terminare tutte le connessioni al tuo database postgres.

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

Attenzione: questo ucciderà tutti i processi in esecuzione che l' postgresutente ha aperto, quindi assicurati di volerlo fare prima.


11
Nel mio sistema sudo kill -9 `ps -u postgres -o pid=` invece uso , quindi un'intestazione PID non verrà stampata da ps, quindi non viene passato un argomento stringa kill, quindi non verrà generato un errore. Ottimo suggerimento in ogni caso.
deivid

1
Continuo a ricevere voti positivi e negativi, con conseguente valutazione quasi zero. Sembra essere una controversa "soluzione rapida". Lasciatemi solo Stato per il record che ho fatto dare un avvertimento che è pericoloso. :)
Jamon Holmgren

3
Usalo per riavviare postgresql se sei su Ubuntu:sudo service postgresql start
Frikster

9

Quando abbiamo usato il metodo "kill processi" dall'alto, db: drop non funzionava (se: kill_postgres_connections era il prerequisito). Credo che fosse perché la connessione che stava usando quel comando rake veniva uccisa. Invece, stiamo usando un comando sql per interrompere la connessione. Questo funziona come prerequisito per db: drop, evita il rischio di terminare i processi tramite un comando piuttosto complesso e dovrebbe funzionare su qualsiasi sistema operativo (gentoo richiede una sintassi diversa per kill).

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

Ecco un task rake che legge il nome del database da database.yml ed esegue un comando migliorato (IMHO). Inoltre aggiunge db: kill_postgres_connections come prerequisito a db: drop. Include un avviso che urla dopo aver aggiornato rails, indicando che questa patch potrebbe non essere più necessaria.

vedi: https://gist.github.com/4455341 , riferimenti inclusi


8

Uso il seguente task rake per sovrascrivere il drop_databasemetodo 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

Hai mai finito per leggere quell'avvertimento sulla produzione? Solo curioso: P
Vinicius Brasil

6

Controlla se la tua console o server rails è in esecuzione in un'altra scheda e poi

fermare il server rails e la console.

quindi corri

 rake db:drop

5

Consenti alla tua applicazione di chiudere la connessione al termine. PostgreSQL non mantiene le connessioni aperte, è l'applicazione che mantiene la connessione.


1
Posso eliminare il database utilizzando l'interfaccia di phppgadmin ma non con l'attività rake. Come posso replicare il rilascio di phppgadmin con un'attività di rake?
fjuan

Mi spiace, non posso aiutarti, non ho esperienza con il rake. Tuttavia, l'errore indica che un altro utente sta ancora utilizzando il database. Ecco perché non puoi cancellare il database, non con rake non con PhpPgAdmin, impossibile. Dal manuale, DROP DATABASE: non può essere eseguito mentre tu o chiunque altro siete connessi al database di destinazione.
Frank Heikens

3

È probabile che Rails si connetta al database per rilasciarlo, ma quando si accede tramite phppgadmin si accede tramite il database template1 o postgres, quindi non ne sei influenzato.


Come posso forzare rails a rilasciare il database? Devo definire la mia azione rake utilizzando i comandi SQL di postgres?
fjuan

2

Ho scritto una gemma chiamata pgreset che interromperà automaticamente le connessioni al database in questione quando esegui rake db: drop (o db: reset, ecc.). Tutto quello che devi fare è aggiungerlo al tuo Gemfile e questo problema dovrebbe scomparire. Al momento della stesura di questo documento funziona con Rails 4 e versioni successive ed è stato testato su Postgres 9.x. Il codice sorgente è disponibile su GitHub per chiunque sia interessato.

gem 'pgreset'

Nient'altro che la tua gemma lo farebbe - la connessione non esisteva (ps grep ricerca ecc.), Ma rails pensava che lo fosse. Grazie mille !!
JosephK

1

Puoi semplicemente Monkeypatch il codice ActiveRecord che esegue il rilascio.

Per 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

Per 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

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


1

Ho avuto lo stesso problema lavorando con un'applicazione Rails 5.2 e un database PostgreSQL in produzione.

Ecco come l'ho risolto :

Innanzitutto, disconnettersi da ogni connessione al server di database sul client PGAdmin, se presente.

Interrompi ogni sessione utilizzando il database dal terminale.

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

Avvia il server PostgreSQL, poiché l'operazione kill sopra ha interrotto il server PostgreSQL.

sudo systemctl start postgresql

Rilascia il database nell'ambiente di produzione aggiungendo gli argomenti di produzione.

rails db:drop RAILS_ENV=production DISABLE_DATABASE_ENVIRONMENT_CHECK=1

È tutto.

Spero che aiuti


1

Questo ha funzionato per me (rotaie 6): rake db:drop:_unsafe

Penso che avessimo qualcosa nella nostra base di codice che ha avviato una connessione db prima che l'attività di rake tentasse di rilasciarla.


0

Assicurati solo di essere uscito dalla console di rails su qualsiasi finestra di terminale aperta e di essere uscito dal server rails ... questoèuno degli errori più comuni commessi dalle persone


0

Ho avuto un errore simile dicendo che 1 utente stava usando il database, ho capito che ero io! Ho spento il mio server rails e poi ho eseguito il comando rake: drop e ha funzionato!


0

Dopo aver riavviato il server o il computer, riprova.

Potrebbe essere la soluzione semplice.


0

Soluzione

Script di 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
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.