Μηδενική ανάπτυξη και βάσεις δεδομένων

Μηδενική ανάπτυξη και βάσεις δεδομένων

Αυτό το άρθρο εξηγεί λεπτομερώς τον τρόπο επίλυσης ζητημάτων συμβατότητας βάσης δεδομένων κατά την ανάπτυξη. Θα σας πούμε τι μπορεί να συμβεί στις εφαρμογές παραγωγής σας εάν προσπαθήσετε να αναπτύξετε χωρίς προκαταρκτική προετοιμασία. Στη συνέχεια, θα περάσουμε από τα στάδια του κύκλου ζωής της εφαρμογής που απαιτούνται για μηδενικό χρόνο διακοπής λειτουργίας (περίπου. λωρίδα: περαιτέρω - μηδενικός χρόνος διακοπής λειτουργίας). Το αποτέλεσμα των εργασιών μας θα είναι να εφαρμόσουμε την αλλαγή της βάσης δεδομένων που δεν είναι συμβατή προς τα πίσω με συμβατό προς τα πίσω τρόπο.

Εάν θέλετε να κατανοήσετε τα παραδείγματα κώδικα από το άρθρο, μπορείτε να τα βρείτε στη διεύθυνση GitHub.

Εισαγωγή

Μηδενική ανάπτυξη χρόνου διακοπής λειτουργίας

Τι μυστικιστικό μηδενική ανάπτυξη χρόνου διακοπής λειτουργίας? Μπορείτε να πείτε ότι αυτό συμβαίνει όταν η εφαρμογή σας αναπτύσσεται με τέτοιο τρόπο ώστε να μπορείτε να εισάγετε με επιτυχία μια νέα έκδοση της εφαρμογής στην παραγωγή, ενώ ο χρήστης δεν παρατηρεί τη μη διαθεσιμότητα της. Από την πλευρά του χρήστη και της εταιρείας, αυτό είναι το καλύτερο δυνατό σενάριο ανάπτυξης, επειδή επιτρέπει την εισαγωγή νέων λειτουργιών και τη διόρθωση σφαλμάτων χωρίς διακοπή.

Πώς να το πετύχετε αυτό; Υπάρχουν διάφοροι τρόποι, εδώ είναι ένας από αυτούς:

  • αναπτύξτε την έκδοση Νο. 1 της υπηρεσίας σας
  • εκτελέστε μια μετεγκατάσταση βάσης δεδομένων
  • Αναπτύξτε την έκδοση #2 της υπηρεσίας σας παράλληλα με την έκδοση #1
  • μόλις δείτε ότι η έκδοση Νο. 2 λειτουργεί όπως θα έπρεπε, αφαιρέστε την έκδοση Νο. 1
  • έτοιμος!

Εύκολο, έτσι δεν είναι; Δυστυχώς, δεν είναι τόσο απλό και θα το δούμε αναλυτικά αργότερα. Τώρα ας ελέγξουμε μια άλλη αρκετά κοινή διαδικασία ανάπτυξης - την ανάπτυξη μπλε πράσινου.

Έχετε ακούσει ποτέ για γαλαζοπράσινη ανάπτυξη? Το Cloud Foundry το κάνει εξαιρετικά εύκολο. Απλά κοιτάξτε αυτο το αρθρο, όπου το περιγράφουμε πιο αναλυτικά. Για να συνοψίσουμε εν συντομία, ας σας υπενθυμίσουμε πώς να κάνετε την ανάπτυξη μπλε πράσινου:

  • βεβαιωθείτε ότι λειτουργούν δύο αντίγραφα του κωδικού παραγωγής σας ("μπλε" και "πράσινο").
  • κατευθύνουν όλη την κίνηση στο μπλε περιβάλλον, δηλ. έτσι ώστε οι διευθύνσεις URL παραγωγής να δείχνουν εκεί.
  • ανάπτυξη και δοκιμή όλων των αλλαγών εφαρμογών σε ένα πράσινο περιβάλλον.
  • εναλλαγή url από μπλε σε πράσινο περιβάλλον

Η ανάπτυξη του μπλε πράσινου είναι μια προσέγγιση που σας επιτρέπει να εισάγετε εύκολα νέα χαρακτηριστικά χωρίς να ανησυχείτε για το σπάσιμο της παραγωγής. Αυτό οφείλεται στο γεγονός ότι ακόμα κι αν συμβεί κάτι, μπορείτε εύκολα να επιστρέψετε στο προηγούμενο περιβάλλον απλά «πατώντας ένα διακόπτη».

Αφού διαβάσετε όλα τα παραπάνω, μπορείτε να θέσετε την ερώτηση: Τι σχέση έχει ο μηδενικός χρόνος διακοπής λειτουργίας με την ανάπτυξη του μπλε πράσινου;

Λοιπόν, έχουν πολλά κοινά, αφού η διατήρηση δύο αντιγράφων του ίδιου περιβάλλοντος απαιτεί διπλάσια προσπάθεια για τη διατήρησή τους. Αυτός είναι ο λόγος που ορισμένες ομάδες ισχυρίζονται Μάρτιν Φάουλερ, ακολουθήστε μια παραλλαγή αυτής της προσέγγισης:

Μια άλλη επιλογή είναι να χρησιμοποιήσετε την ίδια βάση δεδομένων, δημιουργώντας μπλε-πράσινους διακόπτες για τα επίπεδα ιστού και τομέα. Σε αυτήν την προσέγγιση, η βάση δεδομένων μπορεί συχνά να είναι πρόβλημα, ειδικά όταν πρέπει να αλλάξετε το σχήμα της για να υποστηρίξετε μια νέα έκδοση του λογισμικού.

Και εδώ ερχόμαστε στο κύριο πρόβλημα σε αυτό το άρθρο. Βάση δεδομένων. Ας ρίξουμε μια άλλη ματιά σε αυτή τη φράση.

εκτελέστε μια μετεγκατάσταση βάσης δεδομένων.

Τώρα πρέπει να αναρωτηθείτε - τι γίνεται αν η αλλαγή της βάσης δεδομένων δεν είναι συμβατή προς τα πίσω; Δεν θα χαλάσει η πρώτη μου έκδοση της εφαρμογής; Στην πραγματικότητα, αυτό ακριβώς θα συμβεί...

Έτσι, ακόμη και παρά τα τεράστια πλεονεκτήματα του μηδενικού χρόνου διακοπής λειτουργίας / ανάπτυξης μπλε πράσινου, οι εταιρείες τείνουν να ακολουθούν την παρακάτω ασφαλέστερη διαδικασία για την ανάπτυξη των εφαρμογών τους:

  • ετοιμάστε ένα πακέτο με μια νέα έκδοση της εφαρμογής
  • τερματίστε μια εφαρμογή που εκτελείται
  • εκτελέστε σενάρια για μετεγκατάσταση της βάσης δεδομένων
  • αναπτύξτε και ξεκινήστε μια νέα έκδοση της εφαρμογής

Σε αυτό το άρθρο, θα αναφέρουμε λεπτομερώς πώς μπορείτε να εργαστείτε με τη βάση δεδομένων και τον κώδικά σας για να επωφεληθείτε από την ανάπτυξη μηδενικού χρόνου διακοπής λειτουργίας.

Προβλήματα βάσης δεδομένων

Εάν διαθέτετε μια εφαρμογή χωρίς κράτος που δεν αποθηκεύει δεδομένα στη βάση δεδομένων, μπορείτε να λάβετε μηδενική ανάπτυξη χρόνου διακοπής αμέσως. Δυστυχώς, το μεγαλύτερο μέρος του λογισμικού χρειάζεται να αποθηκεύει δεδομένα κάπου. Αυτός είναι ο λόγος για τον οποίο θα πρέπει να σκεφτείτε δύο φορές πριν κάνετε οποιεσδήποτε αλλαγές στο κύκλωμα. Πριν μπούμε στις λεπτομέρειες σχετικά με τον τρόπο αλλαγής του σχήματος ώστε να είναι δυνατή η ανάπτυξη χωρίς διακοπή λειτουργίας, ας εστιάσουμε πρώτα στο σχήμα έκδοσης.

Σχέδιο έκδοσης

