Implementare și baze de date fără timpi de nefuncționare

Implementare și baze de date fără timpi de nefuncționare

Acest articol explică în detaliu cum să rezolvi problemele de compatibilitate cu bazele de date în implementare. Vă vom spune ce se poate întâmpla cu aplicațiile dvs. de producție dacă încercați să implementați fără pregătire preliminară. Vom trece apoi prin etapele ciclului de viață al aplicației care sunt necesare pentru a avea un timp de nefuncționare zero (aproximativ banda: mai departe - zero timpi de nefuncționare). Rezultatul operațiunilor noastre va fi aplicarea modificării bazei de date incompatibile cu înapoi într-o manieră compatibilă cu înapoi.

Dacă doriți să înțelegeți exemplele de cod din articol, le puteți găsi la GitHub.

Introducere

Implementare fără timpi de nefuncționare

Ce mistic implementare fără timpi de nefuncționare? Puteți spune că este atunci când aplicația dvs. este implementată în așa fel încât să puteți introduce cu succes o nouă versiune a aplicației în producție, în timp ce utilizatorul nu observă indisponibilitatea acesteia. Din perspectiva utilizatorului și a companiei, acesta este cel mai bun scenariu posibil de implementare, deoarece permite introducerea de noi funcții și remedierea erorilor fără întreruperi.

Cum să realizezi acest lucru? Există mai multe moduri, iată una dintre ele:

  • implementați versiunea nr. 1 a serviciului dvs
  • efectua o migrare a bazei de date
  • Implementați versiunea #2 a serviciului dvs. în paralel cu versiunea #1
  • de îndată ce vedeți că versiunea nr. 2 funcționează așa cum ar trebui, eliminați versiunea nr. 1
  • gata!

Ușor, nu-i așa? Din păcate, nu este atât de simplu și ne vom uita la asta în detaliu mai târziu. Acum să verificăm un alt proces de implementare destul de comun - implementarea albastru verde.

Ai auzit vreodată de asta desfășurare albastru verde? Cloud Foundry face acest lucru extrem de ușor. Uită-te doar la acest articol, unde descriem acest lucru mai detaliat. Pentru a rezuma pe scurt, permiteți-ne să vă reamintim cum să faceți implementarea albastru-verde:

  • asigurați-vă că două copii ale codului dvs. de producție („albastru” și „verde”) funcționează;
  • direcționează tot traficul către mediul albastru, adică astfel încât adresele URL de producție să indice acolo;
  • implementați și testați toate modificările aplicațiilor într-un mediu verde;
  • comutați adresele URL de la mediu albastru la mediu verde

Implementarea albastru verde este o abordare care vă permite să introduceți cu ușurință noi funcții, fără a vă face griji cu privire la întreruperea producției. Acest lucru se datorează faptului că, chiar dacă se întâmplă ceva, puteți reveni cu ușurință la mediul anterior prin simpla „apăsare a unui comutator”.

După ce ați citit toate cele de mai sus, puteți pune întrebarea: Ce legătură are timpul de nefuncționare zero cu implementarea Blue green?

Ei bine, au destul de multe în comun, deoarece menținerea a două copii ale aceluiași mediu necesită efort dublu pentru a le menține. Acesta este motivul pentru care susțin unele echipe Martin Fowler, urmați o variantă a acestei abordări:

O altă opțiune este să utilizați aceeași bază de date, creând comutatoare albastru-verde pentru straturile web și de domeniu. În această abordare, baza de date poate fi adesea o problemă, mai ales atunci când trebuie să-i schimbați schema pentru a accepta o nouă versiune a software-ului.

Și aici ajungem la problema principală din acest articol. bază de date. Să aruncăm o altă privire la această frază.

efectua o migrare a bazei de date.

Acum trebuie să vă puneți întrebarea - ce se întâmplă dacă modificarea bazei de date nu este compatibilă cu înapoi? Nu se va rupe prima mea versiune a aplicației? De fapt, exact asta se va întâmpla...

Așadar, chiar și în ciuda avantajelor uriașe ale implementării zero downtime/verde albastru, companiile tind să urmeze următorul proces mai sigur pentru implementarea aplicațiilor lor:

  • pregătiți un pachet cu o nouă versiune a aplicației
  • închide o aplicație care rulează
  • rulați scripturi pentru a migra baza de date
  • implementați și lansați o nouă versiune a aplicației

