Implementación y bases de datos sin tiempo de inactividad

Implementación y bases de datos sin tiempo de inactividad

Este artículo explica en detalle cómo resolver problemas de compatibilidad de bases de datos durante la implementación. Le diremos qué puede pasar con sus aplicaciones de producción si intenta implementarlas sin una preparación previa. Luego, pasaremos por las etapas del ciclo de vida de la aplicación que se requieren para tener cero tiempo de inactividad (aprox. carril: más lejos - cero tiempo de inactividad). El resultado de nuestras operaciones será aplicar el cambio de base de datos incompatible con versiones anteriores de una manera compatible con versiones anteriores.

Si desea comprender los ejemplos de código del artículo, puede encontrarlos en GitHub.

introducción

Implementación sin tiempo de inactividad

que místico implementación sin tiempo de inactividad? Puede decir que esto es cuando su aplicación se implementa de tal manera que puede introducir con éxito una nueva versión de la aplicación en producción, mientras que el usuario no nota su falta de disponibilidad. Desde la perspectiva del usuario y de la empresa, este es el mejor escenario de implementación posible porque permite introducir nuevas funciones y corregir errores sin interrupciones.

¿Cómo lograr esto? Hay varias formas, aquí tienes una de ellas:

  • implemente la versión No. 1 de su servicio
  • realizar una migración de base de datos
  • Implemente la versión n.° 2 de su servicio en paralelo con la versión n.° 1
  • Tan pronto como vea que la versión No. 2 funciona como debería, elimine la versión No. 1
  • hecho!

Fácil, ¿no? Desafortunadamente, no es tan simple y lo veremos en detalle más adelante. Ahora veamos otro proceso de implementación bastante común: la implementación azul-verde.

Alguna vez has oído hablar de despliegue azul verde? Cloud Foundry hace que esto sea extremadamente fácil. solo mira este articulo, donde describimos esto con más detalle. Para resumir brevemente, permítanos recordarle cómo realizar una implementación azul-verde:

  • asegúrese de que funcionen dos copias de su código de producción (“azul” y “verde”);
  • dirigir todo el tráfico al entorno azul, es decir, para que las URL de producción apunten allí;
  • implementar y probar todos los cambios en las aplicaciones en un entorno ecológico;
  • cambiar las URL del entorno azul al verde

La implementación azul-verde es un enfoque que le permite introducir fácilmente nuevas funciones sin preocuparse por interrupciones en la producción. Esto se debe al hecho de que, incluso si sucede algo, puedes volver fácilmente al entorno anterior simplemente "apretando un interruptor".

Después de leer todo lo anterior, es posible que se pregunte: ¿Qué tiene que ver el tiempo de inactividad cero con la implementación azul-verde?

Bueno, tienen bastante en común, ya que mantener dos copias del mismo entorno requiere el doble de esfuerzo para mantenerlas. Por eso algunos equipos afirman Martín Cazador, siga una variación de este enfoque:

Otra opción es utilizar la misma base de datos, creando conmutadores azul-verde para las capas web y de dominio. En este enfoque, la base de datos a menudo puede ser un problema, especialmente cuando es necesario cambiar su esquema para admitir una nueva versión del software.

Y aquí llegamos al problema principal de este artículo. База данных. Echemos otro vistazo a esta frase.

realizar una migración de base de datos.

Ahora debe hacerse la pregunta: ¿qué pasa si el cambio de la base de datos no es compatible con versiones anteriores? ¿No se romperá mi primera versión de la aplicación? De hecho, esto es exactamente lo que sucederá...

Entonces, incluso a pesar de los enormes beneficios de la implementación sin tiempo de inactividad/verde azul, las empresas tienden a seguir el siguiente proceso más seguro para implementar sus aplicaciones:

  • preparar un paquete con una nueva versión de la aplicación
  • cerrar una aplicación en ejecución
  • ejecutar scripts para migrar la base de datos
  • implementar y lanzar una nueva versión de la aplicación

En este artículo, detallaremos cómo puede trabajar con su base de datos y código para aprovechar la implementación sin tiempo de inactividad.

