একজন ডেভেলপার CREATE OR REPLACE FUNCTION ব্যবহার করে একটি বিদ্যমান ফাংশনে একটি ঐচ্ছিক আর্গুমেন্ট যোগ করার পর একটি প্রোডাকশন PostgreSQL ডিপ্লয়মেন্ট ক্র্যাশ করে। এই পরিবর্তনের ফলে একই নামের দুটি ফাংশন তৈরি হয়, যার ফলে ডাটাবেস “function is not unique” এরর দেয় এবং API একটি 400 এরর প্রদান করে। এই ঘটনাটি দেখায় যে কীভাবে একটি মাত্র মাইগ্রেশন ভুল নীরবে একটি লাইভ স্কিমাকে ক্ষতিগ্রস্ত করতে পারে এবং কেন শুধুমাত্র কোড-লেভেল চেক যথেষ্ট নয়।
কী ভুল হয়েছিল
টিমের একটি স্টোরড প্রসিডিউরে (stored procedure) একটি অতিরিক্ত, ঐচ্ছিক প্যারামিটার যোগ করার প্রয়োজন ছিল। তারা ধরে নিয়েছিল যে CREATE OR REPLACE FUNCTION … কমান্ডটি পুরনো ডেফিনিশনটিকে ওভাররাইট করবে। কিন্তু PostgreSQL একটি ফাংশনকে কেবল তখনই প্রতিস্থাপন করে যখন আর্গুমেন্ট লিস্টটি হুবহু মিলে যায়। সিগনেচার (signature) পরিবর্তন করলে মূল ফাংশনটি অপরিবর্তিত রেখে একটি সম্পূর্ণ নতুন ফাংশন এন্ট্রি তৈরি হয়।
যেহেতু নতুন আর্গুমেন্টটির একটি ডিফল্ট ভ্যালু ছিল, তাই যে কলগুলো পুরনো সংখ্যক আর্গুমেন্ট প্রদান করছিল, সেগুলো উভয় ডেফিনিশনের সাথেই মিলে যেতে পারত। PostgreSQL সিদ্ধান্ত নিতে পারছিল না কোনটি কল করতে হবে এবং “function is not unique” এররটি থ্রো করল, যা API থেকে একটি 400 রেসপন্স হিসেবে দেখা দিল।
কোড রিপোজিটরিতে একটি মাত্র ডেফিনিশন দেখাচ্ছিল এবং একটি কাস্টম স্ক্রিপ্ট যা সোর্স ট্রি স্ক্যান করেছিল, সেটি কোনো ডুপ্লিকেট রিপোর্ট করেনি। ডুপ্লিকেটটি শুধুমাত্র ডাটাবেসে ছিল, যা সার্ভারে একটি পুরনো মাইগ্রেশন ফাইল পুনরায় এক্সিকিউট করার সময় তৈরি হয়েছিল।
কেন মাইগ্রেশনটি ধরা পড়েনি
ঐচ্ছিক প্যারামিটার যোগ করার মাইগ্রেশনটি কেবল CREATE OR REPLACE FUNCTION কমান্ডটি চালিয়েছিল। যখন মাইগ্রেশনটি দ্বিতীয়বার চালানো হয়েছিল—সম্ভবত একটি রোলব্যাক বা রিপিট ডিপ্লয়মেন্টের সময়—ডাটাবেস কমান্ডটিকে “বিদ্যমানটি প্রতিস্থাপন করুন” এর পরিবর্তে “একটি নতুন ওভারলোড যোগ করুন” হিসেবে গণ্য করেছিল। মাইগ্রেশনটি এর ফলে সৃষ্ট অবস্থা যাচাই করেনি, তাই ডুপ্লিকেটটি অলক্ষিতভাবে থেকে গিয়েছিল।
রিপোজিটরি চেক করা স্ক্রিপ্টটি সোর্স ফাইলগুলো পরীক্ষা করেছিল, লাইভ স্কিমা নয়। এটি ছিল সামনের দরজার দিকে নজর দেওয়া, যখন বাগটি পেছনের দরজা দিয়ে প্রবেশ করেছিল।
ঝুঁকির মাত্রা
একটি মাত্র অস্পষ্ট (ambiguous) ফাংশন সেই সমস্ত সার্ভিসকে অচল করে দিতে পারে যা এর ওপর নির্ভরশীল। এর সমাধান ছিল ফাংশনের সংখ্যা ভুল হলে ট্রানজ্যাকশন রোলব্যাক করা এবং স্কিমা ক্যাশ (schema cache) রিলোড করার জন্য নোটিফাই করা।
কীভাবে মাইগ্রেশন সুরক্ষিত রাখা যায়
টিমটি মাইগ্রেশনটিকে স্পষ্ট চেকের মাধ্যমে পুনরায় তৈরি করেছে, যা এটিকে একটি সেলফ-অ্যাসার্টিং (self-asserting) অপারেশনে পরিণত করেছে:
- একটি ট্রানজ্যাকশন শুরু করুন যাতে কোনো ব্যর্থতা ঘটলে পুরো পরিবর্তনটি রোলব্যাক হয়ে যায়।
- নতুন ভার্সনটি তৈরি করার আগে পুরনো ফাংশনটি স্পষ্টভাবে ড্রপ (drop) করুন, যাতে নিশ্চিত হওয়া যায় যে কেবল একটি ডেফিনিশন বিদ্যমান।
- কাঙ্ক্ষিত সিগনেচার দিয়ে নতুন ফাংশনটি তৈরি করুন।
pg_catalog-এ নির্দিষ্ট নামের ফাংশনগুলোর সংখ্যা গণনা করুন এবং নিশ্চিত করুন যে সংখ্যাটি ঠিক এক।- যদি সংখ্যাটি ভিন্ন হয়, তবে ট্রানজ্যাকশন রোলব্যাক করুন, যাতে ডুপ্লিকেটটি থেকে না যায়।
- স্কিমা ক্যাশ রিলোড করার জন্য নোটিফাই করুন, যাতে পরবর্তী কুয়েরিগুলো আপডেট করা ডেফিনিশন দেখতে পায়।
কোডটি সঠিক বলে ধরে না নিয়ে ডাটাবেসকে “কী কী ফাংশন উপস্থিত আছে?” জিজ্ঞাসা করার মাধ্যমে, মাইগ্রেশনটি বারবার চালানো, আংশিক ডিপ্লয়মেন্ট বা ম্যানুয়াল এডিট করার ক্ষেত্রে নির্ভরযোগ্য হয়ে ওঠে।
পাল্টা যুক্তি: সুবিধা বনাম নিরাপত্তা
CREATE OR REPLACE FUNCTION আকর্ষণীয় কারণ এটি ডেভেলপারদের আলাদা ড্রপ স্টেটমেন্ট না লিখে দ্রুত ইটারেট করতে সাহায্য করে। যে পরিবেশে মাইগ্রেশন একবার চলে এবং আর কখনো পুনরায় চলে না, সেখানে এই শর্টকাটটি ঠিকঠাক কাজ করে। ঝুঁকি তখনই দেখা দেয় যখন মাইগ্রেশনগুলো পুনরায় চালানো হয়—তা টেস্ট ডাটাবেস রিসেট করা CI পাইপলাইন, স্বয়ংক্রিয় রোলব্যাক বা প্রোডাকশনে ম্যানুয়াল রিম্যালোকেশন যাই হোক না কেন।
সারকথা
CREATE OR REPLACE দিয়ে একটি ফাংশনের সিগনেচার পরিবর্তন করা প্রতিস্থাপনের নিশ্চয়তা দেয় না—আর্গুমেন্ট লিস্ট ভিন্ন হলে PostgreSQL নীরবে একটি ওভারলোড তৈরি করবে। যে প্রোডাকশন এনভায়রনমেন্টগুলো মাইগ্রেশনের ওপর নির্ভর করে, তাদের অবশ্যই ফলাফল হিসেবে প্রাপ্ত স্কিমা যাচাই করতে হবে, শুধুমাত্র সোর্স কোড নয়। স্পষ্ট ড্রপ (drop), ট্রানজ্যাকশনাল চেক এবং মাইগ্রেশন-পরবর্তী অ্যাসারশন যুক্ত করা একটি সুবিধাজনক শর্টকাটকে একটি নির্ভরযোগ্য এবং পুনরাবৃত্তিযোগ্য প্রক্রিয়ায় রূপান্তরিত করে।
