2 คะแนน โดย GN⁺ 2023-12-14 | 1 ความคิดเห็น | แชร์ทาง WhatsApp
  • Knock ยกระดับ Postgres ซึ่งเป็นที่เก็บข้อมูลหลักของเอนจินเวิร์กโฟลว์การแจ้งเตือน จาก AWS RDS Aurora 11.9 เป็น 15.3 พร้อมจัดทำขั้นตอนการย้ายโดยไม่กระทบลูกค้า
  • หากไม่ดำเนินการก่อน วันที่ 29 กุมภาพันธ์ 2024 ซึ่งเป็นวันเลิกให้บริการ Postgres 11.9 ของ Amazon RDS ก็ต้องยอมรับการอัปเกรดแบบบังคับและ downtime
  • ตัดวิธีอัปเกรดแบบ in-place และ pg_dump/pg_restore ออก เพราะต้องหยุดระบบนาน แล้วเลือกใช้วิธีตั้งค่า logical replication บน DB ใหม่ด้วย PUBLICATION/SUBSCRIPTION
  • แยกกลยุทธ์การ replicate ตามขนาดตารางและรูปแบบการเขียน โดยตารางเล็ก replicate โดยตรง ส่วนตารางขนาดใหญ่แบบ append-only ใช้ copy_data = false ร่วมกับการ backfill จาก snapshot
  • การสลับขั้นสุดท้ายเสร็จในไม่กี่วินาที โดยคงการเชื่อมต่อทั้งสอง DB ไว้แล้วเปลี่ยน flag ให้เวลา query ที่กำลังรันอยู่ 500ms จากนั้นหยุด request ไปยัง DB ใหม่ 1 วินาทีเพื่อลดความเสี่ยง stale read

เป้าหมายและข้อจำกัดของการอัปเกรด

  • Knock พึ่งพา Postgres สำหรับเอนจินเวิร์กโฟลว์การแจ้งเตือน และใช้ Postgres กับการตั้งค่าเวิร์กโฟลว์ เทมเพลตข้อความ การเก็บ log หลายล้านรายการ และการจัดคิวงานเบื้องหลัง
  • ด้วยลักษณะของฐานข้อมูลเชิงสัมพันธ์ Postgres ต้องมีการ reboot อย่างน้อยเมื่ออัปเกรด และการอัปเกรด major version อาจต้องปิดระบบทั้งหมดหลายนาทีขึ้นไป เนื่องจากมีการเปลี่ยนรูปแบบการจัดเก็บข้อมูลและ index บนดิสก์
  • Postgres 11.9 ที่ใช้มาตั้งแต่เริ่มบริษัทมีกำหนดเลิกให้บริการบน Amazon RDS และหากไม่ดำเนินการเพิ่มเติม ก็มีความเป็นไปได้ที่จะถูกบังคับอัปเกรดและถูกบังคับให้มี downtime
  • เงื่อนไขการอัปเกรดถูกกำหนดให้มุ่งลดความเสี่ยงด้านปฏิบัติการ
    • ข้ามไปยังเวอร์ชันล่าสุดเท่าที่เป็นไปได้คือ Postgres 15.3 สำหรับ Aurora
    • ไม่ยอมรับ downtime เกิน 60 วินาที และโดยอุดมคติคือ system downtime เป็น 0
    • ทำให้เสร็จก่อนเส้นตายของ Amazon ในเดือนกุมภาพันธ์ 2024
    • ลดผลกระทบต่อลูกค้าให้น้อยที่สุด เช่น API error response เป็น 0
    • ทำขั้นตอนให้เป็น runbook เพื่อใช้ซ้ำในการอัปเกรดครั้งถัดไป
  • จาก 11.9 ไป 15.3 เท่ากับอัปเกรดข้าม 4 major version ดังนั้นจึงตัดทางเลือกการอัปเกรดแบบ in-place ซ้ำ 4 ครั้งออก

การเตรียมล่วงหน้า: ลดความเสี่ยงและเพิ่ม observability

  • การอัปเกรด Postgres เริ่มจากทำรายการความเสี่ยง แล้วลดความเสี่ยงที่มีผลกระทบสูงแต่กำจัดล่วงหน้าได้ง่ายก่อน
    • downtime ยาวนาน
    • ข้อมูลสูญหาย
    • ประสิทธิภาพ DB ของ workload แอปพลิเคชันเปลี่ยนไป
    • ความถี่หรือพฤติกรรมของ VACUUM เปลี่ยนไป
    • จำเป็นต้องย้าย replication slot หรือไม่
  • ตรวจสอบความเปลี่ยนแปลงระหว่างเวอร์ชันจาก Postgres release notes และระบุความเสี่ยง เช่น พฤติกรรม VACUUM ที่เปลี่ยนไป หรือความจำเป็นในการ reindex ในบางการอัปเกรด
  • ระหว่างอัปเกรดต้องตรวจสอบ metric ของระบบและฐานข้อมูล อย่างต่อเนื่อง
    • Max TXN ID เพื่อป้องกัน transaction wraparound
    • อัตราการใช้ CPU ของ DB
    • session ที่รออยู่บน writer instance
    • latency ของ query
    • latency ของ API response ของแอปพลิเคชัน
  • Knock ยัง monitor metric เฉพาะของแอปพลิเคชันด้วย เช่น เวลาที่ใช้ตั้งแต่ API request ถูกเปลี่ยนเป็นการแจ้งเตือน
  • หากไม่มี metric ที่ตรวจสอบได้ทันท่วงที ระหว่างอัปเกรดก็เหมือนอยู่ในสภาพ ถูกปิดตา

วิธีที่ตัดออก: in-place upgrade และ dump/restore

  • In-place upgrade ของ AWS RDS ถูกเรียกจาก AWS Console โดย AWS จะหยุด DB รันสคริปต์อัปเกรด แล้วนำกลับมา online
  • กระบวนการนี้อาจใช้ตั้งแต่หลายนาทีไปจนถึงหลายชั่วโมงขึ้นไป ขึ้นอยู่กับปริมาณข้อมูลและขอบเขตความเปลี่ยนแปลงระหว่างเวอร์ชัน
  • แม้ DB จะกลับมา online แล้ว ก็อาจยังต้องมีงานบำรุงรักษา เช่น VACUUM หรือ REINDEX ทำให้ยังไม่พร้อมใช้งานเต็มที่ทันที
  • วิธี pg_dump และ pg_restore ต้องแยกแอปพลิเคชันทั้งหมดออกจาก DB เดิมเพื่อให้ได้ backup ที่เชื่อถือได้ และสำหรับ DB ขนาดใหญ่ การ dump และ restore เองก็ใช้เวลานาน
  • ทั้งสองวิธีมีโอกาสสูงที่จะเกินขีดจำกัด downtime ของ Knock อย่างมาก จึงถูกตัดออกทั้งหมด

