Implementación e bases de datos sen tempo de inactividade

Implementación e bases de datos sen tempo de inactividade

Este artigo explica en detalle como resolver problemas de compatibilidade de bases de datos na implantación. Dirémosche o que pode pasar coas túas aplicacións de produción se intentas implementar sen preparación previa. Despois pasaremos polas etapas do ciclo de vida da aplicación que son necesarias para ter cero tempo de inactividade (aprox. carril: máis lonxe - cero tempo de inactividade). O resultado das nosas operacións será aplicar o cambio de base de datos incompatible con versións anteriores de forma compatible.

Se queres entender os exemplos de código do artigo, podes atopalos en GitHub.

Introdución

Implementación sen tempo de inactividade

Que mística implantación de tempo de inactividade cero? Podes dicir que é cando a túa aplicación se desprega de forma que poidas introducir con éxito unha nova versión da aplicación en produción, mentres o usuario non nota a súa indisponibilidade. Desde a perspectiva do usuario e da empresa, este é o mellor escenario de implantación posible porque permite introducir novas funcións e corrixir erros sen interrupcións.

Como conseguir isto? Hai varias formas, aquí tes unha delas:

  • implementa a versión número 1 do teu servizo
  • realizar unha migración de base de datos
  • Implementa a versión 2 do teu servizo en paralelo coa versión 1
  • en canto vexas que a versión número 2 funciona como debería, elimina a versión número 1
  • feito!

Doado, non? Desafortunadamente, non é tan sinxelo, e verémolo en detalle máis adiante. Agora imos comprobar outro proceso de implantación bastante común: o despregamento azul e verde.

Xa escoitou falar diso despregamento azul verde? Cloud Foundry fai isto moi sinxelo. Basta mirar Este artigo, onde describimos isto con máis detalle. Para resumir brevemente, lembrámosche como facer a implementación en azul e verde:

  • asegúrate de que funcionen dúas copias do teu código de produción ("azul" e "verde");
  • dirixir todo o tráfico ao ambiente azul, é dicir. para que os URL de produción apunten alí;
  • implementar e probar todos os cambios de aplicación nun ambiente verde;
  • cambiar os URL de ambiente azul a verde

A implantación de azul verde é un enfoque que che permite introducir novas funcións facilmente sen preocuparse pola interrupción da produción. Isto débese ao feito de que aínda que ocorre algo, podes retroceder facilmente ao ambiente anterior simplemente "premendo un interruptor".

Despois de ler todo o anterior, podes facerte a pregunta: Que ten que ver o tempo de inactividade cero coa implementación de Blue Green?

Pois ben, teñen bastantes cousas en común, xa que manter dúas copias do mesmo entorno require o dobre de esforzo para mantelas. Por iso afirman algúns equipos Martin Fowler, siga unha variación deste enfoque:

Outra opción é usar a mesma base de datos, creando interruptores azul-verde para as capas web e de dominio. Neste enfoque, a base de datos adoita ser un problema, especialmente cando precisa cambiar o seu esquema para admitir unha nova versión do software.

E aquí chegamos ao problema principal deste artigo. Base de datos. Botémoslle outra ollada a esta frase.

realizar unha migración de base de datos.

Agora tes que facerte a pregunta: que pasa se o cambio da base de datos non é compatible cara atrás? Non se romperá a miña primeira versión da aplicación? De feito, isto é exactamente o que vai pasar...

Polo tanto, a pesar dos enormes beneficios do tempo de inactividade cero/a implementación azul verde, as empresas tenden a seguir o seguinte proceso máis seguro para implementar as súas aplicacións:

  • preparar un paquete cunha nova versión da aplicación
  • pechar unha aplicación en execución
  • executar scripts para migrar a base de datos
  • implementar e lanzar unha nova versión da aplicación

Neste artigo, detallaremos como pode traballar coa súa base de datos e código para aproveitar a implementación sen tempo de inactividade.

Problemas de base de datos

