Rails + помилка падіння Postgres: інші користувачі отримують доступ до бази даних


90

У мене є програма 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?


Переконайтеся, що у вас немає підключень до бази даних, інакше вона не втратить її. Дізнайтеся більше про це тут .
Неша Зоріч

Відповіді:


81

Якщо ви вб'єте запущені з'єднання 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"
Кевін Девальт,

6
task "db:drop" => :kill_postgres_connectionsЯ вважаю, що цей рядок слід видалити, на мій погляд, це може поширити поведінку системного завдання.
msa.im

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

40

Простіший та оновлений спосіб: 1. Використовуйте ps -ef | grep postgresдля пошуку з'єднання №2.sudo kill -9 "# of the connection

Примітка: Може бути ідентичний PID. Вбивство одного вбиває всіх.


Яке число в результаті представляє ПІД? Я бачу 3 немарковані стовпці з номерами, схожими на PID.
BradGreens

2
@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

1
Я продовжую отримувати голоси проти і проти, приводячи до майже нульового рейтингу. Здається, це спірне "швидке виправлення". Дозвольте мені просто заявити , що я зробив дати попередження , що це небезпечно. :)
Jamon Holmgren

3
Використовуйте це, щоб знову запустити postgresql, якщо ви користуєтесь Ubuntu:sudo service postgresql start
Frikster

9

Коли ми використовували метод "вбити процеси" зверху, db: drop не вдався (якщо: kill_postgres_connections було обов'язковою умовою). Я вважаю, що це було тому, що зв’язок, який використовувала ця грабліва команда, був убитий. Натомість ми використовуємо команду 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

Я використовую наступне завдання граблі, щоб замінити 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 або сервер на іншій вкладці, а потім

зупиніть сервер і консоль rails.

то біжи

 rake db:drop

5

Нехай ваша програма закриє з'єднання, коли це буде зроблено. PostgreSQL не тримає з'єднання відкритими, це програма, яка підтримує з'єднання.


1
Я можу видалити базу даних за допомогою інтерфейсу phppgadmin, але не за допомогою завдання rake. Як я можу відтворити падіння phppgadmin із завданням rake?
fjuan 03.03.10

Вибачте, не можу вам там допомогти, я не маю досвіду роботи з граблями. Але помилка вказує на те, що інший користувач все ще використовує базу даних. Ось чому ви не можете видалити базу даних, ані рейком, ані PhpPgAdmin, неможливо. З посібника DROP DATABASE: його неможливо виконати, коли ви або хтось інший підключені до цільової бази даних.
Френк Хейкенс 03.03.10

3

Rails, швидше за все, підключається до бази даних, щоб скинути її, але коли ви входите через phppgadmin, він входить через базу даних template1 або postgres, отже, це вас не зачіпає.


Як я можу змусити рейли скинути базу даних? Чи слід визначати власну дію граблі за допомогою команд SQL postgres?
fjuan

2

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

gem 'pgreset'

Нічого, крім вашого дорогоцінного каменя, це не зробило б - з'єднання не існувало (ps grep search etc), але rails думали, що воно існує. Велике спасибі !!
JosephK

2

Це працювало для мене (рейки 6): rake db:drop:_unsafe

Думаю, у нашій кодовій базі було щось, що ініціювало з’єднання db до того, як завдання rake спробувало його скинути.


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

Рішення

Башовий сценарій

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.