วิธีที่เลือก: อัปเกรดด้วย logical replication

  • ทางเลือกสุดท้ายคือวิธี logical replication โดยใช้ PUBLICATION และ SUBSCRIPTION ของ Postgres
  • ลำดับพื้นฐานมีดังนี้
    • เปิด DB ใหม่ด้วยเวอร์ชัน Postgres เป้าหมาย
    • ย้าย configuration, extension, โครงสร้างตาราง, user ฯลฯ
    • สร้าง publication บน DB เดิม และตั้งค่า subscription บน DB ใหม่
    • เพิ่มตารางเข้า publication
    • เมื่อ replicate เสร็จแล้ว ทำการทดสอบเพื่อตรวจสอบความเสี่ยงที่เหลือ
    • เมื่อยืนยัน configuration ของ DB ใหม่เพียงพอแล้ว ให้สลับแอปพลิเคชันไปใช้ DB ใหม่
    • ลบ DB เดิม
  • สามารถดำเนินการเป็น ขั้นตอนแบบค่อยเป็นค่อยไป แทนที่จะรันการอัปเกรดใหญ่ในครั้งเดียว และสามารถทดสอบ DB ใหม่ด้วยข้อมูลจริงและ workload จริงได้
  • เมื่อ DB ใหม่พร้อมแล้ว ตัวการสลับเองเสร็จในไม่กี่วินาที จึงควบคุมเวลาและวิธีสลับได้ดีกว่า

แก่นสำคัญของการตั้งค่า replication

  • Logical replication ของ Postgres ใช้ parameter ที่จำเป็นสำหรับการตั้งค่า replication slot และในแอปพลิเคชันเรียบง่าย การตั้ง wal_level เป็น logical อาจเป็นความเปลี่ยนแปลงหลัก
  • หากใช้งาน replication slot อยู่แล้วกับ read replica, DB failover, การ sync data warehouse ฯลฯ ต้องปรับ parameter ที่เกี่ยวข้อง เช่น max_replication_slots ตามเอกสาร
  • โครงสร้างตารางของ DB ใหม่ต้อง เหมือนกับ DB เดิม แต่ต้องว่างเปล่า
  • สามารถสร้าง schema snapshot ด้วย pg_dumpall โดยใส่ option --schema-only, --no-role-passwords แล้วนำไปเปรียบเทียบกับ SQL สำหรับ DB ใหม่เพื่อแก้ความแตกต่าง
  • เมื่อตั้ง publication บน DB เดิมและ subscription บน DB ใหม่ จะกำหนด option สำคัญดังนี้
    • enabled = false: ไม่ให้เริ่ม sync ตั้งแต่ต้น
    • create_slot = true: ให้ Postgres จัดการ replication slot
    • copy_data = true: โดยปกติให้คัดลอกเนื้อหาตาราง
    • disable_on_error = true: ให้หยุด subscription เมื่อเกิดข้อผิดพลาดที่ไม่คาดคิด เพื่อแก้ปัญหาแล้วค่อย resume ได้
  • หากใส่ทุกตารางเข้า publication พร้อมกันด้วย FOR ALL TABLES อาจเกิดปัญหาประสิทธิภาพใน DB ขนาดใหญ่ ดังนั้น Knock จึงใช้ ALTER PUBLICATION ... ADD TABLE เพื่อ เพิ่มทีละตาราง

การจัดกลุ่มตารางและกลยุทธ์ replication

  • Knock แบ่งตารางตามขนาดบนดิสก์และจำนวน tuple
    • ตารางเล็กที่ sync ได้ภายในไม่กี่นาที
    • ตารางใหญ่แต่ใกล้เคียง append-only
    • ตารางใหญ่ที่ row ส่วนใหญ่ถูก update บ่อย
  • ตาราง “เล็ก” ตามเกณฑ์ของ Knock คือ ตารางที่ น้อยกว่า 50GB และมี น้อยกว่า 10 ล้าน tuple
  • ใน Postgres tuple คือหน่วยที่เก็บ insert หรือ update และแม้จำนวน row จะน้อย แต่หากมี tuple ที่ยังไม่ได้ทำความสะอาดจำนวนมาก เวลา replicate ก็อาจยาวขึ้น
  • การรัน VACUUM ก่อน replicate อาจช่วยลดจำนวน tuple ที่ source DB ต้องคัดลอกไปยัง target DB
  • เวลาที่ใช้ sync ตารางสัมพันธ์โดยตรงกับขนาดดิสก์และจำนวน tuple และการ sync ที่ใช้เวลานานอาจขัดขวาง VACUUM ของ primary DB ทำให้ประสิทธิภาพลดลงและเกิดความเสี่ยง transaction wraparound

การ replicate ตารางเล็ก

  • ตารางเล็กจัดการโดยเพิ่มตารางเข้า publication บน DB เดิม แล้ว refresh subscription บน DB ใหม่
  • Postgres จะรับผิดชอบการคัดลอกตาราง การ sync และการนำการเปลี่ยนแปลงภายหลังไปใช้
  • ตารางที่เล็กมากอาจ sync ได้ในเวลาไม่ถึง 1 วินาที

การ replicate ตารางใหญ่แบบ append-only

  • ตารางใหญ่ที่ไม่มีการ update หรือ update เฉพาะ row ล่าสุด สามารถสร้าง publication/subscription แยกโดยใช้ copy_data = false ได้
  • Knock ใส่ suffix _nocopy ในชื่อเพื่อแยกจาก replication ปกติ
  • replicate เฉพาะการเปลี่ยนแปลงใหม่ก่อน แล้วค่อย backfill ข้อมูลเก่าแยกจาก backup หรือ snapshot
  • ขั้นตอนที่ใช้บน AWS RDS Aurora มีดังนี้
    • สร้าง snapshot ของ production DB
    • restore snapshot เป็น DB instance ใหม่
    • เติม suffix เช่น _snapshot ให้ชื่อตารางใน snapshot DB ที่จะ replicate
    • สร้างตารางสำหรับ snapshot บน target DB ด้วย schema เดียวกัน
    • ตั้ง publication/subscription จาก snapshot DB ไปยัง target DB
    • monitor ความคืบหน้าของ replication
    • เมื่อ replication ตามทันแล้ว merge เข้า target table จริงด้วย INSERT ... ON CONFLICT DO NOTHING
  • สำหรับตารางใหญ่มาก กระบวนการนี้อาจใช้เวลาหลายวัน แต่ทำงานเบื้องหลัง จึงไม่ควรกระทบ production environment
  • หลัง merge ให้เปรียบเทียบจำนวน row เพื่อตรวจสอบความสอดคล้อง แล้วลบ snapshot table บน target DB, snapshot subscription และ snapshot DB instance

