Noll driftstopp och databaser

Noll driftstopp och databaser

Den här artikeln förklarar i detalj hur du löser problem med databaskompatibilitet under driftsättning. Vi kommer att berätta vad som kan hända med dina produktionsapplikationer om du försöker distribuera utan förberedelser. Vi går sedan igenom applikationens livscykelstadier som krävs för att ha noll stilleståndstid (cirka. körfält: vidare - noll stilleståndstid). Resultatet av vår verksamhet blir att tillämpa den bakåtinkompatibla databasändringen på ett bakåtkompatibelt sätt.

Om du vill förstå kodexemplen från artikeln kan du hitta dem på GitHub.

Inledning

Noll driftstopp

Vilken mystisk noll driftstopp? Du kan säga att det är när din applikation distribueras på ett sådant sätt att du framgångsrikt kan introducera en ny version av applikationen till produktion, samtidigt som användaren inte märker dess otillgänglighet. Ur ett användar- och företagsperspektiv är detta det bästa möjliga installationsscenariot eftersom det tillåter att nya funktioner introduceras och buggar kan fixas utan avbrott.

Hur uppnår man detta? Det finns flera sätt, här är ett av dem:

  • distribuera version nr 1 av din tjänst
  • utföra en databasmigrering
  • Distribuera version #2 av din tjänst parallellt med version #1
  • så fort du ser att version nr 2 fungerar som den ska, ta bort version nr 1
  • redo!

Lätt, är det inte? Tyvärr är det inte så enkelt, och vi ska titta på det i detalj senare. Låt oss nu kontrollera en annan ganska vanlig distributionsprocess - blågrön implementering.

Har du någonsin hört talas om blågrön utbyggnad? Cloud Foundry gör detta extremt enkelt. Titta bara på denna artikel, där vi beskriver detta mer i detalj. För att kort sammanfatta, låt oss påminna dig om hur du implementerar blågrönt:

  • se till att två kopior av din produktionskod ("blå" och "grön") fungerar;
  • styra all trafik till den blå miljön, d.v.s. så att produktionswebbadresser pekar dit;
  • distribuera och testa alla applikationsändringar i en grön miljö;
  • byta webbadresser från blå till grön miljö

Blue green distribution är ett tillvägagångssätt som gör att du enkelt kan introducera nya funktioner utan att behöva oroa dig för att produktionen går sönder. Detta beror på det faktum att även om något händer, kan du enkelt rulla tillbaka till den tidigare miljön genom att helt enkelt "snärta på en switch."

Efter att ha läst allt ovanstående kan du ställa frågan: Vad har noll driftstopp att göra med blågrön implementering?

Tja, de har en hel del gemensamt, eftersom att underhålla två exemplar av samma miljö kräver dubbelt arbete för att underhålla dem. Det är därför vissa lag hävdar Martin Fowler, följ en variant av detta tillvägagångssätt:

Ett annat alternativ är att använda samma databas och skapa blågröna växlar för webb- och domänlager. I detta tillvägagångssätt kan databasen ofta vara 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 kommer vi till huvudproblemet i den här artikeln. databas. Låt oss ta en ny titt på den här frasen.

utföra en databasmigrering.

Nu måste du ställa dig frågan - vad händer om databasändringen inte är bakåtkompatibel? Kommer inte min första version av appen att gå sönder? Det är faktiskt precis vad som kommer att hända...

Så även trots de enorma fördelarna med noll driftstopp/blågrön driftsättning tenderar företag att följa följande säkrare process för att distribuera sina applikationer:

  • förbereda ett paket med en ny version av programmet
  • stänga av ett program som körs
  • kör skript för att migrera databasen
  • distribuera och starta en ny version av programmet

I den här artikeln beskriver vi hur du kan arbeta med din databas och kod för att dra nytta av driftstopp utan driftstopp.

Databasproblem

