عدم استقرار و پایگاه داده صفر

عدم استقرار و پایگاه داده صفر

این مقاله به طور مفصل نحوه حل مشکلات سازگاری پایگاه داده در استقرار را توضیح می دهد. ما به شما می گوییم که اگر بخواهید بدون آماده سازی مقدماتی استقرار کنید، چه اتفاقی برای برنامه های تولید شما می افتد. سپس مراحل چرخه عمر برنامه را طی می کنیم که نیاز به توقف صفر است (تقریبا خط: بیشتر - صفر خرابی). نتیجه عملیات ما اعمال تغییر پایگاه داده ناسازگار به عقب به شیوه ای سازگار با عقب خواهد بود.

اگر می‌خواهید نمونه‌های کد مقاله را بفهمید، می‌توانید آنها را در اینجا بیابید GitHub.

معرفی

استقرار زمان توقف صفر

چه عرفانی استقرار زمان توقف صفر? می توان گفت این زمانی است که برنامه شما به گونه ای مستقر شده است که می توانید با موفقیت نسخه جدیدی از برنامه را به تولید معرفی کنید، در حالی که کاربر متوجه در دسترس نبودن آن نمی شود. از دیدگاه کاربر و شرکت، این بهترین سناریوی استقرار ممکن است زیرا امکان معرفی ویژگی‌های جدید و رفع اشکالات بدون اختلال را فراهم می‌کند.

چگونه می توان به این امر دست یافت؟ راه های مختلفی وجود دارد که در اینجا یکی از آنها وجود دارد:

  • نسخه شماره 1 سرویس خود را مستقر کنید
  • انتقال پایگاه داده را انجام دهید
  • نسخه شماره 2 سرویس خود را به موازات نسخه شماره 1 مستقر کنید
  • به محض اینکه دیدید نسخه شماره 2 همانطور که باید کار می کند ، نسخه شماره 1 را حذف کنید
  • آماده است!

آسان است، اینطور نیست؟ متأسفانه، به این سادگی نیست و بعداً به طور مفصل به آن خواهیم پرداخت. حالا بیایید یکی دیگر از فرآیند استقرار نسبتاً رایج - استقرار سبز آبی را بررسی کنیم.

آیا تا به حال در مورد آن شنیده اید استقرار سبز آبی? Cloud Foundry این کار را بسیار آسان می کند. فقط نگاه کن این مقاله، جایی که ما این را با جزئیات بیشتر توضیح می دهیم. برای خلاصه کردن، اجازه دهید به شما یادآوری کنیم که چگونه استقرار سبز آبی را انجام دهید:

  • اطمینان حاصل کنید که دو نسخه از کد تولید شما ("آبی" و "سبز") کار می کند.
  • تمام ترافیک را به محیط آبی هدایت کنید، یعنی. به طوری که URL های تولید به آنجا اشاره می کنند.
  • استقرار و آزمایش تمام تغییرات برنامه در یک محیط سبز.
  • url ها را از محیط آبی به سبز تغییر دهید

استقرار سبز آبی رویکردی است که به شما امکان می دهد به راحتی ویژگی های جدید را بدون نگرانی در مورد شکستن تولید معرفی کنید. این به خاطر این واقعیت است که حتی اگر اتفاقی بیفتد، می‌توانید به راحتی با «تقطع سوئیچ» به محیط قبلی برگردید.

پس از خواندن تمام موارد بالا، ممکن است این سوال را بپرسید: توقف صفر چه ربطی به استقرار سبز آبی دارد؟

خوب، آنها اشتراکات زیادی دارند، زیرا نگهداری دو نسخه از یک محیط یکسان نیاز به تلاش مضاعف برای نگهداری آنها دارد. به همین دلیل است که برخی تیم ها ادعا می کنند مارتین فاولر، نوعی از این رویکرد را دنبال کنید:

گزینه دیگر استفاده از همان پایگاه داده، ایجاد سوئیچ های سبز-آبی برای لایه های وب و دامنه است. در این رویکرد، پایگاه داده اغلب می تواند مشکل ساز باشد، به خصوص زمانی که شما نیاز به تغییر طرح آن برای پشتیبانی از نسخه جدید نرم افزار دارید.

و در اینجا به مشکل اصلی در این مقاله می رسیم. پایگاه داده. بیایید نگاهی دیگر به این عبارت بیندازیم.

انتقال پایگاه داده را انجام دهید.

حال باید این سوال را از خود بپرسید - اگر تغییر پایگاه داده با عقب سازگار نباشد چه؟ آیا اولین نسخه برنامه من خراب نمی شود؟ در واقع این دقیقا همان چیزی است که اتفاق خواهد افتاد ...

بنابراین، حتی با وجود مزایای عظیم عدم توقف / استقرار سبز آبی، شرکت‌ها تمایل دارند روند ایمن‌تر زیر را برای استقرار برنامه‌های خود دنبال کنند:

  • یک بسته با نسخه جدید برنامه آماده کنید
  • یک برنامه در حال اجرا را خاموش کنید
  • اسکریپت ها را برای انتقال پایگاه داده اجرا کنید
  • استقرار و راه اندازی یک نسخه جدید از برنامه

در این مقاله، نحوه کار با دیتابیس و کد خود را برای استفاده از عدم استفاده از زمان خرابی توضیح خواهیم داد.

مسائل پایگاه داده

اگر یک برنامه بدون حالت دارید که هیچ داده‌ای را در پایگاه داده ذخیره نمی‌کند، می‌توانید فوراً استقرار زمان توقف صفر را دریافت کنید. متأسفانه اکثر نرم افزارها نیاز به ذخیره داده ها در جایی دارند. به همین دلیل است که قبل از ایجاد هرگونه تغییر در مدار باید دو بار فکر کنید. قبل از اینکه به جزئیات نحوه تغییر طرحواره بپردازیم تا امکان استقرار بدون توقف وجود داشته باشد، ابتدا روی طرح نسخه سازی تمرکز می کنیم.

