Como fazer o controle de origem para seu banco de dados usando DevOps

Como fazer o controle de origem para seu banco de dados usando DevOps

Um ambiente DevOps robusto requer uma integração contínua para todos os componentes do sistema. No entanto, frequentemente, o banco de dados é omitido da equação, gerando problemas que vão desde lançamentos de produção frágeis até práticas de desenvolvimento ineficientes. Isso sem mencionar a dificuldade de integração de novos programadores.

No post de hoje vamos tratar sobre alguns aspectos únicos dos bancos de dados, tanto relacionais quanto no SQL, em um ambiente bem-sucedido de integração contínua. Para saber mais, não deixe de conferir!

Controle de origem através do esquema

A primeira questão que vamos abordar é o controle de origem através dos esquemas. Não é correto que desenvolvedores apliquem alterações em um banco de dados de forma ad hoc. Isso seria como fazer alterações em arquivos de JavaScript editando-os diretamente no servidor de produção.

Ao planejar seu controle de origem para banco de dados, certifique-se de capturar tudo, o que inclui, mas não se limita à:

  • Tabelas ou Colecções

  • Restrições

  • Índices

  • Views

  • Procedimentos Armazenados, Funções e Triggers

  • Configuração do banco de dados

Até aqui, talvez você esteja pensando: “Ok, mas estou usando um banco de dados sem esquemas, justamente para não precisar de controle de origem”. No entanto, mesmo assim, você precisará contabilizar os índices e as configurações gerais do banco de dados. Se o esquema de indexação for diferente no banco de dados de controle de qualidade e no de produção, será muito mais difícil realizar testes de desempenho significativos.

Existem dois tipos básicos de controle de origem para bancos de dados, que chamaremos de "esquema total" e "script de alteração".

Esquema total do controle de origem

O controle de origem no esquema total é aquele em que o controle de origem se assemelha bastante com a aparência do banco de dados. Ao usar esse padrão, você pode ver todas as tabelas e exibições, o que facilita a compreensão do banco de dados, sem que haja a necessidade de implantá-lo.

Um exemplo de esquema total de controle de origem para o SQL Server é o SQL Server Data Tools (SSDT). Através dessa ferramenta, todos os seus objetos do banco de dados ficam em termos de scripts CREATE. Isso pode ser especialmente conveniente quando você deseja prototipar um novo objeto usando SQL e, em seguida, colar o rascunho final diretamente no projeto do banco de dados sob o controle de origem.

Outro exemplo de controle de origem de esquema total é a Entity Framework Migrations. Em vez de scripts SQL, o banco de dados é representado principalmente pelas classes C # / VB. Mas, novamente, você pode obter uma boa imagem do banco de dados navegando no código-fonte.

Ao trabalhar com o controle de origem no esquema total, você geralmente não escreve seus scripts de migração diretamente. As ferramentas de implantação determinam quais alterações são necessárias, comparando o estado atual do banco de dados com a versão idealizada no controle de origem. Isso permite que você faça alterações rapidamente no banco de dados e veja os resultados. Ao usar esse tipo de ferramenta, raramente altero o banco de dados diretamente e, em vez disso, permito que o conjunto de ferramentas faça a maior parte do trabalho.

Estabilis-BannerCTA_SITE-1

Ocasionalmente, as ferramentas não são suficientes, mesmo com scripts de pré e pós-implantação. Nesses casos, o script de migração gerado terá que ser modificado manualmente por um desenvolvedor de banco de dados ou DBA, o que pode interromper seu esquema de implantação contínua. Isso geralmente acontece quando há grandes alterações na estrutura de uma tabela, pois o script de migração gerado pode ser ineficiente nesses casos.

Por fim, outra vantagem do controle de fonte de esquema total: ele suporta a análise de código. Por exemplo, se você alterar o nome de uma coluna, mas esquecer de alterá-la em uma exibição, o SSDT retornará com um erro de compilação. Assim como a tipagem estática para linguagens de programação de aplicativos, isso captura muitos erros e impede que você tente implantar scripts claramente quebrados.

Script de alteração no controle de origem

