PostgreSQL-ன் “cannot execute CREATE TABLE in a read-only transaction” பிழை தற்போது பல குழுக்களின் மைக்ரேஷன்களை (migrations) பாதித்து வருகிறது, இதனால் ஸ்கீமாக்கள் (schemas) பாதியிலேயே முடிவடைந்து மைக்ரேஷன் ஹிஸ்டரி டேபிள்கள் லாக் ஆகின்றன. பொதுவாக, ஒரு எழுத்து நடவடிக்கை (write operation) பிரைமரிக்கு (primary) பதிலாக ரெப்ளிகாவில் (replica) நடக்கும்போது இந்தத் தோல்வி ஏற்படுகிறது, இது Flyway, Liquibase, Django ORM போன்ற ஸ்கீமா மாற்றங்களைச் சார்ந்திருக்கும் கருவிகளை முடக்குகிறது.

இந்த பிழை ஏன் ஏற்படுகிறது

ஒரு டிரான்ஸாக்ஷன் (transaction) 'read-only' என்று குறிக்கப்பட்டால், PostgreSQL அனைத்து எழுத்துக்கள் (writes), ஸ்கீமா மாற்றங்கள் மற்றும் சீக்வென்ஸ் அப்டேட்களைத் (sequence updates) தடுத்துவிடுகிறது. மைக்ரேஷன் இத்தகைய நிலைக்குச் செல்லப் பொதுவான காரணங்கள்:

  • Connection poolers (எ.கா., PgBouncer) தவறுதலாக மைக்ரேஷன் இணைப்பை ஒரு 'read replica'-விற்கு அனுப்பும் போது.
  • Roles ที่ default_transaction_read_only அளவுரு (parameter) இயல்பாகவே on என்று அமைக்கப்பட்டிருக்கும் போது.
  • தனித்தனி ரீடர் (reader) மற்றும் ரைட்டர் (writer) URL-களைக் கொண்ட Cloud endpoints; ரீடர் URL-ஐப் பயன்படுத்துவது (AWS RDS அல்லது Aurora-வில் இருப்பது போல) எழுத்து நடவடிக்கைகளை ஒரு ரெப்ளிகாவிற்குத் திருப்பிவிடும்.

இந்த நிபந்தனைகளில் ஏதேனும் ஒன்று பொருந்தும்போது, மைக்ரேஷன் செயல்முறை அட்டவணைகளை உருவாக்கலாம், நெடுவரிசைகளைச் சேர்க்கலாம் அல்லது சீக்வென்ஸ்களைப் புதுப்பிக்கலாம், ஆனால் இறுதியில் அது முடங்கிப் போய், டேட்டாபேஸை பாதியிலேயே மைக்ரேஷன் செய்யப்பட்ட நிலையில் விட்டுவிடும்.

டிரான்ஸாக்ஷனுக்குள் உடனடித் தீர்வு

