Błąd upuszczania Rails + Postgres: dostęp do bazy danych mają inni użytkownicy

91

Mam aplikację railsową działającą na Postgres.

Mam dwa serwery: jeden do testowania, a drugi do produkcji.

Bardzo często muszę sklonować produkcyjną bazę danych na serwerze testowym.

Polecenie uruchamiane przez Vlada to:

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

Problem polega na tym, że pojawia się następujący błąd:

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

Dzieje się tak, jeśli ktoś ostatnio uzyskał dostęp do aplikacji przez Internet (postgres utrzymuje otwartą „sesję”)

Czy jest jakiś sposób, żebym mógł zakończyć sesje w bazie danych postgres?

Dziękuję Ci.

Edytować

Mogę usunąć bazę danych za pomocą interfejsu phppgadmin, ale nie za pomocą zadania rake.

Jak mogę replikować drop phppgadmin za pomocą zadania rake?

fjuan
źródło
Upewnij się, że nie masz połączeń z bazą danych, w przeciwnym razie jej nie upuści. Sprawdź więcej na ten temat tutaj .
Nesha Zoric

Odpowiedzi:

81

Jeśli zabijesz działające połączenia postgresql dla twojej aplikacji, możesz następnie uruchomić db: drop dobrze. Jak więc zabić te połączenia? Używam następującego zadania 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

Zabijanie połączeń spod szyn czasami powoduje zerwanie połączenia przy następnej próbie załadowania strony, ale ponowne jej załadowanie przywraca połączenie.

zakodowane
źródło
2
Musiałem dodać sudo do xargs i zmienić nazwę bazy danych, ale to działa. TY
lzap
1
To samo dotyczy mnie ... zmieniono na „sudo xargs kill” i na stałe wpisano nazwę db_name na „my-development-database-name”
Kevin Dewalt
6
task "db:drop" => :kill_postgres_connectionsMyślę, że ten wiersz powinien zostać usunięty, z mojego punktu widzenia grozi to rozszerzeniem zachowania zadania systemowego.
msa.im
Zamiast na stałe zakodować nazwę bazy danych, po prostu użyj następującego:db_name = Rails.configuration.database_configuration[Rails.env]['database']
tala
41

Prostszy i bardziej aktualny sposób to: 1. Użyj, ps -ef | grep postgresaby znaleźć połączenie # 2.sudo kill -9 "# of the connection

Uwaga: może istnieć identyczny PID. Zabicie jednego zabija wszystkich.

Panie Rene
źródło
Która liczba w wyniku reprezentuje PID? Widzę 3 nieoznakowane kolumny z liczbami, które wyglądają jak PID.
BradGreens
3
@BradGreens druga kolumna (używam terminala Mac)
s2t2
Nic nie zostało znalezione w ps, ale nadal pojawia się błąd w db: drop.
JosephK
17

Oto szybki sposób na zabicie wszystkich połączeń z bazą danych postgres.

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

Ostrzeżenie: spowoduje to zabicie wszystkich uruchomionych procesów, które postgresużytkownik otworzył, więc upewnij się, że chcesz to zrobić najpierw.

Jamon Holmgren
źródło
11
W moim systemie używam sudo kill -9 `ps -u postgres -o pid=` zamiast tego, więc nagłówek PID nie zostanie wydrukowany ps, więc argument łańcuchowy nie jest przekazywany do kill, więc błąd nie zostanie zgłoszony. Świetna wskazówka w każdym przypadku.
deivid
1
Ciągle otrzymuję głosy za i przeciw, co skutkuje oceną bliską zeru. Wydaje się, że jest to kontrowersyjne „szybkie rozwiązanie”. Pozwólcie, że tylko powiem, że dałem ostrzeżenie, że jest to niebezpieczne. :)
Jamon Holmgren
3
Użyj tego, aby ponownie rozpocząć postgresql, jeśli jesteś na Ubuntu:sudo service postgresql start
Frikster
9

Kiedy użyliśmy powyższej metody „zabij procesy”, procedura db: drop nie powiodła się (jeśli warunek wstępny: kill_postgres_connections). Wydaje mi się, że to dlatego, że połączenie, którego używała ta komenda grabieży, zostało zabite. Zamiast tego używamy polecenia sql, aby porzucić połączenie. Działa to jako warunek wstępny dla db: drop, pozwala uniknąć ryzyka zabicia procesów za pomocą dość złożonej komendy i powinno działać na każdym systemie operacyjnym (dla Gentoo wymagana jest inna składnia kill).

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

Oto zadanie rake, które odczytuje nazwę bazy danych z database.yml i uruchamia ulepszoną komendę (IMHO). Dodaje również db: kill_postgres_connections jako warunek wstępny do db: drop. Zawiera ostrzeżenie, które krzyczy po aktualizacji szyn, wskazując, że ta poprawka może nie być już potrzebna.

