Desplegament i bases de dades sense temps d'inactivitat

Desplegament i bases de dades sense temps d'inactivitat

En aquest article s'explica detalladament com resoldre problemes de compatibilitat de bases de dades durant el desplegament. Us direm què pot passar amb les vostres aplicacions de producció si intenteu desplegar-les sense una preparació prèvia. A continuació, passarem per les etapes del cicle de vida de l'aplicació que es requereixen per tenir zero temps d'inactivitat (aprox. carril: més enllà - zero temps d'inactivitat). El resultat de les nostres operacions serà aplicar el canvi de base de dades incompatible cap enrere d'una manera compatible cap enrere.

Si voleu entendre els exemples de codi de l'article, podeu trobar-los a GitHub.

Introducció

Desplegament sense temps d'inactivitat

Quina mística desplegament de temps d'inactivitat zero? Podeu dir que és quan la vostra aplicació es desplega de manera que podeu introduir amb èxit una nova versió de l'aplicació a producció, mentre que l'usuari no nota la seva indisponibilitat. Des de la perspectiva de l'usuari i de l'empresa, aquest és el millor escenari de desplegament possible perquè permet introduir noves funcions i corregir errors sense interrupcions.

Com aconseguir-ho? Hi ha diverses maneres, aquí en teniu una:

  • implementeu la versió núm. 1 del vostre servei
  • realitzar una migració de la base de dades
  • Desplegueu la versió 2 del vostre servei en paral·lel amb la versió 1
  • tan aviat com veieu que la versió núm. 2 funciona com cal, elimineu la versió núm. 1
  • fet!

Fàcil, no? Malauradament, no és tan senzill, i ho veurem en detall més endavant. Ara comprovem un altre procés de desplegament força comú: el desplegament blau verd.

Alguna vegada n’heu sentit a parlar desplegament blau verd? Cloud Foundry ho fa molt fàcil. Només cal mirar aquest article, on ho descriurem amb més detall. Per resumir breument, us recordem com fer un desplegament blau verd:

  • Assegureu-vos que funcionin dues còpies del vostre codi de producció ("blau" i "verd");
  • dirigir tot el trànsit a l'entorn blau, és a dir. de manera que els URL de producció hi apunten;
  • desplegar i provar tots els canvis d'aplicació en un entorn verd;
  • canviar les URL de l'entorn blau a verd

El desplegament blau verd és un enfocament que us permet introduir noves funcions fàcilment sense preocupar-vos de l'interrupció de la producció. Això es deu al fet que, fins i tot si passa alguna cosa, podeu tornar fàcilment a l'entorn anterior simplement "prement un interruptor".

Després de llegir tot l'anterior, podeu fer la pregunta: Què té a veure el temps d'inactivitat zero amb el desplegament de Blue Green?

Bé, tenen força en comú, ja que mantenir dues còpies del mateix entorn requereix el doble d'esforç per mantenir-les. És per això que alguns equips afirmen Martin Fowler, seguiu una variació d'aquest enfocament:

Una altra opció és utilitzar la mateixa base de dades, creant interruptors blau-verd per a les capes web i de domini. En aquest enfocament, la base de dades sovint pot ser un problema, especialment quan necessiteu canviar el seu esquema per donar suport a una nova versió del programari.

I aquí arribem al problema principal d'aquest article. Base de dades. Fem una altra ullada a aquesta frase.

realitzar una migració de la base de dades.

Ara us heu de fer la pregunta: què passa si el canvi de base de dades no és compatible cap enrere? No es trencarà la meva primera versió de l'aplicació? De fet, això és exactament el que passarà...

Per tant, tot i els enormes avantatges del desplegament de temps d'inactivitat zero/verd blau, les empreses solen seguir el següent procés més segur per desplegar les seves aplicacions:

  • preparar un paquet amb una nova versió de l'aplicació
  • tancar una aplicació en execució
  • executar scripts per migrar la base de dades
  • desplegar i llançar una nova versió de l'aplicació

En aquest article, detallarem com podeu treballar amb la vostra base de dades i el codi per aprofitar el desplegament sense temps d'inactivitat.

Problemes de bases de dades

