PostgreSQL کا "cannot execute CREATE TABLE in a read-only transaction" والا ایرر اب بہت سی ٹیموں کے لیے مائیگریشن (migrations) میں رکاوٹ بن رہا ہے، جس سے اسکیما (schemas) ادھورے رہ جاتے ہیں اور مائیگریشن ہسٹری ٹیبلز لاک ہو جاتے ہیں۔ یہ خرابی عام طور پر تب ظاہر ہوتی ہے جب کوئی رائٹ آپریشن (write operation) پرائمری کے بجائے ریپلیکا (replica) پر چلا جاتا ہے، اور یہ ان تمام ٹولز کو روک دیتا ہے جو اسکیما کی تبدیلیوں پر انحصار کرتے ہیں—جیسے Flyway، Liquibase، Django ORM، اور اسی طرح کے دیگر ٹولز۔
یہ ایرر کیوں ظاہر ہوتا ہے
جب کسی ٹرانزیکشن (transaction) کو read-only کے طور پر نشان زد کیا جاتا ہے، تو PostgreSQL تمام رائٹ آپریشنز، اسکیما میں تبدیلیوں اور سیکوئنس اپ ڈیٹس کو غیر فعال کر دیتا ہے۔ مائیگریشن کے اس حالت میں پہنچنے کے سب سے عام طریقے یہ ہیں:
- Connection poolers (مثلاً PgBouncer) جو غلطی سے مائیگریشن کنکشن کو ریڈ ریپلیکا (read replica) پر بھیج دیتے ہیں۔
- Roles جن میں
default_transaction_read_onlyپیرامیٹر ڈیفالٹ کے طور پر on پر سیٹ ہوتا ہے۔ - Cloud endpoints جو الگ الگ ریڈر (reader) اور رائٹر (writer) URLs فراہم کرتے ہیں؛ ریڈر URL کا استعمال کرنا (جیسا کہ AWS RDS یا Aurora میں عام ہے) رائٹ آپریشنز کو ریپلیکا کی طرف موڑ دیتا ہے۔
جب ان میں سے کوئی بھی صورتحال لاگو ہوتی ہے، تو مائیگریشن کا عمل ٹیبلز بنا سکتا ہے، کالمز شامل کر سکتا ہے، یا سیکوئنسز کو اپ ڈیٹ کر سکتا ہے، لیکن آخر کار ایک رکاوٹ کا سامنا کرنا پڑتا ہے، جس سے ڈیٹا بیس جزوی طور پر مائیگریٹ شدہ حالت میں رہ جاتا ہے۔
ٹرانزیکشن کے اندر فوری حل
اگر آپ کو یہ ایرر پہلے ہی آ چکا ہے، تو آپ موجودہ ٹرانزیکشن کے لیے ریڈ-اونلی فلیگ (read-only flag) کو اوور رائڈ کر سکتے ہیں، بغیر سیشن کے باقی حصوں کو متاثر کیے—یہ اس وقت بہت اہم ہے جب کنکشن پول (connection pool) دوسرے کاموں کے لیے اسی سیشن کو دوبارہ استعمال کرتا ہے۔
BEGIN;
SET LOCAL default_transaction_read_only = off;
SET TRANSACTION READ WRITE;
CREATE TABLE orders (id SERIAL PRIMARY KEY, total NUMERIC);
COMMIT;
SET LOCAL صرف ٹرانزیکشن کے دورانیے کے لیے سیٹنگ کو تبدیل کرتا ہے۔ سادہ SET کا استعمال پورے سیشن کے لیے تبدیلی کو برقرار رکھے گا، جو ان دیگر آپریشنز کو خراب کر سکتا ہے جنہیں قانونی طور پر ریڈ-اونلی ڈیفالٹ کی ضرورت ہوتی ہے۔
احتیاطی تدابیر
1. تصدیق کریں کہ آپ پرائمری نوڈ پر ہیں
کسی بھی مائیگریشن کے چلنے سے پہلے ایک فوری چیک شامل کریں:
SELECT CASE WHEN pg_is_in_recovery() THEN 'REPLICA' ELSE 'PRIMARY' END;
اگر نتیجہ REPLICA ہو، تو مائیگریشن کو روک دیں۔ فنکشن pg_is_in_recovery() اسٹینڈ بائی سرور پر true واپس کرتا ہے، جو اس بات کی ضمانت دیتا ہے کہ آپ ریڈ-اونلی کاپی پر لکھنے کی کوشش نہیں کر رہے ہیں۔
2. مخصوص مائیگریشن رولز (migration roles) استعمال کریں
ایک ایسا رول بنائیں جس کا ڈیفالٹ ٹرانزیکشن موڈ رائٹ-ایبل (write-enabled) ہو، اور اسے صرف وہی پر ویلیجز (privileges) دیں جن کی اسے ضرورت ہے:
- ٹارگٹ ڈیٹا بیس پر
CREATEکی اجازت۔ - مائیگریشن ٹول کو سیشن کھولنے کی اجازت دینے کے لیے
CONNECT۔
اس رول کو default_transaction_read_only = on کی خصوصیت دینے سے گریز کریں جو بعض اوقات عام مقصد کے صارفین کے لیے سیٹ کی جاتی ہے۔
3. انفراسٹرکچر کو رائٹر اینڈ پوائنٹ (writer endpoint) پر پوائنٹ کریں
IaC اسکرپٹس (Terraform، CloudFormation، وغیرہ) میں، مائیگریشن رنرز کو کلسٹر کے writer اینڈ پوائنٹ پر سیٹ کریں، نہ کہ ریڈر اینڈ پوائنٹ پر۔ رائٹر اینڈ پوائنٹ پرائمری نوڈ سے منسلک ہوتا ہے، جبکہ ریڈر اینڈ پوائنٹ ایک ریپلیکا سے منسلک ہوتا ہے جو رائٹ آپریشنز کو مسترد کر دے گا۔
4. CI/CD گیٹ شامل کریں
اپنے پائپ لائن (GitHub Actions، GitLab CI، وغیرہ) میں ایک شیل سٹیپ (shell step) شامل کریں جو pg_is_in_recovery() کوئری کو چلائے۔ اگر یہ true واپس کرے، تو ڈیپلائمنٹ کو جلد روکنے کے لیے نان-زیرو (non-zero) اسٹیٹس کے ساتھ جاب سے باہر نکل جائیں۔
if psql $DATABASE_URL -c "SELECT pg_is_in_recovery()" | grep -q t; then
echo "Connected to replica – aborting migration"
exit 1
fi
5. مائیگریشن ٹول کے ری ٹرائیز (retries) کو ٹیون کریں
Flyway جیسے ٹولز اکثر خودکار طور پر کنکشنز کو دوبارہ کوشش (retry) کرتے ہیں۔ Flyway 9+ کے لیے flyway.connectRetries=0 سیٹ کریں۔ یہ ٹول کو بار بار ریپلیکا پر جانے سے روکتا ہے، جس کے نتیجے میں ریپلیکیشن لیگ (replication lag) بڑھ سکتا ہے اور وسائل ضائع ہو سکتے ہیں۔
آگے کیا نظر رکھنا ہے
- Replication lag metrics: بڑھتا ہوا لیگ اس بات کی نشاندہی کر سکتا ہے کہ مائیگریشن نے غلطی سے ریپلیکا کو نشانہ بنایا ہے، جس کی وجہ سے رائٹ آپریشنز کی لائن (queue) لگ گئی ہے۔
- Connection-pooler routing rules: اس بات کو یقینی بنائیں کہ پولر کنفیگریشنز مائیگریشن ٹریفک کو واضح طور پر پرائمری ہوسٹ کی طرف بھیجیں۔
- Role defaults after upgrades: ڈیٹا بیس اپ گریڈز بعض اوقات رول پیرامیٹرز کو ری سیٹ کر دیتے ہیں؛ بڑے ورژن کی تبدیلیوں کے بعد
default_transaction_read_onlyکا دوبارہ آڈٹ کریں۔
خلاصہ یہ کہ: ریڈ-اونلی ٹرانزیکشن ایرر شاذ و نادر ہی PostgreSQL کا بگ ہوتا ہے؛ یہ غلط نوڈ پر ٹریفک بھیجے جانے یا کسی رول کی غلط کنفیگریشن کی علامت ہے۔ نوڈ رول کی جانچ کر کے، مخصوص مائیگریشن اکاؤنٹس استعمال کر کے، اور اپنے CI/CD پائپ لائن کو مضبوط بنا کر، آپ مائیگریشن کو ہموار طریقے سے چلا سکتے ہیں اور ادھورے اسکیما سے بچ سکتے ہیں جو بعد میں ہونے والی ڈویلپمنٹ کو مفلوج کر دیتے ہیں۔
