- Jepsen टेस्ट में यह पाया गया कि Amazon RDS for PostgreSQL Multi-AZ cluster सभी nodes के स्तर पर उपलब्ध सबसे मजबूत isolation level, Snapshot Isolation, का पालन नहीं करता
- मुख्य कारण यह है कि primary पर transactions के visible होने का क्रम in-memory lock से तय होता है, जबकि secondary WAL order का पालन करता है, जिससे दोनों क्रम अलग हो सकते हैं
- failure injection या failover के बिना,
gp3storage औरdb.m6id.largeinstance का उपयोग करने वाली conditions में भी लगभग 150 write TPS / 1600 read-only TPS पर हर कुछ मिनट में G-nonadjacent cycle दिखाई दिया - यह anomaly Long Fork के अंतर्गत आती है; AWS द्वारा समर्थित PostgreSQL 13.15 से 17.4 तक टेस्ट किए गए सभी versions में यह दिखी, जबकि Short Fork/Write Skew observe नहीं हुए
- safety-critical transactions में read-only secondary इस्तेमाल करने पर execution order अलग दिख सकता है, इसलिए केवल writer endpoint इस्तेमाल करने या कम-से-कम 1 write शामिल करने जैसे तरीकों की समीक्षा जरूरी है
Long Fork के कारण पर update
- AWS के Sergey Melnik और HN comment participants matashii, Ants Aasma ने PostgreSQL cluster में Long Fork के कारण की पहचान की
- PostgreSQL primary transactions को visible करने का क्रम in-memory lock से तय करता है
- secondary transactions को Write-Ahead Log(WAL) में उनके क्रम के अनुसार visible करता है
- अगर lock order और WAL order अलग हो जाएं, तो primary और secondary transactions का apparent order अलग-अलग देख सकते हैं
- यह behavior 2013 की PostgreSQL mailing list post में discuss किया गया था, और Melnik ने AWS blog पर PostgreSQL clusters और read replica में transaction visibility समझाने वाला लेख लिखा
- Jepsen का सुझाव है कि AWS और PostgreSQL इस issue को fix work के साथ document करें
RDS for PostgreSQL के isolation levels और architecture
- PostgreSQL एक general-purpose open source SQL database है, और MVCC के जरिए तीन transaction isolation levels देता है
Read UncommittedऔरRead Committedदोनों Read Committed की तरह behave करते हैंRepeatable Readवास्तव में Repeatable Read नहीं, बल्कि Snapshot Isolation देता हैSerializableSerializability देता है
- Amazon RDS for PostgreSQL managed PostgreSQL clusters उपलब्ध कराने वाली AWS service है
- provisioning, storage management, replication, backup, upgrades आदि automate करता है
- Multi-AZ deployments database nodes को कई availability zones में distribute करके correlated failures की संभावना घटाते हैं
- RDS synchronous replication का उपयोग करता है ताकि primary और कम-से-कम 1 secondary instance, दोनों पर transaction durability सुनिश्चित होने के बाद ही response दिया जाए
- users को PostgreSQL wire protocol बोलने वाले दो URL दिए जाते हैं
- primary endpoint: read-write transactions के लिए
- reader endpoint: read-only transactions के लिए
- primary endpoint सभी PostgreSQL isolation levels support करता है, लेकिन secondary Serializable support नहीं करता
- सभी nodes पर इस्तेमाल किया जा सकने वाला सबसे मजबूत isolation level वही Snapshot Isolation है, जिसे PostgreSQL
Repeatable Readकहता है
Test design
- Jepsen ने PostgreSQL के लिए test library को Amazon RDS for PostgreSQL के अनुरूप adjust किया और छोटा wrapper program इस्तेमाल किया
- हर test round में AWS के CreateDBCluster API से RDS cluster provision किया गया
- storage:
gp3 - instance:
db.m6id.large
- storage:
- test चलाने के लिए एक EC2 node launch किया गया और RDS cluster के main endpoint और read-only endpoint दिए गए
- कोई failure injection नहीं की गई और failover भी trigger नहीं किया गया
- मुख्य workload unique integer lists से जुड़े transactions से बना था
- हर list एक single row में store होती है और comma-separated values वाले
TEXTfield के रूप में encode होती है - transactions primary key से list पढ़ते हैं या
CONCATके जरिए unique integer को list में append करते हैं
- हर list एक single row में store होती है और comma-separated values वाले
- इस workload के जरिए Elle checker transactions के बीच data-flow dependencies infer कर सकता है और graph cycles खोजकर विभिन्न isolation levels verify कर सकता है
G-nonadjacent cycle का observation
- normal conditions और medium concurrency पर भी Amazon RDS for PostgreSQL 17.4 हर कुछ मिनट में G-nonadjacent cycle दिखाता है
- एक 2-minute test run ने लगभग 150 write TPS और 1600 read-only TPS किए, और उसमें 4 transactions वाला cycle शामिल था
- example cycle चार transactions
T1,T2,T3,T4से बना थाT1ने row 89 में9append करके list[4 9]बनाई, औरT2ने इसे observe कियाT3ने row 90 में11append करके list[11]बनाईT4ने row 90 में3append किया और result list[11, 3]पढ़ी, जिससेT3का version overwrite हुआT2ने row 89 मेंT1का append observe किया, लेकिन row 90 मेंT3का append नहीं देखा- इसके विपरीत,
T4ने row 90 मेंT3का append observe किया, लेकिन row 89 मेंT1का append miss कर दिया
- चूंकि इस cycle में non-adjacent read-write dependency शामिल है, यह Snapshot Isolation violation वाला G-nonadjacent cycle है
- standard PostgreSQL के
Repeatable Readमें ऐसा behavior नहीं होना चाहिए, और Jepsen ने standard PostgreSQL में इसे observe नहीं किया
Snapshot Isolation से टकराव क्यों होता है
- Snapshot Isolation में हर transaction ऐसा दिखना चाहिए जैसे वह start timestamp
sके समय के database snapshot पर operate कर रहा हो - transaction के effects बाद के commit timestamp
cपर अन्य transactions को visible होते हैं - example cycle के observations को timestamp relationships के रूप में लिखें तो वे परस्पर विरोधी हैं
- चूंकि
T2नेT1का append पढ़ा,T2की startT1के commit के बाद होनी चाहिए:c1 < s2 - चूंकि
T2नेT3का append observe नहीं किया,s2 < c3 - चूंकि
T4नेT3को overwrite और observe किया,c3 < s4 - चूंकि
T4नेT1का append observe नहीं किया,s4 < c1
- चूंकि
- ये relationships सभी साथ-साथ true नहीं हो सकते, इसलिए Snapshot Isolation के timestamp model से conflict करते हैं
Long Fork और version-wise results
- यह cycle Long Fork का भी example है
- पहला और दूसरा transaction एक logical state fork बनाते हैं
- तीसरा और चौथा transaction दूसरा fork बनाते हैं
- दोनों forks अलग-अलग rows update करते हैं, लेकिन एक-दूसरे के effects observe नहीं करते
- Short Fork, यानी Write Skew, observe नहीं हुआ
- यह result संकेत देता है कि Amazon RDS for PostgreSQL, Snapshot Isolation से थोड़ा कमजोर Parallel Snapshot Isolation दे सकता है
- G-nonadjacent anomalies अलग-अलग रूपों में दिखीं—सिर्फ write-read edges से connected cases से लेकर 4 से ज्यादा transactions वाले cases तक
- AWS द्वारा support किए गए सबसे पुराने version PostgreSQL 13.15 से लेकर latest version 17.4 तक, test किए गए सभी versions में इसी प्रकार की anomalies हुईं
users को क्या जांचना चाहिए
- Long Fork और अन्य G-nonadjacent cycles मौजूद होने के कारण Amazon RDS for PostgreSQL Multi-AZ clusters Snapshot Isolation की guarantee नहीं देते
- इस लिहाज से RDS for PostgreSQL Multi-AZ clusters, पिछले Jepsen tests में Strong Snapshot Isolation देने वाले दिखे single-node PostgreSQL की तुलना में कमजोर safety semantics देते हैं
- users को देखना चाहिए कि उनकी transaction structure Long Fork के प्रति vulnerable है या नहीं, या intended invariants बनी रहती हैं या नहीं, इसे experiments से verify कर सकते हैं
- read transactions, transaction execution order के बारे में दूसरे transactions की तुलना में अलग results देख सकते हैं
- anomalies read-only secondary पर queries से जुड़ी लगती हैं, इसलिए निम्न तरीकों से Snapshot Isolation recover करने की संभावना हो सकती है
-
केवल writer endpoint इस्तेमाल करें
- सभी safety-critical transactions में कम-से-कम 1 write शामिल करें
- Jepsen का verification एक experimental approach है; यह bugs की मौजूदगी साबित कर सकता है, लेकिन उनकी अनुपस्थिति साबित नहीं कर सकता
- यह report RDS for PostgreSQL behavior की detailed investigation नहीं, बल्कि preliminary exploration का परिणाम है
-
1 टिप्पणियां
Hacker News की राय
अच्छा होगा अगर software दुनिया की लिखाई अक्सर इसी तरह हो: “Amazon RDS for PostgreSQL, PostgreSQL database के managed instances उपलब्ध कराने वाली Amazon Web Services(AWS) service है। हम दिखाते हैं कि Amazon RDS for PostgreSQL multi-AZ cluster सभी endpoints पर supported सबसे मजबूत consistency model, यानी snapshot isolation, का उल्लंघन करता है…”
सीधा, मुद्दे की बात, बिना सजावट—दूसरे STEM क्षेत्रों में research results share करने के तरीके जैसा। एक समय था जब memes से समझाने वाले witty blog posts पसंद थे, लेकिन अब plain और simple लिखाई की कमी महसूस होती है
जब बहुत deep technical posts लिखता था तो likes और comments लगभग नहीं आते थे, यहाँ तक कि एक Staff Engineer ने कहा था कि “audience को और narrow रखना बेहतर होगा।” इसके उलट, शुरुआती Kubecost को test करते हुए मैंने लिखा कि recommendations से cost saving कम होती है और वे container performance issues पैदा कर सकती हैं; वह CPU throttling और cgroups पर काफी technical post थी, फिर भी उसमें memes डालने पर लोगों ने उसे बहुत पसंद किया
बाद में C में एक छोटी Python external library बनाकर ctypes से access करने और stack/heap allocation की तुलना करने वाली एक और dry post में भी memes डाले, तो नतीजा वैसा ही रहा। मुझे यह trend पसंद नहीं है, लेकिन broad readership तक पहुँचना हो तो इससे बचने का कोई अच्छा तरीका भी नहीं दिखता। Jensen ने वैसे readers को target नहीं किया था, और rigorous व pure writing तारीफ के काबिल है
Title में भी नहीं है और post में भी बहुत साफ नहीं है, लेकिन यह problem RDS के अपेक्षाकृत नए feature multi-AZ cluster तक सीमित है। यह उस multi-AZ instance से अलग है जिससे कई लोग familiar हैं
multi-AZ instance पुराना feature है जिसमें primary DB को दूसरे availability zone के secondary DB पर synchronously replicate किया जाता है, और primary fail होने पर RDS secondary पर failover करता है
multi-AZ cluster में दो secondary होते हैं और transaction उनमें से कम-से-कम एक पर synchronously replicate होता है। किसी एक secondary के fail होने या performance गिरने पर यह multi-AZ instance से ज्यादा robust होता है, और secondary पर read-only access भी संभव है
हालांकि multi-AZ cluster के अंदर PostgreSQL की basic functionality से अलग कुछ extra magic जरूर होगा, और शायद इसी वजह से Jepsen test में fail हुआ
हालांकि PostgreSQL में अभी भी एक flaw है जो इस pattern जैसी problem को possible बनाता है। अगर client commit के दौरान गायब हो जाए तो non-replicated transaction तुरंत visible हो जाता है। Example में T1 isolated leader पर होता है और commit के दौरान connection टूट जाता है, T2 भी isolated node पर होता है, और T3/T4 बाद में नए leader पर होते हैं, तो वही result दिख सकता है। लेकिन यह इस explanation से अच्छी तरह match नहीं करता कि इस test में fault injection नहीं किया गया था
Edit: मैंने यह नहीं देखा था कि replica और primary node के commit order mismatch से यह pattern explain होता है। मैंने कभी इस problem को fix करने का तरीका present किया था, इसलिए थोड़ा embarrassing है
अच्छी जाँच है। आजकल software developers में कई लोग transaction को ही ठीक से नहीं जानते, और अलग-अलग transaction models के बारे में तो और भी कम जानते हैं। यहां तक कि “senior developer” कहे जाने वाले CRUD developers में भी मैंने ऐसे लोग देखे हैं जिन्हें database transaction की बिल्कुल जानकारी नहीं थी
असल में जब traffic का scale होता है और software कोई मामूली समस्या नहीं सुलझा रहा होता, तब transactions और transaction models performance और error-free code के लिए बहुत अहम होते हैं
उदाहरण के लिए, एक बड़े project में काफी analysis के बाद SQL Server के default Read Committed से Read Committed Snapshot Isolation पर switch किया गया, और lock contention काफी हद तक खत्म हो गया, जिससे users बहुत खुश हुए। उस project के software engineers transactions का बहुत इस्तेमाल कर रहे थे, लेकिन basics सिखाने से पहले उन्हें transaction models या locks के बारे में बिल्कुल पता नहीं था
मैं मुख्य रूप से retail sector में काम करता रहा हूँ, इसलिए race conditions जैसी errors से भरे systems अक्सर देखता हूँ; इसलिए और अफसोस होता है क्योंकि ऐसे isolation levels वहाँ बहुत मदद कर सकते हैं
हालांकि ऐसे मामले मैंने ज्यादातर startup engineers में देखे, और बड़ी कंपनियों के typical Oracle/MSSQL developers को मैं काफी ऊँचा मानता हूँ, क्योंकि कम से कम उनकी basics सही होती हैं
अपने career में कुछ बार मैंने देखा है कि यह तरीका सचमुच बहुत खराब नतीजे देता है
हालांकि इस transition में ध्यान रखने वाली बात यह है कि blocking reads पर निर्भर हर code टूट जाएगा। उदाहरण के लिए
select with existsजैसा code explicit locks या किसी और तरीके से फिर से लिखना होगापिछली company में जब backup script के
pg_dumpcommand को बदलकर parallel workers (-jflag) इस्तेमाल करना शुरू किया, तो restore के दौरान कभी-कभार duplicate key errors और foreign key constraint errors जैसे inconsistency का संकेत देने वाले errors दिखेउस समय AWS और PostgreSQL mailing list में report करने की कोशिश की, लेकिन आसानी से reproduce नहीं कर सके, इसलिए कोई progress नहीं हुई, और आखिरकार छोड़कर single-thread dump पर वापस लौट गए। सोचता हूँ उस समय जो phenomenon देखा था, क्या वह इस issue से जुड़ा था
इस article को पढ़ने पर लगता है कि real impact यह है कि जब उसी row पर write के तुरंत बाद जल्दी read होता है, तो stale data return हो सकता है। write transaction completed के रूप में marked होता है, लेकिन multi-AZ RDS instance की distributed layer पूरी तरह update होने से पहले अगर उसी row को तुरंत read किया जाए, तो row अभी मौजूद न हो सकती है या columns पूरी तरह update न हुए हों, इसलिए पुराना value आ सकता है
PostgreSQL के snapshot mechanism को देखते हुए, इसका मतलब यह नहीं लगता कि multi-byte column type के सिर्फ कुछ bytes update होकर कोई बेतुका value read होगा
अंततः यह समय के साथ converge होने वाली race condition जैसा दिखता है। या फिर क्या किसी ने इसे इस अर्थ में पढ़ा है कि “long fork” की बाद की transactions सामान्य हालात में भी हमेशा के लिए complete नहीं हो सकतीं?
“यह काम Jepsen ने बिना compensation के independently किया” वाली line ऐसी चीज है जिसे RDBMS stakeholder अच्छे दिन में भी नहीं देखना चाहेगा। अंदर चिंता भरे कुछ emails इधर-उधर गए होंगे। हमेशा की तरह aphyr को सलाम
यह पूरी तरह स्पष्ट नहीं है कि यह multi-instance upstream PostgreSQL क्लस्टर में समस्या है या नहीं। क्या यह समझना सही होगा कि AWS क्लस्टर सेटिंग में कुछ करता है, या उसने ऐसा पैच जोड़ा है जो यह व्यवहार पैदा करता है?
PostgreSQL replication में आम तौर पर कई तरीके होते हैं और नतीजे भी अलग-अलग होते हैं। उदाहरण के लिए Bin Wang की Patroni रिपोर्ट है: https://www.binwang.me/2024-12-02-PostgreSQL-High-Availabili...
यहां भी जो पाया गया वह यह है कि PostgreSQL फिलहाल primary node और replicas के बीच consistent snapshot behavior नहीं देता। शायद read-only transaction T2 secondary node पर चला था, और modifying transactions T1/T3/T4 primary node पर चले थे।
बैकग्राउंड देखें तो secondary PostgreSQL node का snapshot यह तय करते समय कि कौन-से transactions दिखेंगे, transaction persistence order, यानी WAL में commit record की position, पर निर्भर करता है। वहीं primary node में visibility order इस बात से तय होता है कि उस transaction को accept करने वाले backend को पहली बार कब सूचना मिली कि transaction पूरी तरह commit हो गया है, और उसके बाद कब वह commit mark करता है।
primary node और secondary node, दोनों में जुड़े हुए backends के बीच commit order consistent रहता है, लेकिन primary और secondary के बीच commit order कुछ अलग हो सकता है। इसे सुधारने पर काम चल रहा है, लेकिन अभी यह बहुत work-in-progress stage में है।
लगता है AWS ने PostgreSQL को patch करके दो instances में replicate किया है, और दोनों में से कोई एक change confirm कर दे तो उसे पर्याप्त मानता है। यह confirmation कब होता है, इसकी public जानकारी नहीं है।
निजी तौर पर मुझे लगता है कि PostgreSQL के लिए drbd जैसी filesystem-level replication बेहतर है। पुराने तरीके वाले AWS Multi-AZ instances शायद यही तरीका इस्तेमाल करते होंगे। हालांकि throughput कम हो जाता है और secondary instance से read नहीं कर सकते।
खासकर यह point: https://youtu.be/fLqJXTOhUg4?t=434
submit किए गए title में core सवाल है। RDS for PostgreSQL 17.4 snapshot isolation को ठीक से implement नहीं करता।
title system पर बहुत कठोर है या बहुत friendly, मिली हुई दर्जन-भर समस्याओं में सबसे meaningful चीज बताता है या नहीं, database safety results का honest broker बनने की Jepsen की कसौटी पर fair है या नहीं, और 10 साल बाद जब लोग इसे link करते रहेंगे लेकिन यह recent versions पर लागू नहीं रहेगा तो इसे कैसे समझा जाएगा—ऐसी discussions काफी तीखी हो सकती हैं।
कुछ frustrating attempts के बाद, अब सभी report titles को “Jepsen: ” के रूप में लगाने की policy से इस समस्या से बचा जाता है। अगर HN को ज्यादा explanatory या ज्यादा colorful link text चाहिए, तो वे बेशक खुद चुन सकते हैं।
फिर भी, यह transaction guarantees के Chuck Norris जैसे Kyle Kingsbury ने लिखा है, इसलिए AWS को जवाब देना या clarification देना चाहिए। भले ही यह PostgreSQL के लिए RDS की दो options में से सिर्फ एक, multi-AZ cluster, पर लागू होता दिखे। Multi-AZ deployment में एक standby DB instance हो सकता है या दो, और यहां बात दो standby DB instances वाले configuration की है।
AWS docs में ऐसा कोई वादा नहीं है। RDS का 5494-page manual भी हर engine के parameter docs में isolation या serializable का बस थोड़ा-बहुत ही जिक्र करता है।
Multi-AZ cluster की global read consistency के बारे में भी कुछ नहीं है। कहा जाता है कि semi-synchronous replication है, इसलिए writer एक standby के log record acknowledgement का इंतजार करता है, लेकिन दो readers अलग-अलग snapshots पर हो सकते हैं।
[1] - "New Amazon RDS for MySQL & PostgreSQL Multi-AZ Deployment Option: Improved Write Performance & Faster Failover" - https://aws.amazon.com/blogs/aws/amazon-rds-multi-az-db-clus...
[2] - "Amazon RDS Multi-AZ with two readable standbys: Under the hood" - https://aws.amazon.com/blogs/database/amazon-rds-multi-az-wi...
अगर developer ने snapshot isolation मान लिया हो, लेकिन Amazon RDS for PostgreSQL असल में सिर्फ़ parallel snapshot isolation देता हो, तो खासकर read replica endpoint इस्तेमाल करने वाले multi-AZ setup में कौन-से safety bugs या application-level bugs पैदा हो सकते हैं, यह जानने की उत्सुकता है
git pushजैसे flow के बारे में सोच सकते हैं। transaction शुरू करना, current state पढ़ना, expected state से match करता है या नहीं यह check करना, नई state लिखना, और नई state hash के साथ commit करना। बदकिस्मत स्थिति में ऐसा commit hash बन सकता है जो किसी भी valid state से match न करेसमस्या यह है कि ऐसी चीज़ों का reasoning करना मुश्किल होता है, जिससे उन्हें avoid करना भी मुश्किल हो जाता है। इसलिए सबसे आसान समाधान शायद यह होगा कि अगर write, read पर conditional है, तो “सिर्फ़ writer endpoint इस्तेमाल करने से snapshot isolation शायद वापस मिल सके”
हालांकि यह हैरानी की बात है कि “सिर्फ़ writer endpoint इस्तेमाल” वाला तरीका, खासकर availability loss की स्थिति में, test नहीं किया गया है
User1 comment करता है, फिर User2 comment करता है, उसके बाद User1 एक अलग transaction में check करता है कि सिर्फ़ 1 comment है और badge पा लेता है। User2 भी अलग transaction में वही check करता है और उसे सिर्फ़ अपना 1 comment दिखता है, इसलिए वह भी badge पा सकता है
snapshot isolation में यह संभव नहीं है। अलग-अलग transactions में से कम से कम एक को 2 comments दिखने चाहिए
parallel snapshots पर मूल paper भी पढ़ने लायक है: https://scispace.com/pdf/transactional-storage-for-geo-repli...
“यह phenomenon test किए गए सभी versions, 13.15 से 17.4 तक, में हुआ” वाली line देखकर मुझे चिंता हुई कि क्या major version upgrade करना गलत choice था, लेकिन लगता है ऐसा नहीं है। यह regression नहीं, बल्कि feature request या पुराने bug के ज़्यादा करीब है