Déploiement sans interruption et bases de données

image


Cet article explique en détail comment résoudre les problèmes associés à la compatibilité de la base de données lors du déploiement. Nous vous dirons ce qui peut arriver à vos applications sur le prod si vous essayez d'effectuer un déploiement sans aucune préparation préalable. Ensuite, nous passerons par les étapes du cycle de vie de l'application, qui sont nécessaires pour avoir un temps d'arrêt nul ( environ Transl.: Plus loin - zéro temps d'arrêt ). Le résultat de nos opérations sera l'application d'un changement de base de données incompatible en amont d'une manière compatible en amont.


Si vous voulez comprendre les exemples de code de l'article, vous les trouverez sur GitHub .


Présentation


DĂ©ploiement sans temps d'arrĂŞt


Qu'est-ce que le déploiement mystique zéro temps d'arrêt ? Nous pouvons le dire lorsque votre application est déployée afin que vous puissiez introduire avec succès une nouvelle version de l'application pour la production, tandis que l'utilisateur ne remarque pas son inaccessibilité. Du point de vue de l'utilisateur et de l'entreprise, c'est le meilleur scénario de déploiement possible, car vous pouvez ainsi introduire de nouvelles fonctions et éliminer les erreurs sans interruption.


Comment y parvenir? Il y a plusieurs façons, en voici une:


  • Ă©tendre la version 1 de votre service
  • migrer la base de donnĂ©es
  • dĂ©ployer la version 2 de votre service en parallèle avec la version 1
  • dès que vous voyez que la version numĂ©ro 2 fonctionne comme il se doit, supprimez la version numĂ©ro 1
  • c'est fait!

Facile, non? Malheureusement, ce n'est pas si simple, et nous l'examinerons en détail plus tard. Voyons maintenant un autre processus de déploiement assez courant - le déploiement bleu vert.


Avez-vous déjà entendu parler du déploiement bleu vert ? Avec Cloud Foundry, c'est extrêmement facile à faire. Jetez un œil à cet article où nous le décrivons plus en détail. En résumant brièvement, nous rappelons comment faire un déploiement bleu vert:


  • Assurer le fonctionnement de deux copies de votre code de production («bleu» et «vert»);
  • diriger tout le trafic vers l'environnement bleu, c'est-Ă -dire afin que les URL de production y soient pointĂ©es;
  • DĂ©ployez et testez toutes les modifications apportĂ©es aux applications dans un environnement vert
  • passer des URL du bleu au vert

Le déploiement Blue Green est une approche qui vous permet d'introduire facilement de nouvelles fonctionnalités sans craindre une interruption de la production. Cela est dû au fait que même si quelque chose se produit, vous pouvez facilement revenir à l'environnement précédent en cliquant simplement sur un commutateur.


Après avoir lu tout ce qui précède, vous pouvez vous poser la question: qu'est-ce que le temps d'arrêt zéro a à voir avec le déploiement Blue green?


Eh bien, ils ont beaucoup en commun, car prendre en charge deux copies du même environnement nécessite un double effort pour les maintenir. C'est pourquoi certaines équipes, selon Martin Fowler , adhèrent aux variations de cette approche:


une autre option consiste à utiliser la même base de données, créant des commutateurs bleu-vert pour les couches Web et de domaine. Dans cette approche, les bases de données peuvent souvent être un problème, en particulier lorsque vous devez modifier son schéma pour prendre en charge une nouvelle version du logiciel.


Et nous arrivons ici au problème principal de cet article. La base de données . Jetons un autre regard sur cette phrase.


migrer la base de données.


Maintenant, vous devez vous poser la question - que faire si le changement de la base de données est incompatible en amont? Ma première version de l'application ne se cassera-t-elle pas? En fait, c'est exactement ce qui va se passer ...


Ainsi, même en dépit des avantages énormes d'un déploiement sans interruption de service / bleu vert, les entreprises ont tendance à suivre le processus de déploiement plus sûr suivant pour leurs applications:


  • prĂ©parer un package avec une nouvelle version de l'application
  • fermer une application en cours d'exĂ©cution
  • exĂ©cuter des scripts pour la migration de la base de donnĂ©es
  • dĂ©ployer et lancer la nouvelle version de l'application