Σε αυτό το άρθρο θα χρησιμοποιήσουμε Flyway ως εργαλείο ελέγχου έκδοσης (περίπου. Μετάφραση: μιλάμε για μετεγκαταστάσεις βάσεων δεδομένων). Φυσικά, θα γράψουμε επίσης μια εφαρμογή Spring Boot που έχει ενσωματωμένη υποστήριξη Flyway και θα εκτελεί μετεγκατάσταση σχήματος κατά τη ρύθμιση του περιβάλλοντος εφαρμογής. Όταν χρησιμοποιείτε το Flyway, μπορείτε να αποθηκεύσετε σενάρια μετεγκατάστασης στο φάκελο των έργων σας (από προεπιλογή στο classpath:db/migration). Εδώ μπορείτε να δείτε ένα παράδειγμα τέτοιων αρχείων μετεγκατάστασης

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

Σε αυτό το παράδειγμα βλέπουμε 4 σενάρια μετεγκατάστασης που, αν δεν εκτελεστούν προηγουμένως, θα εκτελεστούν το ένα μετά το άλλο όταν ξεκινήσει η εφαρμογή. Ας δούμε ένα από τα αρχεία (V1__init.sql) ως παράδειγμα.

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

Όλα είναι απολύτως αυτονόητα: μπορείτε να χρησιμοποιήσετε την SQL για να ορίσετε πώς θα τροποποιηθεί η βάση δεδομένων σας. Για περισσότερες πληροφορίες σχετικά με το Spring Boot και το Flyway, ρίξτε μια ματιά Έγγραφα Spring Boot.

Χρησιμοποιώντας ένα εργαλείο ελέγχου πηγής με το Spring Boot, λαμβάνετε 2 μεγάλα πλεονεκτήματα:

  • διαχωρίζετε τις αλλαγές της βάσης δεδομένων από τις αλλαγές κώδικα
  • Η μετεγκατάσταση της βάσης δεδομένων πραγματοποιείται μαζί με την κυκλοφορία της εφαρμογής σας, π.χ. η διαδικασία ανάπτυξής σας είναι απλοποιημένη

Αντιμετώπιση προβλημάτων της βάσης δεδομένων

Στην επόμενη ενότητα του άρθρου, θα επικεντρωθούμε στην εξέταση δύο προσεγγίσεων για τις αλλαγές της βάσης δεδομένων.

  • προς τα πίσω ασυμβατότητα
  • συμβατότητα προς τα πίσω

Το πρώτο θα θεωρηθεί ως προειδοποίηση ότι δεν πρέπει να εκτελέσετε μηδενική ανάπτυξη χρόνου διακοπής χωρίς προκαταρκτική προετοιμασία... Το δεύτερο προσφέρει μια λύση για το πώς μπορείτε να εκτελέσετε μια ανάπτυξη χωρίς χρόνο διακοπής λειτουργίας και ταυτόχρονα να διατηρήσετε συμβατότητα προς τα πίσω.

Το έργο μας στο οποίο θα εργαστούμε θα είναι μια απλή εφαρμογή Spring Boot Flyway που έχει Person с first_name и last_name στη βάση δεδομένων (περίπου. μετάφραση: Person είναι ένα τραπέζι και στirst_name и last_name - αυτά είναι τα χωράφια σε αυτό). Θέλουμε να μετονομάσουμε last_name в surname.

Υποθέσεις

Πριν μπούμε στις λεπτομέρειες, πρέπει να κάνουμε μερικές υποθέσεις σχετικά με τις εφαρμογές μας. Το κύριο αποτέλεσμα που θέλουμε να πετύχουμε θα είναι μια αρκετά απλή διαδικασία.

Το σημείωμα. Business PRO-TIP. Η απλοποίηση των διαδικασιών μπορεί να σας εξοικονομήσει πολλά χρήματα από την υποστήριξη (όσο περισσότερα άτομα εργάζονται για την εταιρεία σας, τόσο περισσότερα χρήματα μπορείτε να εξοικονομήσετε)!

Δεν χρειάζεται να επαναφέρετε τη βάση δεδομένων

Αυτό απλοποιεί τη διαδικασία ανάπτυξης (ορισμένες επαναλήψεις βάσεων δεδομένων είναι σχεδόν αδύνατες, όπως η επαναφορά διαγραφής). Προτιμούμε να επαναφέρουμε μόνο τις εφαρμογές. Με αυτόν τον τρόπο, ακόμα κι αν έχετε διαφορετικές βάσεις δεδομένων (για παράδειγμα, SQL και NoSQL), η διοχέτευση ανάπτυξης θα φαίνεται η ίδια.

