Enfrente o desafio do wraparound no PostgreSQL, interpretando logs, evitando perdas e administrando o banco de dados com eficácia.
Tempo de Leitura: 4 minutos
No dia a dia de todo DBA, é comum a necessidade de investigação quando a aplicação apresenta lentidão em algum ponto. Em umas dessas vezes ao abrir os logs do PostgreSQL me deparei com essa cena:
Eu travei, só conseguia lembrar dos estudos sobre o wraparound e minha mente gritava “O BANCO VAI TRAVAR, VAMOS PERDER TUDO, TEM QUE CORRER PRA RESOLVER”. Depois do choque inicial que durou apenas alguns segundos, mas que pareceu muito mais, comecei a trabalhar.
Então vou te explicar um pouco do porquê esse evento é tão perigoso em bancos de dados PostgreSQL e como consegui resolvê-lo.
No meu post anterior falei sobre as rotinas de vacuum, porque elas existem, o motivo delas serem necessárias e como funcionam, recomendo que o leia.
O wraparound também tem relação com o MCVV Multi Version Concurrency Control, pois a cada nova transação o XID (ID da transação) é comparado com o XID atual. É nessa comparação que o PostgreSQL localiza a versão da linha que deve ser acessada, uma versão de linha com um XID de inserção maior que o XID da transação atual é “no futuro” e não deve ser visível para a transação atual. Aqui tem outro post explicando com mais detalhes sobre o MCVV.
Mas como os IDs de transação têm tamanho limitado (32 bits), o banco de dados pode sofrer wraparound de ID de transação. Quando isso ocorre, o contador XID volta para zero e, de repente, as transações que estavam “no passado” parecem estar “no futuro” - o que significa que seus dados se tornam invisíveis. Ou seja, perda total do seu banco de dados.
Agora você deve ter entendido o porquê a palavra wraparound me causou tamanho choque.
No entanto, isso não implica em uma indisponibilidade imediata do banco de dados. O mecanismo do PostgreSQL oferece várias formas de controle sobre esse processo, incluindo alertas registrados nos logs e procedimentos automáticos. Isso é feito para prevenir a ocorrência do wraparound e garantir a continuidade operacional do banco.
E como evitamos que o wraparound aconteça? Alguma ideia? Se você pensou no vacuum você acertou!
Sim, a mesma ferramenta que usamos para excluir fisicamente as linhas mortas que foram marcadas como invisíveis, mas que ainda existem no disco no banco de dados. Com a exclusão dessas linhas, novos valores de XID são liberados para uso nas próximas transações. O processo que realiza essa liberação é um tanto quanto complexo, por esse motivo não entrarei em detalhes hoje, mas o que precisamos entender é que as rotinas de vaccum são fundamentais para a saúde do PostgreSQL.
Bom, vamos ao “como eu evitei os problemas de wraparound”!
Comecei lendo os logs, sério LEIAM OS LOGS eles sempre te dão um ponto de partida. No meu caso estava bem claro as opções para iniciar a análise, transações antigas e/ou slot de replicação.
Uma pesquisa no Google e voltei com as seguintes queries:
-- verifica conexões abertas a mais de 30 minutos
SELECT pid, usename, application_name, backend_start, state, now() - backend_start AS duration
FROM pg_stat_activity
WHERE state <> 'idle' AND now() - backend_start > interval '30 minutes';
-- verifica o status do slot de replicação
SELECT slot_name, active FROM pg_replication_slots;
É óbvio que para exercer o papel de DBA deve-se estar ciente das ações realizadas no banco de dados, mas mesmo assim vou dizer que no meu caso foi CRUCIAL. Eu sabia que havia sido implementada a replicação de dados para ambiente de testes usando slot de replicação do PostgreSQL.
Com essa informação e pelo meu conhecimento no desenvolvimento da ferramenta, em conjunto com o retorno da primeira query informando o tempo que a transação estava aberta sabia o que precisava fazer. Desligar a ferramenta de replicação.
A segunda query apenas me confirmou a preocupação que houve durante o desenvolvimento da ferramenta, o slot de replicação não poderia ficar inativo e ainda constar no banco. Tínhamos apenas o slot em uso ativo no sistema, o que já evitava problemas maiores.
Depois de desligar a ferramenta de replicação de dados o PostgreSQL iniciou os processos automáticos para normalizar seu estado (deixo aqui meu comentário: que arquitetura incrível!). Várias rotinas de autovacuum foram lançadas que eu optei por deixá-las concluir no tempo necessário sem interrupção, visto que o produto ainda estava totalmente funcional, apenas com uma pequena lentidão.
Com a query a seguir pude acompanhar onde as rotinas de autovacuum estavam trabalhando:
SELECT datname, usename, pid, current_timestamp - xact_start
AS xact_runtime, query
FROM pg_stat_activity
WHERE upper(query) like '%VACUUM%'
and pid <> pg_backend_pid()
ORDER BY xact_start;
Com a próxima query pude comprovar a necessidade de vaccum nas tabelas escolhidas pelos processos de autovacuum, visto que a quantidade de linhas mortas se encontrava acima dos valores já visto nesse banco de dados.
SELECT relname, n_dead_tup
FROM pg_stat_user_tables
ORDER BY n_dead_tup DESC;
Após algumas horas o banco de dados operava de forma segura, livre de potenciais bloqueios e nenhuma instabilidade foi registrada.
Com o banco de dados em seu pleno funcionamento, demos início aos trabalhos para criação de PDCAs.
- Quais indicadores podemos implementar para evitar que chegue a esse ponto novamente?
- Quais melhorias nos processo de administração do banco de dados podem ser realizadas?
Munida da query a seguir pude identificar em quais tabelas os processo de vaccum não estavam sendo realizados. Aqui surgiu o primeiro PDCA, garantir que todas as tabelas do banco de dados sejam abrangidas pelas rotinas de vacuum (vacuum + autovacuum) ao menos uma vez ao mês.
SELECT relname, last_vacuum, last_autovacuum, last_analyze, last_autoanalyze
FROM pg_stat_user_tables
ORDER BY last_vacuum ASC;
O próximo PDCA definido consiste em integrar a observabilidade da métrica "MaximumUsedTransactionIDs". Essa métrica é fundamental por indicar a idade do ID da transação sem vacuum mais antigo. É crucial evitar que alcance o valor crítico de 2.146.483.648, pois, ao atingir esse limite, o banco entra em um estado de somente leitura, podendo resultar em inconsistências nos dados. É aqui que as coisas ficam sérias, então muito trabalho deve ser feito para evitar que ocorra.
Com o PDCA definido, as ações foram executadas, em conjunto o acompanhamento comprovou a conformidade e estabilidade do banco, garantindo assim mais uma ação de grande importância que passou despercebida aos olhos dos usuários finais, mas que como DBA gravei na memória. Espero que meu relato possa ajudar!
Agora te convido a participar da nossa comunidade, o Fórum da Casa do Desenvolvedor, onde você com certeza encontrará ainda mais conteúdos que irão te ajudar no dia-a-dia na área de tecnologia, basta clicar no botão abaixo: