Nolla seisokkikäyttöä ja tietokannat

Nolla seisokkikäyttöä ja tietokannat

Tässä artikkelissa kerrotaan yksityiskohtaisesti, kuinka tietokantojen yhteensopivuusongelmat ratkaistaan ​​käyttöönoton aikana. Kerromme, mitä tuotantosovelluksille voi tapahtua, jos yrität ottaa käyttöön ilman ennakkovalmisteluja. Käymme sitten läpi sovelluksen elinkaaren vaiheet, jotka vaaditaan nollakatkosaikojen saavuttamiseksi (noin kaista: kauempana - nolla seisokkiaikaa). Toimintamme tuloksena on taaksepäin-yhteensopimattoman tietokannan muutoksen soveltaminen taaksepäin yhteensopivalla tavalla.

Jos haluat ymmärtää artikkelin koodiesimerkkejä, löydät ne osoitteesta GitHub.

Esittely

Nolla seisokkiaikaa

Mitä mystistä käyttöönotto ilman seisokkeja? Voit sanoa, että tämä on silloin, kun sovelluksesi otetaan käyttöön siten, että voit tuoda sovelluksen uuden version onnistuneesti tuotantoon, vaikka käyttäjä ei huomaa sen epäkäytettävyyttä. Käyttäjän ja yrityksen näkökulmasta tämä on paras mahdollinen käyttöönottoskenaario, koska sen avulla voidaan ottaa käyttöön uusia ominaisuuksia ja korjata vikoja ilman häiriöitä.

Miten tämä saavutetaan? On olemassa useita tapoja, tässä yksi niistä:

  • ota käyttöön palvelusi versio nro 1
  • suorittaa tietokannan siirto
  • Ota palvelusi versio 2 käyttöön rinnakkain version 1 kanssa
  • heti kun näet, että versio nro 2 toimii niin kuin sen pitääkin, poista versio nro 1
  • valmis!

Helppoa, eikö? Valitettavasti se ei ole niin yksinkertaista, ja tarkastelemme sitä yksityiskohtaisesti myöhemmin. Tarkastetaan nyt toinen melko yleinen käyttöönottoprosessi - sininen vihreä käyttöönotto.

Oletko koskaan kuullut sininen vihreä käyttöönotto? Cloud Foundry tekee tästä erittäin helppoa. Katso vain Tässä artikkelissa, jossa kuvataan tätä tarkemmin. Lyhyen yhteenvedon vuoksi muistutetaan, kuinka sinisen vihreä käyttöönotto:

  • varmista, että kaksi kopiota tuotantokoodistasi ("sininen" ja "vihreä") toimii;
  • ohjata kaikki liikenne siniseen ympäristöön, ts. jotta tuotannon URL-osoitteet osoittavat sinne;
  • ottaa käyttöön ja testata kaikki sovellusmuutokset vihreässä ympäristössä;
  • vaihtaa URL-osoitteet sinisestä vihreään ympäristöön

Sinivihreä käyttöönotto on lähestymistapa, jonka avulla voit helposti ottaa käyttöön uusia ominaisuuksia ilman huolta tuotannon katkeamisesta. Tämä johtuu siitä, että vaikka jotain tapahtuisi, voit helposti palata edelliseen ympäristöön yksinkertaisesti "näpäytä kytkintä".

Kun olet lukenut kaikki yllä olevat, voit kysyä kysymyksen: Mitä tekemistä nollalla seisokkeilla on Blue greenin käyttöönoton kanssa?

No, niillä on melko paljon yhteistä, koska kahden kopion ylläpitäminen samasta ympäristöstä vaatii kaksinkertaista vaivaa niiden ylläpitoon. Tästä syystä jotkut joukkueet väittävät Martin Fowler, noudata tämän lähestymistavan muunnelmaa:

Toinen vaihtoehto on käyttää samaa tietokantaa luomalla sinivihreitä kytkimiä verkko- ja verkkotunnustasoille. Tässä lähestymistavassa tietokanta voi usein olla ongelma, varsinkin kun sen skeemaa on muutettava tukemaan ohjelmiston uutta versiota.

Ja tästä päästään tämän artikkelin pääongelmaan. tietokanta. Katsotaanpa tätä lausetta vielä kerran.

suorittaa tietokannan siirto.

Nyt sinun on kysyttävä itseltäsi kysymys - entä jos tietokannan muutos ei ole taaksepäin yhteensopiva? Eikö sovelluksen ensimmäinen versio katkea? Itse asiassa juuri näin tulee tapahtumaan...

Huolimatta nollakatkon/sinivihreän käyttöönoton valtavista eduista yrityksillä on tapana noudattaa seuraavaa turvallisempaa prosessia sovellusten käyttöönotossa:

  • valmistele paketti sovelluksen uudella versiolla
  • sulje käynnissä oleva sovellus
  • suorita komentosarjat tietokannan siirtämiseksi
  • ottaa käyttöön ja käynnistää sovelluksen uuden version

Tässä artikkelissa kerromme yksityiskohtaisesti, kuinka voit työskennellä tietokannan ja koodin kanssa hyödyntääksesi nollakatkosaikaa.

Tietokantaongelmat

Jos sinulla on tilaton sovellus, joka ei tallenna tietoja tietokantaan, voit ottaa heti käyttöön nollakatkosaikaa. Valitettavasti useimpien ohjelmistojen on tallennettava tiedot jonnekin. Tästä syystä sinun tulee miettiä kahdesti ennen kuin teet mitään muutoksia piiriin. Ennen kuin käsittelemme yksityiskohtia skeeman muuttamisesta niin, että käyttöönotto ilman seisokkeja on mahdollista, keskitytään ensin versiointiskeemaan.

Versiokaavio

Tässä artikkelissa käytämme lentotie versionhallintatyökaluna (noin Käännös: puhumme tietokantojen siirroista). Luonnollisesti kirjoitamme myös Spring Boot -sovelluksen, jossa on sisäänrakennettu Flyway-tuki ja joka suorittaa skeeman siirron sovelluskontekstia määrittäessään. Kun käytät Flywayta, voit tallentaa siirtokomentosarjat projektikansioosi (oletuksena classpath:db/migration). Tässä on esimerkki tällaisista siirtotiedostoista

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

Tässä esimerkissä näemme 4 siirtokomentosarjaa, jotka, jos niitä ei ole suoritettu aiemmin, suoritetaan peräkkäin sovelluksen käynnistyessä. Katsotaanpa yhtä tiedostoista (V1__init.sql) Esimerkiksi.

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

Kaikki on täysin itsestään selvää: voit käyttää SQL:ää määrittääksesi, kuinka tietokantaasi tulee muokata. Lisätietoja Spring Bootista ja Flywaysta on osoitteessa Spring Boot Docs.

Käyttämällä lähteenhallintatyökalua Spring Bootin kanssa saat kaksi suurta etua:

  • erotat tietokannan muutokset koodimuutoksista
  • Tietokannan siirto tapahtuu sovelluksesi käyttöönoton yhteydessä, ts. käyttöönottoprosessisi on yksinkertaistettu

Tietokantaongelmien vianmääritys

Artikkelin seuraavassa osassa keskitymme tarkastelemaan kahta lähestymistapaa tietokannan muutoksiin.

  • taaksepäin yhteensopimattomuus
  • taaksepäin yhteensopivuus

Ensimmäistä pidetään varoituksena, että nollakatkosaikaista käyttöönottoa ei pidä suorittaa ilman alustavaa valmistelua... Toinen tarjoaa ratkaisun, kuinka voit suorittaa käyttöönoton ilman seisokkeja ja samalla säilyttää taaksepäin yhteensopivuuden.

Projektimme, jonka parissa työskentelemme, on yksinkertainen Spring Boot Flyway -sovellus, jolla on Person с first_name и last_name tietokannassa (noin käännös: Person on pöytä ja first_name и last_name - nämä ovat sen kentät). Haluamme nimetä uudelleen last_name в surname.

Oletukset

Ennen kuin menemme yksityiskohtiin, meidän on tehtävä pari oletusta sovelluksistamme. Päätulos, jonka haluamme saavuttaa, on melko yksinkertainen prosessi.

