
Denna artikel förklarar i detalj hur man hanterar problem relaterade till databas-kompatibilitet vid deployment. Vi kommer att berätta vad som kan hända med dina produktionsapplikationer om du försöker göra en deployment utan förberedelser. Därefter kommer vi att gå igenom stegen i applikationens livscykel som krävs för att uppnå noll driftstopp (anm. övers.: nedan – zero downtime). Resultatet av våra operationer kommer att vara att tillämpa en bakåtstridande databasändring på ett framåtriktat kompatibelt sätt.
Om du vill förstå kodexemplen i artikeln, kan du hittar dem på .
Introduktion
Zero downtime deployment
Vad är detta mystiska zero downtime deployment? Можно сказать, это когда ваше приложение развернуто так, что вы можете успешно вводить новую версию приложения на продакшн, в то время как пользователь не замечает его недоступности. С точки зрения пользователя и компании, это наилучший из возможных сценариев деплоя, поскольку таким образом можно вводить новые функции и устранять ошибки без перебоев в работе.
Hur når man detta? Det finns flera sätt, här är ett av dem:
- distribuera version nr 1 av din tjänst
- genomföra databas-migration
- distribuera version nr 2 av din tjänst parallellt med version nr 1
- så snart du ser att version nr 2 fungerar som den ska, ta bort version nr 1
- klart!
Lätt, eller hur? Tyvärr är det inte så enkelt, och vi kommer att gå igenom detta mer ingående senare. Nu, låt oss kolla på en annan ganska vanlig deployment-process – blue green deployment.
Har du någonsin hört talas om ? С Cloud Foundry это чрезвычайно легко сделать. Просто гляньте на , där vi beskriver detta mer detaljerat. För att kort sammanfatta, påminner vi om hur man gör blue green deployment:
- se till att ha två kopior av din produktionskod (“blue” och “green”);
- dirigera all trafik till blue-miljön, dvs. så att produktions-URL:erna pekar dit;
- distribuera och testa alla applikationsändringar i green-miljön;
- byta URL:erna från blue till green-miljön
Blue green deployment är en metod som gör att du enkelt kan införa nya funktioner utan att oroa dig för att produktionen ska gå sönder. Detta beror på att även om något skulle hända, kan du enkelt rulla tillbaka till föregående miljö, helt enkelt genom att "vända på en switch".
Efter att ha läst allt ovan kan du ställa dig frågan: Vilken relation har zero downtime till Blue green deployment?
Tja, de har ganska mycket gemensamt, eftersom underhåll av två kopior av samma miljö kräver dubbla ansträngningar. Det är därför vissa team, som Martin Fowler hävdar, föredrar en variation av denna metod: :
ett annat alternativ är att använda samma databas, vilket skapar blågröna svitchar för webb- och domänlager. I en sådan metod kan databaser ofta bli ett problem, särskilt när du behöver ändra dess schema för att stödja en ny version av programvaran.
Och här når vi huvudproblemet i den här artikeln. Databas. Låt oss titta på denna fras igen.
genomför databasmigration.
Nu måste du ställa dig frågan - vad händer om ändringen av databasen blir bakåtkompatibel? Kommer inte min första version av applikationen att gå sönder? Faktum är att det är just vad som kommer att hända…
Så trots de stora fördelarna med zero downtime / blue green deployment, tenderar företag att följa den nästa mer säkra processen för att distribuera sina applikationer:
- förbered ett paket med den nya versionen av applikationen
- stäng av den körande applikationen
- köra skript för att migrera databasen
- distribuera och starta den nya versionen av applikationen
I den här artikeln kommer vi att beskriva i detalj hur du kan arbeta med databasen och koden för att dra nytta av fördelarna med zero downtime deployment.
Databasproblem
Om du har en stateless applikation som inte lagrar några data i databasen kan du uppnå zero downtime deployment direkt. Tyvärr behöver större delen av programvaran på något sätt lagra data. Det är därför du måste tänka efter två gånger innan du gör några ändringar i schemat. Innan vi dyker in i detaljerna om hur man ändrar schemat på ett sätt som möjliggör distribution utan driftstopp, låt oss först fokusera på versionshanteringsschemat.
Versionshanteringsschema
I den här artikeln kommer vi att använda som ett verktyg för versionshantering (övers. anm.: vi talar om databas-migrationer). Självklart kommer vi också att skriva en Spring Boot-applikation som har inbyggd stöd för Flyway och kommer att utföra schemamigrering vid inställning av applikationskontext. Genom att använda Flyway kan du lagra migreringsskript i mappen för dina projekt (som standard i classpath:db/migration). Här kan du se ett exempel på sådana migreringsfiler
└── db
└── migration
├── V1__init.sql
├── V2__Add_surname.sql
├── V3__Final_migration.sql
└── V4__Remove_lastname.sqlI det här exemplet ser vi fyra migrationsscenarier som, om de inte har utförts tidigare, kommer att utföras en efter en vid start av applikationen. Låt oss titta på en av filerna (V1__init.sql) som ett exempel.
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');Allt talar för sig självt: du kan använda SQL för att ange hur din databas ska ändras. För mer information om Spring Boot och Flyway, kolla in .
Genom att använda ett versionshanteringsverktyg med Spring Boot får du två stora fördelar:
- du separerar databasändringar från kodändringar
- databasens migration sker tillsammans med distributionen av din applikation, dvs. din distributionsprocess förenklas
Problem med databasen
I nästa avsnitt av artikeln kommer vi att fokusera på att titta på två tillvägagångssätt för databasändringar.
- bakåtkompatibilitet
- bakåtkompatibilitet
Den första kommer att behandlas som en varning att man inte ska göra zero downtime-distribution utan föregående förberedelse… Den andra erbjuder en lösning på hur man kan göra distribution utan driftstopp och samtidigt bibehålla bakåtkompatibilitet.
Vårt projekt som vi kommer att arbeta med kommer att vara en enkel Spring Boot Flyway-applikation som har Person med first_name och last_name i databasen (anm. övers.: Person är en tabell, ochfirst_name och last_name — är fälten i den). Vi vill döpa om till last_name på surname.
Antaganden
Innan vi går in på detaljerna är det viktigt att klargöra några antaganden angående våra applikationer. Det huvudsakliga resultat vi vill uppnå kommer att vara en ganska enkel process.
Notera. Business PRO-TIP. Att förenkla processer kan spara dig mycket pengar på support (ju fler människor som arbetar i ditt företag, desto mer pengar kan du spara)!
Det ska inte göras någon databasåterställning
Detta förenklar distributionsprocessen (vissa databasåterställningar är praktiskt taget omöjliga, som att återställa en radering). Vi föredrar att endast återställa applikationer. På så sätt ser din distributionspipeline likadan ut även om du har olika databaser (t.ex. SQL och NoSQL).
Det måste alltid finnas möjlighet att återställa applikationen till en version tillbaka (inte mer)
Rullback bör endast göras vid behov. Om det finns ett fel i den aktuella versionen som är svårt att åtgärda, bör vi kunna återgå till den senaste fungerande versionen. Vi antar att denna senaste fungerande version är den föregående. Att stödja kompatibiliteten för kod och databas för mer än en distribution skulle vara extremt svårt och kostsamt.
Anteckning. För att öka läsbarheten, kommer vi i den här artikeln att ändra majorversionen av applikationen.
Steg 1: Ursprungligt tillstånd
Applikationens version: 1.0.0
Databasens version: v1
Kommentar
Detta kommer att vara det ursprungliga tillståndet för applikationen.
Ändringar i databasen
Databasen innehåller 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');Ändringar i koden
Applikationen sparar Person-data i 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
+ "]";
}
}Bakåtkompatibel omdöpning av kolumn
Låt oss se ett exempel på hur man ändrar kolumnnamnet:
Observera. Följande exempel kommer avsiktligt att orsaka fel. Vi visar detta för att demonstrera problematiken med databasens kompatibilitet.
Applikationens version: 2.0.0.BAD
Databasens version: v2bad
Kommentar
Nuvarande ändringar TILLÅTER oss INTE att köra två instanser (gammal och ny) samtidigt. Därför blir zero downtime deployment svårt att uppnå (med vissa antaganden är det faktiskt omöjligt).
A/B-testning
Den nuvarande situationen är att vi har en applikation av versionen 1.0.0, implementerad i produktion, och databasen v1. Vi måste distribuera en andra instans av applikationen, version 2.0.0.BAD, och uppdatera databasen till v2bad.
Steg:
- har distribuerats en ny instans av applikationen version
2.0.0.BAD, som uppdaterar databasen tillv2bad - i databasen
v2badkolumnenlast_namefinns inte längre — den har ändrats tillsurname - uppdateringen av databasen och applikationen har genomförts framgångsrikt, och vissa instanser körs i
1.0.0, andra i2.0.0.BAD. Alla är kopplade till databasenv2bad - alla instanser av versionen
1.0.0kommer att börja ge fel, eftersom de kommer att försöka infoga data i kolumnenlast_name, som inte längre finns - alla instanser av versionen
2.0.0.BADkommer att fungera utan problem
Som du ser, om vi gör bakåtkompatibla ändringar i databasen och applikationen, är A/B-testning omöjligt.
Återställning av applikationen
Låt oss anta att efter att ha försökt genomföra en A/B-distribution (not: förmodligen avser författaren A/B-testning) beslutade vi att vi behövde återgå applikationen till versionen 1.0.0. Antag att vi inte vill göra en återställning av databasen.
Steg:
- vi stoppar applikationsinstansen i version
2.0.0.BAD - databasen är fortfarande
v2bad - eftersom versionen
1.0.0inte förstår vad som menas medsurname, vi kommer att se fel - helvetet har släppts löst, vi kan inte gå tillbaka längre
Som ni ser, om vi gör bakåtkompatibla förändringar av databasen och applikationen, kan vi inte rulla tillbaka till den föregående versionen.
Loggar för skriptkörning
Bakåtkompatibelt scenario:
01) Kör 1.0.0
02) Vänta på att appen (1.0.0) ska starta
03) Generera en person genom att anropa POST localhost:9991/person till version 1.0.0
04) Kör 2.0.0.BAD
05) Vänta på att appen (2.0.0.BAD) ska starta
06) Generera en person genom att anropa POST localhost:9991/person till version 1.0.0 <-- detta bör misslyckas
07) Generera en person genom att anropa POST localhost:9992/person till version 2.0.0.BAD <-- detta bör lyckas
Startar appen i version 1.0.0
Generera en person i version 1.0.0
Skickar ett POST-anrop till 127.0.0.1:9991/person. Detta är svaret:
{"firstName":"b73f639f-e176-4463-bf26-1135aace2f57","lastName":"b73f639f-e176-4463-bf26-1135aace2f57"}
Startar appen i version 2.0.0.BAD
Generera en person i version 1.0.0
Skickar ett POST-anrop till 127.0.0.1:9991/person. Detta är svaret:
curl: (22) Begärd URL returnerade fel: 500 Internt serverfel
Generera en person i version 2.0.0.BAD
Skickar ett POST-anrop till 127.0.0.1:9995/person. Detta är svaret:
{"firstName":"e156be2e-06b6-4730-9c43-6e14cfcda125","surname":"e156be2e-06b6-4730-9c43-6e14cfcda125"}Ändringar i databasen
Migrationsskriptet som döper om last_name på surname
Ursprungligt Flyway-skript:
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');Skriptet som döper om last_name.
-- Denna ändring är bakåtkompatibel - du kan inte göra A/B-testning
ALTER TABLE PERSON CHANGE last_name surname VARCHAR;Ändringar i koden
Vi har ändrat namn på fältet lastName till surname.
Döp om kolumnen på ett bakåtkompatibelt sätt
Detta är den vanligaste situationen som vi kan stöta på. Vi måste göra bakåtinkompatibla förändringar. Vi har redan bevisat att för att göra en driftsättning utan driftstopp måste vi inte bara tillämpa databasmigreringarna utan ytterligare åtgärder. I detta avsnitt av artikeln kommer vi att utföra 3 driftsättningar av applikationen tillsammans med databasmigreringar för att nå önskat resultat och samtidigt behålla bakåtkompatibiliteten.
Notera. Kom ihåg att vi har en databasversion
v1. Den innehåller kolumnernafirst_nameochlast_name. Vi måste ändralast_nametillsurname. Vi har också en applikation version1.0.0,som fortfarande inte användersurname.
Steg 2: Lägga till surname
Applikationens version: 2.0.0
Databasens version: v2
Kommentar
Genom att lägga till en ny kolumn och kopiera innehållet skapar vi bakåtkompatibla databasanpassningar. Samtidigt, om vi rullar tillbaka JAR:en eller om vi har en fungerande gammal JAR, bryts den inte under körning.
Vi rullar ut en ny version
Steg:
- genomför databasmigrering för att skapa den nya kolumnen
surname. Nu är din databasversionv2 - kopiera data från
last_namepåsurname. Observera, vilket, om du har mycket av dessa data, innebär att du bör överväga batchmigrering! - skriv koden där de används BÅDA och avsnitt, och gammal kolumn. Nu är din appversion
2.0.0 - läs värdet från kolumnen
surname, om det intenull, eller från last_name, omsurnameinte angivet. Du kan ta bortgetLastName()från koden, eftersom det kommer att genullvid återställning av din app med3.0.0till2.0.0.
Om du använder Spring Boot Flyway kommer dessa två steg att utföras vid start av versionen 2.0.0 av applikationen. Om du kör databashanteringsverktyget manuellt måste du göra två olika åtgärder (först uppdatera db-versionen manuellt och sedan distribuera den nya applikationen).
Viktigt. Kom ihåg att den nyligen skapade kolumnen FÅR INTE vara NOT NULL. Om du gör en återställning vet inte den gamla applikationen om den nya kolumnen och kommer inte att ställa in den under
Insert.Men om du lägger till detta begränsning och din databas ärv2, kommer det att kräva att man sätter ett värde för den nya kolumnen. Detta kommer att leda till begränsningsbrott.Viktigt. Du bör ta bort metoden
getLastName(), eftersom det i versionen3.0.0inte finns något begrepp om kolumnenlast_name. Det betyder att det kommer att ställas in på null. Du kan behålla metoden och lägga till kontroller förnull, men det mycket bättre lösningen är att se till att logikengetSurname()är korrekt med ett icke-null värde.
A/B-testning
Den nuvarande situationen är att vi har en applikation av versionen 1.0.0, distribuerad i produktionen, och databasen i v1. Vi måste distribuera en andra instans av appversionen 2.0.0, som kommer att uppdatera databasen till v2.
Steg:
- har distribuerats en ny instans av applikationen version
2.0.0, som uppdaterar databasen tillv2 - under tiden har vissa förfrågningar behandlats av versionens instanser
1.0.0 - uppdateringen genomfördes framgångsrikt, och du har flera fungerande instanser av appversionen
1.0.0och andra versioner2.0.0.Alla kommunicerar med databasen iv2 - version
1.0.0använder inte kolumnen surname i databasen, medan versionen2.0.0anser. De stör inte varandra, och inga fel bör uppstå. - version
2.0.0sparar data både i den gamla och den nya kolumnen, vilket säkerställer bakåtkompatibilitet.
Viktigt. Om du har några förfrågningar som räknar element baserat på värden från den gamla / nya kolumnen, bör du komma ihåg att du nu har värden som dubblar (de migrerar förmodligen fortfarande). Till exempel, om du vill räkna antalet användare vars efternamn (vad kolumnen nu än kallas) börjar med bokstaven
A, så fram till att datamigreringen är klar (old→newkolumn) kan du få inkonsekventa data om du gör en förfrågan till den nya kolumnen.
Återställning av applikationen
Nu har vi appversionen 2.0.0 och databasen i v2.
Steg:
- återställ din applikation till version
1.0.0. - version
1.0.0använder inte kolumnen i databasensurname, så återställningen bör lyckas
Ändringar i DB
DB:n innehåller en kolumn med namnet last_name.
Ursprungligt Flyway-skript:
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');Tilläggsskript surname.
Observera. Kom ihåg att DU INTE KAN LÄGGA TILL några NOT NULL-restriktioner på den tillagda kolumnen. Om du återställer JAR-filen, kommer den gamla versionen inte att känna till den tillagda kolumnen och kommer automatiskt att sätta den till NULL. Om det finns en sådan restriktion kommer den gamla applikationen helt enkelt att gå sönder.
-- OBS: Denna kolumn kan inte ha NOT NULL-restriktion eftersom om du återställer, kommer den gamla versionen inte att veta om denna kolumn
-- och kommer alltid att sätta den till NULL
ALTER TABLE PERSON ADD surname varchar(255);
-- VI FÖRUTSÄTTER ATT DET ÄR EN SNABB MIGRATION - ANNARS SKULLE VI MÅSTE MIGRERA I OMgångar
UPDATE PERSON SET PERSON.surname = PERSON.last_nameÄndringar i koden
Vi sparar data både i last_name.ts surname. Samtidigt läser vi från last_name, eftersom denna kolumn är mest relevant. Under deployment-processen kan vissa förfrågningar ha hanterats av en instans av applikationen som ännu inte hade uppdaterats.
/*
* 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
+ "]";
}
}Steg 3: Ta bort last_name från koden
Applikationens version: 3.0.0
Databasens version:v3
Kommentar
Anm. övers.: Uppenbarligen har texten i denna block kopierats felaktigt av författaren från steg 2 i den ursprungliga artikeln. I detta steg skall ändringar i applikationskoden göras för att ta bort funktionaliteten som använder kolumnen last_name.
Genom att lägga till en ny kolumn och kopiera dess innehåll skapade vi bakåtkompatibla ändringar i DB. Om vi återställer JAR-filen eller har en fungerande gammal JAR, kommer den inte att gå sönder under körning.
Återställning av applikationen
För närvarande har vi applikationen version 3.0.0 och databasen v3. Version 3.0.0 sparar inte data i last_name. Detta innebär att den mest aktuella informationen lagras i surname .
Steg:
- återställ din applikation till version
2.0.0. - version
2.0.0använder ochlast_nameochsurname. - version
2.0.0kommer att tasurname, om den inte är null, annars —last_name
Ändringar i databasen
Det finns inga strukturella förändringar i DB. Följande skript körs för att genomföra den slutliga migreringen av de gamla data:
-- VI FÖRUTSÄTTER ATT DET ÄR EN SNABB MIGRATION - ANNARS SKULLE VI MÅSTE MIGRERA I OMgångar
-- OCKSÅ KONTROLLERAR VI INTE OM VI INTE ÖVERTRÄDER EXISTERANDE ANSUBER. VI SKULLE BEHÖVA JÄMFÖRA
-- ANMÄLNINGAR FÖR ATT SÄKERSTÄLLA ATT OM DET REDAN FINNS EN ANMÄLAN MED HÖGRE VERSIONNUMMER
-- KOMMER VI INTE ATT ÖVERTRÄDA DEN.
UPDATE PERSON SET PERSON.surname = PERSON.last_name;
-- TA BORT NOT NULL-RESTRIKTIONEN; ANNARS FÖRSÖKER DU ATT INFÖRA NULL-VÄRDE PÅ LAST_NAME
-- MED NOT_NULL RESTRIKTION.
ALTER TABLE PERSON MODIFY COLUMN last_name varchar(255) NULL DEFAULT NULL;Ändringar i koden
Anm. övers.: Beskrivningen av detta block har även felaktigt kopierats av författaren från steg 2. I enlighet med berättelsens logik i artikeln bör ändringarna i koden i detta steg syfta till att ta bort de element som hanterar kolumnen last_name.
Vi lagrar data både i last_name.ts surname. Dessutom läser vi från kolumnen last_name, eftersom den är mest relevant. Under distributionen kan vissa förfrågningar behandlas av en instans som ännu inte har uppdaterats.
/*
* 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
+ "]";
}
}Steg 4: Ta bort last_name från databasen
Applikationens version: 4.0.0
Databasens version: v4
Kommentar
På grund av att versionskoden 3.0.0 inte använde kolumnen last_name, under körningen kommer inget dåligt att hända om vi återgår till 3.0.0 efter att kolumnen har tagits bort från databasen.
Loggar för skriptkörning
Vi kommer att göra det på följande sätt:
01) Starta 1.0.0
02) Vänta på att appen (1.0.0) startar
03) Generera en person genom att anropa POST localhost:9991/person till version 1.0.0
04) Starta 2.0.0
05) Vänta på att appen (2.0.0) startar
06) Generera en person genom att anropa POST localhost:9991/person till version 1.0.0
07) Generera en person genom att anropa POST localhost:9992/person till version 2.0.0
08) Stäng appen (1.0.0)
09) Starta 3.0.0
10) Vänta på att appen (3.0.0) startar
11) Generera en person genom att anropa POST localhost:9992/person till version 2.0.0
12) Generera en person genom att anropa POST localhost:9993/person till version 3.0.0
13) Stäng appen (3.0.0)
14) Starta 4.0.0
15) Vänta på att appen (4.0.0) startar
16) Generera en person genom att anropa POST localhost:9993/person till version 3.0.0
17) Generera en person genom att anropa POST localhost:9994/person till version 4.0.0
Startar appen i version 1.0.0
Generera en person i version 1.0.0
Skickar en post till 127.0.0.1:9991/person. Detta är svaret:
{"firstName":"52b6e125-4a5c-429b-a47a-ef18bbc639d2","lastName":"52b6e125-4a5c-429b-a47a-ef18bbc639d2"}
Startar appen i version 2.0.0
Generera en person i version 1.0.0
Skickar en post till 127.0.0.1:9991/person. Detta är svaret:
{"firstName":"e41ee756-4fa7-4737-b832-e28827a00deb","lastName":"e41ee756-4fa7-4737-b832-e28827a00deb"}
Generera en person i version 2.0.0
Skickar en post till 127.0.0.1:9992/person. Detta är svaret:
{"firstName":"0c1240f5-649a-4bc5-8aa9-cff855f3927f","lastName":"0c1240f5-649a-4bc5-8aa9-cff855f3927f","surname":"0c1240f5-649a-4bc5-8aa9-cff855f3927f"}
Stänger appen 1.0.0
Startar appen i version 3.0.0
Generera en person i version 2.0.0
Skickar en post till 127.0.0.1:9992/person. Detta är svaret:
{"firstName":"74d84a9e-5f44-43b8-907c-148c6d26a71b","lastName":"74d84a9e-5f44-43b8-907c-148c6d26a71b","surname":"74d84a9e-5f44-43b8-907c-148c6d26a71b"}
Generera en person i version 3.0.0
Skickar en post till 127.0.0.1:9993/person. Detta är svaret:
{"firstName":"c6564dbe-9ab5-40ae-9077-8ae6668d5862","surname":"c6564dbe-9ab5-40ae-9077-8ae6668d5862"}
Stänger appen 2.0.0
Startar appen i version 4.0.0
Generera en person i version 3.0.0
Skickar en post till 127.0.0.1:9993/person. Detta är svaret:
{"firstName":"cbe942fc-832e-45e9-a838-0fae25c10a51","surname":"cbe942fc-832e-45e9-a838-0fae25c10a51"}
Generera en person i version 4.0.0
Skickar en post till 127.0.0.1:9994/person. Detta är svaret:
{"firstName":"ff6857ce-9c41-413a-863e-358e2719bf88","surname":"ff6857ce-9c41-413a-863e-358e2719bf88"}Ändringar av databasen
Angående v3 tar vi helt enkelt bort kolumnen last_name och lägger till de saknade begränsningarna.
-- TA BORT KOLUMNEN
ALTER TABLE PERSON DROP last_name;
-- LÄGG TILL BEGRÄNSNINGAR
UPDATE PERSON SET surname='' WHERE surname IS NULL;
ALTER TABLE PERSON ALTER COLUMN surname VARCHAR NOT NULL;Ändringar i koden
Ändringar i koden saknas.
Utdata
Vi har framgångsrikt tillämpat bakåtkompatibla ändringar av kolumnnamnet genom att genomföra flera bakåtkompatibla distributioner. Nedan följer en sammanfattning av de genomförda åtgärderna:
- distribution av applikationen version
1.0.0medv1databas schema (kolumnnamn =last_name) - distribution av applikationen version
2.0.0,som sparar data ilast_nameochsurname. Applikationen läser frånlast_name. Databasen är i versionv2, som innehåller kolumner somlast_name, såväl somsurname. surnameär en kopia av last_name. (OBS: denna kolumn får inte ha begränsningen not null) - distribution av applikationen version
3.0.0, som sparar data endast isurnameoch läser från surname. När det gäller databasen sker den senaste migreringenlast_namepåsurname. Även begränsningen NOT NULL tas bort frånlast_name. Databasen är nu i versionv3 - distribution av applikationen version
4.0.0— inga ändringar görs i koden. Databasen deployasv4, som tar bortlast_name. Här kan du lägga till eventuella saknade begränsningar i databasen.
Genom att följa detta tillvägagångssätt kan du alltid återgå till en tidigare version utan att bryta kompatibiliteten mellan databasen / applikationen.
Kod
All kod som används i den här artikeln är tillgänglig på . Nedan finns en ytterligare beskrivning.
Projekt
Efter kloning av repositoriet kommer du att se följande mappstruktur.
├── boot-flyway-v1 - 1.0.0 version av appen med v1 av schemat
├── boot-flyway-v2 - 2.0.0 version av appen med v2 av schemat (bakåtkompatibel - app kan rullas tillbaka)
├── boot-flyway-v2-bad - 2.0.0.BAD version av appen med v2bad av schemat (bakåtinkompatibel - app kan inte rullas tillbaka)
├── boot-flyway-v3 - 3.0.0 version av appen med v3 av schemat (app kan rullas tillbaka)
└── boot-flyway-v4 - 4.0.0 version av appen med v4 av schemat (app kan rullas tillbaka)Skript
Du kan köra skripten som beskrivs nedan, vilka demonstrerar bakåtkompatibla och inkompatibla förändringar i databasen.
För att se fallet med bakåtkompatibla förändringar, kör:
.\/scripts\/scenario_backward_compatible.shOch för att se fallet med bakåtinkompatibla förändringar, kör:
.\/scripts\/scenario_backward_incompatible.shSpring Boot Sample Flyway
Alla exempel är hämtade från Spring Boot Sample Flyway.
Du kan titta på http://localhost:8080/flyway, där det finns en lista över skript.
Det här exemplet inkluderar också H2-konsolen (vid http://localhost:8080/h2-console), så att du kan se status på databasen (standard JDBC URL — jdbc:h2:mem:testdb).
Ytterligare
Läs även andra artiklar i vår blogg:
Källa: habr.com