Si teniu una aplicació sense estat que no emmagatzema cap dada a la base de dades, podeu obtenir un desplegament sense temps d'inactivitat immediatament. Malauradament, la majoria del programari necessita emmagatzemar dades en algun lloc. És per això que hauríeu de pensar-ho dues vegades abans de fer qualsevol canvi al circuit. Abans d'entrar en els detalls de com canviar l'esquema perquè sigui possible el desplegament sense temps d'inactivitat, primer centrem-nos en l'esquema de versions.

Esquema de versions

En aquest article utilitzarem Flyway com a eina de control de versions (aprox. Traducció: estem parlant de migracions de bases de dades). Naturalment, també escriurem una aplicació Spring Boot que tingui suport integrat per Flyway i realitzarà la migració d'esquemes mentre configurem el context de l'aplicació. Quan utilitzeu Flyway, podeu emmagatzemar scripts de migració a la carpeta de projectes (per defecte a classpath:db/migration). Aquí podeu veure un exemple d'aquests fitxers de migració

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

En aquest exemple veiem 4 scripts de migració que, si no s'executen prèviament, s'executaran un darrere l'altre quan s'iniciï l'aplicació. Vegem un dels fitxers (V1__init.sql) com un exemple.

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

Tot s'explica perfectament: podeu utilitzar SQL per definir com s'ha de modificar la vostra base de dades. Per obtenir més informació sobre Spring Boot i Flyway, consulteu Spring Boot Docs.

Si utilitzeu una eina de control de fonts amb Spring Boot, obteniu dos grans avantatges:

  • separeu els canvis de base de dades dels canvis de codi
  • La migració de la base de dades es produeix juntament amb el llançament de la vostra aplicació, és a dir. el vostre procés de desplegament es simplifica

Resolució de problemes de la base de dades

A la següent secció de l'article, ens centrarem a analitzar dos enfocaments dels canvis a la base de dades.

  • incompatibilitat cap enrere
  • retrocompatibilitat

El primer es considerarà com un avís que no s'ha de realitzar un desplegament sense temps d'inactivitat sense una preparació prèvia... El segon ofereix una solució sobre com es pot realitzar un desplegament sense temps d'inactivitat i alhora mantenir la compatibilitat enrere.

El nostre projecte en el qual treballarem serà una senzilla aplicació Spring Boot Flyway que tingui Person с first_name и last_name a la base de dades (aprox. traducció: Person és una taula i first_name и last_name - aquests són els camps que hi ha). Volem canviar el nom last_name в surname.

Hipòtesis

Abans d'entrar en els detalls, hi ha un parell de suposicions que hem de fer sobre les nostres aplicacions. El principal resultat que volem aconseguir serà un procés bastant senzill.

La nota. PRO-TIP empresarial. Simplificar els processos us pot estalviar molts diners en suport (com més persones treballeu per a la vostra empresa, més diners podreu estalviar)!

No cal revertir la base de dades

Això simplifica el procés de desplegament (algunes de les reversions de bases de dades són gairebé impossibles, com ara la recuperació de la supressió). Preferim revertir només les aplicacions. D'aquesta manera, fins i tot si teniu bases de dades diferents (per exemple, SQL i NoSQL), la vostra canalització de desplegament tindrà el mateix aspecte.

SEMPRE ha de ser possible retrocedir l'aplicació una versió (no més)

La recuperació només s'ha de fer quan sigui necessari. Si hi ha un error a la versió actual que no es soluciona fàcilment, hauríem de poder tornar a la darrera versió de treball. Suposem que aquesta darrera versió de treball és l'anterior. Mantenir la compatibilitat del codi i la base de dades per a més d'un llançament seria extremadament difícil i costós.

La nota. Per a una major llegibilitat, en aquest article canviarem la versió principal de l'aplicació.

Pas 1: estat inicial

Versió de l'aplicació: 1.0.0
Versió de DB: v1

Comentari

Aquest serà l'estat inicial de l'aplicació.

Canvis de bases de dades

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

Canvis de codi

L'aplicació emmagatzema les dades de la 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
                + "]";
    }
}

Canvi de nom de columna incompatible cap enrere

Vegem un exemple de com canviar el nom d'una columna:

Atenció. L'exemple següent trencarà coses intencionadament. Mostrem això per demostrar el problema de la compatibilitat de bases de dades.

Versió de l'aplicació: 2.0.0.BAD

Versió de DB: v2bad

Comentari

Els canvis actuals NO ens permeten executar dues instàncies (antiga i nova) alhora. Per tant, el desplegament de temps d'inactivitat zero serà difícil d'aconseguir (si es tenen en compte les hipòtesis, és realment impossible).

Prova A/B

La situació actual és que tenim una versió de l'aplicació 1.0.0, desplegat en producció i base de dades v1. Hem de desplegar una segona instància de l'aplicació, versió 2.0.0.BADi actualitzeu la base de dades a v2bad.

Passos:

  1. es desplega una nova instància de la versió de l'aplicació 2.0.0.BADque actualitza la base de dades v2bad
  2. a la base de dades v2bad columna last_name ja no existeix: es va canviar a surname
  3. L'actualització de la base de dades i de l'aplicació ha tingut èxit i s'estan executant algunes instàncies 1.0.0, altres - a 2.0.0.BAD. Tot està connectat a la base de dades v2bad
  4. totes les instàncies de la versió 1.0.0 començaran a llançar errors perquè intentaran inserir dades a la columna last_nameque ja no existeix
  5. totes les instàncies de la versió 2.0.0.BAD funcionarà sense problemes

Com podeu veure, si fem canvis incompatibles cap enrere a la base de dades i l'aplicació, les proves A/B són impossibles.

Retrocés de l'aplicació

Suposem que després d'intentar fer un desplegament A/B (aprox. per.: l'autor probablement volia dir proves A/B aquí) vam decidir que hem de tornar l'aplicació a la versió 1.0.0. Suposem que no volem revertir la base de dades.

Passos:

  1. aturem la instància de l'aplicació de versió 2.0.0.BAD
  2. la base de dades encara està v2bad
  3. des de la versió 1.0.0 no entén què és surname, veurem errors
  4. s'ha desfet l'infern, ja no podem tornar

Com podeu veure, si fem canvis incompatibles cap enrere a la base de dades i l'aplicació, no podem tornar a la versió anterior.

Registres d'execució de scripts

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

Canvis de bases de dades

Script de migració que canvia el nom last_name в surname

Guió font 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');

Guió que canvia el nom last_name.

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

Canvis de codi

Hem canviat el nom del camp lastName en surname.

Canviar el nom d'una columna d'una manera compatible amb les versions anteriors

Aquesta és la situació més habitual que ens podem trobar. Hem de fer canvis incompatibles cap enrere. Ja hem demostrat que per a un desplegament sense temps d'inactivitat, no hauríem d'aplicar simplement la migració de bases de dades sense passos addicionals. En aquesta secció de l'article, realitzarem 3 desplegaments de l'aplicació juntament amb migracions de bases de dades per aconseguir el resultat desitjat mantenint la compatibilitat enrere.

La nota. Recordeu que tenim una base de dades de versions v1. Conté columnes first_name и last_name. Hem de canviar last_name en surname. També tenim versió de l'aplicació 1.0.0, que encara no s'utilitza surname.

Pas 2: afegiu el cognom

Versió de l'aplicació: 2.0.0
Versió de DB: v2

Comentari

En afegir una nova columna i copiar-ne el contingut, creem canvis de base de dades compatibles enrere. Al mateix temps, si retrocedim el JAR o tenim un JAR antic en funcionament, no es trencarà durant l'execució.

Estem llançant una nova versió

Passos:

  1. realitzar una migració de base de dades per crear una columna nova surname. Ara la vostra versió de base de dades v2
  2. copiar dades de last_name в surname. Preste atencióque si teniu moltes d'aquestes dades, hauríeu de considerar la migració per lots!
  3. escriure el codi on s'utilitzen TOTS DOS и nouI el vell columna. Ara la versió de l'aplicació 2.0.0
  4. llegiu el valor de la columna surname, si no ho és null, o de last_name, si a surname sense especificar. Podeu esborrar getLastName() del codi, ja que sortirà null en revertir l'aplicació des de 3.0.0 до 2.0.0.

Si utilitzeu Spring Boot Flyway, aquests dos passos es realitzaran durant l'inici de la versió 2.0.0 aplicacions. Si executeu l'eina de control de versions de la base de dades manualment, haureu de fer dues coses diferents per fer-ho (primer actualitzeu la versió de base de dades manualment i després implementeu la nova aplicació).

És important. Recordeu que la columna de nova creació NO HA DE ser NO NUL. Si feu una recuperació, l'aplicació antiga no coneix la nova columna i no l'instal·larà durant Insert. Però si afegiu aquesta restricció i la vostra base de dades serà v2, això requerirà establir el valor de la nova columna. El que comportarà violacions de les restriccions.

És important. Hauríeu d'eliminar el mètode getLastName(), perquè a la versió 3.0.0 No hi ha cap concepte de columna al codi last_name. Això vol dir que s'establirà null allà. Podeu deixar el mètode i afegir comprovacions null, però una solució molt millor seria assegurar-se que a la lògica getSurname() heu seleccionat el valor diferent de zero correcte.

Prova A/B

La situació actual és que tenim una versió de l'aplicació 1.0.0, desplegat en producció i la base de dades a v1. Hem de desplegar una segona instància de l'aplicació de versió 2.0.0que actualitzarà la base de dades v2.

Passos:

  1. es desplega una nova instància de la versió de l'aplicació 2.0.0que actualitza la base de dades v2
  2. mentrestant algunes sol·licituds eren processades per instàncies de versió 1.0.0
  3. l'actualització ha tingut èxit i teniu diverses instàncies en execució de l'aplicació de versió 1.0.0 i altres versions 2.0.0. Tothom es comunica amb la base de dades v2
  4. versió 1.0.0 no utilitza la columna del cognom a la base de dades, sinó la versió 2.0.0 usos. No interfereixen entre ells i no hi hauria d'haver errors.
  5. versió 2.0.0 emmagatzema dades tant a la columna antiga com a la nova, garantint la compatibilitat amb les versions anteriors

És important. Si teniu consultes que comptabilitzen elements basats en valors de la columna antiga/nova, haureu de recordar que ara teniu valors duplicats (el més probable és que encara estiguin migrant). Per exemple, si voleu comptar el nombre d'usuaris el cognom dels quals (sigui com es digui la columna) va començar amb la lletra Ai després fins que s'hagi completat la migració de dades (oldnew columna) és possible que tingueu dades incoherents si consulteu una columna nova.

Retrocés de l'aplicació

Ara tenim la versió de l'aplicació 2.0.0 i base de dades a v2.

Passos:

  1. retrocedeix l'aplicació a la versió 1.0.0.
  2. versió 1.0.0 no utilitza cap columna a la base de dades surname, de manera que la recuperació hauria de tenir èxit

Canvis de DB

La base de dades conté una columna anomenada last_name.

Guió font de 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');

Afegeix script surname.

Atenció. Recordeu que NO PODEU AFEGIR cap restricció NOT NULL a la columna que esteu afegint. Si desfer el JAR, la versió antiga no tindrà ni idea de la columna afegida i automàticament l'establirà a NULL. Si hi ha aquesta limitació, l'aplicació antiga simplement es trencarà.

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

Canvis de codi

Emmagatzemem dades com last_name, i a surname. Al mateix temps llegim de last_name, ja que aquesta columna és la més rellevant. Durant el procés de desplegament, algunes sol·licituds poden haver estat processades per una instància d'aplicació que encara no s'ha actualitzat.

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

Pas 3: eliminació del cognom del codi

Versió de l'aplicació: 3.0.0

Versió de DB:v3

Comentari

Nota per.: Pel que sembla, a l'article original l'autor va copiar per error el text d'aquest bloc del pas 2. En aquest pas, s'haurien de fer canvis en el codi de l'aplicació amb l'objectiu d'eliminar la funcionalitat que utilitza la columna last_name.

En afegir una nova columna i copiar-ne el contingut, hem creat canvis de base de dades compatibles enrere. A més, si retrocedim el JAR o tenim un JAR antic en funcionament, no es trencarà durant l'execució.