Muistilappu. Business PRO-TIP. Yksinkertaistamalla prosesseja voit säästää paljon tukirahoja (mitä enemmän ihmisiä työskentelet yrityksessäsi, sitä enemmän rahaa voit säästää)!

Tietokantaa ei tarvitse palauttaa

Tämä yksinkertaistaa käyttöönottoprosessia (jotkut tietokannan palautukset ovat lähes mahdottomia, kuten poistojen palautus). Mieluummin peruutamme vain sovellukset. Tällä tavalla, vaikka sinulla olisi erilaisia ​​tietokantoja (esimerkiksi SQL ja NoSQL), käyttöönottoputki näyttää samalta.

Sovelluksen on AINA voitava palauttaa yhden version takaisin (ei enempää)

Palautus tulee tehdä vain tarvittaessa. Jos nykyisessä versiossa on virhe, jota ei voida korjata helposti, meidän pitäisi pystyä palaamaan uusimpaan toimivaan versioon. Oletamme, että tämä uusin toimiva versio on edellinen. Koodin ja tietokannan yhteensopivuuden ylläpitäminen useamman kuin yhden käyttöönoton aikana olisi erittäin vaikeaa ja kallista.

Muistilappu. Paremman luettavuuden vuoksi tässä artikkelissa muutamme sovelluksen pääversiota.

Vaihe 1: Alkutila

Sovelluksen versio: 1.0.0
DB-versio: v1

Kommentti

Tämä on sovelluksen alkutila.

Tietokannan muutokset

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

Koodin muutokset

Sovellus tallentaa henkilötiedot 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
                + "]";
    }
}

Taaksepäin yhteensopimaton sarakkeen uudelleennimeäminen

Katsotaanpa esimerkkiä sarakkeen nimen muuttamisesta:

Huomio. Seuraava esimerkki rikkoo asioita tarkoituksella. Näytämme tämän osoittaaksemme tietokannan yhteensopivuuden ongelman.

Sovelluksen versio: 2.0.0.BAD

DB-versio: v2bad

Kommentti

Nykyiset muutokset EIVÄT salli meidän ajaa kahta esiintymää (vanhaa ja uutta) samanaikaisesti. Siten nollakatkosajan käyttöönotto on vaikea saavuttaa (jos oletukset otetaan huomioon, se on itse asiassa mahdotonta).

A/B-testaus

Nykyinen tilanne on, että meillä on sovellusversio 1.0.0, käyttöön tuotannossa ja tietokannassa v1. Meidän on otettava käyttöön sovelluksen toinen esiintymä, versio 2.0.0.BADja päivitä tietokanta muotoon v2bad.

vaiheet:

  1. versiosovelluksen uusi esiintymä otetaan käyttöön 2.0.0.BADjoka päivittää tietokannan v2bad
  2. tietokannassa v2bad sarake last_name ei ole enää olemassa - se muutettiin muotoon surname
  3. Tietokannan ja sovelluksen päivitys onnistui ja jotkin esiintymät ovat käynnissä 1.0.0, muut - sisään 2.0.0.BAD. Kaikki on yhdistetty tietokantaan v2bad
  4. kaikki version esiintymät 1.0.0 alkaa heittää virheitä, koska he yrittävät lisätä tietoja sarakkeeseen last_namejota ei ole enää olemassa
  5. kaikki version esiintymät 2.0.0.BAD toimii ilman ongelmia

Kuten näet, jos teemme taaksepäin yhteensopimattomia muutoksia tietokantaan ja sovellukseen, A/B-testaus on mahdotonta.

Sovelluksen palautus

Oletetaan, että kun olet yrittänyt tehdä A/B-käyttöönoton (noin per.: kirjoittaja tarkoitti tässä luultavasti A/B-testausta) päätimme, että meidän on palautettava sovellus versioon 1.0.0. Oletetaan, että emme halua palauttaa tietokantaa.

vaiheet:

  1. pysäytämme versiosovelluksen ilmentymän 2.0.0.BAD
  2. tietokanta on edelleen v2bad
  3. versiosta lähtien 1.0.0 ei ymmärrä mikä se on surname, näemme virheitä
  4. helvetti on irronnut, emme voi enää palata

