- การทดสอบของ Jepsen พบกรณีที่คลัสเตอร์ Amazon RDS for PostgreSQL Multi-AZ ไม่รักษา Snapshot Isolation ซึ่งเป็นระดับ isolation ที่แข็งแกร่งที่สุดเมื่อพิจารณาทุก node
- สาเหตุหลักคือ ลำดับที่ transaction บน primary ถูกทำให้มองเห็นได้นั้นกำหนดด้วย lock ในหน่วยความจำ ขณะที่ secondary ทำตาม ลำดับ WAL ทำให้สองลำดับนี้อาจคลาดเคลื่อนกันได้
- แม้ในเงื่อนไขที่ใช้ storage
gp3และ instancedb.m6id.largeโดยไม่มีการ inject failure หรือ failover ก็พบ G-nonadjacent cycle ทุก ๆ ไม่กี่นาทีที่ประมาณ 150 write TPS / 1600 read-only TPS - ความผิดปกตินี้เข้าข่าย Long Fork และพบในทุกเวอร์ชันที่ทดสอบตั้งแต่ PostgreSQL 13.15 ซึ่ง AWS รองรับ ไปจนถึง 17.4 โดยไม่พบ Short Fork/Write Skew
- สำหรับ transaction ที่ความปลอดภัยสำคัญ การใช้ read-only secondary อาจทำให้เห็นลำดับการทำงานต่างออกไป จึงควรพิจารณาใช้เฉพาะ writer endpoint หรือทำให้มี write อย่างน้อย 1 รายการ
อัปเดตสาเหตุของ Long Fork
- Sergey Melnik จาก AWS และผู้ร่วมคอมเมนต์ใน HN คือ matashii กับ Ants Aasma ระบุ สาเหตุของ Long Fork ในคลัสเตอร์ PostgreSQL ได้แล้ว
- PostgreSQL primary กำหนดลำดับที่ทำให้ transaction มองเห็นได้ด้วย lock ในหน่วยความจำ
- secondary ทำให้ transaction มองเห็นได้ตามลำดับใน Write-Ahead Log (WAL)
- หากลำดับ lock กับลำดับ WAL ต่างกัน primary และ secondary อาจเห็นลำดับปรากฏของ transaction ต่างกันได้
- พฤติกรรมนี้เคยถูกกล่าวถึงในโพสต์ mailing list ของ PostgreSQL เมื่อปี 2013 และ Melnik ได้เขียนบทความในบล็อก AWS อธิบาย transaction visibility ของคลัสเตอร์ PostgreSQL และ read replica
- Jepsen แนะนำให้ AWS และ PostgreSQL จัดทำเอกสารเกี่ยวกับประเด็นนี้ควบคู่ไปกับการแก้ไข
ระดับ isolation และโครงสร้างของ RDS for PostgreSQL
- PostgreSQL เป็นฐานข้อมูล SQL แบบ open source อเนกประสงค์ และให้ระดับ transaction isolation 3 ระดับผ่าน MVCC
Read UncommittedและRead Committedทั้งคู่ทำงานเป็น Read CommittedRepeatable Readไม่ใช่ Repeatable Read จริง ๆ แต่ให้ Snapshot IsolationSerializableให้ Serializability
- Amazon RDS for PostgreSQL เป็นบริการของ AWS ที่ให้คลัสเตอร์ PostgreSQL แบบ managed
- ทำ provisioning, การจัดการ storage, replication, backup, upgrade และอื่น ๆ โดยอัตโนมัติ
- Multi-AZ deployments กระจาย node ฐานข้อมูลไปยังหลาย Availability Zone เพื่อลดความเป็นไปได้ของ failure ที่สัมพันธ์กัน
- RDS ใช้ synchronous replication เพื่อให้ตอบกลับหลังจาก transaction durability ถูกยืนยันแล้วทั้งบน primary และ instance secondary อย่างน้อย 1 ตัว
- ผู้ใช้จะได้รับ URL สองรายการที่สื่อสารด้วย PostgreSQL wire protocol
- primary endpoint: สำหรับ transaction แบบ read-write
- reader endpoint: สำหรับ transaction แบบ read-only
- primary endpoint รองรับ isolation level ทั้งหมดของ PostgreSQL แต่ secondary ไม่รองรับ Serializable
- ระดับ isolation ที่แข็งแกร่งที่สุดซึ่งใช้ได้ทั่วทุก node คือ Snapshot Isolation ที่ PostgreSQL เรียกว่า
Repeatable Read
การออกแบบการทดสอบ
- Jepsen ปรับ ไลบรารีทดสอบ สำหรับ PostgreSQL ให้เข้ากับ Amazon RDS for PostgreSQL และใช้ wrapper program ขนาดเล็ก
- ในแต่ละรอบการทดสอบ จะ provision คลัสเตอร์ RDS ด้วย API CreateDBCluster ของ AWS
- storage เป็น
gp3 - instance เป็น
db.m6id.large
- storage เป็น
- เปิด EC2 node 1 ตัวสำหรับรันการทดสอบ และให้ main endpoint กับ read-only endpoint ของคลัสเตอร์ RDS
- ไม่มีการ inject failure และ ไม่ trigger failover
- workload หลักประกอบด้วย transaction ที่จัดการลิสต์ของจำนวนเต็มไม่ซ้ำกัน
- แต่ละลิสต์ถูกเก็บใน row เดียว และ encode เป็นฟิลด์
TEXTที่มีค่าคั่นด้วย comma - transaction อ่านลิสต์ด้วย primary key หรือ append จำนวนเต็มที่ไม่ซ้ำเข้าลิสต์ด้วย
CONCAT
- แต่ละลิสต์ถูกเก็บใน row เดียว และ encode เป็นฟิลด์
- workload นี้ทำให้ Elle checker สามารถอนุมาน data-flow dependency ระหว่าง transaction และค้นหา graph cycle เพื่อตรวจสอบ isolation level ต่าง ๆ ได้
การสังเกต G-nonadjacent cycle
- ภายใต้เงื่อนไขปกติและ concurrency ระดับกลาง Amazon RDS for PostgreSQL 17.4 แสดง G-nonadjacent cycle ทุก ๆ ไม่กี่นาที
- การรันทดสอบ 2 นาที ครั้งหนึ่งทำงานที่ประมาณ 150 write TPS และ 1600 read-only TPS และมี cycle ของ transaction 4 รายการ
- ตัวอย่าง cycle ประกอบด้วย transaction สี่รายการ
T1,T2,T3,T4T1append9ไปยัง row 89 ทำให้เกิดลิสต์[4 9]และT2สังเกตเห็นสิ่งนี้T3append11ไปยัง row 90 ทำให้เกิดลิสต์[11]T4append3ไปยัง row 90 และอ่านลิสต์ผลลัพธ์[11, 3]จึงเขียนทับ version ของT3T2สังเกตเห็น append ของT1ที่ row 89 แต่ไม่เห็น append ของT3ที่ row 90- ในทางกลับกัน
T4สังเกตเห็น append ของT3ที่ row 90 แต่พลาด append ของT1ที่ row 89
- cycle นี้มี read-write dependency ที่ไม่อยู่ติดกัน จึงเป็น G-nonadjacent cycle ซึ่งละเมิด Snapshot Isolation
- ใน
Repeatable Readของ PostgreSQL มาตรฐาน พฤติกรรมเช่นนี้ไม่ควรเกิดขึ้น และ Jepsen ก็ไม่พบพฤติกรรมนี้ใน PostgreSQL มาตรฐาน
เหตุผลที่ขัดแย้งกับ Snapshot Isolation
- ใน Snapshot Isolation transaction ทุกตัวควรดูเหมือนทำงานบน snapshot ของฐานข้อมูล ณ timestamp เริ่มต้น
s - ผลของ transaction จะมองเห็นได้ต่อ transaction อื่นที่ commit timestamp
cภายหลัง - หากเขียนผลการสังเกตจาก cycle ตัวอย่างเป็นความสัมพันธ์ของ timestamp จะเกิดความขัดแย้งกัน
- เพราะ
T2อ่าน append ของT1จุดเริ่มต้นของT2จึงต้องอยู่หลัง commit ของT1:c1 < s2 - เพราะ
T2ไม่สังเกตเห็น append ของT3:s2 < c3 - เพราะ
T4เขียนทับและสังเกตเห็นT3:c3 < s4 - เพราะ
T4ไม่สังเกตเห็น append ของT1:s4 < c1
- เพราะ
- ความสัมพันธ์เหล่านี้ไม่สามารถเป็นจริงพร้อมกันทั้งหมดได้ จึงขัดแย้งกับโมเดล timestamp ของ Snapshot Isolation
Long Fork และผลลัพธ์ตามเวอร์ชัน
- cycle ดังกล่าวยังเป็นตัวอย่างของ Long Fork ด้วย
- transaction ตัวที่หนึ่งและตัวที่สองประกอบกันเป็น fork ของสถานะเชิงตรรกะหนึ่งชุด
- transaction ตัวที่สามและตัวที่สี่ประกอบกันเป็น fork ชุดที่สอง
- fork ทั้งสองอัปเดต row คนละตัว แต่ไม่สังเกตเห็นผลของกันและกัน
- ไม่พบ Short Fork หรือ Write Skew
- ผลลัพธ์นี้ชี้ว่า Amazon RDS for PostgreSQL อาจให้ Parallel Snapshot Isolation ซึ่งอ่อนกว่า Snapshot Isolation เล็กน้อย
- ความผิดปกติแบบ G-nonadjacent ปรากฏได้หลากหลาย ทั้งกรณีที่เชื่อมกันด้วย write-read edge เท่านั้น และกรณีที่มี transaction มากกว่า 4 รายการ
- พบความผิดปกติชนิดเดียวกันในทุกเวอร์ชันที่ทดสอบ ตั้งแต่ PostgreSQL 13.15 ซึ่งเป็นเวอร์ชันเก่าที่สุดที่ AWS รองรับ ไปจนถึงเวอร์ชันล่าสุด 17.4
สิ่งที่ผู้ใช้ควรตรวจสอบ
- เนื่องจากมี Long Fork และ G-nonadjacent cycle แบบอื่น ๆ คลัสเตอร์ Amazon RDS for PostgreSQL Multi-AZ จึง ไม่รับประกัน Snapshot Isolation
- ในแง่นี้ คลัสเตอร์ RDS for PostgreSQL Multi-AZ ให้ semantics ด้านความปลอดภัยที่อ่อนกว่า PostgreSQL แบบ node เดียว ซึ่งในการทดสอบ Jepsen ก่อนหน้านี้ดูเหมือนให้ Strong Snapshot Isolation
- ผู้ใช้สามารถตรวจสอบว่าโครงสร้าง transaction ของตนเปราะบางต่อ Long Fork หรือไม่ หรือทดลองยืนยันว่า invariant ที่ตั้งใจไว้ยังคงถูกรักษาอยู่หรือไม่
- read transaction อาจเห็นผลลัพธ์เกี่ยวกับลำดับการทำงานของ transaction ต่างจาก transaction อื่นได้
- ความผิดปกติดูเหมือนเกี่ยวข้องกับ query ไปยัง read-only secondary ดังนั้นอาจกู้คืน Snapshot Isolation ได้ด้วยวิธีต่อไปนี้
-
ใช้เฉพาะ writer endpoint
- ใส่ write อย่างน้อย 1 รายการในทุก transaction ที่ความปลอดภัยสำคัญ
- การตรวจสอบของ Jepsen เป็นแนวทางเชิงทดลอง และสามารถพิสูจน์ได้ว่ามี bug แต่พิสูจน์ได้ยากว่าไม่มี bug
- รายงานนี้เป็นผลจาก การสำรวจเบื้องต้น ไม่ใช่การตรวจสอบพฤติกรรมของ RDS for PostgreSQL อย่างละเอียด
-
1 ความคิดเห็น
ความคิดเห็นจาก Hacker News
อยากให้บทความในโลกซอฟต์แวร์เป็นแบบนี้ให้บ่อยกว่านี้: “Amazon RDS for PostgreSQL คือบริการของ Amazon Web Services (AWS) ที่ให้บริการอินสแตนซ์ PostgreSQL แบบมีการจัดการ เราแสดงให้เห็นว่า multi-AZ cluster ของ Amazon RDS for PostgreSQL ละเมิด snapshot isolation ซึ่งเป็นโมเดลความสอดคล้องที่เข้มแข็งที่สุดที่รองรับในทุก endpoint…”
ตรงไปตรงมา มีแต่ประเด็นสำคัญ ไม่มีการประดับประดา เลยคล้ายกับ วิธีเผยแพร่ผลการวิจัยในสาขา STEM อื่นๆ มาก สมัยก่อนเคยชอบบล็อกโพสต์แนวมีไหวพริบที่อธิบายด้วยมีม แต่ตอนนี้กลับโหยหางานเขียนที่ plain และเรียบง่าย
ถ้าเขียนบทความเทคนิคที่ลึกมากๆ ก็แทบไม่มีไลก์หรือคอมเมนต์ ถึงขนาดที่ Staff Engineer บอกว่า “น่าจะเจาะกลุ่มเป้าหมายให้แคบกว่านี้” ในทางกลับกัน ตอนทดลอง Kubecost ช่วงแรกแล้วเขียนว่าคำแนะนำของมันช่วยลดค่าใช้จ่ายได้น้อย แถมอาจทำให้เกิดปัญหาด้านประสิทธิภาพของคอนเทนเนอร์ แม้จะเป็นบทความเทคนิคพอสมควรที่พูดถึง CPU throttling และ cgroups แต่พอใส่ มีม ลงไป คนกลับชอบกันมาก
หลังจากนั้นพอเขียนบทความที่แห้งกว่านี้เกี่ยวกับการสร้าง Python external library เล็กๆ ด้วย C แล้วเข้าถึงผ่าน ctypes พร้อมเปรียบเทียบการจัดสรร stack/heap แล้วใส่มีมลงไป ผลก็ออกมาคล้ายกัน ไม่ชอบแนวโน้มแบบนี้นัก แต่ถ้าอยากเข้าถึงผู้อ่านวงกว้างก็ดูไม่ค่อยมีทางเลี่ยง Jensen ไม่ได้เขียนเพื่อผู้อ่านแบบนั้น และงานเขียนที่เคร่งครัดบริสุทธิ์แบบนี้ก็น่ายกย่อง
ไม่ได้อยู่ในชื่อเรื่อง และในบทความก็ไม่ได้เขียนชัดมากนัก แต่ปัญหานี้จำกัดอยู่ที่ฟีเจอร์ที่ค่อนข้างใหม่ของ RDS คือ multi-AZ cluster เท่านั้น ซึ่งต่างจาก multi-AZ instance ที่หลายคนคุ้นเคย
multi-AZ instance เป็นฟีเจอร์เก่าที่มีการทำ synchronous replication จากฐานข้อมูลหลักไปยังฐานข้อมูลสำรองใน availability zone อื่น และถ้าฝั่งหลักล้มเหลว RDS จะทำ failover ไปยังตัวสำรอง
multi-AZ cluster มีตัวสำรองสองตัว และธุรกรรมจะถูกทำ synchronous replication ไปยังอย่างน้อยหนึ่งในนั้น มันทนทานกว่า multi-AZ instance เมื่อสำรองตัวใดตัวหนึ่งล้มเหลวหรือประสิทธิภาพตก และยังเปิดให้อ่านแบบ read-only จากตัวสำรองได้ด้วย
แต่ multi-AZ cluster น่าจะมี เวทมนตร์เพิ่มเติม ภายในที่ไม่ใช่ฟีเจอร์พื้นฐานของ PostgreSQL และนั่นอาจเป็นสาเหตุที่ทำให้ไม่ผ่านการทดสอบของ Jepsen
อย่างไรก็ตาม PostgreSQL ก็ยังมีข้อบกพร่องที่ทำให้เกิดปัญหาคล้ายแพตเทิร์นนี้ได้อยู่ ธุรกรรมที่ไม่ได้ถูก replicate และไคลเอนต์หายไประหว่าง commit จะถูกมองเห็นได้ทันที ในตัวอย่าง ถ้า T1 เกิดขึ้นบน leader ที่ถูกแยกออกและการเชื่อมต่อขาดระหว่าง commit, T2 ก็เกิดบนโหนดที่ถูกแยกเช่นกัน และ T3/T4 เกิดทีหลังบน leader ตัวใหม่ ก็อาจเห็นผลลัพธ์แบบเดียวกันได้ แต่ก็ดูไม่ค่อยสอดคล้องกับคำอธิบายที่ว่าการทดสอบนี้ไม่ได้ทำ fault injection
แก้ไข: ไม่ได้เห็นบทความที่อธิบายว่าแพตเทิร์นนี้เกิดจาก ลำดับการ commit ที่ไม่ตรงกัน ระหว่าง replica กับโหนดหลัก เคยนำเสนอวิธีแก้ปัญหานี้ไว้ด้วย เลยรู้สึกเขินนิดหน่อย
เป็นการสืบสวนที่ดีมาก ทุกวันนี้นักพัฒนาซอฟต์แวร์จำนวนไม่น้อยไม่ค่อยเข้าใจแม้แต่เรื่อง ทรานแซกชัน เอง และยิ่งไม่รู้จักโมเดลทรานแซกชันที่หลากหลายเข้าไปอีก ถึงขั้นเคยเจอคนที่ถูกเรียกว่า “นักพัฒนาอาวุโส” ซึ่งทำงาน CRUD แต่ไม่รู้เรื่องทรานแซกชันของฐานข้อมูลเลย
ในทางปฏิบัติ เมื่อคุณต้องแก้ปัญหาที่ซอฟต์แวร์ไม่ใช่เรื่องเล็กน้อยและมีทราฟฟิกในระดับหนึ่ง ทรานแซกชันและโมเดลทรานแซกชันมีความสำคัญมากต่อทั้งประสิทธิภาพและโค้ดที่ไม่มีข้อผิดพลาด
ตัวอย่างเช่น ในโปรเจ็กต์ใหญ่แห่งหนึ่ง หลังจากวิเคราะห์กันอย่างมาก เราเปลี่ยนจากค่าเริ่มต้นของ SQL Server คือ Read Committed ไปเป็น Read Committed Snapshot Isolation แล้วปัญหาการแย่งล็อกก็หายไปอย่างมาก ผู้ใช้พอใจกันมาก วิศวกรซอฟต์แวร์ในโปรเจ็กต์นั้นใช้ทรานแซกชันกันอยู่แล้ว แต่ไม่รู้เรื่องโมเดลทรานแซกชันหรือล็อกเลยจนกระทั่งมีการสอนพื้นฐานให้
เพราะทำงานในสายรีเทลเป็นหลัก จึงเห็นระบบที่เต็มไปด้วยข้อผิดพลาดคล้าย race condition อยู่บ่อย ๆ และน่าเสียดายยิ่งขึ้นเพราะระดับการแยกกันเหล่านี้ช่วยได้มากในจุดแบบนี้
อย่างไรก็ตาม กรณีแบบนี้มักเจอในหมู่วิศวกรสตาร์ตอัปเป็นหลัก ส่วนนักพัฒนา Oracle/MSSQL แบบดั้งเดิมในบริษัทใหญ่ ๆ อย่างน้อยก็มีพื้นฐานแน่นพอสมควร เลยประเมินไว้ค่อนข้างสูง
ตลอดอาชีพที่ผ่านมา เคยเห็นแนวทางนี้จบลงแบบแย่มากจริง ๆ หลายครั้ง
แต่สิ่งที่ต้องระวังตอนเปลี่ยนคือ โค้ดทุกส่วนที่พึ่งพาการอ่านแบบ blocking จะพัง ตัวอย่างเช่นโค้ดอย่าง
select with existsต้องเขียนใหม่โดยใช้ explicit lock หรือวิธีอื่นที่บริษัทเก่า ตอนที่เราเปลี่ยนคำสั่ง
pg_dumpในสคริปต์แบ็กอัปให้เริ่มใช้ worker แบบขนาน (แฟลก-j) เราพบข้อผิดพลาดเป็นครั้งคราวระหว่างการกู้คืน เช่น duplicate key error และข้อผิดพลาดเรื่อง foreign key constraint ซึ่งบ่งชี้ถึงความไม่สอดคล้องกันตอนนั้นพยายามจะไปรายงานกับ AWS และ mailing list ของ PostgreSQL แต่เพราะทำให้เกิดซ้ำได้ไม่ง่ายจึงไม่คืบหน้าอะไร สุดท้ายเลยยอมแพ้และกลับไปใช้การดัมป์แบบเธรดเดียวแทน สิ่งที่เห็นตอนนั้นเกี่ยวข้องกับปัญหานี้หรือเปล่าก็น่าสงสัย
พออ่านบทความนี้แล้ว ดูเหมือนว่าผลกระทบจริงคือ หากมีการอ่านอย่างรวดเร็วทันทีหลังเขียนลงแถวเดิม อาจได้ ข้อมูลเก่า กลับมา แม้ทรานแซกชันที่เขียนจะถูกระบุว่าเสร็จสิ้นแล้วก็ตาม แต่ชั้นกระจายข้อมูลของ RDS แบบ multi-AZ อาจยังอัปเดตไม่ครบทั้งหมด ดังนั้นถ้าอ่านแถวเดิมทันที อาจยังไม่พบแถวนั้น หรือคอลัมน์อาจยังอัปเดตไม่ครบจึงได้ค่าก่อนหน้าออกมา
จากวิธีทำ snapshot ของ PostgreSQL ดูเหมือนว่าจะไม่ได้หมายความว่ามีการอัปเดตแค่บางไบต์ของคอลัมน์ชนิดหลายไบต์จนอ่านค่าเพี้ยนไร้ความหมายออกมา
สรุปแล้วมันดูเหมือน race condition ที่สุดท้ายจะค่อย ๆ เข้าสู่สภาวะสอดคล้องกันตามเวลา หรือมีใครตีความว่า “long fork” หมายความว่าทรานแซกชันที่มาทีหลังอาจไม่มีวันเสร็จสิ้นเลยแม้ในสถานการณ์ปกติหรือไม่
คำว่า “งานนี้ Jepsen ทำอย่างอิสระโดยไม่ได้รับค่าตอบแทน” เป็นประโยคที่คนมีส่วนได้ส่วนเสียกับ RDBMS คงไม่อยากเห็นแม้ในวันที่ทุกอย่างดูดีนัก น่าจะมีอีเมลภายในส่งไปมาหลายฉบับด้วยความกังวลอยู่พอสมควร ขอคารวะ aphyr เช่นเคย
ยังไม่ชัดเจนนักว่านี่ไม่ใช่ปัญหาในคลัสเตอร์ PostgreSQL แบบ multi-instance upstream หรือไม่ สงสัยว่าควรเข้าใจว่า AWS ทำอะไรบางอย่างกับการตั้งค่าคลัสเตอร์ หรือเพิ่มแพตช์ที่ทำให้เกิดพฤติกรรมนี้หรือเปล่า
โดยทั่วไปการทำซ้ำข้อมูลของ PostgreSQL มีได้หลายแบบและให้ผลลัพธ์ต่างกัน ตัวอย่างเช่นมีรายงาน Patroni ของ Bin Wang: https://www.binwang.me/2024-12-02-PostgreSQL-High-Availabili...
สิ่งที่พบตรงนี้ด้วยคือ ปัจจุบัน PostgreSQL ไม่ได้ให้พฤติกรรมสแนปช็อตที่สอดคล้องกันระหว่างโหนดหลักกับรีพลิกา อาจเป็นไปได้ว่าธุรกรรมแบบอ่านอย่างเดียว T2 รันอยู่บนโหนดรอง ขณะที่ธุรกรรมที่มีการเปลี่ยนแปลง T1/T3/T4 รันอยู่บนโหนดหลัก
หากดูจากฉากหลัง สแนปช็อตของโหนด PostgreSQL รองจะอาศัยลำดับการทำให้ธุรกรรมคงอยู่ถาวร กล่าวคือ ตำแหน่งของ commit record ใน WAL เมื่อตัดสินว่าธุรกรรมใดมองเห็นได้ ขณะที่ลำดับการมองเห็นบนโหนดหลักถูกกำหนดโดยเวลาที่แบ็กเอนด์ซึ่งอนุมัติธุรกรรมนั้นได้รับการแจ้งครั้งแรกว่าธุรกรรม commit เสร็จสมบูรณ์ และเวลาหลังจากนั้นที่มันทำเครื่องหมายว่า commit
ภายในโหนดหลักและโหนดรองแต่ละตัว ลำดับ commit ระหว่างแบ็กเอนด์ที่เชื่อมต่ออยู่นั้นสอดคล้องกัน แต่ลำดับ commit ระหว่างโหนดหลักกับโหนดรองอาจต่างกันได้พอสมควร มีงานที่กำลังทำเพื่อปรับปรุงเรื่องนี้อยู่ แต่ยังอยู่ในระยะเริ่มต้นมาก
ดูเหมือนว่า AWS จะทำแพตช์ PostgreSQL เพื่อจำลองข้อมูลไปยังสองอินสแตนซ์ และถือว่าเพียงพอแล้วถ้ามีหนึ่งในสองยืนยันการเปลี่ยนแปลง การยืนยันนี้เกิดขึ้นเมื่อไรไม่ใช่ข้อมูลที่เปิดเผยสู่สาธารณะ
โดยส่วนตัวคิดว่า PostgreSQL น่าจะเหมาะกับ การทำซ้ำระดับไฟล์ซิสเต็ม แบบ drbd มากกว่า อินสแตนซ์ AWS Multi-AZ แบบเก่าน่าจะใช้แนวทางนี้ แต่ข้อเสียคือ throughput ลดลงและไม่สามารถอ่านจากอินสแตนซ์รองได้
โดยเฉพาะช่วงนี้: https://youtu.be/fLqJXTOhUg4?t=434
ชื่อที่ส่งมาถามประเด็นสำคัญอยู่แล้ว RDS for PostgreSQL 17.4 ไม่ได้ทำ snapshot isolation ได้อย่างถูกต้อง
การถกเถียงมักดุเดือดพอสมควรว่าชื่อนั้นรุนแรงต่อระบบเกินไปหรือเป็นมิตรเกินไปหรือไม่ สะท้อนปัญหาที่มีความหมายที่สุดจากปัญหาสิบกว่าข้อที่พบหรือไม่ ยุติธรรมหรือไม่ตามมาตรฐานที่ Jepsen พยายามเป็นคนกลางที่ซื่อสัตย์ของผลลัพธ์ด้านความปลอดภัยของฐานข้อมูล และอีก 10 ปีข้างหน้าคนยังลิงก์กันอยู่แต่ในเวอร์ชันล่าสุดปัญหานั้นไม่เกี่ยวข้องแล้วควรตีความอย่างไร
หลังจากลองหลายครั้งแบบน่าหงุดหงิด เราจึงเลี่ยงปัญหานี้ด้วยนโยบายตั้งชื่อรายงานทั้งหมดเป็นรูปแบบ “Jepsen: ” ถ้า HN อยากได้ข้อความลิงก์ที่อธิบายมากกว่านี้หรือสีสันกว่านี้ ก็เลือกเองได้แน่นอน
ถึงอย่างนั้นนี่ก็เขียนโดย Kyle Kingsbury ผู้เป็นเหมือน Chuck Norris แห่งการรับประกันธุรกรรม ดังนั้น AWS ควรต้องตอบหรือชี้แจง แม้ว่ามันจะดูเหมือนใช้ได้กับแค่ multi-AZ cluster ซึ่งเป็นหนึ่งในสองตัวเลือกของ RDS สำหรับ PostgreSQL ก็ตาม การ deploy แบบ Multi-AZ อาจมีสแตนด์บาย DB อินสแตนซ์หนึ่งตัวหรือสองตัว และกรณีนี้เกี่ยวกับการตั้งค่าที่มีสแตนด์บาย DB อินสแตนซ์สองตัว
เอกสารของ AWS ไม่มีการรับประกันแบบนั้น แม้แต่คู่มือ RDS ที่ยาว 5494 หน้าก็แทบพูดถึง isolation หรือ serializable เพียงเล็กน้อยในเอกสารพารามิเตอร์ของแต่ละเอนจิน
ไม่มีเรื่องความสอดคล้องของการอ่านแบบ global ของ multi-AZ cluster ด้วย แม้จะบอกว่าเป็นการทำซ้ำแบบกึ่งซิงโครนัสที่ writer รอการยืนยัน log record จาก standby หนึ่งตัว แต่ reader สองตัวก็อาจอยู่บนสแนปช็อตคนละชุดกันได้
[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...
หากนักพัฒนาสมมติว่ามี snapshot isolation แต่ Amazon RDS for PostgreSQL ในความเป็นจริงให้เพียง parallel snapshot isolation เท่านั้น ก็สงสัยว่าโดยเฉพาะในสถาปัตยกรรม multi-AZ ที่ใช้ read replica endpoint จะเกิด safety bug หรือบั๊กระดับแอปพลิเคชันอะไรได้บ้าง
git pushก็ได้ เริ่มทรานแซกชันแล้วอ่านสถานะปัจจุบัน จากนั้นตรวจว่าเป็นไปตามสถานะที่คาดไว้หรือไม่ แล้วเขียนสถานะใหม่และ commit พร้อมแฮชของสถานะใหม่ ในจังหวะที่โชคร้ายอาจเกิด commit hash ที่ไม่ตรงกับสถานะที่ถูกต้องใด ๆ เลยก็ได้แค่ความจริงที่ว่าเรื่องแบบนี้อนุมานได้ยาก ก็ทำให้หลีกเลี่ยงปัญหาได้ยากอยู่แล้ว ดังนั้นทางแก้ที่ง่ายที่สุดน่าจะใกล้เคียงกับ “ถ้าเป็นการเขียนที่มีเงื่อนไขจากการอ่าน อาจกู้ snapshot isolation กลับมาได้ถ้าใช้เฉพาะ writer endpoint”
อย่างไรก็ตาม ก็น่าแปลกที่วิธี “ใช้เฉพาะ writer endpoint” ดูเหมือนจะยังไม่ได้ทดสอบ โดยเฉพาะในสถานการณ์ที่สูญเสียความพร้อมใช้งาน
User1 คอมเมนต์ก่อน แล้ว User2 คอมเมนต์ตามมา จากนั้น User1 ตรวจในอีกทรานแซกชันหนึ่งว่ามีคอมเมนต์อยู่แค่ 1 อันจึงได้แบดจ์ ส่วน User2 ก็ตรวจแบบเดียวกันในอีกทรานแซกชันหนึ่ง และเห็นเพียงคอมเมนต์ของตัวเอง 1 อันจึงได้แบดจ์ได้เช่นกัน
ใน snapshot isolation เรื่องนี้เกิดขึ้นไม่ได้ อย่างน้อยหนึ่งในทรานแซกชันแยกเหล่านั้นต้องเห็นคอมเมนต์ 2 อัน
งานวิจัยต้นฉบับเกี่ยวกับ parallel snapshot ก็น่าอ่านเช่นกัน: https://scispace.com/pdf/transactional-storage-for-geo-repli...
พอเห็นประโยคที่ว่า “ปรากฏการณ์นี้เกิดในทุกเวอร์ชันที่ทดสอบ ตั้งแต่ 13.15 ถึง 17.4” ก็แอบกังวลว่าการอัปเกรดเมเจอร์เวอร์ชันเป็นการตัดสินใจที่ผิดหรือเปล่า แต่ดูเหมือนจะไม่ใช่ นี่ไม่ใช่ regression แต่ใกล้เคียงกับ feature request หรือบั๊กเก่ามากกว่า