
Este artículo explica en detalle cómo resolver los problemas asociados con la compatibilidad de la base de datos durante la implementación. Le diremos qué puede pasarle a sus aplicaciones en el producto si intenta realizar una implementación sin ninguna preparación preliminar. Luego, pasaremos por las etapas del ciclo de vida de la aplicación, que son necesarias para tener un tiempo de inactividad cero ( aprox. Transl .: más adelante, tiempo de inactividad cero ). El resultado de nuestras operaciones será la aplicación de un 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, los encontrará en GitHub .
Introduccion
Implementación de tiempo de inactividad cero
¿Cuál es la implementación mística de tiempo de inactividad cero ? Podemos decir esto cuando su aplicación se implementa para que pueda introducir con éxito una nueva versión de la aplicación para producción, mientras que el usuario no nota su inaccesibilidad. Desde el punto de vista del usuario y de la empresa, este es el mejor escenario de implementación posible, ya que de esta manera puede introducir nuevas funciones y eliminar errores sin interrupción.
¿Cómo lograr esto? Hay varias formas, aquí hay una de ellas:
- expanda la versión 1 de su servicio
- migrar la base de datos
- Implemente la versión 2 de su servicio en paralelo con la versión 1
- tan pronto como vea que la versión número 2 funciona como debería, elimine la versión número 1
- hecho!
Fácil, verdad? Desafortunadamente, esto no es tan simple, y lo consideraremos 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 del despliegue verde azul ? Con Cloud Foundry, esto es extremadamente fácil de hacer. Solo eche un vistazo a este artículo donde lo describimos con más detalle. Resumiendo brevemente, recordamos cómo hacer un despliegue verde azul:
- Asegure la operación de 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 se apunten allí;
- Implemente y pruebe todos los cambios de la aplicación en un entorno verde
- cambiar las URL del entorno azul al verde
El despliegue verde azulado es un enfoque que le permite introducir fácilmente nuevas características sin preocuparse de que la producción se interrumpa. Esto se debe al hecho de que incluso si algo sucede, puede retroceder fácilmente al entorno anterior simplemente haciendo clic en un interruptor.
Después de leer todo lo anterior, puede hacer la pregunta: ¿Qué tiene que ver el tiempo de inactividad cero con la implementación verde azul?
Bueno, tienen mucho en común, porque admitir dos copias del mismo entorno requiere un doble esfuerzo para mantenerlas. Es por eso que algunos equipos, según Martin Fowler , se adhieren a las variaciones de este enfoque:
Otra opción es utilizar la misma base de datos, creando conmutadores azul-verdes para las capas web y de dominio. En este enfoque, las bases de datos a menudo pueden ser un problema, especialmente cuando necesita cambiar su esquema para admitir una nueva versión del software.
Y aquí llegamos al problema principal en este artículo. La base de datos Echemos otro vistazo a esta frase.
migrar la base de datos.
Ahora tiene que hacerse la pregunta: ¿qué sucede si cambiar la base de datos es incompatible con versiones anteriores? ¿No se romperá mi primera versión de la aplicación? De hecho, esto es exactamente lo que sucederá ...
Por lo tanto, incluso a pesar de los enormes beneficios del tiempo de inactividad cero / implementación verde azul, las empresas tienden a seguir el siguiente proceso de implementación más seguro para sus aplicaciones:
- preparar un paquete con una nueva versión de la aplicación
- cerrar una aplicación en ejecución
- ejecutar scripts para la migración de la base de datos
- implementar y lanzar la nueva versión de la aplicación
En este artículo, describiremos en detalle cómo puede trabajar con una base de datos y un código para aprovechar la implementación de tiempo de inactividad cero.
Problemas de 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 de tiempo de inactividad cero de inmediato. Desafortunadamente, la mayoría del software necesita almacenar datos en algún lugar. Es por eso que debe pensarlo dos veces antes de realizar cambios en el circuito. Antes de profundizar en los detalles de cómo cambiar el esquema para que sea posible implementarlo sin tiempo de inactividad, centrémonos primero en el esquema de control de versiones.
Esquema de control de versiones
En este artículo, utilizaremos Flyway como herramienta para el control de versiones ( aprox. Traducción: estamos hablando de migración de bases de datos ). Naturalmente, también escribiremos una aplicación Spring Boot que tenga soporte Flyway incorporado y migrará el circuito mientras configura el contexto de la aplicación. Al usar Flyway, puede almacenar scripts de migración en la carpeta de sus proyectos (de manera 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 escenarios de migración que, si no se ejecutaron antes, 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 habla perfectamente por sí mismo: puede usar SQL para determinar cómo se debe modificar su base de datos. Para obtener más información sobre Spring Boot y Flyway, consulte Spring Boot Docs .
El uso del control de versiones con Spring Boot le brinda 2 grandes beneficios:
- separa los cambios de la base de datos de los cambios de código
- la migración de la base de datos ocurre junto con el despliegue de su aplicación, es decir su proceso de implementación se simplifica
Resolver problemas de la base de datos
En la siguiente sección del artículo, nos centraremos en considerar 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 de tiempo de inactividad cero sin una preparación preliminar ... El segundo ofrece una solución sobre cómo puede 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 aplicación simple Spring Boot Flyway en la que hay una Person
con last_name
la base de datos ( aprox. Por Person
: Person
es una tabla, y irst_name
y last_name
son los campos en ella ). Queremos cambiar el last_name
de surname
por surname
.
Supuestos
Antes de profundizar en los detalles, necesitamos esbozar un par de suposiciones sobre nuestras aplicaciones. El principal resultado que queremos lograr será un proceso bastante simple.
Nota Negocio PRO-TIP. Los procesos de racionalización pueden ahorrarle mucho dinero en soporte (¡cuanta más gente trabaje en su empresa, más dinero puede ahorrar)!
No es necesario revertir la base de datos
Esto simplifica el proceso de implementación (algunas reversiones de la base de datos son casi imposibles, por ejemplo, eliminación de reversión). Preferimos deshacer las aplicaciones solamente. 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 debe hacerse solo si es necesario. Si hay un error en la versión actual que no es fácil de solucionar, deberíamos poder devolver la última versión de trabajo. Asumimos que esta última versión de trabajo es la anterior. Mantener el código y la compatibilidad de la base de datos para más de una implementación sería extremadamente difícil y costoso.
Nota Para una mayor legibilidad, en el marco de este artículo cambiaremos la versión principal de la aplicación.
Paso 1: estado inicial
Versión de aplicación: 1.0.0
Versión DB: v1
Comentario
Este será el estado inicial de la aplicación.
Cambios de DB
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 guarda los 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
Veamos un ejemplo de cómo cambiar el nombre de una columna:
Atencion El siguiente ejemplo se romperá intencionalmente. Mostramos esto para demostrar el problema de compatibilidad de la base de datos.
Versión de la aplicación: 2.0.0.BAD
Versión DB: v2bad
Comentario
Los cambios actuales NO nos permiten ejecutar dos instancias (antigua y nueva) al mismo tiempo. Por lo tanto, la implementación de tiempo de inactividad cero será difícil de lograr (suposiciones tomadas en cuenta, esto es prácticamente imposible).
Pruebas A / B
La situación actual es que tenemos una versión de aplicación 1.0.0,
implementada en prod, y una base de datos v1
. Debemos implementar la segunda instancia de la aplicación, versión 2.0.0.BAD
, y actualizar la base de datos a v2bad
.
Pasos:
- desplegó una nueva instancia de la aplicación versión
2.0.0.BAD
, que actualiza la base de datos a v2bad
- la columna
last_name
ya no existe en la v2bad
datos v2bad
, se cambió a surname
- Las actualizaciones de la base de datos y las aplicaciones fueron exitosas, y algunas instancias funcionan en
1.0.0
, otras en 2.0.0.BAD
. Todo lo relacionado con v2bad
- todas las instancias de la versión
1.0.0
comenzarán a arrojar errores porque intentarán insertar datos en la columna last_name
, que ya no es - todas las instancias de la versión
2.0.0.BAD
funcionarán sin problemas
Como puede ver, si hacemos cambios incompatibles con la base de datos y las aplicaciones, las pruebas A / B son imposibles.
Aplicación de reversión
Supongamos que después de intentar realizar la implementación A / B ( aprox. Traducción: probablemente, el autor se refería a las pruebas A / B aquí ), decidimos que necesitamos revertir la aplicación a la versión 1.0.0.
Supongamos que no queremos revertir la base de datos.
Pasos:
- paramos la instancia de la aplicación versión
2.0.0.BAD
- la base de datos sigue siendo
v2bad
1.0.0
versión 1.0.0
no comprende qué surname
, veremos errores- el infierno se liberó, ya no podemos regresar
Como puede ver, si realizamos cambios incompatibles con la base de datos y las aplicaciones, no podemos retroceder a la versión anterior.
Registros de ejecución de script
Backward incompatible scenario: 01) Run 1.0.0 02) Wait for the app (1.0.0) to boot 03) Generate a person by calling POST localhost:9991/person to version 1.0.0 04) Run 2.0.0.BAD 05) Wait for the app (2.0.0.BAD) to boot 06) Generate a person by calling POST localhost:9991/person to version 1.0.0 <-- this should fail 07) Generate a person by calling POST localhost:9992/person to version 2.0.0.BAD <-- this should pass Starting app in version 1.0.0 Generate a person in version 1.0.0 Sending a post to 127.0.0.1:9991/person. This is the response: {"firstName":"b73f639f-e176-4463-bf26-1135aace2f57","lastName":"b73f639f-e176-4463-bf26-1135aace2f57"} Starting app in version 2.0.0.BAD Generate a person in version 1.0.0 Sending a post to 127.0.0.1:9991/person. This is the response: curl: (22) The requested URL returned error: 500 Internal Server Error Generate a person in version 2.0.0.BAD Sending a post to 127.0.0.1:9995/person. This is the response: {"firstName":"e156be2e-06b6-4730-9c43-6e14cfcda125","surname":"e156be2e-06b6-4730-9c43-6e14cfcda125"}
Cambios de DB
Script de migración que renombra last_name
a surname
Guión de Flyway 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');
Un script que cambia el nombre de 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 el nombre del campo apellido por 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 hacia atrás incompatibles. Ya hemos demostrado que para una implementación sin tiempo de inactividad, no solo debemos aplicar la migración de la base de datos sin acciones adicionales. En esta sección del artículo, realizaremos 3 implementaciones 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 con versiones anteriores.
Nota Recordemos que tenemos una versión de base de datos v1
. Contiene las columnas first_name
y last_name
. Debemos cambiar el surname
por surname
. También tenemos una versión de la aplicación 1.0.0,
que aún no usa el surname
.
Paso 2: agregar apellido
Versión de aplicación: 2.0.0
Versión DB: v2
Comentario
Al agregar una nueva columna y copiar su contenido, creamos cambios de bases de datos compatibles con versiones anteriores. Al mismo tiempo, si revertimos el JAR o tenemos un JAR antiguo que funciona, no se romperá en el tiempo de ejecución.
Lanzamos la nueva versión
Pasos:
- migre la base de datos para crear una nueva columna de
surname
. Ahora tu versión de db v2
- Copie los datos de
last_name
al surname
. Tenga en cuenta que si tiene muchos de estos datos, debe considerar la migración por lotes. - escriba el código donde AMBOS se utilizan tanto la columna nueva como la antigua . Ahora su aplicación versión
2.0.0
- lea el valor de la columna de
surname
si no es null
, o de l ast_name
si no se especifica el surname
. Puede eliminar getLastName()
del código, ya que devolverá null
cuando getLastName()
su aplicación de 3.0.0
a 2.0.0
.
Si está utilizando Spring Boot Flyway, estos dos pasos se realizarán durante el lanzamiento de la versión 2.0.0
aplicación. Si ejecuta la herramienta de control de versiones de la base de datos manualmente, tendrá que hacer dos cosas diferentes para esto (primero actualice la versión de DB manualmente y luego implemente una nueva aplicación).
Es importante Recuerde que una columna recién creada NO DEBE NO SER NULA . Si retrocede, la aplicación anterior no conoce la nueva columna y no la instalará durante la Insert.
Pero si agrega esta restricción, y su base de datos será v2
, requerirá establecer el valor de la nueva columna. Lo que conducirá a violaciones de las restricciones.
Es importante Debe eliminar el método getLastName()
, ya que la versión 3.0.0
no tiene el concepto de una columna de last_name
en el código. Esto significa que se establecerá nulo allí. Puede abandonar el método y agregar comprobaciones null
, pero una solución mucho mejor sería asegurarse de que en la lógica getSurname()
haya elegido el valor correcto distinto de cero.
Pruebas A / B
La situación actual es que tenemos una aplicación versión 1.0.0
implementada en el producto y una base de datos en v1
. Necesitamos implementar la segunda instancia de la versión 2.0.0
de la aplicación, que actualizará la base de datos a v2
.
Pasos:
- desplegó una nueva instancia de la versión
2.0.0
de la aplicación, que actualiza la base de datos a v2
- Mientras tanto, algunas solicitudes fueron manejadas por instancias de la versión
1.0.0
- la actualización se realizó correctamente y tiene varias instancias de trabajo de la versión de la aplicación
1.0.0
y el resto de la versión 2.0.0.
Todos se comunican con la base de datos en v2
- la versión
1.0.0
no utiliza la columna de apellido en la base de datos, pero sí la versión 2.0.0
. No interfieren entre sí y no debe haber errores. - la versión
2.0.0
almacena datos en la columna anterior y en la nueva, lo que proporciona compatibilidad con versiones anteriores
Es 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 el número de usuarios cuyo apellido (no importa cuál sea el nombre de la columna) comienza con la letra A
, antes de que se complete la migración de datos ( old
→ new
columna), puede tener datos inconsistentes si ejecuta una consulta en una nueva columna.
Aplicación de reversión
Ahora tenemos una versión de aplicación 2.0.0
y una base de datos en v2
.
Pasos:
- revierta su aplicación a la versión
1.0.0
. - la versión
1.0.0
no utiliza la columna de surname
en la base de datos, por lo que la reversión debe ser exitosa
Cambios de DB
La base de datos contiene una columna llamada last_name
.
Guión de Flyway 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');
El script para agregar surname
.
Atencion Recuerde que NO PUEDE AGREGAR ninguna restricción NOT NULL a la columna agregada. Si revierte el JAR, la versión anterior no tiene idea acerca de la columna agregada, y la establecerá automáticamente en NULL. Si existe tal restricció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 tanto en surname
como en surname
. Al mismo tiempo, leemos desde last_name
, ya que esta columna es más relevante. Durante el proceso de implementación, algunas solicitudes pueden haber sido manejadas 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 last_name del código
Versión de aplicación: 3.0.0
Versión DB: v3
Comentario
Nota trans .: Aparentemente, el autor copió por error el texto de este bloque del paso 2 en el artículo original. En este paso, se deben realizar cambios en el código de la aplicación para eliminar la funcionalidad que utiliza la columna last_name
.
Al agregar una nueva columna y copiar su contenido, creamos cambios de bases de datos compatibles con versiones anteriores. Además, si revertimos el JAR o tenemos un JAR antiguo que funciona, no se romperá en tiempo de ejecución.
Aplicación de reversión
Actualmente tenemos la aplicación versión 3.0.0
y la base de datos v3
. La versión 3.0.0
no guarda datos en last_name
. Esto significa que el surname
almacena la información más actualizada.
Pasos:
- revierta su aplicación a la versión
2.0.0
. - La versión
2.0.0
utiliza el surname
y el surname
. - la versión
2.0.0
tomará surname
si no es nulo, de lo contrario last_name
Cambios de DB
No hay cambios estructurales en la base de datos. Se ejecuta el siguiente script, que realiza la migración final de 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 trans .: La descripción de este bloque también fue copiada erróneamente por el autor del paso 2. De acuerdo con la lógica de la historia del artículo, los cambios en el código en este paso deben estar dirigidos a eliminar de él elementos que funcionan con la columna last_name
.
Almacenamos datos tanto en surname.
como en surname.
Además, leemos de la columna last_name
, ya que es 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 last_name de la base de datos
Versión de aplicación: 4.0.0
Versión DB: v4
Comentario
Debido al hecho de que el código de la versión 3.0.0
no usó la columna last_name
, no ocurrirá nada malo durante la ejecución si volvemos a 3.0.0
después de eliminar la columna de la base de datos.
Registros de ejecución de script
We will do it in the following way: 01) Run 1.0.0 02) Wait for the app (1.0.0) to boot 03) Generate a person by calling POST localhost:9991/person to version 1.0.0 04) Run 2.0.0 05) Wait for the app (2.0.0) to boot 06) Generate a person by calling POST localhost:9991/person to version 1.0.0 07) Generate a person by calling POST localhost:9992/person to version 2.0.0 08) Kill app (1.0.0) 09) Run 3.0.0 10) Wait for the app (3.0.0) to boot 11) Generate a person by calling POST localhost:9992/person to version 2.0.0 12) Generate a person by calling POST localhost:9993/person to version 3.0.0 13) Kill app (3.0.0) 14) Run 4.0.0 15) Wait for the app (4.0.0) to boot 16) Generate a person by calling POST localhost:9993/person to version 3.0.0 17) Generate a person by calling POST localhost:9994/person to version 4.0.0 Starting app in version 1.0.0 Generate a person in version 1.0.0 Sending a post to 127.0.0.1:9991/person. This is the response: {"firstName":"52b6e125-4a5c-429b-a47a-ef18bbc639d2","lastName":"52b6e125-4a5c-429b-a47a-ef18bbc639d2"} Starting app in version 2.0.0 Generate a person in version 1.0.0 Sending a post to 127.0.0.1:9991/person. This is the response: {"firstName":"e41ee756-4fa7-4737-b832-e28827a00deb","lastName":"e41ee756-4fa7-4737-b832-e28827a00deb"} Generate a person in version 2.0.0 Sending a post to 127.0.0.1:9992/person. This is the response: {"firstName":"0c1240f5-649a-4bc5-8aa9-cff855f3927f","lastName":"0c1240f5-649a-4bc5-8aa9-cff855f3927f","surname":"0c1240f5-649a-4bc5-8aa9-cff855f3927f"} Killing app 1.0.0 Starting app in version 3.0.0 Generate a person in version 2.0.0 Sending a post to 127.0.0.1:9992/person. This is the response: {"firstName":"74d84a9e-5f44-43b8-907c-148c6d26a71b","lastName":"74d84a9e-5f44-43b8-907c-148c6d26a71b","surname":"74d84a9e-5f44-43b8-907c-148c6d26a71b"} Generate a person in version 3.0.0 Sending a post to 127.0.0.1:9993/person. This is the response: {"firstName":"c6564dbe-9ab5-40ae-9077-8ae6668d5862","surname":"c6564dbe-9ab5-40ae-9077-8ae6668d5862"} Killing app 2.0.0 Starting app in version 4.0.0 Generate a person in version 3.0.0 Sending a post to 127.0.0.1:9993/person. This is the response: {"firstName":"cbe942fc-832e-45e9-a838-0fae25c10a51","surname":"cbe942fc-832e-45e9-a838-0fae25c10a51"} Generate a person in version 4.0.0 Sending a post to 127.0.0.1:9994/person. This is the response: {"firstName":"ff6857ce-9c41-413a-863e-358e2719bf88","surname":"ff6857ce-9c41-413a-863e-358e2719bf88"}
Cambios de DB
Para v3
simplemente last_name
columna last_name
y agregamos 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 el cambio de nombre de columna incompatible con versiones anteriores al realizar varias implementaciones compatibles con versiones anteriores. A continuación se muestra un resumen de los pasos dados:
- implementación de la aplicación versión
1.0.0
con esquema de base de datos v1
(nombre de columna = last_name
) last_name
versión 2.0.0,
aplicación 2.0.0,
que guarda los datos en surname
y surname
. La aplicación lee desde last_name
. La base de datos está en la versión v2
, que contiene las columnas de surname. surname
y surname. surname
surname. surname
es una copia de l ast_name
. (NOTA: esta columna no debe tener una restricción no nula)3.0.0
, surname
surname. , last_name
surname
. NOT NULL last_name
. v3
4.0.0
— . v4
, last_name
. .
, , / .
Código
, , Github . .
Proyectos
, .
├── 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/scenario_backward_compatible.sh
, :
./scripts/scenario_backward_incompatible.sh
Spring Boot Sample Flyway
Spring Boot Sample Flyway.
http://localhost:8080/flyway
, .
H2 ( http://localhost:8080/h2-console
), (URL jdbc — jdbc:h2:mem:testdb
).
Opcional
Lea también otros artículos en nuestro blog: