Erro de drop Rails + Postgres: banco de dados está sendo acessado por outros usuários

90

Eu tenho um aplicativo Rails em execução no Postgres.

Tenho dois servidores: um para teste e outro para produção.

Freqüentemente, preciso clonar o banco de dados de produção no servidor de teste.

O comando que estou executando via Vlad é:

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

O problema que estou tendo é que recebo o seguinte erro:

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

Isso acontece se alguém acessou o aplicativo via web recentemente (o postgres mantém uma "sessão" aberta)

Existe alguma maneira de encerrar as sessões no banco de dados postgres?

Obrigado.

Editar

Posso excluir o banco de dados usando a interface do phppgadmin, mas não com a tarefa rake.

Como posso replicar o drop do phppgadmin com uma tarefa rake?

Fjuan
fonte
Certifique-se de não ter conexões com o banco de dados ou ele não o descartará. Verifique mais sobre isso aqui .
Nesha Zoric

Respostas:

81

Se você encerrar as conexões postgresql em execução para seu aplicativo, poderá executar db: drop muito bem. Então, como matar essas conexões? Eu uso a seguinte tarefa de 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

Eliminar as conexões que estão sob os trilhos às vezes fará com que ela vire na próxima vez que você tentar carregar uma página, mas recarregá-la novamente restabelece a conexão.

codificado
fonte
2
Tive que adicionar sudo ao xargs e alterar o nome do banco de dados, mas funciona. TY
lzap
1
O mesmo para mim ... alterado para "sudo xargs kill" e db_name codificado para "my-development-database-name"
Kevin Dewalt
6
task "db:drop" => :kill_postgres_connectionsAcho que essa linha deve ser removida, é perigoso estender o comportamento das tarefas do sistema, a meu ver.
msa.im
Em vez de codificar o nome do banco de dados, basta usar o seguinte:db_name = Rails.configuration.database_configuration[Rails.env]['database']
tala
39

A maneira mais fácil e atualizada é: 1. Use ps -ef | grep postgrespara encontrar a conexão # 2.sudo kill -9 "# of the connection

Nota: pode haver PID idêntico. Matar um mata todos.

Sr. René
fonte
Qual número no resultado representa o PID? Vejo 3 colunas sem etiqueta com números que parecem PID.
BradGreens,
1
Segunda coluna @BradGreens (estou usando Mac Terminal)
s2t2
Nada foi encontrado com o ps, mas ainda assim obtém o erro em db: drop.
JosephK de
17

Esta é uma maneira rápida de eliminar todas as conexões com seu banco de dados postgres.

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

Aviso: isso eliminará todos os processos em execução que o postgresusuário abriu, portanto, certifique-se de fazer isso primeiro.

Jamon Holmgren
fonte
11
Em meu sistema, eu uso sudo kill -9 `ps -u postgres -o pid=` , então, um cabeçalho PID não será impresso ps, então um argumento de string não é passado para kill, então um erro não será gerado. Ótima dica em qualquer caso.
divido
1
Eu continuo recebendo votos positivos e negativos, resultando em uma classificação quase zero. Parece ser uma "solução rápida" controversa. Deixe-me apenas estado para o registro que eu fiz dar um aviso de que é perigoso. :)
Jamon Holmgren
3
Use isto para iniciar o postgresql novamente se você estiver no Ubuntu:sudo service postgresql start
Frikster
9

Quando usamos o método "kill processos" acima, o db: drop estava falhando (se: kill_postgres_connections fosse um pré-requisito). Eu acredito que foi porque a conexão que aquele comando rake estava usando estava sendo encerrada. Em vez disso, estamos usando um comando sql para interromper a conexão. Isso funciona como um pré-requisito para db: drop, evita o risco de matar processos por meio de um comando bastante complexo, e deve funcionar em qualquer sistema operacional (o gentoo requer uma sintaxe diferente para kill).

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

Aqui está uma tarefa rake que lê o nome do banco de dados em database.yml e executa um comando aprimorado (IMHO). Ele também adiciona db: kill_postgres_connections como um pré-requisito para db: drop. Inclui um aviso que grita depois de atualizar os trilhos, indicando que este patch pode não ser mais necessário.

