
تشرح هذه المقالة بالتفصيل كيفية حل المشكلات المرتبطة بتوافق قاعدة البيانات أثناء النشر. سنخبرك بما يمكن أن يحدث للتطبيقات الخاصة بك على المنتج إذا حاولت إجراء عملية نشر دون أي إعداد أولي. بعد ذلك ، سوف نمر بمراحل دورة حياة التطبيق ، وهي ضرورية من أجل الحصول على وقت تعطل صفري ( تقريبًا. ترجم.: مزيد - وقت تعطل صفري ). ستكون نتيجة عملياتنا هي تطبيق تغيير قاعدة بيانات غير متوافق إلى الوراء بطريقة متوافقة مع الإصدارات السابقة.
إذا كنت ترغب في فهم أمثلة التعليمات البرمجية من المقالة ، فستجدها في GitHub .
مقدمة
صفر تعطل النشر
ما هو النشر باطني صفر التعطل ؟ يمكننا أن نقول هذا عندما يتم نشر التطبيق الخاص بك بحيث يمكنك إدخال إصدار جديد من التطبيق للإنتاج بنجاح ، بينما لا يلاحظ المستخدم عدم إمكانية الوصول إليه. من وجهة نظر المستخدم والشركة ، هذا هو أفضل سيناريو نشر ممكن ، حيث يمكنك بهذه الطريقة تقديم وظائف جديدة والقضاء على الأخطاء دون انقطاع.
كيف تحقق هذا؟ هناك عدة طرق ، إليك إحدى هذه الطرق:
- توسيع الإصدار 1 من الخدمة الخاصة بك
- ترحيل قاعدة البيانات
- نشر الإصدار 2 من الخدمة بالتوازي مع الإصدار 1
- بمجرد أن ترى أن الإصدار رقم 2 يعمل كما يجب ، أزل الإصدار رقم 1
- جاهزة!
سهل ، أليس كذلك؟ لسوء الحظ ، هذا ليس بهذه البساطة ، وسوف ننظر في هذا بالتفصيل في وقت لاحق. الآن دعونا نتحقق من عملية نشر شائعة أخرى - نشر أخضر أزرق.
هل سمعت من قبل نشر الأخضر الأزرق ؟ مع Cloud Foundry ، من السهل جدًا القيام بذلك. مجرد إلقاء نظرة على هذه المقالة حيث وصفناها بمزيد من التفاصيل. بتلخيص لفترة وجيزة ، نتذكر كيفية القيام النشر الأخضر الأزرق:
- تأكد من تشغيل نسختين من رمز الإنتاج ("الأزرق" و "الأخضر") ؛
- توجيه كل حركة المرور إلى البيئة الزرقاء ، أي بحيث تتم الإشارة إلى عناوين 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 Docs .
يمنحك استخدام التحكم في الإصدار مع Spring Boot اثنين من الميزات الرائعة:
- يمكنك فصل التغييرات قاعدة البيانات من التغييرات رمز
- يحدث ترحيل قاعدة البيانات جنبًا إلى جنب مع بدء تطبيقك ، أي عملية النشر مبسطة
حل مشاكل قاعدة البيانات
في القسم التالي من المقالة ، سنركز على النظر في طريقتين لتغيير قواعد البيانات.
- عدم التوافق الخلفي
- التوافق الخلفي
سيتم اعتبار الأول بمثابة تحذير من عدم تنفيذ عملية تعطل صفرية دون تحضير أولي ... والثاني يقدم حلاً حول كيفية إجراء عملية نشر دون توقف وفي الوقت نفسه الحفاظ على التوافق مع الإصدارات السابقة.
سيكون مشروعنا ، الذي سنعمل عليه ، تطبيق Spring Spring Flyway بسيطًا يوجد فيه Person
يحمل first_name
واسم العائلة في قاعدة البيانات ( approx.per.: Person
عبارة عن جدول ، و f irst_name
و last_name
هما irst_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,
نشره في prod ، وقاعدة بيانات v1
. يجب نشر المثيل الثاني للتطبيق ، الإصدار 2.0.0.BAD
، وترقية قاعدة البيانات إلى v2bad
.
الخطوات التالية:
- نشر مثيل جديد من إصدار التطبيق
2.0.0.BAD
، والذي يقوم بتحديث قاعدة البيانات إلى v2bad
- العمود
last_name
لم يعد موجودًا في قاعدة بيانات v2bad
- تم تغييره إلى surname
- كانت تحديثات قاعدة البيانات والتطبيق ناجحة ، وبعض الحالات تعمل في
1.0.0
، والبعض الآخر في 2.0.0.BAD
. جميع المتعلقة v2bad
- ستبدأ جميع مثيلات الإصدار
1.0.0
في إلقاء الأخطاء لأنها ستحاول إدراج البيانات في العمود last_name
، والذي لم يعد - ستعمل جميع مثيلات الإصدار
2.0.0.BAD
دون مشاكل
كما ترون ، إذا أجرينا تغييرات غير متوافقة مع الإصدارات السابقة على قاعدة البيانات والتطبيقات ، فإن اختبار A / B أمر مستحيل.
تطبيق الاستعادة
لنفترض أنه بعد محاولة إجراء نشر A / B ( ترجمة تقريبية.: ربما ، كان المؤلف يشير إلى اختبار A / B هنا ) ، قررنا أننا نحتاج إلى استعادة التطبيق إلى الإصدار 1.0.0.
لنفترض أننا لا نريد استعادة قاعدة البيانات.
الخطوات التالية:
- نتوقف عن نسخة التطبيق
2.0.0.BAD
- قاعدة البيانات لا تزال
v2bad
- منذ الإصدار
1.0.0
لا يفهم ما surname
، سنرى الأخطاء - كسر الجحيم مجانا لم نعد نستطيع العودة
كما ترون ، إذا أجرينا تغييرات غير متوافقة مع الإصدارات السابقة على قاعدة البيانات والتطبيقات ، فلا يمكننا الرجوع إلى الإصدار السابق.
سجلات تنفيذ البرنامج النصي
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"}
التغييرات DB
سيناريو الترحيل الذي يعيد تسمية 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 قديم ، فلن ينكسر في وقت التشغيل.
نحن طرح الإصدار الجديد
الخطوات التالية:
- ترحيل قاعدة البيانات لإنشاء عمود
surname
الجديد. الآن لديك نسخة ديسيبل v2
- نسخ البيانات من
last_name
إلى surname
. لاحظ أنه إذا كان لديك الكثير من هذه البيانات ، فيجب أن تفكر في الترحيل الدفعي! - اكتب الكود حيث يتم استخدام كل من العمود الجديد والقديم . الآن تطبيقك الإصدار
2.0.0
- اقرأ القيمة من عمود
surname
إذا لم تكن null
أو من l ast_name
إذا لم يتم تحديد surname
. يمكنك إزالة getLastName()
من التعليمات البرمجية ، حيث ستعود null
عند إرجاع التطبيق الخاص بك من 3.0.0
إلى 2.0.0
.
إذا كنت تستخدم Spring Boot Flyway ، فسيتم تنفيذ هاتين الخطوتين أثناء إطلاق الإصدار 2.0.0
التطبيق. إذا قمت بتشغيل أداة تعيين قاعدة البيانات يدويًا ، فسيتعين عليك القيام بأمرين مختلفين لهذا (أولاً قم بتحديث إصدار db يدويًا ، ثم نشر تطبيق جديد).
هذا مهم. تذكر أن العمود الذي تم إنشاؤه حديثًا يجب ألا يكون فارغًا . إذا استرجعت ، فإن التطبيق القديم لا يعرف العمود الجديد ولن يقوم بتثبيته أثناء Insert.
ولكن إذا أضفت هذا التقييد ، وستكون قاعدة بياناتك هي v2
، v2
الأمر تعيين قيمة العمود الجديد. مما سيؤدي إلى انتهاكات للقيود.
هذا مهم. يجب إزالة طريقة getLastName()
، لأن الإصدار 3.0.0
getLastName()
يحتوي على مفهوم عمود last_name
في التعليمات البرمجية. هذا يعني أنه سيتم تعيين فارغة. يمكنك ترك الطريقة وإضافة getSurname()
null
، ولكن الحل الأفضل هو التأكد من getSurname()
منطق getSurname()
اخترت القيمة غير الصفرية الصحيحة.
اختبار A / B
الوضع الحالي هو أن لدينا إصدار تطبيق 1.0.0
نشره على prod وقاعدة بيانات في v1
. نحتاج إلى نشر المثيل الثاني من إصدار التطبيق 2.0.0
، والذي سيرفع قاعدة البيانات إلى v2
.
الخطوات التالية:
- نشر مثيل جديد من إصدار التطبيق
2.0.0
، والذي يقوم بتحديث قاعدة البيانات إلى v2
- وفي الوقت نفسه ، تمت معالجة بعض الطلبات بواسطة مثيلات الإصدار
1.0.0
- تم التحديث بنجاح ، ولديك العديد من مثيلات العمل لإصدار التطبيق
1.0.0
وبقية الإصدار 2.0.0.
الجميع يتواصل مع قاعدة البيانات في v2
- لا يستخدم الإصدار
1.0.0
عمود اللقب في قاعدة البيانات ، ولكن يستخدم الإصدار 2.0.0
. أنها لا تتداخل مع بعضها البعض ، ويجب ألا يكون هناك أخطاء. - الإصدار
2.0.0
بتخزين البيانات في كل من العمود القديم والجديد ، والذي يوفر التوافق مع الإصدارات السابقة
هذا مهم. إذا كان لديك أي استعلامات تقوم بحساب العناصر بناءً على قيم من العمود القديم / الجديد ، فيجب أن تتذكر الآن أن لديك قيمًا مكررة (على الأرجح لا تزال تهاجر). على سبيل المثال ، إذا كنت تريد حساب عدد المستخدمين الذين يبدأ اسمهم الأخير (بغض النظر عن اسم العمود) بالحرف A
، ثم قبل اكتمال ترحيل البيانات (العمود old
→ new
) ، قد يكون لديك بيانات غير متسقة إذا قمت بتنفيذ استعلام في عمود جديد.
تطبيق الاستعادة
الآن لدينا إصدار تطبيق 2.0.0
وقاعدة بيانات في v2
.
الخطوات التالية:
- استرجاع التطبيق الخاص بك إلى الإصدار
1.0.0
. - لا يستخدم الإصدار
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
تعليق
تقريبا. trans: على ما يبدو ، قام المؤلف بنسخ نص هذه الكتلة عن طريق الخطأ من الخطوة 2. في هذه المقالة الأصلية ، ويجب إجراء تغييرات في هذه الخطوة على رمز التطبيق بهدف إزالة الوظيفة التي تستخدم عمود last_name
.
بإضافة عمود جديد ونسخ محتوياته ، أنشأنا تغييرات قاعدة بيانات متوافقة مع الإصدارات السابقة. أيضًا ، إذا قمنا باستعادة JAR أو لدينا JAR قديم ، فلن ينكسر في وقت التشغيل.
تطبيق الاستعادة
لدينا حاليا تطبيق الإصدار 3.0.0
وقاعدة بيانات v3
. الإصدار 3.0.0
لا يحفظ البيانات في last_name
. هذا يعني أن surname
يخزن أحدث المعلومات.
الخطوات التالية:
- استرجاع التطبيق الخاص بك إلى الإصدار
2.0.0
. - الإصدار
2.0.0
يستخدم كل من last_name
surname
. - الإصدار
2.0.0
سيأخذ surname
إذا لم يكن فارغًا ، وإلا last_name
التغييرات DB
لا توجد تغييرات هيكلية في قاعدة البيانات. يتم تنفيذ البرنامج النصي التالي ، الذي ينفذ الترحيل النهائي للبيانات القديمة:
-- 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.0.0
مع مخطط قاعدة بيانات v1
(اسم العمود = اسم العائلة) - نشر تطبيق الإصدار
2.0.0,
مما يحفظ البيانات في last_name
surname
. يقرأ التطبيق من last_name
. قاعدة البيانات في الإصدار v2
، الذي يحتوي على أعمدة كل من last_name
surname. surname
surname. surname
هو نسخة من l ast_name
. (: not null) 3.0.0
, surname
surname. , last_name
surname
. NOT NULL last_name
. v3
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
Spring Boot Sample Flyway
Spring Boot Sample Flyway.
http://localhost:8080/flyway
, .
H2 ( http://localhost:8080/h2-console
), (URL jdbc — jdbc:h2:mem:testdb
).
بالإضافة إلى ذلك
اقرأ أيضًا مقالات أخرى على مدونتنا: