Zero Downtime Deployment en databases

Zero Downtime Deployment en databases

In dit artikel wordt uitgebreid uitgelegd hoe je problemen met de compatibiliteit van databases bij de deployment kunt oplossen. We bespreken wat er kan gebeuren met je productie-applicaties als je probeert een deployment uit te voeren zonder voorbereiding. Vervolgens bespreken we de fasen van de levenscyclus van een applicatie die nodig zijn voor een zero downtime.opmerking van de vertaler: hierna - zero downtime. Het resultaat van onze handelingen zal zijn dat we een achterwaarts incompatibele databasewijziging op een compatibele manier toepassen.

Als je wilt werken met de codevoorbeelden uit het artikel, vind je ze op GitHub.

Inleiding

Zero downtime deployment

Wat is die mysterieuze zero downtime deployment? Можно сказать, это когда ваше приложение развернуто так, что вы можете успешно вводить новую версию приложения на продакшн, в то время как пользователь не замечает его недоступности. С точки зрения пользователя и компании, это наилучший из возможных сценариев деплоя, поскольку таким образом можно вводить новые функции и устранять ошибки без перебоев в работе.

Hoe bereik je dit? Er zijn verschillende manieren, hier is er een:

  • deploy versie №1 van je service
  • voer de database-migratie uit
  • deploy versie №2 van je service parallel aan versie №1
  • zodra je ziet dat versie №2 correct werkt, verwijder je versie №1
  • klaar!

Gemakkelijk, niet waar? Helaas is het niet zo eenvoudig, en we zullen dit later in detail bespreken. Laten we nu kijken naar een andere vrij voorkomende deployment proces - blue green deployment.

Heb je ooit gehoord van blue green deployment? С Cloud Foundry это чрезвычайно легко сделать. Просто гляньте на dit artikel, waar we dit verder in detail beschrijven. Kort samengevat, laten we herinneren hoe je blue green deployment moet uitvoeren:

  • zorg voor twee kopieën van je productiecode (“blue” en “green”);
  • stuur al het verkeer naar de blue omgeving, dat wil zeggen, de URL's van de productie wijzen daarheen;
  • deploy en test alle wijzigingen van de applicatie in de green omgeving;
  • schakel de URL's om van de blue naar de green omgeving

Blue green deployment is een benadering die je in staat stelt om eenvoudig nieuwe functies in te voeren zonder je zorgen te maken dat de productie stuk gaat. Dit komt doordat, zelfs als er iets misgaat, je gemakkelijk kunt terugschakelen naar de vorige omgeving door eenvoudigweg de 'schakelaar om te zetten'.

Na het lezen van bovenstaande, kun je jezelf de vraag stellen: Wat heeft zero downtime met blue green deployment te maken?

Nou, ze hebben veel gemeen, omdat het ondersteunen van twee kopieën van dezelfde omgeving dubbele inspanning vereist voor hun onderhoud. Daarom houden sommige teams, zoals Martin Fowler, zich aan een variatie van deze aanpak:

Een andere optie is het gebruik van dezelfde database, waarbij blue-green switches voor de web- en domainlagen worden gemaakt. Bij deze aanpak kunnen databases vaak problematisch zijn, vooral wanneer u de structuur ervan moet wijzigen om een nieuwe versie van de software te ondersteunen.

En hier komen we bij het belangrijkste probleem in dit artikel. Database. Laten we nog eens naar deze zin kijken.

Voer de database-migratie uit.

Nu moet u zichzelf de vraag stellen: wat als de wijziging van de database niet meer compatibel is? Zal mijn eerste versie van de applicatie dan niet kapot gaan? In feite zal dit precies gebeuren...

Dus, ondanks de enorme voordelen van zero downtime / blue-green deployment, zijn bedrijven geneigd om de volgende veiligere proces van het implementeren van hun applicaties te volgen:

  • Bereid het pakket met de nieuwe versie van de applicatie voor.
  • Schakel de actieve applicatie uit.
  • Voer de scripts voor de database-migratie uit.
  • Implementeer en start de nieuwe versie van de applicatie.

In dit artikel zullen we in detail beschrijven hoe u met de database en de code kunt werken om te profiteren van zero downtime deployment.

Databaseproblemen

