মাইগ্রেশন যা উৎপাদন কমিয়ে দেয় তা খুব কমই জটিল। এটি সাধারণত এককALTER TABLE যেটি একটি বড় টেবিলে একটি একচেটিয়া লক নিয়েছিল যখন অ্যাপ্লিকেশনটি অপেক্ষা করছে। জিরো-ডাউনটাইম মাইগ্রেশন একটি টুলের পরিবর্তে একটি নিয়মানুবর্তিতা: প্রতিটি পরিবর্তনকে ধাপে বিভক্ত করুন যা একবারে চলমান পুরানো এবং নতুন কোডের সাথে পৃথকভাবে নিরাপদ।
📋 Table of Contents
মূল নীতি: প্রসারিত এবং চুক্তি
আপনি একটি স্কিমা পরিবর্তন স্থাপন করতে পারবেন না এবং যে কোডটি একই তাত্ক্ষণিকভাবে প্রয়োজন। যে কোনো রোলিং স্থাপনার সময়, পুরানো এবং নতুন অ্যাপ্লিকেশন সংস্করণ একই সাথে চলে। প্রতিটি স্থানান্তর তাই উভয়ের সাথে সামঞ্জস্যপূর্ণ হতে হবে।
প্রসারিত-কন্ট্রাক্ট প্যাটার্ন প্রতিটি পরিবর্তনকে তিনটি স্থাপনায় বিভক্ত করে:
- প্রসারিত – নতুন কাঠামো যোগ করুন। পুরানো কোড এটি উপেক্ষা করে; নতুন কোড এটি ব্যবহার করতে পারেন।
- মাইগ্রেট — ব্যাকফিল ডেটা এবং অ্যাপ্লিকেশনটিকে নতুন কাঠামোতে স্যুইচ করুন।
- চুক্তি – পুরানো কাঠামোটি সরিয়ে ফেলুন যখন কিছুই এটির উল্লেখ না করে।
প্রতিটি পদক্ষেপ পৃথকভাবে স্থাপন করে এবং প্রতিটি পৃথকভাবে বিপরীতমুখী। এটিই পুরো সিকোয়েন্সটিকে নিরাপদ করে তোলে।
উদাহরণ: একটি কলামের নাম পরিবর্তন করা
একটি পুনঃনাম তুচ্ছ মনে হয় এবং এটি সবচেয়ে বিপজ্জনক ক্রিয়াকলাপগুলির মধ্যে একটি, কারণ এটি চালানোর সাথে সাথে পুরানো কোডটি ভেঙে দেয়৷
-- ❌ Never do this on a live system.
ALTER TABLE users RENAME COLUMN email TO email_address;
-- Every running instance still querying "email" fails immediately.
নিরাপদ ক্রম:
-- Step 1 (expand): add the new column. Nullable, no default — instant.
ALTER TABLE users ADD COLUMN email_address TEXT;
-- Step 2: deploy code that WRITES both columns and READS the old one.
-- UPDATE users SET email = $1, email_address = $1 WHERE id = $2
-- Step 3: backfill existing rows in batches (see below).
-- Step 4: deploy code that READS the new column and still writes both.
-- Step 5: deploy code that only uses the new column.
-- Step 6 (contract): drop the old column, once nothing references it.
ALTER TABLE users DROP COLUMN email;
একটি পুনঃনামকরণের জন্য ছয়টি স্থাপনা অত্যধিক অনুভূত হয় যতক্ষণ না প্রথমবার একটি এক-ধাপে পুনঃনামকরণ বিভ্রাটের কারণ হয়৷ বেশিরভাগ দল শেষ পর্যন্ত সিদ্ধান্ত নেয় যে নাম পরিবর্তন করা মূল্যবান নয় এবং কেবল আসল নামটি রাখবে।
কোন অপারেশন লক
কোন বিবৃতি একটি ব্লকিং লক লাগে তা জানা বেশিরভাগ দক্ষতা। আধুনিক PostgreSQL-এ:
| অপারেশন | নিরাপদ? | নোট |
|---|---|---|
| বাতিলযোগ্য কলাম যোগ করুন | নিরাপদ | শুধুমাত্র মেটাডেটা, তাত্ক্ষণিক |
| ধ্রুবক ডিফল্ট সহ কলাম যোগ করুন | নিরাপদ | আধুনিক সংস্করণে কোনো টেবিল পুনর্লিখন নেই |
| উদ্বায়ী ডিফল্ট সহ কলাম যোগ করুন | বিপজ্জনক | পুরো টেবিলটি আবার লিখুন |
| সূচক যোগ করুন | বিপজ্জনক | ব্লক লেখেন — একযোগে ব্যবহার করুন |
| বিদ্যমান কলামে NOT NULL যোগ করুন | বিপজ্জনক | লকের অধীনে সম্পূর্ণ স্ক্যান — প্রথমে একটি চেক সীমাবদ্ধতা ব্যবহার করুন |
| কলামের ধরন পরিবর্তন করুন | বিপজ্জনক | সাধারণত একটি সম্পূর্ণ পুনর্লিখন |
| বিদেশী কী যোগ করুন | বিপজ্জনক | সমস্ত সারি যাচাই করে — প্রথমে বৈধ নয় যোগ করুন |
| ড্রপ কলাম | নিরাপদ | শুধুমাত্র মেটাডেটা, কিন্তু পুরানো কোড ভঙ্গ করে |
সর্বদা আপনার নির্দিষ্ট ডাটাবেস সংস্করণের বিরুদ্ধে যাচাই করুন। সাম্প্রতিক রিলিজের তুলনায় এই ক্রিয়াকলাপের আচরণ উল্লেখযোগ্যভাবে উন্নত হয়েছে, এবং পুরানো সংস্করণগুলির জন্য লেখা পরামর্শগুলি প্রায়শই অপ্রয়োজনীয়ভাবে সতর্ক হয় – বা অন্য দিকে বিপজ্জনকভাবে পুরানো।
নিরাপদে সূচী যোগ করা হচ্ছে
-- ❌ Blocks all writes to the table for the duration.
CREATE INDEX idx_users_email ON users(email);
-- ✅ Builds without blocking writes.
CREATE INDEX CONCURRENTLY idx_users_email ON users(email);
সম্পর্কে জানার দুটি জিনিসCONCURRENTLY. এটি একটি লেনদেন ব্লকের ভিতরে চলতে পারে না, যার মানে বেশিরভাগ মাইগ্রেশন ফ্রেমওয়ার্কের এটির অনুমতি দেওয়ার জন্য স্পষ্ট কনফিগারেশন প্রয়োজন। এবং এটি আংশিকভাবে ব্যর্থ হতে পারে, একটি অবৈধ সূচক রেখে যা অবশ্যই পরিষ্কার করতে হবে।
-- Find invalid indexes left behind by a failed concurrent build
SELECT indexrelid::regclass AS index_name
FROM pg_index
WHERE NOT indisvalid;
-- Drop and retry
DROP INDEX CONCURRENTLY idx_users_email;
একটি দীর্ঘ লক ছাড়া NOT NULL যোগ করা হচ্ছে
যোগ করা হচ্ছেNOT NULL একটি লক ধারণ করার সময় সরাসরি পুরো টেবিলটি স্ক্যান করে। একটি যাচাইকৃত চেক সীমাবদ্ধতা দুটি নন-ব্লকিং ধাপে একই গ্যারান্টি অর্জন করে।
-- 1. Add the constraint without validating existing rows — instant.
ALTER TABLE users
ADD CONSTRAINT users_email_not_null
CHECK (email IS NOT NULL) NOT VALID;
-- 2. Validate separately. Takes a weaker lock that allows reads and writes.
ALTER TABLE users VALIDATE CONSTRAINT users_email_not_null;
একই NOT VALID/VALIDATE প্যাটার্ন বিদেশী কীগুলির ক্ষেত্রে প্রযোজ্য, এবং একই কারণে।
বড় টেবিল ব্যাকফিলিং
একটি এককUPDATE লক্ষ লক্ষ সারি জুড়ে তালা ধারণ করে, প্রচুর লেখার-আগে লগ ভলিউম তৈরি করে এবং প্রতিলিপি স্থগিত করতে পারে। এটা ব্যাচ.
-- ❌ One statement, one very long transaction, many locked rows.
UPDATE users SET email_address = email;
-- ✅ Batched, resumable, and gentle on replication.
DO $$
DECLARE
batch_size INT := 5000;
affected INT;
BEGIN
LOOP
UPDATE users
SET email_address = email
WHERE id IN (
SELECT id FROM users
WHERE email_address IS NULL AND email IS NOT NULL
ORDER BY id
LIMIT batch_size
FOR UPDATE SKIP LOCKED
);
GET DIAGNOSTICS affected = ROW_COUNT;
EXIT WHEN affected = 0;
COMMIT;
PERFORM pg_sleep(0.1); -- let replicas catch up
END LOOP;
END $$;
FOR UPDATE SKIP LOCKED আপনার অ্যাপ্লিকেশন বর্তমানে আপডেট করা সারিগুলিতে ব্যাকফিলকে ব্লক করা থেকে বাধা দেয়। ব্যাচের মধ্যে সংক্ষিপ্ত ঘুম যা প্রতিলিপিকরণের ব্যবধানকে বাড়তে বাধা দেয় এবং এটি এমন পদক্ষেপ যা লোকেরা বাদ দেয় এবং অনুশোচনা করে।
খুব বড় টেবিলের জন্য, একটি মাইগ্রেশনের ভিতরে না হয়ে ব্যাকফিলটিকে একটি পৃথক কাজ হিসাবে চালান, তাই একটি স্থাপনা কখনই এটির জন্য অপেক্ষা করে না।
একটি লক টাইমআউট সেট করুন
এই একক সেটিং বেশিরভাগ মাইগ্রেশন বিভ্রাট প্রতিরোধ করে। এটি ছাড়া, একটি স্থানান্তর যা একটি তালা অর্জন করতে পারে না তা অনির্দিষ্টকালের জন্য অপেক্ষা করে — এবং এর পিছনে সারিবদ্ধ প্রতিটি প্রশ্নও অপেক্ষা করে, এটি কীভাবে একটিALTER TABLE একটি সম্পূর্ণ অ্যাপ্লিকেশন স্টল.
-- Fail fast instead of queueing behind a long-running query.
SET lock_timeout = '3s';
SET statement_timeout = '30s';
ALTER TABLE users ADD COLUMN email_address TEXT;
যদি এটি সময় শেষ হয়, পরে আবার চেষ্টা করুন. পিক ট্রাফিকের সময় লক করা টেবিলের চেয়ে একটি ব্যর্থ মাইগ্রেশন আপনি পুনরায় চালাতে পারেন।
ডেটাবেসগুলির মধ্যে স্থানান্তরিত হচ্ছে
একটি নতুন ডাটাবেস ইঞ্জিন বা উদাহরণে সরানো একটি বৃহত্তর স্কেলে একই নীতি ব্যবহার করে।
- প্রতিলিপি — যৌক্তিক প্রতিলিপি সেট আপ করুন বা পুরানো থেকে নতুন পরিবর্তন-ডেটা-ক্যাপচার করুন। এটি সম্পূর্ণরূপে ধরা যাক.
- দ্বৈত পাঠ — পুরানো ডাটাবেস থেকে পড়ুন, এবং ফলাফলের তুলনা করতে পটভূমিতে নতুন থেকে পড়ুন। প্রতিটি অমিল লগ করুন এবং এগিয়ে যাওয়ার আগে কারণগুলি ঠিক করুন৷
- দ্বৈত লিখুন – উভয়কে লিখুন। পুরানোটি কর্তৃত্বপূর্ণ থাকে।
- কাট ওভার — নতুন ডাটাবেসে রিড সুইচ করুন। দুজনকেই লিখতে থাকুন।
- ডিকমিশন– একটি অর্থপূর্ণ সময়ের জন্য এটিকে রোলব্যাক পাথ হিসাবে রেখে আত্মবিশ্বাসী হয়ে গেলে পুরানো ডাটাবেসে লেখা বন্ধ করুন।
ধাপ 2-এ তুলনা পর্বটি এটিকে নিরাপদ করে তোলে। এটি এড়িয়ে যাওয়া মানে কাটওভারের পরে ডেটা পার্থক্য আবিষ্কার করা, যখন ফিরে আসা ব্যয়বহুল।
রোলব্যাক পরিকল্পনা
প্রতিটি মাইগ্রেশনের একটি উত্তর প্রয়োজন “যদি এটি ভুল হয়?” এটি চালানোর আগে।
সংযোজন পরিবর্তনগুলি তুচ্ছভাবে ফিরে আসে – আপনি এইমাত্র যোগ করা একটি কলাম বাদ দেওয়া নিরাপদ কারণ কিছুই এটির উপর নির্ভর করে না।
ধ্বংসাত্মক পরিবর্তনগুলি ফিরে আসে না। একবার একটি কলাম ড্রপ হয়ে গেলে, ডেটা চলে যায়। ঠিক এই কারণেই চুক্তির পদক্ষেপগুলি শেষ এবং শুধুমাত্র আত্মবিশ্বাসের সময় পরে আসে।
ব্যাকফিলগুলিরও প্রত্যাবর্তনশীলতা প্রয়োজন। আপনি যদি একটি কলাম ওভাররাইট করেন, আপনি নিশ্চিত না হওয়া পর্যন্ত মূল মানগুলি কোথাও রাখুন।
কখনই এমন মাইগ্রেশন লিখবেন না যার ডাউন স্টেপ ডেটা মুছে ফেলে। যদি একটি রোলব্যাক তথ্য হারাবে, মাইগ্রেশন স্বয়ংক্রিয়ভাবে বিপরীত হওয়া উচিত নয় — পুনরুদ্ধারের পথটিকে একটি ইচ্ছাকৃত, ম্যানুয়াল সিদ্ধান্ত নিন।
প্রি-ফ্লাইট চেকলিস্ট
- প্রোডাকশন স্কেলে, প্রোডাকশন ডেটার পুনরুদ্ধার করা কপিতে পরীক্ষা করুন
- প্রতিটি বিবৃতি একটি ব্লকিং লক লাগে কিনা তা জানুন
- সেট
lock_timeoutএবংstatement_timeout - প্রতিটি ব্যাকফিল ব্যাচ করুন এবং স্থাপনার বাইরে এটি চালান
- পুরানো এবং নতুন অ্যাপ্লিকেশন কোড উভয়ই মধ্যবর্তী স্কিমার সাথে কাজ করে তা নিশ্চিত করুন
- সময় এবং পরে প্রতিলিপি ল্যাগ দেখুন
- একটি যাচাইকৃত ব্যাকআপ রাখুন, এবং জানুন কতক্ষণ পুনরুদ্ধার করতে লাগে
- কম ট্রাফিকের সময় মোতায়েন করুন এমনকি যখন আপনি কোন লক না আশা করেন
উপসংহার
জিরো-ডাউনটাইম মাইগ্রেশন কয়েকটি শৃঙ্খলায় নেমে আসে:প্রতিটি পরিবর্তনকে প্রসারিত, স্থানান্তর, এবং চুক্তির পর্যায়গুলি আলাদাভাবে স্থাপন করা হয়েছে; কোন বিবৃতিগুলি ব্লকিং লকগুলি গ্রহণ করে তা জানুন এবং সেগুলি এড়াতে একযোগে ব্যবহার করুন এবং বৈধ নয়; বিরাম দিয়ে ব্যাচ ব্যাকফিল করে যাতে প্রতিলিপি চলতে থাকে; এবং সর্বদা একটি লক টাইমআউট সেট করুন যাতে আপনার সম্পূর্ণ অ্যাপ্লিকেশনটিকে পিছনে সারিবদ্ধ করার পরিবর্তে একটি মাইগ্রেশন দ্রুত ব্যর্থ হয়। প্রথম স্থানান্তর না হওয়া পর্যন্ত অতিরিক্ত স্থাপনাগুলি ওভারহেডের মতো মনে হয় যা অন্যথায় বিভ্রাটের কারণ হয়ে উঠত।
🔗 Share this article
✍️ Leave a Comment