১১ ডিসে, ২০২৫·7 মিনিট পড়তে

ডাউনটাইম ছাড়া স্কিমা পরিবর্তন: নিরাপদ অ্যাডিটিভ মাইগ্রেশন

অ্যাডিটিভ মাইগ্রেশন, নিরাপদ ব্যাকফিল এবং ধাপে রোলআউট নিয়ে শিখুন—যাতে রিলিজের সময় পুরনো ক্লায়েন্টরা কাজ চালিয়ে যায় এবং ডাউনটাইম এড়ায়।

ডাউনটাইম ছাড়া স্কিমা পরিবর্তন: নিরাপদ অ্যাডিটিভ মাইগ্রেশন

স্কিমা পরিবর্তনে আসলে "শূন্য-ডাউনটাইম" মানে কী

শূন্য-ডাউনটাইম স্কিমা পরিবর্তন মানে কিছুই বদলায় না তা নয়। এর মানে ব্যবহারকারীরা ডাটাবেস এবং অ্যাপ আপডেটের সময়ও কাজ চালিয়ে যেতে পারে—ত্রুটি বা ব্লক হওয়া ওয়ার্কফ্লো ছাড়াই।

ডাউনটাইম বলতে বোঝায় কোনো মুহূর্ত যখন আপনার সিস্টেম স্বাভাবিকভাবে আচরণ করা বন্ধ করে। সেটা 500 এরর, API টাইমআউট, লোড হওয়া স্ক্রিনে ফাঁকা বা ভুল ভ্যালু দেখানো, ব্যাকগ্রাউন্ড জব ক্র্যাশ করা, কিংবা লম্বা মাইগ্রেশনের কারণে ডাটাবেস পড়তে গ্রহণ করে কিন্তু লেখার উপর লক দেয়া—সবই হতে পারে।

একটা স্কিমা পরিবর্তন শুধু প্রধান অ্যাপ UI ভাঙে না। সাধারণ ব্যর্থতা হয় API ক্লায়েন্ট যারা পুরনো রেসপন্স কাঠামো আশা করে, ব্যাকগ্রাউন্ড জব যারা নির্দিষ্ট কলাম পড়ে/লিখে, রিপোর্ট যারা টেবিল সরাসরি কুয়েরি করে, থার্ড পার্টি ইন্টিগ্রেশন, এবং অভ্যন্তরীণ অ্যাডমিন স্ক্রিপ্টগুলো যা “গতকাল পর্যন্ত ঠিক কাজ করছিল।”

পুরনো মোবাইল অ্যাপ এবং ক্যাশ করা ক্লায়েন্টগুলো একটা সাধারণ সমস্যা কারণ সেগুলো দ্রুত আপডেট করা যায় না। কিছু ব্যবহারকারী অ্যাপের একটি ভার্সন কয়েক সপ্তাহ ধরে রাখে। কেউ কেউ অনিয়মিত কানেক্টিভিটির কারণে পরে পুরনো অনুরোধ রিট্রাই করে। এমনকি ওয়েব ক্লায়েন্টগুলোও সার্ভিস ওয়ার্কার, CDN বা প্রক্সি ক্যাশের কারণে “পুরোনো ভার্সনের মতো” আচরণ করতে পারে।

বাস্তব লক্ষ্যটি হচ্ছে “একবারের বড় মাইগ্রেশন যা দ্রুত শেষ হয়” নয়। এটা ছোট ছোট ধাপের এক ক্রম যাতে প্রতিটি ধাপ নিজেই কাজ করে, এমনকি ক্লায়েন্টদের ভিন্ন ভিন্ন ভার্সনে থাকলে-ও।

একটি ব্যবহারিক সংজ্ঞা: আপনাকে যে কোনো ক্রমে নতুন কোড এবং নতুন স্কিমা ডিপ্লয় করতে সক্ষম হতে হবে এবং সিস্টেম তবুও কাজ করবে।

এই মানসিকতা আপনাকে ক্লাসিক ফাঁদ থেকে বাঁচায়: নতুন অ্যাপ এমন আশা করলে যে নতুন কলাম আছে যদিও কলামটি এখনও তৈরি হয়নি, অথবা নতুন কলাম দিয়ে দিলে পুরোনো কোড হ্যান্ডেল করতে পারে না। পরিবর্তনগুলো প্রথমে অ্যাডিটিভ হিসেবে পরিকল্পনা করুন, ধাপে চালু করুন, এবং পুরনো পথ কেবল তখনই সরান যখন নিশ্চিত হবেন কেউ আর তা ব্যবহার করছে না।

বিদ্যমান কোড ভাঙবে না এমনভাবে অ্যাডিটিভ পরিবর্তন দিয়ে শুরু করুন

শূন্য-ডাউনটাইম স্কিমা পরিবর্তনের সবচেয়ে নিরাপদ পথ হলো যোগ করা, বদলে ফেলা নয়। নতুন কলাম বা নতুন টেবিল যোগ করা খুব কমই কিছু ভাঙে কারণ বিদ্যমান কোড পুরনো আকারেই পড়া ও লেখা চালিয়ে যেতে পারে।

রিনেম ও ডিলেট করা ঝুঁকিপূর্ণ। এক রিনেম প্রকৃতপক্ষে “নতুন যোগ + পুরানো সরানো” এবং ‘পুরানো সরানো’ অংশেই পুরনো ক্লায়েন্ট ক্র্যাশ করে। যদি রিনেমের প্রয়োজন হয়, তা দুই ধাপে বিবেচনা করুন: আগে নতুন ফিল্ড যোগ করুন, পুরানো ফিল্ড কিছু সময় রাখুন, এবং নিশ্চিত না হওয়া পর্যন্ত সেটা মুছবেন না।

কলাম যোগ করার সময় শুরুতেই nullable রাখুন। nullable কলাম পুরনো কোডকে নতুন কলামের ব্যাপারে না জেনেই রো ইনসার্ট করতে দেয়। আপনি যদি শেষ পর্যন্ত NOT NULL চান, প্রথমে nullable হিসেবে যোগ করুন, ব্যাকফিল করুন, তারপর পরে NOT NULL জোরদার করুন। ডিফল্ট সহায়ক হতে পারে, তবে সাবধান: কিছু ডাটাবেসে ডিফল্ট যোগ করলেও অনেক রো ছুঁতে হতে পারে যা পরিবর্তন ধীর করে দিতে পারে।

ইন্ডেক্স আরেকটি “নিরাপদ কিন্তু ফ্রি নয়” যোগ। ইন্ডেক্স পড়া দ্রুত করতে পারে, কিন্তু ইন্ডেক্স তৈরি ও বজায় রাখা লেখাকে ধীর করতে পারে। ঠিক কোন কুয়েরি এটি ব্যবহার করবে সেটা জানলে ইন্ডেক্স যোগ করুন, আর যদি ডাটাবেস ব্যস্ত হয় তো নিরিবিলি সময়ে রোলআউট বিবেচনা করুন।

অ্যাডিটিভ ডাটাবেস মাইগ্রেশনের সহজ নিয়মগুলো:

  • প্রথমে নতুন টেবিল বা কলাম যোগ করুন, পুরনোগুলো অচ্ছাদিত রেখে দিন।
  • নতুন ফিল্ডগুলো ডেটা ভর্তি হওয়া পর্যন্ত অপশনাল (nullable) রাখুন।
  • ক্লায়েন্ট আপডেট না হওয়া পর্যন্ত পুরনো কুয়েরি ও পেআলোড কাজ করার মতো রাখুন।
  • ব্যাকফিলের পরেই কনস্ট্রেইন্ট (NOT NULL, unique, foreign keys) প্রয়োগ করুন।

ধাপে ধাপে রোলআউট পরিকল্পনা যা পুরনো ক্লায়েন্টকে সচল রাখে

শূন্য-ডাউনটাইম স্কিমা পরিবর্তনকে একটি রোলআউট হিসেবে দেখুন, একক ডিপ্লয় নয়। লক্ষ্য হলো পুরনো ও নতুন অ্যাপ ভার্সন পাশাপাশি চলতে পারবে যতক্ষণ ডাটাবেস ধীরে ধীরে নতুন আকারে যায়।

একটি ব্যবহারিক ক্রম:

  1. সামঞ্জস্যপূর্ণভাবে নতুন স্কিমা যোগ করুন। নতুন কলাম বা টেবিল তৈরি করুন, নাল অনুমোদন করুন, এবং কঠোর কনস্ট্রেইন্ট এড়ান যা পুরনো কোড পূরণ করতে পারে না। ইন্ডেক্স দরকার হলে এমনভাবে যোগ করুন যা রাইট ব্লক না করে।
  2. ব্যাকএন্ডে এমন পরিবর্তন ডিপ্লয় করুন যা দুই ‘ভাষা’ বুঝতে পারে। API আপডেট করুন যাতে এটি পুরনো এবং নতুন অনুরোধ দুইটাই গ্রহণ করে। নতুন ফিল্ড লিখতে শুরু করুন এবং পুরোনো ফিল্ড সঠিক রাখুন। এই “ডুয়াল রাইট” ফেজই মিশ্র ক্লায়েন্ট সংস্করণকে নিরাপদ করে।
  3. বিদ্যমান ডেটা ছোট ব্যাচে ব্যাকফিল করুন। পুরনো রো ক্রমান্বয়ে নতুন কলামে পূরণ করুন। ব্যাচ সাইজ সীমাবদ্ধ রাখুন, দরকারে দেরি যোগ করুন, এবং লোড বাড়লে থামানোর জন্য অগ্রগতি ট্র্যাক করুন।
  4. কভারেজ উচ্চ হলে পড়া (reads) সুইচ করুন। বেশিরভাগ রো ব্যাকফিল হয়ে গেলে ও আপনি আত্মবিশ্বাস পেলে ব্যাকএন্ডকে নতুন ফিল্ডকে প্রাধান্য দিতে বলুন। কিছু সময় পুরোনো ফিল্ডেfallback রাখুন।
  5. শেষে পুরনো ফিল্ডটি সরান, এবং কেবল তখনই যখন এটি সত্যিই অপ্রচলিত। পুরনো মোবাইল বিল্ডগুলো বেশিরভাগ বিলোপ না হওয়া পর্যন্ত অপেক্ষা করুন, লগে পুরনো ফিল্ডে কোন রিড নেই তা নিশ্চিত করুন, এবং একটি পরিষ্কার রোলব্যাক প্ল্যান রাখুন। তারপর পুরনো কলাম ও সংশ্লিষ্ট কোড সরান।

উদাহরণ: আপনি full_name যোগ করেছেন কিন্তু পুরোনো ক্লায়েন্ট এখনও first_name এবং last_name পাঠায়। কিছু সময়ের জন্য ব্যাকএন্ড লিখার সময় first_name/last_name থেকে full_name তৈরি করতে পারে, পুরনো ইউজার ব্যাকফিল করতে পারে, পরে ডিফল্টভাবে full_name পড়া হলে পুরনো পেআলোড সমর্থন চালানো যায়। গ্রহণটা স্পষ্ট হলে পুরনো ফিল্ডগুলো ড্রপ করা যায়।

ব্যাকফিল অবাক করা ছাড়াই: নতুন ডেটা নিরাপদে ভরাট করা

ব্যাকফিলটি একটি নতুন কলাম বা টেবিল পুরোনো রো পূরণ করে। এটি শূন্য-ডাউনটাইম স্কিমা পরিবর্তনের সবচেয়ে ঝুঁকিপূর্ণ অংশ কারণ এটি ভারী ডাটাবেস লোড, দীর্ঘ লক এবং বিভ্রান্তকর “আধ-মাইগ্রেটেড” আচরণ তৈরি করতে পারে।

শুরুতেই নির্ধারণ করুন কিভাবে ব্যাকফিল চালাবেন। ছোট ডেটাসেটের জন্য একবারের ম্যানুয়াল রুনবুক ঠিক আছে। বড় ডেটাসেটের জন্য ব্যাকগ্রাউন্ড ওয়ার্কার বা নির্ধারিত টাস্ক ব্যবহার করুন যা বারবার চালাতে ও নিরাপদে থামাতে পারে।

কাজটি ব্যাচে ভাগ করুন যাতে ডাটাবেসের উপর চাপ নিয়ন্ত্রণে থাকে। মিলিয়ন রো এক ট্রানজ্যাকশনে আপডেট করবেন না। পূর্বানুমানযোগ্য চাঙ্ক সাইজ এবং ব্যাচগুলোর মধ্যে সংক্ষিপ্ত বিরতি রাখুন যাতে স্বাভাবিক ট্র্যাফিক মসৃণ থাকে।

একটি ব্যবহারিক প্যাটার্ন:

  • একটি ছোট ব্যাচ (উদাহরণস্বরূপ পরবর্তী 1,000 রো) নির্বাচ করুন, একটি ইনডেক্স করা কী ব্যবহার করে।
  • শুধুমাত্র মিসিংগুলো আপডেট করুন (আগে থেকেই ব্যাকফিল করা রো পুনরায় লিখবেন না)।
  • দ্রুত কমিট করুন, তারপর স্বল্প বিরতি নিন।
  • অগ্রগতি রেকর্ড করুন (শেষ প্রক্রিয়াজাত ID বা টাইমস্ট্যাম্প)।
  • ব্যর্থ হলে পুনরায় চেষ্টা করুন এমনভাবে যাতে পুরো কাজ শুরু থেকে না গড়াতে হয়।

জবটি রিস্টার্টেবল রাখুন। একটি সরল অগ্রগতি মার্কার ডেডিকেটেড টেবিলে রাখুন, এবং জবটি এমনভাবে ডিজাইন করুন যাতে পুনরায় চালালেও ডেটা নষ্ট না হয়। আইডেমপটেন্ট আপডেট (যেমন UPDATE WHERE new_field IS NULL) আপনার সহায়ক।

চলাকালীন বৈধতা রাখুন। কতগুলো রো এখনও নতুন ভ্যালু নেই তা ট্র্যাক করুন, এবং কয়েকটি স্যানিটি চেক যোগ করুন। উদাহরণ: নেতিবাচক ব্যালেন্স নেই, টাইমস্ট্যাম্প প্রত্যাশিত রেঞ্জে, স্ট্যাটাস অনুমোদিত সেটের মধ্যে। নমুনা নিয়ে বাস্তব রেকর্ড স্পট-চেক করুন।

ব্যাকফিল অসম্পূর্ণ থাকাকালীন অ্যাপটি কী করবে তা নির্ধারণ করুন। নিরাপদ অপশন হচ্ছে fallback reads: যদি নতুন ফিল্ড null হয়, পুরনো ভ্যালু হিসাব বা পড়ে দেখানো। উদাহরণ: আপনি preferred_language যোগ করলে, ব্যাকফিল শেষ না হওয়া পর্যন্ত API প্রোফাইল সেটিং থেকে বিদ্যমান ভাষা ফিরিয়ে দিতে পারে এবং সম্পূর্ণ হওয়ার পরে কেবল নতুন ফিল্ড আবশ্যক করা যাবে।

মিশ্র ক্লায়েন্ট ভার্সনের জন্য API সামঞ্জস্য নীতিসমূহ

কম ফ্রিকশনে ব্যাকফিল ট্র্যাক করুন
এক জায়গায় ব্যাকফিল, অগ্রগতি এবং যাচাইকরণ চেক মনিটর করার অভ্যন্তরীণ টুল তৈরি করুন।
অ্যাডমিন তৈরি করুন

আপনি যখন স্কিমা পরিবর্তন শিপ করেন, সাধারণত সব ক্লায়েন্ট নিয়ন্ত্রণে রাখেন না। ওয়েব ব্যবহারকারী দ্রুত আপডেট করে, যেখানে পুরনো মোবাইল বিল্ডগুলো সপ্তাহ ধরে থাকতে পারে। এজন্য পেছনে-সামঞ্জস্যপূর্ণ API গুরুত্বপূর্ণ, এমনকি আপনার ডাটাবেস মাইগ্রেশন "নিরাপদ" হলেও।

প্রথমে নতুন ডেটাকে অপশনাল হিসেবে বিবেচনা করুন। অনুরোধ ও রেসপন্সে নতুন ফিল্ড যোগ করুন, কিন্তু প্রথম দিন থেকেই সেগুলো বাধ্যতামূলক করবেন না। যদি পুরনো ক্লায়েন্ট নতুন ফিল্ড না পাঠায়, সার্ভার অনুরোধ গ্রহণ করে এবং আগের মতো আচরণ করবে।

বিদ্যমান ফিল্ডের মান বদলাবেন না। ফিল্ড রিনেম করা ঠিক থাকে যদি পুরনো নামটিও কাজ করে। একটি ফিল্ডকে নতুন অর্থে পুনঃব্যবহার করলে সূক্ষ্ম ব্রেকেজ হয়।

সার্ভার-সাইড ডিফল্ট আপনার নিরাপত্তা জাল। যখন preferred_language মত নতুন কলাম আনা হয়, সার্ভারে মিসিং হলে একটি ডিফল্ট সেট করুন। API রেসপন্স নতুন ফিল্ড দেখাতে পারে, এবং পুরনো ক্লায়েন্ট তা উপেক্ষা করতে পারবে।

কম্প্যাটিবিলিটি নিয়ম যা বেশিরভাগ আউটেজ প্রতিরোধ করে:

  • প্রথমে নতুন ফিল্ডগুলো অপশনাল হিসেবে যোগ করুন, পরে গ্রহণ বাড়লে বাধ্যতামূলক করুন।
  • পুরনো আচরণ স্থিতিশীল রাখুন, এমনকি আপনি بہتر আচরণ ফ্ল্যাগের পেছনে রাখেন।
  • সার্ভারে ডিফল্ট প্রয়োগ করুন যাতে পুরনো ক্লায়েন্ট নতুন ফিল্ড না পাঠালেও ঠিক থাকে।
  • মিশ্র ট্রাফিক ধরে ধরে দুটো পথকে পরীক্ষা করুন: “নতুন ক্লায়েন্ট পাঠায়” এবং “পুরনো ক্লায়েন্ট বাদ দেয়।”
  • এরর মেসেজ ও কোড স্থিতিশীল রাখুন যাতে মনিটরিং হঠাৎ করে শব্দ করে না।

