Implementazione è basa di dati Zero Downtime

Implementazione è basa di dati Zero Downtime

Questu articulu spiega in dettagliu cumu risolve i prublemi di cumpatibilità di a basa di dati in a distribuzione. Vi diceremu ciò chì pò succede à e vostre applicazioni di produzzione se pruvate di implementà senza preparazione preliminare. Passeremu dopu e fasi di u ciclu di vita di l'applicazione chì sò richiesti per avè zero downtime (ca. lane: in più - zero downtime). U risultatu di e nostre operazioni serà di applicà u cambiamentu di basa di dati incompatibile in retrocede in una manera retrocompatibile.

Sè vo vulete capisce l'esempii di codice da l'articulu, pudete truvà à GitHub.

Introduzione

Implementazione zero downtime

Chì misticu implementazione zero downtime? Pudete dì chì questu hè quandu a vostra applicazione hè implementata in tale manera chì pudete introduci cù successu una nova versione di l'applicazione à a produzzione, mentre chì l'utilizatore ùn hà micca nutatu a so indisponibilità. Da una perspettiva di l'utilizatori è di a cumpagnia, questu hè u megliu scenariu di implementazione pussibule perchè permette di introduzione di novi funzioni è di risolve i bug senza interruzzione.

Cumu ottene questu? Ci hè parechje manere, quì hè unu di elli:

  • implementà a versione n ° 1 di u vostru serviziu
  • fà una migrazione di basa di dati
  • Implementa a versione #2 di u vostru serviziu in parallelu cù a versione #1
  • appena vi vede chì a versione n ° 2 travaglia cum'è si deve, sguassà versione n ° 1
  • fattu!

Facile, ùn hè micca? Sfurtunatamente, ùn hè micca cusì simplice, è l'avemu vistu in dettagliu dopu. Avà cuntrollemu un altru prucessu di implementazione abbastanza cumuni - implementazione blu verde.

Avete mai intesu parlà implementazione blu verde? Cloud Foundry rende questu estremamente faciule. Basta à fighjà stu articulu, induve descrivimu questu in più detail. Per riassume brevemente, vi ricurdemu cumu fà una implementazione blu verde:

  • assicuratevi chì duie copie di u vostru codice di produzzione ("blu" è "verde") funzionanu;
  • diretta tuttu u trafficu à l'ambienti turchinu, i.e. cusì chì l'URL di produzzione puntanu quì;
  • implementà è pruvà tutti i cambiamenti di l'applicazione in un ambiente verde;
  • cambia l'urls da l'ambiente blu à u verde

A implementazione di u verde blu hè un approcciu chì vi permette di intruduce facilmente e funzioni novi senza preoccupassi di a rottura di a produzzione. Questu hè duvuta à u fattu chì ancu s'ellu succede qualcosa, pudete facilmente retrocede à l'ambiente precedente semplicemente "fendu un interruttore".

Dopu avè lettu tuttu ciò chì sopra, pudete dumandà a quistione: Chì ci hè u tempu d'inattività zero à fà cù l'implementazione blu verde?

Ebbè, anu assai in cumunu, postu chì mantene duie copie di u stessu ambiente richiede u doppiu sforzu per mantene. Hè per quessa chì certi squadre pretendenu Martin Fowler, seguitate una variazione di stu approcciu:

Un'altra opzione hè di utilizà a stessa basa di dati, creendu switches blu-verde per u web è i strati di duminiu. In questu approcciu, a basa di dati pò esse spessu un prublema, soprattuttu quandu avete bisognu di cambià u so schema per sustene una nova versione di u software.

È quì ghjunghjemu à u prublema principali in questu articulu. Base di dati. Fighjemu un altru sguardu à sta frasa.

fà una migrazione di basa di dati.

Avà vi tocca à dumandà sè stessu a quistione - chì si u cambiamentu di basa di dati ùn hè micca retrocompatibile? A mo prima versione di l'app ùn si romperà? In fatti, questu hè esattamente ciò chì succederà ...

Dunque, ancu malgradu l'enorme benefici di u tempu di inattività zero / implementazione di u verde blu, e cumpagnie tendenu à seguità u seguente prucessu più sicuru per implementà e so applicazioni:

  • preparanu un pacchettu cù una nova versione di l'applicazione
  • chjude una applicazione in esecuzione
  • eseguite scripts per migrà a basa di dati
  • implementate è lanciate una nova versione di l'applicazione

In questu articulu, detagliaremu cumu pudete travaglià cù a vostra basa di dati è u codice per prufittà di a implementazione zero downtime.

Problemi di basa di dati

Se tenete una applicazione senza statu chì ùn guarda micca dati in a basa di dati, pudete uttene u tempu di inattività zero immediatamente. Sfortunatamente, a maiò parte di u software hà bisognu di almacenà e dati in un locu. Hè per quessa chì duvete pensà duie volte prima di fà qualsiasi cambiamenti à u circuitu. Prima di entrà in i dettagli di cumu cambià u schema in modu chì l'implementazione senza tempi di inattività hè pussibule, concentremu prima nantu à u schema di versione.

Schema di versione

In questu articulu avemu da aduprà Flyway cum'è strumentu di cuntrollu di versione (ca. Traduzzione: si parla di migrazioni di basa di dati). Naturalmente, scriveremu ancu una applicazione Spring Boot chì hà un supportu Flyway integratu è eseguisce a migrazione di schema mentre stabilisce u cuntestu di l'applicazione. Quandu utilizate Flyway, pudete almacenà script di migrazione in u vostru cartulare di prughjetti (per difettu in classpath:db/migration). Quì si pò vede un esempiu di tali schedari migrazzioni

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

In questu esempiu, vedemu 4 script di migrazione chì, se micca eseguitu prima, seranu eseguiti unu dopu à l'altru quandu l'applicazione principia. Fighjemu unu di i schedari (V1__init.sql) per esempiu.

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

Tuttu hè perfettamente auto-esplicativu: pudete aduprà SQL per definisce cumu a vostra basa di dati deve esse mudificata. Per più infurmazione nantu à Spring Boot è Flyway, verificate Spring Boot Docs.

Utilizendu un strumentu di cuntrollu di fonte cù Spring Boot, uttene 2 grandi benefici:

  • separate i cambiamenti di basa di dati da i cambiamenti di codice
  • A migrazione di basa di dati si faci cù u rollout di a vostra applicazione, i.e. u vostru prucessu di implementazione hè simplificatu

Risoluzione di prublemi di basa di dati

In a prossima seccione di l'articulu, avemu da fucalizza nantu à guardà dui approcci à i cambiamenti di basa di dati.

  • incompatibilità à l'inversu
  • cumpatibilità retrocede

U primu serà cunsideratu cum'è un avvirtimentu chì ùn deve micca esse realizatu un dispiegamentu di downtime zero senza preparazione preliminare ... U sicondu offre una suluzione nantu à cumu pudete fà una implementazione senza downtime è à u stessu tempu mantene a cumpatibilità inversa.

U nostru prughjettu chì avemu da travaglià serà una semplice applicazione Spring Boot Flyway chì hà Person с first_name и last_name in a basa di dati (ca. traduzzione: Person hè una tavola è first_name и last_name - Quessi sò i campi in questu). Vulemu rinominà last_name в surname.

Assunzioni

Prima di entre in i dettagli, ci sò un paru di supposizioni chì avemu bisognu di fà nantu à e nostre applicazioni. U risultatu principale chì vulemu ottene serà un prucessu abbastanza simplice.

A nota. Business PRO-TIP. I prucessi di simplificazione ponu salvà assai soldi nantu à u supportu (quantu più persone avete travagliatu per a vostra cumpagnia, più soldi pudete salvà)!

Ùn ci hè bisognu di rollback a basa di dati

Questu simplifica u prucessu di implementazione (alcuni rollbacks di basa di dati sò quasi impussibili, cum'è rollback di eliminazione). Preferimu ritruvà solu l'applicazioni. In questu modu, ancu s'è avete diverse basa di dati (per esempiu, SQL è NoSQL), u vostru pipeline di implementazione parerà u listessu.

Deve esse SEMPRE pussibule di rinvià l'applicazione una versione (micca più)

U rollback deve esse fattu solu quandu hè necessariu. Se ci hè un bug in a versione attuale chì ùn hè micca facilmente riparatu, duvemu esse capace di vultà à l'ultima versione di travagliu. Assumimu chì questa ultima versione di travagliu hè a precedente. Mantene u codice è a compatibilità di a basa di dati per più di un rollout seria estremamente difficiule è caru.

A nota. Per una leggibilità più grande, in questu articulu cambieremu a versione maiò di l'applicazione.

Passu 1: Statu iniziale

Versione App: 1.0.0
Versione DB: v1

cumentu

Questu serà u statu iniziale di l'applicazione.

Cambiamenti di basa di dati

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

U codice cambia

L'applicazione guarda i dati di a persona 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
                + "]";
    }
}

Rinominazione di colonna incompatibile à l'inversu

Fighjemu un esempiu di cumu cambià u nome di una colonna:

Attenti. L'esempiu seguente romperà intenzionalmente e cose. Mostremu questu per dimustrà u prublema di cumpatibilità di basa di dati.

Versione App: 2.0.0.BAD

Versione DB: v2bad

cumentu

I cambiamenti attuali ùn ci permettenu micca di eseguisce duie istanze (vechju è novu) à u stessu tempu. Cusì, l'implementazione di zero downtime serà difficiule di ottene (se l'assunzioni sò cunsiderate, hè veramente impussibile).

Test A/B

A situazione attuale hè chì avemu una versione di l'applicazione 1.0.0, implementatu in pruduzzione, è basa di dati v1. Avemu bisognu di implementà una seconda istanza di l'applicazione, versione 2.0.0.BAD, è aghjurnà a basa di dati à v2bad.

Passi:

  1. una nova istanza di l'applicazione versione hè implementata 2.0.0.BADchì aghjurnà a basa di dati v2bad
  2. in a basa di dati v2bad culonna last_name ùn esiste più - hè statu cambiatu in surname
  3. A basa di dati è l'aghjurnamentu di l'applicazioni anu successu è certi casi sò in esecuzione 1.0.0, altri - in 2.0.0.BAD. Tuttu hè cunnessu à a basa di dati v2bad
  4. tutti i casi di a versione 1.0.0 principià à scaccià l'errori perchè pruvaranu à inserisce dati in a colonna last_namechì ùn esiste più
  5. tutti i casi di a versione 2.0.0.BAD travaglià senza prublemi

Comu pudete vede, se facemu cambiamenti incompatibili à l'inversu à a basa di dati è l'applicazione, a prova A / B hè impussibile.

Rollback di l'applicazione

Assumimu chì dopu avè pruvatu à fà una implementazione A/B (ca. per.: l'autore probabilmente vulia dì A / B testing quì) avemu decisu chì avemu bisognu di rinvià l'applicazione à a versione 1.0.0. Diciamu chì ùn vulemu micca rollback a basa di dati.

Passi:

  1. fermemu l'istanza di l'applicazione di versione 2.0.0.BAD
  2. a basa di dati hè sempre v2bad
  3. dapoi a versione 1.0.0 ùn capisce micca ciò chì hè surname, videremu errori
  4. l'infernu s'hè lampatu, ùn pudemu più vultà

Comu pudete vede, se facemu cambiamenti incompatibili à a basa di dati è l'applicazione, ùn pudemu micca ritruvà à a versione precedente.

Logs di esecuzione di script

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

Cambiamenti di basa di dati

Scrittura di migrazione chì rinomina last_name в surname

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

Scrittura chì rinomina last_name.

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

U codice cambia

Avemu cambiatu u nome di u campu lastName nantu surname.

Rinominazione di una colonna in una manera retrocompatibile

Questa hè a situazione più cumuna chì pudemu truvà. Avemu bisognu di fà cambiamenti incompatibili à l'inversu. Avemu digià dimustratu chì per a implementazione di zero-downtime, ùn duvemu micca solu applicà a migrazione di basa di dati senza passi supplementari. In questa sezione di l'articulu, realizaremu 3 implementazioni di l'applicazione inseme cù migrazioni di basa di dati per ottene u risultatu desideratu mantenendu a cumpatibilità inversa.

A nota. Ricurdativi chì avemu una basa di dati di versione v1. Contene colonne first_name и last_name. Avemu da cambià last_name nantu surname. Avemu ancu a versione di l'app 1.0.0, chì ùn hè ancu usatu surname.

Passu 2: aghjunghje u cognome

Versione App: 2.0.0
Versione DB: v2

cumentu

Aghjunghjendu una nova colonna è copiendu u so cuntenutu, creamu cambiamenti di basa di dati cumpatibili in retrocede. À u listessu tempu, se rollback u JAR o avè un vechju JAR in esecuzione, ùn si romperà durante l'esekzione.

Avemu lanciatu una nova versione

Passi:

  1. realizà una migrazione di basa di dati per creà una nova colonna surname. Avà a vostra versione DB v2
  2. cupià dati da last_name в surname. Attentichì s'è vo avete assai di sti dati, tu avissi a cunsiderà migrazione batch!
  3. scrivite u codice induve sò usati TAMBI и новыйe u vechju culonna. Avà a vostra versione di l'app 2.0.0
  4. leghje u valore da a colonna surname, s'ellu ùn hè micca null, o da last_name, sì a surname micca specificatu. Pudete sguassà getLastName() da u codice, postu ch'ellu sarà output null quandu si ritruvà a vostra applicazione da 3.0.0 à 2.0.0.

Sè vo aduprate Spring Boot Flyway, sti dui passi seranu realizati durante l'iniziu di a versione 2.0.0 applicazioni. Se eseguite manualmente l'utillita di versione di basa di dati, avete da fà duie cose diverse per fà questu (prima aghjurnà a versione db manualmente è dopu implementà a nova applicazione).

Hè impurtante. Ricurdativi chì a colonna di novu creatu Ùn deve micca per esse Micca nulu. Se fate un rollback, l'antica applicazione ùn cunnosci micca a nova colonna è ùn l'installarà micca durante Insert. Ma si aghjunghje sta limitazione è u vostru db serà v2, questu serà bisognu di stabilisce u valore di a nova colonna. Chì portarà à violazioni di e restrizioni.

Hè impurtante. Tu avissi a caccià u metudu getLastName(), perchè in a versione 3.0.0 Ùn ci hè micca cuncettu di una colonna in u codice last_name. Questu significa chì null serà stabilitu quì. Pudete lascià u metudu è aghjunghje cuntrolli per null, ma una suluzione assai megliu seria di assicurà chì in a logica getSurname() avete sceltu u valore currettu non-zero.

Test A/B

A situazione attuale hè chì avemu una versione di l'applicazione 1.0.0, implementatu nantu à a produzzione, è a basa di dati in v1. Avemu bisognu di implementà una seconda istanza di l'applicazione di versione 2.0.0chì aghjurnà a basa di dati à v2.

Passi:

  1. una nova istanza di l'applicazione versione hè implementata 2.0.0chì aghjurnà a basa di dati v2
  2. intantu alcune dumande sò state trattate da istanze di versione 1.0.0
  3. l'aghjurnamentu hè successu è avete parechje istanze in esecuzione di l'applicazione di versione 1.0.0 è altre versioni 2.0.0. Tutti cumunicanu cù a basa di dati in v2
  4. versione 1.0.0 ùn usa micca a colonna di u cognome in a basa di dati, ma a versione 2.0.0 usa. Ùn interferiscenu micca cù l'altri, è ùn deve esse micca errore.
  5. versione 2.0.0 almacenà e dati in a colonna antica è nova, assicurendu a cumpatibilità retrocede

Hè impurtante. Sì avete qualchì dumanda chì conta l'articuli basati nantu à i valori da a colonna antica / nova, duvete ricurdà chì avà avete valori duplicati (probabilmente sò sempre migrati). Per esempiu, sè vo vulete cuntà u nùmeru d'utilizatori chì u so cognome (qualunque sia chjamatu a colonna) cuminciò cù a lettera A, dopu finu à chì a migrazione di dati hè cumpletata (oldnew colonna) pudete avè dati inconsistenti se dumandate una nova colonna.

Rollback di l'applicazione

Avà avemu a versione di l'app 2.0.0 è basa di dati in v2.

Passi:

  1. retrocede a vostra applicazione à a versione 1.0.0.
  2. versione 1.0.0 ùn usa micca una colonna in a basa di dati surname, cusì u rollback deve esse successu

DB cambia

A basa di dati cuntene una colonna chjamata last_name.

Scrittura fonte Flyway:

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

Aghjunghjite script surname.

Attenti. Ricurdatevi chì ùn pudete micca aghjunghje alcuna limitazione NOT NULL à a colonna chì aghjunghje. Se rollback u JAR, l'antica versione ùn averà micca idea di a colonna aghjuntu è a mette automaticamente in NULL. Se ci hè una tale limitazione, l'antica applicazione si romperà solu.

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

U codice cambia

Avemu guardatu dati cum'è last_name, è in surname. À u listessu tempu avemu lettu da last_name, postu chì sta colonna hè a più pertinente. Durante u prucessu di implementazione, alcune richieste ponu esse trattate da una istanza di applicazione chì ùn hè micca stata aghjurnata.

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

Passu 3: Eliminazione last_name da u codice

Versione App: 3.0.0

Versione DB:v3

cumentu

Nota per.: Apparentemente, in l'articulu uriginale l'autore hà copiatu per errore u testu di stu bloccu da u passu 2. À questu passu, i cambiamenti deve esse fatti in u codice di l'applicazione destinatu à caccià a funziunalità chì usa a colonna. last_name.

Agghiuncennu una nova colonna è copiendu u so cuntenutu, avemu creatu cambiamenti di basa di dati cumpatibili retrocede. Inoltre, se rollback u JAR o avè un vechju JAR in esecuzione, ùn si romperà micca durante l'esecuzione.

Rollback di l'applicazione

Attualmente avemu a versione di l'app 3.0.0 è basa di dati v3. Versione 3.0.0 ùn salva micca dati à last_name. Questu significa chì in surname l'infurmazione più aghjurnata hè almacenata.

Passi:

  1. retrocede a vostra applicazione à a versione 2.0.0.
  2. versione 2.0.0 usu è last_name и surname.
  3. versione 2.0.0 piglià surname, s'ellu ùn hè micca zero, altrimenti -last_name

Cambiamenti di basa di dati

Ùn ci sò micca cambiamenti strutturali in a basa di dati. U script seguente hè eseguitu per fà a migrazione finale di i vechji dati:

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

U codice cambia

Nota per.: A descrizzione di stu bloccu hè stata ancu copiata per errore da l'autore da u passu 2. In cunfurmità cù a logica di l'articulu, i cambiamenti in u codice in questu passu duveranu esse destinati à caccià da ellu elementi chì travaglianu cù a colonna. last_name.

Avemu guardatu dati cum'è last_name, è in surname. Inoltre, leghjemu da a colonna last_name, postu chì hè u più pertinente. Durante u prucessu di implementazione, alcune dumande ponu esse trattate da una istanza chì ùn hè micca stata aghjurnata.

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

Passu 4: Eliminazione last_name da a basa di dati

Versione App: 4.0.0

Versione DB: v4

cumentu

A causa di u fattu chì u codice versione 3.0.0 ùn hà micca usatu a colonna last_name, nunda di male ùn succederà durante l'esekzione se vultemu 3.0.0 dopu avè eliminatu una colonna da a basa di dati.

Logs di esecuzione di script

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 cambia

Relativamente v3 avemu solu sguassà a colonna last_name è aghjunghje restrizioni mancanti.

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

U codice cambia

Ùn ci sò micca cambiamenti à u codice.

cunchiusioni

Avemu applicatu cù successu un cambiamentu di nome di colonna incompatibile à l'inversu eseguendu parechje implementazioni retrocompatibili. Quì sottu hè un riassuntu di l'azzioni realizati:

  1. implementazione di a versione di l'applicazione 1.0.0 с v1 schema di basa di dati (nome di a colonna = last_name)
  2. implementazione di a versione di l'applicazione 2.0.0, chì guarda i dati in last_name и surname. L'applicazione leghje da last_name. A basa di dati hè in versione v2chì cuntenenu colonne cum'è last_name, è surname. surname hè una copia di last_name. (NOTA: Questa colonna ùn deve micca avè una limitazione micca nulla)
  3. implementazione di a versione di l'applicazione 3.0.0, chì guarda solu dati in surname è leghje da u cognome. In quantu à a basa di dati, l'ultima migrazione hè accaduta last_name в surname. Ancu una limitazione Micca nulu ritirata da last_name. A basa di dati hè avà in versione v3
  4. implementazione di a versione di l'applicazione 4.0.0 - ùn ci sò micca cambiamenti à u codice. Impiegazione di basa di dati v4, chì sguassate last_name. Quì pudete aghjunghje ogni limitazione mancante à a basa di dati.

Seguendu stu approcciu, pudete sempre retrocede una versione senza rompe a compatibilità di basa di dati / applicazioni.

codice

Tuttu u codice utilizatu in questu articulu hè dispunibule à Github. Sottu hè una descrizzione supplementaria.

Prughjetti

Dopu a clonazione di u repository, vi vede a seguente struttura di cartulare.

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

Pudete eseguisce i scripts descritti in i scripts quì sottu, chì dimustraranu cambiamenti incompatibili è incompatibili à a basa di dati.

Per vede u casu cù cambiamenti cumpatibili in daretu, corre :

./scripts/scenario_backward_compatible.sh

È à vede casu cù cambiamenti incompatibili in daretu, corre :

./scripts/scenario_backward_incompatible.sh

Spring Boot Sample Flyway

Tutti l'esempii sò presi da Spring Boot Sample Flyway.

Pudete piglià un ochju http://localhost:8080/flyway, ci hè una lista di scripts.

Questu esempiu include ancu a cunsola H2 (at http://localhost:8080/h2-console) cusì pudete vede u statutu di a basa di dati (URL jdbc predeterminatu hè jdbc:h2:mem:testdb).

cliccà

Leghjite ancu altri articuli nantu à u nostru blog:

Source: www.habr.com

Add a comment