Problemas de la base de datos

Si tiene una aplicación sin estado que no almacena ningún dato en la base de datos, puede obtener una implementación sin tiempo de inactividad de inmediato. Desafortunadamente, la mayoría del software necesita almacenar datos en algún lugar. Por eso deberías pensártelo dos veces antes de realizar cualquier cambio en el circuito. Antes de entrar en detalles sobre cómo cambiar el esquema para que sea posible una implementación sin tiempo de inactividad, centrémonos primero en el esquema de control de versiones.

Esquema de versiones

En este artículo usaremos Flyway como herramienta de control de versiones (aprox. per.: estamos hablando de migraciones de bases de datos). Naturalmente, también escribiremos una aplicación Spring Boot que tenga soporte Flyway incorporado y realizará la migración del esquema mientras configuramos el contexto de la aplicación. Al utilizar Flyway, puede almacenar scripts de migración en su carpeta de proyectos (de forma predeterminada en classpath:db/migration). Aquí puede ver un ejemplo de dichos archivos de migración.

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

En este ejemplo vemos 4 scripts de migración que, si no se ejecutan previamente, se ejecutarán uno tras otro cuando se inicie la aplicación. Veamos uno de los archivos (V1__init.sql) como ejemplo.

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 sí mismo: puede utilizar SQL para definir cómo se debe modificar su base de datos. Para obtener más información sobre Spring Boot y Flyway, consulte Documentos de arranque de primavera.

Al utilizar una herramienta de control de código fuente con Spring Boot, obtienes 2 grandes beneficios:

  • separa los cambios en la base de datos de los cambios en el código
  • La migración de la base de datos ocurre junto con el lanzamiento de su aplicación, es decir. su proceso de implementación se simplifica

Solución de problemas de bases de datos

En la siguiente sección del artículo, nos centraremos en analizar dos enfoques para los cambios en la base de datos.

  • incompatibilidad hacia atrás
  • compatibilidad con versiones anteriores

El primero se considerará como una advertencia de que no debe realizar una implementación sin tiempo de inactividad sin una preparación preliminar... El segundo ofrece una solución sobre cómo realizar una implementación sin tiempo de inactividad y al mismo tiempo mantener la compatibilidad con versiones anteriores.

Nuestro proyecto en el que trabajaremos será una sencilla aplicación Spring Boot Flyway que tiene Person с first_name и last_name en la base de datos (aprox. traducción: Person es una mesa y first_name и last_name - estos son los campos que contiene). queremos cambiar el nombre last_name в surname.

Supuestos

Antes de entrar en detalles, hay un par de suposiciones que debemos hacer sobre nuestras aplicaciones. El principal resultado que queremos lograr será un proceso bastante simple.

La nota. PRO-TIP empresarial. Simplificar los procesos puede ahorrarle mucho dinero en soporte (cuantas más personas trabajen para su empresa, más dinero podrá ahorrar).

No es necesario revertir la base de datos

Esto simplifica el proceso de implementación (algunas reversiones de bases de datos son casi imposibles, como la reversión de eliminación). Preferimos revertir sólo aplicaciones. De esta manera, incluso si tiene bases de datos diferentes (por ejemplo, SQL y NoSQL), su canal de implementación tendrá el mismo aspecto.

SIEMPRE debe ser posible revertir la aplicación una versión atrás (no más)

La reversión sólo debe realizarse cuando sea necesario. Si hay un error en la versión actual que no se soluciona fácilmente, deberíamos poder volver a la última versión funcional. Suponemos que esta última versión funcional es la anterior. Mantener la compatibilidad del código y la base de datos para más de una implementación sería extremadamente difícil y costoso.

Заметка. Para una mayor legibilidad, en este artículo cambiaremos la versión principal de la aplicación.

Paso 1: Estado inicial

Versión de la aplicación: 1.0.0
Versión de base de datos: v1

comentario

Este será el estado inicial de la aplicación.

Cambios en la base de datos

La base de datos contiene 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

La aplicación almacena datos de la persona 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 nombre de columna incompatible hacia atrás

