
En este artículo se explica detalladamente cómo solucionar problemas relacionados con la compatibilidad de bases de datos al realizar un despliegue. Hablaremos sobre lo que puede ocurrir con sus aplicaciones en producción si intenta realizar un despliegue sin la preparación adecuada. Luego, revisaremos las etapas del ciclo de vida de la aplicación que son necesarias para lograr un tiempo de inactividad cero (Nota del traductor: a partir de ahora — zero downtime). El resultado de nuestras operaciones será aplicar un cambio de base de datos incompatible hacia atrás de una manera compatible.
Si desea revisar ejemplos de código de este artículo, los encontrará en .
Introducción
Zero downtime deployment
¿Qué es este místico zero downtime deployment?? Можно сказать, это когда ваше приложение развернуто так, что вы можете успешно вводить новую версию приложения на продакшн, в то время как пользователь не замечает его недоступности. С точки зрения пользователя и компании, это наилучший из возможных сценариев деплоя, поскольку таким образом можно вводить новые функции и устранять ошибки без перебоев в работе.
¿Cómo lograrlo? Hay varias maneras, aquí hay una de ellas:
- despliegue la versión №1 de su servicio
- realice la migración de la base de datos
- despliegue la versión №2 de su servicio en paralelo con la versión №1
- una vez que vea que la versión №2 funciona como se espera, retire la versión №1
- ¡Listo!
Fácil, ¿verdad? Lamentablemente, no es tan simple, y más adelante lo exploraremos en detalle. Por ahora, revisemos otro proceso de despliegue bastante común: blue green deployment.
¿Alguna vez has oído hablar de ? С Cloud Foundry это чрезвычайно легко сделать. Просто гляньте на , donde lo describimos con más detalle. En resumen, recordemos cómo hacer un despliegue azul-verde:
- asegúrese de que haya dos copias de su código de producción (“blue” y “green”);
- dirigir todo el tráfico al entorno blue, es decir, que las direcciones URL de producción apuntan allí;
- desplegar y probar todos los cambios de la aplicación en el entorno green;
- cambiar las direcciones URL de blue a green
El blue green deployment es un enfoque que le permite introducir nuevas funciones sin preocuparse de que la producción se rompa. Esto se debe a que incluso si ocurre algo, puede retroceder fácilmente al entorno anterior, simplemente “cambiando el interruptor”.
Después de leer todo lo anterior, puede preguntarse: ¿Qué relación tiene el zero downtime con el despliegue blue green?
Bueno, tienen bastante en común, ya que mantener dos copias del mismo entorno requiere esfuerzos dobles para su mantenimiento. Esta es la razón por la cual algunos equipos, según señala , siguen una variación de este enfoque:
Otra opción consiste en utilizar la misma base de datos, creando interruptores blue-green para las capas web y de dominio. En este enfoque, las bases de datos a menudo pueden ser un problema, especialmente cuando necesitas cambiar su esquema para soportar una nueva versión del software.
Y aquí llegamos al principal problema de este artículo. Base de datos. Echemos un vistazo a esta frase nuevamente.
realice la migración de la base de datos.
Ahora debes preguntarte: ¿qué pasa si el cambio en la base de datos es incompatible hacia atrás? ¿Mi primera versión de la aplicación se romperá? De hecho, eso es exactamente lo que sucederá...
Así que, a pesar de las enormes ventajas del zero downtime / blue green deployment, las empresas tienden a seguir el siguiente proceso de despliegue más seguro para sus aplicaciones:
- preparar un paquete con la nueva versión de la aplicación
- apagar la aplicación en ejecución
- ejecutar los scripts para la migración de la base de datos
- desplegar y ejecutar la nueva versión de la aplicación
En este artículo, describiremos detalladamente cómo puedes trabajar con la base de datos y el código para aprovechar las ventajas del deployment sin tiempo de inactividad.
Problemas con la base de datos
Si tienes una aplicación sin estado que no almacena datos en la base de datos, puedes lograr un zero downtime deployment de inmediato. Desafortunadamente, la mayoría del software debe almacenar datos en algún lugar. Por eso, debes pensarlo dos veces antes de hacer cualquier cambio en el esquema. Antes de profundizar en los detalles sobre cómo cambiar el esquema de manera que se permita un despliegue sin tiempo de inactividad, enfoquémonos primero en el esquema de control de versiones.
Esquema de control de versiones
En este artículo, utilizaremos como herramienta para el control de versiones (nota del traductor: se refiere a migraciones de base de datos). Naturalmente, también escribiremos una aplicación Spring Boot que tenga soporte integrado para Flyway y realice la migración del esquema durante la configuración del contexto de la aplicación. Al usar Flyway, puedes almacenar los scripts de migración en la carpeta de tus proyectos (por defecto en classpath:db/migration). Aquí puedes ver un ejemplo de tales archivos de migración
└── db
└── migration
├── V1__init.sql
├── V2__Add_surname.sql
├── V3__Final_migration.sql
└── V4__Remove_lastname.sqlEn este ejemplo, vemos 4 escenarios de migración que, si no se han realizado anteriormente, se ejecutarán uno tras otro al iniciar la aplicación. Vamos a examinar 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 habla por sí mismo: puedes usar SQL para definir cómo debe cambiar tu base de datos. Para más información sobre Spring Boot y Flyway, consulta la .
Al usar una herramienta de gestión de versiones con Spring Boot, obtienes 2 grandes ventajas:
- separa los cambios de la base de datos de los cambios del código
- la migración de la base de datos ocurre junto con el despliegue de tu aplicación, es decir, tu proceso de despliegue se simplifica
Resolución de problemas de la base de datos
En la siguiente sección del artículo, nos centraremos en dos enfoques para los cambios en la base de datos.
- incompatibilidad hacia atrás
- compatibilidad hacia atrás
El primero se considerará como una advertencia de que no debes realizar un despliegue sin tiempo de inactividad sin preparación previa... El segundo ofrece una solución sobre cómo se puede realizar un despliegue sin interrupciones y mantener al mismo tiempo la compatibilidad hacia atrás.
Nuestro proyecto en el que trabajaremos será una aplicación simple de Spring Boot Flyway, que contendrá Persona con first_name y last_name en la base de datos (nota del traductor: Persona es una tabla, y first_name y last_name son los campos dentro de ella). Queremos renombrar last_name en apellido.
Suposiciones
Antes de profundizar en los detalles, es necesario establecer un par de suposiciones sobre nuestras aplicaciones. El principal resultado que queremos lograr será un proceso bastante sencillo.
Nota. Consejo PRO de negocios. Simplificar procesos puede ahorrarte mucho dinero en mantenimiento (¡cuantas más personas trabajen en tu empresa, más dinero puedes ahorrar)!
No se debe hacer un retroceso de la base de datos
Esto simplifica el proceso de despliegue (algunos retrocesos de la base de datos son prácticamente imposibles, como el retroceso de una eliminación). Preferimos retroceder solo las aplicaciones. Así, incluso si tienes diferentes bases de datos (como SQL y NoSQL), tu pipeline de despliegue será igual.
Es necesario que SIEMPRE haya la posibilidad de retroceder la aplicación a una versión anterior (no más).
La reversión solo debe realizarse cuando sea necesario. Si la versión actual presenta un error que es difícil de solucionar, debemos 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 de la base de datos para más de un despliegue sería extremadamente complicado y costoso.
Nota. Para una mejor legibilidad, en este artículo modificaremos la versión principal de la aplicación.
Paso 1: Estado inicial
Versión de la aplicación: 1.0.0
Versión de la 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 en el código
La aplicación guarda datos de Person 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
Veamos un ejemplo de cómo cambiar el nombre de una columna:
Atención. El siguiente ejemplo intencionalmente provocará un fallo. Lo mostramos para ilustrar el problema de compatibilidad de la base de datos.
Versión de la aplicación: 2.0.0.BAD
Versión de la base de datos: v2bad
Comentario
Los cambios actuales NO nos permiten ejecutar dos instancias (la antigua y la nueva) al mismo tiempo. Por lo tanto, alcanzar un despliegue sin tiempo de inactividad sería difícil de lograr (dada la suposición, es prácticamente imposible).
Pruebas A/B
La situación actual es que tenemos una aplicación de la versión 1.0.0, desplegada en producción, y la base de datos v1. Debemos desplegar una segunda instancia de la aplicación, versión 2.0.0.BAD, y actualizar la base de datos a v2bad.
Pasos:
- se ha desplegado una nueva instancia de la aplicación, versión
2.0.0.BAD, que actualiza la base de datos av2bad - en la base de datos
v2badcolumnalast_nameya no existe — se ha cambiado porapellido - la actualización de la base de datos y la aplicación se completó con éxito, y algunas instancias están funcionando en
1.0.0, otras en2.0.0.BAD. Todas están conectadas a la base de datosv2bad - todas las instancias de la versión
1.0.0empezarán a generar errores porque intentarán insertar datos en la columnalast_name, que ya no existe - todas las instancias de la versión
2.0.0.BADfuncionarán sin problemas
Como puedes ver, si hacemos cambios incompatibles en 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 un despliegue A/B (nota del traductor: probablemente el autor se refería a pruebas A/B) decidimos que necesitamos revertir la aplicación a la versión 1.0.0. Supongamos que no queremos hacer una reversión de la base de datos.
Pasos:
- detenemos la instancia de la aplicación de la versión
2.0.0.BAD - la base de datos todavía
v2bad - ya que la versión
1.0.0no entiende qué esapellido, veremos errores - el infierno se desató, ya no podemos volver
Como pueden ver, si hacemos cambios incompatibles hacia atrás en la BD y la aplicación, no podemos regresar a la versión anterior.
Registros de ejecución del script
Escenario incompatible hacia atrás:
01) Ejecutar 1.0.0
02) Esperar a que la app (1.0.0) inicie
03) Generar una persona llamando a POST localhost:9991/person a la versión 1.0.0
04) Ejecutar 2.0.0.BAD
05) Esperar a que la app (2.0.0.BAD) inicie
06) Generar una persona llamando a POST localhost:9991/person a la versión 1.0.0 <-- esto debería fallar
07) Generar una persona llamando a POST localhost:9992/person a la versión 2.0.0.BAD <-- esto debería pasar
Iniciando la app en la versión 1.0.0
Generando una persona en la versión 1.0.0
Enviando un POST a 127.0.0.1:9991/person. Esta es la respuesta:
{"firstName":"b73f639f-e176-4463-bf26-1135aace2f57","lastName":"b73f639f-e176-4463-bf26-1135aace2f57"}
Iniciando la app en la versión 2.0.0.BAD
Generando una persona en la versión 1.0.0
Enviando un POST a 127.0.0.1:9991/person. Esta es la respuesta:
curl: (22) La URL solicitada devolvió error: 500 Internal Server Error
Generando una persona en la versión 2.0.0.BAD
Enviando un POST a 127.0.0.1:9995/person. Esta es la respuesta:
{"firstName":"e156be2e-06b6-4730-9c43-6e14cfcda125","surname":"e156be2e-06b6-4730-9c43-6e14cfcda125"}Cambios en la base de datos
Script de migración que renombra last_name en apellido
Script original 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');Script que renombra last_name.
-- Este cambio es incompatible con versiones anteriores - no se puede hacer pruebas A/B
ALTER TABLE PERSON CHANGE last_name surname VARCHAR;Cambios en el código
Hemos cambiado el nombre del campo lastName en apellido.
Renombrando la columna de manera compatible hacia atrás
Esta es la situación más común con la que podemos enfrentarnos. Necesitamos realizar cambios incompatibles hacia atrás. Ya hemos demostrado que para desplegar sin interrupciones no debemos simplemente aplicar la migración de la base de datos sin acciones adicionales. En esta sección del artículo realizaremos 3 despliegues de la aplicación junto con las migraciones de la base de datos para lograr el resultado deseado y al mismo tiempo mantener la compatibilidad hacia atrás.
Nota. Recordemos que tenemos una base de datos de la versión
v1. Contiene columnasfirst_nameylast_name. Necesitamos cambiarlast_nameenapellido. También tenemos una aplicación de la versión1.0.0,que aún no utilizaapellido.
Paso 2: Agregar surname
Versión de la aplicación: 2.0.0
Versión de la base de datos: v2
Comentario
Al agregar una nueva columna y copiar su contenido, creamos cambios de base de datos compatibles hacia atrás. Al mismo tiempo, si revertimos el JAR o tenemos un JAR antiguo funcionando, no fallará durante la ejecución.
Desplegando nueva versión
Pasos:
- realizar la migración de la base de datos para crear la nueva columna
apellido. Ahora su base de datos de versiónv2 - copie los datos de
last_nameenapellido. Presta atención, si tiene muchos de estos datos, ¡debe considerar la migración por lotes! - escriba el código donde se utilizan AMBOS y nuevocomo antiguo columna. Ahora su aplicación está en la versión
2.0.0 - lea el valor de la columna
apellido, si no estánull, o de last_name, siapellidono está definido. Puede eliminargetLastName()del código, ya que daránullal revertir su aplicación de3.0.0hasta2.0.0.
Si está utilizando Spring Boot Flyway, estos dos pasos se ejecutarán al iniciar la versión 2.0.0 de la aplicación. Si está ejecutando la herramienta de gestión de versiones de la base de datos manualmente, deberá realizar estas dos acciones por separado (primero actualice manualmente la versión de la base de datos y luego despliegue la nueva aplicación).
Es importante. Recuerde que la nueva columna NO DEBE ser NOT NULL. Si está realizando una reversión, la antigua aplicación no conoce la nueva columna y no la establecerá durante
Insert.Pero si añade esta restricción, y su base de datos estáv2, esto requerirá establecer un valor en la nueva columna. Lo que provocará violaciones de restricciones.Es importante. Debería eliminar el método
getLastName(), ya que en la versión3.0.0no existe el concepto de columnalast_name. Esto significa que allí se establecerán null. Puede dejar el método y añadir verificaciones sobrenull, pero una solución mucho mejor sería asegurarse de que en la lógicagetSurname()elija un valor no nulo correcto.
Pruebas A/B
La situación actual es que tenemos una aplicación de la versión 1.0.0, desplegada en producción, y la base de datos en v1. Debemos desplegar la segunda instancia de la versión de la aplicación 2.0.0, que actualizará la base de datos a v2.
Pasos:
- se ha desplegado una nueva instancia de la aplicación, versión
2.0.0, que actualiza la base de datos av2 - mientras tanto, algunas solicitudes fueron procesadas por instancias de la versión
1.0.0 - la actualización se realizó con éxito, y tiene varias instancias de la versión de la aplicación
1.0.0y otras versiones2.0.0.Todos se comunican con la base de datos env2 - versión
1.0.0no utiliza el campo surname en la base de datos, mientras que la versión2.0.0lo utiliza. No interfieren entre sí y no deberían ocurrir errores. - versión
2.0.0almacena datos tanto en la columna antigua como en la nueva, lo que asegura la compatibilidad hacia atrás
Es importante. Si tiene solicitudes que cuentan elementos en función de los valores de la columna antigua/nueva, debe recordar que ahora tiene duplicación de valores (probablemente todavía estén migrando). Por ejemplo, si desea contar el número de usuarios cuya apellido (cualquiera que sea el nombre de la columna) comenzaba con la letra
A, entonces, hasta que finalice la migración de datos (old→nuevocolumna) puede tener datos inconsistentes si consulta la nueva columna.
Reversión de la aplicación
Ahora tenemos la versión de la aplicación 2.0.0 y la base de datos en v2.
Pasos:
- retroceda su aplicación a la versión
1.0.0. - versión
1.0.0no utiliza el campoapellido, por lo que la reversión debe ser exitosa
Cambios en la DB
La base de datos contiene una columna llamada last_name.
Script original 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');Script de adición apellido.
Atención. Recuerde que NO SE PUEDEN AÑADIR restricciones NOT NULL a la columna añadida. Si realiza una reversión del JAR, la versión anterior no tendrá conocimiento de la columna añadida y automáticamente le asignará un valor NULL. En caso de existir tal restricción, la antigua aplicación simplemente fallará.
-- NOTA: Este campo no puede tener la restricción NOT NULL porque si realiza una reversión, la versión anterior no conocerá este campo
-- y siempre lo establecerá en NULL
ALTER TABLE PERSONA ADD apellido varchar(255);
-- ASUMIMOS QUE ES UNA MIGRACIÓN RÁPIDA - DE LO CONTRARIO TENDRÍAMOS QUE MIGRAR EN LOTES
UPDATE PERSONA SET PERSONA.apellido = PERSONA.apellido_inicialCambios en el código
Mantenemos los datos tanto en last_name, así como en apellido. Al mismo tiempo leemos de last_name, ya que esta columna es la más actual. Durante el despliegue, algunas consultas pueden haber sido procesadas por una instancia de la aplicación que aún no ha sido actualizada.
/*
* 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: Eliminación de apellido del código
Versión de la aplicación: 3.0.0
Versión de la base de datos:v3
Comentario
Nota del traductor: Aparentemente, en el artículo original, el autor copió erróneamente el texto de este bloque del paso 2. En este paso, se deben realizar cambios en el código de la aplicación dirigidos a eliminar la funcionalidad que utiliza la columna last_name.
Al agregar una nueva columna y copiar su contenido, hemos creado cambios en la base de datos que son compatibles hacia atrás. Además, si revertimos el JAR o si tenemos un JAR anterior en funcionamiento, no fallará durante la ejecución.
Reversión de la aplicación
Actualmente, tenemos una aplicación de la versión 3.0.0 y una base de datos v3. La versión 3.0.0 no guarda datos en last_name. Esto significa que en apellido se almacena la información más actualizada.
Pasos:
- retroceda su aplicación a la versión
2.0.0. - versión
2.0.0usa y tomarálast_nameyapellido. - versión
2.0.0, si no es nulo, de lo contrario —apellidoNo hay cambios estructurales en la base de datos. Se ejecuta el siguiente script que realiza la migración final de los datos antiguos:last_name
Cambios en la base de datos
-- ASUMIMOS QUE ES UNA MIGRACIÓN RÁPIDA - DE LO CONTRARIO TENDRÍAMOS QUE MIGRAR EN LOTES -- TAMBIÉN NO ESTAMOS VERIFICANDO SI NO ESTAMOS SOBRESCRIBIENDO ENTRADAS EXISTENTES. TENDRÍAMOS QUE COMPARAR -- LAS VERSIONES DE ENTRADA PARA ASEGURAR QUE SI YA HAY UNA ENTRADA CON UN NÚMERO DE VERSIÓN MÁS ALTO -- NO LA SOBRESCRIBIREMOS. UPDATE PERSONA SET PERSONA.apellido = PERSONA.apellido_inicial;-- ELIMINANDO LA RESTRICCIÓN NOT NULL; DE LO CONTRARIO INTENTARÁ INSERTAR UN VALOR NULL DEL APELLIDO -- CON UNA RESTRICCIÓN NOT NULL. ALTER TABLE PERSONA MODIFY COLUMN apellido_inicial varchar(255) NULL DEFAULT NULL;
Nota del traductor: La descripción de este bloque también fue copiada erróneamente por el autor del paso 2. De acuerdo con la lógica del relato del artículo, los cambios en el código en este paso deben dirigirse a eliminar de él los elementos que realizan el trabajo con la columnaCambios en el código
Almacenamos datos tanto en last_name.
apellido. last_name, así como en apellido. Además, leemos de la columna last_name, ya que es la más relevante. Durante el despliegue, algunas solicitudes pueden ser procesadas por una instancia que aún no ha sido actualizada.
/*
* 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 last_name de la base de datos
Versión de la aplicación: 4.0.0
Versión de la base de datos: v4
Comentario
Debido a que el código de versión 3.0.0 no utilizó la columna last_name, durante la ejecución no ocurrirá nada malo si retrocedemos a 3.0.0 después de eliminar la columna de la base de datos.
Registros de ejecución del script
Lo haremos de la siguiente manera:
01) Ejecutar 1.0.0
02) Esperar a que la aplicación (1.0.0) arranque
03) Generar una persona llamando a POST localhost:9991/person a la versión 1.0.0
04) Ejecutar 2.0.0
05) Esperar a que la aplicación (2.0.0) arranque
06) Generar una persona llamando a POST localhost:9991/person a la versión 1.0.0
07) Generar una persona llamando a POST localhost:9992/person a la versión 2.0.0
08) Terminar la aplicación (1.0.0)
09) Ejecutar 3.0.0
10) Esperar a que la aplicación (3.0.0) arranque
11) Generar una persona llamando a POST localhost:9992/person a la versión 2.0.0
12) Generar una persona llamando a POST localhost:9993/person a la versión 3.0.0
13) Terminar la aplicación (3.0.0)
14) Ejecutar 4.0.0
15) Esperar a que la aplicación (4.0.0) arranque
16) Generar una persona llamando a POST localhost:9993/person a la versión 3.0.0
17) Generar una persona llamando a POST localhost:9994/person a la versión 4.0.0
Iniciando la aplicación en la versión 1.0.0
Generar una persona en la versión 1.0.0
Enviando un post a 127.0.0.1:9991/person. Esta es la respuesta:
{"firstName":"52b6e125-4a5c-429b-a47a-ef18bbc639d2","lastName":"52b6e125-4a5c-429b-a47a-ef18bbc639d2"}
Iniciando la aplicación en la versión 2.0.0
Generar una persona en la versión 1.0.0
Enviando un post a 127.0.0.1:9991/person. Esta es la respuesta:
{"firstName":"e41ee756-4fa7-4737-b832-e28827a00deb","lastName":"e41ee756-4fa7-4737-b832-e28827a00deb"}
Generar una persona en la versión 2.0.0
Enviando un post a 127.0.0.1:9992/person. Esta es la respuesta:
{"firstName":"0c1240f5-649a-4bc5-8aa9-cff855f3927f","lastName":"0c1240f5-649a-4bc5-8aa9-cff855f3927f","surname":"0c1240f5-649a-4bc5-8aa9-cff855f3927f"}
Terminando la aplicación 1.0.0
Iniciando la aplicación en la versión 3.0.0
Generar una persona en la versión 2.0.0
Enviando un post a 127.0.0.1:9992/person. Esta es la respuesta:
{"firstName":"74d84a9e-5f44-43b8-907c-148c6d26a71b","lastName":"74d84a9e-5f44-43b8-907c-148c6d26a71b","surname":"74d84a9e-5f44-43b8-907c-148c6d26a71b"}
Generar una persona en la versión 3.0.0
Enviando un post a 127.0.0.1:9993/person. Esta es la respuesta:
{"firstName":"c6564dbe-9ab5-40ae-9077-8ae6668d5862","surname":"c6564dbe-9ab5-40ae-9077-8ae6668d5862"}
Terminando la aplicación 2.0.0
Iniciando la aplicación en la versión 4.0.0
Generar una persona en la versión 3.0.0
Enviando un post a 127.0.0.1:9993/person. Esta es la respuesta:
{"firstName":"cbe942fc-832e-45e9-a838-0fae25c10a51","surname":"cbe942fc-832e-45e9-a838-0fae25c10a51"}
Generar una persona en la versión 4.0.0
Enviando un post a 127.0.0.1:9994/person. Esta es la respuesta:
{"firstName":"ff6857ce-9c41-413a-863e-358e2719bf88","surname":"ff6857ce-9c41-413a-863e-358e2719bf88"}Cambios en la base de datos
En relación con v3 simplemente estamos eliminando la columna last_name y agregando las restricciones que faltan.
-- ELIMINAR LA COLUMNA
ALTER TABLE PERSON DROP last_name;
-- AGREGAR RESTRICCIONES
UPDATE PERSON SET surname='' WHERE surname IS NULL;
ALTER TABLE PERSON ALTER COLUMN surname VARCHAR NOT NULL;Cambios en el código
No hay cambios en el código.
Salida
Hemos revertido exitosamente un cambio de nombre de columna incompatible aplicando varios despliegues compatibles. A continuación, un resumen de las acciones realizadas:
- despliegue de la aplicación versión
1.0.0conv1esquema de la base de datos (nombre de columna =last_name) - despliegue de la aplicación versión
2.0.0,que mantiene los datos enlast_nameyapellido. La aplicación lee desdelast_name. La base de datos está en versiónv2, que contiene columnas comolast_name, comosurname. surnamees una copia de last_name. (NOTA: esta columna no debe tener la restricción not null) - despliegue de la aplicación versión
3.0.0, que guarda datos solo enapellidoy lee desde surname. En cuanto a la base de datos, se está realizando la última migraciónlast_nameenapellido. También se retira la restricción NOT NULL delast_name. La base de datos está actualmente en versiónv3 - despliegue de la aplicación versión
4.0.0— no se realizan cambios en el código. El despliegue de la base de datosv4, que eliminalast_name. Aquí puedes agregar cualquier restricción faltante en la base de datos.
Siguiendo este enfoque, siempre puedes retroceder una versión sin romper la compatibilidad entre la base de datos / aplicación.
Código
Todo el código utilizado en este artículo está disponible en . A continuación, se ofrece una descripción adicional.
Proyectos
Después de clonar el repositorio, verás la siguiente estructura de carpetas.
├── boot-flyway-v1 - versión 1.0.0 de la aplicación con v1 del esquema
├── boot-flyway-v2 - versión 2.0.0 de la aplicación con v2 del esquema (compatible hacia atrás - la aplicación se puede revertir)
├── boot-flyway-v2-bad - versión 2.0.0.BAD de la aplicación con v2bad del esquema (incompatible hacia atrás - la aplicación no se puede revertir)
├── boot-flyway-v3 - versión 3.0.0 de la aplicación con v3 del esquema (la aplicación se puede revertir)
└── boot-flyway-v4 - versión 4.0.0 de la aplicación con v4 del esquema (la aplicación se puede revertir)Scripts
Puedes ejecutar los scripts descritos en los scripts a continuación, que demostrarán cambios compatibles y no compatibles hacia atrás en la base de datos.
Para ver el caso de cambios compatibles hacia atrás, ejecuta:
./scripts/scenario_backward_compatible.shY para ver el caso de cambios no compatibles hacia atrás, ejecuta:
./scripts/scenario_backward_incompatible.shEjemplo de Spring Boot Flyway
Todos los ejemplos están tomados de Ejemplo de Spring Boot Flyway.
Puedes mirar http://localhost:8080/flyway, allí está la lista de scripts.
Este ejemplo también incluye la consola H2 (en http://localhost:8080/h2-console), para que puedas ver el estado de la base de datos (la URL jdbc por defecto es — jdbc:h2:mem:testdb).
Adicionalmente
También lee otros artículos en nuestro blog:
Fuente: habr.com
