Nul nedetidsimplementering og databaser

Nul nedetidsimplementering og databaser

Denne artikel forklarer i detaljer, hvordan du løser problemer med databasekompatibilitet under implementering. Vi vil fortælle dig, hvad der kan ske med dine produktionsapplikationer, hvis du forsøger at implementere uden forudgående forberedelse. Vi gennemgår derefter applikationens livscyklusstadier, der kræves for at have nul nedetid (ca. vognbane: længere - nul nedetid). Resultatet af vores operationer vil være at anvende den bagud-inkompatible databaseændring på en bagudkompatibel måde.

Hvis du vil forstå kodeeksemplerne fra artiklen, kan du finde dem på GitHub.

Indledning

Nul nedetid implementering

Hvilken mystisk nul nedetid implementering? Du kan sige, at det er, når din applikation er implementeret på en sådan måde, at du med succes kan introducere en ny version af applikationen til produktion, mens brugeren ikke bemærker dens utilgængelighed. Fra et bruger- og virksomhedsperspektiv er dette det bedst mulige implementeringsscenarie, fordi det giver mulighed for at introducere nye funktioner og rette fejl uden afbrydelser.

Hvordan opnår man dette? Der er flere måder, her er en af ​​dem:

  • implementer version nr. 1 af din tjeneste
  • udføre en databasemigrering
  • Implementer version #2 af din tjeneste parallelt med version #1
  • så snart du ser, at version nr. 2 fungerer som den skal, fjern version nr. 1
  • parat!

Nemt, er det ikke? Desværre er det ikke så enkelt, og det vil vi se nærmere på senere. Lad os nu tjekke en anden ret almindelig implementeringsproces - blågrøn implementering.

Har du nogensinde hørt om blågrøn indsættelse? Cloud Foundry gør dette ekstremt nemt. Bare se på denne artikel, hvor vi beskriver dette nærmere. For kort at opsummere, lad os minde dig om, hvordan du udfører blågrøn implementering:

  • sikre, at to kopier af din produktionskode ("blå" og "grøn") virker;
  • lede al trafik til det blå miljø, dvs. så produktions-URL'er peger derhen;
  • implementere og teste alle applikationsændringer i et grønt miljø;
  • skifte url fra blåt til grønt miljø

Blågrøn implementering er en tilgang, der giver dig mulighed for nemt at introducere nye funktioner uden at bekymre dig om, at produktionen går i stykker. Dette skyldes det faktum, at selvom der sker noget, kan du nemt rulle tilbage til det tidligere miljø ved blot at "svirpe på en kontakt."

Efter at have læst alt ovenstående kan du stille spørgsmålet: Hvad har nul nedetid at gøre med Blue Green-implementering?

Nå, de har ret meget til fælles, da det kræver dobbelt indsats at vedligeholde dem at vedligeholde to kopier af det samme miljø. Det er derfor nogle hold hævder Martin Fowler, følg en variation af denne tilgang:

En anden mulighed er at bruge den samme database og skabe blå-grønne switche til web- og domænelagene. I denne tilgang kan databasen ofte være et problem, især når du skal ændre dens skema for at understøtte en ny version af softwaren.

Og her kommer vi til hovedproblemet i denne artikel. database. Lad os tage et nyt kig på denne sætning.

udføre en databasemigrering.

Nu skal du stille dig selv spørgsmålet - hvad nu hvis databaseændringen ikke er bagudkompatibel? Vil min første version af appen ikke gå i stykker? Faktisk er det præcis, hvad der vil ske...

Så selv på trods af de enorme fordele ved nul nedetid / blågrøn implementering, har virksomheder en tendens til at følge følgende sikrere proces til implementering af deres applikationer:

  • forberede en pakke med en ny version af applikationen
  • lukke et kørende program
  • køre scripts for at migrere databasen
  • implementere og starte en ny version af applikationen

I denne artikel beskriver vi, hvordan du kan arbejde med din database og kode for at drage fordel af nul nedetidsimplementering.

Database problemer

Hvis du har et statsløst program, der ikke gemmer nogen data i databasen, kan du få nul nedetidsimplementering med det samme. Desværre skal det meste software gemme data et eller andet sted. Det er derfor, du bør tænke dig om to gange, før du foretager ændringer i kredsløbet. Før vi kommer ind på detaljerne i, hvordan man ændrer skemaet, så implementering uden nedetid er mulig, lad os først fokusere på versionsskemaet.

Versioneringsordning

I denne artikel vil vi bruge Flyvej som et versionskontrolværktøj (ca. Oversættelse: vi taler om databasemigreringer). Vi vil naturligvis også skrive en Spring Boot-applikation, der har indbygget Flyway-understøttelse og vil udføre skemamigrering, mens applikationskonteksten opsættes. Når du bruger Flyway, kan du gemme migreringsscripts i din projektmappe (som standard i classpath:db/migration). Her kan du se et eksempel på sådanne migreringsfiler

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

I dette eksempel ser vi 4 migreringsscripts, der, hvis de ikke er udført tidligere, vil blive udført efter hinanden, når applikationen starter. Lad os se på en af ​​filerne (V1__init.sql) som et eksempel.

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

Alt er fuldstændig selvforklarende: du kan bruge SQL til at definere, hvordan din database skal ændres. For mere information om Spring Boot og Flyway, tjek ud Spring Boot Docs.

Ved at bruge et kildekontrolværktøj med Spring Boot får du 2 store fordele:

  • du adskiller databaseændringer fra kodeændringer
  • Databasemigration sker sammen med udrulningen af ​​din applikation, dvs. din implementeringsproces er forenklet

Fejlfinding af databaseproblemer

I det næste afsnit af artiklen vil vi fokusere på at se på to tilgange til databaseændringer.

  • bagudrettet inkompatibilitet
  • bagudkompatibilitet

Den første vil blive betragtet som en advarsel om, at du ikke bør udføre nul nedetid implementering uden forudgående forberedelse... Den anden tilbyder en løsning på, hvordan du kan udføre en implementering uden nedetid og samtidig bevare bagudkompatibilitet.

Vores projekt, vi vil arbejde på, vil være en simpel Spring Boot Flyway-applikation, der har Person с first_name и last_name i databasen (ca. oversættelse: Person er et bord og first_name и last_name - det er felterne i den). Vi ønsker at omdøbe last_name в surname.

Forudsætninger

Før vi kommer ind i detaljerne, er der et par antagelser, vi skal gøre os om vores applikationer. Hovedresultatet, vi ønsker at opnå, vil være en ret simpel proces.

Sedlen. Business PRO-TIP. Forenkling af processer kan spare dig for mange penge på support (jo flere mennesker du har, der arbejder for din virksomhed, jo flere penge kan du spare)!

Ingen grund til at rulle databasen tilbage

Dette forenkler implementeringsprocessen (nogle databaserulninger er næsten umulige, f.eks. sletningsrulning). Vi foretrækker kun at rulle applikationer tilbage. På denne måde vil din implementeringspipeline se ens ud, selvom du har forskellige databaser (f.eks. SQL og NoSQL).

Det skal ALTID være muligt at rulle applikationen én version tilbage (ikke mere)

Tilbageføring bør kun ske, når det er nødvendigt. Hvis der er en fejl i den nuværende version, som ikke er let at rette, bør vi være i stand til at vende tilbage til den seneste fungerende version. Vi antager, at denne seneste arbejdsversion er den forrige. At opretholde kode- og databasekompatibilitet for mere end én udrulning ville være ekstremt vanskeligt og dyrt.

Noten. For større læsbarhed vil vi i denne artikel ændre hovedversionen af ​​applikationen.

Trin 1: Udgangstilstand

Appversion: 1.0.0
DB version: v1

Kommentar

Dette vil være den oprindelige tilstand af ansøgningen.

Databaseændringer

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

Kodeændringer

Applikationen gemmer persondata 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
                + "]";
    }
}

Baglæns inkompatibel kolonneomdøbning

Lad os se på et eksempel på, hvordan man ændrer et kolonnenavn:

Opmærksomhed. Det følgende eksempel vil med vilje bryde tingene. Vi viser dette for at demonstrere problemet med databasekompatibilitet.

Appversion: 2.0.0.BAD

DB version: v2bad

Kommentar

De nuværende ændringer tillader IKKE at køre to instanser (gamle og nye) på samme tid. Nul nedetid vil således være vanskelig at opnå (hvis der tages hensyn til antagelser, er det faktisk umuligt).

A/B test

Den nuværende situation er, at vi har en applikationsversion 1.0.0, implementeret i produktion og database v1. Vi er nødt til at implementere en anden instans af applikationen, version 2.0.0.BAD, og opdater databasen til v2bad.

Trin:

  1. en ny forekomst af versionsapplikationen implementeres 2.0.0.BADsom opdaterer databasen til v2bad
  2. i databasen v2bad kolonne last_name eksisterer ikke længere - det blev ændret til surname
  3. Opdateringen af ​​databasen og applikationen lykkedes, og nogle forekomster kører 1.0.0, andre - i 2.0.0.BAD. Alt er forbundet med databasen v2bad
  4. alle forekomster af versionen 1.0.0 vil begynde at smide fejl, fordi de vil forsøge at indsætte data i kolonnen last_namesom ikke længere eksisterer
  5. alle forekomster af versionen 2.0.0.BAD vil fungere uden problemer

Som du kan se, er A/B-test umuligt, hvis vi laver bagud-inkompatible ændringer af databasen og applikationen.

Ansøgning tilbagerulning

Lad os antage, at efter at have forsøgt at udføre A/B-implementering (ca. pr.: forfatteren mente nok A/B test her) besluttede vi, at vi skulle rulle applikationen tilbage til versionen 1.0.0. Lad os sige, at vi ikke ønsker at rulle databasen tilbage.

Trin:

  1. vi stopper versionsapplikationsinstansen 2.0.0.BAD
  2. databasen er stadig v2bad
  3. siden versionen 1.0.0 forstår ikke hvad det er surname, vil vi se fejl
  4. helvede er brudt løs, vi kan ikke gå tilbage mere

Som du kan se, kan vi ikke rulle tilbage til den tidligere version, hvis vi foretager bagud-inkompatible ændringer af databasen og applikationen.

Scriptudførelseslogfiler

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

Databaseændringer

Migrationsscript, der omdøber last_name в surname

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

Script, der omdøber last_name.

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

Kodeændringer

Vi har ændret feltnavnet lastNamesurname.

Omdøbning af en kolonne på en bagudkompatibel måde

Dette er den mest almindelige situation, vi kan støde på. Vi er nødt til at lave bagud uforenelige ændringer. Vi har allerede bevist, at for nul-downtime-implementering bør vi ikke blot anvende databasemigrering uden yderligere trin. I dette afsnit af artiklen vil vi udføre 3 udrulninger af applikationen sammen med databasemigreringer for at opnå det ønskede resultat og samtidig bevare bagudkompatibilitet.

Sedlen. Husk, at vi har en versionsdatabase v1. Den indeholder kolonner first_name и last_name. Vi er nødt til at ændre os last_namesurname. Vi har også appversion 1.0.0, som endnu ikke er brugt surname.

Trin 2: Tilføj efternavn

Appversion: 2.0.0
DB version: v2

Kommentar

Ved at tilføje en ny kolonne og kopiere dens indhold, skaber vi bagudkompatible databaseændringer. På samme tid, hvis vi ruller JAR tilbage eller har en gammel JAR kørende, går den ikke i stykker under udførelsen.

Vi udruller en ny version

Trin:

  1. udføre en databasemigrering for at oprette en ny kolonne surname. Nu din DB-version v2
  2. kopiere data fra last_name в surname. Bemærk venligstat hvis du har mange af disse data, bør du overveje batchmigrering!
  3. skriv koden, hvor de bruges BEGGE и nyeOg den gamle kolonne. Nu din app-version 2.0.0
  4. aflæs værdien fra kolonnen surname, hvis det ikke er null, eller fra last_namehvis surname ikke specificeret. Du kan slette getLastName() fra koden, da den vil udlæse null når du ruller din ansøgning tilbage fra 3.0.0 til 2.0.0.

Hvis du bruger Spring Boot Flyway, udføres disse to trin under versionsstart 2.0.0 applikationer. Hvis du kører databaseversioneringsværktøjet manuelt, skal du gøre to forskellige ting for at gøre dette (først opdatere db-versionen manuelt og derefter implementere den nye applikation).

Det er vigtigt. Husk at den nyoprettede kolonne BURDE IKKE være IKKE NULL. Hvis du laver en tilbagerulning, kender den gamle applikation ikke til den nye kolonne og installerer den ikke under Insert. Men hvis du tilføjer denne begrænsning, og din db bliver v2, vil dette kræve indstilling af værdien af ​​den nye kolonne. Hvilket vil føre til overtrædelser af restriktioner.

Det er vigtigt. Du bør fjerne metoden getLastName(), fordi i versionen 3.0.0 Der er intet begreb om en kolonne i koden last_name. Det betyder, at null vil blive sat der. Du kan forlade metoden og tilføje checks for null, men en meget bedre løsning ville være at sørge for det i logikken getSurname() du har valgt den korrekte værdi, der ikke er nul.

A/B test

Den nuværende situation er, at vi har en applikationsversion 1.0.0, implementeret på produktion, og databasen i v1. Vi skal implementere en anden instans af versionsapplikationen 2.0.0som vil opdatere databasen til v2.

Trin:

  1. en ny forekomst af versionsapplikationen implementeres 2.0.0som opdaterer databasen til v2
  2. i mellemtiden blev nogle anmodninger behandlet af versionsforekomster 1.0.0
  3. opdateringen lykkedes, og du har flere kørende forekomster af versionsapplikationen 1.0.0 og andre versioner 2.0.0. Alle kommunikerer med databasen i v2
  4. udgave 1.0.0 bruger ikke efternavnskolonnen i databasen, men versionen 2.0.0 bruger. De forstyrrer ikke hinanden, og der bør ikke være nogen fejl.
  5. udgave 2.0.0 gemmer data i både den gamle og nye kolonne, hvilket sikrer bagudkompatibilitet

Det er vigtigt. Hvis du har nogen forespørgsler, der tæller elementer baseret på værdier fra den gamle/nye kolonne, skal du huske, at du nu har duplikerede værdier (mest sandsynligt migrerer de stadig). For eksempel, hvis du vil tælle antallet af brugere, hvis efternavn (uanset hvilken kolonnen hedder) begyndte med bogstavet A, indtil datamigreringen er fuldført (oldnew kolonne) kan du have inkonsistente data, hvis du forespørger efter en ny kolonne.

Ansøgning tilbagerulning

Nu har vi appversion 2.0.0 og database i v2.

Trin:

  1. rulle tilbage din applikation til version 1.0.0.
  2. udgave 1.0.0 bruger ikke en kolonne i databasen surname, så tilbagerulningen burde være vellykket

DB ændringer

Databasen indeholder en kolonne med navnet last_name.

Flyway-kildescript:

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

Tilføj script surname.

Opmærksomhed. Husk, at du IKKE KAN TILFØJE NOGEN NOT NULL-begrænsninger til den kolonne, du tilføjer. Hvis du ruller JAR tilbage, vil den gamle version ikke have nogen idé om den tilføjede kolonne og vil automatisk sætte den til NULL. Hvis der er en sådan begrænsning, vil den gamle applikation simpelthen gå i stykker.

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

Kodeændringer

Vi gemmer data som last_name, og i surname. Samtidig læser vi fra last_name, da denne kolonne er den mest relevante. Under implementeringsprocessen kan nogle anmodninger være blevet behandlet af en applikationsforekomst, der endnu ikke er blevet opdateret.

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

Trin 3: Fjernelse af efternavn fra koden

Appversion: 3.0.0

DB version:v3

Kommentar

Bemærk pr.: Tilsyneladende har forfatteren i den originale artikel fejlagtigt kopieret teksten til denne blok fra trin 2. På dette trin skal der foretages ændringer i applikationskoden med det formål at fjerne den funktionalitet, der bruger kolonnen last_name.

Ved at tilføje en ny kolonne og kopiere dens indhold skabte vi bagudkompatible databaseændringer. Desuden, hvis vi ruller JAR tilbage eller har en gammel JAR kørende, går den ikke i stykker under udførelsen.

Ansøgning tilbagerulning

I øjeblikket har vi appversion 3.0.0 og database v3. Version 3.0.0 gemmer ikke data til last_name. Det betyder, at i surname de mest opdaterede oplysninger gemmes.

Trin:

  1. rulle tilbage din applikation til version 2.0.0.
  2. udgave 2.0.0 bruger og last_name и surname.
  3. udgave 2.0.0 vil tage surname, hvis det ikke er nul, ellers -last_name

Databaseændringer

Der er ingen strukturelle ændringer i databasen. Følgende script udføres for at udføre den endelige migrering af de gamle 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;

Kodeændringer

Bemærk pr.: Beskrivelsen af ​​denne blok blev også fejlagtigt kopieret af forfatteren fra trin 2. I overensstemmelse med artiklens logik bør ændringer i koden på dette trin have til formål at fjerne elementer fra den, der fungerer med kolonnen last_name.

Vi gemmer data som last_name, og i surname. Derudover læser vi fra klummen last_name, da det er det mest relevante. Under implementeringsprocessen kan nogle anmodninger blive behandlet af en instans, der endnu ikke er blevet opgraderet.

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

Trin 4: Fjernelse af efternavn fra databasen

Appversion: 4.0.0

DB version: v4

Kommentar

På grund af det faktum, at versionskoden 3.0.0 brugte ikke kolonnen last_name, vil der ikke ske noget dårligt under udførelsen, hvis vi ruller tilbage til 3.0.0 efter at have fjernet en kolonne fra databasen.

Scriptudførelseslogfiler

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

cirka v3 vi fjerner bare kolonnen last_name og tilføje manglende begrænsninger.

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

Kodeændringer

Der er ingen ændringer i koden.

Output

Vi har gennemført en bagud-inkompatibel kolonnenavnsændring ved at udføre flere bagudkompatible implementeringer. Nedenfor er en oversigt over de udførte handlinger:

  1. udrulning af applikationsversion 1.0.0 с v1 databaseskema (kolonnenavn = last_name)
  2. udrulning af applikationsversion 2.0.0, som gemmer data i last_name и surname. Ansøgningen læser fra last_name. Databasen er i version v2indeholdende kolonner som last_nameOg surname. surname er en kopi af last_name. (BEMÆRK: Denne kolonne må ikke have en ikke null-begrænsning)
  3. udrulning af applikationsversion 3.0.0, som kun gemmer data i surname og læser fra efternavn. Hvad angår databasen, finder den sidste migrering sted last_name в surname. Også en begrænsning IKKE NULL fjernet fra last_name. Databasen er nu i version v3
  4. udrulning af applikationsversion 4.0.0 - der er ikke lavet ændringer i koden. Database udrulning v4, som fjerner last_name. Her kan du tilføje eventuelle manglende begrænsninger til databasen.

Ved at følge denne tilgang kan du altid rulle én version tilbage uden at bryde database-/applikationskompatibiliteten.

Kode

Al kode brugt i denne artikel er tilgængelig på Github. Nedenfor er yderligere beskrivelse.

projekter

Efter kloning af depotet, vil du se følgende mappestruktur.

├── boot-flyway-v1              - 1.0.0 version of the app with v1 of the schema
├── boot-flyway-v2              - 2.0.0 version of the app with v2 of the schema (backward-compatible - app can be rolled back)
├── boot-flyway-v2-bad          - 2.0.0.BAD version of the app with v2bad of the schema (backward-incompatible - app cannot be rolled back)
├── boot-flyway-v3              - 3.0.0 version of the app with v3 of the schema (app can be rolled back)
└── boot-flyway-v4              - 4.0.0 version of the app with v4 of the schema (app can be rolled back)

Scripts

Du kan køre scripts beskrevet i scripts nedenfor, som vil demonstrere bagudkompatible og inkompatible ændringer af databasen.

At se sagen med bagudkompatible ændringer, løb:

./scripts/scenario_backward_compatible.sh

Og at se tilfælde med bagud uforenelige ændringer, løb:

./scripts/scenario_backward_incompatible.sh

Spring Boot Sample Flyway

Alle eksempler er taget fra Spring Boot Sample Flyway.

Du kan tage et kig på http://localhost:8080/flyway, er der en liste over scripts.

Dette eksempel inkluderer også H2-konsollen (kl http://localhost:8080/h2-console), så du kan se databasestatus (standard jdbc URL er jdbc:h2:mem:testdb).

derudover

Læs også andre artikler på vores blog:

Kilde: www.habr.com

Tilføj en kommentar