ตารางใหญ่ที่ update บ่อย

  • ตารางใหญ่ที่ row ส่วนใหญ่ถูก update บ่อยเป็นกรณีที่ยากที่สุด และ replication ที่ใช้เวลานานอาจขัดขวางการรัน AUTOVACUUM
  • แนวทางที่พิจารณาได้มีดังนี้
    • ตรวจสอบว่าสามารถลดขนาดตารางด้วย housekeeping ได้หรือไม่
    • ตรวจสอบว่าเพิ่งรัน VACUUM ไปหรือไม่
    • พิจารณาว่าสามารถ partition ตารางเป็นชิ้นเล็กลงได้หรือไม่
    • ตรวจสอบว่าเมื่อเวลาผ่านไประยะหนึ่ง row หยุด update หรือไม่ เพื่อพิจารณาว่าจะจัดการแบบ append-only ได้หรือไม่
  • หาก source DB ต่ำกว่า PG 15 ตัวเลือกจะจำกัด และต้อง replicate ตามวิธีตารางเล็ก พร้อม monitor ว่าบริการเสื่อมประสิทธิภาพหรือไม่
  • หากจำเป็น สามารถ rollback ได้โดยนำตารางออกจาก publication แล้ว refresh subscription
  • ตารางที่ใหญ่เกินไปอาจเริ่ม replicate ในช่วงเวลาที่ traffic ต่ำ เพื่อลดผลกระทบจากโหลดและกิจกรรมการเขียน

การ replicate ตารางใหญ่แบบแบ่งส่วนที่ทำได้ใน PG 15 ขึ้นไป

  • หาก source DB เป็น PG 15 ขึ้นไป สามารถแบ่ง replication ด้วยหลาย publication เพื่อย้ายตารางใหญ่เป็นชิ้นเล็กลง
  • วิธีนี้ทำงานคล้าย partitioning หรือ sharding โดยแลกกับการใช้ replication slot มากขึ้น
  • Knock ย้ายจาก 11.9 ไป 15.3 จึงใช้วิธีนี้ไม่ได้ และไม่ได้ทดสอบโดยตรง
  • ตัวอย่างคือการใช้ hash ของ primary key และเงื่อนไข WHERE เพื่อแบ่ง row ไปยังหลาย publication
  • ขนาดชิ้นส่วนที่ Knock มองว่าจัดการได้คือประมาณ 100GB เฉพาะข้อมูล ไม่รวม index

การตรวจสอบสถานะ replication และการหยุด

  • เมื่อเพิ่มตารางเข้า subscription แล้ว สามารถตรวจสอบสถานะได้จาก pg_subscription_rel.srsubstate บน target DB
    • i: กำลัง initialize
    • d: กำลังคัดลอกเนื้อหาตาราง
    • f: คัดลอกเสร็จ รอ final sync
    • s: จบ initial sync
    • r: replication ทำงานปกติ
  • ขั้น d ต้องคง transaction ID เก่าของ Postgres ไว้ จึงอาจปิดกั้น VACUUM ได้อย่างมีนัยสำคัญ และนำไปสู่ปัญหาประสิทธิภาพหรือ transaction ID wraparound
  • หากเข้าใกล้ wraparound ควรหยุด migration แล้วแบ่งเป็นชิ้นเล็กลง
  • หากต้องการหยุด replicate ตารางใดตารางหนึ่ง ให้นำตารางออกจาก publication บน DB เดิม แล้ว refresh subscription บน DB ใหม่
  • การ disable เฉพาะ subscription อาจไม่แก้ปัญหาประสิทธิภาพ เพราะ source DB ยังจับ transaction ID เก่าไว้ต่อไป
  • ในสถานการณ์ฉุกเฉิน สามารถลบ publication และ subscription ทั้งหมดแล้วเริ่มใหม่ตั้งแต่ต้น โดย Postgres จะล้าง replication slot ที่เกี่ยวข้องให้

ข้อจำกัดของการย้าย replication slot

  • Replication slot ของ Postgres เก็บ log กิจกรรม DB ที่ DB หรือแอปพลิเคชันอื่นสามารถ consume ได้
  • ความคืบหน้าของ slot ถูกติดตามด้วย Log Sequence Number หรือ LSN และ LSN มีเอกลักษณ์เฉพาะกับ primary Postgres DB
  • ไม่สามารถคัดลอก LSN ของ replication slot จาก DB เดิมไปยัง DB ใหม่ได้ตรง ๆ
  • แอปพลิเคชันที่ consume replication slot เช่น เครื่องมือ data warehouse ต้องกำหนดกลยุทธ์การย้ายตามเอกสารของแต่ละเครื่องมือ
  • หากใช้ replication slot ในแอปพลิเคชันของตนเอง กลไก idempotency ที่สามารถกำจัด transaction ซ้ำระหว่าง DB เดิมและ DB ใหม่จะช่วยได้

การตรวจสอบขั้นสุดท้าย

  • เมื่อเพิ่มทุกตารางเข้า publication และ subscription ตามทันแล้ว ต้องตรวจสอบว่าตารางตรงกันหรือไม่
  • เนื่องจากความล่าช้าของ logical replication ทำให้ DB เดิมและ DB ใหม่ตรงกันสมบูรณ์ในขณะเดียวกันได้ยาก แต่สามารถตรวจสอบว่าใกล้เคียงพอด้วยการเปรียบเทียบจำนวน row
  • Knock เขียนสคริปต์นับจำนวน row ของ DB เดิมและ DB ใหม่สำหรับแต่ละตาราง
  • สำหรับตารางที่มีคอลัมน์ inserted_at จะเปรียบเทียบเฉพาะ row ที่เก่ากว่า 10 วินาที โดยถือว่า row ในช่วง 10 วินาทีล่าสุดจะถูก replicate ตามมาในไม่ช้า
  • บางตารางมีการเปรียบเทียบ sample row แบบสุ่มเพิ่มเติม เพื่อตรวจสอบว่าเนื้อหาตารางตรงกัน