În acest articol, vom detalia cum puteți lucra cu baza de date și codul dvs. pentru a profita de implementarea fără timpi de nefuncționare.

Probleme cu bazele de date

Dacă aveți o aplicație fără stat care nu stochează date în baza de date, puteți obține imediat implementare fără timpi de nefuncționare. Din păcate, majoritatea software-ului trebuie să stocheze datele undeva. Acesta este motivul pentru care ar trebui să vă gândiți de două ori înainte de a face orice modificare a circuitului. Înainte de a intra în detaliile modului de modificare a schemei, astfel încât să fie posibilă implementarea fără timpi de nefuncționare, să ne concentrăm mai întâi pe schema de versiuni.

Schema de versiuni

În acest articol vom folosi Calea de zbor ca instrument de control al versiunilor (aproximativ Traducere: vorbim despre migrarea bazelor de date). Desigur, vom scrie și o aplicație Spring Boot care are încorporat suport Flyway și va efectua migrarea schemei în timp ce se configurează contextul aplicației. Când utilizați Flyway, puteți stoca scripturi de migrare în folderul proiecte (î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 scripturi de migrare care, dacă nu au fost executate anterior, vor fi executate unul după altul la pornirea aplicației. Să ne uităm la 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 se explică de la sine: puteți utiliza SQL pentru a defini modul în care baza de date ar trebui să fie modificată. Pentru mai multe informații despre Spring Boot și Flyway, consultați Spring Boot Docs.

Folosind un instrument de control al sursei cu Spring Boot, obțineți 2 mari beneficii:

  • separați modificările bazei de date de modificările codului
  • Migrarea bazei de date are loc odată cu lansarea aplicației dvs., de exemplu. procesul dvs. de implementare este simplificat

Depanarea problemelor bazei de date

În următoarea secțiune a articolului, ne vom concentra asupra a două abordări ale modificărilor bazei de date.

  • incompatibilitate inversă
  • compatibilitate inversă

Primul va fi considerat ca un avertisment că nu ar trebui să efectuați o implementare zero downtime fără o pregătire preliminară... Al doilea oferă o soluție despre cum puteți realiza o implementare fără timpi de nefuncționare și, în același timp, mențineți compatibilitatea cu versiunea anterioară.

Proiectul nostru la care vom lucra va fi o aplicație simplă Spring Boot Flyway care are Person с first_name и last_name în baza de date (aproximativ traducere: Person este o masă și first_name и last_name - acestea sunt câmpurile din el). Vrem să redenumim last_name в surname.

Ipoteze

Înainte de a intra în detalii, există câteva ipoteze pe care trebuie să le facem despre aplicațiile noastre. Principalul rezultat pe care vrem să-l obținem va fi un proces destul de simplu.

Nota. Afaceri PRO-TIP. Simplificarea proceselor vă poate economisi mulți bani pe suport (cu cât aveți mai mulți oameni care lucrează pentru compania dvs., cu atât mai mulți bani puteți economisi)!

Nu este nevoie să derulați înapoi baza de date

Acest lucru simplifică procesul de implementare (unele derulări ale bazei de date sunt aproape imposibile, cum ar fi anularea ștergerii). Preferăm să facem înapoi numai aplicațiile. În acest fel, chiar dacă aveți baze de date diferite (de exemplu, SQL și NoSQL), conducta de implementare va arăta la fel.

Trebuie să fie ÎNTOTDEAUNA posibilă derularea aplicației cu o versiune înapoi (nu mai mult)

Rollback ar trebui făcută numai atunci când este necesar. Dacă există o eroare în versiunea actuală care nu este ușor de remediat, ar trebui să putem reveni la cea mai recentă versiune de lucru. Presupunem că această ultimă versiune de lucru este cea anterioară. Menținerea compatibilității codului și bazei de date pentru mai mult de o lansare ar fi extrem de dificilă și costisitoare.

Nota. Pentru o mai mare lizibilitate, în acest articol vom schimba versiunea majoră a aplicației.

Pasul 1: starea inițială

Versiunea aplicației: 1.0.0
Versiunea DB: v1

Comentariu

Aceasta va fi starea inițială a aplicației.

Schimbările bazei de date

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

Schimbări de cod

Aplicația stochează datele persoanei î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
                + "]";
    }
}

