Ошибка сброса Rails + Postgres: к базе данных обращаются другие пользователи


91

У меня есть приложение rails, работающее над Postgres.

У меня два сервера: один для тестирования, а другой для производства.

Очень часто мне нужно клонировать производственную БД на тестовом сервере.

Команда, которую я выполняю через Влада:

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

Проблема в том, что я получаю следующую ошибку:

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

Это происходит, если кто-то недавно обращался к приложению через Интернет (postgres сохраняет «сеанс» открытым)

Есть ли способ завершить сеансы в базе данных postgres?

Спасибо.

редактировать

Я могу удалить базу данных с помощью интерфейса phppgadmin, но не с помощью задачи rake.

Как я могу воспроизвести падение phppgadmin с помощью rake task?


Убедитесь, что у вас нет подключений к базе данных, иначе она не отключится. Узнайте больше об этом здесь .
Nesha Zoric

Ответы:


82

Если вы убьете запущенные соединения postgresql для своего приложения, вы сможете запустить db: drop нормально. Так как же убить эти связи? Я использую следующую задачу граблей:

# 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

Удаление соединений из-под рельсов иногда приводит к тому, что при следующей попытке загрузить страницу он будет отключаться, но при повторной загрузке соединение восстанавливается.


2
Мне пришлось добавить sudo в xargs и изменить имя базы данных, но это работает. TY
lzap

1
То же самое для меня ... изменено на «sudo xargs kill» и жестко запрограммированное db_name на «my-development-database-name»
Кевин Девальт

7
task "db:drop" => :kill_postgres_connectionsДумаю, эту строчку нужно убрать, с моей точки зрения, это опасность расширения поведения системной задачи.
msa.im

Вместо того, чтобы жестко кодировать имя вашей базы данных, просто используйте следующее:db_name = Rails.configuration.database_configuration[Rails.env]['database']
tala

41

Более простой и обновленный способ: 1. Используйте, ps -ef | grep postgresчтобы найти соединение №2.sudo kill -9 "# of the connection

Примечание: могут быть идентичные PID. Убийство одного убивает всех.


Какое число в результате представляет PID? Я вижу 3 столбца без меток с числами, которые выглядят как PID.
BradGreens 08

3
@BradGreens второй столбец (я использую Mac Terminal)
s2t2

Ничего не найдено с ps, но по-прежнему появляется ошибка db: drop.
JosephK

17

Вот быстрый способ убить все подключения к вашей базе данных postgres.

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

Предупреждение: это убьет все запущенные процессы, postgresоткрытые пользователем, поэтому сначала убедитесь, что вы хотите это сделать.


11
В своей системе я использую sudo kill -9 `ps -u postgres -o pid=` вместо этого, поэтому заголовок PID не будет напечатан ps, поэтому строковый аргумент не передается kill, поэтому ошибка не возникает. Отличный совет в любом случае.
deivid 03

1
Я продолжаю получать голоса за и против, в результате чего рейтинг почти нулевой. Кажется спорным "быстрое решение". Позвольте мне просто заявить , что я сделал дать предупреждение , что это опасно. :)
Хамон Холмгрен

3
Используйте это, чтобы снова запустить postgresql, если вы используете Ubuntu:sudo service postgresql start
Frikster

9

Когда мы использовали метод «уничтожения процессов», описанный выше, db: drop завершился ошибкой (если: kill_postgres_connections было предварительным условием). Я считаю, что это произошло потому, что соединение, которое использовала эта команда rake, было отключено. Вместо этого мы используем команду sql для разрыва соединения. Это работает как предварительное условие для db: drop, позволяет избежать риска прерывания процессов с помощью довольно сложной команды и должно работать в любой ОС (для gentoo требовался другой синтаксис kill).

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