উদাহরণ: আপনি সাইনআপ ফ্লোতে company_size যোগ করলে, ব্যাকএন্ড ফিল্ড অনুপস্থিত হলে ডিফল্ট হিসেবে “unknown” সেট করতে পারে। নতুন ক্লায়েন্ট সঠিক মান পাঠায়, পুরনো ক্লায়েন্ট কাজ চালায়, এবং ড্যাশবোর্ড পড়ার যোগ্য থাকে।

যখন আপনার অ্যাপ পুনরায় জেনারেট করে: স্কিমা ও লজিক সিংক রাখা

যদি আপনার প্ল্যাটফর্ম অ্যাপ্লিকেশন পুনরায় জেনারেট করে, আপনি কোড ও কনফিগারেশনের পরিষ্কার রිබিল্ড পাচ্ছেন। এটা শূন্য-ডাউনটাইম স্কিমা পরিবর্তনের ক্ষেত্রে সহায়ক কারণ আপনি ছোট, অ্যাডিটিভ ধাপ করে বারবার রিডিপ্লয় করতে পারবেন, মাসের পর মাস প্যাচ বহন না করে।

কীটুকু গুরুত্বপূর্ণ: একমাত্র সূত্রের সত্যতা থাকা। যদি ডাটাবেস স্কিমা এক জায়গায় পরিবর্তিত হয় এবং ব্যবসায়িক লজিক অন্য জায়গায়, ড্রিফট দ্রুত ঘটে। নির্ধারণ করুন কোথায় পরিবর্তন সংজ্ঞায়িত হবে, এবং বাকিগুলো জেনারেটেড আউটপুট হিসেবে দেখুন।

পরিষ্কার নামকরণ ফেজড রোলআউটে দুর্ঘটনা কমায়। যদি আপনি একটি নতুন ফিল্ড নিয়ে আসেন, স্পষ্ট করে দিন কোনটি পুরনো ক্লায়েন্টের জন্য নিরাপদ এবং কোনটি নতুন পথ। উদাহরণ: একটি নতুন কলাম status_v2 নামকরণ status_new এর থেকে নিরাপদ কারণ ছয় মাস পরেও সেটির অর্থ বোঝা যাবে।

প্রতিটি পুনরায় জেনারেশনের পরে কী পুনরায় টেস্ট করবেন

অ্যাডিটিভ হলেও, একটি রিবিল্ড লুকানো কাপলিং খুঁজে বের করতে পারে। প্রতিটি পুনরায় জেনারেশন ও ডিপ্লয়ের পরে একটি ছোট সেট গুরুত্বপূর্ণ ফ্লো চেক করুন:

  • সাইন আপ, লগইন, পাসওয়ার্ড রিসেট, টোকেন রিফ্রেশ।
  • মূল তৈরি ও আপডেট অ্যাকশন (যেগুলো বেশি ব্যবহৃত)।
  • অ্যাডমিন ও পারমিশন চেক।
  • পেমেন্টস ও ওয়েবহুক (উদাহরণ: Stripe ইভেন্ট)।
  • নোটিফিকেশন ও মেসেজিং (ইমেইল/SMS, Telegram)।

মাইগ্রেশন ধাপগুলো নির্ধারণ করে নিন редак্টর খুলার আগে: নতুন ফিল্ড যোগ করুন, দুই ফিল্ড সমর্থন করে ডিপ্লয় করুন, ব্যাকফিল করুন, রিড সুইচ করুন, তারপর পরে পুরনো পথ অবসর করুন। এই ক্রম স্কিমা, লজিক, ও জেনারেটেড কোডকে একসঙ্গে চালিত রাখে যাতে পরিবর্তন ছোট, রিভিউযোগ্য ও রিভার্সযোগ্য থাকে।

আউটেজ ঘটানোর সাধারণ ভুলগুলো (এবং তা এড়ানোর উপায়)

নিঃ-ডাউনটাইম রুটিন করুন
প্রথমে নতুন কলাম যোগ করুন, পরে রিড বদলান, এবং ক্লিনআপ আলাদা পরিকল্পিত ধাপে রাখুন।
AppMaster অন্বেষণ করুন

শূন্য-ডাউনটাইম স্কিমা পরিবর্তন চলাকালীন বেশিরভাগ আউটেজ “হার্ড” ডাটাবেস কাজের কারণে নয়। এগুলো আসে যখন ডাটাবেস, API, ও ক্লায়েন্টের চুক্তি ভুল ক্রমে বদলে ফেলা হয়।

