Zero Downtime Deployment en databases

Zero Downtime Deployment en databases

Dit artikel ferklearret yn detail hoe't jo problemen mei databasekompatibiliteit kinne oplosse yn ynset. Wy sille jo fertelle wat der kin barre mei jo produksjeapplikaasjes as jo besykje te ynsetten sûnder foarriedige tarieding. Wy geane dan troch de libbenssyklusstadia fan 'e applikaasje dy't nedich binne om nul downtime te hawwen (ca. lane: fierder - nul downtime). It resultaat fan ús operaasjes sil wêze om de tebek-ynkompatibele databasewiziging op in efterút-kompatibele manier oan te passen.

As jo ​​​​de koadefoarbylden út it artikel wolle begripe, kinne jo se fine by GitHub.

Ynlieding

Nul downtime ynset

Wat in mystyk nul downtime ynset? Jo kinne sizze dat dit is as jo applikaasje is ynset op sa'n manier dat jo mei súkses kinne yntrodusearje in nije ferzje fan de applikaasje oan produksje, wylst de brûker net merkt syn ûnbeskikberens. Fanút in perspektyf fan brûker en bedriuw is dit it bêste mooglike ynsetsenario, om't it mooglik makket om nije funksjes te yntrodusearjen en bugs sûnder steuring te reparearjen.

Hoe dit te berikken? D'r binne ferskate manieren, hjir is ien fan har:

  • ynsette ferzje No.. 1 fan jo tsjinst
  • in databankmigraasje útfiere
  • Ynsette ferzje #2 fan jo tsjinst parallel mei ferzje #1
  • sa gau as jo sjogge dat ferzje No. 2 wurket sa't it moat, fuortsmite ferzje No. 1
  • dien!

Maklik, is it net? Spitigernôch is it net sa ienfâldich, en wy sille letter yn detail sjen. Litte wy no in oar frij gewoan ynsetproses kontrolearje - blaugriene ynset.

Hawwe jo oait fan heard blau griene ynset? Cloud Foundry makket dit ekstreem maklik. Sjoch mar nei dit artikel, dêr't wy beskriuwe dit yn mear detail. Om koart te gearfetten, lit ús jo herinnerje hoe't jo blau-grien-ynset dwaan kinne:

  • soargje derfoar dat twa kopyen fan jo produksjekoade ("blau" en "grien") wurkje;
  • alle ferkear rjochtsje op de blauwe omjouwing, d.w.s. sadat produksje-URL's dêre wize;
  • ynsette en testen alle applikaasje feroarings yn in griene omjouwing;
  • wikselje urls fan blau nei griene omjouwing

Blauwe griene ynset is in oanpak wêrmei jo maklik nije funksjes kinne yntrodusearje sûnder jo soargen te meitsjen oer produksjebrekken. Dit komt troch it feit dat sels as der wat bart, jo maklik werom kinne rôlje nei de foarige omjouwing troch gewoan "in skeakel te wikseljen."

Nei it lêzen fan al it boppesteande kinne jo de fraach stelle: Wat hat nul downtime te meitsjen mei Blaugriene ynset?

No, se hawwe nochal in protte mienskiplik, om't it ûnderhâld fan twa eksimplaren fan deselde omjouwing dûbele ynspanning fereasket om se te ûnderhâlden. Dit is wêrom guon teams beweare Martin Fowler, folgje in fariaasje fan dizze oanpak:

In oare opsje is om deselde database te brûken, blau-griene skeakels te meitsjen foar it web en domeinlagen. Yn dizze oanpak kin de database faaks in probleem wêze, benammen as jo it skema feroarje moatte om in nije ferzje fan 'e software te stypjen.

En hjir komme wy ta it wichtichste probleem yn dit artikel. Databank. Litte wy nochris nei dizze útdrukking sjen.

in databankmigraasje útfiere.

No moatte jo josels de fraach stelle - wat as de wiziging fan 'e database net efterút kompatibel is? Sil myn earste ferzje fan 'e app net brekke? Yn feite is dit krekt wat sil barre ...

Dat, sels nettsjinsteande de enoarme foardielen fan nul downtime / blau griene ynset, binne bedriuwen de neiging om it folgjende feiliger proses te folgjen foar it ynsetten fan har applikaasjes:

  • tariede in pakket mei in nije ferzje fan de applikaasje
  • in rinnende applikaasje ôfslute
  • skripts útfiere om de databank te migrearjen
  • ynsette en lansearje in nije ferzje fan de applikaasje

Yn dit artikel sille wy detaillearje hoe't jo kinne wurkje mei jo database en koade om foardiel te meitsjen fan nul-downtime-ynset.

Databankproblemen

As jo ​​​​in steatleaze applikaasje hawwe dy't gjin gegevens yn 'e databank opslaan, kinne jo fuortdaliks nul downtime-ynset krije. Spitigernôch moat de measte software gegevens earne opslaan. Dit is de reden dat jo twa kear moatte tinke foardat jo feroaringen meitsje oan it circuit. Foardat wy yn 'e details komme oer hoe't jo it skema kinne feroarje sadat ynset sûnder downtime mooglik is, litte wy earst rjochtsje op it ferzjeskema.

Ferzje skema

Yn dit artikel sille wy brûke Flyway as ferzjekontrôleark (ca. Oersetting: wy hawwe it oer databankmigraasjes). Natuerlik sille wy ek in Spring Boot-applikaasje skriuwe dy't ynboude Flyway-stipe hat en skemamigraasje sil útfiere by it ynstellen fan de applikaasjekontekst. By it brûken fan Flyway kinne jo migraasjeskripts opslaan yn jo projektenmap (standert yn classpath:db/migration). Hjir kinne jo in foarbyld sjen fan sokke migraasjebestannen

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

Yn dit foarbyld sjogge wy 4 migraasjeskripts dy't, as net earder útfierd, ien nei de oare sille wurde útfierd as de applikaasje begjint. Litte wy nei ien fan 'e bestannen sjen (V1__init.sql) as in foarbyld.

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 is perfekt selsferklearjend: jo kinne SQL brûke om te definiearjen hoe't jo databank wizige wurde moat. Foar mear ynformaasje oer Spring Boot en Flyway, check out Spring Boot Docs.

Troch in boarnekontrôleark te brûken mei Spring Boot, krije jo 2 grutte foardielen:

  • jo skiede databank feroarings út koade feroarings
  • Databankmigraasje bart tegearre mei de útrol fan jo applikaasje, d.w.s. jo ynsetproses wurdt ferienfâldige

Troubleshooting database problemen

Yn 'e folgjende seksje fan it artikel sille wy rjochtsje op it besjen fan twa oanpakken foar wizigingen fan databases.

  • efterút ynkompatibiliteit
  • efterút komptabiliteit

De earste sil beskôge wurde as in warskôging dat jo gjin nul-downtime-ynset sûnder foarriedige tarieding moatte útfiere ... De twadde biedt in oplossing oer hoe't jo in ynset sûnder downtime kinne útfiere en tagelyk efterútkompatibiliteit behâlde.

Us projekt dat wy sille wurkje oan sil in ienfâldige Spring Boot Flyway-applikaasje wêze dy't hat Person с first_name и last_name yn de databank (ca. oersetting: Person is in tafel en first_name и last_name - dit binne de fjilden dêryn). Wy wolle omneame last_name в surname.

Oannames

Foardat wy yn 'e details komme, binne d'r in pear oannames dy't wy moatte meitsje oer ús applikaasjes. It haadresultaat dat wy wolle berikke sil in frij ienfâldich proses wêze.

Zametka. Business PRO-TIP. It ferienfâldigjen fan prosessen kinne jo in soad jild besparje op stipe (hoe mear minsken jo hawwe wurkje foar jo bedriuw, hoe mear jild jo kinne besparje)!

Gjin needsaak om de databank werom te draaien

Dit simplifies de ynset proses (guon databank rollbacks binne hast ûnmooglik, lykas wiskjen rollback). Wy wolle leaver allinich applikaasjes weromdraaie. Op dizze manier, sels as jo ferskate databases hawwe (bygelyks SQL en NoSQL), sil jo ynsetpipeline itselde útsjen.

It moat ALTIJD mooglik wêze om de applikaasje ien ferzje werom te rôljen (net mear)

Rollback moat allinich dien wurde as it nedich is. As d'r in brek is yn 'e aktuele ferzje dy't net maklik reparearre is, moatte wy werom kinne nei de lêste wurkferzje. Wy geane derfan út dat dizze lêste wurkferzje de foarige is. It behâlden fan koade en databankkompatibiliteit foar mear dan ien útrol soe ekstreem lestich en djoer wêze.

De notysje. Foar gruttere lêsberens sille wy yn dit artikel de haadferzje fan 'e applikaasje feroarje.

Stap 1: Inisjele steat

Appferzje: 1.0.0
DB ferzje: v1

reaksje

Dit sil de begjinstân wêze fan 'e applikaasje.

Databank feroarings

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

Koade feroarings

De applikaasje bewarret Persoansgegevens yn 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
                + "]";
    }
}

Tebek ynkompatibele kolom omneaming

Litte wy nei in foarbyld sjen fan hoe't jo in kolomnamme kinne feroarje:

Oandacht. It folgjende foarbyld sil dingen mei opsetsin brekke. Wy litte dit sjen om it probleem fan databankkompatibiliteit te demonstrearjen.

Appferzje: 2.0.0.BAD

DB ferzje: v2bad

reaksje

De hjoeddeistige feroarings tastean ús NET twa eksimplaren (âld en nij) tagelyk út te fieren. Sa, nul downtime ynset sil wêze lestich te realisearjen (as útgongspunten wurde rekken holden, it is eins ûnmooglik).

A/B-testen

De hjoeddeistige situaasje is dat wy in applikaasjeferzje hawwe 1.0.0, ynset yn produksje, en databank v1. Wy moatte in twadde eksimplaar fan 'e applikaasje ynsette, ferzje 2.0.0.BAD, en bywurkje de databank nei v2bad.

Stappen:

  1. in nij eksimplaar fan de ferzje applikaasje wurdt ynset 2.0.0.BADdy't de databank bywurket nei v2bad
  2. yn de databank v2bad pylder last_name bestiet net mear - it waard feroare ta surname
  3. De bywurking fan de database en applikaasje wie suksesfol en guon eksimplaren rinne 1.0.0, oaren - yn 2.0.0.BAD. Alles is ferbûn mei de databank v2bad
  4. alle eksimplaren fan 'e ferzje 1.0.0 sil begjinne te smiten flaters omdat se sille besykje te foegjen gegevens yn de kolom last_namedy't net mear bestiet
  5. alle eksimplaren fan 'e ferzje 2.0.0.BAD sil wurkje sûnder problemen

As jo ​​​​sjogge, as wy efterút ynkompatibele feroarings meitsje oan 'e databank en applikaasje, is A / B-testen ûnmooglik.

Applikaasje weromdraaie

Litte wy oannimme dat nei it besykjen om A/B-ynset te dwaan (ca. per.: de skriuwer hat hjir wierskynlik A/B-testen bedoeld) wy besletten dat wy de applikaasje weromsette moatte nei de ferzje 1.0.0. Litte wy sizze dat wy de databank net wolle weromdraaie.

Stappen:

  1. wy stopje de ferzje applikaasje eksimplaar 2.0.0.BAD
  2. de databank is noch v2bad
  3. sûnt de ferzje 1.0.0 begrypt net wat it is surname, wy sille sjen flaters
  4. de hel is losbrutsen, wy kinne net mear werom

As jo ​​​​sjogge, as wy efterút ynkompatibele wizigingen meitsje oan 'e databank en applikaasje, kinne wy ​​​​net weromrolle nei de foarige ferzje.

Skriptútfierlogboeken

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

Databank feroarings

Migraasje skript dat omneamt last_name в surname

Boarne 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 dat omneamt last_name.

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

Koade feroarings

Wy hawwe de fjildnamme feroare lastName op surname.

In kolom omneame op in efterút-kompatibele manier

Dit is de meast foarkommende situaasje dy't wy kinne tsjinkomme. Wy moatte efterút ynkompatibele feroaringen meitsje. Wy hawwe al bewiisd dat wy foar nul-downtime-ynset net gewoan databankmigraasje moatte tapasse sûnder ekstra stappen. Yn dizze seksje fan it artikel sille wy 3-ynset fan 'e applikaasje útfiere tegearre mei databasemigraasjes om it winske resultaat te berikken mei it behâld fan efterútkompatibiliteit.

Zametka. Unthâld dat wy in ferzje databank hawwe v1. It befettet kolommen first_name и last_name. Wy moatte feroarje last_name op surname. Wy hawwe ek app ferzje 1.0.0, dy't noch net brûkt wurdt surname.

Stap 2: Foegje efternamme ta

Appferzje: 2.0.0
DB ferzje: v2

reaksje

Troch in nije kolom ta te foegjen en de ynhâld te kopiearjen, meitsje wy efterút kompatibele databankwizigingen. Tagelyk, as wy de JAR weromdraaie of in âlde JAR rinnen hawwe, sil it net brekke tidens de útfiering.

Wy rôlje in nije ferzje út

Stappen:

  1. in databankmigraasje útfiere om in nije kolom te meitsjen surname. No jo DB-ferzje v2
  2. kopiearje gegevens út last_name в surname. Wês oandachtdat as jo in protte fan dizze gegevens hawwe, moatte jo batchmigraasje beskôgje!
  3. skriuw de koade wêr't se wurde brûkt BEIDE и новыйen de âlde pylder. No jo app ferzje 2.0.0
  4. lês de wearde út 'e kolom surname, as it net is nullof fan last_name, as a surname net oantsjutte. Jo kinne wiskje getLastName() út de koade, sûnt it sil útfier null as jo jo applikaasje weromdraaie fan 3.0.0 до 2.0.0.

As jo ​​Spring Boot Flyway brûke, sille dizze twa stappen wurde útfierd by it opstarten fan ferzje 2.0.0 oanfraach. As jo ​​it ark foar databaseferzje manuell útfiere, moatte jo twa ferskillende dingen dwaan om dit te dwaan (earst de db-ferzje manuell bywurkje en dan de nije applikaasje ynsette).

It is wichtich. Unthâld dat de nij oanmakke kolom SOE NET te wêzen NET NULL. As jo ​​​​in weromdraaie dogge, wit de âlde applikaasje net oer de nije kolom en sil it net ynstalleare tidens Insert. Mar as jo dizze beheining tafoegje en jo db sil wêze v2, dit sil de wearde fan 'e nije kolom ynstelle moatte. Wat sil liede ta oertredings fan beheiningen.

It is wichtich. Jo moatte fuortsmite de metoade getLastName(), want yn 'e ferzje 3.0.0 D'r is gjin konsept fan in kolom yn 'e koade last_name. Dit betsjut dat dêr nul ynsteld wurdt. Jo kinne ferlitte de metoade en tafoegje sjeks foar null, mar in folle bettere oplossing soe wêze om te soargjen dat yn 'e logika getSurname() jo hawwe de juste wearde net-nul selektearre.

A/B-testen

De hjoeddeistige situaasje is dat wy in applikaasjeferzje hawwe 1.0.0, ynset op produksje, en de databank yn v1. Wy moatte in twadde eksimplaar fan 'e ferzjeapplikaasje ynsette 2.0.0dy't de databank bywurkje nei v2.

Stappen:

  1. in nij eksimplaar fan de ferzje applikaasje wurdt ynset 2.0.0dy't de databank bywurket nei v2
  2. yn 'e tuskentiid waarden guon oanfragen ferwurke troch ferzje-ynstânsjes 1.0.0
  3. de fernijing wie suksesfol en jo hawwe meardere rinnende eksimplaren fan 'e ferzjeapplikaasje 1.0.0 en oare ferzjes 2.0.0. Elkenien kommunisearret mei de databank yn v2
  4. ferzje 1.0.0 brûkt net de efternammekolom yn de databank, mar de ferzje 2.0.0 brûkt. Se net bemuoie mei elkoar, en der moatte gjin flaters.
  5. ferzje 2.0.0 bewarret gegevens yn sawol de âlde as nije kolom, en soarget foar efterkompatibiliteit

It is wichtich. As jo ​​​​fragen hawwe dy't items telle basearre op wearden út 'e âlde / nije kolom, moatte jo betinke dat jo no dûbele wearden hawwe (meast wierskynlik binne se noch migrearje). As jo ​​bygelyks it oantal brûkers telle wolle waans achternamme (wat de kolom ek hjit) begon mei de letter A, dan oant gegevensmigraasje foltôge is (oldnew kolom) kinne jo inkonsistente gegevens hawwe as jo in nije kolom opfreegje.

Applikaasje weromdraaie

No hawwe wy app ferzje 2.0.0 en databank yn v2.

Stappen:

  1. rôlje jo applikaasje werom nei ferzje 1.0.0.
  2. ferzje 1.0.0 brûkt gjin kolom yn 'e databank surname, sadat it weromdraaien suksesfol wêze moat

De wiziging yn' e DB

De databank befettet in kolom mei de namme last_name.

Flyway boarne 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 tafoegje surname.

Oandacht. Unthâld dat jo gjin NOT NULL-beheiningen kinne tafoegje oan 'e kolom dy't jo tafoegje. As jo ​​​​de JAR weromdraaie, sil de âlde ferzje gjin idee hawwe oer de tafoege kolom en sil it automatysk ynstelle op NULL. As d'r sa'n beheining is, sil de âlde applikaasje gewoan brekke.

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

Koade feroarings

Wy bewarje gegevens as last_nameen yn surname. Tagelyk lêze wy fan last_name, sûnt dizze kolom is de meast relevante. Tidens it ynsetproses kinne guon oanfragen wurde ferwurke troch in applikaasje-eksimplaar dy't noch net bywurke is.

/*
 * 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: Last_name fuortsmite fan 'e koade

Appferzje: 3.0.0

DB ferzje:v3

reaksje

Noat per .: Blykber, yn it orizjinele artikel de skriuwer fersin kopiearre de tekst fan dit blok út stap 2. By dizze stap moatte wizigingen wurde makke yn de applikaasje koade rjochte op it fuortheljen fan de funksjonaliteit dy't brûkt de kolom last_name.

Troch in nije kolom ta te foegjen en de ynhâld te kopiearjen, hawwe wy efterút kompatibele databasewizigingen makke. Ek as wy de JAR weromdraaie of in âlde JAR rinnen hawwe, sil it net brekke tidens de útfiering.

Applikaasje weromdraaie

Op it stuit hawwe wy app ferzje 3.0.0 en databank v3. Ferzje 3.0.0 net bewarje gegevens oan last_name. Dit betsjut dat yn surname de meast aktuele ynformaasje wurdt opslein.

Stappen:

  1. rôlje jo applikaasje werom nei ferzje 2.0.0.
  2. ferzje 2.0.0 brûkt en last_name и surname.
  3. ferzje 2.0.0 sil nimme surname, as it net nul is, oars -last_name

Databank feroarings

Der binne gjin strukturele feroarings yn de databank. It folgjende skript wurdt útfierd om de definitive migraasje fan 'e âlde gegevens út te fieren:

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

Koade feroarings

Noat per .: De beskriuwing fan dit blok waard ek ferkeard kopiearre troch de skriuwer fan stap 2. Yn oerienstimming mei de logika fan it artikel moatte feroarings yn 'e koade op dizze stap rjochte wêze op it fuortheljen fan eleminten dy't wurkje mei de kolom last_name.

Wy bewarje gegevens as last_nameen yn surname. Dêrneist lêze wy út de kollum last_name, sûnt it is de meast relevante. Tidens it ynsetproses kinne guon oanfragen wurde ferwurke troch in eksimplaar dat noch net opwurdearre is.

/*
 * 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: Last_name fuortsmite fan 'e databank

Appferzje: 4.0.0

DB ferzje: v4

reaksje

Fanwege it feit dat de ferzje koade 3.0.0 brûkte de kolom net last_name, neat min sil barre tidens útfiering as wy rôlje werom nei 3.0.0 nei it fuortsmiten fan in kolom út de databank.

Skriptútfierlogboeken

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

De wiziging yn' e DB

Betreklik v3 wy krekt fuortsmite de kolom last_name en foegje ûntbrekkende beheinings ta.

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

Koade feroarings

Der binne gjin feroarings oan de koade.

konklúzje

Wy hawwe mei súkses in efterút-ynkompatibele kolomnammeferoaring tapast troch ferskate efterút-kompatibele ynset út te fieren. Hjirûnder is in gearfetting fan de útfierde aksjes:

  1. ynset fan applikaasje ferzje 1.0.0 с v1 databaseskema (kolomnamme = last_name)
  2. ynset fan applikaasje ferzje 2.0.0, dy't gegevens bewarret yn last_name и surname. De applikaasje lêst fan last_name. De databank is yn ferzje v2befettet kolommen lykas last_nameen surname. surname is in kopy fan last_name. (OPMERKING: dizze kolom moat gjin net-nul-beheining hawwe)
  3. ynset fan applikaasje ferzje 3.0.0, dy't allinich gegevens bewarret yn surname en lêst út efternamme. Wat de databank oanbelanget, fynt de lêste migraasje plak last_name в surname. Ek in beheining NET NULL fuorthelle út last_name. De databank is no yn ferzje v3
  4. ynset fan applikaasje ferzje 4.0.0 - gjin feroarings wurde makke oan de koade. Databank ynset v4, dy't ferwideret last_name. Hjir kinne jo alle ûntbrekkende beheiningen tafoegje oan de databank.

Troch dizze oanpak te folgjen, kinne jo altyd ien ferzje weromdraaie sûnder de kompatibiliteit fan databank/applikaasje te brekken.

koade

Alle koade brûkt yn dit artikel is beskikber op Github. Hjirûnder is ekstra beskriuwing.

Projekten

Nei it klonen fan it repository sille jo de folgjende mapstruktuer sjen.

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

Skripts

Jo kinne de skripts útfiere dy't beskreaun binne yn 'e skripts hjirûnder, dy't efterútkompatibele en ynkompatibele wizigingen yn' e databank sjen litte.

Sjen it gefal mei efterút kompatibele feroarings, rinne:

./scripts/scenario_backward_compatible.sh

En te sjen gefal mei efterút ynkompatibele feroarings, rinne:

./scripts/scenario_backward_incompatible.sh

Spring Boot Sample Flyway

Alle foarbylden binne nommen út Spring Boot Sample Flyway.

Jo kinne efkes sjen op http://localhost:8080/flyway, der is in list mei skripts.

Dit foarbyld omfettet ek de H2-konsole (at http://localhost:8080/h2-console) sadat jo de databankstatus kinne besjen (standert jdbc URL is jdbc:h2:mem:testdb).

Dêrneist

Lês ek oare artikels op ús blog:

Boarne: www.habr.com

Add a comment