Dans cet article, nous décrirons en détail comment vous pouvez travailler avec une base de données et du code pour tirer parti du déploiement sans interruption de service.


Problèmes de base de données


Si vous avez une application sans état qui ne stocke aucune donnée dans la base de données, vous pouvez obtenir immédiatement un déploiement sans interruption. Malheureusement, la plupart des logiciels doivent stocker des données quelque part. C'est pourquoi vous devriez réfléchir à deux fois avant d'apporter des modifications au circuit. Avant de nous plonger dans les détails de la façon de modifier le schéma afin qu'il devienne possible de le déployer sans temps d'arrêt, concentrons-nous d'abord sur le schéma de contrôle de version.


Schéma de contrôle de version


Dans cet article, nous utiliserons Flyway comme outil de contrôle de version ( environ Transl.: Nous parlons de migration de base de données ). Naturellement, nous écrirons également une application Spring Boot qui prend en charge Flyway et migrera le circuit lors de la configuration du contexte de l'application. Lorsque vous utilisez Flyway, vous pouvez stocker des scripts de migration dans votre dossier de projets (par défaut dans classpath:db/migration ). Ici, vous pouvez voir un exemple de ces fichiers de migration.


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

Dans cet exemple, nous voyons 4 scénarios de migration qui, s'ils n'ont pas été exécutés plus tôt, seront exécutés l'un après l'autre au démarrage de l'application. Regardons un des fichiers ( V1__init.sql ) comme exemple.


 CREATE TABLE PERSON ( id BIGINT GENERATED BY DEFAULT AS IDENTITY, first_name varchar(255) not null, last_name varchar(255) not null ); insert into PERSON (first_name, last_name) values ('Dave', 'Syer'); 

Tout parle parfaitement de lui-même: vous pouvez utiliser SQL pour déterminer comment votre base de données doit être modifiée. Pour plus d'informations sur Spring Boot et Flyway, consultez Spring Boot Docs .


L'utilisation du contrĂ´le de version avec Spring Boot vous offre 2 grands avantages:


  • vous sĂ©parez les modifications de la base de donnĂ©es des modifications du code
  • la migration de la base de donnĂ©es se produit lors du dĂ©ploiement de votre application, c'est-Ă -dire votre processus de dĂ©ploiement est simplifiĂ©

Résolution des problèmes de base de données


Dans la prochaine section de l'article, nous nous concentrerons sur l'examen de deux approches des modifications de la base de données.


  • incompatibilitĂ© vers l'arrière
  • compatibilitĂ© descendante

Le premier sera considéré comme un avertissement selon lequel vous ne devez pas effectuer un déploiement sans interruption de service sans préparation préalable ... Le second offre une solution sur la façon dont vous pouvez effectuer un déploiement sans interruption de service et en même temps maintenir la compatibilité descendante.


