2 คะแนน โดย GN⁺ 2025-04-30 | 1 ความคิดเห็น | แชร์ทาง WhatsApp
  • การทดสอบของ Jepsen พบกรณีที่คลัสเตอร์ Amazon RDS for PostgreSQL Multi-AZ ไม่รักษา Snapshot Isolation ซึ่งเป็นระดับ isolation ที่แข็งแกร่งที่สุดเมื่อพิจารณาทุก node
  • สาเหตุหลักคือ ลำดับที่ transaction บน primary ถูกทำให้มองเห็นได้นั้นกำหนดด้วย lock ในหน่วยความจำ ขณะที่ secondary ทำตาม ลำดับ WAL ทำให้สองลำดับนี้อาจคลาดเคลื่อนกันได้
  • แม้ในเงื่อนไขที่ใช้ storage gp3 และ instance db.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 Committed
    • Repeatable Read ไม่ใช่ Repeatable Read จริง ๆ แต่ให้ Snapshot Isolation
    • Serializable ให้ 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
  • เปิด EC2 node 1 ตัวสำหรับรันการทดสอบ และให้ main endpoint กับ read-only endpoint ของคลัสเตอร์ RDS
  • ไม่มีการ inject failure และ ไม่ trigger failover
  • workload หลักประกอบด้วย transaction ที่จัดการลิสต์ของจำนวนเต็มไม่ซ้ำกัน
    • แต่ละลิสต์ถูกเก็บใน row เดียว และ encode เป็นฟิลด์ TEXT ที่มีค่าคั่นด้วย comma
    • transaction อ่านลิสต์ด้วย primary key หรือ append จำนวนเต็มที่ไม่ซ้ำเข้าลิสต์ด้วย CONCAT
  • 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, T4
    • T1 append 9 ไปยัง row 89 ทำให้เกิดลิสต์ [4 9] และ T2 สังเกตเห็นสิ่งนี้
    • T3 append 11 ไปยัง row 90 ทำให้เกิดลิสต์ [11]
    • T4 append 3 ไปยัง row 90 และอ่านลิสต์ผลลัพธ์ [11, 3] จึงเขียนทับ version ของ T3
    • T2 สังเกตเห็น 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 ความคิดเห็น

 
GN⁺ 2025-04-30
ความคิดเห็นจาก 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 ไม่ได้เขียนเพื่อผู้อ่านแบบนั้น และงานเขียนที่เคร่งครัดบริสุทธิ์แบบนี้ก็น่ายกย่อง
    • ตอนนี้ไม่อยากอ่านบล็อกโพสต์ที่ยัดมีมเต็มไปหมดจริงๆ โดยเฉพาะเวลาพยายามยืดเนื้อหาที่มีแค่ย่อหน้าเดียวให้ออกมายาวๆ ทุกวันนี้ บทความช่องโหว่ความปลอดภัย มักเป็นตัวอย่างที่แย่ที่สุด
    • เพิ่งคิดอยู่เลยว่าคิดถึง Jepsen สมัยก่อนเหมือนกัน งานเขียนแบบนั้นก็เน้นข้อเท็จจริงและตรงไปตรงมา แต่ก็ยังเต็มไปด้วยมีมได้เหมือนกัน ตัวอย่างที่ดีคือบทความ Redis เก่า https://aphyr.com/posts/283-call-me-maybe-redis
    • Amazon ขึ้นชื่อว่าเป็นบริษัทที่มี วัฒนธรรมการเขียนเชิงเทคนิค ที่ดี และจากที่ได้เห็นเองก็เป็นแบบนั้นจริง นี่เป็นความเห็นส่วนตัว ไม่ใช่ความเห็นของบริษัท มีบทความสาธารณะที่เกี่ยวข้องด้วย: https://quartr.com/insights/business-philosophy/amazon-s-wri...
  • ไม่ได้อยู่ในชื่อเรื่อง และในบทความก็ไม่ได้เขียนชัดมากนัก แต่ปัญหานี้จำกัดอยู่ที่ฟีเจอร์ที่ค่อนข้างใหม่ของ 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 พื้นฐานก็รองรับ quorum commit อยู่แล้ว จึงทำสถาปัตยกรรมลักษณะนี้ได้ ด้วย Patroni ก็สร้าง multi-AZ cluster ที่เทียบเท่ากันได้ และถ้าไม่นับบั๊ก ก็สามารถปรับการโปรโมตฝั่งหลักไม่ให้เกิดการสูญหายของธุรกรรมหรือเผยให้เห็นธุรกรรมที่ไม่ durable
      อย่างไรก็ตาม PostgreSQL ก็ยังมีข้อบกพร่องที่ทำให้เกิดปัญหาคล้ายแพตเทิร์นนี้ได้อยู่ ธุรกรรมที่ไม่ได้ถูก replicate และไคลเอนต์หายไประหว่าง commit จะถูกมองเห็นได้ทันที ในตัวอย่าง ถ้า T1 เกิดขึ้นบน leader ที่ถูกแยกออกและการเชื่อมต่อขาดระหว่าง commit, T2 ก็เกิดบนโหนดที่ถูกแยกเช่นกัน และ T3/T4 เกิดทีหลังบน leader ตัวใหม่ ก็อาจเห็นผลลัพธ์แบบเดียวกันได้ แต่ก็ดูไม่ค่อยสอดคล้องกับคำอธิบายที่ว่าการทดสอบนี้ไม่ได้ทำ fault injection
      แก้ไข: ไม่ได้เห็นบทความที่อธิบายว่าแพตเทิร์นนี้เกิดจาก ลำดับการ commit ที่ไม่ตรงกัน ระหว่าง replica กับโหนดหลัก เคยนำเสนอวิธีแก้ปัญหานี้ไว้ด้วย เลยรู้สึกเขินนิดหน่อย
    • ถ้าเกิดการละเมิด snapshot ภายใน multi-AZ instance ได้ ก็สงสัยว่าในการตั้งค่าที่มี read replica หลายตัวภายในรีเจียนเดียวกันจะเกิดได้เหมือนกันหรือไม่ เพียงแต่อาจเป็นว่าค่า latency ใน multi-AZ สูงกว่า จึงสังเกตเห็นได้ง่ายกว่า
    • อยู่ในประโยคที่สองของบทความเลย: “Amazon RDS for PostgreSQL multi-AZ clusters violate Snapshot Isolation” ก็ควรคาดหวังว่าคนจะอ่านกันนะ
  • เป็นการสืบสวนที่ดีมาก ทุกวันนี้นักพัฒนาซอฟต์แวร์จำนวนไม่น้อยไม่ค่อยเข้าใจแม้แต่เรื่อง ทรานแซกชัน เอง และยิ่งไม่รู้จักโมเดลทรานแซกชันที่หลากหลายเข้าไปอีก ถึงขั้นเคยเจอคนที่ถูกเรียกว่า “นักพัฒนาอาวุโส” ซึ่งทำงาน CRUD แต่ไม่รู้เรื่องทรานแซกชันของฐานข้อมูลเลย
    ในทางปฏิบัติ เมื่อคุณต้องแก้ปัญหาที่ซอฟต์แวร์ไม่ใช่เรื่องเล็กน้อยและมีทราฟฟิกในระดับหนึ่ง ทรานแซกชันและโมเดลทรานแซกชันมีความสำคัญมากต่อทั้งประสิทธิภาพและโค้ดที่ไม่มีข้อผิดพลาด
    ตัวอย่างเช่น ในโปรเจ็กต์ใหญ่แห่งหนึ่ง หลังจากวิเคราะห์กันอย่างมาก เราเปลี่ยนจากค่าเริ่มต้นของ SQL Server คือ Read Committed ไปเป็น Read Committed Snapshot Isolation แล้วปัญหาการแย่งล็อกก็หายไปอย่างมาก ผู้ใช้พอใจกันมาก วิศวกรซอฟต์แวร์ในโปรเจ็กต์นั้นใช้ทรานแซกชันกันอยู่แล้ว แต่ไม่รู้เรื่องโมเดลทรานแซกชันหรือล็อกเลยจนกระทั่งมีการสอนพื้นฐานให้

    • เรื่องนี้ไม่ได้จำกัดอยู่แค่นักพัฒนาอาวุโสเท่านั้น เคยเห็นสถาปนิกระบบที่ไม่รู้จัก ระดับการแยกกันของทรานแซกชัน ด้วย และบางคนก็สับสนระหว่าง “ความสอดคล้อง” ใน ACID กับ “ความสอดคล้อง” ใน CAP
      เพราะทำงานในสายรีเทลเป็นหลัก จึงเห็นระบบที่เต็มไปด้วยข้อผิดพลาดคล้าย race condition อยู่บ่อย ๆ และน่าเสียดายยิ่งขึ้นเพราะระดับการแยกกันเหล่านี้ช่วยได้มากในจุดแบบนี้
      อย่างไรก็ตาม กรณีแบบนี้มักเจอในหมู่วิศวกรสตาร์ตอัปเป็นหลัก ส่วนนักพัฒนา Oracle/MSSQL แบบดั้งเดิมในบริษัทใหญ่ ๆ อย่างน้อยก็มีพื้นฐานแน่นพอสมควร เลยประเมินไว้ค่อนข้างสูง
    • การขาดความเข้าใจเรื่องทรานแซกชันพบได้บ่อยเป็นพิเศษในสภาพแวดล้อมแบบ serverless/edge ถ้าจะเรียกสิ่งนั้นว่าสถาปัตยกรรมแบ็กเอนด์ได้ มันก็มักเป็นที่ที่ทุกอย่างขับเคลื่อนตามคำขอจากฝั่งไคลเอนต์ทั้งหมด เช่น โมเดลการคิวรีฐานข้อมูลถูกทำเป็น React hook หรือการเรียก API แบบลำดับต่อกัน
      ตลอดอาชีพที่ผ่านมา เคยเห็นแนวทางนี้จบลงแบบแย่มากจริง ๆ หลายครั้ง
    • อีกไม่นานนักพัฒนาซอฟต์แวร์ส่วนใหญ่คงจะได้แต่คัดลอก ขยะจาก LLM มาเป็นโค้ด โดยไม่รู้ด้วยซ้ำว่าจริง ๆ แล้วเกิดอะไรขึ้น เรื่องนี้ที่ Shopify กลายเป็นสิ่งบังคับไปแล้ว และ Microsoft ก็ยังคุยว่า 1/3 ของซอฟต์แวร์ถูกเขียนด้วยวิธีนี้ ถ้าในอนาคตจะไม่มีงานวิศวกรรมเหลืออยู่ ก็อดสงสัยไม่ได้ว่าจะยังมีใครยอมใช้เวลาไปเรียนรู้เรื่องพวกนี้หรือเปล่า
    • คำแนะนำที่ให้กับจูเนียร์ตลอด 10 ปีก็ยังเหมือนเดิม อ่าน หนังสือฐานข้อมูล SQL สักเล่มในวันหยุดสุดสัปดาห์ แล้วสุดสัปดาห์ถัดไปก็อ่านหนังสือของฐานข้อมูลที่โปรเจ็กต์ปัจจุบันใช้อีกสักเล่ม แค่นั้นก็น่าจะมีโอกาสกลายเป็นผู้เชี่ยวชาญฐานข้อมูลของโปรเจ็กต์นั้นได้แล้ว
    • เมื่อหลายปีก่อนก็มีสถานการณ์คล้ายกัน และตอนนี้เราเปลี่ยนผลิตภัณฑ์ที่มีรายได้ระดับ 1 พันล้านดอลลาร์จาก Read Committed ไปเป็น Read Committed Snapshot แล้วประสิทธิภาพดีขึ้นมาก
      แต่สิ่งที่ต้องระวังตอนเปลี่ยนคือ โค้ดทุกส่วนที่พึ่งพาการอ่านแบบ blocking จะพัง ตัวอย่างเช่นโค้ดอย่าง select with exists ต้องเขียนใหม่โดยใช้ explicit lock หรือวิธีอื่น
  • ที่บริษัทเก่า ตอนที่เราเปลี่ยนคำสั่ง pg_dump ในสคริปต์แบ็กอัปให้เริ่มใช้ worker แบบขนาน (แฟลก -j) เราพบข้อผิดพลาดเป็นครั้งคราวระหว่างการกู้คืน เช่น duplicate key error และข้อผิดพลาดเรื่อง foreign key constraint ซึ่งบ่งชี้ถึงความไม่สอดคล้องกัน
    ตอนนั้นพยายามจะไปรายงานกับ AWS และ mailing list ของ PostgreSQL แต่เพราะทำให้เกิดซ้ำได้ไม่ง่ายจึงไม่คืบหน้าอะไร สุดท้ายเลยยอมแพ้และกลับไปใช้การดัมป์แบบเธรดเดียวแทน สิ่งที่เห็นตอนนั้นเกี่ยวข้องกับปัญหานี้หรือเปล่าก็น่าสงสัย

    • อยากรู้ว่าเป็นอินสแตนซ์เดี่ยว อินสแตนซ์เดียวที่มี standby อยู่ใน availability zone อื่น หรือเป็น คลัสเตอร์ multi-AZ แบบที่ทดสอบในที่นี้
  • พออ่านบทความนี้แล้ว ดูเหมือนว่าผลกระทบจริงคือ หากมีการอ่านอย่างรวดเร็วทันทีหลังเขียนลงแถวเดิม อาจได้ ข้อมูลเก่า กลับมา แม้ทรานแซกชันที่เขียนจะถูกระบุว่าเสร็จสิ้นแล้วก็ตาม แต่ชั้นกระจายข้อมูลของ RDS แบบ multi-AZ อาจยังอัปเดตไม่ครบทั้งหมด ดังนั้นถ้าอ่านแถวเดิมทันที อาจยังไม่พบแถวนั้น หรือคอลัมน์อาจยังอัปเดตไม่ครบจึงได้ค่าก่อนหน้าออกมา
    จากวิธีทำ snapshot ของ PostgreSQL ดูเหมือนว่าจะไม่ได้หมายความว่ามีการอัปเดตแค่บางไบต์ของคอลัมน์ชนิดหลายไบต์จนอ่านค่าเพี้ยนไร้ความหมายออกมา
    สรุปแล้วมันดูเหมือน race condition ที่สุดท้ายจะค่อย ๆ เข้าสู่สภาวะสอดคล้องกันตามเวลา หรือมีใครตีความว่า “long fork” หมายความว่าทรานแซกชันที่มาทีหลังอาจไม่มีวันเสร็จสิ้นเลยแม้ในสถานการณ์ปกติหรือไม่

    • นี่ไม่ใช่แค่ข้อมูลเก่าในความหมายของ “snapshot ที่สอดคล้องกัน ณ ช่วงเวลาหนึ่ง แต่ยังไม่สะท้อนทรานแซกชันล่าสุดบางส่วน” เท่านั้น ในกรณีนี้ดูเหมือนว่า read-only transaction บนโหนดรองอาจมองเห็นทรานแซกชัน T บางตัว แต่กลับพลาดทรานแซกชันที่ตามตรรกะแล้วควรเกิดก่อน T ได้
  • คำว่า “งานนี้ Jepsen ทำอย่างอิสระโดยไม่ได้รับค่าตอบแทน” เป็นประโยคที่คนมีส่วนได้ส่วนเสียกับ RDBMS คงไม่อยากเห็นแม้ในวันที่ทุกอย่างดูดีนัก น่าจะมีอีเมลภายในส่งไปมาหลายฉบับด้วยความกังวลอยู่พอสมควร ขอคารวะ aphyr เช่นเคย

    • “คนมีส่วนได้ส่วนเสียกับ RDBMS” หมายถึงใครบ้าง?
    • ถ้าเป็นฝ่ายผู้รับ ผมกลับมองว่าน่าจะดีใจมากกว่า ตามธรรมเนียมแล้วแทบไม่มีที่ไหนผ่าน Jepsen แบบสบาย ๆ แต่การได้รับความสนใจจาก Aphyr หมายความว่าคุณกำลังถูก มองอย่างจริงจัง
  • ยังไม่ชัดเจนนักว่านี่ไม่ใช่ปัญหาในคลัสเตอร์ PostgreSQL แบบ multi-instance upstream หรือไม่ สงสัยว่าควรเข้าใจว่า AWS ทำอะไรบางอย่างกับการตั้งค่าคลัสเตอร์ หรือเพิ่มแพตช์ที่ทำให้เกิดพฤติกรรมนี้หรือเปล่า

    • เป็นคำถามที่ดี ตอนนี้ยังเข้าใจ สถาปัตยกรรมการทำซ้ำข้อมูล ของ AWS ไม่มากพอที่จะนำไปสร้างซ้ำบน PostgreSQL มาตรฐานได้ ดูเหมือนว่าพฤติกรรมนี้จะไม่เกิดบน PostgreSQL แบบโหนดเดียว แต่ในบางการตั้งค่าการทำซ้ำข้อมูลอาจเกิดขึ้นได้
      โดยทั่วไปการทำซ้ำข้อมูลของ PostgreSQL มีได้หลายแบบและให้ผลลัพธ์ต่างกัน ตัวอย่างเช่นมีรายงาน Patroni ของ Bin Wang: https://www.binwang.me/2024-12-02-PostgreSQL-High-Availabili...
    • ไม่ใช่ปัญหาในคลัสเตอร์ PostgreSQL แบบ single-instance แต่คลัสเตอร์ PostgreSQL แบบ multi-instance ที่ประกอบด้วยโหนดหลักเดียวและรีพลิกาแบบสตรีมมิง/กายภาพได้รับผลกระทบ
      สิ่งที่พบตรงนี้ด้วยคือ ปัจจุบัน PostgreSQL ไม่ได้ให้พฤติกรรมสแนปช็อตที่สอดคล้องกันระหว่างโหนดหลักกับรีพลิกา อาจเป็นไปได้ว่าธุรกรรมแบบอ่านอย่างเดียว T2 รันอยู่บนโหนดรอง ขณะที่ธุรกรรมที่มีการเปลี่ยนแปลง T1/T3/T4 รันอยู่บนโหนดหลัก
      หากดูจากฉากหลัง สแนปช็อตของโหนด PostgreSQL รองจะอาศัยลำดับการทำให้ธุรกรรมคงอยู่ถาวร กล่าวคือ ตำแหน่งของ commit record ใน WAL เมื่อตัดสินว่าธุรกรรมใดมองเห็นได้ ขณะที่ลำดับการมองเห็นบนโหนดหลักถูกกำหนดโดยเวลาที่แบ็กเอนด์ซึ่งอนุมัติธุรกรรมนั้นได้รับการแจ้งครั้งแรกว่าธุรกรรม commit เสร็จสมบูรณ์ และเวลาหลังจากนั้นที่มันทำเครื่องหมายว่า commit
      ภายในโหนดหลักและโหนดรองแต่ละตัว ลำดับ commit ระหว่างแบ็กเอนด์ที่เชื่อมต่ออยู่นั้นสอดคล้องกัน แต่ลำดับ commit ระหว่างโหนดหลักกับโหนดรองอาจต่างกันได้พอสมควร มีงานที่กำลังทำเพื่อปรับปรุงเรื่องนี้อยู่ แต่ยังอยู่ในระยะเริ่มต้นมาก
    • ขึ้นอยู่กับว่าคำว่า “multi instance upstream PostgreSQL cluster” หมายถึงอะไร PostgreSQL ไม่รองรับการ failover ของอินสแตนซ์หลักอย่างเป็นทางการ มีเพียงกลไกการทำซ้ำข้อมูลของ PostgreSQL ที่สามารถซิงก์กันได้ คุณสามารถสร้างเครื่องมือรอบ ๆ มันเองเพื่อประกอบเป็นคลัสเตอร์ได้ และ Patroni ก็เป็นหนึ่งในเครื่องมือเหล่านั้น
      ดูเหมือนว่า AWS จะทำแพตช์ PostgreSQL เพื่อจำลองข้อมูลไปยังสองอินสแตนซ์ และถือว่าเพียงพอแล้วถ้ามีหนึ่งในสองยืนยันการเปลี่ยนแปลง การยืนยันนี้เกิดขึ้นเมื่อไรไม่ใช่ข้อมูลที่เปิดเผยสู่สาธารณะ
      โดยส่วนตัวคิดว่า PostgreSQL น่าจะเหมาะกับ การทำซ้ำระดับไฟล์ซิสเต็ม แบบ drbd มากกว่า อินสแตนซ์ AWS Multi-AZ แบบเก่าน่าจะใช้แนวทางนี้ แต่ข้อเสียคือ throughput ลดลงและไม่สามารถอ่านจากอินสแตนซ์รองได้
    • ใช่ ต่างกัน และมีวิดีโออธิบายเชิงลึกว่าทำอะไรไว้ที่นี่: https://youtu.be/fLqJXTOhUg4
      โดยเฉพาะช่วงนี้: https://youtu.be/fLqJXTOhUg4?t=434
  • ชื่อที่ส่งมาถามประเด็นสำคัญอยู่แล้ว RDS for PostgreSQL 17.4 ไม่ได้ทำ snapshot isolation ได้อย่างถูกต้อง

    • ต้องมีบริบทเพิ่มเล็กน้อย เพราะคนบน HN มักบ่นเรื่องชื่อรายงานของ Jepsen รายงาน Jepsen มักเป็นผลลัพธ์จากการทำงานร่วมกับลูกค้าอย่างใกล้ชิดเป็นเวลานาน และลูกค้ามักมีความเห็นชัดเจนมากเกี่ยวกับชื่อรายงาน
      การถกเถียงมักดุเดือดพอสมควรว่าชื่อนั้นรุนแรงต่อระบบเกินไปหรือเป็นมิตรเกินไปหรือไม่ สะท้อนปัญหาที่มีความหมายที่สุดจากปัญหาสิบกว่าข้อที่พบหรือไม่ ยุติธรรมหรือไม่ตามมาตรฐานที่ Jepsen พยายามเป็นคนกลางที่ซื่อสัตย์ของผลลัพธ์ด้านความปลอดภัยของฐานข้อมูล และอีก 10 ปีข้างหน้าคนยังลิงก์กันอยู่แต่ในเวอร์ชันล่าสุดปัญหานั้นไม่เกี่ยวข้องแล้วควรตีความอย่างไร
      หลังจากลองหลายครั้งแบบน่าหงุดหงิด เราจึงเลี่ยงปัญหานี้ด้วยนโยบายตั้งชื่อรายงานทั้งหมดเป็นรูปแบบ “Jepsen: ” ถ้า HN อยากได้ข้อความลิงก์ที่อธิบายมากกว่านี้หรือสีสันกว่านี้ ก็เลือกเองได้แน่นอน
    • คอมเมนต์นี้ก็ยังขาดประเด็นสำคัญไป นั่นคือมันเกิดใน คลัสเตอร์ multi-AZ
      ถึงอย่างนั้นนี่ก็เขียนโดย 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...
    • ได้ส่งอีเมลถึงผู้ดูแลแล้ว ขอให้เปลี่ยนเป็นข้อความที่คัดลอกมาตรง ๆ จากโพสต์ลิงก์: “Amazon RDS for PostgreSQL multi-AZ clusters violate Snapshot Isolation”
  • หากนักพัฒนาสมมติว่ามี snapshot isolation แต่ Amazon RDS for PostgreSQL ในความเป็นจริงให้เพียง parallel snapshot isolation เท่านั้น ก็สงสัยว่าโดยเฉพาะในสถาปัตยกรรม multi-AZ ที่ใช้ read replica endpoint จะเกิด safety bug หรือบั๊กระดับแอปพลิเคชันอะไรได้บ้าง

    • ลองนึกถึง flow แบบ git push ก็ได้ เริ่มทรานแซกชันแล้วอ่านสถานะปัจจุบัน จากนั้นตรวจว่าเป็นไปตามสถานะที่คาดไว้หรือไม่ แล้วเขียนสถานะใหม่และ commit พร้อมแฮชของสถานะใหม่ ในจังหวะที่โชคร้ายอาจเกิด commit hash ที่ไม่ตรงกับสถานะที่ถูกต้องใด ๆ เลยก็ได้
      แค่ความจริงที่ว่าเรื่องแบบนี้อนุมานได้ยาก ก็ทำให้หลีกเลี่ยงปัญหาได้ยากอยู่แล้ว ดังนั้นทางแก้ที่ง่ายที่สุดน่าจะใกล้เคียงกับ “ถ้าเป็นการเขียนที่มีเงื่อนไขจากการอ่าน อาจกู้ snapshot isolation กลับมาได้ถ้าใช้เฉพาะ writer endpoint”
      อย่างไรก็ตาม ก็น่าแปลกที่วิธี “ใช้เฉพาะ writer endpoint” ดูเหมือนจะยังไม่ได้ทดสอบ โดยเฉพาะในสถานการณ์ที่สูญเสียความพร้อมใช้งาน
    • ลองนึกถึงกรณีคอมเมนต์ใต้โพสต์ สมมติว่าต้องให้ “first commenter badge” กับผู้ใช้คนแรกที่คอมเมนต์
      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 หรือบั๊กเก่ามากกว่า