วิธีสลับแอปพลิเคชัน

  • สำหรับ cutover ขั้นสุดท้าย สามารถเปลี่ยนให้แอปพลิเคชันเชื่อมต่อกับทั้งสอง DB ได้
  • DB ที่มี traffic ต่ำย้ายด้วยวิธีง่าย ๆ คือเปลี่ยน configuration ไปยัง DB ใหม่แล้ว restart แอปพลิเคชัน
  • ในแอปพลิเคชันที่มีกิจกรรมพร้อมกันจำนวนมาก ต้องหลีกเลี่ยง conflicting write ระหว่าง DB เดิมกับ DB ใหม่
  • สคริปต์ cutover ของ Knock ทำงานตามลำดับนี้
    • สั่งให้ application instance ทั้งหมดส่ง query ใหม่ไปยัง DB ใหม่
    • ให้เวลา query DB ที่กำลังรันอยู่ 500ms เพื่อทำให้เสร็จ หลังจากนั้นยกเลิกแบบบังคับ
    • หยุด request ไปยัง DB ใหม่แบบจงใจใน 1 วินาทีแรกหลังเปลี่ยน flag เพื่อให้ pending transaction มีเวลาถูก replicate ไปยัง DB ใหม่
    • จากนั้นทำให้กิจกรรม DB กลับสู่ปกติ แต่ชี้ไปยัง DB ใหม่
    • workload DB เฉพาะบางอย่างถูกหยุดแล้ว restart เพื่อเชื่อมต่อกับ DB ใหม่อีกครั้ง
  • Knock ยืนยันว่า 500ms ยาวกว่าคิวรี DB ส่วนใหญ่มาก และไม่มี error จากการตัดการเชื่อมต่อแบบบังคับ

การจัดการ sequence

  • Logical replication ของ Postgres ไม่ sync sequence
  • แม้ค่า sequence จะถูกใช้บน DB เดิม ค่า sequence บน DB ใหม่ก็ไม่เพิ่มขึ้น
  • Knock รันสคริปต์ที่เชื่อมต่อทั้งสอง DB ก่อนเปลี่ยน feature flag ทันที
    • สำหรับ sequence ทั้งหมดบน DB เดิม ดึงค่าถัดไปด้วย SELECT nextval('sequence_name')
    • บน DB ใหม่ เลื่อน sequence ไปข้างหน้าด้วย SELECT setval('sequence_name', value::int4 + 100000)
  • วิธีนี้ทำให้เกิด gap ใน sequence แต่ sequence ของ Knock เป็น bigint ดังนั้นการข้ามค่า 100,000 ค่าแทบเป็น 0% ของพื้นที่ sequence ที่ใช้งานได้
  • ต้องปรับขนาด gap ให้เหมาะกับจำนวนค่า sequence ที่จะใช้ระหว่าง cutover จริง

สิ่งที่ต้องตรวจสอบก่อน cutover

  • รายการตรวจสอบก่อนสลับขั้นสุดท้ายครอบคลุมความพร้อมด้านปฏิบัติการอย่างกว้างขวาง
    • จำนวน row ของทุกตารางตรงตามที่คาดหรือไม่
    • subscription ทั้งหมดอยู่ในสถานะ enable และทำงานโดยไม่มี error หรือไม่
    • schema ตรงกันหรือไม่ และสามารถ freeze migration release ได้หรือไม่
    • DB ใหม่ sizing เหมาะกับ workload หรือไม่
    • ต้องมี read replica หรือไม่ เพื่อให้ topology ของ cluster ระหว่าง DB เดิมและ DB ใหม่ตรงกัน
    • ได้ทำ REINDEX และ maintenance VACUUM พื้นฐานบน DB ใหม่แล้วหรือไม่
    • ได้ตรวจสอบ release notes ของ Postgres อีกครั้งถึงความเป็นไปได้ของ regression ในแอปพลิเคชันหรือไม่
    • ได้ทำ automated test และ manual test บน staging DB เวอร์ชันใหม่หรือไม่
    • ได้ load test query ที่หนักที่สุดด้วย pg_bench หรือไม่
    • ยังมีความเสี่ยงที่ลดได้เหลืออยู่หรือไม่
    • ได้ซ้อมขั้นตอน cutover หลายครั้งใน staging หรือ test environment หรือไม่
    • ได้สร้าง backup ของ DB ก่อน cutover ทันทีหรือไม่

ผลลัพธ์การสลับจริง

  • Knock replicate ตารางทีละตารางเป็นเวลาหลายสัปดาห์ โดยส่วนใหญ่ทำหลังเวลาทำการและช่วงที่ traffic ต่ำที่สุด
  • ซ้อม cutover หลายครั้งใน staging environment และปรับขั้นตอนให้ทำงานได้โดยไม่ต้องมี operator เข้าแทรกแซงมากนัก
  • หลังจาก PG 15 replica และโค้ดสลับแอปพลิเคชันพร้อมแล้ว ก็ทำการตรวจสอบขั้นสุดท้ายและเปลี่ยน flag
  • cutover จริงเสร็จภายในไม่กี่วินาที และแอปพลิเคชันยังทำงานต่อไป นอกจาก latency blip สั้น ๆ ที่จงใจสร้างเพื่อรอ replication
  • หลังจากนั้นจึงย้อนการเปลี่ยนแปลงแอปพลิเคชันชั่วคราว ย้าย connection ทั้งหมดไปยัง DB ใหม่อย่างถาวร และลบ subscription ของ DB ใหม่กับ DB เดิม
  • Knock ทำการย้ายจาก Postgres 11.9 เป็น 15.3 โดยไม่หยุดให้บริการสำเร็จ

สรุป

  • การข้าม Postgres ทีเดียว 4 major version เป็นงานหนักแต่ทำได้
  • วิธี logical replication ช่วยให้ซ้อม ทดสอบ และทำซ้ำได้หลายครั้งก่อน cutover จริง จึงอาจปลอดภัยกว่า downtime ที่กำหนดไว้
  • หากเกิดปัญหาระหว่างทาง สามารถลบ publication ของ DB เดิมแล้วเริ่มใหม่ได้ ทำให้ย้อนขั้นตอนได้โดยไม่ทำให้บริการเสื่อมลง
  • ความพร้อมใช้งาน 100% ที่สมบูรณ์แบบเป็นไปไม่ได้ในเชิงเทคนิค แต่การ migrate แบบไม่หยุดให้บริการช่วยให้ระบบทำงานต่อไปได้โดยไม่เกิดการหยุดชะงักครั้งใหญ่

