Nula Malfunkcio-Deplojo kaj Datumbazoj

Nula Malfunkcio-Deplojo kaj Datumbazoj

Ĉi tiu artikolo klarigas detale kiel solvi datumbazkongruecajn problemojn en deplojo. Ni diros al vi, kio povas okazi al viaj produktadaplikoj se vi provas disfaldi sen antaŭpreparo. Ni tiam trapasos la aplikaĵajn vivciklojn, kiuj estas postulataj por havi nulan malfunkcion (ĉ. lane: plu - nula malfunkcio). La rezulto de niaj operacioj estos apliki la malantaŭen-nekongruan datumbazan ŝanĝon en malantaŭen-kongrua maniero.

Se vi volas kompreni la kodekzemplojn de la artikolo, vi povas trovi ilin ĉe GitHub.

Enkonduko

Deplojo de nula malfunkcio

Kia mistika nula malfunkcio deplojo? Vi povas diri, ke ĉi tio estas kiam via aplikaĵo estas deplojita tiel, ke vi povas sukcese enkonduki novan version de la aplikaĵo al produktado, dum la uzanto ne rimarkas ĝian nehaveblecon. De uzanto kaj kompanio perspektivo, ĉi tiu estas la plej bona ebla deplojo scenaro ĉar ĝi permesas novajn funkciojn esti enkondukitaj kaj cimoj esti fiksitaj sen interrompo.

Kiel atingi ĉi tion? Estas pluraj manieroj, jen unu el ili:

  • deploji la version n-ro 1 de via servo
  • fari datumbazan migradon
  • Deploji version #2 de via servo paralele kun versio #1
  • tuj kiam vi vidos, ke la versio n-ro 2 funkcias kiel ĝi devus, forigu la version n-ro 1
  • farita!

Facile, ĉu ne? Bedaŭrinde, ĝi ne estas tiel simpla, kaj ni rigardos tion detale poste. Nun ni kontrolu alian sufiĉe oftan deplojprocezon - bluverdan deplojon.

Ĉu vi iam aŭdis pri blua verda deplojo? Cloud Foundry faras ĉi tion ekstreme facila. Nur rigardu ĉi tiu artikolo, kie ni priskribas tion pli detale. Por mallonge resumi, ni memorigu vin kiel fari bluverdan deplojon:

  • certigu, ke du kopioj de via produktkodo ("blua" kaj "verda") funkcias;
  • direkti la tutan trafikon al la blua medio, t.e. tiel ke produktaj URL-oj montras tien;
  • deploji kaj testi ĉiujn aplikajn ŝanĝojn en verda medio;
  • ŝanĝi URL-ojn de blua al verda medio

Blua verda deplojo estas aliro, kiu permesas vin facile enkonduki novajn funkciojn sen zorgi pri produktado-rompado. Ĉi tio estas pro la fakto, ke eĉ se io okazas, vi povas facile reiri al la antaŭa medio simple "premante ŝaltilon".

Post legi ĉion supre, vi povas demandi la demandon: Kion nul malfunkcio devas vidi kun Blua verda deplojo?

Nu, ili havas sufiĉe multajn komunajn, ĉar konservi du kopiojn de la sama medio postulas duoblan penon por konservi ilin. Jen kial iuj teamoj asertas Martin Fowler, sekvu varion de ĉi tiu aliro:

Alia opcio estas uzi la saman datumbazon, kreante bluverdajn ŝaltilojn por la retejo kaj domajnaj tavoloj. En ĉi tiu aliro, la datumbazo ofte povas esti problemo, precipe kiam vi bezonas ŝanĝi sian skemon por subteni novan version de la programaro.

Kaj jen ni venas al la ĉefa problemo en ĉi tiu artikolo. Datumbazo. Ni rigardu alian ĉi tiun frazon.

fari datumbazan migradon.

Nun vi devas demandi al vi mem - kio se la datumbaza ŝanĝo ne estas malantaŭen kongrua? Ĉu mia unua versio de la apo ne rompiĝos? Fakte, ĝuste tio okazos...

Do, eĉ malgraŭ la grandegaj avantaĝoj de nula malfunkcio / blua verda deplojo, kompanioj emas sekvi la sekvan pli sekuran procezon por disfaldi siajn aplikojn:

  • preparu pakaĵon kun nova versio de la aplikaĵo
  • malŝalti funkciantan aplikaĵon
  • rulu skriptojn por migri la datumbazon
  • deploji kaj lanĉi novan version de la aplikaĵo

En ĉi tiu artikolo, ni detalos kiel vi povas labori kun via datumbazo kaj kodo por profiti de nula malfunkcio-deplojo.

Problemoj pri datumbazo

Se vi havas sennacian aplikaĵon, kiu konservas neniujn datumojn en la datumbazo, vi povas tuj akiri nulan malfunkcion. Bedaŭrinde, plej multaj programoj bezonas konservi datumojn ie. Jen kial vi devus pensi dufoje antaŭ fari ajnajn ŝanĝojn al la cirkvito. Antaŭ ol ni eniros la detalojn pri kiel ŝanĝi la skemon por ke ne-malfunkcia deplojo estu ebla, ni unue koncentriĝu pri la versio-skemo.

Versiiga skemo

En ĉi tiu artikolo ni uzos Flugvojo kiel versio-kontrolilo (ĉ. Traduko: ni parolas pri datumbazaj migradoj). Nature, ni ankaŭ skribos Spring Boot-aplikaĵon kiu havas enkonstruitan Flyway-subtenon kaj faros skeman migradon dum agordo de la aplikaĵa kunteksto. Kiam vi uzas Flyway, vi povas konservi migrajn skriptojn en la dosierujo de viaj projektoj (defaŭlte en classpath:db/migration). Ĉi tie vi povas vidi ekzemplon de tiaj migraj dosieroj

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

En ĉi tiu ekzemplo ni vidas 4 migrajn skriptojn kiuj, se ne ekzekutitaj antaŭe, estos ekzekutitaj unu post la alia kiam la aplikaĵo komenciĝas. Ni rigardu unu el la dosieroj (V1__init.sql) kiel ekzemplon.

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

Ĉio estas perfekte memklarigebla: vi povas uzi SQL por difini kiel via datumbazo estu modifita. Por pliaj informoj pri Spring Boot kaj Flyway, kontrolu Spring Boot Docs.

Per uzado de fontkontrolilo kun Spring Boot, vi ricevas 2 grandajn avantaĝojn:

  • vi apartigas datumbazŝanĝojn de kodŝanĝoj
  • Datumbaza migrado okazas kune kun la lanĉo de via aplikaĵo, t.e. via deploja procezo estas simpligita

Solvado de problemoj pri datumbazo

En la sekva sekcio de la artikolo, ni fokusos rigardi du alirojn al datumbazaj ŝanĝoj.

  • malantaŭen nekongrueco
  • retrokongruo

La unua estos konsiderata kiel averto, ke vi ne devus plenumi nulan malfunkcian deplojon sen antaŭpreparo... La dua proponas solvon pri kiel vi povas plenumi disfaldiĝon sen malfunkcio kaj samtempe konservi malantaŭan kongruecon.

Nia projekto, pri kiu ni laboros, estos simpla aplikaĵo Spring Boot Flyway, kiu havas Person с first_name и last_name en la datumbazo (ĉ. traduko: Person estas tablo kaj first_name и last_name - jen la kampoj en ĝi). Ni volas renomi last_name в surname.

Supozoj

Antaŭ ol ni eniros la detalojn, estas kelkaj supozoj, kiujn ni devas fari pri niaj aplikoj. La ĉefa rezulto, kiun ni volas atingi, estos sufiĉe simpla procezo.

La noto. Komerco PRO-TIP. Simpligaj procezoj povas ŝpari al vi multe da mono pri subteno (ju pli da homoj vi havas laborantajn por via kompanio, des pli da mono vi povas ŝpari)!

Ne necesas restarigi la datumbazon

Ĉi tio simpligas la deplojprocezon (kelkaj datumbazrevenoj estas preskaŭ maleblaj, kiel forigo-reveno). Ni preferas refari nur aplikojn. Tiel, eĉ se vi havas malsamajn datumbazojn (ekzemple, SQL kaj NoSQL), via disfalda dukto aspektos same.

Devas ĈIAM ebli retroiri la aplikaĵon unu version reen (ne pli)

Rollback devus esti farita nur kiam necese. Se estas cimo en la nuna versio, kiu ne estas facile riparita, ni devus povi reveni al la plej nova funkcianta versio. Ni supozas, ke ĉi tiu lasta laborversio estas la antaŭa. Konservi kodon kaj datumbazkongruon por pli ol unu lanĉo estus ege malfacila kaj multekosta.

La noto. Por pli granda legebleco, en ĉi tiu artikolo ni ŝanĝos la ĉefan version de la aplikaĵo.

Paŝo 1: Komenca Ŝtato

Versio de la programo: 1.0.0
DB-versio: v1

komento

Ĉi tio estos la komenca stato de la aplikaĵo.

Datumbazŝanĝoj

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

Kodoŝanĝoj

La aplikaĵo stokas Person-datumojn en 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
                + "]";
    }
}

Malantaŭen nekongrua kolona renomado

Ni rigardu ekzemplon pri kiel ŝanĝi kolumnan nomon:

Atento. La sekva ekzemplo intence rompos aferojn. Ni montras ĉi tion por pruvi la problemon de datumbaza kongruo.

Versio de la programo: 2.0.0.BAD

DB-versio: v2bad

komento

La nunaj ŝanĝoj NE permesas al ni ruli du okazojn (malnovan kaj novan) samtempe. Tiel, nula malfunkcio-deplojo estos malfacile atingi (se supozoj estas enkalkulitaj, ĝi estas fakte malebla).

A/B-testado

La nuna situacio estas, ke ni havas aplikaĵversion 1.0.0, deplojita en produktado, kaj datumbazo v1. Ni devas deploji duan okazon de la aplikaĵo, versio 2.0.0.BAD, kaj ĝisdatigi la datumbazon al v2bad.

Paŝoj:

  1. nova kazo de la versio-aplikaĵo estas deplojita 2.0.0.BADkiu ĝisdatigas la datumbazon al v2bad
  2. en la datumbazo v2bad kolumno last_name ne plu ekzistas - ĝi estis ŝanĝita al surname
  3. La ĝisdatigo de datumbazo kaj aplikaĵo sukcesis kaj kelkaj okazoj funkcias 1.0.0, aliaj - en 2.0.0.BAD. Ĉio estas konektita al la datumbazo v2bad
  4. ĉiuj okazoj de la versio 1.0.0 komencos ĵeti erarojn ĉar ili provos enmeti datumojn en la kolumnon last_namekiu ne plu ekzistas
  5. ĉiuj okazoj de la versio 2.0.0.BAD funkcios sen problemoj

Kiel vi povas vidi, se ni faras malantaŭen nekongruajn ŝanĝojn al la datumbazo kaj aplikaĵo, A/B-testado estas neebla.

Apliko-revenigo

Ni supozu, ke post provi fari A/B-deplojon (ĉ. per.: la aŭtoro verŝajne celis A/B-testadon ĉi tie) ni decidis, ke ni devas revenigi la aplikaĵon al la versio 1.0.0. Ni diru, ke ni ne volas restarigi la datumbazon.

Paŝoj:

  1. ni haltigas la version-aplik-instancon 2.0.0.BAD
  2. la datumbazo ankoraŭ estas v2bad
  3. ekde la versio 1.0.0 ne komprenas kio ĝi estas surname, ni vidos erarojn
  4. la infero liberiĝis, ni ne plu povas reiri

Kiel vi povas vidi, se ni faras malantaŭen nekongruajn ŝanĝojn al la datumbazo kaj aplikaĵo, ni ne povas reveni al la antaŭa versio.

Protokoloj de ekzekuto de skriptoj

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

Datumbazŝanĝoj

Migrado-skripto kiu renomas last_name в surname

Fonta Flyway-skripto:

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

Skripto kiu renomas last_name.

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

Kodoŝanĝoj

Ni ŝanĝis la kamponomon lastName sur surname.

Renomi kolumnon en malantaŭen kongrua maniero

Ĉi tiu estas la plej ofta situacio, kiun ni povas renkonti. Ni devas fari malantaŭen nekongruajn ŝanĝojn. Ni jam pruvis, ke por nul-malfunkcia deplojo, ni ne simple apliki datumbazan migradon sen pliaj paŝoj. En ĉi tiu sekcio de la artikolo, ni faros 3 deplojojn de la aplikaĵo kune kun datumbazaj migradoj por atingi la deziratan rezulton konservante malantaŭan kongruecon.

La noto. Memoru, ke ni havas version-datumbazon v1. Ĝi enhavas kolumnojn first_name и last_name. Ni devas ŝanĝi last_name sur surname. Ni ankaŭ havas app-version 1.0.0, kiu ankoraŭ ne estas uzata surname.

Paŝo 2: Aldonu familian nomon

Versio de la programo: 2.0.0
DB-versio: v2

komento

Aldonante novan kolumnon kaj kopiante ĝian enhavon, ni kreas malantaŭen kongruajn datumbazŝanĝojn. Samtempe, se ni malakceptas la JAR aŭ havas malnovan JAR funkciantan, ĝi ne rompiĝos dum ekzekuto.

Ni lanĉas novan version

Paŝoj:

  1. fari datumbazan migradon por krei novan kolumnon surname. Nun via DB-versio v2
  2. kopii datumojn de last_name в surname. Atentuke se vi havas multajn ĉi tiujn datumojn, vi devus konsideri grupan migradon!
  3. skribu la kodon kie ili estas uzataj AMBAŬ и новыйkaj la malnova kolumno. Nun via app-versio 2.0.0
  4. legu la valoron el la kolumno surname, se ĝi ne estas null, aŭ de last_namese surname ne specifita. Vi povas forigi getLastName() de la kodo, ĉar ĝi eligos null kiam revenigu vian aplikaĵon de 3.0.0 por 2.0.0.

Se vi uzas Spring Boot Flyway, ĉi tiuj du paŝoj estos plenumitaj dum versio-komenco 2.0.0 aplikoj. Se vi rulas la datumbazan version-ilon mane, vi devos fari du malsamajn aferojn por fari tion (unue ĝisdatigi la db-version permane kaj poste deploji la novan aplikaĵon).

Gravas. Memoru, ke la lastatempe kreita kolumno NE DEVUS esti NE NULA. Se vi faras malfunkciigon, la malnova aplikaĵo ne scias pri la nova kolumno kaj ne instalos ĝin dum Insert. Sed se vi aldonas ĉi tiun limon kaj via db estos v2, ĉi tio postulos agordi la valoron de la nova kolumno. Kio kondukos al malobservoj de limigoj.

Gravas. Vi devus forigi la metodon getLastName(), ĉar en la versio 3.0.0 Ne estas koncepto de kolumno en la kodo last_name. Ĉi tio signifas, ke nulo estos agordita tie. Vi povas forlasi la metodon kaj aldoni ĉekojn por null, sed multe pli bona solvo estus certigi tion en la logiko getSurname() vi elektis la ĝustan ne-nulan valoron.

A/B-testado

La nuna situacio estas, ke ni havas aplikaĵversion 1.0.0, deplojita sur produktado, kaj la datumbazo en v1. Ni devas deploji duan okazon de la versio-aplikaĵo 2.0.0kiu ĝisdatigos la datumbazon al v2.

Paŝoj:

  1. nova kazo de la versio-aplikaĵo estas deplojita 2.0.0kiu ĝisdatigas la datumbazon al v2
  2. intertempe iuj petoj estis prilaboritaj de versioj 1.0.0
  3. la ĝisdatigo estis sukcesa kaj vi havas plurajn kurantajn okazojn de la versio-aplikaĵo 1.0.0 kaj aliaj versioj 2.0.0. Ĉiuj komunikas kun la datumbazo en v2
  4. versio 1.0.0 ne uzas la familinoman kolumnon en la datumbazo, sed la version 2.0.0 uzoj. Ili ne malhelpas unu la alian, kaj ne devus esti eraroj.
  5. versio 2.0.0 konservas datumojn en la malnova kaj nova kolumno, certigante malantaŭan kongruon

Gravas. Se vi havas demandojn, kiuj kalkulas erojn surbaze de valoroj de la malnova/nova kolumno, vi devas memori, ke vi nun havas duplikatajn valorojn (plej verŝajne ili ankoraŭ migras). Ekzemple, se vi volas kalkuli la nombron da uzantoj, kies familia nomo (kiu ajn nomiĝas la kolumno) komenciĝis per la litero A, tiam ĝis la datummigrado finiĝos (oldnew kolumno) vi eble havas malkonsekvencan datumojn se vi demandas novan kolumnon.

Apliko-revenigo

Nun ni havas app-version 2.0.0 kaj datumbazo en v2.

Paŝoj:

  1. reiru vian aplikaĵon al versio 1.0.0.
  2. versio 1.0.0 ne uzas kolumnon en la datumbazo surname, do la retroiro devus sukcesi

DB ŝanĝiĝas

La datumbazo enhavas kolumnon nomitan last_name.

Flugvoja fontskripto:

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

Aldonu skripton surname.

Atento. Memoru, ke vi NE POVAS ALDONI iujn ajn NOT NULL-limojn al la kolumno, kiun vi aldonas. Se vi malakceptas la JAR, la malnova versio ne havos ideon pri la aldonita kolumno kaj aŭtomate agordos ĝin al NULL. Se ekzistas tia limigo, la malnova aplikaĵo simple rompiĝos.

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

Kodoŝanĝoj

Ni konservas datumojn kiel last_name, kaj en surname. Samtempe ni legas el last_name, ĉar ĉi tiu kolumno estas la plej grava. Dum la deploja procezo, iuj petoj eble estis procesitaj de aplikaĵa petskribo kiu ankoraŭ ne estis ĝisdatigita.

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

Paŝo 3: Forigante familinomon de la kodo

Versio de la programo: 3.0.0

DB-versio:v3

komento

Notu per.: Ŝajne, en la originala artikolo la aŭtoro erare kopiis la tekston de ĉi tiu bloko de la paŝo 2. Je ĉi tiu paŝo oni devas fari ŝanĝojn en la aplika kodo celante forigi la funkciojn, kiuj uzas la kolumnon. last_name.

Aldonante novan kolumnon kaj kopiante ĝian enhavon, ni kreis malantaŭen kongruajn datumbazŝanĝojn. Ankaŭ, se ni malakceptas la JAR aŭ havas malnovan JAR funkciantan, ĝi ne rompiĝos dum ekzekuto.

Apliko-revenigo

Nuntempe ni havas app-version 3.0.0 kaj datumbazo v3. Versio 3.0.0 ne konservas datumojn al last_name. Ĉi tio signifas, ke en surname la plej ĝisdataj informoj estas konservitaj.

Paŝoj:

  1. reiru vian aplikaĵon al versio 2.0.0.
  2. versio 2.0.0 uzas kaj last_name и surname.
  3. versio 2.0.0 prenos surname, se ĝi ne estas nulo, alie -last_name

Datumbazŝanĝoj

Ne estas strukturaj ŝanĝoj en la datumbazo. La sekva skripto estas ekzekutita por plenumi la finan migradon de la malnovaj datumoj:

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