Πρέπει ΠΑΝΤΑ να είναι δυνατή η επαναφορά της εφαρμογής μία έκδοση πίσω (όχι άλλη)

Η επαναφορά θα πρέπει να γίνεται μόνο όταν είναι απαραίτητο. Εάν υπάρχει κάποιο σφάλμα στην τρέχουσα έκδοση που δεν διορθώνεται εύκολα, θα πρέπει να μπορούμε να επιστρέψουμε στην πιο πρόσφατη έκδοση που λειτουργεί. Υποθέτουμε ότι αυτή η τελευταία έκδοση εργασίας είναι η προηγούμενη. Η διατήρηση της συμβατότητας κώδικα και βάσης δεδομένων για περισσότερες από μία εκδόσεις θα ήταν εξαιρετικά δύσκολη και δαπανηρή.

Το σημείωμα. Για μεγαλύτερη αναγνωσιμότητα, σε αυτό το άρθρο θα αλλάξουμε την κύρια έκδοση της εφαρμογής.

Βήμα 1: Αρχική κατάσταση

Έκδοση εφαρμογής: 1.0.0
Έκδοση DB: v1

Σχόλιο

Αυτή θα είναι η αρχική κατάσταση της εφαρμογής.

Αλλαγές βάσης δεδομένων

Το DB περιέχει 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');

Αλλαγές κώδικα

Η εφαρμογή αποθηκεύει δεδομένα ατόμου 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
                + "]";
    }
}

Μη συμβατή μετονομασία στηλών προς τα πίσω

Ας δούμε ένα παράδειγμα για το πώς να αλλάξετε το όνομα μιας στήλης:

Προσοχή. Το παρακάτω παράδειγμα θα σπάσει σκόπιμα τα πράγματα. Το δείχνουμε αυτό για να δείξουμε το πρόβλημα της συμβατότητας της βάσης δεδομένων.

Έκδοση εφαρμογής: 2.0.0.BAD

Έκδοση DB: v2bad

Σχόλιο

Οι τρέχουσες αλλαγές ΔΕΝ μας επιτρέπουν να εκτελούμε δύο παρουσίες (παλιές και νέες) ταυτόχρονα. Έτσι, η ανάπτυξη μηδενικού χρόνου διακοπής θα είναι δύσκολο να επιτευχθεί (αν ληφθούν υπόψη οι υποθέσεις, είναι στην πραγματικότητα αδύνατο).

Δοκιμή A/B

Η τρέχουσα κατάσταση είναι ότι έχουμε μια έκδοση εφαρμογής 1.0.0, που αναπτύσσονται στην παραγωγή και τη βάση δεδομένων v1. Πρέπει να αναπτύξουμε μια δεύτερη παρουσία της εφαρμογής, την έκδοση 2.0.0.BADκαι ενημερώστε τη βάση δεδομένων σε v2bad.

Βήματα:

  1. αναπτύσσεται μια νέα παρουσία της εφαρμογής έκδοσης 2.0.0.BADπου ενημερώνει τη βάση δεδομένων σε v2bad
  2. στη βάση δεδομένων v2bad στήλη last_name δεν υπάρχει πλέον - άλλαξε σε surname
  3. Η ενημέρωση της βάσης δεδομένων και της εφαρμογής ήταν επιτυχής και ορισμένες παρουσίες εκτελούνται 1.0.0, άλλα - μέσα 2.0.0.BAD. Όλα συνδέονται με τη βάση δεδομένων v2bad
  4. όλες οι περιπτώσεις της έκδοσης 1.0.0 θα αρχίσει να ρίχνει σφάλματα επειδή θα προσπαθήσει να εισαγάγει δεδομένα στη στήλη last_nameπου δεν υπάρχει πια
  5. όλες οι περιπτώσεις της έκδοσης 2.0.0.BAD θα λειτουργήσει χωρίς προβλήματα

Όπως μπορείτε να δείτε, εάν κάνουμε προς τα πίσω μη συμβατές αλλαγές στη βάση δεδομένων και την εφαρμογή, η δοκιμή A/B είναι αδύνατη.

