
Este artigo explica em detalhes como resolver os problemas associados à compatibilidade do banco de dados durante a implantação. Informaremos o que pode acontecer com seus aplicativos no produto se você tentar executar uma implantação sem qualquer preparação preliminar. Em seguida, percorreremos as etapas do ciclo de vida do aplicativo, que são necessárias para que o tempo de inatividade seja zero ( aprox. Transl .: mais - zero de inatividade ). O resultado de nossas operações será a aplicação de uma alteração de banco de dados incompatível com versões anteriores de uma maneira compatível com versões anteriores.
Se você quiser entender os exemplos de código do artigo, você os encontrará no GitHub .
1. Introdução
Implantação de tempo de inatividade zero
Qual é a implantação mística de tempo de inatividade zero ? Podemos dizer isso quando seu aplicativo é implantado, para que você possa introduzir com êxito uma nova versão do aplicativo para produção, enquanto o usuário não percebe sua inacessibilidade. Do ponto de vista do usuário e da empresa, esse é o melhor cenário de implantação possível, pois dessa forma é possível introduzir novas funções e eliminar erros sem interrupção.
Como conseguir isso? Existem várias maneiras, aqui está uma delas:
- expanda a versão 1 do seu serviço
- migrar o banco de dados
- implemente a versão 2 do seu serviço em paralelo com a versão 1
- assim que vir que o número da versão 2 está funcionando como deveria, remova o número da versão 1
- pronto!
Fácil né? Infelizmente, isso não é tão simples, e consideraremos isso em detalhes posteriormente. Agora vamos verificar outro processo de implantação bastante comum - implantação verde azul.
Você já ouviu falar em implantação verde azul ? Com o Cloud Foundry, isso é extremamente fácil de fazer. Dê uma olhada neste artigo, onde o descrevemos com mais detalhes. Resumindo, lembramos como fazer a implantação verde azul:
- Garanta a operação de duas cópias do seu código de produção ("azul" e "verde");
- direcione todo o tráfego para o ambiente azul, ou seja, para que os URLs de produção sejam apontados para lá;
- Implante e teste todas as alterações de aplicativo em um ambiente verde
- alternar URLs do ambiente azul para o verde
A implantação verde azul é uma abordagem que permite introduzir facilmente novos recursos sem se preocupar com a interrupção da produção. Isso ocorre porque mesmo que algo aconteça, você pode reverter facilmente para o ambiente anterior simplesmente clicando em um botão.
Depois de ler todas as opções acima, você pode fazer a pergunta: O que o tempo de inatividade zero tem a ver com a implantação verde azul?
Bem, eles têm muito em comum, porque o suporte a duas cópias do mesmo ambiente requer esforços duplos para mantê-las. É por isso que algumas equipes, de acordo com Martin Fowler , aderem a variações dessa abordagem:
outra opção é usar o mesmo banco de dados, criando opções verde-azuladas para camadas da web e de domínio. Nessa abordagem, os bancos de dados geralmente podem ser um problema, especialmente quando você precisa alterar seu esquema para suportar uma nova versão do software.
E aqui chegamos ao principal problema neste artigo. O banco de dados Vamos dar uma outra olhada nesta frase.
migrar o banco de dados.
Agora você deve se perguntar: e se a alteração do banco de dados for incompatível com versões anteriores? Minha primeira versão do aplicativo não será interrompida? De fato, é exatamente isso que vai acontecer ...
Assim, apesar dos enormes benefícios de zero tempo de inatividade / implantação verde azul, as empresas tendem a seguir o seguinte processo de implantação mais seguro para seus aplicativos:
- preparar um pacote com uma nova versão do aplicativo
- desligar um aplicativo em execução
- executar scripts para migração de banco de dados
- implantar e iniciar a nova versão do aplicativo
Neste artigo, descreveremos em detalhes como você pode trabalhar com um banco de dados e código para aproveitar a implantação de tempo de inatividade zero.
Problemas de banco de dados
Se você tiver um aplicativo sem estado que não armazena nenhum dado no banco de dados, poderá obter a implantação de tempo de inatividade zero imediatamente. Infelizmente, a maioria dos softwares precisa armazenar dados em algum lugar. É por isso que você deve pensar duas vezes antes de fazer alterações no circuito. Antes de nos aprofundarmos nos detalhes de como alterar o esquema para que seja possível implantar sem tempo de inatividade, vamos nos concentrar primeiro no esquema de controle de versão.
Esquema de controle de versão
Neste artigo, usaremos o Flyway como uma ferramenta para controle de versão ( aproximadamente tradução: estamos falando sobre migração de banco de dados ). Naturalmente, também escreveremos um aplicativo Spring Boot que tenha suporte Flyway embutido e migrará o circuito ao configurar o contexto do aplicativo. Ao usar o Flyway, você pode armazenar scripts de migração na pasta de projetos (por padrão, em classpath:db/migration
). Aqui você pode ver um exemplo desses arquivos de migração.
└── db └── migration ├── V1__init.sql ├── V2__Add_surname.sql ├── V3__Final_migration.sql └── V4__Remove_lastname.sql
Neste exemplo, vemos 4 cenários de migração que, se não foram executados anteriormente, serão executados um após o outro quando o aplicativo for iniciado. Vejamos um dos arquivos ( V1__init.sql
) como um exemplo.
CREATE TABLE PERSON ( id BIGINT GENERATED BY DEFAULT AS IDENTITY, first_name varchar(255) not null, last_name varchar(255) not null ); insert into PERSON (first_name, last_name) values ('Dave', 'Syer');
Tudo fala perfeitamente por si: você pode usar o SQL para determinar como seu banco de dados deve ser modificado. Para obter mais informações sobre o Spring Boot e o Flyway, consulte Documentos de inicialização do Spring .
O uso do controle de versão com o Spring Boot oferece 2 grandes benefícios:
- você separa as alterações no banco de dados das alterações no código
- a migração do banco de dados ocorre junto com a distribuição do seu aplicativo, ou seja, seu processo de implantação é simplificado
Solucionando problemas de banco de dados
Na próxima seção do artigo, focaremos em considerar duas abordagens para alterações no banco de dados.
- incompatibilidade com versões anteriores
- compatibilidade com versões anteriores
O primeiro será considerado como um aviso de que você não deve executar uma implantação de tempo de inatividade zero sem preparação preliminar ... O segundo oferece uma solução para como você pode executar uma implantação sem tempo de inatividade e, ao mesmo tempo, manter a compatibilidade com versões anteriores.
Nosso projeto, no qual trabalharemos, será um aplicativo Spring Boot Flyway simples, no qual há uma Person
com nome e last_name
no banco de dados ( aprox. Por.: Person
é uma tabela e f irst_name
e irst_name
são os campos ). Queremos renomear last_name
para surname
.
Pressupostos
Antes de nos aprofundarmos nos detalhes, precisamos esboçar algumas suposições sobre nossos aplicativos. O principal resultado que queremos alcançar será um processo bastante simples.
Nota Negócios PRO-DICA. A racionalização de processos pode economizar muito dinheiro em suporte (quanto mais pessoas trabalham em sua empresa, mais dinheiro você pode economizar)!
Não há necessidade de reverter o banco de dados
Isso simplifica o processo de implantação (algumas reversões do banco de dados são quase impossíveis, por exemplo, exclusão de reversão). Preferimos reverter apenas os aplicativos. Dessa forma, mesmo se você tiver bancos de dados diferentes (por exemplo, SQL e NoSQL), seu pipeline de implantação terá a mesma aparência.
SEMPRE é possível reverter o aplicativo uma versão novamente (não mais)
A reversão deve ser feita apenas se necessário. Se houver um erro na versão atual que não seja fácil de corrigir, poderemos retornar a versão funcional mais recente. Assumimos que esta versão de trabalho mais recente seja a anterior. Manter a compatibilidade de código e banco de dados para mais de uma distribuição seria extremamente difícil e dispendioso.
Nota Para maior legibilidade, dentro da estrutura deste artigo, alteraremos a versão principal do aplicativo.
Etapa 1: Estado inicial
Versão do aplicativo: 1.0.0
Versão do banco de dados: v1
Comentário
Este será o estado inicial do aplicativo.
Alterações no banco de dados
O banco de dados contém last_name.
CREATE TABLE PERSON ( id BIGINT GENERATED BY DEFAULT AS IDENTITY, first_name varchar(255) not null, last_name varchar(255) not null ); insert into PERSON (first_name, last_name) values ('Dave', 'Syer');
Alterações de código
O aplicativo salva os dados da pessoa em last_name
:
/* * Copyright 2012-2016 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package sample.flyway; import javax.persistence.Entity; import javax.persistence.GeneratedValue; import javax.persistence.Id; @Entity public class Person { @Id @GeneratedValue private Long id; private String firstName; private String lastName; public String getFirstName() { return this.firstName; } public void setFirstName(String firstName) { this.firstName = firstName; } public String getLastName() { return this.lastName; } public void setLastName(String lastname) { this.lastName = lastname; } @Override public String toString() { return "Person [firstName=" + this.firstName + ", lastName=" + this.lastName + "]"; } }
Renomeação de coluna incompatível
Vejamos um exemplo de como alterar o nome de uma coluna:
Atenção O exemplo a seguir será interrompido intencionalmente. Mostramos isso para demonstrar o problema de compatibilidade do banco de dados.
Versão do aplicativo: 2.0.0.BAD
Versão do banco de dados: v2bad
Comentário
As alterações atuais NÃO nos permitem executar duas instâncias (antigas e novas) ao mesmo tempo. Portanto, será difícil conseguir uma implantação com tempo de inatividade zero (suposições levadas em consideração, isso é praticamente impossível).
Teste A / B
A situação atual é que temos um aplicativo versão 1.0.0,
implementado no prod, e um banco de dados v1
. Devemos implantar a segunda instância do aplicativo, versão 2.0.0.BAD
, e atualizar o banco de dados para v2bad
.
Passos:
- implementou uma nova instância do aplicativo versão
2.0.0.BAD
, que atualiza o banco de dados para v2bad
- a coluna
last_name
não existe mais no banco de dados v2bad
- foi alterada para surname
- As atualizações de banco de dados e aplicativos foram bem-sucedidas e algumas instâncias funcionam na
1.0.0
, outras na 2.0.0.BAD
. Tudo relacionado ao v2bad
- todas as instâncias da versão
1.0.0
começarão a gerar erros porque tentarão inserir dados na coluna last_name
, que não é mais - todas as instâncias da versão
2.0.0.BAD
funcionarão sem problemas
Como você pode ver, se fizermos alterações incompatíveis no banco de dados e nos aplicativos, o teste A / B será impossível.
Aplicação de reversão
Vamos supor que, depois de tentar executar a implantação A / B ( aprox. Transl.: Provavelmente, o autor estava se referindo ao teste A / B aqui ), decidimos que precisamos reverter o aplicativo para a versão 1.0.0.
Suponha que não queremos reverter o banco de dados.
Passos:
- paramos a instância da aplicação versão
2.0.0.BAD
- banco de dados ainda é
v2bad
- como a versão
1.0.0
não entende o que surname
, veremos erros - inferno se libertou, não podemos mais voltar
Como você pode ver, se fizermos alterações incompatíveis no banco de dados e nos aplicativos, não podemos reverter para a versão anterior.
Logs de execução de script
Backward incompatible scenario: 01) Run 1.0.0 02) Wait for the app (1.0.0) to boot 03) Generate a person by calling POST localhost:9991/person to version 1.0.0 04) Run 2.0.0.BAD 05) Wait for the app (2.0.0.BAD) to boot 06) Generate a person by calling POST localhost:9991/person to version 1.0.0 <-- this should fail 07) Generate a person by calling POST localhost:9992/person to version 2.0.0.BAD <-- this should pass Starting app in version 1.0.0 Generate a person in version 1.0.0 Sending a post to 127.0.0.1:9991/person. This is the response: {"firstName":"b73f639f-e176-4463-bf26-1135aace2f57","lastName":"b73f639f-e176-4463-bf26-1135aace2f57"} Starting app in version 2.0.0.BAD Generate a person in version 1.0.0 Sending a post to 127.0.0.1:9991/person. This is the response: curl: (22) The requested URL returned error: 500 Internal Server Error Generate a person in version 2.0.0.BAD Sending a post to 127.0.0.1:9995/person. This is the response: {"firstName":"e156be2e-06b6-4730-9c43-6e14cfcda125","surname":"e156be2e-06b6-4730-9c43-6e14cfcda125"}
Alterações no banco de dados
Script de migração que renomeia surname
Script Flyway de origem:
CREATE TABLE PERSON ( id BIGINT GENERATED BY DEFAULT AS IDENTITY, first_name varchar(255) not null, last_name varchar(255) not null ); insert into PERSON (first_name, last_name) values ('Dave', 'Syer');
Um script que renomeia last_name
.
-- This change is backward incompatible - you can't do A/B testing ALTER TABLE PERSON CHANGE last_name surname VARCHAR;
Alterações de código
lastName
nome do campo lastName
para surname
.
Renomeando uma coluna de maneira compatível com versões anteriores
Essa é a situação mais comum que podemos encontrar. Precisamos fazer alterações incompatíveis com versões anteriores. Já provamos que, para uma implantação sem tempo de inatividade, não devemos aplicar apenas a migração do banco de dados sem ações adicionais. Nesta seção do artigo, faremos 3 implantações do aplicativo junto com as migrações do banco de dados para alcançar o resultado desejado e, ao mesmo tempo, manter a compatibilidade com versões anteriores.
Nota Lembre-se de que temos uma versão de banco de dados v1
. Ele contém as colunas first_name
e last_name
. Devemos mudar last_name
para surname
. Também temos um aplicativo versão 1.0.0,
que ainda não usa surname
.
Etapa 2: adicionar sobrenome
Versão do aplicativo: 2.0.0
Versão do banco de dados: v2
Comentário
Ao adicionar uma nova coluna e copiar seu conteúdo, criamos alterações no banco de dados compatíveis com versões anteriores. Ao mesmo tempo, se revertermos o JAR ou tivermos um JAR antigo funcional, ele não será interrompido no tempo de execução.
Lançamos a nova versão
Passos:
- migrar o banco de dados para criar uma nova coluna de
surname
. Agora sua versão db v2
- copie dados do
last_name
para o surname
. Observe que, se você tiver muitos desses dados, considere a migração em lote! - escreva o código onde ambas as colunas nova e antiga são usadas. Agora seu aplicativo versão
2.0.0
- leia o valor da coluna
surname
, se não for null
, ou de l ast_name
se o surname
não surname
especificado. Você pode remover getLastName()
do código, pois ele retornará null
quando você reverter seu aplicativo de 3.0.0
para 2.0.0
.
Se você estiver usando o Spring Boot Flyway, essas duas etapas serão executadas durante o lançamento da versão 2.0.0
aplicativo. Se você executar a ferramenta de controle de versão do banco de dados manualmente, precisará fazer duas coisas diferentes para isso (primeiro atualize a versão do banco de dados manualmente e, em seguida, implante um novo aplicativo).
É importante. Lembre-se de que uma coluna recém-criada NÃO DEVE SER NULA . Se você reverter, o aplicativo antigo não saberá sobre a nova coluna e não a instalará durante a Insert.
Mas se você adicionar essa restrição, e seu banco de dados for v2
, será necessário definir o valor da nova coluna. O que levará a violações de restrições.
É importante. Você deve remover o método getLastName()
, pois a versão 3.0.0
não possui o conceito de uma coluna last_name
no código. Isso significa que nulo será definido lá. Você pode deixar o método e adicionar verificações null
, mas uma solução muito melhor seria garantir que você tenha escolhido o valor diferente de zero correto na lógica getSurname()
.
Teste A / B
A situação atual é que temos uma versão 1.0.0
aplicativo implantada no produto e um banco de dados na v1
. Precisamos implantar a segunda instância do aplicativo versão 2.0.0
, que atualizará o banco de dados para a v2
.
Passos:
- implementou uma nova instância do aplicativo versão
2.0.0
, que atualiza o banco de dados para a v2
- Enquanto isso, alguns pedidos foram tratados por instâncias da versão
1.0.0
- a atualização foi bem-sucedida e você tem várias instâncias de trabalho do aplicativo versão
1.0.0
e do restante da versão 2.0.0.
Todos se comunicam com o banco de dados na v2
- a versão
1.0.0
não usa a coluna de sobrenome no banco de dados, mas a versão 2.0.0
. Eles não interferem um com o outro e não deve haver erros. - versão
2.0.0
armazena dados na coluna antiga e na nova, o que fornece compatibilidade com versões anteriores
É importante. Se você tiver alguma dúvida que conte itens com base em valores da coluna antiga / nova, lembre-se de que agora você tem valores duplicados (provavelmente eles ainda estão migrando). Por exemplo, se você quiser contar o número de usuários cujo sobrenome (independentemente do nome da coluna) começa com a letra A
, antes que a migração de dados ( old
→ new
coluna) seja concluída, você poderá ter dados inconsistentes se executar uma consulta em uma nova coluna.
Aplicação de reversão
Agora, temos um aplicativo versão 2.0.0
e um banco de dados na v2
.
Passos:
- reverta seu aplicativo para a versão
1.0.0
. - a versão
1.0.0
não usa a coluna de surname
no banco de dados, portanto, a reversão deve ser bem-sucedida
Alterações no banco de dados
O banco de dados contém uma coluna chamada last_name
.
Script Flyway de origem:
CREATE TABLE PERSON ( id BIGINT GENERATED BY DEFAULT AS IDENTITY, first_name varchar(255) not null, last_name varchar(255) not null ); insert into PERSON (first_name, last_name) values ('Dave', 'Syer');
O script para adicionar surname
.
Atenção Lembre-se de que você NÃO PODE ADICIONAR nenhuma restrição NOT NULL à coluna adicionada. Se você reverter o JAR, a versão antiga não tem idéia da coluna adicionada e a definirá automaticamente como NULL. Se houver essa restrição, o aplicativo antigo simplesmente será interrompido.
-- NOTE: This field can't have the NOT NULL constraint cause if you rollback, the old version won't know about this field -- and will always set it to NULL ALTER TABLE PERSON ADD surname varchar(255); -- WE'RE ASSUMING THAT IT'S A FAST MIGRATION - OTHERWISE WE WOULD HAVE TO MIGRATE IN BATCHES UPDATE PERSON SET PERSON.surname = PERSON.last_name
Alterações de código
Armazenamos dados em last_name
e surname
. Ao mesmo tempo, lemos de last_name
, pois essa coluna é mais relevante. Durante o processo de implantação, algumas solicitações podem ter sido tratadas por uma instância de aplicativo que ainda não foi atualizada.
/* * Copyright 2012-2016 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package sample.flyway; import javax.persistence.Entity; import javax.persistence.GeneratedValue; import javax.persistence.Id; @Entity public class Person { @Id @GeneratedValue private Long id; private String firstName; private String lastName; private String surname; public String getFirstName() { return this.firstName; } public void setFirstName(String firstName) { this.firstName = firstName; } /** * Reading from the new column if it's set. If not the from the old one. * * When migrating from version 1.0.0 -> 2.0.0 this can lead to a possibility that some data in * the surname column is not up to date (during the migration process lastName could have been updated). * In this case one can run yet another migration script after all applications have been deployed in the * new version to ensure that the surname field is updated. * * However it makes sense since when looking at the migration from 2.0.0 -> 3.0.0. In 3.0.0 we no longer * have a notion of lastName at all - so we don't update that column. If we rollback from 3.0.0 -> 2.0.0 if we * would be reading from lastName, then we would have very old data (since not a single datum was inserted * to lastName in version 3.0.0). */ public String getSurname() { return this.surname != null ? this.surname : this.lastName; } /** * Storing both FIRST_NAME and SURNAME entries */ public void setSurname(String surname) { this.lastName = surname; this.surname = surname; } @Override public String toString() { return "Person [firstName=" + this.firstName + ", lastName=" + this.lastName + ", surname=" + this.surname + "]"; } }
Etapa 3: Removendo last_name do código
Versão do aplicativo: 3.0.0
Versão do banco de dados: v3
Comentário
Nota trans .: Aparentemente, o autor copiou por engano o texto deste bloco da etapa 2. No artigo original, nesta etapa, devem ser feitas alterações no código do aplicativo que visa remover a funcionalidade que usa a coluna last_name
.
Adicionando uma nova coluna e copiando seu conteúdo, criamos alterações no banco de dados compatíveis com versões anteriores. Além disso, se revertermos o JAR ou tivermos um JAR antigo funcionando, ele não será interrompido no tempo de execução.
Aplicação de reversão
Atualmente, temos o aplicativo versão 3.0.0
e o banco de dados v3
. A versão 3.0.0
não salva dados no last_name
. Isso significa que o surname
armazena as informações mais atuais.
Passos:
- reverta seu aplicativo para a versão
2.0.0
. - versão
2.0.0
usa last_name
e surname
. - a versão
2.0.0
usará surname
se não for nulo; caso contrário, last_name
Alterações no banco de dados
Não há alterações estruturais no banco de dados. O script a seguir é executado, que executa a migração final dos dados antigos:
-- WE'RE ASSUMING THAT IT'S A FAST MIGRATION - OTHERWISE WE WOULD HAVE TO MIGRATE IN BATCHES -- ALSO WE'RE NOT CHECKING IF WE'RE NOT OVERRIDING EXISTING ENTRIES. WE WOULD HAVE TO COMPARE -- ENTRY VERSIONS TO ENSURE THAT IF THERE IS ALREADY AN ENTRY WITH A HIGHER VERSION NUMBER -- WE WILL NOT OVERRIDE IT. UPDATE PERSON SET PERSON.surname = PERSON.last_name; -- DROPPING THE NOT NULL CONSTRAINT; OTHERWISE YOU WILL TRY TO INSERT NULL VALUE OF THE LAST_NAME -- WITH A NOT_NULL CONSTRAINT. ALTER TABLE PERSON MODIFY COLUMN last_name varchar(255) NULL DEFAULT NULL;
Alterações de código
Nota trans .: a descrição desse bloco também foi copiada por engano pelo autor da etapa 2. De acordo com a lógica da matéria do artigo, as alterações no código nessa etapa devem ter como objetivo remover dele elementos que funcionam com a coluna last_name
.
Armazenamos dados em last_name
e surname.
Além disso, lemos da coluna last_name
, pois é mais relevante. Durante o processo de implantação, algumas solicitações podem ser processadas por uma instância que ainda não foi atualizada.
/* * Copyright 2012-2016 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package sample.flyway; import javax.persistence.Entity; import javax.persistence.GeneratedValue; import javax.persistence.Id; @Entity public class Person { @Id @GeneratedValue private Long id; private String firstName; private String surname; public String getFirstName() { return this.firstName; } public void setFirstName(String firstName) { this.firstName = firstName; } public String getSurname() { return this.surname; } public void setSurname(String lastname) { this.surname = lastname; } @Override public String toString() { return "Person [firstName=" + this.firstName + ", surname=" + this.surname + "]"; } }
Etapa 4: Removendo last_name do banco de dados
Versão do aplicativo: 4.0.0
Versão do banco de dados: v4
Comentário
Devido ao fato de o código da versão 3.0.0
não usar a coluna last_name
, nada de ruim acontecerá durante a execução se voltarmos para a 3.0.0
após remover a coluna do banco de dados.
Logs de execução de script
We will do it in the following way: 01) Run 1.0.0 02) Wait for the app (1.0.0) to boot 03) Generate a person by calling POST localhost:9991/person to version 1.0.0 04) Run 2.0.0 05) Wait for the app (2.0.0) to boot 06) Generate a person by calling POST localhost:9991/person to version 1.0.0 07) Generate a person by calling POST localhost:9992/person to version 2.0.0 08) Kill app (1.0.0) 09) Run 3.0.0 10) Wait for the app (3.0.0) to boot 11) Generate a person by calling POST localhost:9992/person to version 2.0.0 12) Generate a person by calling POST localhost:9993/person to version 3.0.0 13) Kill app (3.0.0) 14) Run 4.0.0 15) Wait for the app (4.0.0) to boot 16) Generate a person by calling POST localhost:9993/person to version 3.0.0 17) Generate a person by calling POST localhost:9994/person to version 4.0.0 Starting app in version 1.0.0 Generate a person in version 1.0.0 Sending a post to 127.0.0.1:9991/person. This is the response: {"firstName":"52b6e125-4a5c-429b-a47a-ef18bbc639d2","lastName":"52b6e125-4a5c-429b-a47a-ef18bbc639d2"} Starting app in version 2.0.0 Generate a person in version 1.0.0 Sending a post to 127.0.0.1:9991/person. This is the response: {"firstName":"e41ee756-4fa7-4737-b832-e28827a00deb","lastName":"e41ee756-4fa7-4737-b832-e28827a00deb"} Generate a person in version 2.0.0 Sending a post to 127.0.0.1:9992/person. This is the response: {"firstName":"0c1240f5-649a-4bc5-8aa9-cff855f3927f","lastName":"0c1240f5-649a-4bc5-8aa9-cff855f3927f","surname":"0c1240f5-649a-4bc5-8aa9-cff855f3927f"} Killing app 1.0.0 Starting app in version 3.0.0 Generate a person in version 2.0.0 Sending a post to 127.0.0.1:9992/person. This is the response: {"firstName":"74d84a9e-5f44-43b8-907c-148c6d26a71b","lastName":"74d84a9e-5f44-43b8-907c-148c6d26a71b","surname":"74d84a9e-5f44-43b8-907c-148c6d26a71b"} Generate a person in version 3.0.0 Sending a post to 127.0.0.1:9993/person. This is the response: {"firstName":"c6564dbe-9ab5-40ae-9077-8ae6668d5862","surname":"c6564dbe-9ab5-40ae-9077-8ae6668d5862"} Killing app 2.0.0 Starting app in version 4.0.0 Generate a person in version 3.0.0 Sending a post to 127.0.0.1:9993/person. This is the response: {"firstName":"cbe942fc-832e-45e9-a838-0fae25c10a51","surname":"cbe942fc-832e-45e9-a838-0fae25c10a51"} Generate a person in version 4.0.0 Sending a post to 127.0.0.1:9994/person. This is the response: {"firstName":"ff6857ce-9c41-413a-863e-358e2719bf88","surname":"ff6857ce-9c41-413a-863e-358e2719bf88"}
Alterações no banco de dados
Para a v3
simplesmente removemos a coluna last_name
e adicionamos as restrições ausentes.
-- REMOVE THE COLUMN ALTER TABLE PERSON DROP last_name; -- ADD CONSTRAINTS UPDATE PERSON SET surname='' WHERE surname IS NULL; ALTER TABLE PERSON ALTER COLUMN surname VARCHAR NOT NULL;
Alterações de código
Não há alterações no código.
Conclusão
Aplicamos com êxito a alteração do nome da coluna incompatível com versões anteriores executando várias implantações compatíveis com versões anteriores. Abaixo está um resumo das etapas executadas:
- implementação de aplicativo versão
1.0.0
com esquema de banco de dados v1
(nome da coluna = last_name
) - implemente o aplicativo versão
2.0.0,
que salva os dados em last_name
e surname
. O aplicativo lê de last_name
. O banco de dados está na versão v2
, que contém as colunas last_name
e surname. surname
surname. surname
é uma cópia de l ast_name
. (: not null) 3.0.0
, surname
surname. , last_name
surname
. NOT NULL last_name
. v3
4.0.0
— . v4
, last_name
. .
, , / .
Código
, , Github . .
, .
├── boot-flyway-v1 - 1.0.0 version of the app with v1 of the schema ├── boot-flyway-v2 - 2.0.0 version of the app with v2 of the schema (backward-compatible - app can be rolled back) ├── boot-flyway-v2-bad - 2.0.0.BAD version of the app with v2bad of the schema (backward-incompatible - app cannot be rolled back) ├── boot-flyway-v3 - 3.0.0 version of the app with v3 of the schema (app can be rolled back) └── boot-flyway-v4 - 4.0.0 version of the app with v4 of the schema (app can be rolled back)
, , .
, :
./scripts/scenario_backward_compatible.sh
, :
./scripts/scenario_backward_incompatible.sh
Spring Boot Sample Flyway
Spring Boot Sample Flyway.
http://localhost:8080/flyway
, .
H2 ( http://localhost:8080/h2-console
), (URL jdbc — jdbc:h2:mem:testdb
).
Opcional
Leia também outros artigos em nosso blog: