Durability & Persistence
บททดสอบ restart
หัวข้อที่มีชื่อว่า “บททดสอบ restart”ทุกอย่างที่ผ่านมาสมมติว่า broker ยังรันอยู่ แต่ broker restart ได้ — deploy, crash, kernel update คำถามที่แยก setup เล่น ๆ ออกจาก production คือ: เมื่อ RabbitMQ restart อะไรยังอยู่?
โดย default น้อยจนน่าผิดหวัง queue กับ message อยู่ใน memory และ restart จะล้างทิ้งเว้นแต่คุณขอ durability ไว้ — และนี่คือกับดักที่เกือบทุกคนพลาด: ต้องใช้ setting สอง ตัวที่เป็นอิสระต่อกัน และถ้ามีตัวเดียวโดยขาดอีกตัวก็ยังทำข้อมูลหาย
สอง setting ต้องมีทั้งคู่
หัวข้อที่มีชื่อว่า “สอง setting ต้องมีทั้งคู่”การรอด restart ต้องเป็นจริงทั้งสองข้อ:
- queue เป็น durable นิยาม ของ durable queue ถูกเขียนลง disk ดังนั้น queue ยังอยู่หลัง restart ตั้ง
durable: trueตอน declare - message เป็น persistent body ของ persistent message ถูกเขียนลง disk ตั้ง
delivery_mode = 2(persistent) ตอน publish
ขาดข้อใดข้อหนึ่งก็เสียข้อมูลตอน restart:
flowchart TB a["durable queue + persistent message"] -->|restart| a2["survives"] b["durable queue + transient message"] -->|restart| b2["queue stays, MESSAGES LOST"] c["non-durable queue + persistent message"] -->|restart| c2["QUEUE GONE (messages with it)"] d["non-durable + transient"] -->|restart| d2["all lost"]
incident production คลาสสิก: ทีม declare durable queue รู้สึกปลอดภัย แล้วลืม publish message แบบ persistent queue รอดทุก restart อย่างซื่อสัตย์ — แบบว่าง ๆ เพราะ message ข้างในเป็น transient durable queue, transient message: queue อยู่ ข้อมูลตาย
// 1) durable queue — survives restartawait channel.assertQueue('orders', { durable: true });
// 2) persistent message — body written to diskchannel.sendToQueue('orders', Buffer.from(body), { persistent: true });# 1) durable queuechannel.queue_declare(queue="orders", durable=True)
# 2) persistent message (delivery_mode=2)channel.basic_publish( exchange="", routing_key="orders", body=body, properties=pika.BasicProperties(delivery_mode=2),)// 1) durable queuech.QueueDeclare("orders", true, false, false, false, nil)
// 2) persistent messagech.PublishWithContext(ctx, "", "orders", false, false, amqp.Publishing{ DeliveryMode: amqp.Persistent, // = 2 Body: body,})Trade-off: durability แลกมาด้วย latency
หัวข้อที่มีชื่อว่า “Trade-off: durability แลกมาด้วย latency”การเขียนลง disk ช้ากว่าอยู่ใน memory ดังนั้น persistence มีต้นทุน แต่สังเกตจุดละเอียดสำคัญ: การ mark message เป็น persistent ไม่ การันตีว่าอยู่บน disk ทันทีที่คุณ publish — RabbitMQ อาจ batch การเขียน เพื่อให้แน่ใจว่า message ถูกเก็บอย่างปลอดภัยก่อนคุณถือว่าส่งแล้ว ให้จับคู่ persistence กับ publisher confirm (บทเรียนก่อนหน้า): confirm จะมาถึงก็ต่อเมื่อ message ถูก persist อย่างปลอดภัยแล้วเท่านั้น
ภาพที่ซื่อสัตย์: durable queue + persistent message + publisher confirm คือคอมโบที่ปลอดภัยเต็มที่ และแลก throughput กับ latency บางส่วนเพื่อไม่ทำข้อมูลหายตอน restart สำหรับ stream ที่ volume สูงและทนการหายได้ (metrics, log) คุณอาจจงใจข้าม persistence เพื่อความเร็ว — เป็นทางเลือกที่ถูกต้อง ตัดสินใจอย่างรู้ตัว
หมายเหตุเรื่อง quorum queue
หัวข้อที่มีชื่อว่า “หมายเหตุเรื่อง quorum queue”durable queue แบบ classic รอด restart ของ หนึ่ง node — แต่ classic queue อยู่บน node เดียวทั้งหมด ดังนั้นถ้า disk ของ node นั้นพัง queue ก็ตายไปด้วย Quorum queue ยกระดับขึ้น: replicate queue ข้ามหลาย node ด้วย Raft consensus algorithm ดังนั้นข้อมูลรอดแม้เสีย node ทั้งตัว ไม่ใช่แค่ restart เป็น default สมัยใหม่สำหรับอะไรก็ตามที่ห้ามหาย — และโมดูล Operations & Scaling จะลงลึกเรื่องนี้