1 ความคิดเห็น

 
GN⁺ 2023-12-14
ความคิดเห็นจาก Hacker News
  • วิธีคัดลอกข้อมูลทั้งตารางทีละรายการสร้างภาระ I/Oสูงเกินไป และใช้ไม่ได้กับตารางที่ใหญ่มาก
    วิธีที่ดีกว่าคือสร้าง replication slot, ทำ snapshot, กู้คืนไปยังอินสแตนซ์ใหม่, ขยับ LSN ไปข้างหน้า แล้วค่อยทำ replication ต่อจากจุดนั้น
    แบบนี้จะได้ logical replica ที่มีข้อมูลครบทั้งหมด แล้วค่อยอัปเกรด replica นั้น
    บทความของ Instacart อธิบายวิธีนี้ไว้: https://archive.ph/K5ZuJ
    ถ้าจำไม่ผิด บทความนั้นมีข้อผิดพลาดเล็ก ๆ อยู่บ้าง แต่ขั้นตอนโดยรวมใช้ได้จริง และเคยอัปเกรดอินสแตนซ์ระดับ TB แบบนี้มาหลายครั้งแล้ว

    • วิธีนี้เป็นสูตรที่ดี แต่ต้องมีการแก้ไขเล็กน้อยแต่สำคัญในลำดับการแทรก pg_upgrade
      ถ้าเริ่ม logical replication ก่อนแล้วค่อยรัน pg_upgrade จะมีความเสี่ยงต่อความเสียหาย มีการพูดคุยไว้ใน pgsql-hackers: https://www.postgresql.org/message-id/flat/20230217075433.u5...
      วิธีแก้คือต้องสร้าง logical slot ก่อน จากนั้นขยับคลัสเตอร์ใหม่ไปจนถึงตำแหน่ง LSN ของ slot โดยที่ยังไม่เริ่ม logical replication แล้วจึงรัน pg_upgrade และหลังจากคลัสเตอร์ขึ้นมาบน PostgreSQL เวอร์ชันใหม่แล้วค่อยเริ่ม logical replication
      ไม่นานมานี้ Postgres.ai ใช้วิธีนี้แบบเดียวกันเป๊ะตอนอัปเกรดคลัสเตอร์ขนาด multi-TiBหลายชุดของ GitLab แบบไร้ดาวน์ไทม์ภายใต้โหลดสูง และยังใช้ PAUSE/RESUME ของ PgBouncer ร่วมด้วย สัปดาห์นี้ช่วงปลายสัปดาห์จะมีการบรรยายโดย Alexander Sosna: https://www.postgresql.eu/events/pgconfeu2023/schedule/sessi...
    • ในฐานะ OP ผมก็พิจารณาวิธีนี้เหมือนกัน แต่ไม่มั่นใจพอกับการขยับ LSN แบบแมนนวลตามที่เสนอ และก็ไม่มั่นใจว่าจะตรวจจับความไม่สอดคล้องได้หาก replication หลุดไป
      การไล่ทำทีละตารางยุ่งยากกว่ามาก แต่ดูน่าเชื่อถือกว่า
    • บทความถูกอัปเดตแล้ว: https://tech.instacart.com/zero-downtime-postgresql-cutovers...
    • บทความนั้นอธิบายพื้นฐานของวิธีอัปเกรดของ Instacart แต่ค่อนข้างเก่าแล้ว และบทความด้านล่างแสดงขั้นตอนปัจจุบันได้ดีกว่า
      พวกเขาอัปเกรดฐานข้อมูลขนาดใหญ่มากและมีการใช้งานสูงสำเร็จมาแล้วจำนวนมากด้วยวิธีนี้
      https://www.instacart.com/company/how-its-made/zero-downtime...
  • แนวทางนี้น่าสนใจและมีเอกสารประกอบดี แต่สะดุดกับประโยคที่ว่า “ลูกค้าสมัยใหม่คาดหวังความพร้อมใช้งาน 100%
    นี่ไม่ใช่ทั้งความชอบในฐานะลูกค้า และไม่ใช่ประสบการณ์ของผมในฐานะผู้ให้บริการ สำหรับหลายเวิร์กโหลด ความสอดคล้องสำคัญกว่าความพร้อมใช้งานมาก
    ถ้าผู้ให้บริการประกาศช่วง downtime ล่วงหน้า บางครั้งกลับทำให้รู้สึกอุ่นใจกว่า เพราะดูเหมือนพวกเขากำลังจัดการข้อมูลของเราอย่างรอบคอบ

    • ในฐานะ OP นี่เป็นฟีดแบ็กที่ดี
      ผมอยากสร้างความเชื่อมั่นทั้งต่อความน่าเชื่อถือของผลิตภัณฑ์และความสอดคล้องของเวิร์กโหลด แน่นอนว่าการจัดการความคาดหวังของลูกค้าและยอมมี downtime อย่างตั้งใจเพื่อให้ได้ uptime ที่ดีกว่าในระยะยาว ย่อมดีกว่าทำเหมือนว่าระบบสอดคล้องดีทั้งที่จริงไม่เสถียร
      การทำให้ผู้คนคาดการณ์ช่วงบำรุงรักษาตามรอบได้ล่วงหน้า อาจนำไปสู่สถาปัตยกรรมที่แข็งแรงขึ้นโดยรวม หากลูกค้าสร้างกลไกรองรับ downtime ได้ ความยืดหยุ่นก็จะเพิ่มขึ้น และทีมเองก็จะมีเวลาไปลงทุนกับผลิตภัณฑ์ที่ดีกว่าได้เมื่อสามารถไว้ใจลูกค้าในลักษณะนั้น
      หลังอัปเกรดเมเจอร์เวอร์ชันครั้งหน้า ผมอาจเขียนบทความชื่อ “การตั้งความคาดหวังเรื่อง downtime คือเส้นทางไปสู่ uptime ระดับสูงมาก” ก็ได้
    • มันขึ้นอยู่กับว่าลูกค้าคือใคร
      ในฐานะลูกค้าของ AWS ผมคาดหวังความพร้อมใช้งาน 100% เพราะลูกค้าของผมอยู่ทั่วโลก และไม่มีช่วงเวลาไหนที่สามารถปล่อยให้ downtime ได้เลย
  • ตอนนี้ AWS รองรับการ deploy แบบ blue/greenแล้ว: https://aws.amazon.com/about-aws/whats-new/2023/10/amazon-rd...

    • ผมเพิ่งลองเองเมื่อไม่กี่สัปดาห์ก่อน และสำหรับ PostgreSQL ตอนนี้ยังไม่ค่อยอยากเชื่อใจมัน
      หลังคุยโต้ตอบกับ AWS ไปหลายรอบ การทดสอบก็ค้างอยู่หลายชั่วโมง ก่อนที่ภายหลัง AWS UI จะยอมรับว่าไม่ได้มีการสลับจริงเกิดขึ้น โชคดีที่มันล้มเหลวแบบปลอดภัย แต่ผมยังไม่เชื่อว่าจะจับจังหวะ cutover จริงได้แม่นกับชุดข้อมูลระดับ GB ขึ้นไป
    • เห็นด้วยเลย ในฐานะ OP ตอนนั้นเราใช้Aurora 11.9อยู่ และยังไม่อยู่ในกลุ่มที่รองรับ blue/green deployment
      รอบหน้าอาจจะทำได้
  • นี่เยี่ยมมาก
    ผมสร้างเครื่องมือที่ช่วยทำสิ่งที่เจอส่วนใหญ่ให้เป็นอัตโนมัติ และยินดีมากถ้าใครเห็นว่ามันมีประโยชน์ หรืออยากต่อยอดด้วยฟีดแบ็ก/ไอเดีย: https://github.com/shayonj/pg_easy_replicate

    • เป็นเครื่องมือที่เจ๋งมาก
      สิ่งที่ค้นพบจากตารางขนาดใหญ่อาจน่าสนใจสำหรับเครื่องมือแบบนี้ ถ้าช่วยให้ใช้กลยุทธ์ที่เหมาะกับแต่ละตารางได้ง่ายขึ้น มันอาจกลายเป็นเครื่องมือที่ขาดไม่ได้สำหรับทีมที่ต้องทำ migration แบบนี้ในอนาคตก็ได้
  • น่าสงสัยกับคำพูดที่ว่า “บริการอย่าง Knock ไม่สามารถยอมให้มี downtime ได้เลยไม่ว่าจะวางแผนไว้หรือไม่ก็ตาม”
    ถ้าเป็นระบบที่ซับซ้อนก็ย่อมมีเหตุขัดข้องและมี downtime ได้ downtime 15 นาทีที่ประกาศล่วงหน้า ถือว่ายอมรับได้สำหรับธุรกิจ SaaS แทบทั้งหมด ไม่ใช่โรงพยาบาลหรือโรงไฟฟ้าเสียหน่อย
    การคิดว่าบริการสำคัญกว่าความเป็นจริงทำให้เกิดงานลวงขึ้นมามากมาย ถ้าเอาเวลาเชิงวิศวกรรมที่ลงไปกับเรื่องนี้ไปใช้พัฒนาผลิตภัณฑ์หรือเพิ่มประสิทธิภาพให้ทีมพัฒนา ผู้ใช้น่าจะมีความสุขมากกว่า โดยเฉพาะถ้าสามารถเอา notification เข้า queue ไว้แล้วค่อยไล่ตามหลัง downtime ได้
    ถ้ามี enterprise SLA ที่มีเงื่อนไขชดเชยสำหรับ downtime 15 นาที ก็พอจะอธิบายได้ แต่ส่วนใหญ่ไม่เป็นแบบนั้น และในความเป็นจริงก็น่าจะเคยมีเหตุขัดข้องที่ใกล้เคียงหรือยาวกว่านี้อยู่แล้วหลายครั้ง
    ในงานย้ายฐานข้อมูล ความต่างของปริมาณงานระหว่าง “downtime สั้น ๆ” กับ “ไม่หยุดให้บริการ” มักมากพอสมควร จึงยิ่งสำคัญ และถ้าเป็นงานครั้งเดียวแบบนี้ แถม PostgreSQL เวอร์ชันล่าสุดบน RDS ก็รองรับให้อยู่แล้วตามปกติ ก็ยิ่งรู้สึกว่ายากจะหาเหตุผลมารองรับ

    • ในฐานะ OP ก็จริงที่ทุกบริการมี downtime ได้ไม่ว่าจะด้วยเหตุผลใดก็ตาม
      เราคุยกันเรื่องการกำหนด maintenance window เหมือนกัน แต่สิ่งที่คิดซ้ำ ๆ คือจะซ้อมอัปเกรดกับข้อมูล production อย่างไร สำเนา PG 15 ที่ซิงก์กับข้อมูล production มีความสำคัญมากในการตรวจสอบว่า workload ทำงานตามที่คาดไว้หรือไม่
      การใช้ replica แบบเรียลไทม์ทำให้ซ้อมได้โดยลดผลกระทบต่อ production ให้เหลือน้อยที่สุด
      บทเรียนใหญ่จาก migration ครั้งนี้คือ การไล่ตามและลดความเสี่ยงทุกอย่างที่พอนึกออกในโปรเจกต์แบบนี้มีประโยชน์มากแค่ไหน สุดท้ายความเสี่ยงของการอัปเกรดแบบ in-place ดูจะสูงกว่าความเสี่ยงของแนวทางที่เลือก และนี่เป็นการตัดสินใจที่แยกจากเรื่องจะมี maintenance window หรือไม่
      แถมถ้าในอนาคตต้องใช้แนวทางนี้อีก บทความบล็อกนี้ก็จะเป็นจุดเริ่มต้นที่ช่วยประหยัดเวลาได้หลายสัปดาห์ หวังว่าจะเป็นประโยชน์กับทีมอื่นที่อยู่ในสถานการณ์คล้ายกันด้วย
    • ในฐานะแพทย์ รู้สึกขำดีที่คำว่า “ก็ไม่ใช่โรงพยาบาลนี่” ถูกยกมาเป็นตัวอย่างของระบบที่ทน downtime ไม่ได้
      แม้แต่ Epic ผู้ให้บริการเวชระเบียนอิเล็กทรอนิกส์รายใหญ่ระดับต้น ๆ ของสหรัฐฯ ก็ยังมี planned downtime อย่างน้อยเดือนละครั้ง ครั้งละประมาณ 30–60 นาทีเพื่อทำการอัปเกรด
    • ปัญหาคือบน RDS ไม่มีวิธีอัปเกรด PostgreSQL instance โดยให้เกิด planned downtime 15 นาที ได้
      คุณควบคุมจังหวะ reboot ไม่ได้ เมื่อเริ่มกระบวนการแล้ว การสลับอาจเริ่มอีกหนึ่งชั่วโมง สองชั่วโมง หรือสามชั่วโมงให้หลัง และคุณก็ไม่รู้ ไม่สามารถควบคุมได้ว่ามันจะ reboot เมื่อไร
      ถ้ามี replica อยู่ มันจะถูกอัปเกรดแบบขนานกันและ reboot ในเวลาสุ่ม ทำให้ปวดหัวกว่าเดิม
      ดังนั้น ถ้าคุณรับภาวะไม่พร้อมใช้งานแบบสุ่มในช่วงเวลาที่อาจยืดไปได้หลายชั่วโมงตามขนาดฐานข้อมูลไม่ได้ สำหรับการอัปเกรด RDS แล้ว logical replication แทบจะเป็นทางเลือกเดียว
      ยิ่ง instance ใหญ่ ปัญหาก็ยิ่งยาก
    • ปัญหาที่แท้จริงของ downtime คือเวลาที่ทุกระบบล่มพร้อมกัน
      ถ้า Jira ล่มวันละ 15 นาที ปกติก็ไม่ได้กระทบมาก ยังมีงานอื่นในคิวให้ทำ และในกรณีเลวร้ายที่สุด ต่อให้มีเหตุขัดข้องหลายอย่างซ้อนกัน ก็ยังมีงานเอกสารที่เคยรับปากใครไว้
      แต่ถ้าผลิตภัณฑ์ทั้งชุดของ Atlassian ล่มพร้อมกัน การรักษางานกันชนไว้ให้ทำต่อได้จะยากขึ้นมาก ถ้าทำให้แอปทั้งหมดของบริษัทใช้ storage array เดียวกัน ความสูญเสียด้าน productivity อาจกระโดดจาก 5% ไปเป็น 95% ได้
    • ตรงข้ามกับคำพูดที่ว่า “downtime 15 นาทีที่ประกาศล่วงหน้ายอมรับได้สำหรับธุรกิจ SaaS แทบทั้งหมด” ก็อาจมีคู่แข่งที่ไม่มี downtime เลยในแต่ละเดือน
      คู่แข่งแบบนั้นเท่ากับให้ความต้องการของฉันมาก่อนความสะดวกของตัวเอง
      เหตุขัดข้องของคุณก็คือเหตุขัดข้องของฉันด้วย
  • ตอนนี้ที่ hava.io ก็กำลังผ่านกระบวนการนี้อยู่
    กำลังอัปเกรด AWS RDS PostgreSQL จาก 11.13 เป็น 15.5
    สุดท้ายเราเลือกแนวทางที่ค่อนข้างเรียบง่าย คือ replication ทางเดียวด้วย pglogical เคยทำการย้ายแบบไม่หยุดให้บริการจาก Google Cloud SQL มา AWS RDS ด้วยวิธีเดียวกันมาก่อน จึงมั่นใจว่ามันจะทำงานได้โดยไม่ส่งผลที่ลูกค้ารับรู้ได้
    pglogical ทำให้ migration แบบนี้ง่ายขึ้นมาก แม้จะไม่ได้เร็วเสมอไป แต่ถ้ารอได้หลายวันให้ทั้งฐานข้อมูลค่อย ๆ ถูก replicate ไปยัง instance ใหม่ ก็นับว่าใช้ได้
    วิธีนี้ยังให้อิสระมากขึ้นในการเปลี่ยนประเภทและขนาดของ storage ด้วย เราจัด storage ไว้เกินความจำเป็นเพื่อให้ได้ IOPS เลยอยากเปลี่ยนประเภท storage และลดขนาดลงด้วย ดังนั้นการ restore จาก snapshot ตรง ๆ จึงใช้ไม่ได้

  • ชวนให้นึกว่านี่คือฟีเจอร์ที่ AWS เคยสัญญาไว้ตอนช่วง “sales engineering” หรือเปล่า
    แต่พอถึงเวลาที่ต้องบังคับทำ major version upgrade จริง ๆ กลับให้ไม่ได้

  • น่าแปลกใจที่ไม่สามารถเริ่มต้น replica จาก backup ได้
    ถ้าทำได้ก็คงลดความลำบากในการสตรีมข้อมูลจากฐานข้อมูลเดิมที่เสถียรไปยังเซิร์ฟเวอร์ใหม่ได้มาก
    และนี่ก็ไม่ใช่ “ไร้ downtime” จริง ๆ เพราะยังมี downtime ไม่กี่วินาทีตอนสลับบริการไปยังเซิร์ฟเวอร์ใหม่
    บทความไม่ได้อธิบายว่ารักษาความสอดคล้องกันอย่างไร เช่น จะให้แอปเชื่อมต่อกับทั้งสองเซิร์ฟเวอร์ไปพร้อมกันช่วงหนึ่งแบบนั้นไม่ได้ การอ่านอาจให้บริการจากทั้งคู่ได้ แต่ก็ยังไม่สมบูรณ์ และการเขียนต้องไปที่เซิร์ฟเวอร์เดียวเท่านั้น
    สุดท้ายคือไม่มีตัวเลือก rollback งานย้ายข้อมูลขนาดใหญ่อย่างการยกทั้งก้อนแบบนี้มักมีปัญหาตอนดึก ๆ เสมอ จึงต้องมีแผนที่มั่นใจได้ว่าสามารถย้อนกลับไปขั้นก่อนหน้าและนอนหลับได้โดยเชื่อว่าระบบจะยังให้บริการได้ในตอนเช้า
    โดยเฉพาะถ้าส่ง write transaction ไปยังเซิร์ฟเวอร์ใหม่แล้ว แล้วต้องกลับไปใช้เซิร์ฟเวอร์เก่าไม่ว่าด้วยเหตุผลใดก็ตาม เรื่องจะยากมาก และข้อมูลก็ไม่สอดคล้องกันไปแล้ว

    • ในฐานะ OP สามารถเริ่มต้น replica จาก backup ได้ แต่จะไม่ได้ write ที่เกิดขึ้นต่อเนื่องระหว่างทำ backup
      ถ้าไม่มีวิธี replication บางอย่างหรือไม่ย้ายเรื่องนี้ขึ้นไปจัดการที่ชั้นแอปพลิเคชัน ระบบที่ restore แล้วก็จะมี write ที่หายไป
      ตัวอย่างเช่น อาจแก้แอปให้ทำ dual write ได้ ผมเข้าใจว่าทีมที่ replatform ทั้งแอปไปยังฐานข้อมูลคนละแบบโดยสิ้นเชิงอย่าง Apache Cassandra จาก RDBMS ก็ทำแบบนั้นเหมือนกัน
      ในกรณีของเรา dual write ดูเสี่ยงกว่าการตั้งค่า streaming replication ด้วยความสามารถพื้นฐานของ PostgreSQL แต่สำหรับบางทีมมันอาจเป็นทางเลือกที่ดีกว่า
      เรื่อง “ไม่ใช่ไร้ downtime” และ “ขาดรายละเอียดการรักษาความสอดคล้อง” บทความได้อธิบายละเอียดถึงวิธีรักษาความสอดคล้องและหลีกเลี่ยง API downtime แล้ว ประเด็นสำคัญคือแอปเชื่อมต่อกับฐานข้อมูลทั้งสอง แต่ยังไม่ได้ใช้ฐานข้อมูลใหม่เป็นตัวหลักสำหรับการเขียน
      จากนั้นเราใช้ LaunchDarkly ส่งสัญญาณสลับให้กับทุกอินสแตนซ์ของแอป โดย LaunchDarkly รักษาการเชื่อมต่อแบบหน่วงต่ำกับทุกอินสแตนซ์ไว้
      ใน 1 วินาทีแรกหลังสัญญาณ เซิร์ฟเวอร์จะนำคำขอฐานข้อมูลไปเข้าคิวไว้เพื่อให้ replication ตามทัน เรื่องนี้ทำให้เกิด latency spike ชั่วครู่ แต่ยังอยู่ในขอบเขตที่เราคำนวณและยอมรับไว้โดยตั้งใจ หลังจากหยุดชั่วคราวนั้น คำขอก็ไหลตามปกติแต่ไปยังฐานข้อมูลใหม่ และการสลับก็เสร็จสมบูรณ์
      สำหรับทราฟฟิกที่ยังค้างอยู่บนฐานข้อมูลเดิม เราใส่การบังคับตัดการเชื่อมต่อด้วย timeout 500ms ด้วย ค่านี้สูงกว่าเวลา query แบบ p99 มาก จึงไม่ได้ไป kill query ที่กำลังรันอยู่ วิธีนี้ทำให้ทราฟฟิกไปฐานข้อมูลเดิมหยุดลง และเปิดเวลามากพอให้ replication ตามทัน
      ตัวเลือก rollback ไม่ได้ใส่ไว้ในโพสต์บล็อก แต่เราเคยพิจารณาวิธีสร้างฐานข้อมูลสำรองของ PG 11.9 แล้วให้ฐานข้อมูล 15.3 ทำ replication ไปยังฐานข้อมูลตัวที่สามนั้น หากต้องยกเลิกกลางคันก็จะสามารถ roll forward ไปยังฐานข้อมูลเวอร์ชันเดียวกันนี้ได้
      หลังจากซ้อมขั้นตอนอัปเกรดใน staging หลายรอบจนมั่นใจในโอกาสสำเร็จ เราก็ตัดสินใจไม่ใช้แนวทางนี้ เพราะซ้อมหลายครั้งแล้วจึงมั่นใจในการสลับจริง นอกจากนี้ใน production เรายังตรวจสอบบาง workload แบบอ่านอย่างเดียวกับอินสแตนซ์ 15.3 ผ่าน canary deployment และปฏิบัติต่อมันเหมือน read replica
      เพื่อเลี่ยงปัญหาตอนดึก เราจงใจทำในช่วงหัวค่ำของวันหยุดสุดสัปดาห์ การสลับถูกทำเป็นสคริปต์และซ้อมอย่างรอบคอบเพื่อลดความเสี่ยงจากความผิดพลาดของมนุษย์
      หากเกิดความล้มเหลวร้ายแรง ระบบก็พร้อมจะย้อนกลับไปฐานข้อมูลเดิมเช่นกัน ในกรณีนั้นคงมีข้อมูลบางส่วนที่ถูกเขียนเข้าไปในฐานข้อมูลใหม่สูญหาย และเราเตรียมพร้อมจะ reconcile ส่วนสำคัญไว้แล้ว เพื่อลดความเสี่ยงเรื่องข้อมูลสูญหาย เราหยุดงานเบื้องหลังบางอย่างชั่วคราวระหว่างการสลับเพื่อลดจำนวนการเขียน
      รายละเอียดเหล่านี้ไม่ได้ใส่ไว้ในบล็อก เพราะต้องการเน้นรายละเอียดที่เกี่ยวกับ PostgreSQL มากกว่าข้อพิจารณาเฉพาะของ Knock ทีมที่ต้องการนำ playbook นี้ไปใช้ควรทำรายการความเสี่ยงและแนวทางบรรเทาตามบริบทของตัวเองเสมอ
  • ส่วนที่เกี่ยวกับ sequence น่าสนใจมากจริง ๆ
    ช่วงหลังมานี้ผมแทบไม่ใช้ sequence แล้ว ส่วนใหญ่ใช้ sequential UUID, UUID v7 หรือวิธีอย่าง HiLo แทน
    https://en.wikipedia.org/wiki/Hi/Lo_algorithm

    • จนกว่า PostgreSQL จะรองรับแบบเนทีฟ สำหรับคนที่อยากคงความรับผิดชอบในการ สร้าง UUID v7 ไว้ในฐานข้อมูล ฟังก์ชัน PL/pgSQL อาจช่วยได้
      แนวทางคือสร้าง sequence 12 บิตตามร่างมาตรฐาน IETF แล้วประกอบ UUID จากค่า UNIX epoch ปัจจุบันในหน่วยมิลลิวินาทีร่วมกับบิตสุ่ม 62 บิต
      ใจความหลักคือมี uuidv7_seq และให้ฟังก์ชัน generate_uuidv7() ใช้ clock_timestamp(), NEXTVAL, RANDOM() เพื่อคืนค่าที่มีรูปแบบ UUID v7
    • ในฐานะ OP ผมหลีกเลี่ยง sequence ยกเว้นในแอปหนึ่งจุดเพราะ dependency
      หลายที่เราใช้ KSUID กับ UUID v4 “กับดัก” นี้ใช้ได้กับทุก sequence จึงควรยกเป็นคำแนะนำทั่วไปเมื่อทำ migration แบบนี้
      [1]: https://segment.com/blog/a-brief-history-of-the-uuid/
  • ไม่ได้ตั้งใจจะลดทอนความยิ่งใหญ่ของงานที่ทำสำเร็จนี้ แต่สงสัยว่าทำไมไม่ อัปเกรดทีละน้อย ทุกครั้งที่มีเวอร์ชันใหม่ออกมา
    อ่านแล้วน่าสนใจมาก แต่ให้ความรู้สึกเหมือนเรื่องของกะลาสีที่เลือกฝ่าเข้ากลางพายุใหญ่ทั้งที่รู้ว่าสามารถอ้อมได้และถ้าโชคร้ายอาจจบแบบโศกนาฏกรรม
    ในกรณีนี้การอัปเกรดเล็ก ๆ ไม่ใช่ตัวเลือกหรือ? หรือเป็นแนวคิดประมาณว่า “แม้แต่อัปเกรดเล็กหนึ่งครั้งก็มีต้นทุน downtime พอ ๆ กับการอัปเกรดใหญ่ เลยเลื่อนออกไปให้นานที่สุด” เห็นมีนัยแบบนั้นอยู่ในบทนำ แต่ก็อาจจะตีความมากไปเอง

    • ในฐานะ OP แม้เป็นการอัปเกรด minor ผมก็คงใช้แนวทางเดียวกัน
      ไม่ได้ถึงกับ “ผัดวันไปจนจนมุม” แต่เป็นแนวคิดประมาณ “ถ้ามันยังไม่พัง ก็ไม่ต้องไปซ่อม” มากกว่า
    • การอัปขึ้น N เวอร์ชัน ไม่ว่า N จะเป็น 1 หรือ 3 ก็แทบไม่ต่างกันในแง่ ภัยคุกคามต่อความพร้อมใช้งาน
    • การอัปเกรดแต่ละครั้งมี downtime
      ต่อให้คำตอบจริงจะต่ำกว่า 60 วินาที แต่ก็คงต้องเจอ downtime นั้นหลายรอบระหว่างทางไปถึง 15