সাধারণ ফাঁদ ও নিরাপদ পদক্ষেপ:

  • কোনো কলাম রিনেম করা যখন পুরনো কোড এখনও তা পড়ে। পুরনো কলাম রাখুন, নতুনটি যোগ করুন, এবং কিছু সময় দুটোকে ম্যাপ করুন (উভয়েই লিখুন বা ভিউ ব্যবহার করুন)। রিনেম কেবল তখনই করুন যখন প্রমাণ থাকে কেউ পুরনো নামের উপর নির্ভর করছে না।
  • nullable ফিল্ডকে সময়ের আগেই required করা। কলামটিকে প্রথমে nullable হিসেবে যোগ করুন, কোড এমনভাবে পাঠান যাতে সব জায়গায় লেখা হয়, পুরোনো রো ব্যাকফিল করুন, তারপর চূড়ান্ত মাইগ্রেশনে NOT NULL প্রয়োগ করুন।
  • একটি বৃহৎ ট্রানজ্যাকশনে মন্থর ব্যাকফিল করা যা টেবিল লক করে। ছোট ব্যাচে ব্যাকফিল করুন, সীমা ও বিরতি সহ। অগ্রগতি ট্র্যাক করুন যাতে নিরাপদে পুনরায় শুরু করা যায়।
  • রাইটগুলো নয় করে আগে রিড সুইচ করা। আগে রাইট সুইচ করুন, তারপর ব্যাকফিল, তারপর রিড সুইচ করুন। যদি রিড আগে বদলে ফেলা হয় তাহলে খালি স্ক্রিন, ভুল টোটাল বা “মিসিং ফিল্ড” এরর হবে।
  • পুরনো ফিল্ড বাদ দিয়ে দেওয়া যখন প্রমাণ নেই যে পুরনো ক্লায়েন্ট অনুপস্থিত। পুরনো ফিল্ডটি যতটা মনে করেন তার বেশি সময় রাখুন। মেট্রিকস দেখিয়ে যখন পুরনো ভার্সন কার্যত নিষ্ক্রিয় হয়ে যায় এবং ডিপ্রেকশন উইন্ডো কমিউনিকেট করা হয়েছে তখনই মুছুন।

যদি আপনি আপনার অ্যাপ পুনরায় জেনারেট করেন, একবারে নাম ও কনস্ট্রেইন্ট সেট পরিষ্কার করার প্রলুব্ধতা আসতে পারে—তাতে প্রতিরোধ করুন। ক্লিনআপ শেষ ধাপ, প্রথম নয়।

একটা ভাল নিয়ম: যদি কোনো পরিবর্তন সামনে-পিছ করতে নিরাপদভাবে না যায়, তা প্রোডাকশনের জন্য প্রস্তুত নয়।

ধাপে ধাপে মাইগ্রেশনের জন্য মনিটরিং ও রোলব্যাক পরিকল্পনা

দ্রুত অ্যাডিটিভ মাইগ্রেশন অনুশীলন করুন
নিরাপদ, অ্যাডিটিভ স্কিমা মডেল তৈরি করুন এবং প্রয়োজনে ব্যাকএন্ড ও API পুনরায় জেনারেট করুন।
AppMaster ব্যবহার করে দেখুন

শূন্য-ডাউনটাইম স্কিমা পরিবর্তনের সাফল্য দুটির উপর নির্ভর করে: আপনি কী দেখছেন, এবং কত দ্রুত আপনি বন্ধ করতে পারেন।

আপনি যেগুলো দেখবেন সেগুলো এমন সিগন্যাল হওয়া উচিত যা ব্যবহারকারী-প্রভাব প্রতিফলিত করে, শুধু “ডিপ্লয় শেষ” নয়:

  • API এরর রেট (বিশেষ করে আপডেট করা এন্ডপয়েন্টে 4xx/5xx স্পাইক)।
  • ধীর কুয়েরি (আপনি স্পর্শ করা টেবিলগুলোর p95 বা p99 কুয়েরি টাইম)।
  • রাইট লেটেন্সি (পীক ট্র্যাফিকে ইনসার্ট ও আপডেট কত সময় নিচ্ছে)।
  • কিউ ডেপথ (ব্যাকফিল বা ইভেন্ট প্রসেসিংয়ের জন্য জব জমে আছে কি না)।
  • ডাটাবেস CPU/IO চাপ (পরিবর্তনের পরে কোনো হঠাৎ কুড়ানো)।

ডুয়াল রাইট করলে (পুরনো ও নতুন কলাম উভয়েই লেখা) অস্থায়ী লগিং যোগ করুন যা দুইটির তুলনা করে। এটা সীমিত রাখুন: কেবল ভ্যালুগুলো ভিন্ন হলে লগ করুন, রেকর্ড ID ও সংক্ষিপ্ত রিজন কোড রাখুন, ভলিউম বেশি হলে স্যাম্পল করুন। মাইগ্রেশনের পরে এই লগিং অপসারণ করার রিমাইন্ডার তৈরি করুন যাতে এটি স্থায়ী আয়ত্তশব্দে পরিণত না হয়।

রোলব্যাক বাস্তবসম্মত হতে হবে। বেশিরভাগ সময়ে আপনি স্কিমা রোলব্যাক করবেন না; আপনি কোড রিভাট করবেন এবং অ্যাডিটিভ স্কিমাকে রেখে দেবেন।

একটি ব্যবহারিক রোলব্যাক রুনবুক:

  • অ্যাপ্লিকেশন লজিককে শেষ পরিচিত ভাল ভার্সনে রিভাট করুন।
  • নতুন রিড প্রথমে নিষ্ক্রিয় করুন, তারপর নতুন রাইট বন্ধ করুন।
  • নতুন টেবিল বা কলাম রেখে দিন, কিন্তু ব্যবহার বন্ধ করুন।
  • ব্যাকফিল স্থগিত করুন যতক্ষণ মেট্রিকস স্থিতিশীল না হয়।

ব্যাকফিলের জন্য একটি স্টপ সুইচ তৈরি করুন যা সেকেন্ডের মধ্যে ফ্লিপ করা যায় (ফিচার ফ্ল্যাগ, কনফিগ মান, জব পজ)। এছাড়াও পর্যায়গুলি আগে থেকে কমিউনিকেট করুন: কখন ডুয়াল রাইট শুরু হবে, কখন ব্যাকফিল চলবে, কখন রিড সুইচ হবে, এবং “স্টপ” কেমন দেখায়—তাহলে চাপের সময় কেউ হঠাৎ সিদ্ধান্ত নেবে না।

দ্রুত প্রি-ডিপ্লয় চেকলিস্ট

স্কিমা পরিবর্তন শিপ করার ঠিক আগেই একটা বিরতি নিয়ে এই ত্বরিত পরীক্ষা চালান। এটি মিশ্র ক্লায়েন্ট ভার্সনের সঙ্গে ছোট অনুমানগুলো কে ধরা দেয় যা আউটেজে পরিণত হয়।

  • পরিবর্তন অ্যাডিটিভ, ধ্বংসাত্মক নয়। মাইগ্রেশন কেবল টেবিল, কলাম, বা ইন্ডেক্স যোগ করে। কিছুই সরানো, রিনেম বা এমনভাবে কঠোর করা উচিত নয় যা পুরনো লেখাগুলো প্রত্যাখ্যান করবে।
  • রিড দুটি আকারেই কাজ করে। নতুন সার্ভার কোড “নতুন ফিল্ড আছে” এবং “নতুন ফিল্ড নেই”—উভয় অবস্থায় ত্রুটিহীন কাজ করে। অপশনাল মানগুলোর নিরাপদ ডিফল্ট আছে।
  • রাইটস সামঞ্জস্যপূর্ণ থাকে। নতুন ক্লায়েন্ট নতুন ডেটা পাঠাতে পারে, পুরনো ক্লায়েন্ট পুরনো পো লোড পাঠালেও সফল হয়। যদি দুইটা ভার্সন একসাথে থাকা লাগে, সার্ভার উভয় ফরম্যাট গ্রহণ করে এবং পুরনো ক্লায়েন্ট পার্স করতে পারে এমন রেসপন্স তৈরি করে।
  • ব্যাকফিল থামানো ও শুরু করা নিরাপদ। জব ব্যাচে চলে, রিস্টার্ট করলে ডুপ্লিকেট বা ডেটা নষ্ট না হয়, এবং “রো বাকি” নামক একটি পরিমেয় সংখ্যা থাকে।
  • আপনি ডিলিট ডেট জানেন। কবে পুরনো ফিল্ড বা লজিক সরানো নিরাপদ হবে তার একটি কংক্রিট নিয়ম আছে (উদাহরণ: X দিনের পরে এবং নিশ্চিতকরণ যে Y% রিকুয়েস্ট আপডেটেড ক্লায়েন্ট থেকে আসছে)।

আপনি যদি একটি পুনরায় জেনারেটিং প্ল্যাটফর্ম ব্যবহার করেন, আরেকটি স্যানিটি চেক যোগ করুন: আপনি যে মডেলে মাইগ্রেট করছেন সেটি থেকে একটি বিল্ড জেনারেট করে ডিপ্লয় করুন, তারপর নিশ্চিত করুন জেনারেটেড API ও ব্যবসায়িক লজিক এখনও পুরনো রেকর্ড সহ্য করে। একটি সাধারণ ব্যর্থতা হলো নতুন স্কিমা মানেই নতুন আবশ্যক লজিক—এমনটা ধরে নেওয়া।

এছাড়া ডিপ্লয়ের পরে কিছু ভুল দেখা দিলে আপনি দ্রুত কী করবেন তা দুইটি ত্বরিত অ্যাকশন লিখে রাখুন: কী মনিটর করবেন (এরর, টাইমআউট, ব্যাকফিল অগ্রগতি) এবং প্রথমে কী রিভাট বা বন্ধ করবেন (ফিচার ফ্ল্যাগ অফ, ব্যাকফিল পজ, সার্ভার রিলিজ রিভাট)। এটি “আমরা দ্রুত প্রতিক্রিয়া দেব”কে বাস্তব পরিকল্পনায় পরিণত করে।

উদাহরণ: নতুন ফিল্ড যোগ করা যখন পুরনো মোবাইল অ্যাপগুলো এখনও সক্রিয়

আপনার রোলআউটকে ধাপে ভাগ করুন
ভিজ্যুয়াল টুল ব্যবহার করে ফিল্ড যোগ করুন, নিরাপদে ব্যাকফিল করুন এবং পুরনো ক্লায়েন্টগুলিকে সচল রাখুন।
অ্যাপ তৈরি করুন

আপনি একটি অর্ডার অ্যাপ চালান। নতুন ফিল্ড দরকার, delivery_window, এবং এটি নতুন ব্যবসায়িক নিয়মের জন্য আবশ্যক হবে। সমস্যা হলো পুরনো iOS ও Android বিল্ডগুলো এখনও ব্যবহার করা হচ্ছে এবং সেগুলো কয়েকদিন বা সপ্তাহ ধরে সেই ফিল্ড পাঠাবে না। ডাটাবেস যদি তা তাত্ক্ষণিকভাবে আবশ্যক করে দেবে, ঐ ক্লায়েন্টগুলো ব্যর্থ হতে শুরু করবে।

নিরাপদ পথ:

  • ধাপ 1: কলামটি nullable হিসেবে যোগ করুন, কোনো কনস্ট্রেইন্ট ছাড়াই। বিদ্যমান রিড ও রাইট অপরিবর্তিত রাখুন।
  • ধাপ 2: ডুয়াল রাইট। নতুন ক্লায়েন্ট (অথবা ব্যাকএন্ড) নতুন ফিল্ড লিখবে। পুরনো ক্লায়েন্ট কাজ চালিয়ে যাবে কারণ কলামটি null গ্রহণ করে।
  • ধাপ 3: ব্যাকফিল। পুরনো রোগুলোকে নিয়ম অনুযায়ী delivery_window দিয়ে পূরণ করুন (শিপিং পদ্ধতি থেকে অনুমান, বা গ্রাহক এডিট না করা পর্যন্ত ডিফল্ট “anytime” রাখুন)।
  • ধাপ 4: রিড সুইচ করুন। API ও UI-তে প্রথমে delivery_window পড়ুন, কিন্তু এটি মিসিং হলে অনুমানকৃত মানে fallback রাখুন।
  • ধাপ 5: পরে জোরদার করুন। গ্রহণ ও ব্যাকফিল সম্পন্ন হলে NOT NULL যোগ করুন এবং fallback সরান।

প্রতিটি ধাপে ব্যবহারকারীরা কেমন অনুভব করবে তা বিরল থাকা উচিত (এটাই লক্ষ্য):

  • পুরনো মোবাইল ব্যবহারকারীরা এখনও অর্ডার দিতে পারবে কারণ API অনুপস্থিত ডেটা প্রত্যাখ্যান করে না।
  • নতুন মোবাইল ব্যবহারকারীরা নতুন ফিল্ড দেখবে, এবং তাদের নির্বাচন ধারাবাহিকভাবে সংরক্ষিত হবে।
  • সাপোর্ট ও অপসরা ক্ষেত্র ধীরে ধীরে ফিল্ডটি পূরণ হতে দেখবে, হঠাৎ গ্যাপ নয়।

প্রতি ধাপের জন্য একটি সহজ মনিটরিং গেইট: নতুন অর্ডারগুলোর মধ্যে delivery_window নন-নালের শতাংশ ট্র্যাক করুন। যখন এটি ধারাবাহিকভাবে উচ্চ থাকবে (এবং “ফিল্ড মিসিং” এর ভ্যালিডেশন এরর শূন্যের কাছাকাছি থাকবে), সাধারণত ব্যাকফিল থেকে enforcement-এ যাওয়া নিরাপদ।

পরবর্তী পদক্ষেপ: পুনরাবৃত্তি যোগ্য মাইগ্রেশন প্লেবুক বানান

একটা এককালীন সতর্ক রোলআউট কৌশল নয়; স্কিমা পরিবর্তনকে একটি রুটিন হিসেবে বিবেচনা করুন: একই ধাপ, একই নামকরণ, একই স্বাক্ষর। তাহলে পরের অ্যাডিটিভ পরিবর্তনও শুকনো ও নির্ভয়ে থাকবে, এমনকি অ্যাপ ব্যস্ত অবস্থায় এবং ক্লায়েন্টরা ভিন্ন ভার্সনে থাকলেও।

প্লেবুকটিকে সংক্ষিপ্ত রাখুন। এর উত্তর থাকা উচিত: আমরা কি যোগ করছি, কিভাবে নিরাপদে শিপ করব, এবং কখন পুরনো অংশগুলো সরাব।

একটি সরল টেমপ্লেট:

  • কেবল যোগ করুন (নতুন কলাম/টেবিল/ইন্ডেক্স, নতুন API ফিল্ড যা অপশনাল)।
  • কোড শিপ করুন যা পুরনো ও নতুন দুই রূপই পড়তে পারে।
  • ক্ষুদ্র ব্যাচে ব্যাকফিল করুন, একটি পরিষ্কার “সিদ্ধ” সিগন্যাল সহ।
  • আচরণ ফ্লিপ করুন ফিচার ফ্ল্যাগ বা কনফিগ দিয়ে, পুনরায় ডিপ্লয় না করে।
  • কাটঅফ তারিখ ও ভেরিফিকেশন ছাড়া পুরনো ফিল্ড/এন্ডপয়েন্ট সরাবেন না।

কম ঝুঁকির একটি টেবিল দিয়ে শুরু করুন (একটি নতুন অপশনাল স্ট্যাটাস, একটি নোটস ফিল্ড) এবং পুরো প্লেবুকটি একে অপরের সাথে চালান: অ্যাডিটিভ পরিবর্তন, ব্যাকফিল, মিশ্র-ভার্সন ক্লায়েন্ট, তারপর ক্লিনআপ। ঐ অনুশীলন পর্যবেক্ষণ, ব্যাচিং, ও কমিউনিকেশনে ফাঁক বের করে দেবে এমনকি আপনি বড় রিডিজাইনের চেষ্টা করার আগে।

একটি অভ্যাস যা দীর্ঘমেয়াদে গোলমাল রোধ করে: “পরে সরান” আইটেমগুলোকে প্রকৃত কাজ হিসেবে ট্র্যাক করুন। আপনি যখন একটি অস্থায়ী কলাম, সামঞ্জস্য কোড, বা ডুয়াল-রাইট লজিক যোগ করেন, সঙ্গে সঙ্গেই একটি ক্লিনআপ টিকিট তৈরি করুন যার একটি মালিক ও একটি নির্ধারিত তারিখ আছে। রিলিজ ডকুমেন্টে ছোট একটি “compatibility debt” নোট রাখুন যাতে এটি দৃশ্যমান থাকে।

আপনি যদি AppMaster দিয়ে তৈরি করেন, আপনি পুনরায় জেনারেশনকে নিরাপত্তা প্রক্রিয়ার অংশ হিসেবে গণ্য করতে পারেন: অ্যাডিটিভ স্কিমা মডেল করুন, ট্রানজিশনের সময় পুরনো ও নতুন ফিল্ড দুইটাই হ্যান্ডল করার জন্য ব্যবসায়িক লজিক আপডেট করুন, এবং পুনরায় জেনারেট করুন যাতে সোর্স কোড পরিবর্তনের সাথে পরিচ্ছন্ন থাকে। যদি আপনি দেখতে চান কিভাবে এই ওয়ার্কফ্লো একটি নো-কোড সেটআপে মানানসই হয় যা বাস্তব সোর্স কোডও উৎপন্ন করে, AppMaster (appmaster.io) সেই ধরনের ধাপে ধাপে ডেলিভারির চারপাশে ডিজাইন করা হয়েছে।

লক্ষ্য পারফেকশন নয়—পুনরাবৃত্তি: প্রতিটি মাইগ্রেশনের একটি পরিকল্পনা, একটি পরিমাপ, ও একটি এক্সিট র‍্যাম্প আছে।

প্রশ্নোত্তর

স্কিমা পরিবর্তনের জন্য “শূন্য-ডাউনটাইম” আসলে কী বোঝায়?

শূন্য-ডাউনটাইম মানে ব্যবহারকারীরা স্বাভাবিকভাবে কাজ চালিয়ে যেতে পারে যখন আপনি স্কিমা পরিবর্তন করে কোড ডিপ্লয় করছেন। এতে স্পষ্ট আউটেজ এড়ানো ছাড়াও গোপন ভাঙনগুলিও প্রতিরোধ করা অন্তর্ভুক্ত, যেমন ফাঁকা স্ক্রিন, ভুল মান, ব্যাকগ্রাউন্ড চাকরি ক্র্যাশ, বা দীর্ঘ লকজনিত কারণে লেখাগুলি ব্লক হওয়া।