Kuten näet, jos teemme taaksepäin yhteensopimattomia muutoksia tietokantaan ja sovellukseen, emme voi palata edelliseen versioon.

Komentosarjan suorituslokit

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

Tietokannan muutokset

Siirtokomentosarja, joka nimeää uudelleen last_name в surname

Lähde Flyway käsikirjoitus:

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

Skripti, joka nimeää uudelleen last_name.

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

Koodin muutokset

Olemme muuttaneet kentän nimen lastName päälle surname.

Sarakkeen uudelleennimeäminen taaksepäin yhteensopivalla tavalla

Tämä on yleisin tilanne, jonka voimme kohdata. Meidän on tehtävä taaksepäin yhteensopimattomia muutoksia. Olemme jo osoittaneet, että ilman seisokkiaikaa tapahtuvaa käyttöönottoa meidän ei pitäisi vain soveltaa tietokannan siirtoa ilman lisävaiheita. Tässä artikkelin osassa suoritamme 3 sovelluksen käyttöönottoa sekä tietokantojen siirtoja halutun tuloksen saavuttamiseksi säilyttäen samalla yhteensopivuuden taaksepäin.

Muistilappu. Muista, että meillä on versiotietokanta v1. Se sisältää sarakkeita first_name и last_name. Meidän on muututtava last_name päälle surname. Meillä on myös sovellusversio 1.0.0, jota ei vielä käytetä surname.

Vaihe 2: Lisää sukunimi

Sovelluksen versio: 2.0.0
DB-versio: v2

Kommentti

Lisäämällä uuden sarakkeen ja kopioimalla sen sisällön luomme taaksepäin yhteensopivia tietokantamuutoksia. Samaan aikaan, jos peruutamme JAR:n tai käytämme vanhaa JAR:ia, se ei hajoa suorituksen aikana.

Otamme käyttöön uuden version

vaiheet:

  1. Suorita tietokannan siirto uuden sarakkeen luomiseksi surname. Nyt DB-versiosi v2
  2. kopioi tiedot kohteesta last_name в surname. Kiinnitä huomiotaettä jos sinulla on paljon näitä tietoja, sinun tulee harkita eräsiirtoa!
  3. kirjoita koodi, jossa niitä käytetään mOLEMMAT и uusiJa vanha sarakkeessa. Nyt sovelluksesi versio 2.0.0
  4. lue arvo sarakkeesta surname, jos ei ole null, tai last_namejos surname ei määritelty. Voit poistaa getLastName() koodista, koska se tulostuu null kun palautat hakemuksesi osoitteesta 3.0.0 до 2.0.0.

Jos käytät Spring Boot Flyway -ohjelmaa, nämä kaksi vaihetta suoritetaan version käynnistyksen aikana 2.0.0 sovellukset. Jos käytät tietokannan versiotyökalua manuaalisesti, sinun on tehtävä kaksi eri asiaa (päivitä ensin db-versio manuaalisesti ja ota sitten uusi sovellus käyttöön).

Se on tärkeää. Muista, että juuri luotu sarake EI PITÄISI быть EI TYHJÄ. Jos teet palautuksen, vanha sovellus ei tiedä uudesta sarakkeesta eikä asenna sitä sen aikana Insert. Mutta jos lisäät tämän rajoitteen, tietosi tulee olemaan v2, tämä edellyttää uuden sarakkeen arvon asettamista. Mikä johtaa rajoitusten rikkomiseen.

Se on tärkeää. Menetelmä kannattaa poistaa getLastName(), koska versiossa 3.0.0 Koodissa ei ole sarakkeen käsitettä last_name. Tämä tarkoittaa, että siellä asetetaan nolla. Voit jättää menetelmän ja lisätä tarkistuksia null, mutta paljon parempi ratkaisu olisi varmistaa se logiikassa getSurname() valitsit oikean nollasta poikkeavan arvon.

A/B-testaus