O outro tipo de controle de origem é o script de alteração. Em vez de armazenar os objetos do banco de dados, você armazena a lista de etapas necessárias para criar os objetos do banco de dados. O que eu usei com sucesso no passado é o Liquibase, mas todos funcionam praticamente do mesmo jeito.

A principal vantagem do script de alteração é que você tem o controle total de como será o script de migração final. Isso facilita a realização de alterações complexas, como quando as tabelas são divididas, ou mesmo, combinadas.

Porém, infelizmente, existem diversas desvantagens nesse tipo de controle de origem. A primeira é a necessidade de escrever os scripts de mudança. Por mais que isso te dê mais controle, o processo é muito demorado. É mais fácil adicionar apenas uma propriedade a uma classe C # do que escrever o script ALTER TABLE em longhand.

O script de alteração no controle de origem torna-se ainda mais problemático quando se trabalha com coisas como o SchemaBinding. Para aqueles que não estão familiarizados com o termo, isso permite alguns recursos avançados no SQL Server ao bloquear o esquema da tabela. Se você fizer alterações no design de uma tabela ou visualização, terá primeiro que remover o SchemaBinding de qualquer visualização que toque na tabela ou exibição. E se essas exibições estiverem vinculadas ao esquema por outras exibições, elas também precisarão estar temporariamente desvinculadas. Tudo isso pode soar como um belo clichê, porém, a verdade é que enquanto para uma ferramenta de controle de origem no esquema você tem mais facilidades, fazer o processo manualmente dá muito mais trabalho e as chances de erros são maiores.

Outra desvantagem das ferramentas de script de alteração no controle de origem é que elas tendem a ser muito opacas. Assim, se você quiser conhecer as colunas de uma tabela sem implantá-las, você precisará ter cada script de alteração que irá tocar a tabela. E isso facilita a perda de dados.

Qual modelo de controle de origem usar?

Ao iniciar um projeto de banco de dados do zero, eu sempre escolho o controle de origem de acordo com a disponibilidade. Isso permite que os desenvolvedores trabalhem com mais rapidez, já que não é necessário grandes conhecimentos para utilizá-lo com sucesso, desde que você tenha uma pessoa para configurá-lo.

Para bancos de dados já existentes, especialmente aqueles que já tem alguns anos, a alteração de script no controle de origem é geralmente a melhor opção. Ferramentas do esquema podem ser menos ou mais eficientes de acordo com design do banco de dados, porém, as ferramentas de alteração de script funcionam em qualquer banco de dados, independentemente do layout ou design.

Além disso, ferramentas de esquema são mais difíceis de construir, portanto, podem simplesmente não estar disponíveis para seu banco de dados específico.

Gestão de dados

Tabelas podem ser facilmente classificadas como “gerenciadas”, “de usuários” ou “híbridas”, dependendo da natureza dos dados que elas contêm. A maneira como você utiliza essas tabelas varia, dependendo da categoria a qual elas pertencem.

Tabelas Gerenciadas

Um erro comum ao fazer o controle de origem é esquecer os dados. Invariavelmente irão existir aquelas “tabelas de consulta” que contém dados que os usuários não devem modificar. Por exemplo, podem existir dados na tabela relativos às regras do negócio, os vários códigos de status para uma máquina de estado, ou ainda, uma lista de pares com chaves de valor que correspondem ao código do aplicativo.

Os dados nesse tipo de tabela devem ser tratados como código-fonte. Alterações a ele precisam passar pelo mesmo processo de revisão e controle de qualidade que você usaria para qualquer outra alteração de código. E é especialmente importante que as alterações sejam implantadas automaticamente, junto com outros aplicativos e bancos de dados. Caso contrário, essas alterações podem ser esquecidas acidentalmente.

No SQL Server Data Tools, manipulo isso usando um script de pós-implantação. Nesse script, preencho uma tabela temporária com a aparência dos dados e uso uma instrução MERGE para atualizar a tabela real.

Se sua ferramenta de controle de código-fonte não lida bem com isso, você também pode criar uma ferramenta autônoma para realizar as atualizações de dados. O importante não é como você faz isso, mas se o processo é fácil de usar e confiável.

Tabelas de Usuários

As chamadas tabelas de usuários são qualquer tabela em que um usuário pode adicionar ou modificar dados. No nosso caso, isso inclui as duas tabelas que os usuários podem modificar diretamente (por exemplo, nome e endereço) e tabelas modificadas indiretamente por suas ações (por exemplo, recibos de envio, registros).

Os dados reais do usuário quase nunca são adicionados diretamente ao controle de origem. No entanto, é uma boa prática ter dados de amostra, de aparência realista, disponíveis para desenvolvimento e teste. Isso pode estar diretamente no projeto do banco de dados, armazenado em outro lugar no controle de origem ou, se for particularmente grande, mantido em um compartilhamento de arquivos separado.

Tabelas Híbridas

Uma tabela híbrida é aquela que armazena dados gerenciados e de usuário. Existem duas maneiras pelas quais isso pode ser dividido.

Uma divisão de coluna é quando os usuários podem modificar algumas colunas, mas não outras. Nesse cenário, você trata a tabela como uma tabela gerenciada normal, mas com a restrição adicional e nunca atualiza uma coluna controlada pelo usuário.

Uma divisão de linha é quando você tem determinados registros que os usuários não podem modificar. Um cenário comum que me deparo é a necessidade de ter valores codificados na tabela usuários. Em sistemas maiores, posso ter um ID de usuário separado para cada micro-serviço que possa fazer alterações independentemente de qualquer pessoa real. Por exemplo, um usuário pode ser chamado de "Importador de dados bancários".

A melhor maneira que encontrei para gerenciar tabelas híbridas divididas por linha é por meio de chaves reservadas. Quando defino a coluna de identidade / número automático, defino o valor inicial em 1.000, deixando os usuários de 1 a 999 gerenciados por meio do controle de origem. Isso requer um banco de dados que permita definir manualmente um valor em uma coluna de identidade. No SQL Server, isso é feito por meio do comando SET IDENTITY_INSERT.

Outra opção para lidar com esse cenário é ter uma coluna chamada “SystemControlled” ou algo do gênero. Quando definido como 1 / true, significa que o aplicativo não permitirá modificações diretas. Se definido como 0 / false, os scripts de implantação sabem como ignorá-lo.

Bancos de dados de desenvolvedores individuais

Depois de ter o esquema e os dados no controle de origem, você pode dar o próximo passo e começar a configurar bancos de dados de desenvolvedores individuais. Assim como cada desenvolvedor deve ser capaz de executar sua própria instância de um servidor da Web, cada desenvolvedor que deve conseguir modificar o design do banco de dados e ser capaz de executar sua própria cópia do banco de dados.

Esta regra é frequentemente violada, prejudicando a equipe de desenvolvimento. Invariavelmente, os desenvolvedores que fazem alterações em um ambiente compartilhado vão interferir uns com os outros. Eles podem até entrar em “duelos de implantação”, onde dois desenvolvedores tentam implantar alterações no banco de dados. Quando isso acontece com uma ferramenta de esquema, ela alternadamente reverte as alterações da outra, mesmo sem perceber. Se estiver usando uma ferramenta de alteração de script, o banco de dados poderá ser colocado em um estado indeterminado, em que os scripts de migração não funcionarão mais e precisarão ser restaurados a partir de backups.

Outro problema é o desvio do esquema. É quando o banco de dados de desenvolvimento não corresponde mais ao que está no controle de origem. Com o tempo, os bancos de dados de desenvolvedores tendem a acumular tabelas que não são de produção, scripts de teste, visualizações temporárias e outras informações a serem limpas. Isso é muito mais fácil de fazer quando cada desenvolvedor possui seu próprio banco de dados, o qual poderá redefinir quando quiser.

A última e mais importante questão é que os desenvolvedores de serviços e interfaces de usuário precisam de uma plataforma estável para escrever seu código. Se o banco de dados compartilhado estiver constantemente em fluxo, eles não poderão funcionar de maneira eficiente. Em uma empresa em que trabalhei, não era comum ver os desenvolvedores gritarem “os serviços caíram novamente” e depois passar uma hora jogando videogames enquanto o banco de dados compartilhado era colocado de volta.

Bancos de Dados Compartilhados de Desenvolvedor e Integração