Вот задача rake, которая считывает имя базы данных из database.yml и запускает улучшенную (IMHO) команду. Он также добавляет db: kill_postgres_connections в качестве предварительного условия к db: drop. Он включает в себя предупреждение, которое кричит после обновления рельсов, указывая на то, что этот патч больше не нужен.

см .: https://gist.github.com/4455341 , включая ссылки


8

Я использую следующую задачу rake, чтобы переопределить drop_databaseметод 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

Вы когда-нибудь читали это производственное предупреждение? Просто любопытно: P
Vinicius Brasil

6

Пожалуйста, проверьте, работает ли ваша консоль или сервер rails на другой вкладке, а затем

остановите сервер рельсов и консоль.

затем беги

 rake db:drop

5

Позвольте вашему приложению закрыть соединение, когда это будет сделано. PostgreSQL не поддерживает соединения открытыми, это приложение поддерживает соединение.


1
Я могу удалить базу данных с помощью интерфейса phppgadmin, но не с помощью задачи rake. Как я могу воспроизвести падение phppgadmin с помощью rake task?
fjuan 03

Извините, не могу вам помочь, у меня нет опыта работы с граблями. Но ошибка указывает на то, что другой пользователь все еще использует базу данных. Вот почему нельзя удалить базу ни граблями, ни PhpPgAdmin, невозможно. Из руководства DROP DATABASE: он не может быть выполнен, пока вы или кто-либо еще подключены к целевой базе данных.
Фрэнк Хейкенс, 03

3

Rails, вероятно, подключается к базе данных, чтобы удалить ее, но когда вы входите в систему через phppgadmin, он входит в систему через базу данных template1 или postgres, поэтому вас это не касается.


Как я могу заставить рельсы сбросить базу данных? Должен ли я определять собственное действие rake с помощью команд SQL postgres?
fjuan 07

3

Это сработало для меня (рельсы 6): rake db:drop:_unsafe

Я думаю, что у нас было что-то в нашей кодовой базе, которое инициировало соединение с базой данных до того, как задача rake попыталась его сбросить.


2

Я написал гем под названием pgreset , который автоматически уничтожает соединения с рассматриваемой базой данных, когда вы запускаете rake db: drop (или db: reset и т. Д.). Все, что вам нужно сделать, это добавить его в свой Gemfile, и эта проблема должна исчезнуть. На момент написания он работал с Rails 4 и новее и был протестирован на Postgres 9.x. Исходный код доступен на github для всех желающих.

gem 'pgreset'

Ничего, кроме вашего драгоценного камня, не могло бы сделать это - соединение не существовало (ps grep search и т. Д.), Но rails думали, что это так. Большое спасибо!!
JosephK

1

Вы можете просто исправить код ActiveRecord, который выполняет удаление.

Для 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

Для 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

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


1

У меня была такая же проблема при работе с приложением Rails 5.2 и базой данных PostgreSQL в производственной среде.

Вот как я это решил :

Во-первых, выходите из каждого подключения к серверу базы данных на клиенте PGAdmin, если таковое имеется.

Останавливайте каждую сессию с использованием базы данных с терминала.

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

Запустите сервер PostgreSQL, поскольку описанная выше операция уничтожения остановила сервер PostgreSQL.

sudo systemctl start postgresql

Отбросьте базу данных в производственной среде, добавив производственные аргументы.

rails db:drop RAILS_ENV=production DISABLE_DATABASE_ENVIRONMENT_CHECK=1

Вот и все.

надеюсь, это поможет


0

Просто убедитесь, что вы вышли из консоли rails в любом открытом окне терминала и вышли из сервера rails ... это одна из самых распространенных ошибок, которые делают люди.


0

У меня была аналогичная ошибка, говоря, что 1 пользователь использовал базу данных, я понял, что это Я! Я выключил свой сервер rails, а затем выполнил команду rake: drop, и это сработало!


0

После перезапуска сервера или компьютера попробуйте еще раз.

Это могло быть простое решение.


0

Решение

Сценарий 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
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.