आज के पोस्ट में, हम रेल्स माइग्रेशन में गहराई से गोता लगाएंगे। हम माइग्रेशन को विभिन्न टुकड़ों में तोड़ेंगे, और इस प्रक्रिया में, एक प्रभावी माइग्रेशन लिखना सीखेंगे। हम कई डेटाबेस के लिए माइग्रेशन लिखना सीखेंगे, साथ ही विफल माइग्रेशन को कैसे संभालना है और रोलबैक करने की तकनीकें।
पूरे पोस्ट को समझने के लिए, आपको डेटाबेस और रेल्स की बुनियादी समझ की आवश्यकता होगी।
माइग्रेशन 101
रेल्स में माइग्रेशन हमें एप्लिकेशन के जीवनकाल में डेटाबेस को विकसित करने की अनुमति देते हैं। माइग्रेशन हमें सादे रूबी कोड को लिखकर डेटाबेस की स्थिति को बदलने की अनुमति देते हैं जो एक सुंदर DSL प्रदान करता है। हमें डेटाबेस-विशिष्ट SQL लिखने की ज़रूरत नहीं है क्योंकि माइग्रेशन डेटाबेस को मैनिपुलेट करने के लिए अब्स्ट्रैक्शन प्रदान करते हैं और पर्दे के पीछे DSL को डेटाबेस-विशिष्ट SQL क्वेरी में बदलने के विवरण का ख्याल रखते हैं। यदि ऐसी आवश्यकता उत्पन्न होती है तो माइग्रेशन हमारे रास्ते से हट जाते हैं और डेटाबेस पर कच्चे SQL को निष्पादित करने के तरीके प्रदान करते हैं।
रेल्स डेटाबेस माइग्रेशन में बीस हजार लीग
हम माइग्रेशन का उपयोग करके टेबल बना सकते हैं, कॉलम जोड़ या हटा सकते हैं, और कॉलम पर इंडेक्स जोड़ सकते हैं।
हर रेल्स ऐप में एक विशेष डायरेक्टरी होती है—
db/migrate—जहां सभी माइग्रेशन संग्रहीत होते हैं।
आइए एक माइग्रेशन के साथ शुरू करें जो हमारे डेटाबेस में events टेबल बनाता है।
$ rails g migration CreateEvents category:stringयह कमांड db/migrate डायरेक्टरी में एक टाइमस्टैम्प्ड फ़ाइल 20200405103635_create_events.rb उत्पन्न करती है। फ़ाइल की सामग्री निम्नानुसार है।
class CreateEvents < ActiveRecord::Migration[6.0] def change create_table :events do |t| t.string :category t.timestamps end end endआइए इस माइग्रेशन फ़ाइल को तोड़ें।
rails g migrationकमांड का उपयोग करके उत्पन्न हर माइग्रेशन फ़ाइल में एक टाइमस्टैम्प होता है जो फ़ाइल नाम में मौजूद होता है। यह टाइमस्टैम्प महत्वपूर्ण है और रेल्स द्वारा यह पुष्टि करने के लिए उपयोग किया जाता है कि माइग्रेशन चला है या नहीं, जैसा कि हम बाद में देखेंगे।- माइग्रेशन में एक क्लास होती है जो
ActiveRecord::Migration[6.0]से विरासत में मिली है। चूंकि मैं रेल्स 6 का उपयोग कर रहा हूं, माइग्रेशन सुपरक्लास में[6.0]है। यदि मैं रेल्स 5.2 का उपयोग कर रहा होता, तो सुपरक्लासActiveRecord::Migration[5.2]होता। बाद में, हम चर्चा करेंगे कि रेल्स संस्करण सुपरक्लास नाम का हिस्सा क्यों है। - माइग्रेशन में एक मेथड
changeहोता है जिसमें DSL कोड होता है जो डेटाबेस को मैनिपुलेट करता है। इस मामले में,changeमेथडcategoryटाइपstringके कॉलम के साथ एकeventsटेबल बना रहा है। - माइग्रेशन
eventsटेबल में टाइमस्टैम्पcreated_atऔरupdated_atजोड़ने के लिएt.timestampsकोड का उपयोग करता है।
जब इस माइग्रेशन को rails db:migrate कमांड का उपयोग करके चलाया जाता है, तो यह category टाइप string के कॉलम और टाइमस्टैम्प कॉलम created_at और updated_at के साथ एक events टेबल बनाएगा।
वास्तविक डेटाबेस कॉलम टाइप डेटाबेस के आधार पर varchar या text होगा।
माइग्रेशन टाइमस्टैम्प और schema_migrations टेबल का महत्व
हर बार rails g migration कमांड का उपयोग करके माइग्रेशन उत्पन्न होने पर, रेल्स एक अद्वितीय टाइमस्टैम्प के साथ माइग्रेशन फ़ाइल उत्पन्न करता है। टाइमस्टैम्प YYYYMMDDHHMMSS प्रारूप में है। जब भी माइग्रेशन चलाया जाता है, रेल्स माइग्रेशन फ़ाइल के टाइमस्टैम्प को एक आंतरिक टेबल schema_migrations में डालता है। यह टेबल रेल्स द्वारा बनाई जाती है जब हम अपना पहला माइग्रेशन चलाते हैं। टेबल में केवल version कॉलम होता है, जो इसका प्राथमिक कुंजी भी है। यह schema_migrations टेबल की संरचना है।
CREATE TABLE IF NOT EXISTS "schema_migrations" ("version" varchar NOT NULL PRIMARY KEY);अब जब हमने events टेबल बनाने के लिए माइग्रेशन चलाया है, तो आइए देखें कि क्या रेल्स ने इस माइग्रेशन का टाइमस्टैम्प schema_migrations टेबल में संग्रहीत किया है।
sqlite> select * from schema_migrations; 20200405103635यदि हम माइग्रेशन को फिर से चलाते हैं, तो रेल्स पहले जांचेगा कि क्या schema_migrations टेबल में माइग्रेशन फ़ाइल के टाइमस्टैम्प के साथ कोई प्रविष्टि मौजूद है, और केवल तभी निष्पादित करेगा जब ऐसी कोई प्रविष्टि न हो। यह सुनिश्चित करता है कि हम समय के साथ डेटाबेस में परिवर्तन को बढ़ा सकते हैं और एक माइग्रेशन डेटाबेस पर केवल एक बार चलेगा।
डेटाबेस स्कीमा
जैसे-जैसे हम अधिक से अधिक माइग्रेशन चलाते हैं, डेटाबेस स्कीमा विकसित होती रहती है। रेल्स सबसे हालिया डेटाबेस स्कीमा को फ़ाइल db/schema.rb में संग्रहीत करता है। यह फ़ाइल एप्लिकेशन के जीवनकाल में आपके डेटाबेस पर चलाए गए सभी माइग्रेशन का रूबी प्रतिनिधित्व है। इस फ़ाइल के कारण, हमें कोडबेस में पुरानी माइग्रेशन फ़ाइलों को रखने की आवश्यकता नहीं है। रेल्स डेटाबेस से schema.rb में नवीनतम स्कीमा को dump करने और schema.rb से डेटाबेस में स्कीमा को load करने के लिए कार्य प्रदान करता है। इसलिए पुराने माइग्रेशन को कोडबेस से सुरक्षित रूप से हटाया जा सकता है। डेटाबेस में स्कीमा लोड करना हर बार प्रत्येक माइग्रेशन चलाने की तुलना में तेज़ है जब हम एप्लिकेशन सेट करते हैं।
रेल्स डेटाबेस स्कीमा को SQL प्रारूप में संग्रहीत करने का एक तरीका भी प्रदान करता है। हमारे पास पहले से ही दोनों प्रारूपों की तुलना करने के लिए एक लेख है। आप यहां इसके बारे में अधिक पढ़ सकते हैं।
माइग्रेशन में रेल्स संस्करण
हमारे द्वारा उत्पन्न हर माइग्रेशन में सुपरक्लास के हिस्से के रूप में रेल्स संस्करण होता है। इसलिए रेल्स 6 ऐप द्वारा उत्पन्न माइग्रेशन में सुपरक्लास ActiveRecord::Migration[6.0] होता है जबकि रेल्स 5.2 ऐप द्वारा उत्पन्न माइग्रेशन में सुपरक्लास ActiveRecord::Migration[5.2] होता है। यदि आपके पास रेल्स 4.2 या उससे नीचे के साथ एक पुराना ऐप है, तो आप देखेंगे कि सुपरक्लास में कोई संस्करण नहीं है। सुपरक्लास बस ActiveRecord::Migration है।
रेल्स संस्करण को रेल्स 5 में माइग्रेशन सुपरक्लास नाम में जोड़ा गया था। यह मूल रूप से सुनिश्चित करता है कि माइग्रेशन API पुराने रेल्स संस्करणों द्वारा उत्पन्न माइग्रेशन को तोड़े बिना समय के साथ विकसित हो सकता है।
आइए रेल्स 4.2 ऐप में events टेबल बनाने के लिए उसी माइग्रेशन को देखकर इसे गहराई से देखें।
class CreateEvents < ActiveRecord::Migration def change create_table :events do |t| t.string :category t.timestamps null: false end end endयदि हम रेल्स 6 माइग्रेशन द्वारा उत्पन्न events टेबल के स्कीमा को देखते हैं, तो हम देख सकते हैं कि टाइमस्टैम्प कॉलम के लिए NOT NULL कंस्ट्रेंट मौजूद है।
sqlite> .schema events CREATE TABLE IF NOT EXISTS "events" ("id" integer PRIMARY KEY AUTOINCREMENT NOT NULL, "category" varchar, "created_at" datetime(6) NOT NULL, "updated_at" datetime(6) NOT NULL);ऐसा इसलिए है क्योंकि, रेल्स 5 से शुरू करके, माइग्रेशन API स्वचालित रूप से टाइमस्टैम्प कॉलम में NOT NULL कंस्ट्रेंट जोड़ता है बिना इसे माइग्रेशन फ़ाइल में स्पष्ट रूप से जोड़े। सुपरक्लास नाम में रेल्स संस्करण यह सुनिश्चित करता है कि माइग्रेशन उस रेल्स संस्करण के माइग्रेशन API का उपयोग करता है जिसके लिए माइग्रेशन उत्पन्न किया गया था। यह रेल्स को पुराने माइग्रेशन के साथ पिछड़ी संगतता बनाए रखने की अनुमति देता है, साथ ही माइग्रेशन API को विकसित करता है।
डेटाबेस स्कीमा बदलना
change मेथड माइग्रेशन में प्राथमिक मेथड है। जब माइग्रेशन चलाया जाता है, तो यह change मेथड को कॉल करता है और इसके अंदर का कोड निष्पादित करता है।
create_table के साथ, रेल्स एक और शक्तिशाली मेथड प्रदान करता है—change_table। जैसा कि नाम से पता चलता है, इसका उपयोग मौजूदा टेबल के स्कीमा को बदलने के लिए किया जाता है।
def change change_table :events do |t| t.remove :category t.string :event_type t.boolean :active, default: false end endयह माइग्रेशन events टेबल से category कॉलम को हटा देगा, एक नया स्ट्रिंग कॉलम events_type और एक नया बूलियन कॉलम active डिफ़ॉल्ट मान false के साथ जोड़ देगा।
रेल्स कई अन्य हेल्पर मेथड्स भी प्रदान करता है जिनका उपयोग माइग्रेशन के अंदर किया जा सकता है जैसे:
change_columnadd_indexremove_indexrename_table
और भी बहुत कुछ। चेंज के साथ उपयोग किए जा सकने वाले सभी मेथड्स यहां पाए जा सकते हैं
टाइमस्टैम्प
हमने देखा कि t.timestamps को रेल्स द्वारा माइग्रेशन में जोड़ा गया था और इसने events टेबल में created_at और updated_at कॉलम जोड़े। ये विशेष कॉलम रेल्स द्वारा उपयोग किए जाते हैं ताकि यह ट्रैक किया जा सके कि रिकॉर्ड कब बनाया गया और अपडेट किया गया। रेल्स इन कॉलम में मान जोड़ता है जब एक रिकॉर्ड बनाया जाता है और सुनिश्चित करता है कि जब रिकॉर्ड अपडेट होता है तो उन्हें अपडेट किया जाए। ये कॉलम डेटाबेस रिकॉर्ड के जीवनकाल को ट्रैक करने में हमारी मदद करते हैं।
updated_atकॉलम अपडेट नहीं होता जब हम रेल्स सेupdated_allमेथड निष्पादित करते हैं।
विफलताओं को संभालना
माइग्रेशन बुलेटप्रूफ नहीं हैं। वे विफल हो सकते हैं। कारण गलत सिंटैक्स या अमान्य डेटाबेस क्वेरी हो सकती है। चाहे कारण कुछ भी हो, हमें विफलता को संभालना होगा और उससे उबरना होगा ताकि डेटाबेस असंगत स्थिति में न जाए। रेल्स इस समस्या को प्रत्येक माइग्रेशन को एक ट्रांजैक्शन के अंदर चलाकर हल करता है। यदि माइग्रेशन विफल होता है, तो ट्रांजैक्शन रोल बैक हो जाता है। यह सुनिश्चित करता है कि डेटाबेस असंगत स्थिति में न जाए।
यह केवल उन डेटाबेस के लिए किया जाता है जो डेटाबेस स्कीमा को अपडेट करने के लिए ट्रांजैक्शन का समर्थन करते हैं। उन्हें डेटा डेफिनिशन लैंग्वेज (DDL) ट्रांजैक्शन के रूप में जाना जाता है। MySQL और PostgreSQL दोनों DDL ट्रांजैक्शन का समर्थन करते हैं।
कभी-कभी, हम कुछ माइग्रेशन को ट्रांजैक्शन के अंदर निष्पादित नहीं करना चाहते हैं। एक सरल उदाहरण है जब PostgreSQL में एक समवर्ती इंडेक्स जोड़ा जा रहा है। ऐसे माइग्रेशन को DDL ट्रांजैक्शन के अंदर निष्पादित नहीं किया जा सकता क्योंकि PostgreSQL टेबल पर लॉक प्राप्त किए बिना इंडेक्स जोड़ने का प्रयास करता है ताकि हम लाइव प्रोडक्शन डेटाबेस पर डेटाबेस को डाउन लिए बिना इंडेक्स जोड़ सकें। रेल्स माइग्रेशन के अंदर disable_ddl_transactions! के रूप में ट्रांजैक्शन से ऑप्ट-आउट करने का एक तरीका प्रदान करता है।
def change disable_ddl_transactions! add_index :events, :user_id, algorithm: :concurrentlyयह माइग्रेशन को ट्रांजैक्शन के अंदर नहीं चलाएगा। यदि ऐसा माइग्रेशन विफल होता है, तो हमें इसे स्वयं पुनर्प्राप्त करना होगा। इस मामले में, हम या तो REINDEX कर सकते हैं या इंडेक्स को हटा सकते हैं और इसे फिर से जोड़ने का प्रयास कर सकते हैं।
प्रतिवर्ती माइग्रेशन
रेल्स हमें निम्न कमांड के साथ डेटाबेस में परिवर्तनों को रोलबैक करने की अनुमति देता है।
rails db:rollbackयह कमांड डेटाबेस पर चलाए गए अंतिम माइग्रेशन को वापस ले लेता है। यदि माइग्रेशन ने event_type कॉलम जोड़ा था तो रोलबैक उस कॉलम को हटा देगा। यदि माइग्रेशन ने इंडेक्स जोड़ा था, तो रोलबैक उस इंडेक्स को हटा देगा।
पिछले माइग्रेशन को रोल बैक करने और इसे चलाने के लिए एक कमांड भी है। यह
rails db:redoहै।
रेल्स अधिकांश माइग्रेशन को कैसे वापस करना है, यह जानने के लिए पर्याप्त स्मार्ट है। लेकिन हम change मेथड का उपयोग करने के बजाय up और down मेथड प्रदान करके रेल्स को संकेत भी दे सकते हैं कि माइग्रेशन को कैसे वापस करना है। up मेथड का उपयोग तब किया जाएगा जब माइग्रेशन चलाया जाता है जबकि down मेथड का उपयोग तब किया जाएगा जब माइग्रेशन रोल बैक किया जाता है।
def up change_table :events do |t| t.change :price, :string end end def down change_table :events do |t| t.change :price, :integer end endइस उदाहरण में, हम events के price कॉलम को integer से string में बदल रहे हैं। हम निर्दिष्ट करते हैं कि इसे down मेथड में कैसे वापस किया जाना चाहिए।
यही माइग्रेशन change मेथड का उपयोग करके भी लिखा जा सकता है।
def change reversible do |direction| change_table :events do |t| direction.up { t.change :price, :string } direction.down { t.change :price, :integer } end end endरेल्स revert मेथड का उपयोग करके पिछले माइग्रेशन को पूरी तरह से वापस करने का एक तरीका भी प्रदान करता है।
def change revert CreateEvents create_table :events do ... end endrevert मेथड एक ब्लॉक को भी स्वीकार करता है ताकि माइग्रेशन को आंशिक रूप से वापस किया जा सके।
def change revert do reversible do |direction| change_table :events do |t| direction.up { t.remove :event_type } direction.down { t.string :event_type } end end end endइसे कच्चे में निष्पादित करना
कभी-कभी, हम माइग्रेशन के अंदर जटिल SQL निष्पादित करना चाहते हैं। ऐसे मामलों में, हम सामान्य माइग्रेशन DSL को भूल सकते हैं और इसके बजाय कच्चे SQL को निम्नानुसार निष्पादित कर सकते हैं।
def change execute <<-SQL .... SQL endएकाधिक डेटाबेस और माइग्रेशन
रेल्स 6 ने एक ही रेल्स एप्लिकेशन के भीतर कई डेटाबेस का उपयोग करने के लिए समर्थन जोड़ा। यदि हम कई डेटाबेस का उपयोग करना चाहते हैं, तो हम उन्हें database.yml फ़ाइल में कॉन्फ़िगर करते हैं।
development: primary: <<: *default database: db/development.sqlite3 analytics: adapter: sqlite3 database: db/analytics_dev.sqlite3यह कॉन्फ़िगरेशन रेल्स को बताता है कि हम दो डेटाबेस—primary और analytics का उपयोग करना चाहते हैं। जैसा कि हमने पहले देखा, माइग्रेशन डिफ़ॉल्ट रूप से db/migrate डायरेक्टरी में संग्रहीत होते हैं। लेकिन इस मामले में, हम दोनों डेटाबेस के माइग्रेशन को एक ही डायरेक्टरी में नहीं जोड़ सकते। हम analytics डेटाबेस के माइग्रेशन को primary डेटाबेस पर और इसके विपरीत नहीं चलाना चाहते। यदि हम कई डेटाबेस का उपयोग कर रहे हैं, तो हमें दूसरे डेटाबेस के लिए माइग्रेशन संग्रहीत करने के लिए एक पथ प्रदान करना आवश्यक है। यह database.yml में migrations_paths प्रदान करके किया जा सकता है।
development: primary: <<: *default database: db/development.sqlite3 analytics: adapter: sqlite3 database: db/analytics_dev.sqlite3 migrations_paths: db/analytics_migrateहम तब analytics डेटाबेस के लिए माइग्रेशन बना सकते हैं जैसे:
rails generate migration AddExperiments rule:string active:boolean --db=analyticsयह db/analytics_migrate के अंदर माइग्रेशन बनाएगा, और हम इसे निम्नानुसार चला सकते हैं।
rails db:migrate --db=analyticsयदि हम केवल rails db:migrate चलाते हैं, तो यह सभी डेटाबेस के लिए माइग्रेशन निष्पादित करेगा।
analyticsडेटाबेस का अपनाschema_migrationsटेबल होगा ताकि यह ट्रैक किया जा सके कि कौन से माइग्रेशन चलाए गए हैं और कौन से नहीं।
डिप्लॉयमेंट के दौरान माइग्रेशन चलाना
चूंकि माइग्रेशन डेटाबेस की स्थिति को बदल सकते हैं, और हमारा कोड उन परिवर्तनों पर निर्भर हो सकता है, यह अत्यंत महत्वपूर्ण है कि माइग्रेशन नए कोड लागू होने से पहले पहले चलाए जाएं।
Heroku-आधारित डिप्लॉयमेंट में, माइग्रेशन को Procfile के release चरण में चलाया जा सकता है।
# Profile web: bin/puma -C config/puma.rb release: bundle exec rake db:migrate यह सुनिश्चित करता है कि ऐप डायनो के पुनरारंभ होने से पहले माइग्रेशन चलाए जाते हैं।
Capistrano-आधारित डिप्लॉयमेंट में, सर्वर के पुनरारंभ होने से पहले माइग्रेशन चलने चाहिए।
Docker-आधारित डिप्लॉयमेंट में, हम माइग्रेशन को पहले चलाने के लिए एक साइडकार कंटेनर चला सकते हैं इससे पहले कि ऐप पुनरारंभ हो। यह बहुत महत्वपूर्ण है क्योंकि अन्यथा, नए कंटेनर असंगत स्थिति में जा सकते हैं यदि वे नए कोड का उपयोग करना शुरू कर देते हैं इससे पहले कि उस नए कोड के लिए डेटाबेस परिवर्तन लागू किए जाएं।
निष्कर्ष
इस पोस्ट में, हमने रेल्स में डेटाबेस माइग्रेशन लिखने के विभिन्न पहलुओं को देखा। हमने यह भी देखा कि माइग्रेशन का गठन क्या होता है साथ ही विफलताओं को कैसे संभालना है और यदि आवश्यक हो तो माइग्रेशन को कैसे रोल बैक करना है। रेल्स 6 हमें कई डेटाबेस का उपयोग करने की अनुमति देता है और प्रत्येक के लिए माइग्रेशन को अलग से जोड़ा जाना चाहिए। अंत में, हमने संक्षेप में देखा कि डिप्लॉयमेंट के दौरान माइग्रेशन कैसे चलाएं ताकि डेटाबेस परिवर्तन किसी भी नए कोड द्वारा उनका उपयोग शुरू करने से पहले ठीक से लागू किए जाएं।
P.S. यदि आप रूबी मैजिक पोस्ट को प्रेस से निकलते ही पढ़ना चाहते हैं, तो हमारे रूबी मैजिक न्यूज़लेटर की सदस्यता लें और कभी भी एक भी पोस्ट मिस न करें!