طرح نسخه سازی

در این مقاله استفاده خواهیم کرد مسیر پرواز به عنوان یک ابزار کنترل نسخه (تقریبا ترجمه: ما در مورد مهاجرت پایگاه داده صحبت می کنیم). طبیعتاً، ما همچنین یک برنامه 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 و Flyway، بررسی کنید اسناد بوت بهار.

با استفاده از ابزار کنترل منبع با Spring Boot، 2 مزیت بزرگ دریافت می کنید:

  • شما تغییرات پایگاه داده را از تغییرات کد جدا می کنید
  • انتقال پایگاه داده همراه با عرضه برنامه شما اتفاق می افتد، یعنی. فرآیند استقرار شما ساده شده است

عیب یابی مشکلات پایگاه داده

در بخش بعدی مقاله، به بررسی دو رویکرد برای تغییرات پایگاه داده می پردازیم.

  • ناسازگاری عقب مانده
  • سازگاری به عقب

مورد اول به عنوان یک هشدار در نظر گرفته خواهد شد که نباید بدون آمادگی اولیه، استقرار زمان توقف صفر را انجام دهید... دومی راه حلی را ارائه می دهد که چگونه می توانید یک استقرار را بدون خرابی انجام دهید و در عین حال سازگاری با عقب را حفظ کنید.

پروژه ما که روی آن کار خواهیم کرد یک برنامه ساده Spring Boot Flyway خواهد بود که دارد Person с first_name и last_name در پایگاه داده (تقریبا ترجمه: Person یک جدول است و first_name и last_name - اینها زمینه های موجود در آن هستند). ما می خواهیم نام خود را تغییر دهیم last_name в surname.

مفروضات

قبل از اینکه به جزئیات بپردازیم، چند فرضیه وجود دارد که باید در مورد برنامه های خود انجام دهیم. نتیجه اصلی که می خواهیم به آن برسیم، یک فرآیند نسبتاً ساده خواهد بود.

یادداشت. کسب و کار 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 (تقریبا per.: احتمالا منظور نویسنده تست 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، یا از last_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.

توجه. به یاد داشته باشید که نمی توانید هیچ گونه محدودیت NOT NULL را به ستونی که اضافه می کنید اضافه کنید. اگر 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

توضیح

توجه داشته باشید per.: ظاهراً در مقاله اصلی نویسنده به اشتباه متن این بلوک را از مرحله 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;

کد تغییر می کند

توجه داشته باشید per.: توضیحات این بلوک نیز به اشتباه توسط نویسنده از مرحله 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 یک کپی از l استast_name. (توجه: این ستون نباید دارای محدودیت non null باشد)
  3. استقرار نسخه برنامه 3.0.0، که فقط داده ها را در آن ذخیره می کند surname و از روی نام خانوادگی می خواند. در مورد پایگاه داده، آخرین مهاجرت در حال انجام است last_name в surname. همچنین یک محدودیت تهی نیست حذف شده از last_name. پایگاه داده اکنون در نسخه است v3
  4. استقرار نسخه برنامه 4.0.0 - هیچ تغییری در کد ایجاد نمی شود. استقرار پایگاه داده v4، که حذف می کند last_name. در اینجا می توانید هر گونه محدودیت از دست رفته را به پایگاه داده اضافه کنید.

با پیروی از این رویکرد، همیشه می‌توانید یک نسخه را بدون شکستن سازگاری پایگاه داده/برنامه‌ها به عقب برگردانید.

رمز

تمام کدهای استفاده شده در این مقاله در این آدرس موجود است گیتهاب. در زیر توضیحات تکمیلی آمده است.

پروژه ها

پس از کلون سازی مخزن، ساختار پوشه زیر را مشاهده خواهید کرد.

├── boot-flyway-v1              - 1.0.0 version of the app with v1 of the schema
├── boot-flyway-v2              - 2.0.0 version of the app with v2 of the schema (backward-compatible - app can be rolled back)
├── boot-flyway-v2-bad          - 2.0.0.BAD version of the app with v2bad of the schema (backward-incompatible - app cannot be rolled back)
├── boot-flyway-v3              - 3.0.0 version of the app with v3 of the schema (app can be rolled back)
└── boot-flyway-v4              - 4.0.0 version of the app with v4 of the schema (app can be rolled back)

اسکریپت ها

می‌توانید اسکریپت‌هایی را که در اسکریپت‌های زیر توضیح داده شده‌اند اجرا کنید، که تغییرات سازگار با عقب و ناسازگار را در پایگاه داده نشان می‌دهد.

دیدن مورد با تغییرات سازگار با عقب، اجرا کن:

./scripts/scenario_backward_compatible.sh

و برای دیدن مورد با تغییرات ناسازگار با عقب، اجرا کن:

./scripts/scenario_backward_incompatible.sh

نمونه چکمه فنری فلای وی

همه نمونه ها از Spring Boot Sample Flyway.

می توانید نگاهی بیندازید http://localhost:8080/flyway، لیستی از اسکریپت ها وجود دارد.

این مثال همچنین شامل کنسول H2 (در http://localhost:8080/h2-console) بنابراین می توانید وضعیت پایگاه داده را مشاهده کنید (URL پیش فرض jdbc است jdbc:h2:mem:testdb).

علاوه بر این

همچنین سایر مقالات وبلاگ ما را بخوانید:

منبع: www.habr.com

اضافه کردن نظر