Force drop db enquanto outros podem estar conectados

104

Preciso remover um banco de dados de um cluster do PostgreSQL DB. Como posso fazer isso mesmo se houver conexões ativas? Preciso de um tipo de -forcesinalizador, que abandone todas as conexões e depois o banco de dados.

Como posso implementá-lo?

Estou usando dropdbatualmente, mas outras ferramentas são possíveis.

Alex
fonte

Respostas:

155

No PostgreSQL * , você não pode eliminar um banco de dados enquanto os clientes estiverem conectados a ele.

Pelo menos, não com o dropdbutilitário - que é apenas um invólucro simples em torno da DROP DATABASEconsulta ao servidor.

Solução alternativa bastante robusta a seguir:

Conecte-se ao seu servidor como superusuário , usando psqlou outro cliente. Você não usar o banco de dados que você quer deixar cair.

psql -h localhost postgres postgres

Agora, usando o cliente de banco de dados simples, você pode forçar o descarte do banco de dados usando três etapas simples:

  1. Verifique se ninguém pode se conectar a este banco de dados. Você pode usar um dos seguintes métodos (o segundo parece mais seguro, mas não impede as conexões dos superusuários).

    /* Method 1: update system catalog */
    UPDATE pg_database SET datallowconn = 'false' WHERE datname = 'mydb';
    
    /* Method 2: use ALTER DATABASE. Superusers still can connect!
    ALTER DATABASE mydb CONNECTION LIMIT 0; */
    
  2. Forçar a desconexão de todos os clientes conectados a esse banco de dados usando pg_terminate_backend.

    SELECT pg_terminate_backend(pid)
    FROM pg_stat_activity
    WHERE datname = 'mydb';
    
    /* For old versions of PostgreSQL (up to 9.1), change pid to procpid:
    
    SELECT pg_terminate_backend(procpid)
    FROM pg_stat_activity
    WHERE datname = 'mydb'; */
    
  3. Solte o banco de dados.

    DROP DATABASE mydb;

A Etapa 1 requer privilégios de superusuário para o 1º método e privilégios de proprietário do banco de dados para o 2º. A etapa 2 requer privilégios de superusuário . Etapa 3 requer privilégio de proprietário do banco de dados .


* Isso se aplica a todas as versões do PostgreSQL, até a versão 11.


filiprem
fonte
Portanto, não sei o que fiz de errado, mas agora não consigo nem me conectar ao banco de dados que segmentei! Também não posso soltá-lo como ele diz "banco de dados de manutenção não pode ser descartado"
Matt Skeldon
@ MattSkeldon, não faço ideia do que essa mensagem significa. No PostgreSQL de baunilha, você pode eliminar qualquer banco de dados, exceto o template0 e o template1. Talvez você use alguma versão não gratuita / comercial? Talvez seja problema do cliente, não do servidor? Você tentou o psql?
Filiprem
Infelizmente, venho de um background SQL, usando o PGSQL está sendo usado devido ao status não comercial / gratuito.
22617 Matt Skeldon
Isso não funciona para mim onde há sessões de zumbi de longa duração. O pg_terminate_backend () não interrompe essas sessões, por isso ainda estou um pouco preocupado com o que fazer: sou um Postgres su, mas não tenho acesso ao servidor em que ele está sendo executado.
285 Alexander Alexander
6

Não é uma maneira de fazer isso com os utilitários de shell dropdb& pg_ctl(ou pg_ctlclusterem Debian e seus derivados). Mas o método do @ filiprem é superior por várias razões:

  • Apenas desconecta os usuários do banco de dados em questão.
  • Não é necessário reiniciar o cluster inteiro.
  • Evita reconexões imediatas, possivelmente estragando o dropdbcomando.

Cito man pg_ctlcluster:

Com a --forceopção, o modo "rápido" é usado, o que reverte todas as transações ativas, desconecta os clientes imediatamente e, portanto, é encerrado de maneira limpa. Se isso não funcionar, o desligamento é tentado novamente no modo "imediato", o que pode deixar o cluster em um estado inconsistente e, portanto, levará a uma recuperação na próxima inicialização. Se isso ainda não ajudar, o processo do postmaster será interrompido. Sai com 0 em caso de sucesso, com 2 se o servidor não estiver em execução e com 1 em outras condições de falha. Este modo deve ser usado apenas quando a máquina estiver prestes a ser desligada.

pg_ctlcluster 9.1 main restart --force

ou

pg_ctl restart -D datadir -m fast

ou

pg_ctl restart -D datadir -m immediate

imediatamente seguido por:

dropdb mydb

Possivelmente em um roteiro para sucessão imediata.

Erwin Brandstetter
fonte
4
Isso não é apenas ideal, pois retrocede a instância completa do postgres, mas não é garantido que funcione. É possível que um cliente se conecte entre o momento em que você reinicia o servidor e tenta executar o dropdb novamente. A resposta do @filiprem acima desabilita todas as conexões com o banco de dados antes de desconectar e mantém outros bancos de dados ativos.
Jim Mitchener
6

Usando a resposta de @ filiprem no meu caso e simplificando-a:

-- Connecting to the current user localhost's postgres instance
psql

-- Making sure the database exists
SELECT * from pg_database where datname = 'my_database_name'

-- Disallow new connections
UPDATE pg_database SET datallowconn = 'false' WHERE datname = 'my_database_name';
ALTER DATABASE my_database_name CONNECTION LIMIT 1;

-- Terminate existing connections
SELECT pg_terminate_backend(pid) FROM pg_stat_activity WHERE datname = 'my_database_name';

-- Drop database
DROP DATABASE my_database_name
Dorian
fonte
0

Se você estiver em algo como o RDS, em que conexões sem um banco de dados selecionado o colocam no banco de dados que você pediu para ser criado por padrão, você pode fazer essa variante para evitar que seja a última conexão aberta.

 DROP DATABASE IF EXISTS temporary_db_that_shouldnt_exist; 

 CREATE DATABASE temporary_db_that_shouldnt_exist with OWNER your_user; 

 \connect temporary_db_that_shouldnt_exist 
 SELECT pg_terminate_backend(pid) FROM pg_stat_activity WHERE datname = 'the_db_you_want_removed'; 


 DROP DATABASE IF EXISTS the_db_you_want_removed; 
 -- 
 -- Name: the_db_you_want_removed; Type: DATABASE; Schema: -; Owner: your_user 
 -- 

 CREATE DATABASE savings_champion WITH TEMPLATE = template0 ENCODING = 'UTF8' LC_COLLATE = 'en_US.UTF-8' LC_CTYPE = 'en_US.UTF-8'; 


 ALTER DATABASE the_db_you_want_removed OWNER TO your_user; 

 \connect the_db_you_want_removed 

 DROP DATABASE IF EXISTS temporary_db_that_shouldnt_exist;
Jharwood
fonte