consulte: https://gist.github.com/4455341 , referências incluídas

Matt Scilipoti
fonte
8

Eu uso a seguinte tarefa rake para substituir o drop_databasemétodo 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
fonte
Você já acabou lendo aquele aviso de produção? Só por curiosidade: P
Vinicius Brasil
6

Por favor, verifique se o seu console Rails ou servidor está rodando em outra aba e então

pare o servidor Rails e o console.

então corra

 rake db:drop
Amol Udage
fonte
5

Deixe seu aplicativo fechar a conexão quando terminar. PostgreSQL não mantém as conexões abertas, é a aplicação que mantém a conexão.

Frank Heikens
fonte
1
Posso excluir o banco de dados usando a interface do phppgadmin, mas não com a tarefa rake. Como posso replicar o drop do phppgadmin com uma tarefa rake?
fjuan,
Desculpe, não posso te ajudar nisso, não tenho experiência com rake. Porém, o erro indica que outro usuário ainda está usando o banco de dados. É por isso que você não pode deletar o banco de dados, nem por rake nem por PhpPgAdmin, impossível. Do manual, DROP DATABASE: não pode ser executado enquanto você ou qualquer outra pessoa estiver conectado ao banco de dados de destino.
Frank Heikens,
3

O Rails provavelmente está se conectando ao banco de dados para eliminá-lo, mas quando você se autentica via phppgadmin, ele se autentica via template1 ou banco de dados postgres, portanto, você não é afetado por isso.

Joshua D. Drake
fonte
Como posso forçar os rails a descartar o banco de dados? Devo definir minha própria ação rake usando comandos SQL do postgres?
2010
2

Eu escrevi uma gema chamada pgreset que irá encerrar automaticamente as conexões com o banco de dados em questão quando você executar rake db: drop (ou db: reset, etc). Tudo que você precisa fazer é adicioná-lo ao seu Gemfile e este problema deve desaparecer. No momento em que este artigo foi escrito, ele funcionava com Rails 4 e superior e foi testado no Postgres 9.x. O código-fonte está disponível no github para todos os interessados.

gem 'pgreset'
Dan
fonte
Nada além de sua gem faria isso - a conexão não existia (ps grep search etc), mas o rails pensava que sim. Muito obrigado !!
JosephK de
1

Você pode simplesmente aplicar um monkeypatch ao código ActiveRecord que faz a eliminação.

Para 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

Para 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

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

Manuel Meurer
fonte
1

Eu tive esse mesmo problema ao trabalhar com um aplicativo Rails 5.2 e banco de dados PostgreSQL em produção.

Veja como eu resolvi :

Primeiro, efetue logout de todas as conexões com o servidor de banco de dados no Cliente PGAdmin, se houver.

Pare todas as sessões usando o banco de dados do terminal.

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

Inicie o servidor PostgreSQL, pois a operação kill acima interrompeu o servidor PostgreSQL.

sudo systemctl start postgresql

Elimine o banco de dados no ambiente de produção anexando os argumentos de produção.

rails db:drop RAILS_ENV=production DISABLE_DATABASE_ENVIRONMENT_CHECK=1

Isso é tudo.

Eu espero que isso ajude

Promise Preston
fonte
1

Isso funcionou para mim (trilhos 6): rake db:drop:_unsafe

Acho que tínhamos algo em nossa base de código que iniciou uma conexão db antes que a tarefa rake tentasse derrubá-la.

cyrilchampier
fonte
0

Apenas certifique-se de que você saiu do console do Rails em qualquer janela de terminal aberta e saiu do servidor Rails ... este é um dos erros mais comuns cometidos por pessoas

Mutuma
fonte
0

Eu tive um erro semelhante dizendo que 1 usuário estava usando o banco de dados, percebi que era EU! Eu desliguei meu servidor Rails e então fiz o comando rake: drop e funcionou!

ravip0711
fonte
0

Depois de reiniciar o servidor ou computador, tente novamente.

Pode ser a solução simples.

hobbydev
fonte
0

Solução

Script 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
Vasily Bodnarchuk
fonte