Als u een stateless applicatie heeft die geen gegevens in de database opslaat, kunt u direct zero downtime deployment bereiken. Helaas moet de meeste software ergens gegevens opslaan. Daarom moet u goed nadenken voordat u wijzigingen in de structuur aanbrengt. Voordat we ingaan op de details van hoe de structuur zodanig te wijzigen dat deployment zonder downtime mogelijk wordt, laten we ons eerst concentreren op versiebeheer.

Versiebeheer

In dit artikel zullen we gebruik maken van Flyway als hulpmiddel voor versiebeheer (opmerking van de vertaler: dit gaat over database-migraties). Uiteraard zullen we ook een Spring Boot-applicatie schrijven die ingebouwde ondersteuning biedt voor Flyway en de structuur zal migreren tijdens het instellen van de applicatiecontext. Bij het gebruik van Flyway kunt u migratiescripts opslaan in de map van uw projecten (standaard in classpath:db/migration). Hier ziet u een voorbeeld van dergelijke migratiebestanden

└── db
 └── migration
     ├── V1__init.sql
     ├── V2__Add_surname.sql
     ├── V3__Final_migration.sql
     └── V4__Remove_lastname.sql

In dit voorbeeld zien we 4 migratiescenario's die, indien ze nog niet eerder zijn uitgevoerd, één voor één worden uitgevoerd bij het opstarten van de applicatie. Laten we een van de bestanden (V1__init.sql) beschouwen als voorbeeld.

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

Alles spreekt voor zich: je kunt SQL gebruiken om te bepalen hoe je database moet worden gewijzigd. Voor meer informatie over Spring Boot en Flyway kun je kijken naar de Spring Boot Docs.

Met de versiebeheer tool in combinatie met Spring Boot krijg je 2 grote voordelen:

  • je scheidt databasewijzigingen van codewijzigingen
  • de migratie van de database gebeurt samen met de uitrol van je applicatie, d.w.z. je implementatieproces wordt vereenvoudigd

Probleemoplossing met de database

In de volgende sectie van het artikel zullen we ons richten op twee benaderingen van databasewijzigingen.

  • terugwaartse incompatibiliteit
  • voorwaartse compatibiliteit

De eerste wordt besproken als waarschuwing dat je geen zero downtime deployment moet uitvoeren zonder voorbereid te zijn... De tweede biedt een oplossing voor hoe je kunt implementeren zonder onderbrekingen en tegelijkertijd voorwaartse compatibiliteit kunt behouden.

Ons project, waaraan we gaan werken, zal een eenvoudige Spring Boot Flyway-applicatie zijn, waarin Person met first_name en last_name in de database (opmerking van de vertaler: Person is een tabel, en first_name en last_name is een veld daarin). We willen het hernoemen naar last_name in achternaam.

Aannames

Voordat we in de details duiken, moeten we een paar aannames over onze applicaties vaststellen. Het belangrijkste doel dat we willen bereiken, zal een vrij eenvoudig proces zijn.

Opmerking. Business PRO-TIP. Het vereenvoudigen van processen kan je veel geld besparen op ondersteuning (hoe meer mensen er in je bedrijf werken, hoe meer geld je kunt besparen)!

Dat je geen database-rollback hoeft uit te voeren

Dit vereenvoudigt het implementatieproces (sommige database-rollback is praktisch onmogelijk, bijvoorbeeld rollback van een verwijdering). We geven de voorkeur aan het terugdraaien van alleen applicaties. Op deze manier, zelfs als je verschillende databases hebt (bijvoorbeeld SQL en NoSQL), zal je implementatiepipeline er identiek uitzien.

Het moet ALTIJD mogelijk zijn om de applicatie één versie terug te draaien (niet meer)

Een rollback moet alleen worden uitgevoerd indien nodig. Als er een fout is in de huidige versie die moeilijk te verhelpen is, moeten we in staat zijn om de laatste werkende versie terug te zetten. We gaan ervan uit dat deze laatste werkende versie de vorige is. Ondersteuning voor code- en databasecompatibiliteit voor meer dan één release zou extreem moeilijk en kostbaar zijn.

Notitie. Voor een betere leesbaarheid zullen we in dit artikel de hoofdversie van de applicatie aanpassen.

Stap 1: Initiële staat

Applicatieversie: 1.0.0
DB-versie: v1

Opmerking

Dit zal de initiële staat van de applicatie zijn.

Databasewijzigingen

De database bevat achternaam.

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

Codewijzigingen