Επαναφορά εφαρμογής

Ας υποθέσουμε ότι αφού προσπαθήσουμε να κάνουμε ανάπτυξη A/B (περίπου. ανά.: ο συγγραφέας μάλλον εννοούσε εδώ τη δοκιμή A/B) αποφασίσαμε ότι πρέπει να επαναφέρουμε την εφαρμογή στην έκδοση 1.0.0. Ας πούμε ότι δεν θέλουμε να επαναφέρουμε τη βάση δεδομένων.

Βήματα:

  1. διακόπτουμε την παρουσία της εφαρμογής έκδοσης 2.0.0.BAD
  2. η βάση δεδομένων είναι ακόμα v2bad
  3. από την έκδοση 1.0.0 δεν καταλαβαίνει τι είναι surname, θα δούμε σφάλματα
  4. Η κόλαση χάλασε, δεν μπορούμε να επιστρέψουμε πια

Όπως μπορείτε να δείτε, εάν κάνουμε προς τα πίσω μη συμβατές αλλαγές στη βάση δεδομένων και την εφαρμογή, δεν μπορούμε να επιστρέψουμε στην προηγούμενη έκδοση.

Αρχεία καταγραφής εκτέλεσης σεναρίου

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

Αλλαγές βάσης δεδομένων

Σενάριο μετεγκατάστασης που μετονομάζει last_name в surname

Πηγή σεναρίου 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');

Σενάριο που μετονομάζει last_name.

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

Αλλαγές κώδικα

Έχουμε αλλάξει το όνομα του πεδίου lastName επί surname.

Μετονομασία στήλης με τρόπο συμβατό προς τα πίσω

Αυτή είναι η πιο συνηθισμένη κατάσταση που μπορεί να συναντήσουμε. Πρέπει να κάνουμε προς τα πίσω ασύμβατες αλλαγές. Έχουμε ήδη αποδείξει ότι για ανάπτυξη μηδενικού χρόνου διακοπής λειτουργίας, δεν πρέπει να εφαρμόζουμε απλώς τη μετεγκατάσταση της βάσης δεδομένων χωρίς πρόσθετα βήματα. Σε αυτήν την ενότητα του άρθρου, θα εκτελέσουμε 3 αναπτύξεις της εφαρμογής μαζί με μετεγκαταστάσεις βάσης δεδομένων για να επιτύχουμε το επιθυμητό αποτέλεσμα διατηρώντας παράλληλα τη συμβατότητα προς τα πίσω.

Το σημείωμα. Θυμηθείτε ότι έχουμε μια βάση δεδομένων εκδόσεων v1. Περιέχει στήλες first_name и last_name. Πρέπει να αλλάξουμε last_name επί surname. Έχουμε επίσης την έκδοση της εφαρμογής 1.0.0, που δεν έχει χρησιμοποιηθεί ακόμη surname.

Βήμα 2: Προσθέστε επώνυμο

Έκδοση εφαρμογής: 2.0.0
Έκδοση DB: v2

Σχόλιο

Προσθέτοντας μια νέα στήλη και αντιγράφοντας τα περιεχόμενά της, δημιουργούμε αλλαγές στη βάση δεδομένων συμβατές προς τα πίσω. Ταυτόχρονα, εάν επαναφέρουμε το JAR ή έχουμε ένα παλιό JAR σε λειτουργία, δεν θα σπάσει κατά την εκτέλεση.

Κυκλοφορούμε μια νέα έκδοση

Βήματα:

  1. εκτελέστε μια μετεγκατάσταση βάσης δεδομένων για να δημιουργήσετε μια νέα στήλη surname. Τώρα η έκδοση DB σας v2
  2. αντιγραφή δεδομένων από last_name в surname. Обратите вниманиеότι αν έχετε πολλά από αυτά τα δεδομένα, θα πρέπει να εξετάσετε τη μετεγκατάσταση παρτίδων!
  3. γράψτε τον κωδικό όπου χρησιμοποιούνται ΚΑΙ ΤΑ ΔΥΟ и νέοςΚαι το παλιό στήλη. Τώρα η έκδοση της εφαρμογής σας 2.0.0
  4. διαβάστε την τιμή από τη στήλη surname, αν δεν είναι null, ή από λast_nameεάν surname Δεν διευκρινίζεται. Μπορείτε να διαγράψετε getLastName() από τον κωδικό, αφού θα βγει null όταν επαναφέρετε την αίτησή σας από 3.0.0 να 2.0.0.