Retrocés de l'aplicació

Actualment tenim la versió de l'aplicació 3.0.0 i base de dades v3. Versió 3.0.0 no desa dades a last_name. Això vol dir que a surname s'emmagatzema la informació més actualitzada.

Passos:

  1. retrocedeix l'aplicació a la versió 2.0.0.
  2. versió 2.0.0 usos i last_name и surname.
  3. versió 2.0.0 es durà a surname, si no és zero, en cas contrari -last_name

Canvis de bases de dades

No hi ha canvis estructurals a la base de dades. S'executa l'script següent per realitzar la migració final de les dades antigues:

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

Canvis de codi

Nota per.: La descripció d'aquest bloc també va ser copiada per error per l'autor del pas 2. D'acord amb la lògica de l'article, els canvis en el codi en aquest pas haurien d'estar dirigits a eliminar-ne els elements que funcionen amb la columna last_name.

Emmagatzemem dades com last_name, i a surname. A més, llegim de la columna last_name, ja que és el més rellevant. Durant el procés de desplegament, algunes sol·licituds poden ser processades per una instància que encara no s'ha actualitzat.

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

Pas 4: eliminació de last_name de la base de dades

Versió de l'aplicació: 4.0.0

Versió de DB: v4

Comentari

A causa del fet que el codi de versió 3.0.0 no va utilitzar la columna last_name, no passarà res dolent durant l'execució si tornem a 3.0.0 després d'eliminar una columna de la base de dades.

Registres d'execució de scripts

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

Canvis de DB

Relativament v3 només eliminem la columna last_name i afegiu les restriccions que falten.

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

Canvis de codi

No hi ha canvis al codi.

Sortida

Hem aplicat correctament un canvi de nom de columna incompatible amb les versions anteriors mitjançant la realització de diversos desplegaments compatibles amb les versions anteriors. A continuació es mostra un resum de les accions realitzades:

  1. desplegament de la versió de l'aplicació 1.0.0 с v1 esquema de base de dades (nom de columna = last_name)
  2. desplegament de la versió de l'aplicació 2.0.0, que emmagatzema les dades last_name и surname. L'aplicació llegeix des de last_name. La base de dades està en versió v2que conté columnes com last_nameI surname. surname és una còpia de last_name. (NOTA: aquesta columna no ha de tenir una restricció no nul·la)
  3. desplegament de la versió de l'aplicació 3.0.0, que només emmagatzema dades surname i es llegeix del cognom. Pel que fa a la base de dades, s'està produint l'última migració last_name в surname. També una limitació NO NUL retirat de last_name. La base de dades ja està en versió v3
  4. desplegament de la versió de l'aplicació 4.0.0 - no es fan canvis al codi. Desplegament de bases de dades v4, que elimina last_name. Aquí podeu afegir qualsevol restricció que falti a la base de dades.

Seguint aquest enfocament, sempre podeu revertir una versió sense trencar la compatibilitat de base de dades/aplicació.

Codi

Tot el codi utilitzat en aquest article està disponible a Github. A continuació hi ha una descripció addicional.

Projectes

Després de clonar el dipòsit, veureu l'estructura de carpetes següent.

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

Podeu executar els scripts descrits als scripts següents, que demostraran els canvis compatibles amb les versions anteriors i incompatibles a la base de dades.

Veure el cas amb canvis compatibles enrere, correr:

./scripts/scenario_backward_compatible.sh

I a veure cas amb canvis incompatibles cap enrere, correr:

./scripts/scenario_backward_incompatible.sh

Flyway de mostra de bota de primavera

Tots els exemples estan extrets de Spring Boot Sample Flyway.

Podeu fer una ullada a http://localhost:8080/flyway, hi ha una llista d'scripts.

Aquest exemple també inclou la consola H2 (at http://localhost:8080/h2-console) perquè pugueu veure l'estat de la base de dades (l'URL jdbc per defecte és jdbc:h2:mem:testdb).

a més

Llegiu també altres articles al nostre blog:

Font: www.habr.com

Afegeix comentari