De applicatie slaat de gegevens van Person op in 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
                + "]";
    }
}

Inkompatibele hernoeming van de kolom

Laten we een voorbeeld bekijken van hoe een kolomnaam te wijzigen:

Let op. Het volgende voorbeeld zal opzettelijk leiden tot een fout. We tonen dit om het probleem van databasecompatibiliteit te demonstreren.

Applicatieversie: 2.0.0.BAD

DB-versie: v2bad

Opmerking

De huidige wijzigingen staan ons niet toe om twee exemplaren (oud en nieuw) tegelijk te draaien. Dus zal zero downtime deployment moeilijk te bereiken zijn (rekening houdend met de aannames is het feitelijk onmogelijk).

A/B-testen

De huidige situatie is dat we een applicatie versie hebben 1.0.0, uitgerold in productie, en de DB v1. We moeten een tweede exemplaar van de applicatie uitrollen, versie 2.0.0.BAD, en de database bijwerken naar v2bad.

Stappen:

  1. een nieuw exemplaar van de applicatie versie 2.0.0.BAD, dat de database bijwerkt naar v2bad
  2. in de database v2bad kolom last_name bestaat niet meer — deze is gewijzigd naar achternaam
  3. de database- en applicatie-update heeft succesvol plaatsgevonden, en sommige exemplaren draaien in 1.0.0, terwijl andere draaien in 2.0.0.BAD. Allemaal verbonden met de DB v2bad
  4. alle exemplaren van versie 1.0.0 zullen fouten beginnen te genereren, omdat ze proberen gegevens in de kolom in te voegen last_name, die niet meer bestaat
  5. alle exemplaren van versie 2.0.0.BAD zullen zonder problemen werken

Zoals je kunt zien, als we inkompatibele wijzigingen in de DB en applicatie aanbrengen, is A/B-testen onmogelijk.

Rollback van de applicatie

Laten we aannemen dat na de poging om A/B deployment uit te voeren ((opmerking van de vertaler: waarschijnlijk bedoelde de auteur hier A/B-testen) we hebben besloten dat we de applicatie moeten terugzetten naar de versie 1.0.0. Laten we aannemen dat we de database niet willen terugdraaien.

Stappen:

  1. We stoppen de applicatie-instantie versie 2.0.0.BAD
  2. de database is nog steeds v2bad
  3. aangezien de versie 1.0.0 niet begrijpt wat achternaam, zullen we fouten zien
  4. de hel is losgebarsten, we kunnen niet terug.

Zoals je kunt zien, als we achterwaarts incompatibele wijzigingen in de database en de applicatie aanbrengen, kunnen we niet terug naar de vorige versie.

Uitvoeringslogboeken van het script

Achterwaarts incompatibel scenario:

01) Voer 1.0.0 uit
02) Wacht tot de app (1.0.0) opstart
03) Genereer een persoon door POST localhost:9991/person aan te roepen voor versie 1.0.0
04) Voer 2.0.0.BAD uit
05) Wacht tot de app (2.0.0.BAD) opstart
06) Genereer een persoon door POST localhost:9991/person aan te roepen voor versie 1.0.0 <-- dit zou moeten falen
07) Genereer een persoon door POST localhost:9992/person aan te roepen voor versie 2.0.0.BAD <-- dit zou moeten slagen

App starten in versie 1.0.0
Genereer een persoon in versie 1.0.0
Verzend een post naar 127.0.0.1:9991/person. Dit is het antwoord:

{"firstName":"b73f639f-e176-4463-bf26-1135aace2f57","lastName":"b73f639f-e176-4463-bf26-1135aace2f57"}

App starten in versie 2.0.0.BAD
Genereer een persoon in versie 1.0.0
Verzend een post naar 127.0.0.1:9991/person. Dit is het antwoord:

curl: (22) De gevraagde URL gaf een fout terug: 500 Internal Server Error

Genereer een persoon in versie 2.0.0.BAD
Verzend een post naar 127.0.0.1:9995/person. Dit is het antwoord:

{"firstName":"e156be2e-06b6-4730-9c43-6e14cfcda125","surname":"e156be2e-06b6-4730-9c43-6e14cfcda125"}

Databasewijzigingen

Het migratiescript dat hernoemt last_name in achternaam

Origineel Flyway-script:

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

Het script dat hernoemt last_name.