Nykyinen tilanne on, että meillä on sovellusversio 1.0.0, otettu käyttöön tuotannossa ja tietokannassa v1. Meidän on otettava käyttöön versiosovelluksen toinen esiintymä 2.0.0joka päivittää tietokannan v2.

vaiheet:

  1. versiosovelluksen uusi esiintymä otetaan käyttöön 2.0.0joka päivittää tietokannan v2
  2. sillä välin versioinstanssit käsittelivät joitakin pyyntöjä 1.0.0
  3. päivitys onnistui ja sinulla on käynnissä useita versiosovelluksen esiintymiä 1.0.0 ja muita versioita 2.0.0. Kaikki kommunikoivat tietokannan kanssa v2
  4. versio 1.0.0 ei käytä tietokannan sukunimisaraketta, vaan versiota 2.0.0 käyttää. Ne eivät häiritse toisiaan, eikä niissä pitäisi olla virheitä.
  5. versio 2.0.0 tallentaa tiedot sekä vanhaan että uuteen sarakkeeseen, mikä varmistaa taaksepäin yhteensopivuuden

Se on tärkeää. Jos sinulla on kyselyitä, jotka laskevat kohteita vanhan/uuden sarakkeen arvojen perusteella, muista, että sinulla on nyt päällekkäisiä arvoja (todennäköisimmin ne ovat edelleen siirtymässä). Jos esimerkiksi haluat laskea niiden käyttäjien määrän, joiden sukunimi (millä tahansa sarakkeen nimi) alkoi kirjaimella A, sitten kunnes tietojen siirto on valmis (oldnew sarake), sinulla voi olla epäjohdonmukaisia ​​tietoja, jos teet kyselyn uudesta sarakkeesta.

Sovelluksen palautus

Nyt meillä on sovellusversio 2.0.0 ja tietokanta sisään v2.

vaiheet:

  1. palauta sovelluksesi versioon 1.0.0.
  2. versio 1.0.0 ei käytä saraketta tietokannassa surname, joten palautuksen pitäisi onnistua

DB muuttuu

Tietokanta sisältää sarakkeen nimeltä last_name.

Flyway-lähdekoodi:

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

Lisää käsikirjoitus surname.

Huomio. Muista, että et VOI LISÄÄ NOT NULL -rajoituksia lisättävään sarakkeeseen. Jos palautat JAR:n, vanhalla versiolla ei ole aavistustakaan lisätystä sarakkeesta ja se asettaa sen automaattisesti arvoon NULL. Jos tällainen rajoitus on olemassa, vanha sovellus yksinkertaisesti rikkoutuu.

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

Koodin muutokset

Tallennamme tiedot muodossa last_name, ja in surname. Samalla luimme osoitteesta last_name, koska tämä sarake on olennaisin. Käyttöönottoprosessin aikana jotkin pyynnöt ovat saattaneet käsitellä sovellusesiintymässä, jota ei ole vielä päivitetty.

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

Vaihe 3: Poista sukunimi koodista

Sovelluksen versio: 3.0.0

DB-versio:v3

Kommentti

Huomautus per.: Ilmeisesti alkuperäisessä artikkelissa kirjoittaja kopioi vahingossa tämän lohkon tekstin vaiheesta 2. Tässä vaiheessa sovelluskoodiin tulee tehdä muutoksia, joilla pyritään poistamaan saraketta käyttävä toiminto last_name.

Lisäämällä uuden sarakkeen ja kopioimalla sen sisällön loimme taaksepäin yhteensopivat tietokantamuutokset. Lisäksi, jos peruutamme JAR:n tai jos vanha JAR on käynnissä, se ei hajoa suorituksen aikana.

Sovelluksen palautus

Tällä hetkellä meillä on sovellusversio 3.0.0 ja tietokanta v3. Versio 3.0.0 ei tallenna tietoja last_name. Tämä tarkoittaa, että sisään surname ajantasaisimmat tiedot tallennetaan.

vaiheet:

  1. palauta sovelluksesi versioon 2.0.0.
  2. versio 2.0.0 käyttää ja last_name и surname.
  3. versio 2.0.0 ottaa surname, jos se ei ole nolla, muuten -last_name

Tietokannan muutokset