Om du har en tillståndslös applikation som inte lagrar några data i databasen, kan du få noll driftstopp direkt. Tyvärr behöver de flesta program lagra data någonstans. Det är därför du bör tänka två gånger innan du gör några ändringar i kretsen. Innan vi går in på detaljerna om hur man ändrar schemat så att driftsättning utan driftstopp är möjlig, låt oss först fokusera på versionsschemat.

Versioneringsschema

I den här artikeln kommer vi att använda Flygväg som ett versionskontrollverktyg (cirka. Översättning: vi pratar om databasmigreringar). Naturligtvis kommer vi också att skriva en Spring Boot-applikation som har inbyggt Flyway-stöd och kommer att utföra schemamigrering samtidigt som applikationskontexten ställs in. När du använder Flyway kan du lagra migreringsskript i din projektmapp (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.sql

I det här exemplet ser vi 4 migreringsskript som, om de inte har körts tidigare, kommer att köras efter varandra när programmet startar. 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 är helt självförklarande: du kan använda SQL för att definiera hur din databas ska ändras. För mer information om Spring Boot och Flyway, kolla in Spring Boot Docs.

Genom att använda ett källkontrollverktyg med Spring Boot får du två stora fördelar:

  • du skiljer databasändringar från kodändringar
  • Databasmigrering sker tillsammans med utrullningen av din applikation, d.v.s. din distributionsprocess förenklas

Felsökning av databasproblem

I nästa avsnitt av artikeln kommer vi att fokusera på att titta på två metoder för databasändringar.

  • bakåtkompatibilitet
  • bakåtkompatibilitet

Den första kommer att betraktas som en varning om att du inte bör utföra driftsättning utan driftstopp utan preliminära förberedelser... Den andra erbjuder en lösning på hur du kan utföra en driftsättning utan driftstopp och samtidigt upprätthålla bakåtkompatibilitet.

Vårt projekt vi kommer att arbeta med kommer att vara en enkel Spring Boot Flyway-applikation som har Person с first_name и last_name i databasen (cirka. översättning: Person är ett bord och first_name и last_name - det här är fälten i den). Vi vill byta namn last_name в surname.

Antaganden

Innan vi går in på detaljerna finns det ett par antaganden vi måste göra om våra applikationer. Huvudresultatet vi vill uppnå kommer att vara en ganska enkel process.

Anteckningen. Business PRO-TIPS. Genom att förenkla processer kan du spara mycket pengar på support (ju fler personer du har som arbetar för ditt företag, desto mer pengar kan du spara)!

Du behöver inte återställa databasen

Detta förenklar distributionsprocessen (vissa databasåterställningar är nästan omöjliga, till exempel återställning av borttagning). Vi föredrar att endast återställa applikationer. På så sätt, även om du har olika databaser (till exempel SQL och NoSQL), kommer din distributionspipeline att se likadan ut.

Det måste ALLTID vara möjligt att återställa applikationen en version tillbaka (inte mer)

Återställning bör endast göras vid behov. Om det finns en bugg i den aktuella versionen som inte är lätt att fixa, bör vi kunna återgå till den senaste fungerande versionen. Vi antar att den senaste fungerande versionen är den tidigare. Att upprätthålla kod- och databaskompatibilitet för mer än en utrullning skulle vara extremt svårt och dyrt.

Anteckningen. För större läsbarhet kommer vi i den här artikeln att ändra huvudversionen av applikationen.

Steg 1: Ursprungligt tillstånd

Appversion: 1.0.0
DB version: v1

Kommentar

Detta kommer att vara det ursprungliga tillståndet för ansökan.

Databasförändringar

DB 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');

Kodändringar

Applikationen lagrar personuppgifter 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åt inkompatibel kolumnbyte

Låt oss titta på ett exempel på hur man ändrar ett kolumnnamn:

Uppmärksamhet. Följande exempel kommer avsiktligt att bryta saker. Vi visar detta för att visa problemet med databaskompatibilitet.

Appversion: 2.0.0.BAD

DB version: v2bad

Kommentar

De nuvarande ändringarna tillåter INTE att vi kör två instanser (gamla och nya) samtidigt. Således kommer noll driftstopp att vara svår att uppnå (om man tar hänsyn till antaganden är det faktiskt omöjligt).

A/B-testning

Den nuvarande situationen är att vi har en applikationsversion 1.0.0, distribueras i produktion och databas v1. Vi måste distribuera en andra instans av applikationen, version 2.0.0.BADoch uppdatera databasen till v2bad.

steg:

  1. en ny instans av versionsapplikationen distribueras 2.0.0.BADsom uppdaterar databasen till v2bad
  2. i databasen v2bad kolumn last_name finns inte längre - det ändrades till surname
  3. Uppdateringen av databasen och applikationen lyckades och vissa instanser körs 1.0.0, andra - in 2.0.0.BAD. Allt är kopplat till databasen v2bad
  4. alla instanser av versionen 1.0.0 kommer att börja skicka fel eftersom de kommer att försöka infoga data i kolumnen last_namesom inte längre finns
  5. alla instanser av versionen 2.0.0.BAD kommer att fungera utan problem

Som du kan se, om vi gör bakåtinkompatibla ändringar i databasen och applikationen, är A/B-testning omöjlig.

Återställning av ansökan

Låt oss anta att efter att ha försökt göra A/B-distribution (cirka. per.: författaren menade förmodligen A/B-testning här) beslutade vi att vi måste återställa applikationen till versionen 1.0.0. Låt oss säga att vi inte vill återställa databasen.

steg:

  1. vi stoppar versionsapplikationsinstansen 2.0.0.BAD
  2. databasen är stilla v2bad
  3. sedan versionen 1.0.0 förstår inte vad det är surname, vi kommer att se fel
  4. helvetet har brutit lös, vi kan inte gå tillbaka längre

Som du kan se, om vi gör bakåt inkompatibla ändringar i databasen och applikationen, kan vi inte återgå till den tidigare versionen.

Skriptkörningsloggar

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"}

Databasförändringar

Migreringsskript som byter namn last_name в surname

Källa 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');

Skript som byter namn last_name.

-- This change is backward incompatible - you can't do A/B testing
ALTER TABLE PERSON CHANGE last_name surname VARCHAR;

Kodändringar

Vi har ändrat fältnamnet lastNamesurname.

Byta namn på en kolumn på ett bakåtkompatibelt sätt

Detta är den vanligaste situationen vi kan stöta på. Vi måste göra bakåt inkompatibla förändringar. Vi har redan bevisat att för driftsättning utan driftstopp bör vi inte bara tillämpa databasmigrering utan ytterligare steg. I det här avsnittet av artikeln kommer vi att utföra 3 distributioner av applikationen tillsammans med databasmigreringar för att uppnå önskat resultat med bibehållen bakåtkompatibilitet.

Anteckningen. Kom ihåg att vi har en versionsdatabas v1. Den innehåller kolumner first_name и last_name. Vi måste ändra oss last_namesurname. Vi har även appversion 1.0.0, som ännu inte används surname.

Steg 2: Lägg till efternamn

Appversion: 2.0.0
DB version: v2

Kommentar

Genom att lägga till en ny kolumn och kopiera dess innehåll skapar vi bakåtkompatibla databasändringar. Samtidigt, om vi rullar tillbaka JAR eller har en gammal JAR igång, kommer den inte att gå sönder under körningen.

Vi rullar ut en ny version

steg:

  1. utföra en databasmigrering för att skapa en ny kolumn surname. Nu din DB-version v2
  2. kopiera data från last_name в surname. Observeraatt om du har mycket av denna data bör du överväga batchmigrering!
  3. skriv koden där de används BÅDE и nyOch den gamla kolumn. Nu din appversion 2.0.0
  4. läs värdet från kolumnen surname, om det inte är det null, eller från last_nameom surname ej angivet. Du kan ta bort getLastName() från koden, eftersom den kommer att matas ut null när du återställer din ansökan från 3.0.0 до 2.0.0.

Om du använder Spring Boot Flyway kommer dessa två steg att utföras under versionsstart 2.0.0 applikationer. Om du kör verktyget för databasversionering manuellt måste du göra två olika saker för att göra detta (först uppdatera db-versionen manuellt och sedan distribuera den nya applikationen).

Det är viktigt. Kom ihåg att den nyskapade kolumnen BORDE INTE быть INTE NULL. Om du gör en återställning känner den gamla applikationen inte till den nya kolumnen och kommer inte att installera den under Insert. Men om du lägger till denna begränsning så blir din db v2, detta kräver att värdet för den nya kolumnen ställs in. Vilket kommer att leda till överträdelser av restriktioner.

Det är viktigt. Du bör ta bort metoden getLastName(), eftersom i versionen 3.0.0 Det finns inget koncept för en kolumn i koden last_name. Det betyder att null kommer att sättas där. Du kan lämna metoden och lägga till kontroller för null, men en mycket bättre lösning skulle vara att se till det i logiken getSurname() du valde rätt värde som inte är noll.

A/B-testning

Den nuvarande situationen är att vi har en applikationsversion 1.0.0, distribueras på produktion, och databasen i v1. Vi måste distribuera en andra instans av versionsapplikationen 2.0.0som kommer att uppdatera databasen till v2.

steg:

  1. en ny instans av versionsapplikationen distribueras 2.0.0som uppdaterar databasen till v2
  2. under tiden behandlades vissa förfrågningar av versionsinstanser 1.0.0
  3. uppdateringen lyckades och du har flera körande instanser av versionsapplikationen 1.0.0 och andra versioner 2.0.0. Alla kommunicerar med databasen i v2
  4. version 1.0.0 använder inte efternamnskolumnen i databasen, utan versionen 2.0.0 använder. De stör inte varandra, och det bör inte finnas några fel.
  5. version 2.0.0 lagrar data i både den gamla och nya kolumnen, vilket säkerställer bakåtkompatibilitet

Det är viktigt. Om du har några frågor som räknar objekt baserat på värden från den gamla/nya kolumnen, bör du komma ihåg att du nu har dubbletter av värden (mest troligt migrerar de fortfarande). Till exempel, om du vill räkna antalet användare vars efternamn (vad kolumnen heter) började med bokstaven A, sedan tills datamigreringen är klar (oldnew kolumn) kan du ha inkonsekventa data om du frågar efter en ny kolumn.

Återställning av ansökan

Nu har vi appversion 2.0.0 och databas i v2.

steg:

  1. rulla tillbaka din applikation till version 1.0.0.
  2. version 1.0.0 använder inte en kolumn i databasen surname, så återställningen bör vara framgångsrik

DB ändringar

Databasen innehåller en kolumn med namnet last_name.

Flyway källskript:

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');

Lägg till skript surname.

Uppmärksamhet. Kom ihåg att du INTE KAN LÄGG TILL NÅGRA NOT NULL-begränsningar i kolumnen du lägger till. Om du återställer JAR kommer den gamla versionen inte att ha någon aning om den tillagda kolumnen och kommer automatiskt att ställa in den på NULL. Om det finns en sådan begränsning kommer den gamla applikationen helt enkelt att gå sönder.

-- 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

Kodändringar

Vi lagrar data som last_name, och i surname. Samtidigt läser vi från last_name, eftersom den här kolumnen är den mest relevanta. Under distributionsprocessen kan vissa förfrågningar ha behandlats av en applikationsinstans 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 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 efternamn från koden

Appversion: 3.0.0

DB version:v3

Kommentar

Notera per.: Tydligen, i den ursprungliga artikeln kopierade författaren av misstag texten i detta block från steg 2. I detta steg bör ändringar göras i applikationskoden som syftar till 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 databasändringar. Dessutom, om vi återställer JAR eller har en gammal JAR igång, kommer den inte att gå sönder under körningen.

Återställning av ansökan

För närvarande har vi appversion 3.0.0 och databas v3. Version 3.0.0 sparar inte data till last_name. Det betyder att i surname den mest uppdaterade informationen lagras.

steg:

  1. rulla tillbaka din applikation till version 2.0.0.
  2. version 2.0.0 använder och last_name и surname.
  3. version 2.0.0 ska ta surname, om det inte är noll, annars -last_name

Databasförändringar

Det finns inga strukturella förändringar i databasen. Följande skript körs för att utföra den slutliga migreringen av gamla data:

-- 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;

Kodändringar

Notera per.: Beskrivningen av detta block kopierades också av misstag av författaren från steg 2. I enlighet med artikelns logik bör ändringar i koden i detta steg syfta till att ta bort element från den som fungerar med kolumnen last_name.

Vi lagrar data som last_name, och i surname. Dessutom läser vi från spalten last_name, eftersom det är det mest relevanta. Under distributionsprocessen kan vissa förfrågningar behandlas av en instans som ännu inte har uppgraderats.

/*
 * 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 efternamn från databasen

Appversion: 4.0.0

DB version: v4

Kommentar

På grund av det faktum att versionskoden 3.0.0 använde inte kolumnen last_name, inget dåligt kommer att hända under utförandet om vi rullar tillbaka till 3.0.0 efter att ha tagit bort en kolumn från databasen.

Skriptkörningsloggar

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"}

DB ändringar

om v3 vi tar bara bort kolumnen last_name och lägg till begränsningar som saknas.

-- 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;

Kodändringar

Det finns inga ändringar i koden.

Utgång

Vi har tillämpat en bakåtinkompatibel kolumnnamnsändring genom att utföra flera bakåtkompatibla distributioner. Nedan följer en sammanfattning av de åtgärder som utförts:

  1. distribution av applikationsversion 1.0.0 с v1 databasschema (kolumnnamn = last_name)
  2. distribution av applikationsversion 2.0.0, som lagrar data i last_name и surname. Ansökan läser från last_name. Databasen finns i version v2som innehåller kolumner som last_name, och surname. surname är en kopia av last_name. (OBS: Denna kolumn får inte ha en noll-begränsning)
  3. distribution av applikationsversion 3.0.0, som endast lagrar data i surname och läser från efternamn. När det gäller databasen pågår den senaste migreringen last_name в surname. Också en begränsning INTE NULL borttagen från last_name. Databasen är nu i version v3
  4. distribution av applikationsversion 4.0.0 - inga ändringar görs i koden. Databasdistribution v4, som tar bort last_name. Här kan du lägga till eventuella saknade begränsningar till databasen.

Genom att följa detta tillvägagångssätt kan du alltid återställa en version utan att bryta databas-/applikationskompatibiliteten.

Kod

All kod som används i den här artikeln är tillgänglig på Github. Nedan finns ytterligare beskrivning.

Projekt

Efter att ha klonat förvaret kommer du att se följande mappstruktur.

├── 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)

Manus

Du kan köra skripten som beskrivs i skripten nedan, som kommer att visa bakåtkompatibla och inkompatibla ändringar i databasen.

Att se fallet med bakåtkompatibla ändringar, springa:

./scripts/scenario_backward_compatible.sh

Och att se fall med bakåt inkompatibla ändringar, springa:

./scripts/scenario_backward_incompatible.sh

Spring Boot Sample Flyway

Alla exempel är hämtade från Spring Boot Sample Flyway.

Du kan ta en titt på http://localhost:8080/flyway, det finns en lista med skript.

Detta exempel inkluderar även H2-konsolen (at http://localhost:8080/h2-console) så att du kan se databasstatus (standard jdbc URL är jdbc:h2:mem:testdb).

dessutom

Läs även andra artiklar på vår blogg:

Källa: will.com

Lägg en kommentar