-- Deze wijziging is achterwaarts incompatibel - je kunt geen A/B-testen doen
ALTER TABLE PERSON CHANGE last_name surname VARCHAR;

Codewijzigingen

We hebben de naam van het veld lastName en een werkende opdracht krijgen. achternaam.

Hernoemen van de kolom op een achterwaarts compatibele manier

Dit is de meest voorkomende situatie waarmee we kunnen worden geconfronteerd. We moeten achterwaarts incompatibele wijzigingen uitvoeren. We hebben al bewezen dat voor uitrol zonder downtime we de database-migratie niet zomaar moeten toepassen zonder extra stappen. In dit gedeelte van het artikel zullen we 3 uitrol van de applicatie uitvoeren samen met de database-migraties om het gewenste resultaat te bereiken en tegelijkertijd de achterwaartse compatibiliteit te behouden.

Opmerking. Laten we herinneren dat we database met versie v1. Het bevat kolommen first_name en last_name. We moeten wijzigen last_name en een werkende opdracht krijgen. achternaam. We hebben ook een applicatie van versie 1.0.0, die nog geen gebruik maakt van achternaam.

Stap 2: Toevoegen van surname

Applicatieversie: 2.0.0
DB-versie: v2

Opmerking

Door een nieuwe kolom toe te voegen en de inhoud ervan te kopiëren, creëren we achterwaarts compatibele wijzigingen in de database. Tegelijkertijd, als we de JAR terugdraaien of we een werkende oude JAR hebben, zal het niet breken tijdens de uitvoering.

We zetten een nieuwe versie uit

Stappen:

  1. voeren de database-migratie uit om de nieuwe kolom te creëren achternaam. Nu is je database versie v2
  2. kopieer data van last_name in achternaam. Let op, wat als je veel deze data hebt, moet je overwegen batchmigratie!
  3. schrijf code waarin gebruikt wordt BEIDE en met de pseudo-klasse schakelaar, en oude kolom. Nu is uw applicatie versie 2.0.0
  4. lees de waarde uit de kolom achternaam, als deze niet null, of uit last_name, als achternaam niet is opgegeven. U kunt getLastName() uit de code verwijderen, aangezien het zal resulteren in null bij het terugdraaien van uw applicatie met 3.0.0 tot 2.0.0.

Als u Spring Boot Flyway gebruikt, worden deze twee stappen uitgevoerd tijdens het opstarten van versie 2.0.0 van de applicatie. Als u de database versiebeheer tool handmatig uitvoert, moet u deze twee verschillende acties uitvoeren (eerst handmatig de versie van de db bijwerken en vervolgens de nieuwe applicatie implementeren).

Belangrijk. Vergeet niet dat de nieuw aangemaakte kolom NIET MAG te zijn NOT NULL. Als u een terugdraaiing uitvoert, weet de oude applicatie niet van de nieuwe kolom en zal deze niet instellen tijdens Insert. Maar als u deze beperking toevoegt, en uw DB is v2, dan vereist dit dat er een waarde voor de nieuwe kolom moet worden ingesteld. Dit zal leiden tot schendingen van de beperkingen.

Belangrijk. U moet de methode getLastName()verwijderen, aangezien het idee van een kolom in versie 3.0.0 in de code ontbreekt last_name. Dit betekent dat daar null-waarden zullen worden ingesteld. U kunt de methode behouden en controles toevoegen voor null, maar een veel betere oplossing zou zijn om ervoor te zorgen dat in de logica getSurname() u de juiste niet-null waarde hebt gekozen.

A/B-testen

De huidige situatie is dat we een applicatie versie hebben 1.0.0, uitgerold op productie, en de DB in v1. We moeten de tweede instantie van de applicatie versie 2.0.0, die de database zal bijwerken naar v2.

Stappen:

  1. een nieuw exemplaar van de applicatie versie 2.0.0, dat de database bijwerkt naar v2
  2. ondertussen werden enkele verzoeken afgehandeld door instanties van versie 1.0.0
  3. de update is succesvol uitgevoerd en u heeft enkele werkende instanties van de applicatie versie 1.0.0 en de andere versies 2.0.0. Communiceren allemaal met de DB in v2
  4. versie 1.0.0 gebruikt in de DB de kolom achternaam niet, maar versie 2.0.0 gebruikt. Ze storen elkaar niet, en er zouden geen fouten moeten zijn.
  5. versie 2.0.0 bewaart gegevens zowel in de oude als in de nieuwe kolom, wat zorgt voor backwards compatibility