Veamos un ejemplo de cómo cambiar el nombre de una columna:

Atencion El siguiente ejemplo romperá cosas intencionalmente. Mostramos esto para demostrar el problema de la compatibilidad de la base de datos.

Versión de la aplicación: 2.0.0.BAD

Versión de base de datos: v2bad

comentario

Los cambios actuales NO nos permiten ejecutar dos instancias (antigua y nueva) al mismo tiempo. Por lo tanto, será difícil lograr una implementación sin tiempo de inactividad (si se tienen en cuenta las suposiciones, en realidad es imposible).

Pruebas A/B

La situación actual es que tenemos una versión de la aplicación. 1.0.0, implementado en producción y base de datos v1. Necesitamos implementar una segunda instancia de la aplicación, versión 2.0.0.BADy actualizar la base de datos para v2bad.

Pasos

  1. se implementa una nueva instancia de la aplicación de versión 2.0.0.BADque actualiza la base de datos a v2bad
  2. en la base de datos v2bad columna last_name ya no existe - fue cambiado a surname
  3. La actualización de la base de datos y la aplicación fue exitosa y algunas instancias se están ejecutando 1.0.0, otros - en 2.0.0.BAD. Todo está conectado a la base de datos. v2bad
  4. todas las instancias de la versión 1.0.0 comenzará a arrojar errores porque intentarán insertar datos en la columna last_namequien ya no existe
  5. todas las instancias de la versión 2.0.0.BAD funcionará sin problemas

Como puede ver, si realizamos cambios incompatibles con versiones anteriores de la base de datos y la aplicación, las pruebas A/B son imposibles.

Reversión de la aplicación

Supongamos que después de intentar realizar una implementación A/B (aprox. por .: el autor probablemente se refería a las pruebas A/B aquí) decidimos que necesitamos revertir la aplicación a la versión 1.0.0. Digamos que no queremos revertir la base de datos.

Pasos

  1. detenemos la instancia de la aplicación de versión 2.0.0.BAD
  2. la base de datos todavía está v2bad
  3. desde la versión 1.0.0 no entiende lo que es surname, veremos errores
  4. El infierno se ha desatado, ya no podemos volver atrás.

Como puede ver, si realizamos cambios incompatibles con versiones anteriores en la base de datos y la aplicación, no podemos volver a la versión anterior.

Registros de ejecució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 en la base de datos

Script de migración que cambia el nombre last_name в surname

Guión de ruta migratoria de origen:

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

Script que cambia el nombre 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

Hemos cambiado el nombre del campo. lastName en surname.

Cambiar el nombre de una columna de forma compatible con versiones anteriores

Esta es la situación más común que podemos encontrar. Necesitamos hacer cambios incompatibles hacia atrás. Ya hemos demostrado que para una implementación sin tiempo de inactividad, no debemos simplemente aplicar la migración de la base de datos sin pasos adicionales. En esta sección del artículo, realizaremos 3 implementaciones de la aplicación junto con migraciones de bases de datos para lograr el resultado deseado manteniendo la compatibilidad con versiones anteriores.

La nota. Recordemos que tenemos una base de datos de versiones. v1. Contiene columnas first_name и last_name. tenemos que cambiar last_name en surname. También tenemos versión de aplicación. 1.0.0, que aún no se utiliza surname.

Paso 2: Añade apellido

Versión de la aplicación: 2.0.0
Versión de base de datos: v2

comentario

Al agregar una nueva columna y copiar su contenido, creamos cambios en la base de datos compatibles con versiones anteriores. Al mismo tiempo, si revertimos el JAR o ejecutamos un JAR antiguo, no se romperá durante la ejecución.

Estamos lanzando una nueva versión.

Pasos

  1. realizar una migración de base de datos para crear una nueva columna surname. Ahora tu versión DB v2
  2. copiar datos de last_name в surname. Notaque si tiene muchos de estos datos, ¡debería considerar la migración por lotes!
  3. escribe el código donde se usan AMBOS и nuevoY старый columna. Ahora tu versión de la aplicación 2.0.0
  4. leer el valor de la columna surname, si no es null, o de last_nameanguilas surname No especificado. puedes eliminar getLastName() del código, ya que generará null al revertir su aplicación desde 3.0.0 a 2.0.0.