Kodoŝanĝoj

Notu per.: La priskribo de ĉi tiu bloko ankaŭ estis erare kopiita de la aŭtoro de la paŝo 2. Konforme al la logiko de la artikolo, ŝanĝoj en la kodo ĉe ĉi tiu paŝo devas celi forigi el ĝi elementojn, kiuj funkcias kun la kolumno. last_name.

Ni konservas datumojn kiel last_name, kaj en surname. Aldone, ni legas el la rubriko last_name, ĉar ĝi estas la plej grava. Dum la deploja procezo, iuj petoj povas esti procesitaj de petskribo kiu ankoraŭ ne estis ĝisdatigita.

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

Paŝo 4: Forigante familinomon el la datumbazo

Versio de la programo: 4.0.0

DB-versio: v4

komento

Pro la fakto ke la versio kodo 3.0.0 ne uzis la kolumnon last_name, nenio malbona okazos dum ekzekuto se ni reiru al 3.0.0 post forigo de kolumno el la datumbazo.

Protokoloj de ekzekuto de skriptoj

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 ŝanĝiĝas

Relative v3 ni simple forigas la kolumnon last_name kaj aldonu mankantajn limigojn.

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

Kodoŝanĝoj

Ne estas ŝanĝoj al la kodo.

konkludo

Ni sukcese aplikis malantaŭen nekongruan kolumnan nomŝanĝon farante plurajn malantaŭen-kongruajn deplojojn. Malsupre estas resumo de la agoj faritaj:

  1. deplojo de aplika versio 1.0.0 с v1 datumbaza skemo (kolumna nomo = last_name)
  2. deplojo de aplika versio 2.0.0, kiu konservas datumojn en last_name и surname. La aplikaĵo legas de last_name. La datumbazo estas en versio v2enhavantaj kolumnojn kiel last_name, kaj surname. surname estas kopio de last_name. (NOTU: Ĉi tiu kolumno ne devas havi nenulan limon)
  3. deplojo de aplika versio 3.0.0, kiu nur stokas datumojn en surname kaj legas el familia nomo. Koncerne la datumbazon, la lasta migrado okazas last_name в surname. Ankaŭ limigo NE NULA forigita de last_name. La datumbazo nun estas en versio v3
  4. deplojo de aplika versio 4.0.0 - neniuj ŝanĝoj estas faritaj al la kodo. Deplojo de datumbazoj v4, kiu forigas last_name. Ĉi tie vi povas aldoni ajnajn mankantajn limojn al la datumbazo.

Sekvante ĉi tiun aliron, vi ĉiam povas refari unu version sen rompi datumbazon/aplikan kongruecon.

Kodo

Ĉiu kodo uzata en ĉi tiu artikolo estas havebla ĉe GitHub. Malsupre estas plia priskribo.

Projektoj

Post kloni la deponejon, vi vidos la sekvan dosierujon.

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

Skriptoj

Vi povas ruli la skriptojn priskribitajn en la subaj skriptoj, kiuj montros malantaŭen kongruajn kaj nekongruajn ŝanĝojn al la datumbazo.

Vidi la kazo kun malantaŭen kongruaj ŝanĝoj, kuri:

./scripts/scenario_backward_compatible.sh

Kaj vidi kazo kun malantaŭen nekongruaj ŝanĝoj, kuri:

./scripts/scenario_backward_incompatible.sh

Spring Boot Sample Flyway

Ĉiuj ekzemploj estas prenitaj de Spring Boot Sample Flyway.

Vi povas rigardi http://localhost:8080/flyway, estas listo de skriptoj.

Ĉi tiu ekzemplo ankaŭ inkluzivas la H2-konzolon (ĉe http://localhost:8080/h2-console) por ke vi povu vidi la datumbazan staton (defaŭlta jdbc URL estas jdbc:h2:mem:testdb).

aldone

Legu ankaŭ aliajn artikolojn en nia blogo:

fonto: www.habr.com

Aldoni komenton