A regra número um para um banco de dados de integração ou de desenvolvedor compartilhado é que ninguém deveria modificar o banco de dados diretamente. A única maneira de atualizar um banco de dados compartilhado é através do servidor de construção e do processo de integração / implementação contínua. Isso não só impede o desvio do esquema, como permite que as atualizações agendadas reduzam as interrupções.

Como regra geral, permito que o banco de dados de desenvolvedor compartilhado seja atualizado sempre que um check-in for feito na ramificação relevante. Isso pode ser um pouco perturbador, mas geralmente é gerenciável. E você precisa de um local para verificar os scripts de implantação antes que eles atinjam a integração.

Para meus bancos de dados de integração, tenho a tendência de programar implantações para acontecer uma vez por dia, junto com quaisquer serviços. Isso fornece uma plataforma estável com a qual os desenvolvedores de interface do usuário podem trabalhar. Poucas coisas são tão frustrantes para os desenvolvedores da IU quanto não saber se o código que de repente começa a falhar foi um erro ou um problema nos serviços / banco de dados.

Segurança de banco de dados e controle de origem

Um aspecto frequentemente negligenciado no gerenciamento de banco de dados é a segurança. Especificamente, quais usuários e funções têm acesso a quais tabelas, visualizações e procedimentos armazenados. No pior dos casos, o aplicativo obtém acesso total ao banco de dados e pode ler e escrever em todas as colunas de todas as mesas, mesmo aquelas que não interessam. As violações de dados geralmente resultam da exploração de falhas em um aplicativo utilitário secundário com muito mais direitos de acesso do que o necessário.

A principal objeção ao bloqueio do banco de dados é: "não sabemos o que vai quebrar". Como nunca foi bloqueado antes, os desenvolvedores literalmente não têm ideia de quais permissões o aplicativo realmente precisa.

A solução para isso é colocar a permissão no controle de origem desde o primeiro dia. Dessa forma, quando o desenvolvedor testar o aplicativo, ele falhará no início, se as permissões estiverem incorretas. Isso, por sua vez, significa que, no momento em que chegar ao controle de qualidade, todas as permissões são liquidadas e não há adivinhação ou risco de uma permissão ausente.

Conteinerização

Dependendo da natureza do seu projeto, a conteinerização do banco de dados é uma etapa opcional. Para ilustrar por que, ofereceremos dois casos de uso.

Para o nosso primeiro caso de uso, o projeto tem uma estrutura de ramificação muito simples: há uma ramificação "dev" que é alimentada em uma ramificação de QA, que por sua vez alimenta o armazenamento temporário e, finalmente, a produção. Isso pode ser tratado por quatro bancos de dados compartilhados, um para cada estágio no pipeline.

No segundo caso de uso, temos um conjunto de ramificações de recursos principais. Cada ramificação principal é subdividida em uma ramificação dev e QA. Os recursos precisam passar pelo controle de qualidade antes de se tornarem candidatos à mesclagem no ramo principal, portanto, cada recurso principal precisa de seu próprio ambiente de teste.

No primeiro caso de uso, a conteinerização é provavelmente um desperdício de tempo, mesmo que seus serviços da Web precisem de contêineres. Para o segundo caso de uso, a conteinerização de algum tipo é crítica. Se não forem contêineres reais (por exemplo, Docker), pelo menos, scripts de implantação que possam gerar novos ambientes conforme necessário (por exemplo, AWS ou Azure) ao criar novos ramos principais de recursos.

(Texto escrito por Jonathan Allen, programador que trabalha em projetos de MIS para uma clínica de saúde desde final dos anos 90, trazendo-os do Access e do Excel para uma solução empresarial. Depois de passar cinco anos escrevendo sistemas de negociação automatizados para o setor financeiro, ele se tornou consultor em vários projetos, incluindo a interface do usuário para um depósito robótico, a camada intermediária para software de pesquisa de câncer e as grandes necessidades de dados de uma grande companhia de seguros imobiliários.)

Conteúdo traduzido e adaptado de: Infoq

Gostou de saber mais sobre controle de origem em banco de dados? Confira também como fazer a automação de microservices usando AWS. Clique aqui e saiba mais.

Estabilis-BannerCTA_BLOG-1