Se tes unha aplicación sen estado que non almacena ningún dato na base de datos, podes obter unha implementación sen tempo de inactividade de inmediato. Desafortunadamente, a maioría do software precisa almacenar datos nalgún lugar. É por iso que debes pensar dúas veces antes de facer calquera cambio no circuíto. Antes de entrar nos detalles de como cambiar o esquema para que sexa posible a implementación sen tempo de inactividade, centrémonos primeiro no esquema de versión.

Esquema de versións

Neste artigo utilizaremos Pasaxea como ferramenta de control de versións (aprox. Tradución: estamos a falar de migracións de bases de datos). Por suposto, tamén escribiremos unha aplicación Spring Boot que teña soporte incorporado de Flyway e realizará a migración do esquema mentres configura o contexto da aplicación. Cando use Flyway, pode almacenar scripts de migración no cartafol de proxectos (por defecto en classpath:db/migration). Aquí podes ver un exemplo deste tipo de ficheiros de migración

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

Neste exemplo vemos 4 scripts de migración que, se non se executan previamente, executaranse un tras outro cando se inicie a aplicación. Vexamos un dos ficheiros (V1__init.sql) como exemplo.

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

Todo se explica por si mesmo: pode usar SQL para definir como se debe modificar a súa base de datos. Para obter máis información sobre Spring Boot e Flyway, consulta Spring Boot Docs.

Ao usar unha ferramenta de control de código fonte con Spring Boot, obtén dous grandes beneficios:

  • separa os cambios de base de datos dos cambios de código
  • A migración da base de datos prodúcese xunto co lanzamento da súa aplicación, é dicir. o seu proceso de implantación simplifícase

Resolución de problemas de bases de datos

Na seguinte sección do artigo, centrarémonos en analizar dous enfoques para os cambios na base de datos.

  • incompatibilidade cara atrás
  • retrocompatibilidade

A primeira considerarase como unha advertencia de que non debe realizar unha implantación sen tempo de inactividade sen unha preparación previa... A segunda ofrece unha solución sobre como pode realizar unha implantación sen tempo de inactividade e, ao mesmo tempo, manter a compatibilidade con versións anteriores.

O noso proxecto no que traballaremos será unha sinxela aplicación Spring Boot Flyway que teña Person с first_name и last_name na base de datos (aprox. tradución: Person é unha táboa e first_name и last_name - Estes son os campos nel). Queremos renomear last_name в surname.

Suposicións

Antes de entrar nos detalles, hai un par de suposicións que debemos facer sobre as nosas aplicacións. O principal resultado que queremos conseguir será un proceso bastante sinxelo.

A nota. PRO-TIP de negocios. A simplificación dos procesos pode aforrarche moito diñeiro en asistencia (canto máis persoas teñas traballando para a túa empresa, máis diñeiro poderás aforrar)!

Non é necesario revertir a base de datos

Isto simplifica o proceso de implantación (algúns rollbacks de bases de datos son case imposibles, como a eliminación). Preferimos retrotraer só as aplicacións. Deste xeito, aínda que teñas bases de datos diferentes (por exemplo, SQL e NoSQL), a túa canalización de implantación terá o mesmo aspecto.

SEMPRE debe ser posible retrotraer a aplicación unha versión (non máis)

A recuperación só se debe facer cando sexa necesario. Se hai un erro na versión actual que non se soluciona facilmente, deberíamos poder volver á última versión de traballo. Supoñemos que esta última versión de traballo é a anterior. Manter a compatibilidade do código e da base de datos para máis dun lanzamento sería extremadamente difícil e caro.

A nota. Para unha maior lexibilidade, neste artigo cambiaremos a versión principal da aplicación.

Paso 1: Estado inicial

Versión da aplicación: 1.0.0
Versión DB: v1

Comentario

Este será o estado inicial da solicitude.

Cambios na base de datos

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

Cambios de código

A aplicación almacena os datos da persoa 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
                + "]";
    }
}