Εάν χρησιμοποιείτε το Spring Boot Flyway, αυτά τα δύο βήματα θα εκτελεστούν κατά την εκκίνηση της έκδοσης 2.0.0 εφαρμογές. Εάν εκτελείτε το εργαλείο έκδοσης της βάσης δεδομένων με μη αυτόματο τρόπο, θα πρέπει να κάνετε δύο διαφορετικά πράγματα για να το κάνετε αυτό (πρώτα ενημερώστε την έκδοση db με μη αυτόματο τρόπο και, στη συνέχεια, αναπτύξτε τη νέα εφαρμογή).

Είναι σημαντικό. Θυμηθείτε ότι η στήλη που δημιουργήθηκε πρόσφατα ΔΕΝ ΘΑ ΕΠΡΕΠΕ είναι ΟΧΙ ΝΑΙ. Εάν κάνετε επαναφορά, η παλιά εφαρμογή δεν γνωρίζει για τη νέα στήλη και δεν θα την εγκαταστήσει κατά τη διάρκεια Insert. Αλλά αν προσθέσετε αυτόν τον περιορισμό και το db σας θα είναι v2, αυτό θα απαιτήσει να ορίσετε την τιμή της νέας στήλης. Κάτι που θα οδηγήσει σε παραβιάσεις των περιορισμών.

Είναι σημαντικό. Θα πρέπει να αφαιρέσετε τη μέθοδο getLastName(), γιατί στην έκδοση 3.0.0 Δεν υπάρχει έννοια στήλης στον κώδικα last_name. Αυτό σημαίνει ότι εκεί θα οριστεί το null. Μπορείτε να αφήσετε τη μέθοδο και να προσθέσετε επιταγές για null, αλλά μια πολύ καλύτερη λύση θα ήταν να βεβαιωθείτε ότι στη λογική getSurname() επιλέξατε τη σωστή μη μηδενική τιμή.

Δοκιμή A/B

Η τρέχουσα κατάσταση είναι ότι έχουμε μια έκδοση εφαρμογής 1.0.0, που αναπτύχθηκε κατά την παραγωγή και η βάση δεδομένων σε v1. Πρέπει να αναπτύξουμε μια δεύτερη παρουσία της εφαρμογής έκδοσης 2.0.0που θα ενημερώσει τη βάση δεδομένων σε v2.

Βήματα:

  1. αναπτύσσεται μια νέα παρουσία της εφαρμογής έκδοσης 2.0.0που ενημερώνει τη βάση δεδομένων σε v2
  2. Στο μεταξύ ορισμένα αιτήματα υποβλήθηκαν σε επεξεργασία από παρουσίες έκδοσης 1.0.0
  3. η ενημέρωση ήταν επιτυχής και έχετε πολλές εκτελούμενες εμφανίσεις της εφαρμογής έκδοσης 1.0.0 και άλλες εκδόσεις 2.0.0. Όλοι επικοινωνούν με τη βάση δεδομένων v2
  4. εκδοχή 1.0.0 δεν χρησιμοποιεί τη στήλη επωνύμου στη βάση δεδομένων, αλλά την έκδοση 2.0.0 χρήσεις. Δεν παρεμβαίνουν μεταξύ τους και δεν πρέπει να υπάρχουν σφάλματα.
  5. εκδοχή 2.0.0 αποθηκεύει δεδομένα τόσο στην παλιά όσο και στη νέα στήλη, διασφαλίζοντας συμβατότητα προς τα πίσω

Είναι σημαντικό. Εάν έχετε οποιαδήποτε ερωτήματα που μετρούν στοιχεία με βάση τις τιμές από την παλιά/νέα στήλη, θα πρέπει να θυμάστε ότι τώρα έχετε διπλότυπες τιμές (πιθανότατα εξακολουθούν να μετεγκαθίστανται). Για παράδειγμα, εάν θέλετε να μετρήσετε τον αριθμό των χρηστών των οποίων το επίθετο (όποιο κι αν ονομάζεται η στήλη) ξεκινούσε με το γράμμα A, στη συνέχεια μέχρι να ολοκληρωθεί η μετεγκατάσταση δεδομένων (oldnew στήλη) ενδέχεται να έχετε ασυνεπή δεδομένα εάν υποβάλετε ερώτημα σε μια νέα στήλη.