நீங்கள் ஏற்கனவே இந்தத் பிழையைச் சந்தித்துவிட்டால், தற்போதைய டிரான்ஸாக்ஷனுக்கான 'read-only' கொடியை (flag), மற்ற செஷன்களைப் (session) பாதிக்காமல் மாற்றியமைக்க முடியும்—ஒரு கனெக்ஷன் பூலர் (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-ஐப் பயன்படுத்தினால், அந்த மாற்றம் முழு செஷனுக்கும் நிலைத்திருக்கும், இது இயல்பாகவே 'read-only' தேவைப்படும் மற்ற செயல்பாடுகளைப் பாதிக்கும்.

தடுப்பு நடவடிக்கைகள்

1. நீங்கள் பிரைமரி நோடில் (primary node) இருப்பதை உறுதி செய்யவும்

மைக்ரேஷன் தொடங்குவதற்கு முன் ஒரு விரைவான சரிபார்ப்பைச் சேர்க்கவும்:

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

முடிவு REPLICA என்று வந்தால், மைக்ரேஷனை நிறுத்திவிடவும். pg_is_in_recovery() என்ற ஃபங்ஷன் (function) ஒரு ஸ்டாண்ட்பை (standby) சர்வரில் 'true' எனத் தரும், இதன் மூலம் நீங்கள் ஒரு 'read-only' நகலில் எழுத முயற்சிப்பதில்லை என்பதை உறுதி செய்யலாம்.

2. பிரத்யேக மைக்ரேஷன் ரோல்களைப் (migration roles) பயன்படுத்தவும்

இயல்பாகவே 'write-enabled' நிலையில் இருக்கும் ஒரு ரோலை உருவாக்கி, அதற்குத் தேவையான அதிகாரங்களை (privileges) மட்டும் வழங்கவும்:

  • இலக்கு டேட்டாபேஸில் (target database) CREATE அதிகாரம்.
  • மைக்ரேஷன் கருவி ஒரு செஷனைத் திறக்க CONNECT அதிகாரம்.

பொதுவான பயனர்களுக்கு அமைக்கப்படும் default_transaction_read_only = on என்ற பண்பை (attribute) இந்த ரோலுக்கு வழங்க வேண்டாம்.

3. உள்கட்டமைப்பை (infrastructure) ரைட்டர் எண்ட்பாயிண்டிற்கு (writer endpoint) திருப்பவும்

IaC ஸ்கிரிப்ட்களில் (Terraform, CloudFormation, போன்றவை), மைக்ரேஷன் ரன்னர்கள் (migration runners) ரீடர் எண்ட்பாயிண்டிற்குப் பதிலாக கிளஸ்டரின் writer எண்ட்பாயிண்டைப் பயன்படுத்துமாறு அமைக்கவும். ரைட்டர் எண்ட்பாயிண்ட் பிரைமரி நோடிற்குச் செல்லும், ஆனால் ரீடர் எண்ட்பாயிண்ட் எழுத்து நடவடிக்கைகளை நிராகரிக்கும் ஒரு ரெப்ளிகாவிற்குச் செல்லும்.

4. CI/CD கேட் (gate) ஒன்றைச் சேர்க்கவும்

உங்கள் பைப்லைனில் (GitHub Actions, GitLab CI, போன்றவை) pg_is_in_recovery() குவரியை (query) இயக்கும் ஒரு ஷெல் ஸ்டெப்பை (shell step) சேர்க்கவும். அது 'true' எனத் தந்தால், டிப்ளாய்மென்ட்டை (deployment) முன்கூட்டியே நிறுத்த, ஜாப்பை (job) ஒரு 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: அதிகரித்து வரும் லேக் (lag), மைக்ரேஷன் தவறுதலாக ஒரு ரெப்ளிகாவைத் தாக்கியிருக்கலாம் என்பதைக் குறிக்கலாம், இது எழுத்து முயற்சிகள் வரிசையில் (queue) தேங்குவதற்கு வழிவகுக்கும்.
  • Connection-pooler routing rules: பூலர் கட்டமைப்புகள் (pooler configurations) மைக்ரேஷன் டிராஃபிக்கை பிரைமரி ஹோஸ்டிற்குத் தெளிவாகத் திருப்பி அனுப்புவதை உறுதி செய்யவும்.
  • Role defaults after upgrades: டேட்டாபேஸ் அப்டேட்கள் சில நேரங்களில் ரோல் அளவுருக்களை (role parameters) ரீசெட் செய்யும்; முக்கிய பதிப்பு மாற்றங்களுக்குப் பிறகு default_transaction_read_only-ஐ மீண்டும் சரிபார்க்கவும்.

சுருக்கமாகச் சொன்னால்: 'read-only transaction' பிழை என்பது அரிதாகவே ஒரு PostgreSQL பிழையாக இருக்கும்; இது டிராஃபிக் தவறான நோடிற்கு அனுப்பப்படுவதன் அல்லது ஒரு ரோல் தவறாகக் கட்டமைக்கப்பட்டதன் அறிகுறியாகும். நோட் ரோலைச் சரிபார்ப்பதன் மூலமும், பிரத்யேக மைக்ரேஷன் கணக்குகளைப் பயன்படுத்துவதன் மூலமும், உங்கள் CI/CD பைப்லைனை வலுப்படுத்துவதன் மூலமும், மைக்ரேஷன்களைத் தடையின்றி நடத்தவும், அடுத்தகட்ட மேம்பாடுகளைப் பாதிக்கும் பாதியிலேயே முடிந்த ஸ்கீமாக்களைத் தவிர்க்கவும் முடியும்.