Cambio de nome da columna incompatible cara atrás

Vexamos un exemplo de como cambiar o nome dunha columna:

Atención. O seguinte exemplo romperá as cousas intencionadamente. Mostrámolo para demostrar o problema da compatibilidade das bases de datos.

Versión da aplicación: 2.0.0.BAD

Versión DB: v2bad

Comentario

Os cambios actuais NON nos permiten executar dúas instancias (vella e nova) ao mesmo tempo. Así, a implantación de tempo de inactividade cero será difícil de conseguir (se se teñen en conta as suposicións, é realmente imposible).

Probas A/B

A situación actual é que temos unha versión da aplicación 1.0.0, despregado en produción e base de datos v1. Necesitamos implementar unha segunda instancia da aplicación, versión 2.0.0.BAD, e actualice a base de datos a v2bad.

Pasos:

  1. desprágase unha nova instancia da versión da aplicación 2.0.0.BADque actualiza a base de datos v2bad
  2. na base de datos v2bad columna last_name xa non existe - cambiouse a surname
  3. A actualización da base de datos e da aplicación realizouse correctamente e algunhas instancias están en execución 1.0.0, outros - en 2.0.0.BAD. Todo está conectado á base de datos v2bad
  4. todas as instancias da versión 1.0.0 comezará a lanzar erros porque tentarán inserir datos na columna last_nameque xa non existe
  5. todas as instancias da versión 2.0.0.BAD funcionará sen problemas

Como podes ver, se facemos cambios incompatibles cara atrás na base de datos e na aplicación, a proba A/B é imposible.

Reversión da aplicación

Supoñamos que despois de tentar facer a implantación A/B (aprox. per.: o autor probablemente quixo dicir probas A/B aquí) decidimos que necesitamos restaurar a aplicación á versión 1.0.0. Digamos que non queremos revertir a base de datos.

Pasos:

  1. paramos a instancia da aplicación de versión 2.0.0.BAD
  2. a base de datos aínda está v2bad
  3. dende a versión 1.0.0 non entende o que é surname, veremos erros
  4. O inferno desatouse, xa non podemos volver

Como podes ver, se facemos cambios incompatibles cara atrás na base de datos e na aplicación, non podemos volver á versión anterior.

Rexistros de execución 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"}

Cambios na base de datos

Script de migración que cambia o nome last_name в surname

Guión de orixe 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ón que renomea last_name.

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

Cambios de código

Cambiamos o nome do campo lastName en surname.

Cambiar o nome dunha columna dun xeito compatible con versións anteriores

Esta é a situación máis común que nos podemos atopar. Necesitamos facer cambios incompatibles cara atrás. Xa demostramos que para a implementación sen tempo de inactividade, non debemos simplemente aplicar a migración da base de datos sen pasos adicionais. Nesta sección do artigo, realizaremos 3 despregamentos da aplicación xunto con migracións de bases de datos para lograr o resultado desexado mantendo a compatibilidade con versións anteriores.

A nota. Recordemos que temos unha base de datos de versións v1. Contén columnas first_name и last_name. Temos que cambiar last_name en surname. Tamén temos a versión da aplicación 1.0.0, que aínda non se utiliza surname.

Paso 2: Engade o apelido

Versión da aplicación: 2.0.0
Versión DB: v2

Comentario

Ao engadir unha nova columna e copiar o seu contido, creamos cambios de base de datos compatibles con versións anteriores. Ao mesmo tempo, se revertimos o JAR ou temos un antigo JAR en execución, non se romperá durante a execución.

Estamos lanzando unha nova versión

Pasos:

  1. realizar unha migración da base de datos para crear unha nova columna surname. Agora a súa versión de base de datos v2
  2. copiar datos de last_name в surname. Preste atenciónque se tes moitos destes datos, deberías considerar a migración por lotes!
  3. escribir o código onde se usan AMBOS и novoE o vello columna. Agora a versión da túa aplicación 2.0.0
  4. le o valor da columna surname, se non o é null, ou dende last_name, se surname non especificado. Podes eliminar getLastName() desde o código, xa que sairá null ao revertir a súa aplicación desde 3.0.0 para 2.0.0.

Se está a usar Spring Boot Flyway, estes dous pasos realizaranse durante o inicio da versión 2.0.0 aplicacións. Se executas manualmente a ferramenta de control de versións da base de datos, terás que facer dúas cousas diferentes para facelo (primeiro actualizar a versión db manualmente e despois implementar a nova aplicación).

É importante. Lembra que a columna recén creada NON DEBERÍA ser NON NULO. Se fai unha restauración, a aplicación antiga non sabe sobre a nova columna e non a instalará durante Insert. Pero se engades esta restrición e o teu db será v2, isto requirirá establecer o valor da nova columna. O que levará a violacións das restricións.

É importante. Debería eliminar o método getLastName(), porque na versión 3.0.0 Non hai concepto de columna no código last_name. Isto significa que alí se establecerá nulo. Podes deixar o método e engadir comprobacións null, pero unha solución moito mellor sería asegurarse de que na lóxica getSurname() seleccionaches o valor distinto de cero correcto.

Probas A/B

A situación actual é que temos unha versión da aplicación 1.0.0, despregado en produción e a base de datos en v1. Necesitamos implementar unha segunda instancia da aplicación de versión 2.0.0que actualizará a base de datos v2.

Pasos:

  1. desprágase unha nova instancia da versión da aplicación 2.0.0que actualiza a base de datos v2
  2. mentres tanto algunhas solicitudes foron procesadas por instancias de versión 1.0.0
  3. a actualización foi exitosa e tes varias instancias en execución da versión da aplicación 1.0.0 e outras versións 2.0.0. Todos se comunican coa base de datos en v2
  4. versión 1.0.0 non utiliza a columna do apelido na base de datos, senón a versión 2.0.0 usos. Non interfiren entre si e non debe haber erros.
  5. versión 2.0.0 almacena os datos tanto na columna antiga como na nova, garantindo a compatibilidade con versións anteriores

É importante. Se tes algunha consulta que conte elementos en función dos valores da columna antiga/nova, debes lembrar que agora tes valores duplicados (o máis probable é que aínda estean migrando). Por exemplo, se quere contar o número de usuarios cuxo apelido (sexa como se chame a columna) comezou coa letra A, ata que se complete a migración de datos (oldnew columna) pode ter datos inconsistentes se consulta unha nova columna.

Reversión da aplicación

Agora temos a versión da aplicación 2.0.0 e base de datos en v2.

Pasos:

  1. restaurar a súa aplicación á versión 1.0.0.
  2. versión 1.0.0 non utiliza unha columna na base de datos surname, polo que a recuperación debería ter éxito

Cambios de DB

A base de datos contén unha columna chamada last_name.

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

Engadir guión surname.

Atención. Lembre que NON PODE ENGADIR ningunha restrición NOT NULL á columna que está a engadir. Se retrocedes o JAR, a versión antiga non terá idea da columna engadida e estableceraa automaticamente como NULL. Se existe tal limitación, a aplicación antiga simplemente romperase.

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

Cambios de código

Almacenamos datos como last_namee en surname. Ao mesmo tempo lemos de last_name, xa que esta columna é a máis relevante. Durante o proceso de implantación, algunhas solicitudes poden ser procesadas por unha instancia de aplicación que aínda non se actualizou.

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

Paso 3: eliminando apelido do código

Versión da aplicación: 3.0.0

Versión DB:v3

Comentario

Nota per.: Ao parecer, no artigo orixinal o autor copiou por erro o texto deste bloque do paso 2. Neste paso, deberían facerse cambios no código da aplicación destinados a eliminar a funcionalidade que utiliza a columna last_name.