Επαναφορά εφαρμογής

Τώρα έχουμε την έκδοση της εφαρμογής 2.0.0 και βάση δεδομένων σε v2.

Βήματα:

  1. επαναφέρετε την εφαρμογή σας στην έκδοση 1.0.0.
  2. εκδοχή 1.0.0 δεν χρησιμοποιεί στήλη στη βάση δεδομένων surname, επομένως η επαναφορά θα πρέπει να είναι επιτυχής

Αλλαγές DB

Η βάση δεδομένων περιέχει μια στήλη με το όνομα last_name.

Πηγαίο σενάριο 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');

Προσθήκη σεναρίου surname.

Προσοχή. Να θυμάστε ότι ΔΕΝ ΜΠΟΡΕΙΤΕ ΝΑ ΠΡΟΣΘΕΣΕΤΕ ΟΥΔΕΝ ΜΗ ΜΗΝΟΥΣ περιορισμούς στη στήλη που προσθέτετε. Εάν επαναφέρετε το JAR, η παλιά έκδοση δεν θα έχει ιδέα για την προστιθέμενη στήλη και θα την ορίσει αυτόματα σε NULL. Εάν υπάρχει τέτοιος περιορισμός, η παλιά εφαρμογή απλώς θα σπάσει.

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

Αλλαγές κώδικα

Αποθηκεύουμε δεδομένα ως last_name, και στο surname. Ταυτόχρονα διαβάζουμε από 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;
    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
                + "]";
    }
}

Βήμα 3: Αφαίρεση last_name από τον κωδικό

Έκδοση εφαρμογής: 3.0.0

Έκδοση DB:v3

Σχόλιο

Σημείωση ανά.: Προφανώς, στο αρχικό άρθρο ο συγγραφέας αντέγραψε κατά λάθος το κείμενο αυτού του μπλοκ από το βήμα 2. Σε αυτό το βήμα, θα πρέπει να γίνουν αλλαγές στον κώδικα εφαρμογής με στόχο την κατάργηση της λειτουργικότητας που χρησιμοποιεί τη στήλη last_name.

Προσθέτοντας μια νέα στήλη και αντιγράφοντας τα περιεχόμενά της, δημιουργήσαμε αλλαγές στη βάση δεδομένων συμβατές προς τα πίσω. Επίσης, αν επαναφέρουμε το JAR ή έχουμε ένα παλιό JAR σε λειτουργία, δεν θα σπάσει κατά την εκτέλεση.

Επαναφορά εφαρμογής

Αυτήν τη στιγμή έχουμε έκδοση εφαρμογής 3.0.0 και βάση δεδομένων v3. Εκδοχή 3.0.0 δεν αποθηκεύει δεδομένα σε last_name. Αυτό σημαίνει ότι σε surname αποθηκεύονται οι πιο ενημερωμένες πληροφορίες.

Βήματα:

  1. επαναφέρετε την εφαρμογή σας στην έκδοση 2.0.0.
  2. εκδοχή 2.0.0 χρήσεις και last_name и surname.
  3. εκδοχή 2.0.0 θα λάβει surname, αν δεν είναι μηδέν, διαφορετικά -last_name

Αλλαγές βάσης δεδομένων

Δεν υπάρχουν δομικές αλλαγές στη βάση δεδομένων. Το ακόλουθο σενάριο εκτελείται για την εκτέλεση της τελικής μετεγκατάστασης των παλαιών δεδομένων:

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

Αλλαγές κώδικα

Σημείωση ανά.: Η περιγραφή αυτού του μπλοκ αντιγράφηκε επίσης κατά λάθος από τον συγγραφέα από το βήμα 2. Σύμφωνα με τη λογική του άρθρου, οι αλλαγές στον κώδικα σε αυτό το βήμα θα πρέπει να στοχεύουν στην αφαίρεση από αυτό στοιχείων που λειτουργούν με τη στήλη last_name.