Redenumirea coloanei este incompatibilă înapoi

Să ne uităm la un exemplu despre cum să schimbați numele unei coloane:

Atenţie. Următorul exemplu va rupe lucrurile în mod intenționat. Arătăm acest lucru pentru a demonstra problema compatibilității bazei de date.

Versiunea aplicației: 2.0.0.BAD

Versiunea DB: v2bad

Comentariu

Modificările actuale NU ne permit să rulăm două instanțe (veche și noi) în același timp. Astfel, implementarea zero downtime va fi dificil de realizat (dacă sunt luate în considerare ipoteze, este de fapt imposibil).

Testare A/B

Situația actuală este că avem o versiune de aplicație 1.0.0, implementat în producție și bază de date v1. Trebuie să implementăm o a doua instanță a aplicației, versiunea 2.0.0.BADși actualizați baza de date la v2bad.

Pași:

  1. este implementată o nouă instanță a aplicației de versiune 2.0.0.BADcare actualizează baza de date la v2bad
  2. în baza de date v2bad coloană last_name nu mai există - a fost schimbat în surname
  3. Actualizarea bazei de date și a aplicației a avut succes și unele instanțe rulează 1.0.0, altele - în 2.0.0.BAD. Totul este conectat la baza de date v2bad
  4. toate instanțele versiunii 1.0.0 vor începe să arunce erori pentru că vor încerca să introducă date în coloană last_namecare nu mai există
  5. toate instanțele versiunii 2.0.0.BAD va funcționa fără probleme

După cum puteți vedea, dacă facem modificări incompatibile înapoi în baza de date și aplicație, testarea A/B este imposibilă.

Rollback aplicație

Să presupunem că după ce am încercat să facem implementarea A/B (aproximativ per.: autorul probabil a vrut să spună aici testarea A/B) am decis că trebuie să facem înapoi aplicația la versiune 1.0.0. Să presupunem că nu dorim să anulăm baza de date.

Pași:

  1. oprim instanța aplicației de versiune 2.0.0.BAD
  2. baza de date este încă v2bad
  3. încă de la versiune 1.0.0 nu intelege ce este surname, vom vedea erori
  4. s-a declanșat iadul, nu ne mai putem întoarce

După cum puteți vedea, dacă facem modificări incompatibile înapoi în baza de date și aplicație, nu putem reveni la versiunea anterioară.

Jurnalele de execuție a scripturilor

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

Schimbările bazei de date

Script de migrare care redenumește last_name в surname

Script sursă 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 care redenumește last_name.

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

Schimbări de cod

Am schimbat numele câmpului lastName pe surname.

Redenumirea unei coloane într-un mod compatibil cu invers

Aceasta este cea mai frecventă situație pe care o putem întâlni. Trebuie să facem schimbări incompatibile înapoi. Am demonstrat deja că, pentru implementarea fără timpi de nefuncționare, nu ar trebui să aplicăm pur și simplu migrarea bazei de date fără pași suplimentari. În această secțiune a articolului, vom efectua 3 implementări ale aplicației împreună cu migrarea bazei de date pentru a obține rezultatul dorit, menținând în același timp compatibilitatea cu versiunea inversă.

Nota. Amintiți-vă că avem o bază de date cu versiuni v1. Conține coloane first_name и last_name. Trebuie să ne schimbăm last_name pe surname. Avem și versiunea aplicației 1.0.0, care nu este încă folosit surname.

Pasul 2: Adăugați numele de familie

Versiunea aplicației: 2.0.0
Versiunea DB: v2

Comentariu

Prin adăugarea unei noi coloane și copierea conținutului acesteia, creăm modificări compatibile cu bazele de date. În același timp, dacă dăm înapoi JAR-ul sau dacă rulăm un JAR vechi, acesta nu se va rupe în timpul execuției.

Lansăm o nouă versiune

Pași:

  1. efectuați o migrare a bazei de date pentru a crea o nouă coloană surname. Acum versiunea dvs. DB v2
  2. copiați datele din last_name в surname. Notacă, dacă aveți o mulțime de aceste date, ar trebui să luați în considerare migrarea în lot!
  3. scrie codul unde sunt folosite AMBII и nouși vechiul coloană. Acum versiunea aplicației dvs 2.0.0
  4. citiți valoarea din coloană surname, daca nu este null, sau de la last_namedacă surname nu este specificat. Puteți șterge getLastName() din cod, deoarece va ieși null atunci când rulați înapoi aplicația dvs. de la 3.0.0 la 2.0.0.

Dacă utilizați Spring Boot Flyway, acești doi pași vor fi efectuati în timpul pornirii versiunii 2.0.0 aplicatii. Dacă rulați manual instrumentul de versiune a bazei de date, va trebui să faceți două lucruri diferite pentru a face acest lucru (mai întâi actualizați manual versiunea db și apoi implementați noua aplicație).

Important. Amintiți-vă că coloana nou creată NU AR TREBUI fi NU NUL. Dacă faceți un rollback, vechea aplicație nu știe despre noua coloană și nu o va instala în timpul Insert. Dar dacă adăugați această constrângere și db-ul dvs. va fi v2, aceasta va necesita setarea valorii noii coloane. Ceea ce va duce la încălcări ale restricțiilor.

Important. Ar trebui să eliminați metoda getLastName(), pentru că în versiune 3.0.0 Nu există conceptul de coloană în cod last_name. Aceasta înseamnă că null va fi setat acolo. Puteți lăsa metoda și puteți adăuga verificări pentru null, dar o soluție mult mai bună ar fi să te asiguri că în logică getSurname() ați selectat valoarea corectă diferită de zero.

Testare A/B

Situația actuală este că avem o versiune de aplicație 1.0.0, implementat în producție și baza de date în v1. Trebuie să implementăm o a doua instanță a aplicației de versiune 2.0.0care va actualiza baza de date la v2.

Pași:

  1. este implementată o nouă instanță a aplicației de versiune 2.0.0care actualizează baza de date la v2
  2. între timp unele solicitări au fost procesate de instanțe de versiune 1.0.0
  3. actualizarea a avut succes și aveți mai multe instanțe de rulare ale aplicației de versiune 1.0.0 si alte versiuni 2.0.0. Toată lumea comunică cu baza de date în v2
  4. versiune 1.0.0 nu folosește coloana prenume din baza de date, ci versiunea 2.0.0 utilizări. Ele nu interferează între ele și nu ar trebui să existe erori.
  5. versiune 2.0.0 stochează datele atât în ​​coloana veche, cât și în cea nouă, asigurând compatibilitatea cu versiunea anterioară

Important. Dacă aveți întrebări care numără elementele pe baza valorilor din coloana veche/nouă, ar trebui să vă amintiți că acum aveți valori duplicate (cel mai probabil, acestea sunt încă în migrare). De exemplu, dacă doriți să numărați numărul de utilizatori al căror nume de familie (indiferent de numele coloanei) a început cu litera A, apoi până când migrarea datelor este finalizată (oldnew coloană) este posibil să aveți date inconsecvente dacă interogați o nouă coloană.

Rollback aplicație

Acum avem versiunea aplicației 2.0.0 și baza de date în v2.

Pași:

  1. returnați aplicația la versiune 1.0.0.
  2. versiune 1.0.0 nu folosește o coloană în baza de date surname, deci rollback-ul ar trebui să aibă succes

DB se modifică

Baza de date conține o coloană numită last_name.

Scriptul sursă a căii de zbor:

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

Adăugați script surname.

Atenţie. Amintiți-vă că NU POȚI ADĂUGA nicio constrângere NOT NULL la coloana pe care o adăugați. Dacă derulați JAR-ul înapoi, versiunea veche nu va avea idee despre coloana adăugată și o va seta automat la NULL. Dacă există o astfel de limitare, vechea aplicație se va rupe pur și simplu.

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

Schimbări de cod

Stocăm datele ca last_nameși în surname. În același timp citim din last_name, deoarece această coloană este cea mai relevantă. În timpul procesului de implementare, este posibil ca unele solicitări să fi fost procesate de o instanță de aplicație 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 DB:v3

Comentariu

Notă per.: Aparent, în articolul original, autorul a copiat din greșeală textul acestui bloc de la pasul 2. La acest pas, ar trebui făcute modificări în codul aplicației care să vizeze eliminarea funcționalității care utilizează coloana last_name.

Adăugând o nouă coloană și copiend conținutul acesteia, am creat modificări compatibile cu baza de date inversă. De asemenea, dacă derulăm JAR-ul sau dacă rulăm un JAR vechi, acesta nu se va rupe în timpul execuției.

Rollback aplicație

În prezent avem versiunea aplicației 3.0.0 si baza de date v3. Versiune 3.0.0 nu salvează datele în last_name. Aceasta înseamnă că în surname sunt stocate cele mai actualizate informații.

Pași:

  1. returnați aplicația la versiune 2.0.0.
  2. versiune 2.0.0 utilizări și last_name и surname.
  3. versiune 2.0.0 va lua surname, dacă nu este zero, altfel -last_name

Schimbările bazei de date

Nu există modificări structurale în baza de date. Următorul script este executat pentru a efectua migrarea finală a datelor vechi:

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

Schimbări de cod

Notă per.: Descrierea acestui bloc a fost, de asemenea, copiată din greșeală de către autor de la pasul 2. În conformitate cu logica articolului, modificările codului la acest pas ar trebui să aibă ca scop eliminarea din el elemente care funcționează cu coloana last_name.

Stocăm datele ca last_nameși în surname. În plus, citim din rubrică last_name, deoarece este cel mai relevant. În timpul procesului de implementare, unele solicitări 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 baza de date

Versiunea aplicației: 4.0.0

Versiunea DB: v4

Comentariu

Datorită faptului că codul de versiune 3.0.0 nu a folosit coloana last_name, nu se va întâmpla nimic rău în timpul execuției dacă revenim la 3.0.0 după eliminarea unei coloane din baza de date.

Jurnalele de execuție a scripturilor

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

DB se modifică

Relativ v3 doar scoatem coloana last_name și adăugați restricțiile lipsă.

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

Schimbări de cod

Nu există modificări ale codului.

Producție

Am aplicat cu succes o modificare a numelui de coloană incompatibilă cu invers, efectuând mai multe implementări compatibile cu înapoi. Mai jos este un rezumat al acțiunilor efectuate:

  1. implementarea versiunii aplicației 1.0.0 с v1 schema bazei de date (numele coloanei = last_name)
  2. implementarea versiunii aplicației 2.0.0, care stochează datele în last_name и surname. Aplicația citește din last_name. Baza de date este în versiune v2conţinând coloane ca last_nameȘi surname. surname este o copie a lui last_name. (NOTĂ: Această coloană nu trebuie să aibă o constrângere nenulă)
  3. implementarea versiunii aplicației 3.0.0, care stochează doar date în surname și citește de la prenume. În ceea ce privește baza de date, are loc ultima migrare last_name в surname. De asemenea, o limitare NU NUL scos din last_name. Baza de date este acum în versiune v3
  4. implementarea versiunii aplicației 4.0.0 - nu se fac modificări la cod. Implementarea bazei de date v4, care înlătură last_name. Aici puteți adăuga orice constrângeri lipsă în baza de date.

Urmând această abordare, puteți derula oricând înapoi o versiune fără a întrerupe compatibilitatea bazei de date/aplicații.

Cod

Tot codul folosit în acest articol este disponibil la Github. Mai jos este o descriere suplimentară.

proiecte

După clonarea depozitului, veți vedea următoarea structură de foldere.

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

Script-uri

Puteți rula scripturile descrise în scripturile de mai jos, care vor demonstra modificări compatibile cu înapoi și incompatibile în baza de date.

Pentru a vedea cazul cu modificări compatibile cu înapoi, alerga:

./scripts/scenario_backward_compatible.sh

Și să văd caz cu modificări incompatibile înapoi, alerga:

./scripts/scenario_backward_incompatible.sh

Spring Boot Sample Flyway

Toate exemplele sunt preluate din Spring Boot Sample Flyway.

Puteți arunca o privire la http://localhost:8080/flyway, există o listă de scripturi.

Acest exemplu include și consola H2 (at http://localhost:8080/h2-console) astfel încât să puteți vizualiza starea bazei de date (URL-ul jdbc implicit este jdbc:h2:mem:testdb).

în plus

Citiți și alte articole de pe blogul nostru:

Sursa: www.habr.com

Adauga un comentariu