Ao engadir unha nova columna e copiar o seu contido, creamos cambios de base de datos compatibles con versións anteriores. Ademais, se revertimos o JAR ou temos un antigo JAR en execución, non se romperá durante a execución.

Reversión da aplicación

Actualmente temos a versión da aplicación 3.0.0 e base de datos v3. Versión 3.0.0 non garda datos en last_name. Isto significa que en surname gárdase a información máis actualizada.

Pasos:

  1. restaurar a súa aplicación á versión 2.0.0.
  2. versión 2.0.0 usos e last_name и surname.
  3. versión 2.0.0 levará surname, se non é cero, se non -last_name

Cambios na base de datos

Non hai cambios estruturais na base de datos. Exécutase o seguinte script para realizar a migración final dos datos antigos:

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

Cambios de código

Nota per.: A descrición deste bloque tamén foi copiada por erro polo autor do paso 2. De acordo coa lóxica do artigo, os cambios no código neste paso deberían estar dirixidos a eliminar del elementos que funcionan coa columna last_name.

Almacenamos datos como last_namee en surname. Ademais, lemos da columna last_name, xa que é o máis relevante. Durante o proceso de implantación, algunhas solicitudes poden ser procesadas por unha instancia que aínda non se actualizou.

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

Paso 4: eliminando apelido da base de datos

Versión da aplicación: 4.0.0

Versión DB: v4

Comentario

Debido ao feito de que o código de versión 3.0.0 non usou a columna last_name, non pasará nada malo durante a execución se volvemos a 3.0.0 despois de eliminar unha columna da base de datos.

Rexistros de execución 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"}

Cambios de DB

Relativamente v3 só eliminamos a columna last_name e engadir as restricións que faltan.

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

Cambios de código

Non hai cambios no código.

Saída

Aplicamos correctamente un cambio de nome de columna incompatible cara atrás ao realizar varias implementacións compatibles cara atrás. A continuación móstrase un resumo das accións realizadas:

  1. implantación da versión da aplicación 1.0.0 с v1 esquema de base de datos (nome da columna = last_name)
  2. implantación da versión da aplicación 2.0.0, que almacena os datos last_name и surname. A aplicación le desde last_name. A base de datos está en versión v2que contén columnas como last_nameE surname. surname é unha copia de last_name. (NOTA: esta columna non debe ter unha restricción non nula)
  3. implantación da versión da aplicación 3.0.0, que só almacena datos surname e le do apelido. En canto á base de datos, estase a realizar a última migración last_name в surname. Tamén unha limitación NON NULO eliminado de last_name. A base de datos está agora en versión v3
  4. implantación da versión da aplicación 4.0.0 - non se realizan cambios no código. Implantación de bases de datos v4, que elimina last_name. Aquí pode engadir as restricións que falten á base de datos.

Seguindo este enfoque, sempre pode revertir unha versión sen romper a compatibilidade de base de datos/aplicación.

Código

Todo o código usado neste artigo está dispoñible en Github. Abaixo está unha descrición adicional.

Proxectos

Despois de clonar o repositorio, verá a seguinte estrutura de cartafoles.

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

Guións

Pode executar os scripts descritos nos scripts a continuación, que demostrarán os cambios compatibles con versións anteriores e incompatibles na base de datos.

Para ver o caso de cambios compatibles con versións anteriores, executa:

./scripts/scenario_backward_compatible.sh

E a ver caso con cambios incompatibles cara atrás, executa:

./scripts/scenario_backward_incompatible.sh

Spring Boot Mostra Flyway

Todos os exemplos están tirados de Spring Boot Sample Flyway.

Podes botarlle unha ollada http://localhost:8080/flyway, hai unha lista de scripts.

Este exemplo tamén inclúe a consola H2 (at http://localhost:8080/h2-console) para que poida ver o estado da base de datos (URL jdbc predeterminada é jdbc:h2:mem:testdb).

adicionalmente

Lea tamén outros artigos no noso blog:

Fonte: www.habr.com

Engadir un comentario