PostgreSQL चा “cannot execute CREATE TABLE in a read-only transaction” हा एरर आता अनेक टीम्सच्या migrations मध्ये अडथळा निर्माण करत आहे, ज्यामुळे schemas अर्धवट लागू होतात आणि migration history tables लॉक होतात. ही त्रुटी सहसा तेव्हा येते जेव्हा write operation primary ऐवजी replica वर जाते, आणि यामुळे Flyway, Liquibase, Django ORM आणि तत्सम schema बदलंवर अवलंबून असलेल्या कोणत्याही टूलला अडथळा येतो.

ही त्रुटी का येते

जेव्हा एखादे transaction read-only म्हणून मार्क केले जाते, तेव्हा PostgreSQL सर्व writes, schema alterations आणि sequence updates अक्षम (disable) करते. Migration अशा स्थितीत जाण्याची सर्वात सामान्य कारणे खालीलप्रमाणे आहेत:

  • Connection poolers (उदा. PgBouncer) जे चुकून migration connection read replica कडे पाठवतात.
  • Roles ज्यामध्ये default_transaction_read_only पॅरामीटर बाय डिफॉल्ट on सेट केलेला असतो.
  • Cloud endpoints जे वेगळे reader आणि writer URLs उपलब्ध करून देतात; reader URL वापरल्यामुळे (जसे की AWS RDS किंवा Aurora मध्ये सामान्य आहे) writes एका replica कडे वळवले जातात.

जेव्हा यापैकी कोणतीही अट लागू होते, तेव्हा migration प्रक्रिया टेबल्स तयार करू शकते, कॉलम्स जोडू शकते किंवा sequences अपडेट करू शकते, परंतु शेवटी ती अडखळते आणि डेटाबेस अर्धवट migrated स्थितीत राहतो.

Transaction मध्ये त्वरित उपाय

जर तुम्हाला आधीच हा एरर आला असेल, तर तुम्ही उर्वरित session वर परिणाम न करता सध्याच्या transaction साठी read-only flag override करू शकता—जेव्हा connection pool इतर कामासाठी त्याच session चा पुनर्वापर करते तेव्हा हे अत्यंत महत्त्वाचे असते.

BEGIN;
SET LOCAL default_transaction_read_only = off;
SET TRANSACTION READ WRITE;
CREATE TABLE orders (id SERIAL PRIMARY KEY, total NUMERIC);
COMMIT;

SET LOCAL हे सेटिंग केवळ transaction च्या कालावधीसाठी बदलते. साधे SET वापरल्यास बदल संपूर्ण session साठी कायम राहतील, ज्यामुळे ज्या ऑपरेशन्सना read-only default ची आवश्यकता आहे ती बिघडू शकतात.

प्रतिबंधात्मक उपाय

1. तुम्ही primary node वर आहात याची खात्री करा

कोणतीही migration चालण्यापूर्वी एक जलद तपासणी (check) जोडा:

SELECT CASE WHEN pg_is_in_recovery() THEN 'REPLICA' ELSE 'PRIMARY' END;

जर निकाल REPLICA असेल, तर migration थांबवा (abort करा). pg_is_in_recovery() हे function standby server वर true रिटर्न करते, ज्यामुळे तुम्ही read-only कॉपीमध्ये लिहिण्याचा प्रयत्न करत नाही याची खात्री मिळते.

2. समर्पित (dedicated) migration roles वापरा

असा role तयार करा ज्याचा default transaction mode write-enabled असेल आणि त्याला फक्त आवश्यक असलेले अधिकार (privileges) द्या:

  • target database वर CREATE अधिकार.
  • migration tool ला session उघडण्यासाठी CONNECT अधिकार.

या role ला default_transaction_read_only = on हे attribute देणे टाळा, जे कधीकधी सामान्य वापराच्या (general-purpose) users साठी सेट केलेले असते.

3. Infrastructure ला writer endpoint कडे निर्देशित करा

IaC scripts (Terraform, CloudFormation, इ.) मध्ये, migration runners ला cluster च्या writer endpoint चा वापर करण्यासाठी कॉन्फिगर करा, reader endpoint चा नाही. Writer endpoint primary node कडे निर्देशित होतो, तर reader endpoint एका replica कडे निर्देशित होतो जो writes ना नकार देईल.

4. CI/CD gate जोडा

तुमच्या pipeline मध्ये (GitHub Actions, GitLab CI, इ.) एक shell step समाविष्ट करा जो pg_is_in_recovery() query चालवेल. जर ते true रिटर्न करत असेल, तर deployment लवकर थांबवण्यासाठी non-zero status सह job मधून बाहेर पडा (exit करा).

if psql $DATABASE_URL -c "SELECT pg_is_in_recovery()" | grep -q t; then
  echo "Connected to replica – aborting migration"
  exit 1
fi

5. Migration-tool retries ट्यून करा

Flyway सारखी टूल्स अनेकदा connections आपोआप retry करतात. Flyway 9+ साठी flyway.connectRetries=0 सेट करा. यामुळे टूल वारंवार replica कडे न जाता थांबते, अन्यथा यामुळे replication lag वाढू शकतो आणि संसाधनांचा (resources) अपव्यय होऊ शकतो.

पुढे काय लक्ष ठेवायचे

  • Replication lag metrics: वाढणारा lag हे सूचित करू शकतो की migration चुकून replica ला लक्ष्यित केले गेले आहे, ज्यामुळे write attempts रांगेत (queue) राहतात.
  • Connection-pooler routing rules: pooler configurations migration traffic स्पष्टपणे primary host कडे वळवत आहेत याची खात्री करा.
  • Role defaults after upgrades: Database upgrades कधीकधी role parameters रीसेट करतात; major version बदलल्यानंतर default_transaction_read_only पुन्हा ऑडिट करा.

थोडक्यात सांगायचे तर: read-only transaction error हा क्वचितच PostgreSQL चा bug असतो; तो चुकीच्या node कडे traffic पाठवला जाण्याचे किंवा role चुकीच्या पद्धतीने कॉन्फिगर केल्याचे लक्षण आहे. Node role तपासून, समर्पित migration accounts वापरून आणि तुमची CI/CD pipeline मजबूत करून, तुम्ही migrations सुरळीत चालू ठेवू शकता आणि अर्धवट लागू झालेल्या schemas टाळू शकता जे पुढील (downstream) डेव्हलपमेंटमध्ये अडथळा आणतात.