Αποθηκεύουμε δεδομένα ως last_name, και στο surname. Επιπλέον, διαβάζουμε από τη στήλη 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 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
                + "]";
    }
}

Βήμα 4: Αφαίρεση last_name από τη βάση δεδομένων

Έκδοση εφαρμογής: 4.0.0

Έκδοση DB: v4

Σχόλιο

Λόγω του γεγονότος ότι ο κωδικός έκδοσης 3.0.0 δεν χρησιμοποίησε τη στήλη last_name, τίποτα κακό δεν θα συμβεί κατά την εκτέλεση εάν επιστρέψουμε στο 3.0.0 αφού αφαιρέσετε μια στήλη από τη βάση δεδομένων.

Αρχεία καταγραφής εκτέλεσης σεναρίου

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

Σχετικά v3 απλώς αφαιρούμε τη στήλη last_name και προσθέστε περιορισμούς που λείπουν.

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

Αλλαγές κώδικα

Δεν υπάρχουν αλλαγές στον κωδικό.

Παραγωγή

Εφαρμόσαμε επιτυχώς μια αλλαγή ονόματος στήλης μη συμβατή προς τα πίσω, εκτελώντας αρκετές αναπτύξεις συμβατές με τα πίσω. Ακολουθεί μια περίληψη των ενεργειών που εκτελέστηκαν:

  1. ανάπτυξη της έκδοσης της εφαρμογής 1.0.0 с v1 σχήμα βάσης δεδομένων (όνομα στήλης = last_name)
  2. ανάπτυξη της έκδοσης της εφαρμογής 2.0.0, που αποθηκεύει δεδομένα σε last_name и surname. Η εφαρμογή διαβάζεται από last_name. Η βάση δεδομένων είναι σε έκδοση v2που περιέχει στήλες όπως last_nameΚαι surname. surname είναι αντίγραφο του λast_name. (ΣΗΜΕΙΩΣΗ: Αυτή η στήλη δεν πρέπει να έχει μη μηδενικό περιορισμό)
  3. ανάπτυξη της έκδοσης της εφαρμογής 3.0.0, το οποίο αποθηκεύει μόνο δεδομένα surname και διαβάζει από το επώνυμο. Όσον αφορά τη βάση δεδομένων, πραγματοποιείται η τελευταία μετεγκατάσταση last_name в surname. Επίσης περιορισμός ΟΧΙ ΝΑΙ αφαιρέθηκε από last_name. Η βάση δεδομένων είναι τώρα σε έκδοση v3
  4. ανάπτυξη της έκδοσης της εφαρμογής 4.0.0 - δεν γίνονται αλλαγές στον κωδικό. Ανάπτυξη βάσης δεδομένων v4, που αφαιρεί last_name. Εδώ μπορείτε να προσθέσετε τυχόν περιορισμούς που λείπουν στη βάση δεδομένων.

Ακολουθώντας αυτήν την προσέγγιση, μπορείτε πάντα να επαναφέρετε μια έκδοση χωρίς να διαταραχθεί η συμβατότητα βάσης δεδομένων/εφαρμογών.

Κώδικας

Όλος ο κώδικας που χρησιμοποιείται σε αυτό το άρθρο είναι διαθέσιμος στη διεύθυνση Github. Παρακάτω είναι πρόσθετη περιγραφή.

Έργα

Μετά την κλωνοποίηση του αποθετηρίου, θα δείτε την ακόλουθη δομή φακέλου.

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

Ανοιξιάτικο Δείγμα Μπότας Flyway

Όλα τα παραδείγματα λαμβάνονται από Spring Boot Sample Flyway.

Μπορείτε να ρίξετε μια ματιά σε http://localhost:8080/flyway, υπάρχει μια λίστα με σενάρια.

Αυτό το παράδειγμα περιλαμβάνει επίσης την κονσόλα H2 (στο http://localhost:8080/h2-console) ώστε να μπορείτε να δείτε την κατάσταση της βάσης δεδομένων (η προεπιλεγμένη διεύθυνση URL jdbc είναι jdbc:h2:mem:testdb).

επιπλέον

Διαβάστε επίσης άλλα άρθρα στο ιστολόγιό μας:

Πηγή: www.habr.com

Προσθέστε ένα σχόλιο