মাইগ্রেশন সফল হলেও কেন স্কিমা পরিবর্তন সবসময় ভাঙে?

কারণ আপনার সিস্টেমের অনেক অংশ ডাটাবেসের আকৃতির উপর নির্ভর করে, শুধু প্রধান UI নয়। ব্যাকগ্রাউন্ড জব, রিপোর্ট, অ্যাডমিন স্ক্রিপ্ট, ইন্টিগ্রেশন এবং পুরনো মোবাইল অ্যাপগুলি নতুন কোড ডিপ্লয়ের পরেও পুরনো ফিল্ড পাঠানো বা প্রত্যাশা করতে পারে।

মাইগ্রেশন চলাকালীন কেন পুরনো মোবাইল অ্যাপগুলো এত বড় ঝুঁকি?

পুরনো মোবাইল বিল্ডগুলো কয়েক সপ্তাহ সচল থাকতে পারে, এবং কিছু ক্লায়েন্ট পুরনো অনুরোধগুলো পরে রিট্রাই করে। আপনার API-কে একটি সময়ের জন্য পুরনো ও নতুন উভয় প্যালোড গ্রহণ করতে সক্ষম হতে হবে যাতে মিশ্র সংস্করণগুলো ত্রুটিহীনভাবে সহাবস্থান করে।

ডাউনটাইম ছাড়াই কোন ধরনের স্কিমা পরিবর্তন সবচেয়ে নিরাপদ?

অ্যাডিটিভ পরিবর্তন সাধারণত বিদ্যমান কোড ভাঙে না কারণ পুরনো স্কিমা এখনও থাকে। রিনেম বা ডিলিট ঝুঁকিপূর্ণ, কারণ সেগুলো পুরনো ক্লায়েন্ট যা এখনও পড়ে/লিখে তার কিছু সরিয়ে দেয়, ফলে ক্র্যাশ বা ব্যর্থ অনুরোধ হয়।

কীভাবে একটি নতুন আবশ্যক ফিল্ড যোগ করে পুরনো ক্লায়েন্টকে ভাঙ্গা ছাড়া রাখা যায়?

প্রথমে কলামটি nullable হিসেবে যোগ করুন যাতে পুরনো কোড এখনও রো ইনসার্ট করতে পারে। পুরনো রো ব্যাকফিল ব্যাচ করে পূরণ করুন, এবং কভারেজ উচ্চ হলে শুধুমাত্র তারপর NOT NULL জোরদার করুন—এটাই শেষ ধাপ হওয়া উচিত।

শূন্য-ডাউনটাইম মাইগ্রেশনের জন্য বাস্তবিক রোলআউট ক্রম কি?

একটি রোলআউট হিসেবে বিবেচনা করুন: উপযোগী স্কিমা যোগ করুন, উভয় ‘ভাষা’ বলতে পারে এমন কোড ডিপ্লয় করুন, ছোট ব্যাচে ব্যাকফিল করুন, ফলোব্যাক রেখে রিড সুইচ করুন, এবং প্রমাণ মেলে শুধুমাত্র তারপর পুরনো ফিল্ড মুছুন। প্রতিটি ধাপ নিজেই নিরাপদ হওয়া উচিত।

কীভাবে ব্যাকফিল ডেডলাইন বা লক ছাড়াই করা যায়?

ছোট ব্যাচে, সংক্ষিপ্ত ট্রানজ্যাকশনে ব্যাকফিল চালান যাতে টেবিল লক বা লোড স্পাইক না ঘটে। যেগুলো মিসিং আছে শুধু তেগুলো আপডেট করুন, অগ্রগতি রেকর্ড করুন এবং জব রিস্টার্টেবল রাখুন।

স্কিমা বদলাতে গেলে আমার API-কে কিভাবে সামঞ্জস্য রাখতে হবে?

প্রথমে নতুন ফিল্ডগুলো অপশনাল রাখুন এবং সার্ভারে উপস্থিত না থাকলে ডিফল্ট প্রয়োগ করুন। পুরনো আচরণ স্থিতিশীল রাখুন, বিদ্যমান ফিল্ডের অর্থ পরিবর্তন এড়ান, এবং দুটো পথই টেস্ট করুন: “নতুন ক্লায়েন্ট পাঠিয়েছে” এবং “পুরনো ক্লায়েন্ট মিস করেছে।”

ধাপে ধাপে মাইগ্রেশনের সময় সেরা রোলব্যাক পরিকল্পনা কি?

অধিকাংশ ক্ষেত্রে আপনি স্কিমা রোলব্যাক করেন না; কোড রিভাট করেন। অ্যাডিটিভ কলাম/টেবিল রাখুন, নতুন রিড প্রথমে নিষ্ক্রিয় করুন, তারপর নতুন রাইট বন্ধ করুন, এবং ব্যাকফিল থামান যতক্ষণ না মেট্রিকস স্থিতিশীল হয়। ব্যাকফিলের জন্য একটি দ্রুত স্টপ-সুইচ রাখুন (ফিচার ফ্ল্যাগ বা কনফিগ)।

পরবর্তী ধাপে যাওয়ার জন্য আমি কী মনিটর করব?

প্রতিটি ধাপে ব্যবহারকারী-প্রভাব সম্পর্কিত সিগন্যাল দেখুন: ত্রুটি হার, ধীর কুয়েরি (p95/p99), রাইট লেটেন্সি, কিউ ডেপথ, এবং ডাটাবেস CPU/IO। নতুন ফিল্ডের জন্য কভারেজ উচ্চ হওয়া এবং ত্রুটি কম থাকলে পরবর্তী ধাপে যাওয়া ঠিক।

শুরু করা সহজ
কিছু আশ্চর্যজনকতৈরি করুন

বিনামূল্যের পরিকল্পনা সহ অ্যাপমাস্টারের সাথে পরীক্ষা করুন।
আপনি যখন প্রস্তুত হবেন তখন আপনি সঠিক সদস্যতা বেছে নিতে পারেন৷

এবার শুরু করা যাক