patrz: https://gist.github.com/4455341 , w tym odniesienia

Matt Scilipoti
źródło
8

Używam następującego zadania rake, aby zastąpić drop_databasemetodę 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
źródło
Czy kiedykolwiek przeczytałeś to ostrzeżenie dotyczące produkcji? Po prostu ciekawy: P
Vinicius Brasil
6

Sprawdź, czy twoja konsola lub serwer rails działa na innej karcie, a następnie

zatrzymaj serwer railsowy i konsolę.

następnie uruchomić

 rake db:drop
Amol Udage
źródło
5

Pozwól aplikacji zamknąć połączenie po zakończeniu. PostgreSQL nie utrzymuje otwartych połączeń, to aplikacja utrzymuje połączenie.

Frank Heikens
źródło
1
Mogę usunąć bazę danych za pomocą interfejsu phppgadmin, ale nie za pomocą zadania rake. Jak mogę replikować drop phppgadmin za pomocą zadania rake?
fjuan
Przepraszam, nie mogę ci tam pomóc, nie mam doświadczenia z rake. Jednak błąd wskazuje, że inny użytkownik nadal korzysta z bazy danych. Dlatego nie możesz usunąć bazy danych, nie przez rake ani przez PhpPgAdmin, niemożliwe. Z podręcznika, DROP DATABASE: nie można go wykonać, gdy Ty lub ktokolwiek inny jest połączony z docelową bazą danych.
Frank Heikens
3

Railsy prawdopodobnie łączą się z bazą danych, aby ją porzucić, ale kiedy logujesz się przez phppgadmin, loguje się przez bazę danych template1 lub postgres, więc nie ma to na ciebie wpływu.

Joshua D. Drake
źródło
Jak zmusić railsy do usunięcia bazy danych? Czy powinienem zdefiniować własną akcję rake za pomocą poleceń SQL postgres?
fjuan,
2

Napisałem klejnot o nazwie pgreset , który automatycznie zabija połączenia z daną bazą danych po uruchomieniu rake db: drop (lub db: reset itp.). Wszystko, co musisz zrobić, to dodać go do pliku Gemfile, a ten problem powinien zniknąć. W chwili pisania tego tekstu działa z Railsami 4 i nowszymi oraz został przetestowany na Postgres 9.x. Kod źródłowy jest dostępny na github dla wszystkich zainteresowanych.

gem 'pgreset'
Dan
źródło
Nic poza twoim klejnotem by tego nie zrobiło - połączenie nie istniało (wyszukiwanie ps grep itp.), Ale Railsy uważały, że tak. Dzięki wielkie!!
JosephK
2

To zadziałało dla mnie (szyny 6): rake db:drop:_unsafe

Myślę, że mieliśmy coś w naszej bazie kodu, co zainicjowało połączenie bazy danych, zanim zadanie rake'a próbowało je porzucić.

cyrilchampier
źródło
1

Możesz po prostu wpisać monkeypatch kod ActiveRecord, który wykonuje upuszczanie.

Dla 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

Dla 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

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

Manuel Meurer
źródło
1

Miałem ten sam problem podczas pracy z aplikacją Rails 5.2 i bazą danych PostgreSQL w środowisku produkcyjnym.

Oto jak to rozwiązałem :

Najpierw wyloguj się z każdego połączenia z serwerem bazy danych na kliencie PGAdmin, jeśli takie istnieje.

Zatrzymaj każdą sesję przy użyciu bazy danych z terminala.

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

Uruchom serwer PostgreSQL, ponieważ powyższa operacja kill zatrzymała serwer PostgreSQL.

sudo systemctl start postgresql

Usuń bazę danych w środowisku produkcyjnym, dołączając argumenty produkcyjne.

rails db:drop RAILS_ENV=production DISABLE_DATABASE_ENVIRONMENT_CHECK=1

To wszystko.

mam nadzieję, że to pomoże

Obietnica Preston
źródło
0

Tylko upewnij się, że opuściłeś konsolę rails w dowolnym otwartym oknie terminala i opuściłeś serwer rails ... to jeden z najczęstszych błędów popełnianych przez ludzi

Mutuma
źródło
0

Miałem podobny błąd, że 1 użytkownik korzystał z bazy danych, zdałem sobie sprawę, że to JA! Wyłączyłem serwer rails, a następnie wykonałem polecenie rake: drop i zadziałało!

ravip0711
źródło
0

Po ponownym uruchomieniu serwera lub komputera spróbuj ponownie.

To mogłoby być proste rozwiązanie.

hobbydev
źródło
0

Rozwiązanie

Skrypt 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
Wasilij Bodnarczuk
źródło