Si está utilizando Spring Boot Flyway, estos dos pasos se realizarán durante el inicio de la versión. 2.0.0 aplicaciones. Si ejecuta la herramienta de control de versiones de la base de datos manualmente, tendrá que hacer dos cosas diferentes para lograrlo (primero actualizar la versión de la base de datos manualmente y luego implementar la nueva aplicación).

Importante. Recuerde que la columna recién creada No ser NO NULO. Si realiza una reversión, la aplicación anterior no conoce la nueva columna y no la instalará durante Insert. Pero si agrega esta restricción y su base de datos será v2, esto requerirá establecer el valor de la nueva columna. Lo que conducirá a violaciones de las restricciones.

Importante. Deberías eliminar el método. getLastName(), porque en la versión 3.0.0 No existe el concepto de columna en el código. last_name. Esto significa que allí se establecerá nulo. Puede abandonar el método y agregar cheques para null, pero una solución mucho mejor sería asegurarse de que en la lógica getSurname() seleccionó el valor correcto distinto de cero.

Pruebas A/B

La situación actual es que tenemos una versión de la aplicación. 1.0.0, implementado en producción y la base de datos en v1. Necesitamos implementar una segunda instancia de la aplicación de versión. 2.0.0que actualizará la base de datos a v2.

Pasos

  1. se implementa una nueva instancia de la aplicación de versión 2.0.0que actualiza la base de datos a v2
  2. Mientras tanto, algunas solicitudes fueron procesadas por instancias de versión. 1.0.0
  3. la actualización fue exitosa y tiene varias instancias en ejecución de la versión de la aplicación 1.0.0 y otras versiones 2.0.0. Todos se comunican con la base de datos en v2
  4. versión 1.0.0 no utiliza la columna de apellido en la base de datos, sino la versión 2.0.0 usos. No interfieren entre sí y no debería haber errores.
  5. versión 2.0.0 almacena datos tanto en la columna antigua como en la nueva, lo que garantiza la compatibilidad con versiones anteriores

Importante. Si tiene alguna consulta que cuente elementos en función de los valores de la columna antigua/nueva, debe recordar que ahora tiene valores duplicados (lo más probable es que todavía estén migrando). Por ejemplo, si desea contar la cantidad de usuarios cuyo apellido (como se llame la columna) comenzó con la letra A, luego hasta que se complete la migración de datos (oldnew columna) es posible que tenga datos inconsistentes si realiza la consulta en una nueva columna.

Reversión de la aplicación

Ahora tenemos la versión de la aplicación. 2.0.0 y base de datos en v2.

Pasos

  1. revertir su aplicación a la versión 1.0.0.
  2. versión 1.0.0 no utiliza una columna en la base de datos surname, por lo que la reversión debería ser exitosa

cambios de base de datos

La base de datos contiene una columna llamada last_name.

Script fuente de ruta migratoria:

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

Agregar guión surname.

Atencion Recuerde que NO PUEDE AGREGAR restricciones NO NULAS a la columna que está agregando. Si revierte el JAR, la versión anterior no tendrá idea de la columna agregada y la establecerá automáticamente en NULL. Si existe tal limitación, la aplicación anterior simplemente se romperá.

-- 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_namey en surname. Al mismo tiempo leemos de last_name, ya que esta columna es la más relevante. Durante el proceso de implementación, es posible que algunas solicitudes hayan sido procesadas por una instancia de aplicación que aún no se ha actualizado.

/*
 * 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: Eliminar apellido del código

Versión de la aplicación: 3.0.0

Versión de base de datos:v3

comentario

Nota per.: Aparentemente, en el artículo original el autor copió por error el texto de este bloque del paso 2. En este paso, se deben realizar cambios en el código de la aplicación destinados a eliminar la funcionalidad que utiliza la columna. last_name.

Al agregar una nueva columna y copiar su contenido, creamos cambios en la base de datos compatibles con versiones anteriores. Además, si revertimos el JAR o tenemos un JAR antiguo ejecutándose, no se romperá durante la ejecución.

Reversión de la aplicación

Actualmente tenemos versión de la aplicación. 3.0.0 y base de datos v3. Versión 3.0.0 no guarda datos en last_name. Esto significa que en surname Se almacena la información más actualizada.

Pasos

  1. revertir su aplicación a la versión 2.0.0.
  2. versión 2.0.0 usos y last_name и surname.
  3. versión 2.0.0 tomará surname, si no es cero, en caso contrario -last_name

Cambios en la base de datos

No hay cambios estructurales en la base de datos. Se ejecuta el siguiente script para realizar la migración final de los datos antiguos:

-- 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.: La descripción de este bloque también fue copiada por error por el autor del paso 2. De acuerdo con la lógica del artículo, los cambios en el código en este paso deben tener como objetivo eliminar elementos que funcionan con la columna. last_name.

Almacenamos datos como last_namey en surname. Además, leemos de la columna. last_name, ya que es el más relevante. Durante el proceso de implementación, algunas solicitudes pueden ser procesadas por una instancia que aún no se ha actualizado.

/*
 * 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: eliminar apellido de la base de datos

Versión de la aplicación: 4.0.0

Versión de base de datos: v4

comentario

Debido al hecho de que el código de versión 3.0.0 no usé la columna last_name, no pasará nada malo durante la ejecución si volvemos a 3.0.0 después de eliminar una columna de la base de datos.

Registros de ejecució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 base de datos

Sobre v3 simplemente eliminamos la columna last_name y agregue las restricciones 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

No hay cambios en el código.

conclusión

Aplicamos con éxito un cambio de nombre de columna incompatible con versiones anteriores mediante la realización de varias implementaciones compatibles con versiones anteriores. A continuación se muestra un resumen de las acciones realizadas:

  1. implementación de la versión de la aplicación 1.0.0 с v1 esquema de base de datos (nombre de columna = last_name)
  2. implementación de la versión de la aplicación 2.0.0, que almacena datos en last_name и surname. La aplicación lee desde last_name. La base de datos está en versión. v2que contiene columnas como last_nameY surname. surname es una copia de last_name. (NOTA: esta columna no debe tener una restricción no nula)
  3. implementación de la versión de la aplicación 3.0.0, que sólo almacena datos en surname y lee desde el apellido. En cuanto a la base de datos, se está realizando la última migración. last_name в surname. También una limitación NO NULO retirado de last_name. La base de datos ya está en versión. v3
  4. implementación de la versión de la aplicación 4.0.0 - No se realizan cambios en el código. Implementación de base de datos v4, que elimina last_name. Aquí puede agregar cualquier restricción que falte a la base de datos.

Siguiendo este enfoque, siempre puede revertir una versión sin romper la compatibilidad de la base de datos/aplicación.

código

Todo el código utilizado en este artículo está disponible en Github. A continuación se muestra una descripción adicional.

Proyectos

Después de clonar el repositorio, verá la siguiente estructura de carpetas.

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

Puede ejecutar los scripts que se describen a continuación, que demostrarán cambios compatibles e incompatibles con versiones anteriores en la base de datos.

Ver el caso de los cambios compatibles con versiones anteriores, correr:

./scripts/scenario_backward_compatible.sh

y para ver caso con cambios incompatibles hacia atrás, correr:

./scripts/scenario_backward_incompatible.sh

Ruta migratoria de muestra de bota de primavera

Todos los ejemplos están tomados de Spring Boot Sample Flyway.

Puedes echar un vistazo a http://localhost:8080/flyway, hay una lista de scripts.

Este ejemplo también incluye la consola H2 (en http://localhost:8080/h2-console) para que pueda ver el estado de la base de datos (la URL jdbc predeterminada es jdbc:h2:mem:testdb).

además

Lea también otros artículos en nuestro blog:

Fuente: habr.com

Añadir un comentario