Notre projet, sur lequel nous travaillerons, sera une simple application Spring Boot Flyway dans laquelle il y a une Person avec first_name et last_name dans la base de données ( environ par Person : Person est une table, et irst_name et nom de last_name sont les champs qu'elle last_name ). Nous voulons renommer last_name en surname de surname .


Hypothèses


Avant de nous plonger dans les détails, nous devons définir quelques hypothèses sur nos applications. Le principal résultat que nous voulons atteindre sera un processus assez simple.


Remarque Entreprise PRO-TIP. La rationalisation des processus peut vous faire Ă©conomiser beaucoup d'argent en support (plus il y a de personnes travaillant dans votre entreprise, plus vous pouvez Ă©conomiser)!

Pas besoin de restaurer la base de données


Cela simplifie le processus de déploiement (certaines restaurations de base de données sont presque impossibles, par exemple, la suppression de la restauration). Nous préférons annuler uniquement les demandes. De cette façon, même si vous avez différentes bases de données (par exemple SQL et NoSQL), votre pipeline de déploiement sera identique.


Il doit être TOUJOURS possible de restaurer l'application d'une version en arrière (pas plus)


La restauration ne doit être effectuée que si nécessaire. S'il y a une erreur dans la version actuelle qui n'est pas facile à corriger, nous devrions pouvoir renvoyer la dernière version de travail. Nous supposons que cette dernière version de travail est la précédente. Maintenir la compatibilité du code et de la base de données pour plusieurs déploiements serait extrêmement difficile et coûteux.


Remarque Pour une meilleure lisibilité, dans le cadre de cet article nous allons changer la version majeure de l'application.

Étape 1: état initial


Version d'application: 1.0.0
Version DB: v1


Commentaire


Ce sera l'Ă©tat initial de l'application.


Modifications de la base de données


La base de données contient 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'); 

Modifications du code


L'application enregistre les données de la personne dans 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 + "]"; } } 

Renommage de colonne incompatible


Voyons un exemple de modification d'un nom de colonne:


Attention L'exemple suivant se casse intentionnellement. Nous le montrons afin de démontrer le problème de compatibilité de la base de données.

Version d'application: 2.0.0.BAD


Version DB: v2bad


Commentaire


Les modifications actuelles ne nous permettent PAS d'exécuter deux instances (ancienne et nouvelle) en même temps. Ainsi, un déploiement sans temps d'arrêt sera difficile à réaliser (hypothèses prises en compte, cela est pratiquement impossible).


Test A / B


La situation actuelle est que nous avons une version d'application 1.0.0, déployée dans prod, et une base de données v1 . Nous devons déployer la deuxième instance de l'application, la version 2.0.0.BAD , et mettre à niveau la base de données vers v2bad .


Étapes:


  1. déployé une nouvelle instance de la version d'application 2.0.0.BAD , qui met à jour la base de données vers v2bad
  2. la colonne last_name n'existe plus dans la base de données v2bad - elle a été changée en surname
  3. Les mises à jour des bases de données et des applications ont réussi et certaines instances fonctionnent dans 1.0.0 , d'autres dans 2.0.0.BAD . Toutes liées à la v2bad
  4. toutes les instances de la version 1.0.0 commenceront à générer des erreurs car elles essaieront d'insérer des données dans la colonne last_name , qui n'est plus
  5. toutes les instances de la version 2.0.0.BAD fonctionneront sans problème

Comme vous pouvez le voir, si nous apportons des modifications incompatibles à la base de données et aux applications, les tests A / B sont impossibles.


Application de restauration


Supposons qu'après avoir essayé d'effectuer un déploiement A / B ( environ. Transl.: Probablement, l'auteur faisait référence aux tests A / B ici ), nous avons décidé que nous devions restaurer l'application à la version 1.0.0. Supposons que nous ne voulons pas restaurer la base de données.


Étapes:


  1. nous arrĂŞtons l'instance d'application version 2.0.0.BAD
  2. la base de données est toujours v2bad
  3. comme la version 1.0.0 ne comprend pas le surname , nous verrons des erreurs
  4. l'enfer s'est libéré, nous ne pouvons plus revenir

Comme vous pouvez le voir, si nous apportons des modifications incompatibles à la base de données et aux applications, nous ne pouvons pas revenir à la version précédente.


Journaux d'exécution des 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"} 

Modifications de la base de données


Script de migration qui renomme last_name en surname de surname


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

Un script qui renomme last_name .


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

Modifications du code


Nous avons changé le nom du champ lastName nom de surname .


Renommer une colonne d'une manière rétrocompatible


C'est la situation la plus courante que nous pouvons rencontrer. Nous devons apporter des modifications incompatibles en amont. Nous avons déjà prouvé que pour un déploiement sans temps d'arrêt, nous ne devons pas simplement appliquer la migration de base de données sans actions supplémentaires. Dans cette section de l'article, nous effectuerons 3 déploiements de l'application ainsi que des migrations de base de données afin d'atteindre le résultat souhaité et en même temps de maintenir la compatibilité descendante.


Remarque Rappelons que nous avons une version de base de données v1 . Il contient les colonnes last_name et last_name . Nous devons changer le last_name de surname en surname de surname . Nous avons également une version d'application 1.0.0, qui n'utilise pas encore de surname .

Étape 2: ajouter le nom de famille


Version d'application: 2.0.0
Version DB: v2


Commentaire


En ajoutant une nouvelle colonne et en copiant son contenu, nous créons des modifications de base de données rétrocompatibles. Dans le même temps, si nous restaurons le JAR ou si nous avons un ancien JAR fonctionnel, il ne se cassera pas lors de l'exécution.


Nous déployons la nouvelle version


Étapes:


  1. migrer la base de données pour créer une nouvelle colonne de surname . Maintenant, votre version db v2
  2. copier les données du last_name de surname vers le surname de surname . Notez que si vous avez beaucoup de ces données, vous devriez envisager la migration par lots!
  3. écrire du code où les deux la nouvelle et l' ancienne colonne sont utilisées. Maintenant, votre application version 2.0.0
  4. lire la valeur de la colonne du surname si elle n'est pas null , ou de l ast_name si le surname pas spécifié. Vous pouvez supprimer getLastName() du code, car il renverra null lorsque vous restaurez votre application de 3.0.0 à 2.0.0 .

Si vous utilisez Spring Boot Flyway, ces deux étapes seront effectuées lors du lancement de la version 2.0.0 application. Si vous exécutez manuellement l'outil de gestion des versions de la base de données, vous devrez effectuer deux opérations différentes pour cela (mettre à jour la version db manuellement, puis déployer une nouvelle application).


C'est important. N'oubliez pas qu'une colonne nouvellement créée NE DOIT PAS être NON NULL . Si vous rétrogradez, l'ancienne application ne connaît pas la nouvelle colonne et ne l'installe pas lors de l' Insert. Mais si vous ajoutez cette restriction, et votre base de données sera v2 , il faudra définir la valeur de la nouvelle colonne. Ce qui entraînera des violations des restrictions.

C'est important. Vous devez supprimer la méthode getLastName() , car la version 3.0.0 getLastName() pas le concept d'une colonne last_name dans le code. Cela signifie que null y sera défini. Vous pouvez quitter la méthode et ajouter null vérifications null , mais une bien meilleure solution serait de vous assurer que dans la logique getSurname() vous avez choisi la bonne valeur non nulle.

Test A / B


La situation actuelle est que nous avons une version d'application 1.0.0 déployée sur le prod et une base de données en v1 . Nous devons déployer la deuxième instance de la version 2.0.0 de l'application, qui mettra à niveau la base de données vers la v2 .


Étapes:


  1. déployé une nouvelle instance de l'application version 2.0.0 , qui met à jour la base de données vers la v2
  2. Pendant ce temps, certaines demandes ont été traitées par des instances de la version 1.0.0
  3. la mise à jour a réussi et vous disposez de plusieurs instances de travail de la version d'application 1.0.0 et du reste de la version 2.0.0. Tout le monde communique avec la base de données en v2
  4. la version 1.0.0 n'utilise pas la colonne de nom de famille dans la base de données, mais la version 2.0.0 utilise. Ils n'interfèrent pas entre eux et il ne devrait pas y avoir d'erreurs.
  5. la version 2.0.0 stocke les données dans l'ancienne et la nouvelle colonne, ce qui offre une compatibilité descendante

C'est important. Si vous avez des requêtes qui comptent les éléments en fonction des valeurs de l'ancienne / nouvelle colonne, vous devez vous rappeler que vous avez maintenant des valeurs en double (très probablement, elles migrent toujours). Par exemple, si vous souhaitez compter le nombre d'utilisateurs dont le nom de famille (quel que soit le nom de la colonne) commence par la lettre A , avant que la migration des données ( old → new colonne) ne soit terminée, vous risquez d'avoir des données incohérentes si vous exécutez une requête sur une nouvelle colonne.

Application de restauration


Nous avons maintenant une version d'application 2.0.0 et une base de données en v2 .


Étapes:


  1. restaurez votre application Ă  la version 1.0.0 .
  2. la version 1.0.0 n'utilise pas la colonne de surname dans la base de données, donc la restauration doit être réussie

Modifications de la base de données


La base de données contient une colonne nommée last_name .


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

Le script pour ajouter le surname .


Attention N'oubliez pas que vous NE POUVEZ PAS AJOUTER de contraintes NOT NULL à la colonne ajoutée. Si vous restaurez le JAR, l'ancienne version n'a aucune idée de la colonne ajoutée et la définira automatiquement sur NULL. S'il existe une telle restriction, l'ancienne application se cassera simplement.

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

Modifications du code


Nous stockons les données dans le last_name et le surname . Dans le même temps, nous lisons dans last_name , car cette colonne est la plus pertinente. Au cours du processus de déploiement, certaines demandes peuvent avoir été traitées par une instance d'application qui n'a pas encore été mise à jour.


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

Étape 3: suppression du nom de famille du code


Version d'application: 3.0.0


Version DB: v3


Commentaire


Remarque Lane: Apparemment, dans l'article d'origine, l'auteur a copié par erreur le texte de ce bloc de l'étape 2. À cette étape, des modifications doivent être apportées au code d'application visant à supprimer la fonctionnalité qui utilise la colonne last_name .


En ajoutant une nouvelle colonne et en copiant son contenu, nous avons créé des modifications de base de données rétrocompatibles. De plus, si nous rétrogradons le JAR ou si nous avons un ancien JAR fonctionnel, il ne se cassera pas lors de l'exécution.


Application de restauration


Nous avons actuellement l'application version 3.0.0 et la base de données v3 . La version 3.0.0 n'enregistre pas les données dans last_name . Cela signifie que le surname stocke les informations les plus récentes.


Étapes:


  1. restaurez votre application Ă  la version 2.0.0 .
  2. la version 2.0.0 utilise Ă  la fois le last_name et le surname .
  3. la version 2.0.0 prendra le surname s'il n'est pas nul, sinon last_name

Modifications de la base de données


Il n'y a aucun changement structurel dans la base de données. Le script suivant est exécuté, qui effectue la migration finale des anciennes données:


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

Modifications du code


Remarque trans.: La description de ce bloc a également été copiée par erreur par l'auteur de l'étape 2. Conformément à la logique de l'histoire de l'article, les modifications du code à cette étape devraient viser à en supprimer les éléments qui fonctionnent avec la colonne last_name .


Nous stockons les données dans le last_name et le surname. De plus, nous lisons dans la colonne last_name , car elle est la plus pertinente. Au cours du processus de déploiement, certaines demandes peuvent être traitées par une instance qui n'a pas encore été mise à jour.


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

Étape 4: suppression de last_name de la base de données


Version d'application: 4.0.0


Version DB: v4


Commentaire


Étant donné que le code de la version 3.0.0 n'utilisait pas la colonne last_name , rien de mal ne se produira pendant l'exécution si nous revenons à 3.0.0 après avoir supprimé la colonne de la base de données.


Journaux d'exécution des 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"} 

Modifications de la base de données


Pour la v3 nous last_name simplement la colonne last_name et ajoutons les contraintes manquantes.


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

Modifications du code


Il n'y a aucun changement dans le code.


Conclusion


Nous avons réussi à appliquer le changement de nom de colonne incompatible en amont en effectuant plusieurs déploiements compatibles en amont. Voici un résumé des étapes franchies:


  1. déploiement d'application version 1.0.0 avec schéma de base de données v1 (nom de colonne = nom)
  2. déployer l'application version 2.0.0, qui enregistre les données dans le last_name et le surname . L'application lit à partir de last_name . La base de données est en version v2 , qui contient les colonnes du surname. surname et du surname. surname surname. surname est une copie de l ast_name . (REMARQUE: cette colonne ne doit pas avoir de contrainte non nulle)
  3. 3.0.0 , surname surname. , last_name surname . NOT NULL last_name . v3
  4. 4.0.0 — . v4 , last_name . .

, , / .


Code


, , Github . .


Les projets


, .


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


En option



Lisez Ă©galement d'autres articles sur notre blog:


Source: https://habr.com/ru/post/fr481932/


All Articles