Desafio 8: migração de banco de dados para a nuvem

A sua empresa vai migrar para a nuvem. Desenhe uma solução para manter os bancos de dados on-premise e na nuvem sincronizados, de modo que seja possível utilizar um ambiente ou outro com uma simples configuração e de maneira rápida.

O diagrama abaixo representa o escopo geral do problema:

Escopo macro do problema

É possível fazer a replicação de várias formas, porém, vamos analisar os extremos dessas abordagens, para entendermos o que é possível de acontecer dentro do escopo do problema replicação de uma maneira geral:

Opção 1: Você possui alguns servidores conectados a uma rede local que você deseja manter sempre atualizados para fins de failover e balanceamento de carga. Nesse caso, você estaria considerando soluções que são síncronas, pre-emptivas e, portanto, sem conflitos.

Opção 2: Os usuários da sua empresa possuem uma cópia local do banco de dados em seus laptops quando saem do escritório. Eles fazem alterações enquanto estão ausentes e precisam mesclar essas alterações com o banco de dados principal quando retornam. Nesse caso, você deseja uma abordagem de replicação assíncrona utilizando lazy loading. Será necessário considerar como lidar com conflitos nos casos em que o mesmo registro foi modificado tanto no servidor principal quanto na cópia local.

Para o problema proposto, a solução síncrona faz mais sentido. O que nos deixa com uma única opção, basicamente: replicação bidirecional síncrona entre ambos os bancos de dados. Qualquer outra forma de replicação irá gerar problemas adicionais na gestão do failover.

Com base na minha experiência, o banco de dados mais popular atualmente (seja na versão on-premise ou nuvem), é o PostgreSQL (ou pg, para os íntimos). Portanto, vou me ater aos conceitos utilizados nesse sistema gerenciador de banco de dados para facilitar o entendimento. No entanto, caso você use outros sistemas gerenciadores (como MySQL ou SQL Server), existem mecanismos similares que fazem a mesma função com mais detalhes disponíveis aqui e aqui.

Formas de replicação

A grosso modo, podemos efetuar a replicação de três formas. Vamos explorar cada uma delas em maiores detalhes.

Replicação Física: esta opera no nível binário, replicando todo o cluster de dados do servidor de banco de dados primário para um ou mais servidores secundários. Ela replica as alterações feitas no banco de dados primário transmitindo os logs de registro de transações (WAL) para os servidores secundários, que aplicam essas alterações aos seus próprios dados. A replicação física oferece alto desempenho e é adequada para cenários de failover, onde um servidor secundário pode assumir se o servidor primário falhar.

Replicação Lógica: ao contrário da replicação física, que replica no nível binário, a replicação lógica opera no nível lógico, replicando objetos de banco de dados individuais (como tabelas, schemas e similares) e suas alterações. Ela oferece mais flexibilidade, permitindo a replicação seletiva de tabelas ou bancos de dados específicos. A replicação lógica é útil em casos de uso como integração de dados, data warehousing e distribuição de dados em vários bancos de dados. Pode ser usada para o nosso problema também, mas o grande problema aqui seria a necessidade de escolher quais tabelas serão replicadas (o que pode gerar problemas de integridade em caso de algum problema na configuração).

Extensões: Além dos mecanismos de replicação nativos, existem ferramentas de terceiros que fornecem recursos avançados de replicação para o PostgreSQL, como a pglogical. Ela funciona replicando os dados via um mecanismo de streaming, usando o modelo pub/sub. Além disso, suporta resolução de conflitos e filtragem de dados no momento da replicação também.

Com todas as opções analisadas, dado a natureza do problema, a solução é utilizar replicação física. Lembrando que para uma replicação dessa ser bem sucedida, é necessário criar um caminho viável entre as duas instâncias de banco de dados, através da internet (usando VPNs ou algum mecanismos similar na camada de infraestrutura).

Porém, outro problema permanece: como fazer o chaveamento entre as conexões? Afinal, se um banco falhar e o outro assumir, como a aplicação que utiliza aquele banco de dados saberá qual utilizar?

A solução passa por um usar uma solução de connection pool junto com um load balancer. No ecossistema do pg, o pooler mais conhecido é o pg-bouncer. Esse tipo de extensão gerencia melhor as conexões (através do reaproveitamento das mesmas entre os usuários conectados), inclusive, agindo como uma espécie de load balancer na camada do banco (que é exatamente o que a precisamos). Com isso, além de melhorar a performance por meio do uso inteligente de recursos, é possível controlar o chaveamento por ele. Na frente dele, podemos usar um load balancer comum, como um HAProxy.

Com todos esses itens rodando, temos a seguinte topologia:

Basicamente, temos várias camadas de abstração gerenciando o chaveamento e balanceamento de carga entre as camadas. Isso resulta na seguinte solução final:

Solução completa, usando todos os pontos discutidos

Com essa topologia, ocorre uma replicação física entre os bancos, mesmo em localidades diferentes. Além disso, há uma boa gestão de conexões, resultando em melhor desempenho e utilização de recursos de forma geral. O chaveamento é realizado através da configuração no load balancer, permitindo até mesmo o chaveamento automático entre os bancos em caso de detecção de falha em algum deles (essa verificação é possível automatizar usando health checks).

Com esses mecanismos funcionando, temos a solução do problema de uma forma minimamente razoável.

Caso você encontre algum erro ou informação incompleta neste artigo, por favor, me mande uma mensagem que eu ajustarei o texto.

Obrigado por ler até aqui!

Até!

Você gostou do conteúdo e gostaria de fazer mentoria comigo? Clique aqui e descubra como.

--

--