Tietokannassa ei ole rakenteellisia muutoksia. Seuraava komentosarja suoritetaan vanhojen tietojen lopullisen siirron suorittamiseksi:

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

Koodin muutokset

Huomautus per.: Tekijä kopioi myös tämän lohkon kuvauksen virheellisesti vaiheesta 2. Artikkelin logiikan mukaisesti koodin muutoksilla tässä vaiheessa tulisi pyrkiä poistamaan siitä elementtejä, jotka toimivat sarakkeen kanssa last_name.

Tallennamme tiedot muodossa last_name, ja in surname. Lisäksi luimme kolumnista last_name, koska se on olennaisin. Käyttöönottoprosessin aikana jotkin pyynnöt saattavat käsitellä ilmentymässä, jota ei ole vielä päivitetty.

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

Vaihe 4: Poista sukunimi tietokannasta

Sovelluksen versio: 4.0.0

DB-versio: v4

Kommentti

Johtuen siitä, että versiokoodi 3.0.0 ei käyttänyt saraketta last_name, mitään pahaa ei tapahdu suorituksen aikana, jos palaamme siihen 3.0.0 sarakkeen poistamisen jälkeen tietokannasta.

Komentosarjan suorituslokit

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 muuttuu

Suhteellisesti v3 poistamme vain sarakkeen last_name ja lisää puuttuvia rajoituksia.

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

Koodin muutokset

Koodiin ei ole muutoksia.

johtopäätös

Otimme onnistuneesti käyttöön taaksepäin yhteensopimattoman sarakkeen nimen muutoksen suorittamalla useita taaksepäin yhteensopivia käyttöönottoja. Alla on yhteenveto suoritetuista toimista:

  1. sovellusversion käyttöönotto 1.0.0 с v1 tietokantaskeema (sarakkeen nimi = last_name)
  2. sovellusversion käyttöönotto 2.0.0, joka tallentaa tietoja last_name и surname. Sovellus lukee alkaen last_name. Tietokanta on versiossa v2sisältää sarakkeita, kuten last_nameJa surname. surname on kopio last_name. (HUOMAA: Tässä sarakkeessa ei saa olla nolla-rajoitusta)
  3. sovellusversion käyttöönotto 3.0.0, joka tallentaa vain tietoja surname ja lukee sukunimestä. Tietokannan osalta viimeinen siirto on meneillään last_name в surname. Myös rajoitus EI TYHJÄ poistettu last_name. Tietokanta on nyt versiossa v3
  4. sovellusversion käyttöönotto 4.0.0 - koodiin ei tehdä muutoksia. Tietokannan käyttöönotto v4, joka poistaa last_name. Täällä voit lisätä tietokantaan puuttuvat rajoitteet.

Noudattamalla tätä lähestymistapaa voit aina palauttaa yhden version rikkomatta tietokannan/sovelluksen yhteensopivuutta.

Koodi

Kaikki tässä artikkelissa käytetty koodi on saatavilla osoitteessa Github. Alla lisäkuvaus.

hankkeet

Arkiston kloonauksen jälkeen näet seuraavan kansiorakenteen.

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

skriptejä

Voit suorittaa alla olevissa komentosarjoissa kuvatut komentosarjat, jotka osoittavat taaksepäin yhteensopivia ja yhteensopimattomia muutoksia tietokantaan.

Nähdä tapauksessa taaksepäin yhteensopivilla muutoksilla, juosta:

./scripts/scenario_backward_compatible.sh

Ja nähdä tapaus taaksepäin yhteensopimattomilla muutoksilla, juosta:

./scripts/scenario_backward_incompatible.sh

Spring Boot Sample Flyway

Kaikki esimerkit on otettu Spring Boot Sample Flyway.

Voit katsoa http://localhost:8080/flyway, siellä on luettelo skripteistä.

Tämä esimerkki sisältää myös H2-konsolin (at http://localhost:8080/h2-console), jotta voit tarkastella tietokannan tilaa (oletus jdbc URL on jdbc:h2:mem:testdb).

lisäksi

Lue myös muut artikkelit blogistamme:

Lähde: will.com

Lisää kommentti