
În acest articol, vom explica în detaliu cum să rezolvați problemele legate de compatibilitatea bazelor de date în timpul desfășurării. Vă vom arăta ce se poate întâmpla cu aplicațiile dumneavoastră în producție dacă încercați să realizați desfășurarea fără pregătiri prealabile. Apoi, vom parcurge etapele ciclului de viață al aplicației necesare pentru a avea un timp de nefuncționare nul (notă de traducere: mai departe — zero downtime). Rezultatul acțiunilor noastre va fi aplicarea unei modificări a bazei de date incompatibile înapoi într-un mod compatibil.
Dacă doriți să explorați exemplele de cod din articol, le veți găsi pe .
Introducere
Zero downtime deployment
Ce este acest concept misterios zero downtime deployment? Можно сказать, это когда ваше приложение развернуто так, что вы можете успешно вводить новую версию приложения на продакшн, в то время как пользователь не замечает его недоступности. С точки зрения пользователя и компании, это наилучший из возможных сценариев деплоя, поскольку таким образом можно вводить новые функции и устранять ошибки без перебоев в работе.
Cum să realizăm asta? Există mai multe metode, iar una dintre ele este:
- desfășurați versiunea nr. 1 a serviciului dumneavoastră
- efectuați migrarea bazei de date
- desfășurați versiunea nr. 2 a serviciului dumneavoastră în paralel cu versiunea nr. 1
- de îndată ce observați că versiunea nr. 2 funcționează cum trebuie, eliminați versiunea nr. 1
- gata!
Ușor, nu-i așa? Din păcate, nu este atât de simplu, iar mai târziu vom analiza acest lucru în detaliu. Dar acum, să verificăm un alt proces de desfășurare destul de comun — blue green deployment.
Ați auzit vreodată de ? С Cloud Foundry это чрезвычайно легко сделать. Просто гляньте на , unde descriem acest lucru mai detaliat. Pe scurt, să recapitulăm cum se face blue green deployment:
- asigurați funcționarea a două copii ale codului dumneavoastră de producție („blue” și „green”);
- redirecționați tot traficul către mediu blue, adică URL-urile de producție să indice acolo;
- desfășurați și testați toate modificările aplicației în mediu green;
- comutați URL-urile de la blue la mediu green
Blue green deployment este o abordare care vă permite să introduceți noi funcții ușor, fără a vă face griji că producția se va întrerupe. Aceasta se datorează faptului că, chiar dacă se întâmplă ceva, puteți reveni cu ușurință la mediu anterior, pur și simplu prin „apăsarea unui buton”.
După ce ați citit tot ce a fost menționat mai sus, s-ar putea să vă întrebați: Ce legătură are zero downtime cu desfășurarea blue green?
Ei bine, au multe în comun, deoarece susținerea a două copii ale aceluiași mediu necesită dublu efort pentru întreținerea acestora. De aceea, unele echipe, așa cum afirmă , se aleg cu o variantă a acestei abordări:
o altă variantă constă în utilizarea aceleași baze de date, creând comutatoare blue-green pentru straturile web și de domeniu. Într-un astfel de mod, bazele de date pot deveni adesea o problemă, în special când trebuie să modifici schema pentru a sprijini o nouă versiune a software-ului.
Și aici ajungem la problema principală din acest articol. Baza de date. Să aruncăm încă o dată o privire asupra acestei fraze.
efectuați migrarea bazei de date.
Acum trebuie să vă puneți întrebarea – ce se întâmplă dacă modificarea bazei de date devine incompatibilă? Oare prima versiune a aplicației mele nu se va strica? De fapt, exact asta se va întâmpla...
Astfel, chiar dacă există avantaje uriașe ale zero downtime / blue green deployment, companiile tind să urmeze următorul proces de desplegare a aplicațiilor mai sigur:
- pregătiți un pachet cu noua versiune a aplicației
- oprirea aplicației în execuție
- rularea scripturilor pentru migrarea bazei de date
- desplearea și pornirea noii versiuni a aplicației
În acest articol vom detalia cum puteți lucra cu baza de date și codul pentru a beneficia de avantajele desplegării fără timp de inactivitate.
Probleme cu baza de date
Dacă aveți o aplicație stateless, care nu stochează date în baza de date, puteți obține zero downtime deployment imediat. Din păcate, majoritatea software-ului trebuie să stocheze date undeva. De aceea trebuie să te gândești de două ori înainte de a face orice modificări în schema. Înainte de a ne aprofunda în detalii despre cum să modificăm schema astfel încât să se poată realiza desplegarea fără timp de inactivitate, să ne concentrăm întâi pe schema de gestionare a versiunilor.
Schema de gestionare a versiunilor
În acest articol vom folosi ca instrument pentru gestionarea versiunilor (n.tr.: este vorba despre migrarea bazelor de date). Evident, vom scrie de asemenea o aplicație Spring Boot, care are suport încorporat pentru Flyway și va efectua migrarea schemei în timpul configurării contextului aplicației. Când folosiți Flyway, puteți stoca scripturile de migrare în folderul proiectelor dvs. (în mod implicit în classpath:db/migration). Aici puteți vedea un exemplu de astfel de fișiere de migrare
└── db
└── migration
├── V1__init.sql
├── V2__Add_surname.sql
├── V3__Final_migration.sql
└── V4__Remove_lastname.sqlÎn acest exemplu, vedem 4 scenarii de migrare care, dacă nu au fost efectuate anterior, vor fi executate unul după altul la pornirea aplicației. Să examinăm unul dintre fișiere (V1__init.sql) ca exemplu.
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');Totul vorbește de la sine: puteți folosi SQL pentru a defini cum ar trebui să fie modificată baza dumneavoastră de date. Pentru mai multe informații despre Spring Boot și Flyway, consultați .
Folosind instrumentul de gestionare a versiunilor împreună cu Spring Boot, obțineți 2 avantaje mari:
- separarea modificărilor bazei de date de modificările codului
- migrarea bazei de date are loc împreună cu implementarea aplicației dumneavoastră, adică procesul de desfășurare este simplificat
Rezolvarea problemelor cu baza de date
În secțiunea următoare a articolului, ne vom concentra pe examinarea a două abordări ale modificărilor bazei de date.
- incompatibilitatea inversă
- compabilitatea inversă
Primul va fi tratat ca un avertisment că nu ar trebui să faceți implementări fără timpi de nefuncționare fără o pregătire prealabilă… Al doilea oferă o soluție pentru cum să efectuați implementarea fără pauze și în același timp să mențineți compabilitatea inversă.
Proiectul nostru, la care vom lucra, va fi o aplicație simplă Spring Boot Flyway, care conține Person de first_name și last_name în baza de date (notă de traducere: Person este tabelul, iar first_name și last_name — este câmpul din acesta). Vrem să renunțăm la numele last_name în surname.
Presupoziții
Înainte de a ne aprofunda în detalii, este necesar să stabilim câteva presupuneri referitoare la aplicațiile noastre. Rezultatul principal pe care dorim să-l atingem va fi un proces destul de simplu.
Notă. PRO-TIP pentru Afaceri. Simplificarea proceselor vă poate economisi mulți bani la întreținere (cu cât mai mulți oameni lucrează în compania dumneavoastră, cu atât mai mulți bani puteți economisi)!
Nu trebuie să faceți rollback la baza de date
Aceasta simplifică procesul de desfășurare (unele rollback-uri de baze de date sunt practic imposibile, de exemplu, rollback-ul ștergerii). Preferăm să facem rollback doar la aplicații. Astfel, chiar dacă aveți baze de date diferite (de exemplu, SQL și NoSQL), pipeline-ul dumneavoastră de desfășurare va arăta la fel.
Trebuie să existe întotdeauna posibilitatea de a face rollback aplicației la o versiune anterioară (nu mai mult)
Rollback should only be performed when necessary. If the current version has a bug that is difficult to fix, we must be able to return to the last working version. We assume that this last working version is the previous one. Maintaining compatibility for code and database across multiple deployments would be extremely challenging and costly.
Notă. For better readability, within this article, we will change the major version of the application.
Pasul 1: Starea inițială
Versiunea aplicației: 1.0.0
Versiunea Bazei de Date: v1
Comentariu
Aceasta va fi starea inițială a aplicației.
Modificări ale Bazei de Date
BD conține 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');Modificări ale codului
Aplicația salvează datele Person în 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
+ "]";
}
}Renaming de tip backward incompatible al coloanei
Hai să luăm un exemplu despre cum să schimbăm numele coloanei:
Atenție. Următorul exemplu va provoca intenționat o eroare. Facem acest lucru pentru a demonstra problema de compatibilitate a bazei de date.
Versiunea aplicației: 2.0.0.BAD
Versiunea Bazei de Date: v2bad
Comentariu
Modificările actuale NU ne permit să rulăm două instanțe (veche și nouă) simultan. Astfel, desfășurarea fără downtime va fi greu de realizat (dacă luăm în considerare presupunerile, este practic imposibil).
A/B Testing
Situația actuală este că avem o aplicație de versiune 1.0.0, desfasurată în producție, și BD v1. Trebuie să desfășurăm o a doua instanță a aplicației, versiunea 2.0.0.BAD, și să actualizăm baza de date la v2bad.
Pași:
- s-a desfășurat o nouă instanță a aplicației versiunea
2.0.0.BAD, care actualizează baza de date lav2bad - în baza de date
v2badcoloanalast_namenu mai există — a fost schimbată însurname - actualizarea bazei de date și a aplicației a fost efectuată cu succes, iar unele instanțe funcționează în
1.0.0, altele în2.0.0.BAD. Toate sunt conectate la BDv2bad - toate instanțele versiunii
1.0.0vor începe să returneze erori deoarece vor încerca să insereze date în coloanalast_name, care nu mai există - toate instanțele versiunii
2.0.0.BADvor funcționa fără probleme
După cum vedem, dacă facem modificări de tip backward incompatible în BD și aplicație, A/B testing-ul devine imposibil.
Rollback aplicației
Să presupunem că după încercarea de a efectua desfășurarea A/B (notă de traducere: probabil că autorul s-a referit la A/B testing) am decis că trebuie să revenim aplicația la versiunea 1.0.0. Să presupunem că nu vrem să facem un rollback al bazei de date.
Pași:
- oprim instanța aplicației versiunea
2.0.0.BAD - baza de date este încă
v2bad - deoarece versiunea
1.0.0nu înțelege ce estesurname, vom vedea erori - iadul s-a dezlănțuit, nu mai putem reveni
După cum vedeți, dacă facem modificări incompatibile înapoi între baza de date și aplicație, nu putem reveni la versiunea anterioară.
Jurnalele de execuție a scriptului
Scenariul incompatibil înapoi:
01) Rulați 1.0.0
02) Așteptați ca aplicația (1.0.0) să pornească
03) Generați o persoană apelând POST localhost:9991/person pentru versiunea 1.0.0
04) Rulați 2.0.0.BAD
05) Așteptați ca aplicația (2.0.0.BAD) să pornească
06) Generați o persoană apelând POST localhost:9991/person pentru versiunea 1.0.0 < -- aceasta ar trebui să eșueze
07) Generați o persoană apelând POST localhost:9992/person pentru versiunea 2.0.0.BAD < -- aceasta ar trebui să reușească
Pornind aplicația în versiunea 1.0.0
Generați o persoană în versiunea 1.0.0
Trimiteți un post la 127.0.0.1:9991/person. Acesta este răspunsul:
{"firstName":"b73f639f-e176-4463-bf26-1135aace2f57","lastName":"b73f639f-e176-4463-bf26-1135aace2f57"}
Pornind aplicația în versiunea 2.0.0.BAD
Generați o persoană în versiunea 1.0.0
Trimiteți un post la 127.0.0.1:9991/person. Acesta este răspunsul:
curl: (22) URL-ul solicitat a returnat eroare: 500 Internal Server Error
Generați o persoană în versiunea 2.0.0.BAD
Trimiteți un post la 127.0.0.1:9995/person. Acesta este răspunsul:
{"firstName":"e156be2e-06b6-4730-9c43-6e14cfcda125","surname":"e156be2e-06b6-4730-9c43-6e14cfcda125"}Modificări ale Bazei de Date
Scriptul de migrare care redenumește last_name în surname
Scriptul Flyway original:
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');Scriptul care redenumește last_name.
-- Această schimbare este incompatibilă înapoi - nu poți face testare A/B
ALTER TABLE PERSON CHANGE last_name surname VARCHAR;Modificări ale codului
Am schimbat numele câmpului lastName pe surname.
Redenumirea coloanei într-un mod compatibil înapoi
Aceasta este cea mai frecventă situație cu care ne putem confrunta. Trebuie să facem modificări incompatibile înapoi. Am dovedit deja că pentru implementări fără downtime nu trebuie să aplicăm pur și simplu migrarea bazei de date fără acțiuni suplimentare. În această secțiune a articolului vom face 3 implementări ale aplicației împreună cu migrarea bazei de date, pentru a obține rezultatul dorit și, în același timp, a păstra compatibilitatea înapoi.
Notă. Să ne amintim că avem o bază de date de versiune
v1. Aceasta conține coloanefirst_nameșilast_name. Trebuie să modificămlast_namepesurname. De asemenea, avem aplicația de versiune1.0.0,care deocamdată nu utilizeazăsurname.
Pasul 2: Adăugăm surname
Versiunea aplicației: 2.0.0
Versiunea Bazei de Date: v2
Comentariu
Adăugând o nouă coloană și copind conținutul acesteia, creăm modificări compatibile înapoi în baza de date. În același timp, dacă facem rollback JAR sau avem un JAR vechi funcțional, nu se va strica în timpul execuției.
Implementăm o nouă versiune
Pași:
- efectuați migrarea bazei de date pentru a crea noua coloană
surname. Acum baza dumneavoastră de date de versiunev2 - copiați datele din
last_nameînsurname. Vă rugăm să observați, ceea ce, dacă aveți multe dintre aceste date, ar trebui să luați în considerare migrarea în loturi! - scrieți codul în care se folosesc AM BOTH și nou, și vechi coloană. Acum aplicația dvs. versiunea
2.0.0 - citiți valoarea din coloană
surname, dacă nunull, sau din last_name, dacăsurnamenu este specificat. Puteți eliminagetLastName()din cod, deoarece el va returnanullla revenirea aplicației dvs. de la3.0.0la2.0.0.
Dacă folosiți Spring Boot Flyway, acești doi pași se vor executa în timpul pornirii versiunii 2.0.0 aplicației. Dacă lansați instrumentul de gestionare a versiunilor bazei de date manual, va trebui să faceți două acțiuni diferite (mai întâi să actualizați versiunea bazei de date manual, apoi să desfășurați o nouă aplicație).
Este important. Amintiți-vă că noua coloană NU TREBUIE fii NOT NULL. Dacă faceți revenire, aplicația veche nu știe despre noua coloană și nu o va seta în timpul
Insert.Dar dacă adăugați această constrângere, iar baza de date va fiv2, va necesita setarea valorii noii coloane. Ceea ce va duce la încălcări ale constrângerilor.Este important. Ar trebui să eliminați metoda
getLastName(), deoarece în versiunea3.0.0din cod nu există conceptul de coloanălast_name. Aceasta înseamnă că vor fi setate valori nule. Puteți păstra metoda și adăuga verificări penull, dar o soluție mult mai bună ar fi să vă asigurați că în logicagetSurname()ați ales o valoare corectă non-nulă.
A/B Testing
Situația actuală este că avem o aplicație de versiune 1.0.0, desfășurată pe producție, și baza de date în v1. Trebuie să desfășurăm o a doua instanță a aplicației versiunii 2.0.0, care va actualiza baza de date până la v2.
Pași:
- s-a desfășurat o nouă instanță a aplicației versiunea
2.0.0, care actualizează baza de date lav2 - între timp, unele cereri au fost procesate de instanțele versiunii
1.0.0 - actualizarea a fost finalizată cu succes și aveți câteva instanțe funcționale ale aplicației versiunii
1.0.0și celelalte versiuni2.0.0.Toate comunică cu baza de date înv2 - versiune
1.0.0nu utilizează în baza de date coloana surname, iar versiunea2.0.0o folosește. Ele nu se perturbă reciproc și nu ar trebui să fie erori. - versiune
2.0.0salvează date atât în coloana veche, cât și în cea nouă, ceea ce asigură compatibilitatea inversă
Este important. Dacă aveți vreo cerere care calculează elemente pe baza valorilor din coloana veche / nouă, trebuie să rețineți că acum aveți duplicarea valorilor (cel mai probabil, ele migrează încă). De exemplu, dacă doriți să numărați utilizatorii al căror nume de familie (indiferent cum se numește colona) începe cu litera
A, atunci până la finalizarea migrației datelor (old→noucoloana) puteți avea date inconsistente dacă efectuați o cerere la noua coloană.
Rollback aplicației
Acum avem aplicația versiunii 2.0.0 și baza de date în v2.
Pași:
- revină aplicația dvs. la versiunea
1.0.0. - versiune
1.0.0nu folosește coloană în baza de datesurname, prin urmare rollback-ul ar trebui să fie reușit
Modificări DB
Baza de date conține o coloană numită last_name.
Scriptul inițial 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 adăugare surname.
Atenție. Amintiți-vă că NU TREBUIE SĂ ADĂUGAȚI restricții NOT NULL în coloana adăugată. Dacă faceți rollback la JAR, vechea versiune nu are cunoștință despre coloana adăugată și va seta automat valoarea NULL. În cazul în care există o astfel de restricție, vechea aplicație se va defecta pur și simplu.
-- NOTĂ: Acest câmp nu poate avea restricția NOT NULL pentru că, dacă faceți rollback, vechea versiune nu va cunoaște acest câmp
-- și va seta întotdeauna valoarea la NULL
ALTER TABLE PERSON ADD surname varchar(255);
-- PRESUPUNEM CĂ ESTE O MIGRARE RAPIDĂ - ALTFEL AR TREBUI SĂ MIGRĂM ÎN BATCH-uri
UPDATE PERSON SET PERSON.surname = PERSON.last_nameModificări ale codului
Salvăm datele atât în last_name, cât și în surname. În același timp, citim din last_name, deoarece această coloană este cea mai relevantă. În timpul desfășurării, unele interogări ar fi putut fi procesate de o instanță a aplicației care nu a fost încă actualizată.
/*
* 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
+ "]";
}
}Pasul 3: Eliminarea last_name din cod
Versiunea aplicației: 3.0.0
Versiunea Bazei de Date:v3
Comentariu
Nota editorului: Se pare că în articolul inițial textul acestui bloc a fost copiat din greșeală de către autor din pasul 2. În acest pas ar trebui efectuate modificări în codul aplicației, vizând eliminarea funcționalității care utilizează coloana last_name.
Adăugând o nouă coloană și copierea conținutului acesteia, am creat modificări compatibile în baza de date. De asemenea, dacă revenim la JAR sau avem un JAR vechi funcțional, nu se va defecta în timpul execuției.
Rollback aplicației
În prezent avem aplicația versiunea 3.0.0 și baza de date v3. Versiunea 3.0.0 nu salvează datele în last_name. Aceasta înseamnă că în surname se stochează cele mai recente informații.
Pași:
- revină aplicația dvs. la versiunea
2.0.0. - versiune
2.0.0folosește șilast_nameșisurname. - versiune
2.0.0va luasurname, dacă nu este nul, altfel -last_name
Modificări ale Bazei de Date
Nu există modificări structurale în baza de date. Se execută următorul script, care efectuează migrarea finală a datelor vechi:
-- PRESUPUNEM CĂ ESTE O MIGRARE RAPIDĂ - ALTFEL AR TREBUI SĂ MIGRĂM ÎN BATCH-uri
-- DE ASISTEM, NU VERIFICĂM DACĂ NU SUPRASCRIEM ÎNREGISTRĂRI EXISTENTE. AR TREBUI SĂ COMPARĂM
-- VERSIUNILE ÎNREGISTRĂRII PENTRU A ASIGURA CĂ DACĂ EXISTĂ DEJA O ÎNREGISTRARE CU UN NUMĂR DE VERSIUNE MAI MARE
-- NU O SĂ SUPRASCRIEM.
UPDATE PERSON SET PERSON.surname = PERSON.last_name;
-- RENUNȚÂND LA RESTRICȚIA NOT NULL; ALTFEL VEI ÎNCERCA SĂ INSEREZI O VALOARE NULL A LAST_NAME
-- CU O RESTRICȚIE NOT_NULL.
ALTER TABLE PERSON MODIFY COLUMN last_name varchar(255) NULL DEFAULT NULL;Modificări ale codului
Nota editorului: Descrierea acestui bloc a fost, de asemenea, copiată greșit de către autor din pasul 2. Conform logicii narațiunii articolului, modificările din cod în acest pas ar trebui să fie orientate spre eliminarea elementelor care interacționează cu coloana last_name.
Păstrăm datele atât în last_name, cât și în surname. În plus, citim din coloana last_name, deoarece aceasta este cea mai relevantă. În timpul desfășurării, unele cereri pot fi procesate de o instanță care nu a fost încă actualizată.
/*
* 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
+ "]";
}
}Pasul 4: Eliminarea last_name din Bază de Date
Versiunea aplicației: 4.0.0
Versiunea Bazei de Date: v4
Comentariu
Din cauza faptului că codul versiunii 3.0.0 nu a folosit coloana last_name, în timpul execuției nu se va întâmpla nimic rău dacă revenim la 3.0.0 după eliminarea coloanei din baza de date.
Jurnalele de execuție a scriptului
O vom face în următorul mod:
01) Rulați 1.0.0
02) Așteptați ca aplicația (1.0.0) să pornească
03) Generați o persoană apelând POST localhost:9991/person pentru versiunea 1.0.0
04) Rulați 2.0.0
05) Așteptați ca aplicația (2.0.0) să pornească
06) Generați o persoană apelând POST localhost:9991/person pentru versiunea 1.0.0
07) Generați o persoană apelând POST localhost:9992/person pentru versiunea 2.0.0
08) Opriți aplicația (1.0.0)
09) Rulați 3.0.0
10) Așteptați ca aplicația (3.0.0) să pornească
11) Generați o persoană apelând POST localhost:9992/person pentru versiunea 2.0.0
12) Generați o persoană apelând POST localhost:9993/person pentru versiunea 3.0.0
13) Opriți aplicația (3.0.0)
14) Rulați 4.0.0
15) Așteptați ca aplicația (4.0.0) să pornească
16) Generați o persoană apelând POST localhost:9993/person pentru versiunea 3.0.0
17) Generați o persoană apelând POST localhost:9994/person pentru versiunea 4.0.0
Pornind aplicația în versiunea 1.0.0
Generarea unei persoane în versiunea 1.0.0
Trimițând o postare la 127.0.0.1:9991/person. Acesta este răspunsul:
{"firstName":"52b6e125-4a5c-429b-a47a-ef18bbc639d2","lastName":"52b6e125-4a5c-429b-a47a-ef18bbc639d2"}
Pornind aplicația în versiunea 2.0.0
Generarea unei persoane în versiunea 1.0.0
Trimițând o postare la 127.0.0.1:9991/person. Acesta este răspunsul:
{"firstName":"e41ee756-4fa7-4737-b832-e28827a00deb","lastName":"e41ee756-4fa7-4737-b832-e28827a00deb"}
Generarea unei persoane în versiunea 2.0.0
Trimițând o postare la 127.0.0.1:9992/person. Acesta este răspunsul:
{"firstName":"0c1240f5-649a-4bc5-8aa9-cff855f3927f","lastName":"0c1240f5-649a-4bc5-8aa9-cff855f3927f","surname":"0c1240f5-649a-4bc5-8aa9-cff855f3927f"}
Oprind aplicația 1.0.0
Pornind aplicația în versiunea 3.0.0
Generarea unei persoane în versiunea 2.0.0
Trimițând o postare la 127.0.0.1:9992/person. Acesta este răspunsul:
{"firstName":"74d84a9e-5f44-43b8-907c-148c6d26a71b","lastName":"74d84a9e-5f44-43b8-907c-148c6d26a71b","surname":"74d84a9e-5f44-43b8-907c-148c6d26a71b"}
Generarea unei persoane în versiunea 3.0.0
Trimițând o postare la 127.0.0.1:9993/person. Acesta este răspunsul:
{"firstName":"c6564dbe-9ab5-40ae-9077-8ae6668d5862","surname":"c6564dbe-9ab5-40ae-9077-8ae6668d5862"}
Oprind aplicația 2.0.0
Pornind aplicația în versiunea 4.0.0
Generarea unei persoane în versiunea 3.0.0
Trimițând o postare la 127.0.0.1:9993/person. Acesta este răspunsul:
{"firstName":"cbe942fc-832e-45e9-a838-0fae25c10a51","surname":"cbe942fc-832e-45e9-a838-0fae25c10a51"}
Generarea unei persoane în versiunea 4.0.0
Trimițând o postare la 127.0.0.1:9994/person. Acesta este răspunsul:
{"firstName":"ff6857ce-9c41-413a-863e-358e2719bf88","surname":"ff6857ce-9c41-413a-863e-358e2719bf88"}Modificările Bazei de Date
În ceea ce privește v3 pur și simplu eliminăm coloana last_name și adăugăm constrângerile lipsă.
-- ÎNLOCUIȚI COLOANA
ALTER TABLE PERSON DROP last_name;
-- ADĂUGAȚI CONSTRÂNGERI
UPDATE PERSON SET surname='' WHERE surname IS NULL;
ALTER TABLE PERSON ALTER COLUMN surname VARCHAR NOT NULL;Modificări ale codului
Nu există modificări în cod.
Ieșire
Am aplicat cu succes modificarea incompatibilă a numelui coloanei, realizând mai multe desfășurări compatibile. Mai jos este un rezumat al acțiunilor efectuate:
- desfășurarea aplicației versiunii
1.0.0dev1schema Bazei de Date (numele coloanei =last_name) - desfășurarea aplicației versiunii
2.0.0,care păstrează datele înlast_nameșisurname. Aplicația citește dinlast_name. Baza de date este în versiuneav2, care conține coloane precumlast_name, sausurname. surnameeste o copie a last_name. (NOTĂ: această coloană nu ar trebui să aibă restricția not null) - desfășurarea aplicației versiunii
3.0.0, care stochează datele doar însurnameși citește din surname. Cât despre baza de date, are loc ultima migrațielast_nameînsurname. De asemenea, restricția NOT NULL este ridicată de lalast_name. Baza de date este acum în versiuneav3 - desfășurarea aplicației versiunii
4.0.0— nu se fac modificări în cod. Implementarea bazei de datev4, care ștergelast_name. Aici puteți adăuga orice restricții lipsă în baza de date.
Urmând această abordare, puteți întotdeauna să reveniți la o versiune anterioară fără a afecta compatibilitatea bazei de date / aplicației.
Cod
Întregul cod folosit în acest articol este disponibil pe . Mai jos se află o descriere suplimentară.
Proiecte
După clonarea depozitului, veți vedea următoarea structură de foldere.
├── boot-flyway-v1 - versiunea 1.0.0 a aplicației cu v1 a schemei
├── boot-flyway-v2 - versiunea 2.0.0 a aplicației cu v2 a schemei (compatibilă cu versiunile anterioare - aplicația poate fi revenită)
├── boot-flyway-v2-bad - versiunea 2.0.0.BAD a aplicației cu v2bad a schemei (incompatibilă cu versiunile anterioare - aplicația nu poate fi revenită)
├── boot-flyway-v3 - versiunea 3.0.0 a aplicației cu v3 a schemei (aplicația poate fi revenită)
└── boot-flyway-v4 - versiunea 4.0.0 a aplicației cu v4 a schemei (aplicația poate fi revenită)Scripturi
Puteți rula scripturile descrise în scripturile de mai jos, care vor demonstra modificări compatibile și incompatibile cu baza de date.
Pentru a vedea cazul cu modificări compatibile, rulați:
.\/scripts\/scenario_backward_compatible.shȘi pentru a vedea cazul cu modificări incompatibile, rulați:
.\/scripts\/scenario_backward_incompatible.shExemplu Spring Boot Flyway
Toate exemplele sunt preluate de la Exemplu Spring Boot Flyway.
Puteți viziona http://localhost:8080/flyway, acolo este lista de scripturi.
Acest exemplu include și consola H2 (la adresa http://localhost:8080/h2-console), pentru a putea vizualiza starea bazei de date (URL jdbc implicit — jdbc:h2:mem:testdb).
În plus
Citiți și alte articole de pe blogul nostru:
Sursa: habr.com
