PostgreSQL-ലെ “cannot execute CREATE TABLE in a read-only transaction” എന്ന എറർ ഇപ്പോൾ പല ടീമുകളുടെയും മൈഗ്രേഷനുകളെ തടസ്സപ്പെടുത്തുന്നു, ഇത് സ്കീമകൾ പകുതി മാത്രം നടപ്പിലാക്കാനും മൈഗ്രേഷൻ ഹിസ്റ്ററി ടേബിളുകൾ ലോക്ക് ചെയ്യപ്പെടാനും കാരണമാകുന്നു. പ്രൈമറിക്ക് പകരം ഒരു റെപ്ലിക്കയിൽ (replica) ഒരു റൈറ്റ് ഓപ്പറേഷൻ (write operation) നടക്കുമ്പോഴാണ് സാധാരണയായി ഈ പരാജയം സംഭവിക്കുന്നത്. ഇത് Flyway, Liquibase, Django ORM തുടങ്ങിയ സ്കീമ മാറ്റങ്ങളെ ആശ്രയിക്കുന്ന ടൂളുകളെ തടസ്സപ്പെടുത്തുന്നു.
എറർ വരാനുള്ള കാരണങ്ങൾ
ഒരു ട്രാൻസാക്ഷൻ (transaction) റീഡ്-ഒൺലി (read-only) ആയി അടയാളപ്പെടുമ്പോൾ PostgreSQL എല്ലാ റൈറ്റുകളും (writes), സ്കീമ മാറ്റങ്ങളും (schema alterations), സീക്വൻസ് അപ്ഡേറ്റുകളും (sequence updates) നിർത്തലാക്കുന്നു. മൈഗ്രേഷൻ ഇത്തരമൊരു അവസ്ഥയിൽ എത്തുന്നതിനുള്ള പ്രധാന കാരണങ്ങൾ ഇവയാണ്:
- Connection poolers (ഉദാഹരണത്തിന്, PgBouncer) അബദ്ധവശാൽ മൈഗ്രേഷൻ കണക്ഷൻ ഒരു റീഡ് റെപ്ലിക്കയിലേക്ക് (read replica) അയക്കുന്നു.
- Roles ដែល
default_transaction_read_onlyഎന്ന പാരാമീറ്റർ ഡിഫോൾട്ടായി on എന്ന് സെറ്റ് ചെയ്തിരിക്കുന്നു. - റീഡർ (reader), റൈറ്റർ (writer) എന്നിവയ്ക്കായി പ്രത്യേക URL-കൾ നൽകുന്ന Cloud endpoints; റീഡർ URL ഉപയോഗിക്കുന്നത് (AWS RDS അല്ലെങ്കിൽ Aurora-യിൽ സാധാരണയായി കണ്ടുവരുന്നത് പോലെ) റൈറ്റുകളെ ഒരു റെപ്ലിക്കയിലേക്ക് തിരിച്ചുവിടുന്നു.
ഈ സാഹചര്യങ്ങളിൽ ഏതെങ്കിലും നിലവിലുണ്ടെങ്കിൽ, മൈഗ്രേഷൻ പ്രക്രിയ ടേബിളുകൾ നിർമ്മിക്കാനോ, കോളങ്ങൾ ചേർക്കാനോ, അല്ലെങ്കിൽ സീക്വൻസുകൾ അപ്ഡേറ്റ് ചെയ്യാനോ ശ്രമിക്കുമ്പോൾ തടസ്സപ്പെടുകയും, ഡാറ്റാബേസ് പകുതി മാത്രം മൈഗ്രേറ്റ് ചെയ്ത അവസ്ഥയിൽ അവശേഷിക്കുകയും ചെയ്യുന്നു.
ട്രാൻസാക്ഷനുള്ളിലെ ഉടനടിയുള്ള പരിഹാരം
നിങ്ങൾക്ക് ഈ എറർ നേരത്തെ തന്നെ നേരിടേണ്ടി വന്നിട്ടുണ്ടെങ്കിൽ, ബാക്കി സെഷനെ (session) ബാധിക്കാതെ തന്നെ നിലവിലെ ട്രാൻസാക്ഷന് വേണ്ടി റീഡ്-ഒൺലി ഫ്ലാഗ് (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() എന്ന ഫങ്ക്ഷൻ ഒരു സ്റ്റാൻഡ്ബൈ സെർവറിൽ (standby server) 'true' എന്ന് നൽകുന്നു, ഇത് നിങ്ങൾ ഒരു റീഡ്-ഒൺലി കോപ്പിയിലേക്ക് എഴുതാൻ ശ്രമിക്കുന്നില്ലെന്ന് ഉറപ്പാക്കുന്നു.
2. മൈഗ്രേഷനായി മാത്രമുള്ള റോൾസ് (Roles) ഉപയോഗിക്കുക
ഡിഫോൾട്ട് ട്രാൻസാക്ഷൻ മോഡ് റൈറ്റ്-എനേബിൾ (write-enabled) ആയ ഒരു റോൾ നിർമ്മിക്കുകയും, അതിന് ആവശ്യമായ അവകാശങ്ങൾ (privileges) മാത്രം നൽകുകയും ചെയ്യുക:
- ടാർഗെറ്റ് ഡാറ്റാബേസിൽ
CREATEഅവകാശം. - മൈഗ്രേഷൻ ടൂളിന് ഒരു സെഷൻ തുറക്കാൻ
CONNECTഅവകാശം.
സാധാരണ ഉപയോക്താക്കൾക്കായി ചിലപ്പോൾ നൽകുന്ന default_transaction_read_only = on എന്ന അറ്റ്രിബ്യൂട്ട് ഈ റോളിന് നൽകുന്നത് ഒഴിവാക്കുക.
3. ഇൻഫ്രാസ്ട്രക്ചറിനെ റൈറ്റർ എൻഡ്പോയിന്റിലേക്ക് (writer endpoint) തിരിച്ചുവിടുക
IaC സ്ക്രിപ്റ്റുകളിൽ (Terraform, CloudFormation, മുതലായവ), മൈഗ്രേഷൻ റണ്ണറുകൾ റീഡർ എൻഡ്പോയിന്റിന് പകരം ക്ലസ്റ്ററിന്റെ writer എൻഡ്പോയിന്റ് ഉപയോഗിക്കാൻ കോൺഫിഗർ ചെയ്യുക. റൈറ്റർ എൻഡ്പോയിന്റ് പ്രൈമറി നോഡിലേക്ക് കണക്ട് ചെയ്യുമ്പോൾ, റീഡർ എൻഡ്പോയിന്റ് റൈറ്റുകൾ നിരസിക്കുന്ന ഒരു റെപ്ലിക്കയിലേക്കാണ് കണക്ട് ചെയ്യുന്നത്.
4. ഒരു CI/CD ഗേറ്റ് (gate) ചേർക്കുക
നിങ്ങളുടെ പൈപ്പ്ലൈനിൽ (GitHub Actions, GitLab CI, മുതലായവ) pg_is_in_recovery() ക്വറി പ്രവർത്തിപ്പിക്കുന്ന ഒരു ഷെൽ സ്റ്റെപ്പ് (shell step) ഉൾപ്പെടുത്തുക. ഇത് 'true' എന്ന് നൽകിയാൽ, ഡിപ്ലോയ്മെന്റ് നേരത്തെ നിർത്തുന്നതിനായി നോൺ-സീറോ സ്റ്റാറ്റസോടെ (non-zero status) ജോബ് അവസാനിപ്പിക്കുക.
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 പോലുള്ള ടൂളുകൾ പലപ്പോഴും കണക്ഷനുകൾ ഓട്ടോമാറ്റിക്കായി റീട്രൈ ചെയ്യാറുണ്ട്. Flyway 9+ ഉപയോഗിക്കുമ്പോൾ flyway.connectRetries=0 എന്ന് സെറ്റ് ചെയ്യുക. ഇത് ടൂൾ വീണ്ടും വീണ്ടും ഒരു റെപ്ലിക്കയിലേക്ക് കണക്ട് ചെയ്യുന്നത് ഒഴിവാക്കും; അല്ലാത്തപക്ഷം ഇത് റെപ്ലിക്കേഷൻ ലാഗിന് (replication lag) കാരണമാവുകയും വിഭവങ്ങൾ പാഴാക്കുകയും ചെയ്യും.
ശ്രദ്ധിക്കേണ്ട കാര്യങ്ങൾ
- Replication lag metrics: വർദ്ധിച്ചുവരുന്ന ലാഗ് (lag), മൈഗ്രേഷൻ അബദ്ധവശാൽ ഒരു റെപ്ലിക്കയെ ലക്ഷ്യം വെച്ചതാകാം എന്നതിന്റെ സൂചനയാകാം, ഇത് റൈറ്റ് ശ്രമങ്ങൾ ക്യൂ (queue) ചെയ്യപ്പെടാൻ കാരണമാകുന്നു.
- Connection-pooler routing rules: മൈഗ്രേഷൻ ട്രാഫിക് പ്രൈമറി ഹോസ്റ്റിലേക്ക് തന്നെ പോകുന്നുണ്ടെന്ന് പൂളർ കോൺഫിഗറേഷനുകൾ ഉറപ്പാക്കുന്നുണ്ടെന്ന് പരിശോധിക്കുക.
- Role defaults after upgrades: ഡാറ്റാബേസ് അപ്ഗ്രേഡുകൾ ചിലപ്പോൾ റോൾ പാരാമീറ്ററുകൾ റീസെറ്റ് ചെയ്തേക്കാം; പ്രധാനപ്പെട്ട വേർഷൻ മാറ്റങ്ങൾക്ക് ശേഷം
default_transaction_read_onlyവീണ്ടും പരിശോധിക്കുക.
ചുരുക്കത്തിൽ: ഒരു റീഡ്-ഒൺലി ട്രാൻസാക്ഷൻ എറർ എന്നത് അപൂർവ്വമായി മാത്രമേ ഒരു PostgreSQL ബഗ് ആയി വരുന്നുള്ളൂ; അത് ട്രാഫിക് തെറ്റായ നോഡിലേക്ക് അയക്കുന്നതിന്റെയോ അല്ലെങ്കിൽ ഒരു റോൾ തെറ്റായി കോൺഫിഗർ ചെയ്തതിന്റെയോ ലക്ഷണമാണ്. നോഡ് റോൾ പരിശോധിക്കുന്നതിലൂടെയും, മൈഗ്രേഷനായി മാത്രമുള്ള അക്കൗണ്ടുകൾ ഉപയോഗിക്കുന്നതിലൂടെയും, നിങ്ങളുടെ CI/CD പൈപ്പ്ലൈൻ കൂടുതൽ സുരക്ഷിതമാക്കുന്നതിലൂടെയും മൈഗ്രേഷനുകൾ സുഗമമായി മുന്നോട്ട് കൊണ്ടുപോകാനും ഡെവലപ്മെന്റിനെ തടസ്സപ്പെടുത്തുന്ന പകുതി മാത്രം നടപ്പിലാക്കിയ സ്കീമകൾ ഒഴിവാക്കാനും നിങ്ങൾക്ക് സാധിക്കും.