Belangrijk. Als u vragen heeft die elementen tellen op basis van waarden uit de oude / nieuwe kolom, moet u zich herinneren dat u nu duplicaten van waarden heeft (waarschijnlijk migreren ze nog steeds). Bijvoorbeeld, als u het aantal gebruikers wilt tellen wiens achternaam (hoe de kolom ook wordt genoemd) begon met de letter A, dan kunt u inconsistenties in de gegevens hebben tot de gegevensmigratie is voltooid (oude → nieuw kolom) als u een verzoek doet aan de nieuwe kolom.

Rollback van de applicatie

Momenteel hebben we applicatie versie 2.0.0 en database in v2.

Stappen:

  1. zet uw applicatie terug naar een versie 1.0.0.
  2. versie 1.0.0 maakt geen gebruik van de kolom in de database achternaam, dus de rollback zou succesvol moeten zijn

DB-wijzigingen

De database bevat een kolom met de naam last_name.

Origineel Flyway-script:

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

Toevoegscript achternaam.

Let op. Vergeet niet dat u geen NOT NULL-beperkingen kunt toevoegen aan de toegevoegde kolom. Als u de JAR terugrolt, weet de oude versie niet van de toegevoegde kolom en zal deze automatisch NULL toekennen. Indien er een dergelijk beperking is, zal de oude applicatie eenvoudigweg breken.

-- OPLET: Dit veld kan geen NOT NULL-beperking hebben, omdat als je terugrolt, de oude versie niet zal weten over dit veld
-- en zal het altijd op NULL instellen
ALTER TABLE PERSON ADD achternaam varchar(255);

-- WE GAAN ERVANUIT DAT HET EEN SNELLE MIGRATIE IS - ANDERS ZOUDEN WE IN BATCH VAN TOEPASSEN MOETEN MIGREREN
UPDATE PERSON SET PERSON.achternaam = PERSON.achternaam;

Codewijzigingen

We slaan gegevens op in last_name, alsook in achternaam. Hierbij lezen we uit last_name, aangezien deze kolom de meest actuele is. Tijdens de implementatie kunnen sommige verzoeken zijn afgehandeld door een exemplaar van de applicatie dat nog niet was bijgewerkt.

/*
 * 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
                + "]";
    }
}

Stap 3: Verwijderen van achternaam uit de code

Applicatieversie: 3.0.0

DB-versie:v3

Opmerking

Opmerking van de vertaler: Blijkbaar heeft de auteur in het oorspronkelijke artikel deze tekst onjuist gekopieerd uit stap 2. In deze stap moeten wijzigingen in de applicatiecode worden aangebracht om functionaliteit te verwijderen die gebruik maakt van de kolom last_name.

Door een nieuwe kolom toe te voegen en de inhoud te kopiëren, hebben we back-compatibele wijzigingen in de database gecreëerd. Bovendien, als we de JAR terugrollen of we hebben een werkende oudere JAR, zal het niet breken tijdens de uitvoering.

Rollback van de applicatie

Momenteel hebben we een applicatie versie 3.0.0 en een database v3. Versie 3.0.0 slaat geen gegevens op in last_name. Dit betekent dat in achternaam de meest actuele informatie is opgeslagen.

Stappen:

  1. zet uw applicatie terug naar een versie 2.0.0.
  2. versie 2.0.0 maakt gebruik van en last_name en achternaam.
  3. versie 2.0.0 zal nemen achternaam, als het niet nul is, anders —last_name

Databasewijzigingen

Er zijn geen structurele wijzigingen in de database. De volgende script wordt uitgevoerd, die de definitieve migratie van oude gegevens uitvoert:

-- WE GAAN ERVANUIT DAT HET EEN SNELLE MIGRATIE IS - ANDERS ZOUDEN WE IN BATCH VAN TOEPASSEN MOETEN MIGREREN
-- OOK CONTROLEREN WE NIET OF WE GEEN BESTAANDE ENTRIES OVRIDEN. WE ZOUDEN DE
-- INVOERVERSIES MOETEN VERGELIJKEN OM TE ZORGEN DAT ALS ER AL EEN INVOER IS MET EEN HOGER VERSIE NUMMER
-- WE HET NIET ZULLEN OVRIDEN.
UPDATE PERSON SET PERSON.achternaam = PERSON.achternaam;

-- DE NOT NULL-BEPERKING VERWIJDEREN; ANDERS ZULT U PROBEREN NULL WAARDE VAN DE ACHTERNAAM IN TE VOEGEN
-- MET EEN NOT_NULL-BEPERKING.
ALTER TABLE PERSON MODIFY COLUMN achternaam varchar(255) NULL DEFAULT NULL;

Codewijzigingen

Opmerking: De beschrijving van deze blok is onterecht gekopieerd door de auteur uit stap 2. Volgens de logica van het verhaal in het artikel moeten de wijzigingen in de code op deze stap gericht zijn op het verwijderen van elementen die werken met de kolom. last_name.

We slaan gegevens op in last_name, alsook in achternaam. Daarnaast lezen we uit de kolom last_name, omdat het het meest relevant is. Tijdens de implementatie kunnen sommige aanvragen verwerkt worden door een instantie die nog niet is bijgewerkt.

/*
 * 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
                + "]";
    }
}

Stap 4: Verwijder last_name uit de database

Applicatieversie: 4.0.0

DB-versie: v4

Opmerking

Aangezien de codeversie 3.0.0 de kolom niet gebruikte, zal er tijdens de uitvoering niets ergs gebeuren als we terugschakelen naar last_namena het verwijderen van de kolom uit de database. 3.0.0 We zullen het op de volgende manier doen:01) Voer 1.0.0 uit 02) Wacht tot de app (1.0.0) opstart 03) Genereer een persoon door POST localhost:9991/person aan te roepen naar versie 1.0.0 04) Voer 2.0.0 uit 05) Wacht tot de app (2.0.0) opstart 06) Genereer een persoon door POST localhost:9991/person aan te roepen naar versie 1.0.0 07) Genereer een persoon door POST localhost:9992/person aan te roepen naar versie 2.0.0 08) Sluit app (1.0.0) af 09) Voer 3.0.0 uit 10) Wacht tot de app (3.0.0) opstart 11) Genereer een persoon door POST localhost:9992/person aan te roepen naar versie 2.0.0 12) Genereer een persoon door POST localhost:9993/person aan te roepen naar versie 3.0.0 13) Sluit app (3.0.0) af 14) Voer 4.0.0 uit 15) Wacht tot de app (4.0.0) opstart 16) Genereer een persoon door POST localhost:9993/person aan te roepen naar versie 3.0.0 17) Genereer een persoon door POST localhost:9994/person aan te roepen naar versie 4.0.0App starten in versie 1.0.0 Genereer een persoon in versie 1.0.0 Versturen van een post naar 127.0.0.1:9991/person. Dit is het antwoord:{"firstName":"52b6e125-4a5c-429b-a47a-ef18bbc639d2","lastName":"52b6e125-4a5c-429b-a47a-ef18bbc639d2"}App starten in versie 2.0.0Genereer een persoon in versie 1.0.0 Versturen van een post naar 127.0.0.1:9991/person. Dit is het antwoord:{"firstName":"e41ee756-4fa7-4737-b832-e28827a00deb","lastName":"e41ee756-4fa7-4737-b832-e28827a00deb"}Genereer een persoon in versie 2.0.0 Versturen van een post naar 127.0.0.1:9992/person. Dit is het antwoord:{"firstName":"0c1240f5-649a-4bc5-8aa9-cff855f3927f","lastName":"0c1240f5-649a-4bc5-8aa9-cff855f3927f","surname":"0c1240f5-649a-4bc5-8aa9-cff855f3927f"}App 1.0.0 afsluitenApp starten in versie 3.0.0Genereer een persoon in versie 2.0.0 Versturen van een post naar 127.0.0.1:9992/person. Dit is het antwoord: {"firstName":"74d84a9e-5f44-43b8-907c-148c6d26a71b","lastName":"74d84a9e-5f44-43b8-907c-148c6d26a71b","surname":"74d84a9e-5f44-43b8-907c-148c6d26a71b"}Genereer een persoon in versie 3.0.0 Versturen van een post naar 127.0.0.1:9993/person. Dit is het antwoord: {"firstName":"c6564dbe-9ab5-40ae-9077-8ae6668d5862","surname":"c6564dbe-9ab5-40ae-9077-8ae6668d5862"}App 2.0.0 afsluitenApp starten in versie 4.0.0Genereer een persoon in versie 3.0.0 Versturen van een post naar 127.0.0.1:9993/person. Dit is het antwoord:{"firstName":"cbe942fc-832e-45e9-a838-0fae25c10a51","surname":"cbe942fc-832e-45e9-a838-0fae25c10a51"}Genereer een persoon in versie 4.0.0 Versturen van een post naar 127.0.0.1:9994/person. Dit is het antwoord:{"firstName":"ff6857ce-9c41-413a-863e-358e2719bf88","surname":"ff6857ce-9c41-413a-863e-358e2719bf88"}

Uitvoeringslogboeken van het script

Wijzigingen in de database

Wat betreft

verwijderen we eenvoudig de kolom v3 en voegen we de ontbrekende beperkingen toe. last_name en voegen ontbrekende beperkingen toe.

-- VERWIJDER DE KOLONNE
ALTER TABLE PERSON DROP last_name;

-- VOEG BEPERKINGS TOE
UPDATE PERSON SET surname='' WHERE surname IS NULL;
ALTER TABLE PERSON ALTER COLUMN surname VARCHAR NOT NULL;

Codewijzigingen

Wijzigingen in de code zijn niet aanwezig.

Uitslag

We hebben met succes een niet-compatibele naamswijziging van de kolom teruggedraaid door verschillende compatibele deployments uit te voeren. Hieronder een samenvatting van de uitgevoerde acties:

  1. deployment van applicatie versie 1.0.0 met v1 database schema (kolom naam = last_name)
  2. deployment van applicatie versie 2.0.0, die gegevens opslaat in last_name en achternaam. De applicatie leest uit last_name. De database is in versie v2, die kolommen als bevat last_name, als voor surname. surname is een kopie van last_name. (OPMERKING: deze kolom mag geen not null beperking hebben)
  3. deployment van applicatie versie 3.0.0, die gegevens alleen opslaat in achternaam en leest uit surname. Wat betreft de database, de laatste migratie gebeurt last_name in achternaam. Ook wordt de beperking NOT NULL verwijderd van last_name. De database is nu in versie v3
  4. deployment van applicatie versie 4.0.0 — er worden geen wijzigingen in de code aangebracht. Deployment van de database v4, die verwijdert last_name. Hier kunt u eventuele ontbrekende beperkingen aan de database toevoegen.

Door deze aanpak te volgen, kunt u altijd één versie terugkeren zonder de compatibiliteit van de database/applicatie te breken.

Code

Alle code die in dit artikel wordt gebruikt, is beschikbaar op Github. Hieronder extra beschrijving.

Projecten

Na het clonen van de repository ziet u de volgende mappenstructuur.

├── boot-flyway-v1              - 1.0.0 versie van de app met v1 van het schema
├── boot-flyway-v2              - 2.0.0 versie van de app met v2 van het schema (backward-compatibel - app kan worden teruggedraaid)
├── boot-flyway-v2-bad          - 2.0.0.BAD versie van de app met v2bad van het schema (niet-backward-compatibel - app kan niet worden teruggedraaid)
├── boot-flyway-v3              - 3.0.0 versie van de app met v3 van het schema (app kan worden teruggedraaid)
└── boot-flyway-v4              - 4.0.0 versie van de app met v4 van het schema (app kan worden teruggedraaid)

Scripts

U kunt de scripts uitvoeren die hieronder zijn beschreven, die compatibele en niet-compatibele wijzigingen in de database demonstreren.

Om te zien geval met compatibele wijzigingen, voert u uit:

.\/scripts\/scenario_backward_compatible.sh

En om te zien geval met niet-compatibele wijzigingen, voert u uit:

.\/scripts\/scenario_backward_incompatible.sh

Spring Boot Sample Flyway

Alle voorbeelden zijn afkomstig van Spring Boot Sample Flyway.

U kunt kijken naar http://localhost:8080/flyway, daar is een lijst van scripts.

Dit voorbeeld bevat ook de H2-console (op adres http://localhost:8080/h2-console), zodat u de status van de database kunt bekijken (standaard jdbc url — jdbc:h2:mem:testdb).

Aanvullend

Lees ook andere artikelen op onze blog:

Bron: habr.com

Koop betrouwbare webhosting met bescherming tegen DDoS, VPS VDS servers 🔥 Koop betrouwbare webhosting met bescherming tegen DDoS, VPS VDS servers | ProHoster