คู่มือเกมแลค
ไทย

คู่มือ
เกมแลค

คู่มือนี้อธิบายสาเหตุที่ทำให้ภาพกระตุก ตัวละครวาร์ป หรือหลุดจากเกม โดยแบ่งเป็น 13 ชั้นตั้งแต่หน้าจอเกมของเราไปจนถึงฐานข้อมูลของเซิร์ฟเวอร์ แต่ละสาเหตุระบุวิธียืนยันและทีมที่รับผิดชอบไว้ และตรวจดูได้ด้วยการลองเปลี่ยนเงื่อนไขในการทดลอง เนื้อหาเน้นกรณีของเกม MMO แต่ส่วนใหญ่ใช้ได้กับเกมออนไลน์ทั่วไปไม่ว่าจะเป็นแนวไหน

00เริ่มต้น

ปัจจัยสี่อย่างที่ทำให้เกิดแลค

สาเหตุมีมากกว่าร้อยอย่าง แต่ปัจจัยที่ทำให้เกิดแลคแบ่งได้เป็นสี่อย่างหลัก ๆ คือแพ็กเก็ตมาช้า, มาไม่สม่ำเสมอ, ไม่มาเลย หรือมีบางจุดหยุดคำนวณ เกมใช้หลายเทคนิคเพื่อกลบปัจจัยเหล่านี้ ร่องรอยที่กลบไม่สำเร็จคือ “รูปแบบ” ของแลคที่เราเห็น

ในเกม MMO โลกที่เราเห็นบนจอคือภาพที่วาดขึ้นใหม่จากแพ็กเก็ตที่เซิร์ฟเวอร์ส่งมา เซิร์ฟเวอร์คำนวณสถานะเกมโดยทั่วไป 10–30 ครั้งใน 1 วินาที (ต่างกันไปตามเกม การคำนวณหนึ่งครั้งเรียกว่า ทิก) แล้วเลือกเฉพาะสิ่งที่เปลี่ยนไปรอบตัวผู้เล่นแต่ละคนส่งออกไปเป็นแพ็กเก็ต PC ของเราอ่านแพ็กเก็ตที่มาถึงแล้ววาดภาพบนจอ แลคส่วนใหญ่จึงเป็นเรื่องที่ว่าเกมแสดงผลอย่างไรเมื่อ “แพ็กเก็ตมาไม่ทันเวลา”

มีบางกรณีที่อยู่นอกปัจจัยทั้งสี่ ถ้าเซิร์ฟเวอร์กับ PC ของเราคำนวณเรื่องเดียวกันออกมาต่างกัน (กฎการเคลื่อนที่ไม่ตรงกัน, บั๊ก) ถึงเน็ตจะปกติดีก็ยังเกิดอาการดีดกลับหรือมองไม่เห็นได้ เบาะแสของแลคแบบนี้คือเกิดซ้ำที่จุดเดิมหรือตอนทำแอ็กชันเดิม โดยไม่เกี่ยวกับปิง

จากปัจจัยสู่อาการ

เปรียบเทียบ

ลองนึกถึงการส่งพัสดุ ถ้าทุกกล่องใช้เวลา 3 วันเสมอคือความหน่วง (latency) ถ้าบางกล่องใช้หนึ่งวันแต่บางกล่องใช้ห้าวันคือจิตเตอร์ ถ้ากล่องหายคือแพ็กเก็ตหาย และถ้าศูนย์กระจายสินค้าปิดคือการหยุดชะงัก เกมประคองตัวด้วยวิธีอย่าง “ถ้ากล่องมาช้าหรือไม่มา ก็เดาจากกล่องก่อนหน้าและกล่องถัดไปแล้วเติมให้ (interpolation, extrapolation)” และ “ถ้าไม่มา ก็ขอให้ส่งมาใหม่ (การส่งซ้ำ)”

เริ่มจากสเกลเวลาที่ควรรู้

เรื่องแลคส่วนใหญ่พูดกันในหน่วยมิลลิวินาที (ms, 1/1000 วินาที) แค่จำตัวเลขในตารางด้านล่างไว้ไม่กี่ตัว ก็จะเข้าใจสิ่งที่ทีมพัฒนาเกมและทีมอินฟราพูดได้ง่ายขึ้นมาก

รายการเวลาความหมาย

คุณสมบัติของคิวที่เหมือนกันในทุกชั้น

CPU, ดิสก์, ฐานข้อมูล, เราเตอร์ หรือลิงก์ของ ISP ชั้นต่างกันแต่โครงสร้างเหมือนกัน คือมี worker (คอร์ CPU, เธรด, การเชื่อมต่อ DB เป็นต้น) คอยประมวลผลคำขอ และมีคิวเกิดขึ้นข้างหน้า ถ้า worker ว่าง คิวก็ว่าง แต่เมื่อความยุ่ง (อัตราการใช้งาน) เกิน 80–90% คิวจะยาวขึ้นอย่างรวดเร็ว เมื่อคำขอเข้ามาแบบสุ่มที่ worker ตัวเดียว เวลารอเฉลี่ยจะเท่ากับเวลาประมวลผลที่อัตราการใช้งาน 50%, เป็น 4 เท่าที่ 80% และ 9 เท่าที่ 90% นี่คือคำตอบของคำถาม “CPU ยังเหลือตั้ง 10% ทำไมถึงแลค” นอกจากนี้ ตัวเลข CPU บนหน้าจอมอนิเตอร์มักเป็นค่าเฉลี่ยของหลายคอร์ในช่วง 1–5 นาที จึงกลบสถานการณ์ที่คอร์เดียวเต็ม 100% หรือช่วงที่งานกระจุกตัวแค่ไม่กี่วินาที

สิ่งที่คู่มือนี้ครอบคลุมและไม่ครอบคลุม

คู่มือนี้ครอบคลุมสาเหตุที่ทำให้เกมออนไลน์แลคระหว่างเล่น ตั้งแต่ PC หรือมือถือของเราที่วาดภาพเกม ไปจนถึงเครือข่ายในบ้าน, ISP, ดาต้าเซ็นเตอร์, เซิร์ฟเวอร์และฐานข้อมูล ส่วนคลาวด์เกมมิ่งที่รับภาพเกมมาเป็นวิดีโอ, แชตเสียงที่รันเป็นบริการแยก และความเร็วแพตช์และดาวน์โหลด มีโครงสร้างต่างออกไปจึงไม่ครอบคลุม แต่สาเหตุด้านเครือข่ายในนั้น (Wi-Fi, bufferbloat, เครือข่ายแออัด ฯลฯ) ก็เหมือนกับสาเหตุในคู่มือนี้

01แผนที่ภาพรวม

เส้นทางของแพ็กเก็ต: จากอินพุตถึง DB ของเซิร์ฟเวอร์

เมื่อกดปุ่มสกิล สัญญาณจะผ่าน PC ของเรา, บ้าน, ISP และดาต้าเซ็นเตอร์ไปถึงเซิร์ฟเวอร์ ผลที่ผ่านการประมวลผลหลายชั้นในเซิร์ฟเวอร์จะย้อนกลับผ่านชั้นเดิมเหล่านั้นมาวาดบนหน้าจอ รวมทั้งหมด 13 ชั้น และไม่ว่าชั้นไหนติดขัดก็แลค กดที่ชั้นในแผนที่ด้านล่างเพื่อไปยังบทของชั้นนั้น

02ลองเอง

ห้องทดลองแลค

การจำลองเล็ก ๆ ที่มีเซิร์ฟเวอร์หนึ่งเครื่อง เน็ตหนึ่งเส้น และ PC ของเราหนึ่งเครื่อง ลองทำให้เงื่อนไขพังทีละอย่าง แล้วดูว่าอาการกระตุก, วาร์ป, ดีดกลับ, กรอเร็ว, สโลว์โมชั่น, อินพุตดีเลย์, ค้าง และหลุด แต่ละอย่างเกิดขึ้นได้อย่างไร ไทม์ไลน์แพ็กเก็ตแสดงเป็นเส้นว่าแพ็กเก็ตถูกส่งเมื่อไรและมาถึงเมื่อไร เส้นยิ่งเอียงมากแปลว่ายิ่งใช้เวลานาน ส่วน × คือแพ็กเก็ตที่หายไป

03ค้นหาจากรูปแบบที่เห็น

พจนานุกรมอาการ

ผู้เล่นมักพูดแค่ว่า “เกมแลค” แต่รูปแบบของแลคบอกสาเหตุได้ค่อนข้างมาก ภาพเล็กของแต่ละอาการคือร่องรอยที่ตัวละครบนจอเคลื่อนผ่าน ถ้าจุดซ้อนกันแปลว่าหยุด ถ้าจุดห่างกันแปลว่าเร็วขึ้นหรือข้ามตำแหน่งไป

กระตุก

การเคลื่อนไหวไม่ลื่น หยุดสั้น ๆ แล้วขยับต่อ สลับกันไปเรื่อย ๆ ถ้าค่าปิงปกติ มักเป็นปัญหาเฟรมบน PC ของเรา (ไคลเอนต์/OS) ถ้าปิงขึ้นลงไม่นิ่ง มักเป็นจิตเตอร์จาก Wi-Fi หรือเน็ต อย่างไรก็ตาม ปิงที่เกมแสดงมักวัดในลูปของเกมที่ทำงานทุกเฟรม เมื่อเฟรมไทม์พุ่ง ตัวเลขปิงก็พุ่งตามได้

วาร์ป

ตัวละครย้ายไปยังตำแหน่งที่ไกลออกไปในทีเดียว โดยไม่เห็นช่วงระหว่างทาง ส่วนใหญ่แปลว่าแพ็กเก็ตขาดหายไปช่วงหนึ่ง ให้ดูแพ็กเก็ตหาย, เน็ตขาดช่วงสั้น ๆ, เซิร์ฟเวอร์หยุด และ extrapolation ที่พลาด ถ้าคนอื่นปกติแต่มีคนเดียวที่วาร์ป ให้สงสัยเน็ตของคนนั้นก่อน

ดีดกลับ

ตัวละครของเรากำลังเดินหน้า แล้วโดนดึงกลับไปยังจุดที่เพิ่งผ่านมา ภาพบนจอเรา (prediction) กับผลตัดสินของเซิร์ฟเวอร์ไม่ตรงกัน อาจเพราะอินพุตของเราไปไม่ถึงเซิร์ฟเวอร์ (แพ็กเก็ตหาย), เซิร์ฟเวอร์ตรวจการเคลื่อนที่แล้วตัดทิ้ง หรือสองฝั่งคำนวณการเคลื่อนที่ไม่เหมือนกัน

กรอเร็ว

หน้าจอที่หยุดไปกลับมาขยับอีกครั้ง แล้วการเคลื่อนไหว การโจมตี และดาเมจที่สะสมไว้ก็ผ่านไปอย่างรวดเร็วในทีเดียว แพ็กเก็ตไปกองรออยู่ที่ใดที่หนึ่ง แล้วถูกปล่อยออกมาพร้อมกัน ตัวอย่างที่พบบ่อยคือการรอส่งซ้ำของ TCP, เซิร์ฟเวอร์เร่งคำนวณให้ทัน และไคลเอนต์ประมวลผลไม่ทัน

สโลว์โมชั่น

ทุกอย่างเคลื่อนที่ช้าลง การร่ายสกิลและการเดินของมอนสเตอร์ดูยืดยาด ขึ้นกับการออกแบบเซิร์ฟเวอร์ บางเกมความเร็วยังเท่าเดิม และไปแสดงเป็นอาการกระตุกหรือวาร์ปแทน เซิร์ฟเวอร์ประมวลผลทิกไม่ทันเวลา เน็ตยังปกติ ปิงที่วัดจากนอกเกมจึงเท่าเดิม ส่วนปิงในเกมอาจสูงขึ้นเล็กน้อยถ้ารวมเวลารอประมวลผลของเซิร์ฟเวอร์ไว้ด้วย ให้ดูจำนวนคนที่พุ่งขึ้น, การคำนวณระยะมองเห็น, broadcast และหน่วยความจำไม่พอ

อินพุตดีเลย์

กดแล้วต้องรอสักพักกว่าผลจะออก ตัวภาพบนจออาจยังลื่นตามปกติ เวลาไปกลับ (ปิง) สูง หรือมีคิวสะสมอยู่ที่ใดที่หนึ่ง ให้ดูระยะทาง, คิวในเราเตอร์, Nagle (ฟีเจอร์ของ TCP ที่รวบแพ็กเก็ตเล็ก ๆ ไว้ส่งทีเดียว) และคิวของเซิร์ฟเวอร์ ถ้าปิงต่ำแต่ยังตอบสนองช้าตลอด ให้ดูฝั่ง PC ของเรา เช่น V-Sync หรือ FPS ต่ำ หรือดูว่าเกมออกแบบให้ทุกการกระทำต้องรอเซิร์ฟเวอร์ยืนยันหรือไม่ (บทรูปแบบการซิงก์)

ค้าง

ทุกอย่างบนจอหยุดไปชั่วครู่ (0.5 วินาทีถึงหลายวินาที) แล้วกลับมาขยับต่อ เซิร์ฟเวอร์หยุดทั้งตัว (GC, เดดล็อก, synchronous call), เน็ตขาดไปชั่วครู่ หรือ PC ของเราหยุดทำงานชั่วขณะ

กดไม่ติด/โรลแบ็ค

การกระทำที่ทำไปแล้วแน่ ๆ กลับไม่เกิดผล หรือผลถูกพลิกกลับหลังผ่านไปพักใหญ่ คำขอหายไประหว่างทาง (แพ็กเก็ตหาย, คิวล้น), เซิร์ฟเวอร์ตัดสินต่างจากที่เห็นบนจอเรา (จังหวะเวลาตัดสินต่างกัน, เซิร์ฟเวอร์ปฏิเสธหลังแสดงผลล่วงหน้าไปแล้ว) หรือบันทึกล้มเหลวกลางทาง (DB ติดล็อกหรือล่ม, เซิร์ฟเวอร์แครช)

หลุด

การเชื่อมต่อขาดระหว่างเล่น แล้วเด้งกลับไปหน้าล็อกอินหรือหน้าต่างเชื่อมต่อใหม่ ไม่มีแพ็กเก็ตมาถึงเลยสักตัวภายในเวลาไทม์เอาต์ ให้ดูเน็ตขาดเป็นเวลานาน, idle timeout, เซิร์ฟเวอร์แครชหรือรีสตาร์ต และเซิร์ฟเวอร์หรือ PC ของเราที่หยุดนานกว่าไทม์เอาต์ (โหลดนาน) ถ้าเกมปิดตัวไปเลยโดยไม่มีข้อความแจ้ง ให้ดูว่าไคลเอนต์ถูกปิดกะทันหัน (แครช, หน่วยความจำไม่พอ) ก่อนเรื่องการเชื่อมต่อ

เข้าเกมไม่ได้/โหลดไม่จบ

เข้าไปในเกมไม่ได้ หรือติดอยู่ที่หน้าโหลดหรือหน้าเข้าเกม จุดที่รับการเชื่อมต่อใหม่ (คิวรอเชื่อมต่อของเซิร์ฟเวอร์, ไฟร์วอลล์, เซิร์ฟเวอร์ล็อกอิน, DB) เต็ม พบบ่อยเป็นพิเศษทันทีหลังปิดปรับปรุง

มองไม่เห็น/ตัวผี

NPC มอนสเตอร์ หรือผู้เล่นที่ควรจะอยู่ กลับไม่มีบนจอของเราคนเดียว หรือสิ่งที่หายไปแล้วยังเหลืออยู่บนจอของเราคนเดียว เป็นสภาพที่แพ็กเก็ตบางตัวหายไปหรือวาดไม่สำเร็จ มากกว่าจะเป็นเรื่องความเร็ว ให้ดูแชนแนลหรือเฟสที่ต่างกัน, ข้อความแจ้งการปรากฏตัว/ออกไปที่หล่นหาย, การทิ้งข้อมูลระหว่างโหลด และการโหลดแอสเซ็ตล้มเหลว เบาะแสชี้ขาดคือ ถ้าเดินออกนอกระยะมองเห็นแล้วกลับมา จะมองเห็นหรือไม่

04การออกแบบการซิงก์

รูปแบบการซิงก์และฟีลการเล่น

บางเกมเล่นที่ปิง 150 ms ก็ยังปกติ แต่บางเกมแค่ 60 ms ก็อืดแล้ว และไม่จำเป็นต้องเป็นเกมแอ็กชันด้วย ถ้าใช้เน็ตเดียวกัน ความต่างนี้ส่วนใหญ่มาจากวิธีที่ไคลเอนต์กับเซิร์ฟเวอร์ตกลงกันไว้ว่า “ใครตัดสินอะไร และเมื่อไร” ซึ่งก็คือการออกแบบการซิงก์ บางส่วนเป็นการเลือกโดยตั้งใจ และบางส่วนก็ออกแบบผิดจริง ๆ

เกมเครือข่ายทุกเกมต้องแก้ปัญหาเดียวกัน ระหว่างเซิร์ฟเวอร์กับ PC ของเรามีเวลาที่ต่างกันอยู่เสมอ และต้องมีฝ่ายใดฝ่ายหนึ่งตัดสินใจว่าจะจัดการกับ “สิ่งที่ยังไม่ได้ยืนยัน” อย่างไร ทางเลือกหลัก ๆ มีสี่ทาง

  • รอ: ไม่แสดงอะไรเลยจนกว่าเซิร์ฟเวอร์จะยืนยัน แม่นยำ แต่ปิงจะกลายเป็นความเร็วในการตอบสนองโดยตรง
  • แสดงก่อน แล้วค่อยแก้ทีหลัง: แสดงการกระทำของเราทันที ถ้าผลจากเซิร์ฟเวอร์ต่างออกไปก็แก้ให้ตรง เร็ว แต่บางครั้งจะเห็นอาการดีดกลับหรือการกระทำถูกยกเลิก
  • นัดเวลาไว้ล่วงหน้า: แจ้งพร้อมเวลาในอนาคต เช่น “ทุบลงพื้นในอีก 1.5 วินาที” ถ้าช่วงเวลาแสดงผลยาวกว่าปิง จะมองไม่เห็นผลของปิงเลย
  • ทุกคนคำนวณแบบเดียวกัน: รับส่งกันแค่อินพุต แล้วต่างคนต่างคำนวณให้ได้ผลเหมือนกัน (lockstep, rollback) ปริมาณข้อมูลที่ส่งน้อย แต่ความหน่วงของคนเดียวลามไปถึงทุกคน

ความไวต่อปิงจึงขึ้นอยู่กับคำถามสองข้อมากกว่าแนวเกม คือ แอ็กชันหลักหนึ่งครั้งต้องรอไปกลับเซิร์ฟเวอร์กี่รอบ และ เวลาที่กฎของเกมเปิดให้ยาวพอเมื่อเทียบกับ “ปิง + เวลาตอบสนองของคน” หรือไม่

รูปแบบการซิงก์ที่ใช้กันบ่อย

รูปแบบทำงานอย่างไรพบบ่อยในสิ่งที่เห็นที่ปิง 150 msจุดอ่อน
request-response
แสดงผลหลังเซิร์ฟเวอร์ยืนยัน
กดแล้วถามเซิร์ฟเวอร์ เมื่อคำตอบมาถึงจึงค่อยแสดงผลเกมเทิร์นเบส, เกมการ์ด, เกมแนว idle, UI ร้านค้า/เทรด/คราฟต์, การใช้สกิลและไอเทมใน MMO ยุคเก่าทุกแอ็กชันเริ่มช้าไปราว 0.2 วินาที ถ้าเป็นเกมเทิร์นเบสแทบไม่รู้สึกแอ็กชันต่อเนื่อง, UI ที่ต้องไปกลับหลายรอบในหน้าจอเดียว
ซิงก์สถานะ + interpolation
server authoritative
เซิร์ฟเวอร์ส่งสถานะเกมทุกทิก ไคลเอนต์วาดเชื่อมระหว่างสองสถานะการแสดงผู้เล่นอื่นและมอนสเตอร์ใน MMO ส่วนใหญ่เห็นคนอื่นเป็นภาพในอดีตราว 0.2 วินาที ปกติแทบไม่รู้สึกจิตเตอร์ (ช่วงเวลาที่แพ็กเก็ตมาถึงไม่สม่ำเสมอ) และแพ็กเก็ตหาย → วาร์ป, ทิกเรตต่ำ
client-side prediction + reconciliationอินพุตของเราแสดงผลทันที เมื่อผลจากเซิร์ฟเวอร์มาถึงก็เทียบแล้วแก้ให้ตรงFPS, MMO แนวแอ็กชัน, การเคลื่อนที่ใน MMO ส่วนใหญ่การควบคุมของเราตอบสนองทันที บางครั้งดีดกลับสั้น ๆถ้าคำนวณไม่ตรงกับเซิร์ฟเวอร์ จะถูกแก้ตำแหน่งบ่อย
lag compensation
เซิร์ฟเวอร์ย้อนเวลาเพื่อตัดสินผล
เซิร์ฟเวอร์ย้อนเวลากลับไปยังจังหวะในอดีตที่ผู้โจมตีเห็น แล้วตัดสินว่าโดนหรือไม่FPS, เกมแอ็กชันแบบ non-targetคนยิงรู้สึกว่ายุติธรรม แต่คนโดนยิงรู้สึกว่า “หลบหลังที่กำบังแล้วยังโดน”ฝั่งที่โดนรู้สึกไม่ยุติธรรม ยิ่งผู้โจมตีปิงสูงยิ่งย้อนเวลามาก อาการยิ่งหนัก
ซิงก์คำสั่ง/จุดหมายส่งแค่เจตนา เช่น “ไปตรงนี้” หรือ “โจมตีเป้าหมายนี้” แล้วทั้งสองฝั่งคำนวณเองMMO แบบคลิกเดิน, การต่อสู้แบบ tab target, MOBA บางเกมแค่ออกตัวช้าไปนิด การเคลื่อนที่และการโจมตียังลื่นไหลถ้าเส้นทางหรือผลลัพธ์ไม่ตรงกันต้องแก้ให้ตรง
นัดเวลาอีเวนต์ล่วงหน้า
อิงเวลาของเซิร์ฟเวอร์
แจ้งพร้อมเวลาในอนาคต เช่น “เริ่มที่เวลาเซิร์ฟเวอร์ T” แล้วแต่ละฝั่งเล่นตามเวลานั้นแพตเทิร์นบอสในเรด, คัตซีน, อีเวนต์ตรงเวลาถ้าสัญญาณเตือนยาวกว่าปิง แทบไม่มีผลเลยถ้ามาถึงช้ากว่าเวลาที่นัดไว้ ช่วงต้นจะถูกข้ามไป
deterministic lockstepรวบรวมอินพุตของทุกคน แล้วคำนวณแบบเดียวกันในเทิร์นเดียวกัน และใส่ input delay คงที่ให้ทุกอินพุตRTS (แนว StarCraft), เกม co-op และเกมพัซเซิลบางเกมอินพุตทุกอย่างช้าเท่า ๆ กัน (กลบด้วยเสียงเอฟเฟกต์หรือการแสดงผลทันทีที่กด) ถ้าจิตเตอร์สูง ทุกคนค้างพร้อมกันจิตเตอร์/แพ็กเก็ตหาย, คนที่ช้าที่สุดหนึ่งคน
rollback
คาดการณ์ก่อนแล้วย้อนเวลา
คาดการณ์อินพุตของอีกฝ่ายแล้วเดินเกมไปก่อน ถ้าคาดผิดก็ย้อนกลับไปอดีตแล้วคำนวณใหม่เกมต่อสู้ (แนว GGPO), เกมแอ็กชันและเกมกีฬาบางเกมฟีลการควบคุมแทบจะทันที (ปกติ input delay 1–3 เฟรม) ท่าทางของอีกฝ่ายกระโดดข้ามไปไม่กี่เฟรมเป็นบางครั้งถ้าปิงสูง ช่วงที่ย้อนเวลาจะกว้างขึ้นจนดูเหมือนวาร์ป
client authoritativeแต่ละฝั่งตัดสินผลของตัวเอง เซิร์ฟเวอร์ทำแค่ส่งต่อและบันทึกเกมมือถือและแคชวลบางเกม, โครงสร้างแบบ P2P/relayจอของเราลื่น แต่ผลลัพธ์ไม่ตรงกับจอของคนอื่นการโกง, “เรายิงโดนแล้วแต่ไม่โดน”

เกมจริงใช้หลายรูปแบบผสมกัน โดยปกติจะเลือกต่างกันไปตามแอ็กชัน เช่น การเคลื่อนที่ใช้ prediction, สกิลใช้การแสดงผลล่วงหน้าแล้วค่อยยืนยัน, แพตเทิร์นบอสใช้การนัดเวลาอีเวนต์ล่วงหน้า และการเทรดใช้ request-response

สิ่งที่เกมที่เล่นลื่นแม้ปิง 150 ms มีเหมือนกัน

1. แอ็กชันหนึ่งครั้งไม่ต้องรอไปกลับเลยแม้แต่รอบเดียว กดปุ่มแล้วเริ่มแอนิเมชัน เสียงเอฟเฟกต์ และเอฟเฟกต์ทันที (การแสดงผลล่วงหน้า) ส่วนผลจากเซิร์ฟเวอร์ใช้เฉพาะกับส่วนที่มาช้าแล้วไม่มีใครสังเกต อย่างตัวเลขดาเมจ

2. เวลาที่กฎของเกมเปิดให้ยาวกว่าปิงพอสมควร ถ้าสัญญาณเตือนของบอสยาว 1–2 วินาที ต่อให้แพ็กเก็ตมาช้าราว 0.2 วินาที และคนใช้เวลาตอบสนองอีก 0.25 วินาที ก็ยังหลบทันสบาย ๆ สกิลของเราก็เช่นกัน ถ้ามีเวลาร่าย การยืนยันจากเซิร์ฟเวอร์จะเสร็จไปพร้อมกับตอนที่แถบร่ายเต็ม เวลารอจึงซ่อนอยู่ในเวลาร่าย นี่คือเหตุผลหลักที่ MMO แบบ tab target ไม่ค่อยไวต่อปิง ในทางกลับกัน สัญญาณเตือนสั้น ๆ ราว 0.5 วินาที แค่ปิง 150 ms ก็ดูแล้วหลบได้ยาก (ดูการทดลองช่วงเวลาตัดสินด้านล่าง)

3. รับแอ็กชันต่อเนื่องไว้ล่วงหน้า ถ้ามีการกดล่วงหน้า (คิวสกิล) ที่รับสกิลถัดไปไว้แม้จะกดก่อนคูลดาวน์หมด ก็จะไม่มีเวลาไปกลับแทรกระหว่างคอมโบ

4. ดูดซับจิตเตอร์ interpolation buffer และการแสดงผลที่อิงเวลาของเซิร์ฟเวอร์ เปลี่ยนแพ็กเก็ตที่ “ส่วนใหญ่ใช้ 150 ms บางครั้ง 250 ms” ให้กลายเป็นกระแสข้อมูลที่ช้าคงที่ราว 250 ms เสมอ แลกกับการเห็นภาพในอดีตนานขึ้นอีกนิด แต่ภาพจะลื่น คนเราชินกับความช้าที่คงที่ได้เร็ว แต่ชินกับความไม่สม่ำเสมอได้ยาก เกมที่ทำมาดีจะเพิ่มหรือลดความยาวบัฟเฟอร์เองตามจิตเตอร์ที่สูงขึ้นหรือต่ำลง

5. การตัดสินผลตรงกับ “สิ่งที่เราเห็น” ตัดสินการหลบและการโจมตีโดนตามจังหวะที่ผู้เล่นเห็น (lag compensation) หรือใช้กฎที่ตำแหน่งไม่สำคัญตั้งแต่แรก (การเลือกเป้าหมาย)

6. ความหน่วงของคนเดียวไม่ทำให้คนอื่นต้องรอ ในโครงสร้าง server authoritative ต่อให้ปิงของเราแย่ คนอื่นก็ยังเล่นได้ปกติ ส่วนโครงสร้าง lockstep หรือแบบหัวห้อง (host) คนที่ช้าที่สุดคนเดียวเป็นตัวกำหนดฟีลของทุกคน

แยกการออกแบบโดยตั้งใจออกจากการออกแบบที่ผิดพลาด

สาเหตุที่เกมไวต่อปิง บางครั้งมาจากการออกแบบโดยตั้งใจ และบางครั้งก็มาจากความผิดพลาดในการพัฒนา

อาจเป็นการเลือกโดยตั้งใจ
  • ช่วงเวลาตัดสินสั้น: เกมที่ความสนุกอยู่ที่ช่วงเวลาตัดสินสั้น ๆ เอง เช่น แพร์รี 0.2 วินาที หรือ just dodge เวลาตอบสนองจะหายไปเท่ากับปิง และจิตเตอร์ทำให้จังหวะเพี้ยน ซึ่งเลี่ยงไม่ได้ จึงลดผลกระทบด้วย lag compensation หรือเซิร์ฟเวอร์ประจำภูมิภาค
  • ให้เซิร์ฟเวอร์ยืนยันเพื่อกันการโกง: ผลลัพธ์ที่ห้ามถูกหลอกเด็ดขาด เช่น เงินในเกม ไอเทม และอันดับ ควรรอการยืนยันจากเซิร์ฟเวอร์
  • lockstep: เป็นโครงสร้างที่ใช้งานได้จริงที่สุดถ้าต้องซิงก์ยูนิตหลายร้อยตัวด้วยอินพุตอย่างเดียว แต่ต้องปรับ input delay ตามปิง
  • ความยุติธรรม: บางเกมเลือกจงใจลด lag compensation ลง เพื่อไม่ให้ฝั่งที่โดนโจมตีรู้สึกไม่ยุติธรรมเพราะคนที่ปิงสูง
สัญญาณว่าน่าจะออกแบบผิด
  • เกมที่บังคับตัวละครตรง ๆ ด้วยคีย์บอร์ดหรือจอย แต่การเคลื่อนที่และการโจมตีปกติก็ยังต้องรอเซิร์ฟเวอร์ยืนยัน ถ้าทำโดยไม่มี prediction ปิงจะกลายเป็นฟีลการเล่นตรง ๆ ส่วนการควบคุมแบบสั่งการอย่างคลิกเดิน ต่อให้รอเซิร์ฟเวอร์ยืนยันก็ไม่ค่อยรู้สึก บางเกมอย่าง MOBA จึงจงใจเลือกแบบนี้
  • UI หนึ่งครั้งต้องไปกลับหลายรอบ: ถ้าเปิดหน้าต่าง → รับรายการ → ยืนยัน → ซื้อ แต่ละขั้นต้องไปกลับหนึ่งรอบ ที่ปิง 150 ms จะใช้เวลา 0.7–0.8 วินาที ซึ่งรวบเป็นรอบเดียวได้
  • ปิงของเน็ตต่ำ แต่ตอบสนองช้าตลอด: ให้สงสัย Nagle (การทำงานปกติของ TCP ที่รวบแพ็กเก็ตเล็ก ๆ ไว้ส่งทีเดียว ปิดได้ด้วย TCP_NODELAY), การรอสองชั้นที่เก็บคำขอไว้จนถึงทิกถัดไปแล้วส่งผลกลับในทิกถัดไปอีก และโครงสร้างที่ต้องบันทึก DB ให้เสร็จก่อนจึงตอบทุกแอ็กชัน V-Sync หรือ FPS ต่ำบน PC ของเราก็ให้ความรู้สึกแบบเดียวกัน
  • ไม่มีคิวสกิล ต้อง “รอยืนยันก่อนจึงกดต่อได้”: ทุกคอมโบมีเวลาไปกลับแทรก DPS จึงลดลงตามปิง
  • เล่นทันทีที่มาถึง: ถ้าแสดงผลตามลำดับที่ได้รับโดยไม่มี interpolation buffer หรือเวลาของเซิร์ฟเวอร์ จิตเตอร์จะกลายเป็นอาการกระตุกของแอนิเมชันโดยตรง

สาเหตุของแลคที่มาจากการออกแบบการซิงก์

แสดงผลหลังเซิร์ฟเวอร์ตอบเท่านั้น (แบบ request-response) Request-response (no client-side feedback)

กดปุ่มแล้วไม่มีทั้งแอนิเมชันและเสียงจนกว่าคำตอบจากเซิร์ฟเวอร์จะมาถึง ปิงจึงกลายเป็นความเร็วในการตอบสนองโดยตรง

ทำไม: แสดงผลสกิล, การเดิน และการเก็บของ หลังจากเซิร์ฟเวอร์ยืนยันแล้ว → ผลคือ: ตั้งแต่วินาทีที่กดจะไม่มีปฏิกิริยาใด ๆ เป็นเวลาเท่ากับเวลาไปกลับ + เวลารอทิก → บนหน้าจอ: ถ้าปิง 150 ms ทุกการกระทำจะตอบสนองช้าไปครั้งละ 0.2 วินาที

อาการ: อินพุตดีเลย์ · ผู้รับผิดชอบหลัก พัฒนาไคลเอนต์ (ทีมพัฒนาเกม) · ร่วมกับ พัฒนาเซิร์ฟเวอร์ (ทีมพัฒนาเกม)

โปรโตคอลที่ต้องไปกลับตามลำดับหลายรอบ (chatty) Chatty protocol / sequential round trips

ถ้าการกระทำหนึ่งครั้งต้องไปกลับเซิร์ฟเวอร์หลายรอบตามลำดับ ปิงจะคูณตามจำนวนรอบนั้น

ทำไม: เปิดร้านค้า → ขอรายการ → ตรวจราคา → ซื้อ → อัปเดตกระเป๋า แยกเป็นคำขอทีละรายการ → ผลคือ: ต้องได้คำตอบของคำขอก่อนหน้าก่อนจึงส่งคำขอถัดไป → บนหน้าจอ: ที่ปิง 150 ms ซื้อของครั้งเดียวใช้เวลาเกือบ 1 วินาที และโหลดนานผิดปกติ

อาการ: อินพุตดีเลย์, เข้าเกมไม่ได้/โหลดไม่จบ · ผู้รับผิดชอบหลัก พัฒนาเซิร์ฟเวอร์ (ทีมพัฒนาเกม) · ร่วมกับ พัฒนาไคลเอนต์ (ทีมพัฒนาเกม)

ไม่มีการกดสกิลล่วงหน้า No input/spell queue

ถ้าต้องได้การยืนยันจากเซิร์ฟเวอร์ว่าสกิลก่อนหน้าจบแล้วจึงจะกดสกิลถัดไปได้ ทุกคอมโบจะมีเวลาไปกลับแทรกเข้ามา

ทำไม: รับอินพุตสกิลถัดไปเฉพาะ “หลังสกิลก่อนหน้ายืนยันแล้ว” เท่านั้น → ผลคือ: ระหว่างสกิลแต่ละครั้งมีช่วงว่างเท่ากับปิง → บนหน้าจอ: เกิดช่องว่างระหว่างคอมโบทุกครั้ง และยิ่งปิงสูง DPS ยิ่งลดลง

อาการ: อินพุตดีเลย์, กดไม่ติด/โรลแบ็ค · ผู้รับผิดชอบหลัก พัฒนาไคลเอนต์ (ทีมพัฒนาเกม) · ร่วมกับ พัฒนาเซิร์ฟเวอร์ (ทีมพัฒนาเกม)

ช่วงเวลาตัดสินสั้นจนปิงกินหมด Timing window too short for latency + reaction

ถ้าเวลาที่ต้องตอบสนอง เช่น การหลบ, การแพร์รี และการกัน สั้นเกินไป ปิงจะกินเวลานั้นไปจนเกิดการโจมตีที่หลบไม่ได้

ทำไม: ช่วงเวลาตัดสินที่สั้น เช่น สัญญาณเตือนท่าโจมตีของบอส 0.5 วินาที หรือช่วงเวลาตัดสินการแพร์รี 0.2 วินาที → ผลคือ: เห็นสัญญาณเตือนช้า (ดีเลย์ขาลง + interpolation) และอินพุตของเราก็ไปถึงช้า (ดีเลย์ขาขึ้น + เวลารอทิก) → บนหน้าจอ: หลบพ้นแน่ ๆ แต่ยังโดน, แพร์รีไม่ออก

อาการ: กดไม่ติด/โรลแบ็ค, อินพุตดีเลย์ · ผู้รับผิดชอบหลัก พัฒนาเซิร์ฟเวอร์ (ทีมพัฒนาเกม) · ร่วมกับ พัฒนาไคลเอนต์ (ทีมพัฒนาเกม), อินฟราเซิร์ฟเวอร์ (ทีมอินฟรา)

การตัดสินผลที่ไม่มี lag compensation Server-now hit validation

ถ้าเซิร์ฟเวอร์ตัดสินการโดนโดยดูแค่ “ตำแหน่งบนเซิร์ฟเวอร์ ณ ตอนนี้” ผลการตัดสินจะไม่ตรงกับที่เราเห็นบนจอ

ทำไม: คู่ต่อสู้บนจอเราอยู่ในตำแหน่งย้อนหลังไปประมาณ 0.2 วินาที (เมื่อปิง 150 ms และ interpolation 100 ms) → ผลคือ: เซิร์ฟเวอร์ตัดสินจากตำแหน่งปัจจุบัน ตรงที่เราเล็งไว้จึงไม่มีเป้าแล้ว → บนหน้าจอ: ยิงโดนแน่ ๆ แต่พลาด ต้องยิงดักหน้าเป้าที่กำลังเคลื่อนที่

อาการ: กดไม่ติด/โรลแบ็ค · ผู้รับผิดชอบหลัก พัฒนาเซิร์ฟเวอร์ (ทีมพัฒนาเกม) · ร่วมกับ พัฒนาไคลเอนต์ (ทีมพัฒนาเกม)

lag compensation มากเกินไป Excessive lag compensation

ถ้าย้อนเวลาให้ผู้โจมตีไกลเกินไป ฝ่ายที่ถูกโจมตีจะโดนทั้งที่หลบไปแล้ว

ทำไม: เซิร์ฟเวอร์ย้อนเวลาไกลเพื่อผู้โจมตีที่ปิงสูง แล้วจึงตัดสิน → ผลคือ: บนจอของฝ่ายที่ถูกโจมตี เขาเข้าที่กำบังไปแล้ว → บนหน้าจอ: “หลบหลังกำแพงแล้วยังโดน”, คนปิงสูงได้เปรียบ

อาการ: กดไม่ติด/โรลแบ็ค · ผู้รับผิดชอบหลัก พัฒนาเซิร์ฟเวอร์ (ทีมพัฒนาเกม)

client authoritative Client-authoritative results

ถ้าแต่ละคนตัดสินผลของตัวเอง จอของเราจะลื่น แต่ผลจะไม่ตรงกับจอของคนอื่นและโกงได้ง่าย

ทำไม: ไคลเอนต์เป็นผู้กำหนดตำแหน่งและการโดน เซิร์ฟเวอร์แค่ส่งต่อ → ผลคือ: สองคนต่างอ้างว่าตัวเองยิงโดนก่อน เซิร์ฟเวอร์ตรวจสอบไม่ได้ → บนหน้าจอ: อีกฝ่ายวาร์ปหรือเดินทะลุกำแพง, “ยิงโดนแล้วแต่ไม่เข้า”

อาการ: วาร์ป, กดไม่ติด/โรลแบ็ค · ผู้รับผิดชอบหลัก พัฒนาเซิร์ฟเวอร์ (ทีมพัฒนาเกม) · ร่วมกับ พัฒนาไคลเอนต์ (ทีมพัฒนาเกม)

lockstep ต้องรอผู้เล่นที่ช้าที่สุด Lockstep waits for the slowest peer

ในโครงสร้างที่ทุกคนคำนวณเทิร์นเดียวกันไปพร้อมกัน ถ้าอินพุตของคนหนึ่งช้า ทุกคนต้องรอ

ทำไม: แต่ละเทิร์นต้องได้อินพุตของผู้เล่นครบทุกคนจึงจะคำนวณได้ → ผลคือ: อินพุตของคนหนึ่งมาถึงช้าเพราะจิตเตอร์หรือแพ็กเก็ตหาย → บนหน้าจอ: ทุกคนหยุดแวบพร้อมกัน ถ้าหนักจะขึ้นหน้าต่าง “กำลังรอผู้เล่น”

อาการ: ค้าง, กระตุก, อินพุตดีเลย์ · ผู้รับผิดชอบหลัก พัฒนาเซิร์ฟเวอร์ (ทีมพัฒนาเกม) · ร่วมกับ พัฒนาไคลเอนต์ (ทีมพัฒนาเกม)

rollback netcode คาดการณ์พลาด Rollback misprediction

แสดงผลไปก่อนโดยคาดการณ์อินพุตของอีกฝ่าย ถ้าผิดจะย้อนกลับไปคำนวณใหม่ ยิ่งปิงสูง ช่วงที่ต้องย้อนยิ่งยาว

ทำไม: อีกฝ่ายเปลี่ยนอินพุต (ไม่ตรงกับที่คาดการณ์) → ผลคือ: อินพุตจริงมาถึงช้าไปครึ่งหนึ่งของปิง จึงต้องย้อนกลับไปคำนวณใหม่เท่ากับช่วงนั้น → บนหน้าจอ: ท่าทางของอีกฝ่ายกระโดดข้ามไปหลายเฟรม หรือเปลี่ยนไปกะทันหัน

อาการ: วาร์ป · ผู้รับผิดชอบหลัก พัฒนาไคลเอนต์ (ทีมพัฒนาเกม)

เล่นทันทีที่มาถึงโดยไม่มี timestamp Events played on arrival (no timestamps)

ถ้าไม่ติดเวลาที่เกิดให้อีเวนต์จากเซิร์ฟเวอร์ แล้วเล่นทันทีที่ได้รับ จังหวะการแสดงผลจะไม่สม่ำเสมอตามจิตเตอร์ของเครือข่าย

ทำไม: รันอีเวนต์ “เริ่มโจมตี” และ “เล่นเอฟเฟกต์” ทันทีที่มาถึง → ผลคือ: แต่ละแพ็กเก็ตมาถึงในเวลาต่างกัน ช่วงห่างจึงไม่สม่ำเสมอ → บนหน้าจอ: ท่าโจมตีต่อเนื่องเร็วบ้างช้าบ้าง, จังหวะแพตเทิร์นบอสไม่เหมือนเดิมทุกครั้ง

อาการ: กระตุก, กรอเร็ว · ผู้รับผิดชอบหลัก พัฒนาไคลเอนต์ (ทีมพัฒนาเกม) · ร่วมกับ พัฒนาเซิร์ฟเวอร์ (ทีมพัฒนาเกม)

รอทิกซ้อนสองชั้น Double tick quantization

ถ้าเก็บคำขอรอจนถึงทิกถัดไปแล้วจึงประมวลผล และส่งผลลัพธ์ในทิกถัดจากนั้นอีก ช่วงห่างระหว่างทิกจะถูกบวกเพิ่มสองครั้ง

ทำไม: คำขอที่ได้รับจะประมวลผลในทิกถัดไป → ผลคือ: ผลการประมวลผลก็เก็บรวมไว้ส่งในทิกส่งถัดไป → บนหน้าจอ: ปิงของเน็ตต่ำ แต่การตอบสนองช้าคงที่ประมาณ 1.5 เท่าของช่วงห่างระหว่างทิก ถ้าเซิร์ฟเวอร์ 10 ทิก จะเฉลี่ย 0.15 วินาที แย่สุด 0.2 วินาที

อาการ: อินพุตดีเลย์ · ผู้รับผิดชอบหลัก พัฒนาเซิร์ฟเวอร์ (ทีมพัฒนาเกม)

เซิร์ฟเวอร์ตรวจสอบเข้มงวดเกินไป Over-strict server validation

ถ้าเซิร์ฟเวอร์ตรวจความเร็วการเคลื่อนที่, คูลดาวน์ และระยะเข้มงวดเกินไป จะปฏิเสธแม้อินพุตปกติที่มาถึงรวมกันเพราะจิตเตอร์

ทำไม: เกณฑ์ที่เข้มงวด เช่น “ระยะที่เคลื่อนที่ได้ในหนึ่งทิก” หรือ “ยอมให้คูลดาวน์คลาดได้ 0 ms” → ผลคือ: เมื่อคำสั่งสองคำสั่งมาถึงรวมกันในทิกเดียวเพราะจิตเตอร์ จะถูกตัดสินว่าผิดกฎ → บนหน้าจอ: โดนดีดกลับ, คูลดาวน์ครบแล้วแต่สกิลถูกปฏิเสธ

อาการ: ดีดกลับ, กดไม่ติด/โรลแบ็ค · ผู้รับผิดชอบหลัก พัฒนาเซิร์ฟเวอร์ (ทีมพัฒนาเกม)

โครงสร้างแบบโฮสต์ (หัวห้อง) Listen server / host advantage

ถ้า PC ของผู้เล่นคนหนึ่งทำหน้าที่เป็นเซิร์ฟเวอร์ เน็ตและสเปก PC ของคนนั้นจะกำหนดฟีลการเล่นของทุกคน

ทำไม: PC ของหัวห้องทำหน้าที่เป็นเซิร์ฟเวอร์ (P2P, listen server) → ผลคือ: ถ้าเน็ตหรือ PC ของหัวห้องช้า จะส่งผลไปถึงทุกคน ส่วนหัวห้องปิง 0 → บนหน้าจอ: หัวห้องได้เปรียบคนเดียว ถ้าหัวห้องออก ทุกคนค้างหรือหลุด

อาการ: กระตุก, ค้าง, หลุด · ผู้รับผิดชอบหลัก พัฒนาเซิร์ฟเวอร์ (ทีมพัฒนาเกม) · ร่วมกับ พัฒนาไคลเอนต์ (ทีมพัฒนาเกม), อินฟราเซิร์ฟเวอร์ (ทีมอินฟรา)

แสดงผลล่วงหน้าแล้วเซิร์ฟเวอร์ปฏิเสธ Client-side feedback rejected by server

ถ้าการโจมตีหรือสกิลที่จอเราแสดงไปก่อนแล้วถูกเซิร์ฟเวอร์ปฏิเสธทีหลัง ผลที่เห็นกับตาจะกลายเป็นเหมือนไม่เคยเกิดขึ้น

ทำไม: เล่นเอฟเฟกต์การโจมตีและท่าสกิลก่อนเซิร์ฟเวอร์ยืนยัน (การแสดงผลล่วงหน้า) → ผลคือ: เซิร์ฟเวอร์ตรวจระยะ, ตำแหน่งเป้า, คูลดาวน์ และทรัพยากรอีกครั้งแล้วปฏิเสธ → บนหน้าจอ: เลือดกระเด็นแต่ไม่มีดาเมจ, ท่าสกิลออกแต่ไม่มีผล, มีแต่คูลดาวน์ที่เดิน

อาการ: กดไม่ติด/โรลแบ็ค, ดีดกลับ · ผู้รับผิดชอบหลัก พัฒนาไคลเอนต์ (ทีมพัฒนาเกม) · ร่วมกับ พัฒนาเซิร์ฟเวอร์ (ทีมพัฒนาเกม)

การคำนวณเส้นทางไม่ตรงกันในการซิงก์คำสั่ง Command sync with divergent pathing

ถ้ารับส่งแค่ “ให้ไปที่นี่” แล้วแต่ละฝั่งคำนวณเส้นทางเอง เมื่อการคำนวณต่างกันแม้เพียงเล็กน้อย ตัวละครหรือมอนสเตอร์จะเดินไปคนละเส้นทางแล้วถูกดึงกลับมาที่ตำแหน่งที่ถูกต้อง

ทำไม: การเดินแบบคลิกและการไล่ตามของมอนสเตอร์ ส่งแค่จุดหมาย และไคลเอนต์คำนวณเส้นทางแยกเอง → ผลคือ: ข้อมูลภูมิประเทศต่างกัน, การชนกับตัวละครอื่น และลำดับการคำนวณที่ต่างกัน ทำให้เดินคนละเส้นทางกับเซิร์ฟเวอร์ → บนหน้าจอ: มอนสเตอร์เดินทะลุกำแพงแล้ววืดไปอยู่อีกที่, ตัวละครที่คลิกเดินเลี้ยวเหมือนไถลไป

อาการ: วาร์ป, ดีดกลับ · ผู้รับผิดชอบหลัก พัฒนาเซิร์ฟเวอร์ (ทีมพัฒนาเกม) · ร่วมกับ พัฒนาไคลเอนต์ (ทีมพัฒนาเกม)

อัตราส่งสแนปช็อตต่ำ Low snapshot / update rate

ถ้าเซิร์ฟเวอร์ส่งอัปเดตตำแหน่ง (สแนปช็อต) แค่ไม่กี่ครั้งใน 1 วินาที ต้องตั้ง interpolation buffer ให้ยาวตามไปด้วย จึงเห็นตัวละครอื่นเป็นภาพในอดีตที่ไกลขึ้น

ทำไม: ส่งอัปเดตตำแหน่งแค่ 5–10 ครั้งใน 1 วินาทีเพื่อประหยัดปริมาณการส่ง → ผลคือ: ถ้าจะวาดให้ลื่น ต้องตั้งบัฟเฟอร์เป็น 2 เท่าของช่วงห่างแพ็กเก็ต (200–400 ms) ถ้าตั้งสั้น พลาดแพ็กเก็ตเดียวตัวละครก็หยุดนิ่ง → บนหน้าจอ: เห็นอีกฝ่ายเปลี่ยนทิศช้าและไม่ตรงกับการตัดสินผล ถ้าบัฟเฟอร์สั้นจะกระตุก และวาร์ปเมื่อแพ็กเก็ตหาย

อาการ: กระตุก, วาร์ป, กดไม่ติด/โรลแบ็ค · ผู้รับผิดชอบหลัก พัฒนาเซิร์ฟเวอร์ (ทีมพัฒนาเกม) · ร่วมกับ พัฒนาไคลเอนต์ (ทีมพัฒนาเกม)

05ขอบเขตผลกระทบ

เมื่อช้าอยู่คนเดียว หรือผิดปกติอยู่ฝั่งเดียว

เคสที่สับสนที่สุดในการแจ้งแลคคือเมื่อมีแค่ไม่กี่คน หรือแค่ฝั่งเดียวที่เจอ คนที่ช้าคนเดียวจะดูเป็นอย่างไรในสายตาคนอื่น และคนนั้นทำให้คนอื่นช้าตามไปด้วยหรือไม่ ขึ้นอยู่กับวิธีที่เซิร์ฟเวอร์ประมวลผลอินพุตและรูปแบบการซิงก์ ซึ่งให้ผลต่างกันโดยสิ้นเชิง บทนี้ยังพูดถึงกรณีที่เปิดไคลเอนต์สองตัวบน PC เดียวกันแล้วมองไม่เห็น NPC อยู่ฝั่งเดียวด้วย

เมื่อช้าเฉพาะผู้เล่นบางคนหรือเน็ตบางสาย

MMO ส่วนใหญ่ในปัจจุบันใช้โครงสร้าง server authoritative เซิร์ฟเวอร์เป็นผู้ตัดสินผลทั้งหมด และไคลเอนต์วาดผลที่ได้รับ ในโครงสร้างนี้แลคส่วนใหญ่จะเกิดเฉพาะกับคนที่ช้า

  • ตัวคนที่ช้าเองจะเจออินพุตดีเลย์ คือแอ็กชันที่ต้องรอเซิร์ฟเวอร์ยืนยันอย่างการใช้สกิลหรือการเก็บของจะช้าไปเท่ากับปิง การเคลื่อนที่จะเห็นทันทีเพราะมี prediction แต่ถ้าจิตเตอร์ (ช่วงเวลาที่แพ็กเก็ตมาถึงไม่สม่ำเสมอ) สูง ก็จะเจอทั้งอาการดีดกลับและเห็นคนอื่นวาร์ปด้วย
  • คนอื่นจะเห็นแค่ตัวละครของคนที่ช้าหยุดแวบแล้วขยับรวดเดียว หรือวาร์ป การควบคุมของตัวเองและการเคลื่อนที่ของมอนสเตอร์ยังปกติ ถ้าปิงสูงอย่างเดียวโดยไม่มีจิตเตอร์และแพ็กเก็ตหาย ก็แค่เห็นตัวละครนั้นอยู่ในตำแหน่งที่ช้าไปเล็กน้อยอย่างลื่นไหล สิ่งที่ทำให้คนอื่นเห็นเป็นแลคคือจิตเตอร์และแพ็กเก็ตหายมากกว่าปิง
  • ถ้าเน็ตแย่เฉพาะบาง ISP หรือบางพื้นที่ ผู้ใช้กลุ่มนั้นจะเจออาการข้างต้นพร้อมกัน จากมุมของเซิร์ฟเวอร์ มีแค่อินพุตของคนกลุ่มนั้นที่มาถึงไม่สม่ำเสมอ ระบบตรวจการเคลื่อนที่หรือระบบตรวจจับโปรแกรมโกงจึงตัดสินผิด (false positive) กับคนกลุ่มนั้นมากเป็นพิเศษ
  • ถ้าช้าเฉพาะตอนใช้ตัวละครบางตัว ให้สงสัยข้อมูลของตัวละครนั้นมากกว่าเน็ต ตัวละครที่มีไอเทมหรือจดหมายสะสมหลายพันชิ้น ต้องอ่านและเขียนข้อมูลมากกว่าคนอื่นหลายเท่าทุกครั้งที่ล็อกอินและเซฟ ลองล็อกอินตัวละครเดียวกันจาก PC หรือเน็ตอื่นแล้วดูว่ายังช้าเหมือนเดิมไหม ก็แยกได้

แต่ก็มีโครงสร้างที่คนช้าคนเดียวทำให้ทุกคนช้าไปด้วย จุดร่วมคือ “มีคนรอคนนั้นอยู่”

  • โครงสร้างที่ทุกคนรอเทิร์นเดียวกัน: lockstep (RTS) และคอนเทนต์ co-op ที่เดินเกมตามเทิร์น ถ้าอินพุตของคนเดียวมาช้า ทุกคนจะค้าง ต่อให้แค่ช้าโดยไม่มีจิตเตอร์ อินพุตของทุกคนก็จะมีผลช้าไปเท่ากับปิงของคนที่ช้าที่สุด
  • โครงสร้างที่เซิร์ฟเวอร์ต้องรอเพราะส่งให้คนที่ช้า: การส่งแบบ blocking (การส่งที่หยุดรอจนกว่า send buffer จะมีที่ว่าง) และการประมวลผลแบบ synchronous ทุกคนที่เธรดเซิร์ฟเวอร์นั้นดูแลจะช้าลง โดยทั่วไปจึงแยกคิวส่งให้แต่ละคนและไม่รอ ในกรณีนี้แลคจะเกิดเฉพาะกับคนที่ช้า และถ้าคิวยาวเกินไป คนนั้นคนเดียวจะหลุด
  • โครงสร้างที่คนช้าเป็นศูนย์กลาง: P2P (ผู้เล่นเชื่อมต่อกันโดยตรงโดยไม่มีเซิร์ฟเวอร์) หรือ listen server (PC ของผู้เล่นทำหน้าที่เป็นเซิร์ฟเวอร์ไปด้วย) ที่ PC ของคนนั้นเป็นโฮสต์ (หัวห้อง) และอีเวนต์ที่เดินได้ด้วยสิทธิ์ของหัวปาร์ตี้เท่านั้น ถ้าเป็นเกมที่ให้ไคลเอนต์ของผู้เล่นที่อยู่ใกล้คำนวณการเคลื่อนที่ของมอนสเตอร์เพื่อลดภาระเซิร์ฟเวอร์ มอนสเตอร์ที่คนนั้นดูแลจะกระตุกบนจอของทุกคน
  • โครงสร้างที่ย้อนเวลาตัดสินผลตามคนที่ช้า: lag compensation คนที่ช้ายิงโดนอย่างยุติธรรม แต่ฝั่งที่โดนจะรู้สึกไม่ยุติธรรมว่า “หลบไปแล้วยังโดน” จึงต้องจำกัดช่วงที่ย้อนเวลาได้ ขีดจำกัดต่างกันไปตามเกม อยู่ราว 0.2 วินาทีถึง 1 วินาที (ค่าเริ่มต้นของ Source engine คือ 1 วินาที)

สิ่งที่เห็นต่างกันตามวิธีที่เซิร์ฟเวอร์ประมวลผลอินพุต

วิธีที่เซิร์ฟเวอร์ประมวลผลอินพุตสิ่งที่คนช้าเจอเองคนช้าในสายตาคนอื่นเกมของคนอื่นเอง
รวบประมวลผลทุกทิก
ทิกคงที่ ประมวลผลอินพุตที่ได้รับพร้อมกัน
ผลของสกิลช้าไปเท่ากับปิงบวกเวลารอทิก (อินพุตดีเลย์) ถ้าตรวจการเคลื่อนที่เข้มงวดจะโดนดีดกลับหยุดแวบแล้วเดินหลายก้าวรวดเดียว (กรอเร็ว/วาร์ป) ถ้าปิงสูงอย่างเดียวโดยไม่มีจิตเตอร์ยังดูลื่นไม่มีผล
ประมวลผลทันทีที่มาถึง
แบบ event-driven ได้รับแล้วใช้และส่งต่อทันที
อินพุตดีเลย์เท่ากับปิง เร็วขึ้นแค่ส่วนที่ไม่ต้องรอทิกการเคลื่อนที่เร็วบ้างช้าบ้าง (กรอเร็วแบบอ่อน ๆ) สกิลหลายอันที่มาพร้อมกันทำงานในพริบตาเดียวไม่มีผล
บัฟเฟอร์อินพุตรายผู้เล่น
เก็บแยกไว้ทีละคน ใช้ทิกละหนึ่งอัน
ยืนยันผลช้าไปเท่ากับความยาวบัฟเฟอร์ค่อนข้างลื่น ถ้าบัฟเฟอร์ว่างจะยืนนิ่งอยู่กับที่ชั่วครู่ไม่มีผล
ตัดสินด้วย lag compensation
ย้อนเวลาไปจังหวะที่ผู้โจมตีเห็น
เล็งแล้วโดนตามนั้น (ภายในขีดจำกัดการย้อนเวลา)หลบไปแล้วก็ยังโดนการโจมตีของคนนั้นโดนแบบไม่ยุติธรรม (ลามไปถึงคนอื่น)
lockstep/รอเทิร์นอินพุตดีเลย์ ถ้าอินพุตมาช้าจะค้างทุกคนค้างค้าง แค่ช้าก็เกิดอินพุตดีเลย์ (ลามไปถึงทุกคน)
ส่งแบบ blocking/ประมวลผลแบบ synchronous
เซิร์ฟเวอร์รอคนนั้น
ค้างแล้วกรอเร็วทุกคนที่เธรดเซิร์ฟเวอร์นั้นดูแลช้าลงสโลว์โมชั่น/ค้าง (ลามไปถึงคนที่เธรดนั้นดูแล)
คนช้าเป็นโฮสต์
P2P, listen server
ตัวเองปิง 0จอของทุกคนกระตุกทุกคนแลค
คนช้าเป็นผู้ควบคุมมอนสเตอร์
ให้ไคลเอนต์คำนวณการเคลื่อนที่ของมอนสเตอร์
มอนสเตอร์บนจอตัวเองปกติมอนสเตอร์ที่คนนั้นดูแลหยุดแวบแล้ววาร์ปทุกคนที่สู้กับมอนสเตอร์ตัวนั้น (ลามไปถึงคนอื่น)

ไคลเอนต์สองตัวบน PC เดียวกัน แต่มองไม่เห็น NPC อยู่ฝั่งเดียว

ถ้าคนคนเดียวเปิดไคลเอนต์สองตัวบน PC เดียวกัน แล้วมีฝั่งเดียวที่มองไม่เห็น NPC เน็ตแทบจะไม่ใช่สาเหตุ ไคลเอนต์ทั้งสองใช้เราเตอร์เดียวกันและเน็ตเดียวกัน ความต่างเกิดได้จากสามจุด

  1. เซิร์ฟเวอร์ไม่ได้ส่งให้ไคลเอนต์นั้น: แชนแนล อินสแตนซ์ หรือ phasing ของเควสต์ (ฟีเจอร์ที่แบ่ง NPC ที่มองเห็นตามความคืบหน้า) ต่างกัน, ลำดับการลงทะเบียนระยะมองเห็นพันกัน, ขีดจำกัดปริมาณการส่งต่อการเชื่อมต่อ, บั๊กเซสชันที่มอง PC เดียวกันหรือ IP เดียวกันเป็นคนเดียว, การจำกัดการเปิดหลายไคลเอนต์
  2. ส่งมาแล้วแต่ไคลเอนต์ทิ้ง: ทิ้งข้อความแจ้งการปรากฏตัวที่มาถึงระหว่างโหลด, ข้อมูลการปรากฏตัวที่ทะลักเข้ามาทันทีหลังเข้าพื้นที่หายไปเพราะ receive buffer ล้นหรือเพราะช่องทางแบบ unreliable (ช่องทางที่หายแล้วไม่ส่งซ้ำ), สแนปช็อตตั้งต้น (ข้อมูลเต็มที่เป็นจุดตั้งต้นเมื่อส่งเฉพาะส่วนที่เปลี่ยน) หาย, เข้าใจผิดว่า NPC ใหม่ที่ใช้ ID ซ้ำเป็น NPC ตัวเก่า, ไคลเอนต์อื่นแย่งรับไปเพราะพอร์ต UDP แบบตายตัวชนกัน, receive buffer ล้นเพราะเป็นหน้าต่างเบื้องหลังจึงประมวลผลไม่ทัน, การประมาณเวลาเซิร์ฟเวอร์คลาดเคลื่อนจนเลื่อนการแสดงผลออกไป
  3. ได้รับแล้วแต่วาดไม่ได้: ไคลเอนต์สองตัวเขียนไฟล์แคชเดียวกันพร้อมกันจนโหลดโมเดลล้มเหลว, หน่วยความจำกราฟิก (VRAM) ไม่พอ, ตัวเลือกการแสดงผลต่างกัน เช่น จำกัดจำนวนตัวละครที่แสดง, เวอร์ชันหรือข้อมูลไม่ตรงกัน

เบาะแสที่ชัดที่สุดมีสามข้อ คือ มีป้ายชื่อแต่ไม่มีโมเดลตัวละครหรือไม่ (เซิร์ฟเวอร์ส่งแล้ว แต่วาดไม่สำเร็จ), ออกนอกระยะมองเห็นแล้วกลับมาจะเห็นหรือไม่ (ข้อความแจ้งการปรากฏตัวหายไปหนึ่งอัน) และ ดึงหน้าต่างฝั่งที่มองไม่เห็นขึ้นมาด้านหน้าแล้วดีขึ้นหรือไม่ (การจำกัดการประมวลผลของหน้าต่างเบื้องหลัง) ในทางกลับกัน “ตัวผี” ที่มอนสเตอร์ตายไปแล้วแต่ยังยืนอยู่บนจอเราคนเดียว เกิดจากข้อความแจ้งการออกไปหายไป

ปัญหาที่เกิดกับบางคนเท่านั้น

คนเน็ตช้าขยับแบบกรอเร็วบนจอคนอื่น Laggy player seen by others (bursty inputs)

อินพุตของคนที่เน็ตไม่ดีจะไปถึงเซิร์ฟเวอร์แบบไม่สม่ำเสมอและมาเป็นกลุ่ม ถ้าเซิร์ฟเวอร์ใช้คำสั่งตามที่ได้รับในแต่ละทิก คนอื่นจะเห็นตัวละครนั้นหยุดแวบแล้วเดินหลายก้าวในทีเดียว

ทำไม: คำสั่งเคลื่อนที่ของคนที่ช้า บางทิกมาถึง 0 คำสั่ง บางทิกมา 2–3 คำสั่ง → ผลคือ: เซิร์ฟเวอร์ใช้คำสั่งทั้งหมดในทิกที่ได้รับ ตำแหน่งของตัวละครนั้นจึงเปลี่ยนเป็นขั้นบันได → บนหน้าจอ: บนจอคนอื่น ตัวละครนั้นตัวเดียวหยุดแวบแล้วขยับรวดเดียวแบบกรอเร็ว ที่เหลือปกติ

อาการ: กรอเร็ว, วาร์ป · ผู้รับผิดชอบหลัก พัฒนาเซิร์ฟเวอร์ (ทีมพัฒนาเกม) · ร่วมกับ ภายนอก (ภายนอก)

อาการกรอเร็วจากเซิร์ฟเวอร์ที่ประมวลผลทันทีที่ได้รับ Event-driven processing of bursty inputs

บนเซิร์ฟเวอร์ที่ประมวลผลและแจ้งผลทันทีที่แพ็กเก็ตมาถึง การกระทำของคนที่ช้าซึ่งมาถึงเป็นกลุ่มจะถูกรันทันทีต่อเนื่องกัน

ทำไม: คำขอสกิลและการเคลื่อนที่ของคนที่ช้ามาถึงเป็นกลุ่ม → ผลคือ: เซิร์ฟเวอร์รันตามลำดับทันทีที่ได้รับ และแจ้งทุกคนทันที → บนหน้าจอ: คนอื่นเห็นคนนั้นใช้สกิลหลายสกิลในพริบตาเดียว หรือขยับเหมือนกดกรอ

อาการ: กรอเร็ว · ผู้รับผิดชอบหลัก พัฒนาเซิร์ฟเวอร์ (ทีมพัฒนาเกม) · ร่วมกับ พัฒนาไคลเอนต์ (ทีมพัฒนาเกม)

ขนาดบัฟเฟอร์อินพุตของผู้เล่นแต่ละคน Per-player server input buffer (jitter buffer)

ถ้าเซิร์ฟเวอร์เก็บอินพุตของแต่ละคนไว้เล็กน้อยแล้วดึงมาใช้ทิกละหนึ่งอัน คนอื่นจะเห็นลื่นไหล แต่จังหวะที่การกระทำของเจ้าตัวถูกยืนยันบนเซิร์ฟเวอร์จะช้าลงตามไปด้วย

ทำไม: เซิร์ฟเวอร์เก็บอินพุตของคนที่ช้าไว้ในบัฟเฟอร์ แล้วใช้ทิกละหนึ่งอัน → ผลคือ: ถ้าบัฟเฟอร์เล็กจะว่างบ่อย ตัวละครนั้นยืนนิ่งอยู่กับที่หรือเซิร์ฟเวอร์เดาจากอินพุตล่าสุดแล้วขยับให้ ถ้าบัฟเฟอร์ใหญ่ อินพุตของเจ้าตัวจะถูกยืนยันช้า → บนหน้าจอ: ถ้าเล็ก คนอื่นเห็นหยุดแวบ ถ้าใหญ่ ผลสกิลของเจ้าตัวออกช้า (อินพุตดีเลย์)

อาการ: กระตุก, อินพุตดีเลย์ · ผู้รับผิดชอบหลัก พัฒนาเซิร์ฟเวอร์ (ทีมพัฒนาเกม) · ร่วมกับ พัฒนาไคลเอนต์ (ทีมพัฒนาเกม)

false positive ของการตรวจสอบที่กระจุกกับผู้ใช้บาง ISP Anti-cheat / movement validation false positives on bad ISPs

คนที่ใช้เน็ตที่จิตเตอร์สูง อินพุตจะไปถึงเป็นกลุ่ม จึงติดการตรวจความเร็วและคูลดาวน์ของเซิร์ฟเวอร์บ่อย

ทำไม: จิตเตอร์ของเน็ตจากบาง ISP หรือบางพื้นที่สูงขึ้นในช่วงหัวค่ำ → ผลคือ: เซิร์ฟเวอร์ตัดสินว่าอินพุตปกติที่มาถึงเป็นกลุ่มคือการเคลื่อนที่เร็วเกินหรือผิดกฎคูลดาวน์ → บนหน้าจอ: เฉพาะผู้ใช้ ISP นั้นที่โดนดีดกลับ สกิลถูกปฏิเสธ ถ้าหนักจะถูกเซิร์ฟเวอร์เตะออกจนหลุด

อาการ: ดีดกลับ, กดไม่ติด/โรลแบ็ค, หลุด · ผู้รับผิดชอบหลัก พัฒนาเซิร์ฟเวอร์ (ทีมพัฒนาเกม) · ร่วมกับ อินฟราเครือข่าย (ทีมอินฟรา)

เพื่อนในปาร์ตี้ที่ช้าหนึ่งคนกับกิมมิกบอส One laggy member in a synchronized mechanic

ในกิมมิกเรดที่ทุกคนต้องตอบสนองพร้อมกันในจังหวะที่กำหนด การตอบสนองช้าของคนที่ช้าเพียงคนเดียวจะทำให้ทั้งปาร์ตี้ล้มเหลว

ทำไม: กิมมิกร่วม เช่น “ทุกคนกระจายพร้อมกัน” หรือ “ให้คนหนึ่งกดปุ่ม” → ผลคือ: คนที่ช้าเห็นสัญญาณเตือนช้า และอินพุตก็ไปถึงช้า → บนหน้าจอ: ตายยกปาร์ตี้เพราะคนคนเดียว เพื่อนในปาร์ตี้คนอื่นรู้สึกว่า “เพราะคนที่แลค”

อาการ: กดไม่ติด/โรลแบ็ค, อินพุตดีเลย์ · ผู้รับผิดชอบหลัก พัฒนาเซิร์ฟเวอร์ (ทีมพัฒนาเกม) · ร่วมกับ พัฒนาไคลเอนต์ (ทีมพัฒนาเกม)

สิทธิ์ควบคุมมอนสเตอร์อยู่ที่ไคลเอนต์ที่ช้า Monster movement delegated to a player client

บางเกมให้ไคลเอนต์ของผู้เล่นคนหนึ่งที่อยู่ใกล้คำนวณการเคลื่อนที่ของมอนสเตอร์เพื่อลดโหลดเซิร์ฟเวอร์ ถ้าเน็ตของคนนั้นไม่ดี มอนสเตอร์ตัวนั้นจะขยับแปลก ๆ บนจอของทุกคน

ทำไม: เซิร์ฟเวอร์ให้ไคลเอนต์ของผู้เล่นที่อยู่ใกล้ที่สุด (หรือมาถึงก่อน) คำนวณการเคลื่อนที่ของมอนสเตอร์ → ผลคือ: รายงานผลของคนที่รับหน้าที่ไปถึงเซิร์ฟเวอร์ช้าหรือมาเป็นกลุ่ม → บนหน้าจอ: เฉพาะมอนสเตอร์ตัวนั้นที่หยุดแวบแล้ววาร์ปบนจอของทุกคนรอบ ๆ ส่วนบนจอของคนที่รับหน้าที่เองปกติ

อาการ: วาร์ป, กระตุก, กรอเร็ว · ผู้รับผิดชอบหลัก พัฒนาเซิร์ฟเวอร์ (ทีมพัฒนาเกม)

ข้อมูลของตัวละครบางตัวใหญ่เกินไป One character with oversized data (inventory, mail, buffs)

ตัวละครที่มีไอเทมหรือจดหมายสะสมหลายพันชิ้น หรือมีรายชื่อเพื่อน รายชื่อบล็อก และบัฟมากผิดปกติ ต้องโหลดตอนเชื่อมต่อ บันทึก และแจ้งคนรอบข้างมากกว่าคนอื่นหลายเท่า จึงช้าเฉพาะตัวละครนั้นโดยไม่เกี่ยวกับเน็ต

ทำไม: ตัวละครที่เล่นมานานหรือของรางวัลอีเวนต์สะสมในกระเป๋าและกล่องจดหมายหลายพันชิ้น → ผลคือ: ทุกครั้งที่เชื่อมต่อ ย้ายแมพ หรือบันทึก ต้องอ่านเขียน DB มากตามไปด้วย และข้อมูลอุปกรณ์สวมใส่และบัฟที่ต้องส่งให้คนรอบข้างก็ใหญ่ → บนหน้าจอ: เฉพาะตัวละครนั้นที่โหลดเข้าโซนนาน และหยุดแวบตอนเปิดกระเป๋าหรือกล่องจดหมาย ถ้าเซิร์ฟเวอร์รอการบันทึกบนเธรดเกม คนรอบข้างก็ค้างไปครู่หนึ่งด้วย

อาการ: เข้าเกมไม่ได้/โหลดไม่จบ, อินพุตดีเลย์, ค้าง · ผู้รับผิดชอบหลัก พัฒนาเซิร์ฟเวอร์ (ทีมพัฒนาเกม) · ร่วมกับ อินฟรา DB (ทีมอินฟรา)

แชนแนล/อินสแตนซ์/phasing ต่างกัน Different channel / instance / phase

ถ้าตัวละครสองตัวอยู่คนละแชนแนลหรือคนละอินสแตนซ์ หรืออยู่คนละ “phase” ที่เห็น NPC ต่างกันตามความคืบหน้าของเควสต์ จะเห็นโลกคนละแบบ

ทำไม: ตัวละครตัวที่สองถูกจัดไปแชนแนลอื่น หรืออยู่ขั้นเควสต์ต่างกัน → ผลคือ: เซิร์ฟเวอร์ไม่ส่ง NPC นั้นให้ตัวละครนั้น (ปกติ) → บนหน้าจอ: NPC หายไปฝั่งเดียว ดูเหมือนบั๊กแต่เป็นไปตามการออกแบบ

อาการ: มองไม่เห็น/ตัวผี · ผู้รับผิดชอบหลัก พัฒนาเซิร์ฟเวอร์ (ทีมพัฒนาเกม) · ร่วมกับ พัฒนาไคลเอนต์ (ทีมพัฒนาเกม)

ทิ้งข้อความแจ้งการปรากฏตัวที่มาถึงระหว่างโหลด Spawn messages dropped before the client is ready

ทันทีที่เข้าโซน เซิร์ฟเวอร์ส่งข้อความแจ้งการปรากฏตัวของ NPC รอบตัวมา แต่ไคลเอนต์ยังโหลดแมพอยู่จึงทิ้งข้อความนั้น

ทำไม: เซิร์ฟเวอร์ส่งข้อความแจ้งการปรากฏตัวของเอนทิตีรอบตัวทันทีหลังประมวลผลการเข้าโซน → ผลคือ: ไคลเอนต์กำลังโหลดและยังไม่มี message handler จึงทิ้งข้อความ → บนหน้าจอ: เซิร์ฟเวอร์ถือว่าส่งไปแล้วจึงไม่ส่งซ้ำ มองไม่เห็น NPC จนกว่าจะออกจากระยะมองเห็นแล้วกลับมา

อาการ: มองไม่เห็น/ตัวผี · ผู้รับผิดชอบหลัก พัฒนาไคลเอนต์ (ทีมพัฒนาเกม) · ร่วมกับ พัฒนาเซิร์ฟเวอร์ (ทีมพัฒนาเกม)

ลำดับการลงทะเบียนระยะมองเห็นพันกัน Interest-management race on enter/leave

ถ้าจังหวะที่ตัวละครถูกลงทะเบียนในตาราง (grid) ระยะมองเห็นตรงกับจังหวะที่ NPC ย้ายช่องตาราง ข้อความแจ้งการปรากฏตัวของ NPC นั้นอาจตกหล่น

ทำไม: การประมวลผลการเข้าโซน, การย้ายแชนแนล หรือการเทเลพอร์ต เกิดในจังหวะเดียวกับการเคลื่อนที่ของ NPC → ผลคือ: NPC นั้นตกไปจากการคำนวณ “เอนทิตีที่เพิ่งมองเห็น” → บนหน้าจอ: มองไม่เห็นแค่ NPC บางตัว หรือ NPC ที่ออกไปแล้วยังยืนอยู่

อาการ: มองไม่เห็น/ตัวผี · ผู้รับผิดชอบหลัก พัฒนาเซิร์ฟเวอร์ (ทีมพัฒนาเกม)

สแนปช็อตตั้งต้น (baseline) หาย Lost baseline for delta compression

ในแบบที่เซิร์ฟเวอร์ส่ง “เฉพาะส่วนที่ต่างจากครั้งก่อน” ถ้าข้อมูลเต็ม (baseline) ที่ส่งครั้งแรกหายไป ก็จะใช้ส่วนเปลี่ยนแปลงที่ตามมาไม่ได้

ทำไม: แพ็กเก็ตข้อมูลเต็ม (baseline) ของเอนทิตีหาย หรือถูกทิ้งก่อนประมวลผล → ผลคือ: ไคลเอนต์ไม่มีเป้าหมายให้ใช้ส่วนเปลี่ยนแปลงที่ตามมา จึงไม่สนใจ → บนหน้าจอ: มองไม่เห็นเอนทิตีนั้น หรือผ่านไปนานแล้วจู่ ๆ ก็โผล่ขึ้นมา

อาการ: มองไม่เห็น/ตัวผี, วาร์ป · ผู้รับผิดชอบหลัก พัฒนาเซิร์ฟเวอร์ (ทีมพัฒนาเกม) · ร่วมกับ พัฒนาไคลเอนต์ (ทีมพัฒนาเกม)

ข้อความแจ้งการออกไปหาย (ตัวผี) Missed despawn (ghost entity)

ในทางกลับกัน ถ้าพลาดข้อความแจ้งว่า “หายไปแล้ว” NPC หรือผู้เล่นที่ตายหรือออกไปแล้วจะยังอยู่บนจอเราคนเดียว

ทำไม: ข้อความแจ้งการตาย, การออกไป หรือการออกจากระยะมองเห็นหาย หรือลำดับสลับกัน → ผลคือ: ไคลเอนต์ถือว่าเอนทิตีนั้นยังอยู่ → บนหน้าจอ: มอนสเตอร์ที่ตีแล้วไม่มีปฏิกิริยา, ผู้เล่นที่ออกไปแล้วยังยืนอยู่

อาการ: มองไม่เห็น/ตัวผี · ผู้รับผิดชอบหลัก พัฒนาเซิร์ฟเวอร์ (ทีมพัฒนาเกม) · ร่วมกับ พัฒนาไคลเอนต์ (ทีมพัฒนาเกม)

ข้อมูลการปรากฏตัวที่ทะลักมาหลังเข้าโซนหาย Initial spawn burst lost (unreliable channel, receive buffer, fragmentation)

วินาทีที่เข้าโซน เซิร์ฟเวอร์จะส่งข้อมูลการปรากฏตัวของเอนทิตีรอบตัวหลายสิบถึงหลายร้อยตัวมาพร้อมกัน ถ้าส่งผ่านช่องทางแบบ unreliable หรือ receive buffer ล้นในช่วงที่ยังโหลดอยู่จนอ่านซ็อกเก็ตไม่ได้ บางส่วนจะหายไปและไม่ถูกส่งมาอีก

ทำไม: ทันทีหลังเข้าโซน ข้อมูลการปรากฏตัวมาถึงพร้อมกันในช่วงสั้น ๆ → ผลคือ: ไคลเอนต์ที่กำลังโหลดอ่านซ็อกเก็ตช้าจน receive buffer ของ OS ล้น หรือแพ็กเก็ต UDP ขนาดใหญ่ถูก fragment และหาย fragment เดียวก็หายทั้งแพ็กเก็ต ถ้าเป็นช่องทางแบบ unreliable ก็ไม่ส่งซ้ำให้ด้วย → บนหน้าจอ: NPC หายไปบางตัวเฉพาะในไคลเอนต์ฝั่งที่โหลดช้า ถ้าออกจากระยะมองเห็นแล้วกลับมาจะเห็น

อาการ: มองไม่เห็น/ตัวผี · ผู้รับผิดชอบหลัก พัฒนาเซิร์ฟเวอร์ (ทีมพัฒนาเกม) · ร่วมกับ พัฒนาไคลเอนต์ (ทีมพัฒนาเกม)

สับสนจากการนำ entity ID กลับมาใช้ซ้ำ Entity ID reused without a generation counter

ถ้าเซิร์ฟเวอร์ใช้ entity ID เดิมซ้ำเมื่อ NPC ที่ตายเกิดใหม่ ไคลเอนต์ที่พลาดข้อความแจ้งการออกไปในช่วงนั้นจะเข้าใจผิดว่า NPC ตัวใหม่คือตัวเก่า

ทำไม: NPC ตายแล้วเกิดใหม่ด้วย entity ID เดิม → ผลคือ: ไคลเอนต์ที่พลาดข้อความแจ้งการออกไปถือว่าเป็น “เอนทิตีที่รู้จักอยู่แล้ว” จึงไม่สนใจข้อความแจ้งการปรากฏตัว หรือคงสถานะตายไว้เหมือนเดิม → บนหน้าจอ: NPC หายไปหรือเห็นนอนตายอยู่บนจอฝั่งเดียว บางครั้งเห็นเป็นหน้าตาของ NPC ตัวอื่น

อาการ: มองไม่เห็น/ตัวผี · ผู้รับผิดชอบหลัก พัฒนาเซิร์ฟเวอร์ (ทีมพัฒนาเกม) · ร่วมกับ พัฒนาไคลเอนต์ (ทีมพัฒนาเกม)

พอร์ต UDP แบบตายตัวชนกัน Two clients bound to the same local UDP port

ถ้าไคลเอนต์ถูกออกแบบให้ใช้พอร์ตภายในเครื่องที่กำหนดตายตัว ไคลเอนต์ตัวที่สองบน PC เดียวกันจะใช้พอร์ตไม่ได้ หรือต้องแบ่งรับแพ็กเก็ตกับตัวแรก

ทำไม: สองไคลเอนต์พยายามเปิดพอร์ต UDP ภายในเครื่องพอร์ตเดียวกัน (ฝืนใช้ร่วมกันด้วยออปชัน reuse) → ผลคือ: OS ส่งแพ็กเก็ตขาเข้าให้ซ็อกเก็ตฝั่งเดียว หรือไม่รับประกันว่าฝั่งไหนจะได้รับ เราเตอร์และเซิร์ฟเวอร์ก็มองสองไคลเอนต์เป็นที่อยู่เดียวกัน → บนหน้าจอ: ฝั่งหนึ่งไม่ได้รับแพ็กเก็ตของโลกในเกม จึงมองไม่เห็น NPC และผู้เล่นคนอื่น หรือหลุด

อาการ: มองไม่เห็น/ตัวผี, หลุด, เข้าเกมไม่ได้/โหลดไม่จบ · ผู้รับผิดชอบหลัก พัฒนาไคลเอนต์ (ทีมพัฒนาเกม) · ร่วมกับ พัฒนาเซิร์ฟเวอร์ (ทีมพัฒนาเกม)

บั๊กแยกเซสชันด้วย IP/ID เครื่อง Session keyed by IP or machine ID

ถ้าเซิร์ฟเวอร์หรือเซิร์ฟเวอร์ตัวกลางแยกการเชื่อมต่อด้วย IP หรือ ID เครื่อง สองไคลเอนต์ใน PC เดียวกัน (public IP เดียวกัน) จะถูกมองเป็นคนเดียว

ทำไม: สร้างตารางเซสชันโดยใช้ IP หรือ IP+ID เครื่องเป็นคีย์ → ผลคือ: ข้อมูลของไคลเอนต์ตัวที่สองเขียนทับหรือปนกับเซสชันแรก → บนหน้าจอ: ฝั่งหนึ่งมองไม่เห็น NPC อีกฝั่งหลุดหรือได้รับข้อมูลของคนอื่น

อาการ: มองไม่เห็น/ตัวผี, หลุด · ผู้รับผิดชอบหลัก พัฒนาเซิร์ฟเวอร์ (ทีมพัฒนาเกม) · ร่วมกับ พัฒนาไคลเอนต์ (ทีมพัฒนาเกม)

การจำกัดการเปิดหลายไคลเอนต์ Multi-client restriction policy

ถ้าโมดูลความปลอดภัยหรือนโยบายของเซิร์ฟเวอร์จำกัดการเปิดหลายไคลเอนต์ใน PC เดียว ไคลเอนต์ตัวที่สองจะเปิดหรือเชื่อมต่อไม่ได้ หรือตัวที่เปิดก่อนจะหลุด บางเกมบล็อกแค่บางฟีเจอร์ของไคลเอนต์ที่เปิดเพิ่ม

ทำไม: โมดูลความปลอดภัยตรวจพบการเปิดซ้ำ หรือเซิร์ฟเวอร์จำกัดการเชื่อมต่อเพิ่มจากเครื่องเดียวกัน → ผลคือ: ปฏิเสธการเปิดหรือการเชื่อมต่อครั้งที่สอง หรือตัดฝั่งหนึ่งออก บางกรณีบล็อกเฉพาะบางฟีเจอร์ของไคลเอนต์ที่เปิดเพิ่ม → บนหน้าจอ: เข้าเกมไม่ได้หรือฝั่งหนึ่งหลุด ในเกมที่บล็อกแค่ฟีเจอร์ ฝั่งหนึ่งจะมองไม่เห็น NPC หรือร้านค้า

อาการ: เข้าเกมไม่ได้/โหลดไม่จบ, มองไม่เห็น/ตัวผี, หลุด · ผู้รับผิดชอบหลัก พัฒนาไคลเอนต์ (ทีมพัฒนาเกม) · ร่วมกับ พัฒนาเซิร์ฟเวอร์ (ทีมพัฒนาเกม)

จำกัดการประมวลผลของหน้าต่างเบื้องหลัง Background window throttling

สำหรับไคลเอนต์ที่เป็นหน้าต่างเบื้องหลัง ตัวเกม เอนจิน และ OS จะลดเฟรมและการประมวลผลลง แพ็กเก็ตที่ได้รับจึงประมวลผลไม่ทันจนกองรอหรือล้น

ทำไม: การจำกัดเฟรมของหน้าต่างเบื้องหลังจากออปชันเกมหรือไดรเวอร์การ์ดจอ (เช่น ไดรเวอร์ NVIDIA ตั้งได้ตั้งแต่ 20–200 เฟรมต่อวินาที), โหมดประหยัดพลังงาน, การตั้งค่าหยุดทำงานเบื้องหลังของเอนจิน OS เองก็ให้ CPU/GPU กับหน้าต่างที่อยู่ด้านหน้า (foreground) ก่อน → ผลคือ: จำนวนแพ็กเก็ตที่ประมวลผลต่อเฟรมลดลงจนคิวสะสม และถ้า receive buffer ล้นจะถูกทิ้ง → บนหน้าจอ: เมื่อดึงหน้าต่างขึ้นมาด้านหน้า ทุกอย่างโผล่มารวดเดียว หรือ NPC บางตัวไม่โผล่เลย

อาการ: มองไม่เห็น/ตัวผี, กรอเร็ว, หลุด · ผู้รับผิดชอบหลัก พัฒนาไคลเอนต์ (ทีมพัฒนาเกม) · ร่วมกับ ภายนอก (ภายนอก)

การเข้าถึงไฟล์แคช/แอสเซ็ตพร้อมกันจนชนกัน Shared cache / asset file lock conflicts

ถ้าสองไคลเอนต์เขียนลงโฟลเดอร์แคชเดียวกันพร้อมกันหรือล็อกไฟล์ไว้ ฝั่งหนึ่งจะโหลดโมเดลหรือเท็กซ์เจอร์ของ NPC ไม่ได้

ทำไม: สองไคลเอนต์เขียนไฟล์แคชหรือไฟล์แพตช์ในโฟลเดอร์ติดตั้งเดียวกันพร้อมกัน → ผลคือ: ล็อกไฟล์ไม่สำเร็จ หรืออ่านไฟล์ที่เขียนไม่เสร็จ จึงโหลดล้มเหลว → บนหน้าจอ: NPC ที่มีป้ายชื่อแต่ไม่มีโมเดลตัวละคร หรือโปร่งใส

อาการ: มองไม่เห็น/ตัวผี · ผู้รับผิดชอบหลัก พัฒนาไคลเอนต์ (ทีมพัฒนาเกม)

หน่วยความจำ/VRAM ไม่พอจนสตรีมล้มเหลว Memory / VRAM exhaustion

ถ้าสองไคลเอนต์แบ่งหน่วยความจำกราฟิกกันใช้ จะไม่มีที่ให้โหลดโมเดลหรือเท็กซ์เจอร์ที่ต้องใช้ใหม่ บางส่วนจึงไม่ถูกวาด

ทำไม: สองไคลเอนต์แบ่ง VRAM และ RAM กันใช้ บางครั้ง OS ลดโควตาหน่วยความจำกราฟิกของหน้าต่างเบื้องหลังก่อน → ผลคือ: เอนจินโหลดโมเดลหรือเท็กซ์เจอร์ใหม่ไม่ได้ หรือเอาออกแล้วโหลดใหม่ซ้ำไปมา → บนหน้าจอ: NPC โผล่ช้า ภาพเบลอ หรือมองไม่เห็น, กระตุก

อาการ: มองไม่เห็น/ตัวผี, กระตุก · ผู้รับผิดชอบหลัก พัฒนาไคลเอนต์ (ทีมพัฒนาเกม) · ร่วมกับ ภายนอก (ภายนอก)

ตัวเลือกการแสดงผลต่างกัน Different display settings

ถ้าตัวเลือกอย่างการจำกัดจำนวนตัวละครที่แสดง, การซ่อนป้ายชื่อหรือโมเดล NPC และโหมดสเปกต่ำ ตั้งไว้ต่างกันในสองไคลเอนต์ สิ่งที่มองเห็นก็จะต่างกัน

ทำไม: ไคลเอนต์ฝั่งเดียวที่ตั้ง “จำกัดจำนวนตัวละครรอบตัวที่แสดง” หรือโหมดสเปกต่ำ → ผลคือ: ไม่วาด NPC ที่อยู่ไกลหรือมีลำดับความสำคัญต่ำ (ปกติ) → บนหน้าจอ: NPC หายไปฝั่งเดียว

อาการ: มองไม่เห็น/ตัวผี · ผู้รับผิดชอบหลัก พัฒนาไคลเอนต์ (ทีมพัฒนาเกม)

เวอร์ชันไคลเอนต์/ข้อมูลไม่ตรงกัน Client version / data table mismatch

ถ้าไคลเอนต์ตัวที่สองเป็นตัวติดตั้งอื่นหรือแพตช์ยังไม่ครบ จะไม่รู้จัก NPC ID ใหม่ที่เซิร์ฟเวอร์ส่งมา และข้ามไปเงียบ ๆ

ทำไม: ตัวติดตั้งในโฟลเดอร์อื่น หรือไคลเอนต์ที่เปิดระหว่างลงแพตช์ → ผลคือ: ถ้าได้รับ NPC ID หรือ model ID ที่ไม่รู้จัก ก็ข้ามไป → บนหน้าจอ: เฉพาะ NPC ที่เพิ่มเข้ามาใหม่ที่มองไม่เห็นในฝั่งเดียว

อาการ: มองไม่เห็น/ตัวผี · ผู้รับผิดชอบหลัก พัฒนาไคลเอนต์ (ทีมพัฒนาเกม) · ร่วมกับ พัฒนาเซิร์ฟเวอร์ (ทีมพัฒนาเกม)

งบการส่ง/ลำดับความสำคัญต่อการเชื่อมต่อ Per-connection bandwidth budget and priority

ถ้าเซิร์ฟเวอร์จำกัดปริมาณที่ส่งต่อการเชื่อมต่อและส่งสิ่งที่อยู่ใกล้ก่อน ฝั่งที่ถูกตั้งเพดานไว้ต่ำจะได้รับ NPC ที่อยู่ไกลช้าหรือไม่ได้รับเลย

ทำไม: ในที่ที่คนเยอะ เซิร์ฟเวอร์ส่งตามลำดับความสำคัญภายในเพดานปริมาณการส่งของแต่ละการเชื่อมต่อ → ผลคือ: การเชื่อมต่อที่ค่าประเมินแบนด์วิดท์ต่ำ (เช่น ฝั่งที่เป็นหน้าต่างเบื้องหลังจึงส่งสัญญาณยืนยันการรับช้า) จะเลื่อนเอนทิตีลำดับท้าย ๆ ออกไปเรื่อย ๆ → บนหน้าจอ: NPC ที่อยู่ไกลโผล่ช้าหรือมองไม่เห็นในฝั่งเดียว

อาการ: มองไม่เห็น/ตัวผี, อินพุตดีเลย์ · ผู้รับผิดชอบหลัก พัฒนาเซิร์ฟเวอร์ (ทีมพัฒนาเกม) · ร่วมกับ พัฒนาไคลเอนต์ (ทีมพัฒนาเกม)

เอนทิตีถูกพักไว้เพราะประมาณเวลาคลาดเคลื่อน Clock estimate error holds or discards entities

ถ้าเวลาเซิร์ฟเวอร์ที่ไคลเอนต์ประมาณไว้ผิด ข้อมูลเอนทิตีที่เพิ่งมาถึงจะถูกพักไว้เพราะ “ยังเป็นอนาคต” หรือถูกทิ้งเพราะ “เก่าเกินไป”

ทำไม: การประมาณเวลาเซิร์ฟเวอร์ของไคลเอนต์ฝั่งหนึ่งคลาดไปมาก (วัดระหว่างโหลด, เพิ่งตื่นจากโหมดประหยัดพลังงาน) → ผลคือ: เวลาอ้างอิงของ interpolation ไม่ตรงกับเวลาของข้อมูลเอนทิตี → บนหน้าจอ: เอนทิตีโผล่ช้าหรือดูหยุดนิ่งอยู่กับที่

อาการ: มองไม่เห็น/ตัวผี, กระตุก · ผู้รับผิดชอบหลัก พัฒนาไคลเอนต์ (ทีมพัฒนาเกม)

06สาเหตุที่พบบ่อย

การส่งซ้ำของ TCP: สาเหตุที่เกิดขึ้นและเหตุที่ความหน่วงเพิ่มขึ้น

เมื่อการส่งซ้ำของ TCP (retransmission) ในเมตริกของเซิร์ฟเวอร์เพิ่มขึ้น การแจ้งแลคก็มักเพิ่มตามไปด้วย การส่งซ้ำเป็นสัญญาณว่า “แพ็กเก็ตหายไป” หรือ “ตัดสินผิดว่าหายไป” สาเหตุอยู่ได้ทุกจุดตลอดเส้นทาง ตั้งแต่ Wi-Fi ไปจนถึงการ์ดเครือข่ายของเซิร์ฟเวอร์ และในการเชื่อมต่อที่ส่งแพ็กเก็ตเล็ก ๆ เป็นระยะห่าง ๆ อย่างเกม แพ็กเก็ตหายแค่ตัวเดียวก็ลามเป็นอาการค้างหลายร้อย ms บทนี้สรุปต้นเหตุของการส่งซ้ำ วิธีหาสาเหตุ และแนวทางแก้

เซิร์ฟเวอร์ส่งเกมได้รับ123×หาย456124563รอจนกว่าจะส่งซ้ำ (ขึ้นกับวิธีกู้คืน)3ไม่มีอะไรมาถึงเกมเลย: ค้าง3/4/5/6 มาพร้อมกัน: กรอเร็ว
กรณีที่แพ็กเก็ตหมายเลข 3 หายไปหนึ่งตัว ในการเชื่อมต่อที่ส่งทุก 50 ms TCP ส่งต่อข้อมูลตามลำดับเท่านั้น แม้หมายเลข 4, 5, 6 จะมาถึงแล้ว ก็จะยังไม่ส่งต่อให้เกมจนกว่าจะได้รับหมายเลข 3 อีกครั้ง แพ็กเก็ตหายตัวเดียวจึงกลายเป็นอาการค้างแล้วตามด้วยกรอเร็ว จังหวะที่ส่งซ้ำต่างกันไปตามวิธีกู้คืน ตั้งแต่ราวหนึ่งรอบไปกลับ จนถึง “เวลาไปกลับ + อย่างน้อย 200 ms” (ตัวจับเวลาส่งซ้ำ) (ดู “ประเภทของการส่งซ้ำ” ด้านล่าง)

สี่เหตุผลที่การส่งซ้ำทำให้เกมช้า

  1. การรอตามลำดับ (HOL blocking): TCP จะไม่ส่งแพ็กเก็ตที่มาถึงทีหลังให้เกม จนกว่าจะได้รับตัวที่หายไปอีกครั้ง ถ้าหายหนึ่งตัว ทุกตัวที่ตามมาจะหยุดรอไปด้วยแล้วถูกปล่อยออกมาพร้อมกัน (ค้างแล้วตามด้วยกรอเร็ว)
  2. เวลารอส่งซ้ำ: ฝั่งผู้ส่งจะส่งซ้ำได้ก็ต่อเมื่อตัวจับเวลาส่งซ้ำ (RTO) หมดเวลา บน Linux คือ “เวลาไปกลับ + อย่างน้อย 200 ms” ถ้าตัวที่ส่งซ้ำหายอีก เวลารอจะเพิ่มเป็นสองเท่าทุกครั้ง (0.3 วินาที → 0.6 วินาที → 1.2 วินาที …)
  3. thin stream (การเชื่อมต่อที่ส่งแพ็กเก็ตเล็ก ๆ เป็นระยะห่าง ๆ): fast retransmit ทำงานจากสัญญาณที่ฝั่งผู้รับแจ้งว่า “แพ็กเก็ตที่ตามมาถึงแล้ว 3 ตัว” (duplicate ACK 3 ตัว ส่วน ACK คือสัญญาณยืนยันว่า “ได้รับแล้ว”) แพ็กเก็ตของเกมมาทีละตัวทุก 50–200 ms จึงมักถึงเวลา RTO ก่อนที่สัญญาณจะครบ นี่คือเหตุผลที่การดาวน์โหลดไฟล์ใหญ่ยังไปได้ดี แต่เกมกลับค้างอยู่อย่างเดียว RACK ใน Linux รุ่นใหม่ตัดสินได้แม้มีแพ็กเก็ตตามมาเพียงตัวเดียว จึงลดความต่างนี้ได้มาก แต่ถ้าแพ็กเก็ตห่างกันนานราว 200 ms RACK ก็ไม่ได้เร็วกว่า RTO
  4. การลดอัตราการส่ง: TCP มองว่าแพ็กเก็ตหายคือสัญญาณของความแออัด จึงลดปริมาณที่ส่งได้ในครั้งเดียว (congestion window) ถ้าไปถึง RTO ต้องเริ่มเพิ่มใหม่จากสถานะที่ส่งได้ทีละหนึ่งแพ็กเก็ต ระหว่างนั้นแพ็กเก็ตที่เกิดขึ้นใหม่จะกองรออยู่ที่เซิร์ฟเวอร์ และอัปเดตขนาดใหญ่ในจุดที่คนเยอะก็ล่าช้าต่อกันเป็นทอด ๆ

ประเภทของการส่งซ้ำ

ประเภทเกิดเมื่อไรเวลาที่ใช้กู้คืนสิ่งที่เห็นในเกม
ส่งซ้ำแบบเร็ว
Fast retransmit
เมื่อแพ็กเก็ตที่ตามมามาถึงก่อน แล้วฝั่งผู้รับแจ้งว่า “ตรงกลางขาดหาย” (duplicate ACK/SACK)เวลาไปกลับ + เวลาที่แพ็กเก็ตที่ตามมาถึงครบ 3 ตัวหยุดแวบสั้น ๆ ยิ่งแพ็กเก็ตถี่ยิ่งเร็ว
RACK/TLP
ตัดสินการหายตามเวลา, ส่งแพ็กเก็ตท้ายซ้ำ
เมื่อแพ็กเก็ตที่ส่งทีหลังมาถึงแล้ว แต่ตัวก่อนหน้ายังไม่มาภายในเวลาที่กำหนด หรือเมื่อไม่มี ACK มาสักพักก็จะส่งแพ็กเก็ตท้ายซ้ำอีกครั้งเมื่อได้รับการยืนยันของแพ็กเก็ตที่ตามมาก็ส่งซ้ำได้แทบทันที (RACK ซึ่งรอเพิ่มอีกราว 1/4 ของเวลาไปกลับ เพราะอาจแค่สลับลำดับ) ถ้าไม่มีแพ็กเก็ตตามมา ใช้ราว 2 เท่าของเวลาไปกลับ (TLP) และถ้ามีแพ็กเก็ตที่ยังไม่ได้รับ ACK อยู่เพียงตัวเดียว จะรอเพิ่มอีก 200 ms เผื่อ delayed ACKแม้เป็น thin stream ก็หยุดแวบค่อนข้างสั้น เป็นค่าเริ่มต้นใน Linux รุ่นใหม่
ส่งซ้ำเมื่อ RTO หมดเวลา
Retransmission timeout
เมื่อรอจนหมดเวลาโดยไม่มีสัญญาณใดเลยเวลาไปกลับ + อย่างน้อย 200 ms และเพิ่มเป็นสองเท่าทุกครั้งที่ล้มเหลวค้างหลายร้อย ms ถึงหลายวินาทีแล้วตามด้วยกรอเร็ว ถ้านานขึ้นจะหลุด
ส่ง SYN ซ้ำเมื่อคำขอเชื่อมต่อหายไปตั้งแต่ต้น (คิวรอเชื่อมต่อ (backlog) ล้น, ไฟร์วอลล์บล็อก)1 วินาที, 2 วินาที, 4 วินาที, 8 วินาที … (Linux 6.5 ขึ้นไปส่งซ้ำห่างกัน 1 วินาทีได้ถึงห้าครั้งแล้วจึงเพิ่มเป็นสองเท่า ส่วน Windows รุ่นเก่าเริ่มที่ 3 วินาที)หลังกดปุ่มเชื่อมต่อจะช้าเป็นจังหวะลงตัวหลักวินาที เช่น 1 วินาที หรือ 3 วินาที ถ้าล้มเหลวต่อเนื่องจะเข้าเกมไม่ได้/โหลดไม่จบ
การส่งซ้ำโดยไม่จำเป็น
Spurious retransmission
แพ็กเก็ตยังอยู่ แค่มาช้าหรือสลับลำดับ จึงถูกเข้าใจว่าหายแล้วส่งซ้ำไม่มีอะไรต้องกู้คืน มีแค่อัตราการส่งที่ลดลง (Linux อาจย้อนการลดนั้นกลับ ถ้าตรวจพบได้จาก DSACK (การแจ้งของผู้รับว่า “ได้รับแล้ว”) หรือ timestamp)เปลืองแบนด์วิดท์ ความเร็วในการส่งข้อมูลขนาดใหญ่ลดลง ในเมตริกเห็นแค่อัตราการส่งซ้ำสูง
zero window probe
สิ่งที่มักสับสนกับการส่งซ้ำ
แพ็กเก็ตที่ส่งไปตรวจสอบ เมื่อบัฟเฟอร์ฝั่งผู้รับเต็มจนอยู่ในสถานะ “หยุดส่งก่อน”จนกว่าฝั่งผู้รับจะเริ่มอ่านค้าง เน็ตปกติดี แต่โปรแกรมฝั่งผู้รับอ่านข้อมูลไม่ทัน

วิธีหาว่าหายที่ไหน

อัตราการส่งซ้ำคือ “สัดส่วนแพ็กเก็ตที่ต้องส่งซ้ำจากแพ็กเก็ตที่ส่งทั้งหมด” ค่าสะสมตั้งแต่บูตเครื่องจะกลบค่าปกติ จึงต้องคำนวณจากปริมาณที่เพิ่มขึ้นในช่วงเวลาคงที่ เช่น 1 นาที ไม่มีเกณฑ์อย่างเป็นทางการ แต่ความรู้สึกคร่าว ๆ สำหรับค่าเฉลี่ยทั้งเซิร์ฟเวอร์คือ ต่ำกว่า 0.1% ถือว่าปกติดี, 0.1–1% ผู้เล่นบางคนหยุดแวบเป็นบางครั้ง, เกิน 1% ผู้เล่นจำนวนมากรู้สึกได้ และ เกิน 3% ถือว่าร้ายแรง เกมที่มีผู้เล่นมือถือหรือผู้เล่นต่างประเทศมาก ค่าปกติจะสูงกว่านี้ การตัดสินจากตัวเลขตัวเดียวจึงไม่พอ ต้องดูด้วยว่าเพิ่มขึ้นกี่เท่าจากค่าปกติ ค่าเฉลี่ยถูกดึงโดยเน็ตแย่ ๆ เพียงไม่กี่สาย การแยกดูตามพื้นที่, ISP, เซิร์ฟเวอร์ และช่วงเวลาจึงเป็นทางลัดในการหาสาเหตุ ถ้ามีอุปกรณ์ที่รับการเชื่อมต่อไว้ตรงกลางแล้วเชื่อมต่อใหม่ไปยังเซิร์ฟเวอร์ (proxy, โหลดบาลานเซอร์หรือเกตเวย์บางประเภท) เมตริกของเซิร์ฟเวอร์เกมจะเห็นแค่ช่วงระหว่างอุปกรณ์นั้นกับเซิร์ฟเวอร์ การส่งซ้ำฝั่งผู้เล่นต้องดูที่อุปกรณ์นั้น

ดูที่ไหนดูอะไรบอกอะไรได้
ทั้งเซิร์ฟเวอร์ (Linux)ส่วนที่เพิ่มขึ้นจากการรัน nstat สองครั้งห่างกัน 1 นาที: TcpRetransSegs ÷ TcpOutSegs และตัวนับกลุ่ม TcpExt ได้แก่ TCPTimeouts, TCPLossProbes, TCPLossProbeRecovery, TCPLostRetransmit, TCPSpuriousRTOs, TCPDSACKRecv, TCPSynRetransอัตราการส่งซ้ำ, จำนวนครั้งที่ไปถึง RTO, จำนวนครั้งที่ส่ง TLP และในนั้นมีกี่ครั้งที่อุดแพ็กเก็ตที่หายจริงได้, จำนวนครั้งที่ตัวที่ส่งซ้ำก็หายอีก, การส่งคำขอเชื่อมต่อซ้ำ ถ้า DSACK หรือ Spurious สูง แปลว่า “ส่งซ้ำทั้งที่ไม่ได้หาย” OutSegs ของ Linux ไม่นับส่วนที่ส่งซ้ำ อัตราที่ถูกต้องตามหลักจึงเป็น RetransSegs ÷ (OutSegs + RetransSegs) แต่ที่ระดับราว 1% ความต่างน้อยมาก
แยกตามการเชื่อมต่อ (Linux)retrans (กำลังกู้คืนอยู่/สะสม), rto, backoff, rtt, cwnd, lost, reordering, bytes_retrans จาก ss -tiการส่งซ้ำสูงเฉพาะผู้ใช้หรือพื้นที่บางกลุ่มหรือไม่, RTO ยืดออกไปแค่ไหน (backoff คือจำนวนครั้งที่ RTO เพิ่มเป็นสองเท่าติดต่อกัน) ส่วน bytes_retrans ÷ bytes_sent คืออัตราการส่งซ้ำของการเชื่อมต่อนั้น
ทีละครั้งที่ส่งซ้ำ (Linux)เครื่องมือ eBPF tcpretrans (bcc) โดย -c คือสรุปแยกตามการเชื่อมต่อ และ -l คือรวม TLP ด้วยแสดง IP และพอร์ตของอีกฝั่ง พร้อมสถานะการเชื่อมต่อหนึ่งบรรทัดทุกครั้งที่เกิดการส่งซ้ำ ใช้ตรวจแบบเบา ๆ โดยไม่ต้องทำ packet capture ว่าการส่งซ้ำกระจุกอยู่ที่ช่วง IP ของผู้เล่นกลุ่มไหนหรือเซิร์ฟเวอร์ใด
การ์ดเครือข่ายของเซิร์ฟเวอร์dropped, missed, crc จาก ip -s -s link, rx_missed_errors, rx_no_buffer_count, rx_crc_errors ฯลฯ จาก ethtool -S (ชื่อต่างกันไปตามไดรเวอร์ ใน mlx5 คือ rx_out_of_buffer, rx_discards_phy), คอลัมน์ที่ 2 (dropped) และคอลัมน์ที่ 3 (time_squeeze) ของ /proc/net/softnet_statการ์ดเครือข่ายของเซิร์ฟเวอร์ทิ้งแพ็กเก็ตทันทีที่รับหรือไม่ (ring buffer/CPU) หรือสายหรือโมดูลออปติกเสีย (CRC) softnet_stat มีหนึ่งบรรทัดต่อ CPU และเป็นเลขฐาน 16 ถ้า time_squeeze เพิ่มขึ้นเรื่อย ๆ แปลว่าคอร์ที่ประมวลผลขาเข้าทำงานไม่ทันเวลา
เครือข่ายบนคลาวด์สำหรับ AWS ENA คือ bw_in_allowance_exceeded, bw_out_allowance_exceeded, pps_allowance_exceeded, conntrack_allowance_exceeded, linklocal_allowance_exceeded จาก ethtool -S ค่าเหล่านี้ไม่มีในหน้าจอ CloudWatch พื้นฐาน ต้องเก็บแยกด้วย CloudWatch agentถูกทิ้งเงียบ ๆ เพราะชนขีดจำกัดของอินสแตนซ์หรือไม่ ถ้าค่าเพิ่มขึ้นเรื่อย ๆ แปลว่าเกินขีดจำกัด คลาวด์อื่นก็มีขีดจำกัดแบนด์วิดท์และจำนวนการเชื่อมต่อตามขนาด VM เช่นกัน
สวิตช์/เราเตอร์/ไฟร์วอลล์CRC และ input error ของพอร์ต, output drop, จำนวนที่เกินค่า policer, อัตราการใช้ตารางเซสชัน, log การทิ้งแพ็กเก็ตถูกทิ้งในช่วงอุปกรณ์ของดาต้าเซ็นเตอร์หรือไม่ ถ้าอัตราการใช้งานเฉลี่ย 5 นาทีต่ำ แต่ output drop เพิ่มขึ้น แปลว่าเป็น microburst (ทราฟฟิกกระจุกในช่วงเวลาสั้นมาก)
เส้นทางแพ็กเก็ตหายที่ต่อเนื่องไปจนถึงปลายทาง วัดด้วย mtr หรือ pathping ต้องส่งหลายร้อยครั้งขึ้นไปจึงจะเห็นการหายระดับราว 1% และถ้าส่งไปที่พอร์ต TCP เดียวกับเกม (mtr -T -P PORT) จะแม่นยำกว่าการหายเริ่มตั้งแต่ hop ที่เท่าไร ถ้ามีแค่ hop ตรงกลางจุดเดียวที่ดูเหมือนหาย แต่ hop ถัดไปปกติ แปลว่าอุปกรณ์นั้นแค่จำกัดการตอบกลับสำหรับการวัด (ICMP) เส้นทางขาไปและขากลับอาจต่างกัน จึงต้องวัดจากฝั่งเซิร์ฟเวอร์ไปหาผู้เล่นด้วย
packet capture (ปลายทั้งสองฝั่ง)ฟิลเตอร์ Wireshark tcp.analysis.retransmission และตัวอื่นในกลุ่ม tcp.analysis. เดียวกัน ได้แก่ fast_retransmission, spurious_retransmission, duplicate_ack, lost_segment, zero_windowถ้าแพ็กเก็ตต้นฉบับมีใน capture ฝั่งผู้ส่งแต่ไม่มีในฝั่งผู้รับ แปลว่าหายระหว่างทาง ถ้าฝั่งผู้รับก็มี แปลว่าเป็นการส่งซ้ำโดยไม่จำเป็น หรือ ACK ขากลับมาช้าหรือหายไป แพ็กเก็ตที่ถูกทิ้งใน ring buffer ของเซิร์ฟเวอร์ผู้รับก็จะดูเหมือน “หายระหว่างทาง” ใน capture ด้วย จึงต้องดูคู่กับตัวนับของการ์ดเครือข่าย
Windows ServerTCPv4\Segments Retransmitted/sec ÷ Segments Sent/sec ใน Performance Monitor, Network Interface\Packets Received Discarded, netsh int tcp show global, pktmon (มีในตัวตั้งแต่ Windows 10 1809 และ Windows Server 2019 ขึ้นไป)แนวโน้มอัตราการส่งซ้ำ, การ์ดเครือข่ายทิ้งแพ็กเก็ตทันทีที่รับหรือไม่, การตั้งค่า TCP, ถูกทิ้งที่จุดไหนภายใน Windows

ลำดับการตรวจ: เมื่อตรวจร่วมกับทีมอินฟรา ไล่ตามลำดับนี้จะเร็วที่สุด

  1. เมื่อไรและกับใคร: ดูว่าอัตราการส่งซ้ำเริ่มเพิ่มตั้งแต่เมื่อไร และกระจุกอยู่ที่พื้นที่, ISP, เซิร์ฟเวอร์ หรือช่วงเวลาใดหรือไม่
  2. หายจริงหรือไม่: ถ้า TCPSpuriousRTOs และ DSACK เพิ่มขึ้นพร้อมกัน ให้สงสัยการส่งซ้ำโดยไม่จำเป็นก่อน (แพ็กเก็ตแค่มาช้าแต่ถูกเข้าใจว่าหาย)
  3. ขั้นรับข้อมูลของเซิร์ฟเวอร์: ถ้าในเวลาเดียวกัน ตัวนับของการ์ดเครือข่าย, softnet หรือขีดจำกัดของคลาวด์เพิ่มขึ้น แปลว่าถูกทิ้งที่ฝั่งเซิร์ฟเวอร์
  4. อุปกรณ์ในดาต้าเซ็นเตอร์: ดูตัวนับ drop และ CRC ของสวิตช์และไฟร์วอลล์ รวมถึงตารางเซสชัน
  5. เส้นทางภายนอก: ใช้ mtr ทั้งสองทิศจากฝั่งผู้เล่นที่มีปัญหาและฝั่งเซิร์ฟเวอร์ เพื่อหา hop ที่เริ่มมีแพ็กเก็ตหาย
  6. ถ้ายังไม่รู้อีก: ทำ packet capture ที่ปลายทั้งสองฝั่งในเวลาเดียวกันแล้วเทียบกัน

ถ้าการแจ้งปัญหามีเวลา (ละเอียดถึงวินาที), ISP และพื้นที่ของผู้เล่น, เซิร์ฟเวอร์ที่เชื่อมต่อ และชื่ออาการ ทีมอินฟราจะไล่ตามลำดับนี้ได้ทันที

แนวทางแก้

1. ไม่ให้หาย (แก้ที่ต้นเหตุ)

  • ใช้สาย LAN แทน Wi-Fi, ใช้ 5 GHz/6 GHz, ใช้ SQM/ECN ในเราเตอร์เพื่อลดคิวล้น
  • ให้เซิร์ฟเวอร์กระจายการส่งอัปเดตไปตลอดทิก ไม่ยิงอัปเดตของทั้งทิกออกไปพร้อมกัน ส่วนการส่งรวดเดียวของการเชื่อมต่อเดียว ให้ใช้ pacing (fq, BBR, เพดานอัตราการส่ง) ปรับให้สม่ำเสมอ
  • เพิ่ม ring buffer, กระจายอินเทอร์รัปต์ไปหลายคอร์, ตรวจขีดจำกัดของคลาวด์
  • เปลี่ยนสายหรือโมดูลออปติกที่มี CRC error, ตั้งค่า duplex ให้ตรงกัน
  • ใช้ shaper (เข้าคิวไว้แล้วค่อย ๆ ปล่อยออก) แทน policer (ทิ้งส่วนที่เกินทันที), เพิ่ม burst ที่อนุญาต
  • เผื่อที่ว่างให้ตารางของไฟร์วอลล์และ connection tracking (conntrack) รวมถึงจำนวนแพ็กเก็ตต่อวินาทีของอุปกรณ์ระหว่างทาง, จัดเส้นทางขาไปและขากลับให้ผ่านไฟร์วอลล์ตัวเดียวกัน
  • ป้องกัน MTU black hole ด้วยการปรับ MSS (ขนาดข้อมูลสูงสุดที่ใส่ในแพ็กเก็ตหนึ่งตัว) และอนุญาตข้อความแจ้งว่าขนาดเกิน (ICMP) (MTU probing เป็นด่านสุดท้าย), รักษา mapping ของ NAT/LB ด้วย heartbeat ที่ไคลเอนต์ส่ง

2. ให้กู้คืนได้เร็ว

  • ตรวจว่า SACK และ timestamp ไม่ได้ถูกปิดในการตั้งค่าเซิร์ฟเวอร์ หรือถูกลบทิ้งโดยอุปกรณ์ระหว่างทาง (ถ้าไม่มี SACK, RACK-TLP ก็ไม่ทำงาน)
  • ใช้ RACK-TLP (ค่าเริ่มต้นใน Linux และ Android รุ่นใหม่) ข้อมูลที่ไคลเอนต์เป็นผู้ส่ง เช่น อินพุตของเรา OS ฝั่งไคลเอนต์เป็นผู้กู้คืน การตั้งค่าเซิร์ฟเวอร์จึงเปลี่ยนไม่ได้ (Windows เปิด TLP และ RACK เป็นค่าเริ่มต้นตั้งแต่ Windows 10 (1607) และ Windows Server 2016 ส่วน RACK แบบใหม่ที่กู้คืนได้แม้ตัวที่ส่งซ้ำจะหายอีก มีตั้งแต่ Windows Server 2022)
  • ใช้ tcp_thin_linear_timeouts สำหรับ thin stream และใน Linux 6.15 ขึ้นไปใช้ TCP_RTO_MAX_MS ลดเพดานของ RTO
  • เปิด TCP_NODELAY ไว้สำหรับการเชื่อมต่อของเกม (ถ้า Nagle เก็บแพ็กเก็ตใหม่ไว้ไม่ส่ง RACK จะไม่มีแพ็กเก็ตที่ตามมาให้ใช้ตัดสิน)
  • การเชื่อมต่อระหว่างเซิร์ฟเวอร์ในเครือข่ายภายใน ให้ลดค่าต่ำสุดของ RTO รายเส้นทาง (ip route … rto_min)
  • ใช้ TCP_USER_TIMEOUT และ heartbeat ของเกม ตัดการเชื่อมต่อที่ตายแล้วให้เร็วแล้วเชื่อมต่อใหม่

3. ให้ไวต่อการส่งซ้ำน้อยลง (ระดับโครงสร้าง)

  • ตำแหน่งและการต่อสู้แบบเรียลไทม์ให้ส่งบน UDP และส่งซ้ำเฉพาะสิ่งที่จำเป็น (ตำแหน่งเก่าไม่มีค่าพอจะส่งซ้ำ) ส่วนอินพุต ถ้าส่งอินพุตล่าสุดหลาย ๆ อันซ้อนไปด้วย ต่อให้หายหนึ่งแพ็กเก็ต แพ็กเก็ตถัดไปก็อุดได้
  • แยกสิ่งที่ต้องรักษาลำดับ เช่น แชตและเทรด ออกจากแพ็กเก็ตเรียลไทม์เป็นคนละ stream (QUIC stream, แยกการเชื่อมต่อ TCP ฯลฯ) แพ็กเก็ตหายในฝั่งหนึ่งจะไม่บล็อกอีกฝั่ง
  • ถ้ายังใช้ TCP ต่อ ไม่ปล่อยให้ตำแหน่งเก่ากองอยู่ใน send buffer โดยเขียนทับด้วยสถานะล่าสุด (TCP_NOTSENT_LOWAT ฯลฯ) อาการกรอเร็วหลังค้างนานจะสั้นลง
  • ใช้ interpolation buffer และ prediction กลบการหยุดแวบสั้น ๆ บนจอ แต่กลบการค้างหลายร้อย ms จาก RTO ได้ยาก

สรุปชื่อการตั้งค่า: การตั้งค่าที่ควรเปิดและการตั้งค่าที่มักสับสนกัน

การตั้งค่าที่เกี่ยวกับการกู้คืนจากการส่งซ้ำส่วนใหญ่เป็นการตั้งค่าของระบบปฏิบัติการ (เคอร์เนล) socket option ที่เปิดเฉพาะการเชื่อมต่อของเกมได้มีอยู่ไม่กี่ตัว TCP_NODELAY ที่มักสับสนเพราะชื่อ ไม่ได้ทำให้กู้คืนเร็วขึ้น อย่างไรก็ตาม ถ้าไม่เปิด (ใช้ Nagle) แพ็กเก็ตใหม่ระหว่างกู้คืนจะช้าลงอีก ตารางด้านล่างอิง Linux ส่วน Windows มีชื่อและขอบเขตการรองรับต่างออกไป

การตั้งค่าตั้งที่ไหนเปลี่ยนอะไรข้อควรระวัง
TCP_NODELAYsocket optionปิด Nagle ส่งข้อความเล็ก ๆ ทันทีโดยไม่รวบรวมก่อนตัดการรอ 40–200 ms ที่เกิดขึ้นแม้ไม่มีแพ็กเก็ตหาย ตัวจับเวลาส่งซ้ำ (RTO) เองไม่เปลี่ยน แต่ถ้า Nagle เปิดอยู่ ระหว่างรอกู้คืน แพ็กเก็ตใหม่จะถูกมัดรวมไว้ด้วย หลังกู้คืนต้องรออีกหนึ่งรอบไปกลับ และแพ็กเก็ตที่ตามมาซึ่ง fast retransmit และ RACK ต้องอาศัยก็หายไป จึงไปถึง RTO ได้ง่าย เกมส่วนใหญ่จึงเปิดไว้
net.ipv4.tcp_recovery (RACK)การตั้งค่าเคอร์เนลตัดสินการหายตามเวลา ทนต่อการสลับลำดับ และกู้คืน thin stream ได้เร็วค่าเริ่มต้น 1 (เปิด) เพิ่มเข้ามาใน Linux 4.4 และเป็นรูปแบบปัจจุบันราว 4.18 ตั้งแต่ 6.17 RACK เป็นวิธีตัดสินการหายเพียงวิธีเดียว ตั้งเป็น 0 ก็ไม่มีผล ใช้ไม่ได้กับการเชื่อมต่อที่ไม่มี SACK
net.ipv4.tcp_early_retrans (TLP)การตั้งค่าเคอร์เนลถ้าไม่มี ACK มาสักพัก (ราว 2 เท่าของเวลาไปกลับ) จะส่งแพ็กเก็ตท้ายซ้ำอีกครั้ง เพื่อตรวจพบการหายของแพ็กเก็ตท้าย ๆ (tail loss) ได้เร็วค่าเริ่มต้น 3 (เปิด) ถ้าเป็น 0 คือปิด ต้องมี SACK จึงทำงาน ถ้ามีแพ็กเก็ตที่ยังไม่ได้รับ ACK (in-flight) อยู่เพียงตัวเดียว จะรอเพิ่มอีก 200 ms จนใกล้เคียงกับ RTO
net.ipv4.tcp_sack, tcp_dsack, tcp_timestampsการตั้งค่าเคอร์เนลselective ACK (SACK แจ้งช่วงที่ขาดหาย), การแจ้งว่าได้รับซ้ำ (DSACK), การวัดเวลาไปกลับ (timestamp)ค่าเริ่มต้นเปิดทั้งหมด มีเซิร์ฟเวอร์ที่ปิดไว้ตั้งแต่ช่องโหว่ความปลอดภัยของ SACK ในปี 2019 แล้วไม่ได้เปิดกลับ ถ้าปิด SACK, RACK และ TLP ก็จะไม่ทำงานไปด้วย
net.ipv4.tcp_thin_linear_timeouts / TCP_THIN_LINEAR_TIMEOUTSการตั้งค่าเคอร์เนล / socket optionการเชื่อมต่อที่มีแพ็กเก็ตที่ยังไม่ได้รับ ACK (in-flight) น้อยกว่า 4 ตัว จะไม่เพิ่ม RTO เป็นสองเท่าในช่วง 6 ครั้งแรกค่าเริ่มต้นปิด เปิดเฉพาะการเชื่อมต่อของเกมได้ด้วย socket option ไม่ได้ลด RTO ครั้งแรก
TCP_RTO_MAX_MS / net.ipv4.tcp_rto_max_mssocket option / การตั้งค่าเคอร์เนล (Linux 6.15 ขึ้นไป)ลดเพดานของ RTO ที่เพิ่มขึ้นทีละสองเท่า (ค่าเริ่มต้น 120 วินาที) ต่ำสุด 1 วินาทีไม่ให้ RTO ขยายไปถึงหลายสิบวินาทีหลังแพ็กเก็ตหายต่อเนื่อง การตัดสินว่าการเชื่อมต่อตายแล้วก็เร็วขึ้นด้วย
net.ipv4.tcp_mtu_probingการตั้งค่าเคอร์เนลถ้าแพ็กเก็ตใหญ่หายต่อเนื่อง จะลดขนาดลงเพื่อให้ผ่าน MTU black hole ได้ค่าเริ่มต้น 0 (ปิด) 1 = ลดขนาดเฉพาะเมื่อการส่งซ้ำต่อเนื่องราว 3 วินาทีจนสงสัยว่าเป็น black hole (ระหว่างนั้นค้าง) 2 = เริ่มที่ 1,024 ไบต์ตั้งแต่แรกแล้วค่อย ๆ ลองขยาย
ip route … rto_minการตั้งค่าเส้นทางลดค่าต่ำสุดของ RTO (ค่าเริ่มต้น 200 ms) ของเส้นทางนั้นใช้เฉพาะเครือข่ายภายในระหว่างเซิร์ฟเวอร์ ถ้าลดในช่วงอินเทอร์เน็ต การส่งซ้ำโดยไม่จำเป็นจะเพิ่มขึ้น net.ipv4.tcp_rto_min_us ใน Linux 6.11 ขึ้นไปเป็นค่าของทั้งเซิร์ฟเวอร์ การเชื่อมต่ออินเทอร์เน็ตจึงเปลี่ยนตามไปด้วย ตั้งแต่ 6.15 ใช้ socket option TCP_RTO_MIN_US ลดเฉพาะการเชื่อมต่อภายในได้
TCP_USER_TIMEOUTsocket optionระยะเวลาก่อนยอมแพ้และตัดการเชื่อมต่อเมื่อการส่งซ้ำยังต่อเนื่องไม่ได้เร่งการกู้คืน ช่วยตัดการเชื่อมต่อที่ตายแล้วให้เร็วเพื่อเชื่อมต่อใหม่ ถ้าไม่ตั้ง Linux จะส่งซ้ำราว 15 ครั้ง ประมาณ 15 นาที กว่าจะตัด (tcp_retries2)
SO_KEEPALIVE + TCP_KEEPIDLE ฯลฯsocket optionตรวจว่าการเชื่อมต่อที่ idle ยังใช้งานได้อยู่หรือไม่แยกจากการส่งซ้ำ ใช้รักษา mapping ของ NAT/LB และตรวจจับการเชื่อมต่อที่ตายแล้ว
คิว fq + SO_MAX_PACING_RATE, BBRการตั้งค่าคิว / socket option / การตั้งค่าเคอร์เนลกระจายการส่งแพ็กเก็ตให้สม่ำเสมอ เพื่อลดการหายจาก burst (การส่งรวดเดียวจำนวนมาก)เป็นฝั่ง “ป้องกัน” การหาย ไม่เกี่ยวกับความเร็วในการกู้คืน

ต้นเหตุของการส่งซ้ำใน TCP

แพ็กเก็ตหายช่วงไร้สาย Wi-Fi / cellular link loss

Wi-Fi และเครือข่ายมือถือจะลองส่งซ้ำในช่วงไร้สายอยู่หลายครั้ง ถ้ายังไม่สำเร็จก็จะทิ้งแพ็กเก็ตนั้น แล้ว TCP จะส่งแพ็กเก็ตที่ถูกทิ้งใหม่หลังจากนั้นพักใหญ่

ทำไม: สัญญาณอ่อนหรือถูกรบกวนหนัก การส่งในช่วงไร้สายจึงล้มเหลวติดต่อกัน → ผลคือ: อุปกรณ์ไร้สายลองใหม่จนเกินขีดจำกัด (ปกติไม่กี่ครั้งถึงสิบกว่าครั้ง) แล้วทิ้งแพ็กเก็ต → บนหน้าจอ: ค้างนานเท่ากับเวลาที่ TCP รอส่งซ้ำ แพ็กเก็ตที่ตามมารออยู่ใน receive buffer แล้วทะลักออกมาเป็นอาการกรอเร็ว

อาการ: ค้าง, กรอเร็ว, วาร์ป · ผู้รับผิดชอบหลัก ภายนอก (ภายนอก) · ร่วมกับ อินฟราเซิร์ฟเวอร์ (ทีมอินฟรา), พัฒนาเซิร์ฟเวอร์ (ทีมพัฒนาเกม), พัฒนาไคลเอนต์ (ทีมพัฒนาเกม)

คิวคอขวดล้น (แพ็กเก็ตหายจากความแออัด) Tail drop at a congested bottleneck

จุดที่แคบที่สุดบนเส้นทาง เช่น เราเตอร์, ลิงก์เชื่อมระหว่าง ISP หรือลิงก์ของดาต้าเซ็นเตอร์ เมื่อคิวตรงนั้นเต็ม แพ็กเก็ตที่มาใหม่จะถูกทิ้ง

ทำไม: วิดีโอ, การดาวน์โหลด และทราฟฟิกของผู้ใช้คนอื่นทำให้ช่วงคอขวดเต็ม → ผลคือ: ระหว่างที่คิวเต็ม แพ็กเก็ตที่มาใหม่ถูกทิ้งติดต่อกัน (tail drop) แพ็กเก็ตที่ไม่ถูกทิ้งก็ต้องรออยู่ท้ายคิวที่เต็มแน่น → บนหน้าจอ: แพ็กเก็ตหายหลายตัวพร้อมกัน จึงค้างนานแล้วตามด้วยอาการกรอเร็ว เป็นบ่อยช่วงหัวค่ำ

อาการ: ค้าง, กรอเร็ว, ดีดกลับ · ผู้รับผิดชอบหลัก อินฟราเครือข่าย (ทีมอินฟรา) · ร่วมกับ ภายนอก (ภายนอก), พัฒนาไคลเอนต์ (ทีมพัฒนาเกม)

บัฟเฟอร์ตื้นล้นเพราะ burst จากฝั่งส่ง Sender bursts overflow shallow buffers

ถ้าเซิร์ฟเวอร์ส่งอัปเดตของผู้เล่นหลายพันคนออกไปรวดเดียวในทุกทิก บัฟเฟอร์เล็ก ๆ ของสวิตช์หรือขีดจำกัดชั่วขณะของคลาวด์จะล้นภายในไม่ถึง 1 ms และแพ็กเก็ตบางส่วนถูกทิ้ง

ทำไม: ส่งแพ็กเก็ตของทุกคนออกไปพร้อมกันตอนเริ่มทิก → ผลคือ: บัฟเฟอร์ของพอร์ตสวิตช์ที่ทราฟฟิกจากหลายเซิร์ฟเวอร์มารวมกัน (หลายร้อย KB ถึงหลาย MB ต่อพอร์ต) หรือขีดจำกัดของอินสแตนซ์คลาวด์ล้นชั่วขณะ (อัตราการใช้งานเฉลี่ยต่ำ) → บนหน้าจอ: หลายคนวาร์ปหรือหยุดแวบพร้อมกัน, ดูจากเมตริกค่าเฉลี่ยจะไม่เห็นสาเหตุ

อาการ: วาร์ป, ค้าง, กรอเร็ว · ผู้รับผิดชอบหลัก พัฒนาเซิร์ฟเวอร์ (ทีมพัฒนาเกม) · ร่วมกับ อินฟราเซิร์ฟเวอร์ (ทีมอินฟรา), อินฟราเครือข่าย (ทีมอินฟรา)

policer ทิ้งส่วนที่เกิน Traffic policing

แพ็กเกจเน็ตของ ISP, ขีดจำกัดของอินสแตนซ์คลาวด์ และอุปกรณ์ป้องกัน DDoS บางครั้งจะทิ้งแพ็กเก็ตที่เกินความเร็วที่กำหนดทันทีโดยไม่เข้าคิว

ทำไม: ปริมาณที่ส่งในชั่วขณะเกินความเร็วหรือ burst ที่อนุญาต → ผลคือ: แพ็กเก็ตส่วนที่เกินถูกทิ้งทันทีโดยไม่เข้าคิว (policing) → บนหน้าจอ: ทุกจังหวะที่ burst ใหญ่ แพ็กเก็ตหายหลายตัว จึงค้างแล้วตามด้วยอาการกรอเร็ว, ความเร็วเฉลี่ยดูต่ำกว่าขีดจำกัด

อาการ: ค้าง, กรอเร็ว, วาร์ป · ผู้รับผิดชอบหลัก อินฟราเครือข่าย (ทีมอินฟรา) · ร่วมกับ อินฟราเซิร์ฟเวอร์ (ทีมอินฟรา), พัฒนาเซิร์ฟเวอร์ (ทีมพัฒนาเกม)

ข้อผิดพลาดทางกายภาพ (สายเสีย/โมดูลออปติก/คอนเน็กเตอร์) Bit errors: bad cable, optics, dirty fiber

สายที่ชำรุด, คอนเน็กเตอร์ไฟเบอร์ที่มีฝุ่น และโมดูลออปติกที่หมดอายุการใช้งาน ทำให้เกิด bit error และอุปกรณ์จะทิ้งแพ็กเก็ตที่เสียไปเงียบ ๆ

ทำไม: บิตกลับค่าเพราะสาย, โมดูลออปติก หรือคอนเน็กเตอร์เสีย → ผลคือ: อุปกรณ์ทิ้งแพ็กเก็ตที่ checksum (CRC) ไม่ตรง → บนหน้าจอ: เฉพาะคนที่ผ่านเส้นทางนั้นหยุดแวบสั้น ๆ แล้วกรอเร็วอยู่เป็นระยะ, ไม่ขึ้นกับช่วงเวลา

อาการ: ค้าง, กรอเร็ว, วาร์ป · ผู้รับผิดชอบหลัก อินฟราเครือข่าย (ทีมอินฟรา) · ร่วมกับ อินฟราเซิร์ฟเวอร์ (ทีมอินฟรา), ภายนอก (ภายนอก)

duplex ไม่ตรงกัน Duplex mismatch

ถ้าฝั่งหนึ่งใช้ auto-negotiation แต่อีกฝั่งล็อกความเร็วและ duplex ไว้ ฝั่งหนึ่งจะทำงานแบบ half duplex และทุกครั้งที่มีโหลดจะเกิด collision จนแพ็กเก็ตหาย

ทำไม: ล็อกค่าความเร็วและ duplex ไว้ที่อุปกรณ์ฝั่งเดียว → ผลคือ: ฝั่งหนึ่งทำงานแบบ full duplex อีกฝั่งเป็น half duplex จึงเกิด collision และ late collision → บนหน้าจอ: ปกติไม่มีอาการ แต่พอทราฟฟิกเพิ่ม ทุกคนที่ผ่านอุปกรณ์นั้นค้างแล้วตามด้วยอาการกรอเร็ว

อาการ: ค้าง, กรอเร็ว · ผู้รับผิดชอบหลัก อินฟราเครือข่าย (ทีมอินฟรา) · ร่วมกับ อินฟราเซิร์ฟเวอร์ (ทีมอินฟรา)

เครื่องเซิร์ฟเวอร์ฝั่งรับทิ้งแพ็กเก็ต Receiver host drops (ring, softirq, CPU)

แพ็กเก็ตมาถึงเซิร์ฟเวอร์แล้ว แต่ถูกทิ้งเพราะ ring buffer ของ NIC (บัฟเฟอร์ที่พักแพ็กเก็ตที่มาถึงไว้ชั่วคราว) ล้น หรือคอร์ที่เคอร์เนลใช้ประมวลผลขาเข้าทำงานเต็ม

ทำไม: ผู้เล่นทะลักเข้ามา, อินเทอร์รัปต์กระจุกที่คอร์เดียว, CPU steal ของ VM, virtual switch โหลดเกิน → ผลคือ: ถูกทิ้งที่ ring buffer (เช่น rx_missed_errors ซึ่งชื่อต่างกันตามไดรเวอร์) หรือที่คิวรับของเคอร์เนล (softnet dropped) → บนหน้าจอ: ช่วงที่คนเยอะ อินพุตเข้าช้าและหยุดแวบพร้อมกันทั้งเซิร์ฟเวอร์

อาการ: อินพุตดีเลย์, ค้าง, กรอเร็ว, วาร์ป · ผู้รับผิดชอบหลัก อินฟราเซิร์ฟเวอร์ (ทีมอินฟรา)

ไฟร์วอลล์/connection tracking ทิ้งแพ็กเก็ต Stateful firewall / conntrack drops

ไฟร์วอลล์หรือ connection tracking ของ Linux (conntrack คือฟังก์ชันที่บันทึกการเชื่อมต่อที่ผ่านลงในตาราง) จะทิ้งแพ็กเก็ตเมื่อตารางเต็ม หรือเมื่อตัดสินว่าสถานะการเชื่อมต่อไม่ถูกต้อง

ทำไม: ตาราง connection tracking เต็ม (table full) หรือเส้นทางขาไปกับขากลับต่างกันจนผ่านไฟร์วอลล์แค่ทิศทางเดียว (asymmetric routing) → ผลคือ: ไฟร์วอลล์มองว่าเป็นแพ็กเก็ตของ “การเชื่อมต่อที่ไม่รู้จัก” หรือ “sequence number ที่อยู่นอกช่วง window” แล้วทิ้ง → บนหน้าจอ: ถ้าตารางเต็ม การเชื่อมต่อใหม่จะถูกบล็อก, ถ้าเส้นทางเหลื่อมกัน เฉพาะคนที่ใช้เส้นทางนั้นจะส่งซ้ำวนไปจนหลุด

อาการ: ค้าง, หลุด, เข้าเกมไม่ได้/โหลดไม่จบ · ผู้รับผิดชอบหลัก อินฟราเครือข่าย (ทีมอินฟรา) · ร่วมกับ อินฟราเซิร์ฟเวอร์ (ทีมอินฟรา), พัฒนาเซิร์ฟเวอร์ (ทีมพัฒนาเกม), พัฒนาไคลเอนต์ (ทีมพัฒนาเกม)

อุปกรณ์กลางทางเกินขีดความสามารถ (ไฟร์วอลล์/IPS/ระบบป้องกัน DDoS) Inline appliance PPS / CPU overload

ไฟร์วอลล์, อุปกรณ์ป้องกันการบุกรุก (IPS) และอุปกรณ์ป้องกัน DDoS ตรวจแพ็กเก็ตที่ผ่านทีละตัว เมื่อเกินความสามารถในการตรวจ แพ็กเก็ตที่ประมวลผลไม่ทันจะถูกทิ้งตั้งแต่จังหวะนั้น

ทำไม: ช่วงพีคหรืออีเวนต์ แพ็กเก็ตเกมเล็ก ๆ ทะลักเข้ามาเกินหลายแสนตัวต่อวินาที หรือกฎการตรวจหนัก → ผลคือ: CPU หรือขีดจำกัดจำนวนแพ็กเก็ตต่อวินาทีของอุปกรณ์เต็ม อุปกรณ์จึงทิ้งแพ็กเก็ต ถ้าเป็น false positive แพ็กเก็ตปกติก็ถูกบล็อกด้วย → บนหน้าจอ: ผู้เล่นบนทุกเซิร์ฟเวอร์ที่อยู่หลังอุปกรณ์นั้นค้างหรือวาร์ปพร้อมกัน, หนักขึ้นเฉพาะตอนคนแห่มารวมกัน

อาการ: ค้าง, กรอเร็ว, วาร์ป, หลุด · ผู้รับผิดชอบหลัก อินฟราเครือข่าย (ทีมอินฟรา) · ร่วมกับ พัฒนาเซิร์ฟเวอร์ (ทีมพัฒนาเกม)

MTU black hole (เฉพาะแพ็กเก็ตใหญ่ที่หายซ้ำแล้วซ้ำอีก) PMTU black hole

เมื่อขนาดที่ช่วงกลางทางรับได้เล็กลง แต่ข้อความแจ้งว่า “ใหญ่เกินไป” (ICMP) ถูกบล็อก แพ็กเก็ตใหญ่จะหายไปทุกครั้งไม่ว่าจะส่งซ้ำกี่รอบ

ทำไม: ขนาดสูงสุดลดลงในช่วงที่ผ่าน VPN หรืออุโมงค์ (tunnel) และข้อความแจ้งว่าเกินขนาดถูกไฟร์วอลล์บล็อก → ผลคือ: ฝั่งส่งไม่รู้สาเหตุ จึงส่งแพ็กเก็ตใหญ่ตัวเดิมซ้ำไปเรื่อย ๆ และ RTO เพิ่มเป็นสองเท่าทุกครั้ง → บนหน้าจอ: ปกติไม่มีอาการ แต่ในจังหวะที่มีข้อมูลใหญ่วิ่ง เช่น เปิดกระเป๋า, ไปที่ที่คนเยอะ หรือโหลดตอนเข้าแมพ แพ็กเก็ตเล็กที่ตามมาก็หยุดไปหมด สุดท้ายหลุดหรือโหลดไม่จบ

อาการ: ค้าง, หลุด, เข้าเกมไม่ได้/โหลดไม่จบ · ผู้รับผิดชอบหลัก อินฟราเครือข่าย (ทีมอินฟรา) · ร่วมกับ อินฟราเซิร์ฟเวอร์ (ทีมอินฟรา), พัฒนาเซิร์ฟเวอร์ (ทีมพัฒนาเกม)

mapping ของ NAT/โหลดบาลานเซอร์หมดอายุกลางการเชื่อมต่อ NAT / load balancer mapping expired mid-connection

ถ้าอุปกรณ์กลางทางลบ mapping (รายการที่บันทึกว่าจะส่งต่อการเชื่อมต่อนี้ไปที่ไหน) ของการเชื่อมต่อที่ idle แพ็กเก็ตถัดไปที่ส่งจะไปไม่ถึง การเชื่อมต่อจะส่งซ้ำวนไปจนหลุด หรืออุปกรณ์ตอบกลับด้วยการปฏิเสธการเชื่อมต่อ (RST) แล้วหลุดทันที

ทำไม: การเชื่อมต่อที่ไม่มีแพ็กเก็ตวิ่งอยู่พักหนึ่ง (AFK, อยู่ในล็อบบี้) → ผลคือ: NAT ของเราเตอร์, CGNAT ของ ISP, ไฟร์วอลล์, โหลดบาลานเซอร์ หรือ security group บนคลาวด์ลบ mapping ที่ idle → บนหน้าจอ: พอกลับมาขยับ การส่งซ้ำต่อเนื่องจนหลุด หรือหลุดทันที

อาการ: หลุด, ค้าง · ผู้รับผิดชอบหลัก พัฒนาไคลเอนต์ (ทีมพัฒนาเกม) · ร่วมกับ พัฒนาเซิร์ฟเวอร์ (ทีมพัฒนาเกม), อินฟราเครือข่าย (ทีมอินฟรา), อินฟราเซิร์ฟเวอร์ (ทีมอินฟรา)

เส้นทางเปลี่ยน/เส้นทาง ECMP ที่เสีย Route change / bad ECMP member

แพ็กเก็ตจะหายไปในช่วงไม่กี่วินาทีที่เส้นทางอินเทอร์เน็ตเปลี่ยน หรือในการเชื่อมต่อที่ถูกจัดให้วิ่งบนเส้นทางที่เสียจากหลายเส้นทาง ECMP

ทำไม: BGP คำนวณเส้นทางใหม่ หรืออุปกรณ์/ลิงก์ของเส้นทางหนึ่งในหลายเส้นทาง (ECMP, LAG) เสีย → ผลคือ: แพ็กเก็ตหายชั่วคราวระหว่างสลับเส้นทาง หรือเฉพาะการเชื่อมต่อที่วิ่งบนเส้นทางนั้นหายอยู่ตลอด → บนหน้าจอ: จู่ ๆ ก็ค้างไปไม่กี่วินาทีแล้วตามด้วยอาการกรอเร็ว หรือ “เชื่อมต่อใหม่แล้วดีขึ้น” (ถูกจัดไปเส้นทางอื่น)

อาการ: ค้าง, กรอเร็ว, วาร์ป · ผู้รับผิดชอบหลัก อินฟราเครือข่าย (ทีมอินฟรา) · ร่วมกับ พัฒนาเซิร์ฟเวอร์ (ทีมพัฒนาเกม), ภายนอก (ภายนอก)

การส่งซ้ำโดยไม่จำเป็นเพราะความหน่วงพุ่ง Spurious RTO from delay spikes

แพ็กเก็ตยังอยู่ครบและแค่มาถึงช้ามากชั่วครู่ แต่ถ้าความหน่วงนั้นนานกว่า RTO ฝั่งส่งจะตัดสินว่าหายแล้วส่งซ้ำ

ทำไม: bufferbloat, โหมดประหยัดพลังงานของ Wi-Fi, การเปลี่ยนสถานะวิทยุของมือถือ หรือ VM ถูกพักการทำงาน ทำให้ความหน่วงพุ่งชั่วขณะหลายร้อย ms → ผลคือ: RTO หมดเวลาก่อนจึงส่งซ้ำ แล้วแพ็กเก็ตต้นฉบับก็มาถึงตามมา (ฝั่งรับได้รับซ้ำ) → บนหน้าจอ: อาการค้างและกรอเร็วมาจากความหน่วงพุ่งเอง การส่งซ้ำโดยไม่จำเป็นแทบไม่ทำให้ค้างนานขึ้น แค่ดันเมตริกการส่งซ้ำให้สูงจนถูกเข้าใจผิดว่าแพ็กเก็ตหาย

อาการ: ค้าง, กรอเร็ว, อินพุตดีเลย์ · ผู้รับผิดชอบหลัก ภายนอก (ภายนอก) · ร่วมกับ อินฟราเซิร์ฟเวอร์ (ทีมอินฟรา), พัฒนาไคลเอนต์ (ทีมพัฒนาเกม)

fast retransmit โดยไม่จำเป็นเพราะแพ็กเก็ตสลับลำดับ Reordering triggers spurious fast retransmit

เมื่อแพ็กเก็ตสลับลำดับเพราะวิ่งผ่านหลายเส้นทางหรือลิงก์ที่รวมกัน ฝั่งรับจะแจ้งว่า “มีแพ็กเก็ตขาดหาย” ด้วย duplicate ACK และฝั่งส่งจะส่งแพ็กเก็ตที่ไม่ได้หายซ้ำ

ทำไม: อุปกรณ์ที่แบ่งเส้นทางเป็นรายแพ็กเก็ต, LAG (การรวมลิงก์) ที่กระจายเป็นรายแพ็กเก็ต และจังหวะที่เส้นทางเปลี่ยน ทำให้ลำดับสลับกัน → ผลคือ: แพ็กเก็ตหลังมาถึงก่อน duplicate ACK สะสมครบ 3 ตัว → fast retransmit → บนหน้าจอ: แพ็กเก็ตเกมที่วิ่งห่าง ๆ แทบไม่ได้รับผล อัปเดตใหญ่ในที่ที่คนเยอะและการดาวน์โหลดแพตช์ช้าลง และบางครั้งกระตุก

อาการ: กระตุก, อินพุตดีเลย์ · ผู้รับผิดชอบหลัก อินฟราเครือข่าย (ทีมอินฟรา) · ร่วมกับ อินฟราเซิร์ฟเวอร์ (ทีมอินฟรา)

ACK มาช้าหรือหาย (อัปโหลดเต็ม) ACK path congestion on asymmetric links

ข้อมูลมาถึงเรียบร้อย แต่ถ้า ACK ที่บอกว่า “ได้รับแล้ว” ไปติดอยู่ในคิวอัปโหลดที่เต็มจนช้าหรือหาย ฝั่งส่งจะตัดสินว่าหายแล้วส่งซ้ำ

ทำไม: อัปโหลดเต็มเพราะมีคนในบ้านอัปโหลดวิดีโอหรือสำรองข้อมูลขึ้นคลาวด์ → ผลคือ: ACK ติดอยู่ในคิวของเราเตอร์ช้าไปหลายร้อย ms หรือถูกทิ้งเพราะคิวล้น → บนหน้าจอ: แพ็กเก็ตเกมที่เซิร์ฟเวอร์ส่งมาส่วนใหญ่มาตรงเวลา แต่อินพุตของเราที่กองอยู่ในคิวอัปโหลดเดียวกันไปช้า จึงเกิดอินพุตดีเลย์และดีดกลับ บางครั้งมีการส่งซ้ำโดยไม่จำเป็น

อาการ: อินพุตดีเลย์, ดีดกลับ · ผู้รับผิดชอบหลัก ภายนอก (ภายนอก) · ร่วมกับ พัฒนาไคลเอนต์ (ทีมพัฒนาเกม)

ค่า RTO ไม่เหมาะกับสภาพแวดล้อม RTO min too low or too high

ถ้าลดค่าต่ำสุดของ RTO มากเกินไป แค่ช้านิดเดียวก็เกิดการส่งซ้ำโดยไม่จำเป็น ส่วนค่าเริ่มต้น (200 ms) ก็นานเกินไปสำหรับเกม ทุกครั้งที่แพ็กเก็ตหายจึงหยุดนาน

ทำไม: ลดค่าต่ำสุดของ RTO ลงมากเพื่อใช้ในดาต้าเซ็นเตอร์ หรือใช้ค่าเริ่มต้นตามเดิมกับเส้นทางอินเทอร์เน็ต → ผลคือ: ถ้าต่ำ แค่ความหน่วงชั่วขณะก็ส่งซ้ำทะลัก ถ้าสูง หายทุกครั้งก็ต้องรอนาน → บนหน้าจอ: ถ้าใช้ค่าเริ่มต้น แพ็กเก็ตหายครั้งเดียวจะค้างหลายร้อย ms แล้วตามด้วยอาการกรอเร็ว ถ้าลดต่ำเกินไป อาการค้างจะลดลงแต่การส่งซ้ำโดยไม่จำเป็นพุ่งจนเปลืองแบนด์วิดท์

อาการ: ค้าง, กรอเร็ว, อินพุตดีเลย์ · ผู้รับผิดชอบหลัก อินฟราเซิร์ฟเวอร์ (ทีมอินฟรา) · ร่วมกับ พัฒนาเซิร์ฟเวอร์ (ทีมพัฒนาเกม)

thin stream กู้คืนช้า Thin streams fall back to RTO

ถ้าส่งแพ็กเก็ตเล็ก ๆ ห่าง ๆ แบบเกม RTO จะมาถึงก่อนที่ “แพ็กเก็ตที่ตามมา 3 ตัว” จะครบ เมื่อหายเท่ากัน จึงหยุดนานกว่าการส่งข้อมูลปริมาณมากอยู่มาก

ทำไม: แพ็กเก็ตห่างกันราว 100 ms แพ็กเก็ตที่ยังไม่ได้รับ ACK (in-flight) จึงมีอยู่ไม่กี่ตัว → ผลคือ: กว่า duplicate ACK จะครบ 3 ตัวต้องใช้เวลาเกิน 300 ms จึงเป็น RTO (ปิง + 200 ms) ที่ทำงานก่อน ถ้าหายต่อเนื่องก็เพิ่มเป็นสองเท่าทุกครั้ง → บนหน้าจอ: แพ็กเก็ตหายครั้งเดียวค้างราว 0.3 วินาที ถ้าตัวที่ส่งซ้ำหายอีก จะค้างเกือบ 1 วินาทีแล้วตามด้วยอาการกรอเร็ว

อาการ: ค้าง, กรอเร็ว · ผู้รับผิดชอบหลัก พัฒนาเซิร์ฟเวอร์ (ทีมพัฒนาเกม) · ร่วมกับ อินฟราเซิร์ฟเวอร์ (ทีมอินฟรา), พัฒนาไคลเอนต์ (ทีมพัฒนาเกม)

อุปกรณ์กลางทางตัด TCP option ทิ้ง Middlebox strips TCP options

ถ้าไฟร์วอลล์หรืออุปกรณ์เร่งความเร็วบางตัวลบหรือแก้ TCP option เมื่อแพ็กเก็ตหายหลายตัว จะกู้คืนได้แค่ตัวเดียวต่อหนึ่งรอบไปกลับ หรือ window (ปริมาณที่ส่งได้ในครั้งเดียว) จะเล็กลงจนช้า

ทำไม: “TCP normalization” ของไฟร์วอลล์ หรืออุปกรณ์เร่งความเร็วรุ่นเก่าลบ option SACK, timestamps และ window scaling → ผลคือ: ถ้าแพ็กเก็ตหายหลายตัว จะกู้คืนได้ทีละตัวต่อรอบไปกลับ, window ถูกจำกัดไว้ที่ 64 KB → บนหน้าจอ: ทุกครั้งที่หายจะค้างนานขึ้นมาก (ไม่มี SACK ก็ใช้ RACK-TLP ไม่ได้) แล้วตามด้วยอาการกรอเร็ว การส่งข้อมูลปริมาณมากอย่างแพตช์ก็ช้า

อาการ: ค้าง, กรอเร็ว · ผู้รับผิดชอบหลัก อินฟราเครือข่าย (ทีมอินฟรา) · ร่วมกับ อินฟราเซิร์ฟเวอร์ (ทีมอินฟรา)

zero window (การหยุดที่ดูเหมือนการส่งซ้ำ) Zero window, often mistaken for retransmission

ถ้าโปรแกรมฝั่งรับอ่านซ็อกเก็ตไม่ทันจนบัฟเฟอร์เต็ม ฝั่งส่งจะหยุดส่งและส่งแค่ zero window probe ปัญหานี้ไม่ได้อยู่ที่เน็ต

ทำไม: เฟรมของไคลเอนต์หยุดชะงักหรือเธรดของเซิร์ฟเวอร์ติดขัด จึงอ่านซ็อกเก็ตไม่ได้ → ผลคือ: receive window เป็น 0 ฝั่งส่งจึงหยุดส่งและส่งแค่ probe (ช่วงห่างค่อย ๆ ยาวขึ้น) → บนหน้าจอ: ค้างแล้วตามด้วยอาการกรอเร็ว ใน packet capture เห็น “ZeroWindow” และไม่มีแพ็กเก็ตหาย

อาการ: ค้าง, กรอเร็ว · ผู้รับผิดชอบหลัก พัฒนาไคลเอนต์ (ทีมพัฒนาเกม) · ร่วมกับ พัฒนาเซิร์ฟเวอร์ (ทีมพัฒนาเกม), อินฟราเซิร์ฟเวอร์ (ทีมอินฟรา)

การส่งซ้ำคำขอเชื่อมต่อ (SYN) SYN retransmission on connect

ถ้าคำขอเชื่อมต่อหายไปเพราะคิวรอเชื่อมต่อ (backlog) ล้นหรือถูกไฟร์วอลล์บล็อก OS ฝั่งไคลเอนต์จะส่งใหม่ โดยเริ่มหลัง 1 วินาทีและเว้นช่วงห่างขึ้นเรื่อย ๆ

ทำไม: การเชื่อมต่อทะลักทันทีหลังปิดปรับปรุงจนคิวรอเชื่อมต่อของเซิร์ฟเวอร์ล้น หรือไฟร์วอลล์/ระบบป้องกัน DDoS ทิ้ง SYN → ผลคือ: OS ฝั่งไคลเอนต์ส่ง SYN ซ้ำตามช่วงที่กำหนด โดยเริ่มหลัง 1 วินาที (Linux รุ่นเก่า 1 วินาที → 2 วินาที → 4 วินาที) → บนหน้าจอ: หลังกดปุ่มเชื่อมต่อ ช้าไปเป็นวินาทีพอดี ๆ เช่น 1 วินาที, 3 วินาที และถ้าล้มเหลวต่อเนื่องจะเข้าเกมไม่ได้หรือโหลดไม่จบ

อาการ: เข้าเกมไม่ได้/โหลดไม่จบ · ผู้รับผิดชอบหลัก พัฒนาเซิร์ฟเวอร์ (ทีมพัฒนาเกม) · ร่วมกับ อินฟราเซิร์ฟเวอร์ (ทีมอินฟรา), อินฟราเครือข่าย (ทีมอินฟรา), พัฒนาไคลเอนต์ (ทีมพัฒนาเกม)

07แบ่งหน้าที่รับผิดชอบ

หน้าที่รับผิดชอบของทีมพัฒนาเกมและทีมอินฟรา

แม้จะเป็นแลคเหมือนกัน แต่ทีมที่ต้องแก้ต่างกัน โค้ดไคลเอนต์/เซิร์ฟเวอร์และการออกแบบการซิงก์เป็นงานของทีมพัฒนาเกม ส่วนวงจร อุปกรณ์เครือข่าย เครื่องเซิร์ฟเวอร์ และเครื่อง DB เป็นงานของทีมอินฟรา ปัญหาที่ PC และเครือข่ายในบ้านของผู้เล่น ช่วงเครือข่ายของ ISP และฝั่งผู้ให้บริการคลาวด์ ทั้งสองทีมแก้เองโดยตรงไม่ได้ จึงต้องแนะนำผู้เล่น ร้องขอไปยังผู้ให้บริการ หรือหาทางเลี่ยง การ์ดสาเหตุทุกใบระบุผู้รับผิดชอบหลักและทีมที่ต้องร่วมรับมือไว้ และเมื่อกางส่วน “ตัวเลขที่ควรรู้·วิธียืนยัน·สิ่งที่แต่ละทีมต้องทำ” ของการ์ด จะเห็นงานที่แยกไว้ให้แต่ละทีม

  1. แจ้งปัญหา/alertอาการ, เวลาละเอียดถึงวินาที, เซิร์ฟเวอร์/แชนแนล
  2. ใครเจอคนเดียวหรือบ้านเดียว / บาง ISP หรือบางพื้นที่ / บางเซิร์ฟเวอร์หรือบางแชนแนล / ทั้งหมด
  3. เรียกใครก่อนผู้รับผิดชอบหลักของการ์ดสาเหตุที่เข้าข่าย, “เริ่มดูที่” ในตัวช่วยวิเคราะห์อาการ
  4. ข้อมูลที่ต้องส่งต่อIP/ISP, สาเหตุที่หลุด, กราฟที่เกี่ยวข้อง, การเปลี่ยนแปลงล่าสุด
  5. งานที่ทำร่วมกันแบ่งงานตามรายการงานของแต่ละทีมในการ์ด
ลำดับขั้นตั้งแต่รับ ticket เข้ามาจนแบ่งเป็นงานของแต่ละทีม “ใครเจอ” เป็นตัวแบ่งผู้รับผิดชอบที่สำคัญที่สุด เกณฑ์โดยละเอียดอยู่ในตารางด้านล่างและในชี้สาเหตุจากข้อมูลที่วัดได้
ผู้รับผิดชอบขอบเขตที่ดูแลวิธีแก้ที่ใช้บ่อย
ทีมพัฒนาเกมไคลเอนต์โค้ดไคลเอนต์ของเกม: เฟรม, GC, การโหลด, interpolation, extrapolation, prediction และการจัดการเครือข่ายฝั่งไคลเอนต์ (รวมการส่ง heartbeat และการเชื่อมต่อใหม่อัตโนมัติ)แก้โค้ด, ปรับ interpolation buffer และ prediction, เปลี่ยนวิธีโหลด, ช่วงห่าง heartbeat และขั้นตอนการเชื่อมต่อใหม่, แพตช์ไคลเอนต์
ทีมพัฒนาเกมเซิร์ฟเวอร์โค้ดเซิร์ฟเวอร์ของเกม: ทิก, เธรด, ล็อก, การออกแบบการซิงก์, การรับการเชื่อมต่อ (ลูป accept และอาร์กิวเมนต์ของ listen), การตอบ heartbeat และการเก็บกวาดการเชื่อมต่อที่ขาดไปแล้ว, socket option, การออกแบบคิวรีและทรานแซกชันปรับลอจิกให้เบาลง, เรียกแบบ async, กระจายทิกและพื้นที่, คิวล็อกอิน, รับเซสชันต่อด้วย session token, socket option (TCP_NODELAY ฯลฯ), ออกแบบคิวรีและอินเด็กซ์, แพตช์เซิร์ฟเวอร์
ทีมอินฟราเครือข่ายวงจรอินเทอร์เน็ตและอุปกรณ์เครือข่ายใน IDC (สวิตช์, เราเตอร์, ไฟร์วอลล์, โหลดบาลานเซอร์, ระบบป้องกัน DDoS), network ACL, VPC routing และโหลดบาลานเซอร์บนคลาวด์, ISP และ peeringตั้งค่าหรือเปลี่ยนอุปกรณ์, เพิ่มวงจรและ peering, เปลี่ยนเส้นทาง, escalate ไปยัง ISP, ปรับ idle timeout และขีดจำกัดเซสชันของโหลดบาลานเซอร์และไฟร์วอลล์
ทีมอินฟราเครื่องเซิร์ฟเวอร์/OSเครื่องเซิร์ฟเวอร์และอินสแตนซ์บนคลาวด์ (รวม security group และ connection tracking), การตั้งค่า OS และเคอร์เนล, NIC, สภาพแวดล้อมสำหรับ deploy และมอนิเตอร์เพิ่มเครื่องหรือเปลี่ยนอินสแตนซ์, ตั้งค่าเคอร์เนล (sysctl: somaxconn, conntrack ฯลฯ), ตั้งค่า security group และเวลาของ connection tracking, ring buffer ของ NIC และการกระจายอินเทอร์รัปต์, ปรับเวลาของ cron และการสำรองข้อมูล
ทีมอินฟราเครื่อง DBเซิร์ฟเวอร์ DB และสตอเรจ, การตั้งค่า DB, replication, การสำรองข้อมูล, เซิร์ฟเวอร์แคชเพิ่มเครื่อง DB, จัดหา IOPS ของสตอเรจให้พอ, ตั้งค่าพารามิเตอร์ DB และ replication, ปรับการสำรองข้อมูลและ checkpoint
ภายนอกผู้เล่น/ISP/คลาวด์PC และเครือข่ายในบ้านของผู้เล่น, ช่วงเครือข่ายของ ISP (อยู่นอกสัญญาของเรา), ผู้ให้บริการคลาวด์แนะนำผู้เล่น (ต่อสาย LAN ฯลฯ), ร้องขอไปยัง ISP และผู้ให้บริการคลาวด์, หาทางเลี่ยงหรือบรรเทาจากฝั่งเกม

ผู้รับผิดชอบแยกตามชั้นและหัวข้อในภาพเดียว

ตัวเลขตัวหนาคือจำนวนสาเหตุที่ทีมนั้นเป็นผู้รับผิดชอบหลัก ส่วน +ตัวเลขคือจำนวนสาเหตุที่ต้องร่วมรับมือ กดที่ช่องเพื่อดูสาเหตุและงานของทีมนั้นด้านล่าง

เมื่อขอบเขตไม่ชัด: ทีมที่ต้นเหตุอยู่เป็นผู้รับผิดชอบหลัก ทีมอื่นช่วยบรรเทาและตรวจสอบ

ผู้รับผิดชอบหลักคือฝั่งที่ต้นเหตุอยู่ หรือฝั่งที่กำจัดต้นเหตุนั้นได้ แม้ต้นเหตุจะเป็นวงจรหรืออุปกรณ์ ระหว่างนั้นทีมพัฒนาเกมก็ประคองด้วยการออกแบบที่ลดผลกระทบ (interpolation buffer, การส่งอินพุตซ้ำ, การเชื่อมต่อใหม่) และถ้าต้นเหตุอยู่ที่โค้ดเซิร์ฟเวอร์ ต่อให้ทีมอินฟราเพิ่มเครื่องก็แค่ยืดเวลาออกไป ขอบเขตที่มักสับสนกำหนดไว้ดังนี้

  • อยู่เฉย ๆ แล้วหลุด: idle timeout ของเราเตอร์ผู้เล่นและอุปกรณ์ของ ISP เราเปลี่ยนไม่ได้ และ mapping นั้นจะคงอยู่แน่นอนก็ต่อเมื่อมีแพ็กเก็ตส่งออกจากด้านใน ไคลเอนต์จึงต้องส่ง heartbeat และเชื่อมต่อใหม่อัตโนมัติเมื่อหลุด ส่วนเซิร์ฟเวอร์ตอบ heartbeat ถ้าไม่ได้รับก็เก็บกวาดการเชื่อมต่อก่อน แล้วรับเซสชันต่อด้วย session token ทีมอินฟราแจ้งค่าไทม์เอาต์ของอุปกรณ์ฝั่งเรา และเพิ่มค่าเมื่อจำเป็น
  • คิวรอเชื่อมต่อ (backlog) ล้น: ขีดจำกัดจริงอยู่ที่อาร์กิวเมนต์ของ listen และลูป accept ในโค้ดเซิร์ฟเวอร์ พัฒนาเซิร์ฟเวอร์จึงเป็นผู้รับผิดชอบหลัก ส่วนเครื่องเซิร์ฟเวอร์/OS ดูแลเพดานของเคอร์เนล (somaxconn) และ SYN cookie
  • คลาวด์: security group และ connection tracking ของอินสแตนซ์เป็นงานของเครื่องเซิร์ฟเวอร์/OS ส่วน network ACL, VPC routing และโหลดบาลานเซอร์บนคลาวด์เป็นงานของเครือข่าย

“เรียกใครก่อน” ในตาราง คือทีมที่ควรเรียกเป็นทีมแรก นับจากผู้รับผิดชอบหลักของการ์ดสาเหตุที่ตรงกับอาการนั้น ถ้ามีสองทีม ทีมแรกคือทีมที่เป็นผู้รับผิดชอบหลักของการ์ดมากที่สุด ส่วนทีมหลังคือทีมที่ควรเรียกมาร่วมตั้งแต่แรก

สิ่งที่เกิดขึ้นงานฝั่งทีมพัฒนาเกมงานฝั่งทีมอินฟราเมตริกที่ควรดูก่อน
แพ็กเก็ตหายหรือจิตเตอร์สูงใน ISP หรือพื้นที่บางแห่ง
เรียกใครก่อนทีมอินฟราเครือข่าย
interpolation buffer แบบปรับตัวได้, ส่งอินพุตซ้อนกัน, ส่งผ่าน UDP ที่ทนต่อแพ็กเก็ตหาย, ดึง IP พอร์ต และเวลาของคนที่เจอปัญหาจากสถิติแพ็กเก็ตหายและการส่งซ้ำแยกตามการเชื่อมต่อ, ผ่อนเกณฑ์ตรวจการเคลื่อนที่ตามสภาพเน็ตวัดเส้นทางทั้งสองทิศด้วยโปรโตคอลและพอร์ตเดียวกับเกม (mtr), ตัดเส้นทางที่เสียออก, escalate ไปยัง ISP, เพิ่ม peering และวงจรการกระจายของอัตราแพ็กเก็ตหายและจิตเตอร์แยกตาม ISP, อัตราการส่งซ้ำ
การส่งซ้ำของ TCP เพิ่มขึ้น
เรียกใครก่อนทีมอินฟราเครือข่ายทีมพัฒนาเกมเซิร์ฟเวอร์
TCP_NODELAY, กระจายการส่งของแต่ละทิกไปตลอดทิก, อ่านซ็อกเก็ตให้ทันเวลา (กัน zero window), รักษา mapping ด้วย heartbeat, ส่งแพ็กเก็ตเรียลไทม์ผ่าน UDP หรือการเชื่อมต่ออื่น, ไม่ให้ตำแหน่งเก่ากองใน send buffer (TCP_NOTSENT_LOWAT)กำจัดจุดที่แพ็กเก็ตหาย (สาย, โมดูลออปติก, duplex, policer, connection tracking ของไฟร์วอลล์, MTU), ปรับ MSS, ring buffer และการกระจายอินเทอร์รัปต์ของเซิร์ฟเวอร์, การตั้งค่าการกู้คืนของเคอร์เนล (RACK, tcp_mtu_probing)ส่วนที่เพิ่มขึ้นของการส่งซ้ำ (nstat), จำนวนครั้งของ zero window, ตัวนับ drop และ CRC ของ NIC และพอร์ตสวิตช์
CPU เซิร์ฟเวอร์เต็มจนทิกเกินงบ
เรียกใครก่อนทีมพัฒนาเกมเซิร์ฟเวอร์
ปรับการคำนวณระยะมองเห็นและ broadcast ให้เบาลง, แบ่งทิกไปหลายเธรด, แบ่งพื้นที่หรือแชนแนลที่คนแน่น, ตั้งจำนวน worker thread ให้พอดีกับขีดจำกัด CPU, เก็บเวลาประมวลผลต่อทิกเป็นเมตริกCPU หรืออินสแตนซ์ที่ประสิทธิภาพต่อคอร์ (คล็อก) สูง, alert อัตราการใช้ CPU รายคอร์, ตรวจ CPU steal และ CPU throttling ของคอนเทนเนอร์, แยกคอร์ที่จัดการอินเทอร์รัปต์ออกจากคอร์ของเธรดทิกเวลาประมวลผลต่อทิก, อัตราการใช้ CPU รายคอร์, steal, จำนวนครั้งที่ถูก throttle (nr_throttled)
DB ตอบสนองช้า
เรียกใครก่อนทีมพัฒนาเกมเซิร์ฟเวอร์ทีมอินฟราเครื่อง DB
ออกแบบคิวรี อินเด็กซ์ และทรานแซกชัน (ให้สั้น, ล็อกตามลำดับเดียวกัน), เรียกแบบ async นอกเธรดเกม, รวบคิวรีและใช้แคช, ปรับขนาด connection pool และไทม์เอาต์การรอหาคิวรีที่ช้า, query plan และการรอล็อก แล้วแชร์ให้ทีมพัฒนาเกม, ตั้งค่า checkpoint, replication และการอัปเดตสถิติ, IOPS ของสตอเรจ, ตรวจว่าจำนวนเซิร์ฟเวอร์ × ขนาด pool ไม่เกินจำนวนการเชื่อมต่อสูงสุด, เพิ่มเครื่อง DBslow query log, การรอล็อก, การรอ connection, replication lag, IOPS
เข้าเกมไม่ได้ทันทีหลังปิดปรับปรุง
เรียกใครก่อนทีมพัฒนาเกมเซิร์ฟเวอร์
ไม่ให้เธรดที่รับการเชื่อมต่อ (ลูป accept) หยุดเพราะงานอื่น, เพิ่มอาร์กิวเมนต์ backlog ของ listen, ระบบคิวล็อกอิน, รวบคิวรีตอนล็อกอิน (กำจัด N+1), ให้ไคลเอนต์ลองใหม่โดยเพิ่มช่วงห่างขึ้นเรื่อย ๆ และสุ่มกระจายsomaxconn และ SYN cookie ของเคอร์เนล, ขีดจำกัดเซสชันของไฟร์วอลล์และโหลดบาลานเซอร์, ขีดจำกัด conntrack และ file descriptor ของเซิร์ฟเวอร์, อุ่นแคชของ DB, เพิ่มเซิร์ฟเวอร์ล่วงหน้าก่อนอีเวนต์ListenOverflows, อัตราการใช้ตารางเซสชันและ conntrack, จำนวนคิวรีตอนล็อกอิน, การรอ connection
อยู่เฉย ๆ แล้วหลุด
เรียกใครก่อนทีมพัฒนาเกมไคลเอนต์
ไคลเอนต์: ส่ง heartbeat ทุกช่วงที่ไม่เกินครึ่งหนึ่งของ idle timeout ที่สั้นที่สุด (ให้ตัวถัดไปไปถึงก่อนหมดเวลา แม้ตัวหนึ่งจะช้าหรือหาย), เชื่อมต่อใหม่อัตโนมัติเมื่อหลุด เซิร์ฟเวอร์: ตอบ heartbeat, ถ้าไม่ได้รับในเวลาที่กำหนดให้เก็บกวาดการเชื่อมต่อก่อน, รับเซสชันต่อด้วย session tokenรวบรวม idle timeout ของโหลดบาลานเซอร์และไฟร์วอลล์ตลอดเส้นทาง (เครือข่าย) และเวลาของ connection tracking ใน security group บนคลาวด์ (เครื่องเซิร์ฟเวอร์/OS) แล้วแชร์ให้ทีมพัฒนาเกม, อุปกรณ์ของเราเพิ่มค่าได้ถ้าจำเป็น ส่วนไทม์เอาต์ของเราเตอร์ผู้เล่นและ CGNAT ของ ISP เปลี่ยนไม่ได้การกระจายของเวลา idle ก่อนการเชื่อมต่อหลุด (ถ้ากระจุกใกล้ค่าใดค่าหนึ่ง แปลว่าเป็นอุปกรณ์ที่มีไทม์เอาต์ค่านั้น), ประเภทเครือข่าย (มือถือ/มีสาย)
เซิร์ฟเวอร์หยุดชะงักตรงเวลาเดิมทุกครั้ง
เรียกใครก่อนทีมพัฒนาเกมเซิร์ฟเวอร์ทีมอินฟราเครื่องเซิร์ฟเวอร์/OS
สุ่มกระจายเวลาของอีเวนต์ตรงชั่วโมง, การเซฟ, ตัวจับเวลา และการหมดอายุของแคช, แบ่งคิวรี batch เป็นชิ้นเล็ก ๆ แล้วทยอยทำ, ระบุให้ใช้ GC ที่หยุดสั้นเองกระจายเวลาของ cron, การสำรองข้อมูล และการบีบอัด log พร้อมลดลำดับความสำคัญของ I/O, สำรอง DB จาก replica และทำ checkpoint ให้สม่ำเสมอ, ตรวจ burst credit ของดิสก์, จำกัดความเร็วการส่งข้อมูลสำรองเวลาที่หยุดเทียบกับตารางงาน (cron, การสำรองข้อมูล, batch, checkpoint), GC log
DDoS/ทราฟฟิกทะลัก
เรียกใครก่อนทีมอินฟราเครือข่าย
แชร์รูปแบบทราฟฟิกของเกม (พอร์ต, ขนาดแพ็กเก็ต, จำนวนแพ็กเก็ตต่อวินาที) ให้ทีมอินฟรา, จำกัดความถี่ของคำขอต่อบัญชีและตัวละคร, บล็อกแพ็กเก็ตผิดปกติตั้งแต่เนิ่น ๆระบบป้องกัน DDoS (scrubbing) และกฎป้องกันที่ปรับให้เข้ากับทราฟฟิกเกม, ซ่อนที่อยู่ของเซิร์ฟเวอร์, ขีดจำกัดแพ็กเก็ตต่อวินาทีของอุปกรณ์, การจำกัดตาม IP ต้องคำนึงถึง IP ที่ ISP ให้ใช้ร่วมกันและร้านเกมจำนวนแพ็กเก็ตต่อวินาที, CPU และ drop ของอุปกรณ์, อัตราการเชื่อมต่อล้มเหลวแยกตามพื้นที่และ ISP (ตรวจ false positive)
ปัญหา Wi-Fi หรือ PC ของผู้เล่น
เรียกใครก่อนภายนอกผู้เล่น/ISP/คลาวด์ทีมพัฒนาเกมไคลเอนต์
แสดงสถานะเครือข่ายในเกม (ปิง, แพ็กเก็ตหาย), ปรับความยาว interpolation buffer อัตโนมัติตามจิตเตอร์, บันทึกประเภทเครือข่ายและอัตราการใช้ CPU ของ PC ใน log ตอนที่เกิดแลค, ข้อความแนะนำ เช่น ให้ต่อสาย LANแก้เองโดยตรงไม่ได้ ถ้าการแจ้งปัญหากระจุกอยู่ที่ ISP หรือพื้นที่เดียวกัน ให้จัดประเภทใหม่เป็นปัญหาด้านเครือข่ายข้อมูลเน็ตและอุปกรณ์ในการแจ้งปัญหา, สัดส่วนที่มาจาก ISP หรือพื้นที่เดียวกัน

ข้อมูลที่ต้องแนบเมื่อส่งต่องาน

ทีมพัฒนาเกม → ทีมอินฟรา

  • เวลาที่แน่นอน (ละเอียดถึงวินาที พร้อมระบุเขตเวลา) และระยะเวลา, ตอนนี้ยังเกิดอยู่หรือไม่
  • ID ของเซิร์ฟเวอร์และแชนแนล, ขอบเขตผลกระทบ (คนเดียว, บาง ISP, ทั้งเซิร์ฟเวอร์) และจำนวนคนที่ได้รับผลกระทบ (เทียบกับ CCU)
  • ชื่อและลักษณะของอาการ: ถ้าหลุด ให้ระบุเวลา idle ก่อนหลุด ถ้าค้าง ให้ระบุความยาวและรอบที่เกิดซ้ำ
  • IP, พอร์ต, ISP และพื้นที่ของคนที่เจอ (ถ้ามีเส้นทางหนึ่งในหลายเส้นที่เสีย ต้องมีข้อมูลถึงระดับพอร์ตจึงจะแยกได้), โปรโตคอลที่เกมใช้ (TCP/UDP) และพอร์ตของเซิร์ฟเวอร์
  • เมตริกฝั่งเกม: เวลาประมวลผลต่อทิก, การกระจายของปิงและแพ็กเก็ตหาย, จำนวนการเชื่อมต่อที่การส่งซ้ำเพิ่มขึ้น, สาเหตุที่หลุด (heartbeat หมดเวลา, การเชื่อมต่อถูกปฏิเสธ (RST) ฯลฯ)
  • ช่วงห่าง heartbeat ที่ใช้อยู่, เวลาที่เซิร์ฟเวอร์ใช้ตัดสินว่าไม่ตอบสนอง, วิธีลองใหม่
  • มีการ deploy หรือเปลี่ยนคอนฟิกล่าสุดหรือไม่, สาเหตุที่ตรวจแล้วและตัดออกไปแล้ว

ทีมอินฟรา → ทีมพัฒนาเกม

  • เมตริกของอุปกรณ์และวงจรในเวลาเดียวกัน (อัตราการใช้งาน, ตัวนับ drop และ error, จำนวนเซสชัน) และเมตริกของ OS เซิร์ฟเวอร์ (ListenOverflows, อัตราการใช้ conntrack, CPU steal)
  • ค่าไทม์เอาต์และขีดจำกัดของอุปกรณ์ตลอดเส้นทาง: idle timeout ของโหลดบาลานเซอร์และไฟร์วอลล์, เวลาของ connection tracking ใน security group, ขีดจำกัดจำนวนเซสชันและแพ็กเก็ตต่อวินาที
  • ประวัติการเปลี่ยนแปลงอุปกรณ์และวงจร และงานที่กำหนดไว้ (การเปลี่ยนอุปกรณ์, การเปลี่ยนคอนฟิก, การสำรองข้อมูลและ cron, ประกาศงานของ ISP)
  • หมายเลขเรื่องที่แจ้งไปยัง ISP หรือผู้ให้บริการคลาวด์ และเวลาที่คาดว่าจะได้คำตอบ
  • มาตรการชั่วคราว (ทางเลี่ยง, ผ่อนขีดจำกัด) และเวลาที่จะย้อนกลับ
  • ช่วงที่เป็นต้นเหตุและข้อสรุป, แผนป้องกันไม่ให้เกิดซ้ำ
  • สิ่งที่ฝั่งเกมต้องทำ (ช่วงห่าง heartbeat, วิธีลองใหม่, การจำกัดจำนวนการเชื่อมต่อ ฯลฯ)

ทั้งสองทีม: กำหนดผู้รับผิดชอบการรับมือเหตุขัดข้องหนึ่งคน บันทึกเหตุการณ์ตามลำดับเวลาไว้ในห้องแชตเดียว และแจ้งเวลาอัปเดตครั้งถัดไปล่วงหน้า ถ้าผู้รับผิดชอบหลักเปลี่ยนเป็นทีมอื่น ให้ส่งบันทึกนี้ต่อไปด้วยเพื่อไม่ต้องตรวจเรื่องเดิมซ้ำ เมื่อจบแล้ว ใช้บันทึกเดียวกันนี้แก้ผู้รับผิดชอบและงานในการ์ดสาเหตุที่เกี่ยวข้อง

โปรเซสเกมฝั่งไคลเอนต์

ตัวโปรแกรมเกมที่รันอยู่บน PC หรือมือถือของผู้เล่น ต่อให้เครือข่ายสมบูรณ์แบบ ถ้าเฟรมช้าที่ชั้นนี้ ภาพก็จะกระตุก และชั้นนี้ยังเป็นตัวตัดสินว่าจะกลบปัญหาได้ดีแค่ไหนเมื่อเครือข่ายแย่

เกมทำงานเดิมซ้ำราว 60 ครั้งใน 1 วินาที คืออ่านอินพุต, ประมวลผลแพ็กเก็ตที่ได้รับ, อัปเดตสถานะเกมไปหนึ่งขั้น และวาดภาพ ลูปหนึ่งรอบนี้คือเฟรม ถ้าเป็น 60 FPS จะมีเวลา 16.7 ms ต่อหนึ่งเฟรม (เกมมือถือที่รัน 30 FPS คือ 33.3 ms) ถ้าเฟรมหนึ่งช้าไป ภาพจะหยุดไปเท่านั้น แล้วเฟรมถัดไปจะขยับรวดเดียวเท่ากับส่วนที่ช้าไป

ในด้านเครือข่าย งานของไคลเอนต์คือ “เติมข้อมูลที่ขาด” ตำแหน่งของผู้เล่นอื่นมาจากเซิร์ฟเวอร์เป็นระยะ ๆ จึงต้องวาดเชื่อมช่วงระหว่างนั้น (interpolation) ถ้าแพ็กเก็ตขาดไปก็ต้องเดาแล้วขยับต่อ (extrapolation) และตัวละครของเราจะขยับให้เห็นก่อนโดยไม่รอเซิร์ฟเวอร์ยืนยัน (prediction) รูปแบบที่เทคนิคเหล่านี้ล้มเหลวก็คืออาการวาร์ป ดีดกลับ และกระตุกนั่นเอง

เปรียบเทียบ

ไคลเอนต์เกมคือห้องควบคุมการออกอากาศที่ทำภาพถ่ายทอดสด เมื่อภาพนิ่งจากหน้างาน (เซิร์ฟเวอร์) ส่งมาเป็นระยะ ๆ ก็ต่อช่วงระหว่างนั้นให้ดูลื่นเหมือนวิดีโอ ถ้าภาพมาช้า ไม่มีภาพให้ต่อ จอก็หยุด และถ้าห้องตัดต่อเองยุ่งเกินไป การออกอากาศก็สะดุด

สาเหตุของแลคในชั้นนี้

เฟรมไทม์พุ่ง Frame hitch

เฟรมหนึ่งใช้เวลาคำนวณนานกว่าปกติหลายเท่า ภาพบนจอจึงหยุดไปครู่หนึ่ง

ทำไม: เอฟเฟกต์สกิลเยอะผิดปกติ, spawn จำนวนมาก และการอัปเดต UI ทั้งหน้าจอ มารวมอยู่ในเฟรมเดียว → ผลคือ: ทำไม่เสร็จภายใน 16.7 ms และกินเวลา 50–300 ms → บนหน้าจอ: ภาพหยุดแวบ แล้วทุกตัวขยับไปพร้อมกันในเฟรมถัดไป

อาการ: กระตุก, ค้าง · ผู้รับผิดชอบหลัก พัฒนาไคลเอนต์ (ทีมพัฒนาเกม)

GC ฝั่งไคลเอนต์ Client GC (Unity C#, Unreal, Lua)

ทั้งเกมหยุดระหว่างเก็บคืนหน่วยความจำที่ใช้แล้วทิ้ง (garbage) จุดสังเกตคือกระตุกเป็นจังหวะสม่ำเสมอ

ทำไม: สร้าง string, array และ list ชั่วคราวแล้วทิ้งทุกเฟรม → ผลคือ: เมื่อ garbage สะสมมากพอ GC จะหยุดเมนเธรดเพื่อเก็บคืน → บนหน้าจอ: กระตุกเป็นจังหวะทุกไม่กี่วินาทีถึงหลายสิบวินาที

อาการ: กระตุก, ค้าง · ผู้รับผิดชอบหลัก พัฒนาไคลเอนต์ (ทีมพัฒนาเกม)

โหลดแบบ synchronous และคอมไพล์ shader บนเมนเธรด Synchronous asset load, shader compile

เกมหยุดเพราะต้องอ่านไฟล์และสร้าง shader ก่อนวาดพื้นที่, มอนสเตอร์ หรือเอฟเฟกต์ที่เพิ่งเห็นเป็นครั้งแรก

ทำไม: เข้าพื้นที่ใหม่ หรือเจอสกิล, อุปกรณ์สวมใส่ และมอนสเตอร์ที่ไม่เคยเห็น → ผลคือ: เมนเธรดรอการอ่านไฟล์และการคอมไพล์ shader → บนหน้าจอ: ค้าง 0.1–1 วินาทีแค่ครั้งแรก ตั้งแต่ครั้งที่สองก็ปกติ

อาการ: ค้าง, กระตุก · ผู้รับผิดชอบหลัก พัฒนาไคลเอนต์ (ทีมพัฒนาเกม)

สตอเรจช้าจนสตรีมแอสเซ็ตไม่ทัน Slow storage stalls asset streaming

บนสตอเรจช้าอย่าง HDD การอ่าน texture และโมเดลของโอเพนเวิลด์ตามการเคลื่อนที่ไม่ทัน เอนทิตีจึงโผล่ช้า หรือเกมกระตุกระหว่างรออ่านข้อมูล

ทำไม: เคลื่อนที่เร็วด้วยพาหนะหรือเทเลพอร์ต หรือเข้าไปในที่ที่คนเยอะ จนต้องใช้ texture และโมเดลใหม่จำนวนมากพร้อมกัน → ผลคือ: สตอเรจช้าอย่าง HDD อ่านได้ไม่ทันความเร็วที่ต้องการ คำขออ่านจึงกองรอ และการโหลดบางส่วนทำให้เมนเธรดต้องรอจนเสร็จ → บนหน้าจอ: texture เบลออยู่พักหนึ่ง อาคารและตัวละครโผล่ช้า และกระตุกหรือค้างตอนที่รออ่าน

อาการ: มองไม่เห็น/ตัวผี, กระตุก, ค้าง · ผู้รับผิดชอบหลัก พัฒนาไคลเอนต์ (ทีมพัฒนาเกม) · ร่วมกับ ภายนอก (ภายนอก)

ภาระการเรนเดอร์ตัวละครจำนวนมาก Render/animation cost of crowds

เมื่อคนหลายร้อยคนเข้ามาอยู่ในจอเดียว เช่น ศึกชิงปราสาทหรือเวิลด์บอส ลำพังต้นทุนการวาดภาพก็รับไม่ไหวแล้ว

ทำไม: คนหลายร้อยคนและเอฟเฟกต์ซ้อนกันอยู่ในจอเดียว → ผลคือ: ต้นทุนของแอนิเมชัน, เงา, ป้ายชื่อ และเอฟเฟกต์ เพิ่มขึ้นตามจำนวนคน → บนหน้าจอ: FPS ตกจาก 60 → 15 ทุกการเคลื่อนไหวกระตุก และอินพุตก็ช้าตาม

อาการ: กระตุก, อินพุตดีเลย์ · ผู้รับผิดชอบหลัก พัฒนาไคลเอนต์ (ทีมพัฒนาเกม)

คอขวดการประมวลผลแพ็กเก็ตบนเมนเธรด Network processing on the main thread

ถ้าประมวลผลแพ็กเก็ตที่รับมาได้แค่จำนวนที่กำหนดในแต่ละเฟรม แพ็กเก็ตที่ทะลักเข้ามาจะถูกเลื่อนไปเฟรมถัดไปเรื่อย ๆ

ทำไม: ในที่ที่คนเยอะ มีอัปเดตเข้ามาหลายพันรายการต่อวินาที → ผลคือ: เมนเธรดชนเพดานปริมาณที่ประมวลผลได้ต่อเฟรม จึงอ่านได้ไม่หมด → บนหน้าจอ: การเคลื่อนไหวของคนอื่นแสดงช้าลงเรื่อย ๆ แล้วมาเป็นชุดรวดเดียว

อาการ: กรอเร็ว, อินพุตดีเลย์ · ผู้รับผิดชอบหลัก พัฒนาไคลเอนต์ (ทีมพัฒนาเกม) · ร่วมกับ พัฒนาเซิร์ฟเวอร์ (ทีมพัฒนาเกม)

ไม่มี interpolation buffer หรือสั้นเกินไป Missing/short interpolation buffer

ถ้าวาดทันทีที่ได้รับแพ็กเก็ตจากเซิร์ฟเวอร์ จิตเตอร์ (ช่วงเวลาที่แพ็กเก็ตมาถึงไม่สม่ำเสมอ) จะแสดงออกมาบนจอตรง ๆ

ทำไม: วาดตำแหน่งที่ได้รับทันที หรือบัฟเฟอร์สั้นกว่าจิตเตอร์ → ผลคือ: หยุดนานเท่าที่แพ็กเก็ตมาช้า และกระโดดไปเท่าที่แพ็กเก็ตมาพร้อมกันหลายอัน → บนหน้าจอ: ตัวละครอื่นขยับแบบหยุดแวบเป็นระยะ

อาการ: กระตุก · ผู้รับผิดชอบหลัก พัฒนาไคลเอนต์ (ทีมพัฒนาเกม) · ร่วมกับ พัฒนาเซิร์ฟเวอร์ (ทีมพัฒนาเกม)

extrapolation มากเกินไป (dead reckoning) Over-extrapolation / dead reckoning

ระหว่างที่แพ็กเก็ตไม่มา เกมแสดงตัวละครให้เคลื่อนที่ต่อด้วยความเร็วล่าสุด แล้วดึงกลับเมื่อรู้ว่าผิด

ทำไม: แพ็กเก็ตขาดหาย เกมจึงให้เคลื่อนที่ต่อไปตามทิศทางและความเร็วล่าสุด → ผลคือ: ความจริงอีกฝ่ายหยุดหรือเปลี่ยนทิศไปแล้ว → บนหน้าจอ: ตัวละครอีกฝ่ายเดินไปไกลแล้ววืดไปอยู่ตำแหน่งจริงทันที หรือเดินทะลุกำแพง ถ้าช่วงห่างการมาถึงของแพ็กเก็ตไม่สม่ำเสมอ จะวิ่งนำไปแล้วถูกดึงกลับซ้ำ ๆ จนดูเหมือนสั่น

อาการ: วาร์ป, กระตุก · ผู้รับผิดชอบหลัก พัฒนาไคลเอนต์ (ทีมพัฒนาเกม)

client-side prediction ไม่ตรงกับเซิร์ฟเวอร์ Prediction mismatch / reconciliation

ไคลเอนต์ของเราแสดงการเคลื่อนที่ไปก่อน แต่ถ้าเซิร์ฟเวอร์คำนวณออกมาต่างกัน ตัวละครของเราจะโดนดึงกลับ

ทำไม: ไคลเอนต์ขยับไปก่อนที่เซิร์ฟเวอร์จะยืนยัน (prediction) → ผลคือ: เซิร์ฟเวอร์คำนวณการชน, ความเร็วการเคลื่อนที่ หรือบัฟต่างออกไป หรือไม่ได้รับคำสั่ง → บนหน้าจอ: พอผลยืนยันมาถึง ตัวละครของเราโดนดึงถอยหลัง

อาการ: ดีดกลับ · ผู้รับผิดชอบหลัก พัฒนาไคลเอนต์ (ทีมพัฒนาเกม) · ร่วมกับ พัฒนาเซิร์ฟเวอร์ (ทีมพัฒนาเกม)

fixed timestep ไล่ตามไม่ทันจนบานปลาย Fixed-timestep catch-up / spiral of death

หลังหยุดไปครั้งหนึ่ง เกมเร่งคำนวณส่วนที่ตามหลังรวดเดียว แล้วการคำนวณนั้นเองก็ทำให้ตามหลังอีก

ทำไม: เกมรันการจำลองด้วยช่วงเวลาคงที่ แล้วเกิดหยุดไปครั้งหนึ่ง → ผลคือ: คำนวณ step ที่ตามหลังทั้งหมดรวดเดียวในเฟรมเดียว → บนหน้าจอ: เฟรมยาวเกิดต่อกันเป็นพรวนจนภาพกระตุก หรือชนเพดานแล้วโลกในเกมช้าลง

อาการ: กระตุก, กรอเร็ว, สโลว์โมชั่น · ผู้รับผิดชอบหลัก พัฒนาไคลเอนต์ (ทีมพัฒนาเกม)

ซิงก์นาฬิกาคลาดเคลื่อน Clock sync error

ถ้าเวลาเซิร์ฟเวอร์ที่ไคลเอนต์ประมาณไว้ผิด จังหวะ interpolation และการตัดสินคูลดาวน์จะคลาดกัน

ทำไม: ตั้งเวลาให้ตรงกับเซิร์ฟเวอร์แค่ครั้งเดียวตอนเชื่อมต่อ แม้ปิงเปลี่ยนก็ไม่ปรับ → ผลคือ: จังหวะที่ใช้ interpolate และเวลาที่คูลดาวน์หมดคลาดจากเซิร์ฟเวอร์ → บนหน้าจอ: อีกฝ่ายหยุดแวบเป็นบางครั้ง คูลดาวน์หมดแล้วแต่สกิลถูกปฏิเสธ

อาการ: กระตุก, กดไม่ติด/โรลแบ็ค · ผู้รับผิดชอบหลัก พัฒนาไคลเอนต์ (ทีมพัฒนาเกม)

เวลาแบบ float สูญเสียความแม่นยำ Float time precision loss on long sessions

ถ้าเก็บเวลาในเกมเป็นทศนิยมความแม่นยำต่ำ (float) ยิ่งเปิดเกมไว้นาน ความละเอียดของเวลา (ผลต่างของเวลาที่เล็กที่สุดที่แยกได้) ก็ยิ่งลดลง การเคลื่อนไหวและเอฟเฟกต์จึงสั่น

ทำไม: สะสมเวลาที่ผ่านไปตั้งแต่เปิดเกมเป็น float หรือส่งค่านั้นให้ shader ตรง ๆ → ผลคือ: ยิ่งเปิดไว้นาน ผลต่างที่เล็กที่สุดที่ float แสดงได้ก็ยิ่งใหญ่ขึ้น → บนหน้าจอ: เฉพาะไคลเอนต์ที่เปิดทิ้งไว้หลายวัน ตัวละคร, แอนิเมชัน และเอฟเฟกต์ที่เคลื่อนไหลสั่นระริก แต่เปิดเกมใหม่แล้วปกติ

อาการ: กระตุก · ผู้รับผิดชอบหลัก พัฒนาไคลเอนต์ (ทีมพัฒนาเกม)

V-Sync และคิวเรนเดอร์ V-Sync, render queue

ระหว่างที่ GPU เก็บเฟรมที่วาดเสร็จไว้ในคิวหลายเฟรม แล้วค่อยส่งออกตามรอบของจอ อินพุตจะช้าลง

ทำไม: ไดรเวอร์กราฟิกเก็บเฟรมไว้ล่วงหน้าในคิว 1–3 เฟรม → ผลคือ: อินพุตต้องใช้เวลานานขึ้นเท่านั้นกว่าจะแสดงบนจอ → บนหน้าจอ: ปิงต่ำแต่การควบคุมหนืดและตอบสนองช้า

อาการ: อินพุตดีเลย์, กระตุก · ผู้รับผิดชอบหลัก พัฒนาไคลเอนต์ (ทีมพัฒนาเกม) · ร่วมกับ ภายนอก (ภายนอก)

หน่วยความจำรั่วฝั่งไคลเอนต์ Client memory leak

ยิ่งเปิดไว้นาน หน่วยความจำยิ่งเพิ่ม เกมช้าลงเรื่อย ๆ แล้วสุดท้ายก็ถูกบังคับปิด

ทำไม: texture, UI และเอฟเฟกต์ไม่ถูกปล่อยคืนเมื่อย้ายแมพไปมา → ผลคือ: GC ทำงานถี่ขึ้น และหน่วยความจำของ OS ไม่พอจนเกิด swap → บนหน้าจอ: เล่นไปหลายชั่วโมงแล้วกระตุกขึ้นเรื่อย ๆ จนถูกบังคับปิด (ผู้เล่นเห็นเหมือนหลุด)

อาการ: กระตุก, หลุด · ผู้รับผิดชอบหลัก พัฒนาไคลเอนต์ (ทีมพัฒนาเกม)

ไคลเอนต์แครช Client crash

เกมปิดตัวลงเพราะข้อผิดพลาดที่ไม่ได้จัดการ ผู้เล่นเห็นเหมือนหลุด แต่เซิร์ฟเวอร์ปกติ

ทำไม: null reference, หน่วยความจำไม่พอ, ไดรเวอร์กราฟิกผิดพลาด → ผลคือ: โปรเซสเกมถูกบังคับปิด → บนหน้าจอ: มีคนแจ้งว่า “เกมเด้ง” ขณะที่คนอื่นในเวลาเดียวกันปกติ

อาการ: หลุด · ผู้รับผิดชอบหลัก พัฒนาไคลเอนต์ (ทีมพัฒนาเกม) · ร่วมกับ ภายนอก (ภายนอก)

การตรวจของโมดูลความปลอดภัยเกม (anti-cheat) Anti-cheat scan and heartbeat

โมดูลความปลอดภัยที่ทำงานคู่กับเกมเพื่อกันโปรโกงจะตรวจสอบเป็นระยะ ถ้าการตรวจหนัก หรือ heartbeat (สัญญาณยืนยันว่ายังทำงานอยู่ที่ส่งเป็นระยะ) ที่รับส่งกับเซิร์ฟเวอร์ความปลอดภัยมาช้า เกมจะกระตุกหรือหลุด

ทำไม: โมดูลความปลอดภัยตรวจหน่วยความจำของเกม, โปรแกรมที่กำลังรัน และไดรเวอร์เป็นระยะ → ผลคือ: ระหว่างตรวจ เธรดเกมหยุด หรือ heartbeat ส่งไปไม่ทันเวลา → บนหน้าจอ: หยุดแวบเป็นจังหวะสม่ำเสมอ ถ้าหนักจะหลุดพร้อมข้อความแจ้งข้อผิดพลาดด้านความปลอดภัย

อาการ: กระตุก, ค้าง, หลุด · ผู้รับผิดชอบหลัก พัฒนาไคลเอนต์ (ทีมพัฒนาเกม) · ร่วมกับ พัฒนาเซิร์ฟเวอร์ (ทีมพัฒนาเกม)

OS และอุปกรณ์ฝั่งไคลเอนต์

เกมรันบน Windows, Android หรือ iOS และแบ่ง CPU หน่วยความจำ และเครือข่ายกับโปรแกรมอื่น ถ้า OS ให้ CPU กับเกมช้า ลดความเร็วลงเพื่อประหยัดแบตเตอรี่ หรือพักการทำงาน (suspend) ของแอปเบื้องหลัง ก็จะเกิดแลค

scheduler (ฟีเจอร์ที่กำหนดว่าใครได้ใช้ CPU เมื่อไร) ของระบบปฏิบัติการ (OS) แบ่งเวลา CPU ให้หลายโปรแกรม เกม แอนตี้ไวรัส เบราว์เซอร์ และโปรแกรมอัปเดตต่างรอ “คิวของตัวเอง” และ OS จะสลับให้ใช้คอร์ครั้งละไม่กี่ ms ถึงหลายสิบ ms (time slice) OS ให้ลำดับความสำคัญกับเกมที่เปิดอยู่ด้านหน้า (foreground) มากขึ้นเล็กน้อย แต่ถ้างานมีมากกว่าคอร์ เกมก็ต้องรอ และการรอนั้นทำให้เฟรมช้าลง

เครือข่ายก็ต้องผ่าน OS เช่นกัน แพ็กเก็ตที่การ์ด LAN หรือชิป Wi-Fi รับมาจะอยู่ใน receive buffer ของไดรเวอร์และ OS รอจนกว่าเกมจะดึงไป ถ้าเกมยุ่งจนดึงช้า บัฟเฟอร์จะล้น และถ้าดึงทีเดียวหลายตัวก็จะเป็นอาการกรอเร็ว บนมือถือ สิ่งที่สำคัญเป็นพิเศษคือ OS จะสลับการเชื่อมต่อไร้สายเข้าโหมดประหยัดพลังงานเพื่อถนอมแบตเตอรี่ และพักการทำงานของแอปอยู่เป็นระยะ

เปรียบเทียบ

OS คือหัวหน้าเชฟที่จัดให้เชฟหลายคนใช้ครัวที่มีอยู่ครัวเดียวร่วมกัน แม้เกมจะกำลังทำอาหารด่วน ถ้าเชฟที่ชื่อสแกนไวรัสยึดเตาไป เกมก็ต้องรอ และถ้าครัวร้อนเกินไป (ความร้อน) ก็จะหรี่ไฟลงด้วย

สาเหตุของแลคในชั้นนี้

โปรเซสเบื้องหลังแย่ง CPU Background CPU contention

เมื่อการสแกนของแอนตี้ไวรัส, Windows Update, โปรแกรมสตรีม หรือวิดีโอในเบราว์เซอร์ยึดคอร์ไว้ เธรดเกมจะไม่ได้รับ CPU และต้องรอ

ทำไม: โปรแกรมอื่นยึดคอร์ CPU ไว้นาน → ผลคือ: เธรดเกมต้องรอคิว CPU → บนหน้าจอ: เฟรมช้า และการประมวลผลแพ็กเก็ตที่รับมาก็ช้าตาม

อาการ: กระตุก, กรอเร็ว · ผู้รับผิดชอบหลัก ภายนอก (ภายนอก) · ร่วมกับ พัฒนาไคลเอนต์ (ทีมพัฒนาเกม)

โหมดประหยัดพลังงานและ thermal throttling Power saving, thermal throttling

โหมดแบตเตอรี่ของโน้ตบุ๊ก, โหมดประหยัดพลังงานของมือถือ และความร้อนของเครื่อง ทำให้ CPU และ GPU ช้าลง จุดสังเกตของความร้อนคือช่วงแรกปกติดี แล้วค่อยช้าลงหลังเล่นไปสักพัก

ทำไม: อยู่ในโหมดแบตเตอรี่หรือประหยัดพลังงาน หรือเครื่องร้อน → ผลคือ: ลดคล็อกของ CPU และ GPU ลง 30–50% แล้วแต่เครื่อง → บนหน้าจอ: โหมดประหยัดพลังงานเป็นตั้งแต่เปิดเกม ส่วนความร้อนจะเริ่มหลังเล่นไปไม่กี่นาทีถึงราว 20 นาที FPS ตกและกระตุก

อาการ: กระตุก, อินพุตดีเลย์ · ผู้รับผิดชอบหลัก ภายนอก (ภายนอก) · ร่วมกับ พัฒนาไคลเอนต์ (ทีมพัฒนาเกม)

ความละเอียดของตัวจับเวลา Timer resolution (Windows 15.6ms)

ตัวจับเวลาเริ่มต้นของ Windows ทำงานเป็นหน่วย 15.6 ms การ “พักแค่ 1 ms” จึงกลายเป็นการรอจนถึงรอบตัวจับเวลาถัดไป นานสุดถึง 15.6 ms

ทำไม: เขียนการจำกัดเฟรมและการส่งแพ็กเก็ตด้วยวิธี Sleep (รอสักครู่) → ผลคือ: OS ปลุกให้ได้เป็นหน่วย 15.6 ms เท่านั้น → บนหน้าจอ: ช่วงห่างระหว่างเฟรมและช่วงห่างการส่งอินพุตไม่สม่ำเสมอ

อาการ: กระตุก · ผู้รับผิดชอบหลัก พัฒนาไคลเอนต์ (ทีมพัฒนาเกม)

สลับแอปมือถือไปเบื้องหลัง App suspended in background

ถ้าย่อแอปลงไปแป๊บเดียวเพื่อดูแจ้งเตือน OS จะพักการทำงาน (suspend) ของแอปภายในไม่กี่วินาที และระหว่างนั้นเซิร์ฟเวอร์จะตัดการเชื่อมต่อของเรา

ทำไม: ย่อเกมลงเพื่อเช็กข้อความหรือรับสาย → ผลคือ: เอนจินเกมหยุดการทำงานของเกม และอีกไม่นาน OS ก็หยุดแอปและเครือข่ายด้วย → บนหน้าจอ: กลับมาก็หลุดไปแล้ว ต้องเชื่อมต่อใหม่

อาการ: หลุด · ผู้รับผิดชอบหลัก พัฒนาไคลเอนต์ (ทีมพัฒนาเกม) · ร่วมกับ พัฒนาเซิร์ฟเวอร์ (ทีมพัฒนาเกม)

สลับ Wi-Fi ↔ LTE/5G Network switch changes IP

เมื่อเดินออกจากบ้านแล้ว Wi-Fi หลุดและเปลี่ยนไปใช้ LTE หรือ 5G IP address ของเราจะเปลี่ยน การเชื่อมต่อเดิมจึงใช้ไม่ได้

ทำไม: สัญญาณ Wi-Fi อ่อนลงจนสลับไปใช้เครือข่ายมือถือ → ผลคือ: IP address ของเราเปลี่ยน การเชื่อมต่อที่สร้างด้วยที่อยู่เดิมจึงรับส่งต่อไม่ได้ → บนหน้าจอ: ค้างไปครู่หนึ่ง แล้วหลุดหรือเชื่อมต่อใหม่

อาการ: ค้าง, หลุด · ผู้รับผิดชอบหลัก พัฒนาเซิร์ฟเวอร์ (ทีมพัฒนาเกม) · ร่วมกับ พัฒนาไคลเอนต์ (ทีมพัฒนาเกม), อินฟราเครือข่าย (ทีมอินฟรา)

โปรแกรมความปลอดภัยตรวจแพ็กเก็ต Antivirus / firewall inspection

ถ้าแอนตี้ไวรัสหรือไฟร์วอลล์ตรวจทุกแพ็กเก็ต ความหน่วงจะเพิ่มขึ้น และถ้าตรวจเข้มเกินไปก็อาจเข้าใจผิดว่าเกมเป็นการโจมตีแล้วบล็อก

ทำไม: โปรแกรมความปลอดภัยตรวจแพ็กเก็ตขาเข้าและขาออกทีละแพ็กเก็ต → ผลคือ: ทุกแพ็กเก็ตมีดีเลย์เพิ่ม และถ้าตรวจไม่ทันก็ถูกทิ้ง → บนหน้าจอ: ปิงพุ่งแบบไม่สม่ำเสมอ หรือการเชื่อมต่อถูกบล็อก

อาการ: กระตุก, เข้าเกมไม่ได้/โหลดไม่จบ · ผู้รับผิดชอบหลัก ภายนอก (ภายนอก) · ร่วมกับ พัฒนาไคลเอนต์ (ทีมพัฒนาเกม)

receive buffer ล้น Socket receive buffer overflow

ถ้าเกมยุ่งจนดึงแพ็กเก็ตออกจากซ็อกเก็ต (อินเทอร์เฟซรับส่งข้อมูลเครือข่ายที่ OS จัดให้) ช้า บัฟเฟอร์ของ OS จะล้น

ทำไม: เฟรมช้าจนเกมอ่านซ็อกเก็ตไม่ทัน → ผลคือ: receive buffer ของ OS เต็ม UDP จะถูกทิ้ง ส่วน TCP จะลด receive window ให้ฝั่งส่งหยุดส่ง → บนหน้าจอ: วาร์ป (UDP) หรือกรอเร็ว (TCP)

อาการ: วาร์ป, กรอเร็ว · ผู้รับผิดชอบหลัก พัฒนาไคลเอนต์ (ทีมพัฒนาเกม)

หน่วยความจำฝั่งไคลเอนต์ไม่พอและ swap Paging / swap on client

ถ้าเปิดแท็บเบราว์เซอร์หลายสิบแท็บพร้อมกับเกม OS จะย้ายหน่วยความจำบางส่วนของเกมไปไว้บนดิสก์

ทำไม: RAM ทั้งเครื่องไม่พอ → ผลคือ: OS ย้ายหน่วยความจำของเกมที่ยังไม่ได้ใช้ตอนนี้ไปไว้บนดิสก์ → บนหน้าจอ: จังหวะที่กลับมาใช้ส่วนนั้นอีก เกมค้างหลายสิบถึงหลายร้อย ms ขึ้นกับสตอเรจ

อาการ: ค้าง, กระตุก · ผู้รับผิดชอบหลัก ภายนอก (ภายนอก) · ร่วมกับ พัฒนาไคลเอนต์ (ทีมพัฒนาเกม)

หน่วยความจำกราฟิก (VRAM) ไม่พอ VRAM over-commit

ถ้าหน่วยความจำที่ตัวเลือกกราฟิกต้องการมากกว่าหน่วยความจำของการ์ดจอ OS จะย้าย texture ไปไว้ในหน่วยความจำของ PC แล้วดึงกลับมาใหม่ ภาพจึงกระตุก

ทำไม: ตัวเลือก texture สูง และอุปกรณ์สวมใส่กับเอฟเฟกต์สารพัดในที่ที่คนเยอะ ทำให้หน่วยความจำการ์ดจอเต็ม → ผลคือ: OS ย้าย texture ที่ยังไม่ได้ใช้ตอนนี้ไปไว้ในหน่วยความจำของ PC แล้วดึงกลับผ่านบัส PCIe ที่ช้ากว่าเมื่อต้องใช้ → บนหน้าจอ: หยุดแวบทุกครั้งที่เห็นฉากใหม่หรือตัวละครใหม่ texture เบลออยู่พักหนึ่ง

อาการ: กระตุก, ค้าง · ผู้รับผิดชอบหลัก พัฒนาไคลเอนต์ (ทีมพัฒนาเกม) · ร่วมกับ ภายนอก (ภายนอก)

การสแกน Wi-Fi เบื้องหลัง Periodic Wi-Fi background scan

ระหว่างที่ OS สลับช่องสัญญาณไปมาเป็นระยะเพื่อหา Wi-Fi รอบตัว การรับส่งข้อมูลจะหยุดไปครู่หนึ่ง

ทำไม: OS หรือไดรเวอร์ค้นหา Wi-Fi รอบตัวเป็นรอบ → ผลคือ: ระหว่างค้นหา การรับส่งข้อมูลหยุดไปครู่หนึ่ง → บนหน้าจอ: ปิงพุ่งเป็นจังหวะที่ห่างเท่ากันเป๊ะ (เช่น ทุก 60 วินาที)

อาการ: กระตุก, วาร์ป · ผู้รับผิดชอบหลัก ภายนอก (ภายนอก) · ร่วมกับ พัฒนาไคลเอนต์ (ทีมพัฒนาเกม)

โหมดประหยัดพลังงานของ NIC และปัญหาไดรเวอร์ NIC power saving, driver bugs

ถ้าการ์ด LAN หรือชิป Wi-Fi เข้าสู่โหมดประหยัดพลังงานระหว่างแพ็กเก็ต จะต้องใช้เวลากว่าจะกลับมาทำงาน

ทำไม: เปิดฟีเจอร์ประหยัดพลังงานของอุปกรณ์เครือข่ายไว้ หรือไดรเวอร์เก่า → ผลคือ: ดีเลย์จากการตื่นจากโหมดประหยัดพลังงาน (wake-up) บางครั้งอุปกรณ์รีสตาร์ต → บนหน้าจอ: ดีเลย์ไม่สม่ำเสมอ นาน ๆ ครั้งค้างไปหลายวินาที

อาการ: กระตุก, ค้าง · ผู้รับผิดชอบหลัก ภายนอก (ภายนอก) · ร่วมกับ พัฒนาไคลเอนต์ (ทีมพัฒนาเกม)

แอปอื่นในเครื่องเดียวกันแย่งแบนด์วิดท์ Other apps saturating the link

ถ้าการซิงก์ไฟล์ขึ้นคลาวด์, การดาวน์โหลดไฟล์ใหญ่ หรือแพตช์เกมกำลังทำงานบน PC เครื่องเดียวกัน แพ็กเก็ตเกมจะต้องรอในคิว

ทำไม: แอปอื่นใช้อัปโหลดหรือดาวน์โหลดเต็มที่ → ผลคือ: แพ็กเก็ตเกมกองรอในคิวของ PC และเราเตอร์ → บนหน้าจอ: ปิงพุ่งสูงมาก, อินพุตดีเลย์, กรอเร็ว

อาการ: อินพุตดีเลย์, กรอเร็ว · ผู้รับผิดชอบหลัก ภายนอก (ภายนอก) · ร่วมกับ พัฒนาไคลเอนต์ (ทีมพัฒนาเกม)

จำกัดการประมวลผลเมื่อย่อหรือสลับหน้าต่าง Minimized / unfocused window throttling

เมื่อไปดูหน้าต่างอื่นหรือย่อเกม ตัวเกมและ Windows จะลดความเร็วการทำงานของเกมเพื่อประหยัดไฟ พอกลับมา แพ็กเก็ตที่กองรอจะทะลักเข้ามา หรือหลุดไปแล้ว

ทำไม: กด Alt+Tab ไปดูหน้าต่างอื่น หรือย่อเกม → ผลคือ: ระหว่างที่มองไม่เห็นเกม เกมลด FPS ลงมากหรือหยุด และ Windows ก็ลด priority ของโปรแกรมที่มองไม่เห็นด้วย → บนหน้าจอ: ทันทีที่กลับมาเกิดอาการกรอเร็ว ถ้าย่อไว้นานจะหลุด

อาการ: กรอเร็ว, กระตุก, หลุด · ผู้รับผิดชอบหลัก พัฒนาไคลเอนต์ (ทีมพัฒนาเกม)

โปรแกรมโอเวอร์เลย์รบกวน Overlays and screen hooks

โปรแกรมแชต, launcher, โปรแกรมอัดจอ และโปรแกรมแสดง FPS จะแทรกเข้าไปในขั้นตอนการเรนเดอร์ของเกม (hooking) เพื่อวาด UI ของตัวเองทับบนจอเกม งานในแต่ละเฟรมจึงเพิ่มขึ้น และบางครั้งก็ชนกับเกมจนภาพหยุดแวบหรือเกมถูกบังคับปิด

ทำไม: เปิดโอเวอร์เลย์ของโปรแกรมแชต, game launcher, เครื่องมือของการ์ดจอ หรือโปรแกรมอัดจอไว้ → ผลคือ: ทุกครั้งที่ส่งเฟรมออกจอ โอเวอร์เลย์จะแทรกเข้ามาวาด UI ของตัวเองทับ → บนหน้าจอ: เฟรมช้าลงทีละนิด และตอนที่แจ้งเตือนโผล่ขึ้นมา ภาพหยุดแวบ กราฟิกเพี้ยน หรือเกมถูกบังคับปิด (ผู้เล่นเห็นเหมือนหลุด)

อาการ: กระตุก, ค้าง, หลุด · ผู้รับผิดชอบหลัก ภายนอก (ภายนอก) · ร่วมกับ พัฒนาไคลเอนต์ (ทีมพัฒนาเกม)

ดีเลย์จากจอ, อุปกรณ์อินพุต และ frame generation Display, input device and frame generation latency

ถ้าปิงปกติแต่การควบคุมรู้สึกหนืด อาจเป็นเพราะการประมวลผลภาพของทีวี, คอนโทรลเลอร์ไร้สาย หรือ frame generation กำลังเพิ่มดีเลย์ระหว่างอินพุตกับภาพบนจอ

ทำไม: ปิด Game Mode ของทีวีไว้, ใช้คอนโทรลเลอร์ Bluetooth หรือไร้สาย หรือเปิด frame generation (DLSS/FSR Frame Generation) → ผลคือ: ทีวีส่งเฟรมออกช้าลงระหว่างประมวลผลคุณภาพภาพ, อินพุตไร้สายมาถึงช้าตามรอบการส่งและสัญญาณรบกวน และ frame generation ต้องรอเฟรมถัดไปก่อนจึงสร้างเฟรมคั่นกลางได้ → บนหน้าจอ: ปิงและตัวเลข FPS ดี แต่กดแล้วกว่าจะเห็นผลบนจอช้า เป็นอาการอินพุตดีเลย์

อาการ: อินพุตดีเลย์ · ผู้รับผิดชอบหลัก ภายนอก (ภายนอก) · ร่วมกับ พัฒนาไคลเอนต์ (ทีมพัฒนาเกม)

เครือข่ายในบ้าน: Wi-Fi, เราเตอร์ และเครือข่ายมือถือ

ช่วงไม่กี่เมตรสุดท้ายก่อนแพ็กเก็ตจะออกจากบ้าน ระยะสั้นแต่การแจ้งแลคจำนวนไม่น้อยเกิดที่นี่ เพราะ Wi-Fi ต้องแบ่งช่องสัญญาณไร้สายเดียวกันให้หลายอุปกรณ์ใช้ และเราเตอร์ส่งทราฟฟิกของทุกคนในบ้านออกไปทางคิวเดียว

Wi-Fi ใช้ช่องสัญญาณไร้สาย (ย่านความถี่) เดียวกันร่วมกับเราเตอร์ของเพื่อนบ้าน และย่าน 2.4 GHz ยังทับกับบลูทูธและไมโครเวฟด้วย เมื่อส่งแล้วชนกันก็จะพักสักครู่แล้วส่งใหม่ ถ้าการส่งซ้ำนี้สะสมมากขึ้น แพ็กเก็ตจะมาถึงไม่สม่ำเสมอ ปิงเฉลี่ยอาจดูปกติ แต่พุ่งเป็นช่วง ๆ คือลักษณะประจำของ Wi-Fi

เราเตอร์คืออุปกรณ์ที่ทุกเครื่องในบ้านต้องผ่านเมื่อออกอินเทอร์เน็ต ถ้าส่งข้อมูลมากกว่าความเร็วที่เน็ตรับได้ จะเกิดคิวในเราเตอร์หรือโมเด็ม อุปกรณ์ที่ไม่มีฟีเจอร์จัดการคิว (SQM) จะปล่อยให้คิวนี้ยาวสะสมได้ถึงหลายร้อย ms เราเตอร์ราคาแพงก็เป็นแบบเดียวกันถ้าปิดฟีเจอร์นี้ไว้ ทันทีที่น้องอัปโหลดวิดีโอ แพ็กเก็ตเกมก็ต้องไปรอท้ายคิวนั้น ปรากฏการณ์นี้เรียกว่า bufferbloat

เราเตอร์ยังบันทึกการเชื่อมต่อ “อุปกรณ์ด้านใน ↔ เซิร์ฟเวอร์ด้านนอก” ไว้ในตาราง NAT และถ้าไม่มีแพ็กเก็ตวิ่งผ่านสักพัก ก็จะลบออกจากตาราง นี่คือสาเหตุที่พบบ่อยของอาการอยู่เฉย ๆ แล้วหลุด ส่วนเครือข่ายมือถือยังมีเรื่องการย้ายเสาสัญญาณ โหมดประหยัดพลังงานของการเชื่อมต่อไร้สาย และสัญญาณอ่อนเพิ่มเข้ามาอีก

เปรียบเทียบ

เราเตอร์คือทางเข้าออกทางเดียวของหมู่บ้านจัดสรร ถ้ารถบรรทุกขนของย้ายบ้าน (อัปโหลดวิดีโอ) ต่อแถวยาว มอเตอร์ไซค์ส่งของด่วน (แพ็กเก็ตเกม) ก็ต้องรออยู่หลังรถบรรทุก เราเตอร์ที่ฉลาด (SQM) จะเปิดเลนพิเศษให้รถส่งของด่วน

สาเหตุของแลคในชั้นนี้

Wi-Fi ถูกรบกวนหรือสัญญาณอ่อน Wi-Fi interference, weak signal

ถ้าสัญญาณอ่อนหรือถูกรบกวน ช่วงไร้สายต้องส่งซ้ำหลายรอบ แพ็กเก็ตจึงมาถึงไม่สม่ำเสมอ

ทำไม: คุณภาพสัญญาณแย่ลงเพราะผนัง, ระยะทาง, ไมโครเวฟ, Bluetooth และเราเตอร์ของเพื่อนบ้าน → ผลคือ: ส่งในช่วงไร้สายไม่สำเร็จ → ส่งซ้ำหลายรอบ → บนหน้าจอ: แพ็กเก็ตมาถึงไม่สม่ำเสมอ (จิตเตอร์) ตัวละครจึงหยุดแวบเป็นระยะ ถ้าหนักจะแพ็กเก็ตหายจนวาร์ป

อาการ: กระตุก, วาร์ป, ดีดกลับ · ผู้รับผิดชอบหลัก ภายนอก (ภายนอก) · ร่วมกับ พัฒนาไคลเอนต์ (ทีมพัฒนาเกม)

ช่องสัญญาณ Wi-Fi แออัด Crowded Wi-Fi channel

ในที่ที่มีเราเตอร์หลายสิบตัวอย่างคอนโดหรืออพาร์ตเมนต์ ต้องแบ่งกันใช้ช่องสัญญาณเดียวกัน จึงต้องรอโอกาสส่ง

ทำไม: เราเตอร์หลายสิบตัวใช้ช่องสัญญาณ 2.4 GHz เดียวกัน → ผลคือ: จะส่งได้ต้องรอให้อุปกรณ์อื่นส่งเสร็จจนช่องสัญญาณว่าง → บนหน้าจอ: ช่วงหัวค่ำที่คนกลับบ้าน จิตเตอร์ (ช่วงเวลาที่แพ็กเก็ตมาถึงไม่สม่ำเสมอ) เพิ่มขึ้นจนกระตุก

อาการ: กระตุก, อินพุตดีเลย์ · ผู้รับผิดชอบหลัก ภายนอก (ภายนอก) · ร่วมกับ พัฒนาไคลเอนต์ (ทีมพัฒนาเกม)

bufferbloat (คิวในเราเตอร์) Bufferbloat

เมื่อมีคนในบ้านอัปโหลดวิดีโอหรือดาวน์โหลดไฟล์ใหญ่ แพ็กเก็ตจะกองอยู่ในคิวของเราเตอร์ยาวเป็นหลายร้อย ms และแพ็กเก็ตเกมก็ต้องรอต่อท้าย

ทำไม: คนในบ้านอัปโหลดวิดีโอหรือสำรองข้อมูลขึ้นคลาวด์, เราสตรีมออกอากาศ หรือดาวน์โหลดไฟล์ใหญ่ จนเน็ตเต็ม → ผลคือ: เราเตอร์หรือโมเด็มเก็บแพ็กเก็ตที่ล้นไว้ในคิวขนาดใหญ่ → บนหน้าจอ: แพ็กเก็ตเกมก็ต้องรอท้ายคิว ปิงพุ่งขึ้นไปหลายร้อย ms

อาการ: อินพุตดีเลย์, กรอเร็ว, วาร์ป · ผู้รับผิดชอบหลัก ภายนอก (ภายนอก) · ร่วมกับ พัฒนาไคลเอนต์ (ทีมพัฒนาเกม)

NAT mapping หมดอายุ NAT mapping timeout

เราเตอร์จะลบการเชื่อมต่อที่ idle (ไม่มีแพ็กเก็ตวิ่งมาสักพัก) ออกจากตาราง NAT นี่คือสาเหตุที่พบบ่อยของอาการหลุดตอนที่เพิ่งขยับหลังจากอยู่เฉย ๆ

ทำไม: เราเตอร์บันทึกการเชื่อมต่อ “อุปกรณ์ข้างใน ↔ เซิร์ฟเวอร์ข้างนอก” ไว้ในตาราง NAT (ตารางแปลงที่อยู่) → ผลคือ: ถ้าไม่มีแพ็กเก็ตสักพัก จะลบออกจากตาราง (UDP มักอยู่ที่ 30–120 วินาที) → บนหน้าจอ: แพ็กเก็ตจากเซิร์ฟเวอร์เข้ามาในบ้านไม่ได้ จึงหลุด

อาการ: หลุด · ผู้รับผิดชอบหลัก พัฒนาไคลเอนต์ (ทีมพัฒนาเกม) · ร่วมกับ พัฒนาเซิร์ฟเวอร์ (ทีมพัฒนาเกม)

เราเตอร์สเปกไม่พอหรือร้อนเกิน Router CPU / session table exhaustion

เมื่ออุปกรณ์หลายสิบเครื่องและการเชื่อมต่อหลายพันรายการมารวมที่เราเตอร์ราคาถูก ตัวเราเตอร์เองจะประมวลผลไม่ไหว

ทำไม: อุปกรณ์หลายสิบเครื่อง และ P2P/ทอร์เรนต์เปิดการเชื่อมต่อหลายพันรายการ → ผลคือ: CPU และตารางเซสชันของเราเตอร์เต็ม → บนหน้าจอ: ประมวลผลแพ็กเก็ตช้าหรือแพ็กเก็ตหาย, เชื่อมต่อใหม่ไม่สำเร็จ

อาการ: กระตุก, เข้าเกมไม่ได้/โหลดไม่จบ, หลุด · ผู้รับผิดชอบหลัก ภายนอก (ภายนอก)

handover ระหว่างเสาสัญญาณ (ขณะเดินทาง) Cellular handover

เมื่อเดินทางด้วยรถเมล์หรือรถไฟใต้ดิน การสื่อสารจะขาดช่วงระหว่างที่เปลี่ยนเสาสัญญาณ

ทำไม: เสาสัญญาณที่เชื่อมต่อเปลี่ยนไประหว่างเดินทาง → ผลคือ: ปกติขาดไปหลายสิบ ms แต่ถ้าสัญญาณแย่จนสลับไม่สำเร็จ อาจขาดไปหลายร้อย ms ถึงหลายวินาที → บนหน้าจอ: ค้างแล้ววาร์ป ถ้านานจะหลุด

อาการ: ค้าง, วาร์ป, หลุด · ผู้รับผิดชอบหลัก ภายนอก (ภายนอก) · ร่วมกับ พัฒนาไคลเอนต์ (ทีมพัฒนาเกม), พัฒนาเซิร์ฟเวอร์ (ทีมพัฒนาเกม)

ดีเลย์จากการเปลี่ยนสถานะ RRC (โหมดประหยัดพลังงานของวิทยุมือถือ) Radio state promotion (RRC)

ถ้าไม่มีการสื่อสารสักพัก มือถือจะลดการเชื่อมต่อวิทยุลงเป็นสถานะพลังงานต่ำ และเมื่อมีแพ็กเก็ตถัดไปต้องยกกลับขึ้นมาใหม่ จึงช้าลง

ทำไม: ไม่มีการสื่อสารสักพัก มือถือจึงสลับการเชื่อมต่อวิทยุเป็นสถานะประหยัดพลังงาน → ผลคือ: จะส่งแพ็กเก็ตถัดไปได้ต้องยกการเชื่อมต่อกลับขึ้นมาก่อน → บนหน้าจอ: เฉพาะแอ็กชันแรกหลังอยู่เฉย ๆ ช้าเป็นพิเศษ

อาการ: อินพุตดีเลย์ · ผู้รับผิดชอบหลัก พัฒนาไคลเอนต์ (ทีมพัฒนาเกม)

สัญญาณมือถืออ่อนหรืออยู่ในจุดอับสัญญาณ Weak cellular signal

ในลิฟต์, ชั้นใต้ดิน หรือด้านในอาคาร การส่งซ้ำจะเพิ่มขึ้น ความเร็วลดลง และสุดท้ายก็หลุด

ทำไม: เคลื่อนที่ไปยังจุดที่สัญญาณอ่อน → ผลคือ: การส่งซ้ำทางวิทยุเพิ่มขึ้น, ความเร็วลดลง, การเชื่อมต่อขาดชั่วขณะ → บนหน้าจอ: จิตเตอร์และแพ็กเก็ตหายทำให้กระตุกหรือวาร์ป สุดท้ายก็หลุด

อาการ: กระตุก, วาร์ป, หลุด · ผู้รับผิดชอบหลัก ภายนอก (ภายนอก) · ร่วมกับ พัฒนาไคลเอนต์ (ทีมพัฒนาเกม)

สลับ 5G↔LTE บ่อย (บริเวณขอบพื้นที่ 5G) 5G NSA / LTE switching

ในอาคารที่สัญญาณ 5G อ่อน หรือบริเวณขอบพื้นที่ 5G มือถือจะสลับไปมาระหว่าง 5G กับ LTE บ่อย และทุกครั้งที่สลับ ปิงจะพุ่งหรือการสื่อสารขาดไปครู่หนึ่ง

ทำไม: อยู่ในจุดที่สัญญาณ 5G ไม่สม่ำเสมอ (ในอาคาร, ขอบพื้นที่ 5G) → ผลคือ: มือถือสลับระหว่าง 5G กับ LTE อยู่เรื่อย ๆ และทุกครั้งเกิดช่องว่างสั้น ๆ → บนหน้าจอ: แม้อยู่เฉย ๆ ปิงก็พุ่งแบบไม่มีแบบแผน บางครั้งค้างหรือวาร์ป

อาการ: กระตุก, วาร์ป, ค้าง · ผู้รับผิดชอบหลัก ภายนอก (ภายนอก) · ร่วมกับ พัฒนาไคลเอนต์ (ทีมพัฒนาเกม)

ข้อจำกัดของ Wi-Fi สาธารณะและเครือข่ายบริษัท Captive portal, restrictive network

หน้าล็อกอิน Wi-Fi ของร้านกาแฟหรือไฟร์วอลล์ของบริษัทบล็อกการเชื่อมต่อของเกม

ทำไม: ยังไม่ได้ยืนยันตัวตนในหน้าล็อกอิน หรือไฟร์วอลล์บล็อกพอร์ตเกมหรือ UDP → ผลคือ: ความพยายามเชื่อมต่อถูกบล็อกทั้งหมด หรือผ่านได้แค่บางส่วน → บนหน้าจอ: เข้าเกมไม่ได้ หรือล็อกอินได้แต่เข้าไปในเกมไม่สำเร็จ

อาการ: เข้าเกมไม่ได้/โหลดไม่จบ · ผู้รับผิดชอบหลัก ภายนอก (ภายนอก) · ร่วมกับ พัฒนาไคลเอนต์ (ทีมพัฒนาเกม), พัฒนาเซิร์ฟเวอร์ (ทีมพัฒนาเกม)

เส้นทางอินเทอร์เน็ต: เครือข่าย ISP และเส้นทางระยะไกล

แพ็กเก็ตที่ออกจากบ้านจะผ่านเครือข่ายของ ISP, จุดเชื่อมต่อระหว่าง ISP หลายราย และบางครั้งก็เคเบิลใต้น้ำ ไปถึงดาต้าเซ็นเตอร์ที่เซิร์ฟเวอร์ตั้งอยู่ ความหน่วงในช่วงนี้ส่วนใหญ่กำหนดโดยระยะทางและการเลือกเส้นทาง (routing) และหลายครั้งบริษัทเกมแก้เองโดยตรงไม่ได้

แสงในสายไฟเบอร์ออปติกเดินทางได้ราว 200,000 km ใน 1 วินาที ถ้าเซิร์ฟเวอร์อยู่ห่าง 1,000 km เวลาไปกลับจะใช้อย่างน้อย 10 ms และตราบใดที่ยังใช้ไฟเบอร์ออปติก ตัวเลขนี้ลดไม่ได้ ไม่ว่าจะอัปเกรดเซิร์ฟเวอร์หรืออุปกรณ์แค่ไหน ในความเป็นจริง แพ็กเก็ตวิ่งอ้อมไปตามจุดที่ ISP เชื่อมต่อกัน (peering) จึงมักใช้เวลา 1.5–2 เท่าของค่าทางทฤษฎี ส่วนเส้นทางอย่างเกาหลีไปยุโรปที่แทบไม่มีเคเบิลใหญ่วางตามแนวเส้นตรง จะต้องอ้อมผ่านเอเชียตะวันออกเฉียงใต้/สุเอซ หรือผ่านสหรัฐฯ จนกลายเป็น 2.5–3 เท่า (ไปกลับราว 230–270 ms)

ปัญหาคือเส้นทางนี้เปลี่ยนไปตามเวลาและสถานการณ์ ช่วงราว 21:00–23:00 ทุกคนดูวิดีโอกัน จุดเชื่อมต่อระหว่าง ISP จึงแออัดได้ง่าย เมื่อข้อมูลเส้นทาง (BGP) เปลี่ยน แพ็กเก็ตจะไปไม่ถึงปลายทางอยู่หลายวินาทีถึงหลายสิบวินาที (นาน ๆ ครั้งอาจถึงหลายนาที) และถ้าเคเบิลใต้น้ำขาด ก็ต้องอ้อมไปเส้นทางไกลอยู่หลายสัปดาห์ ถ้าแลค “เฉพาะผู้ใช้ ISP บางราย”, “เฉพาะช่วงหัวค่ำ” หรือ “เฉพาะเมื่อเล่นจากต่างประเทศ” ให้สงสัยชั้นนี้ก่อน

เปรียบเทียบ

เครือข่ายของ ISP คือเครือข่ายทางด่วน แม้ทางจากโซลไปปูซานจะไม่ติด ก็ยังต้องใช้เวลาตามระยะทาง ด่านเก็บเงินช่วงเลิกงาน (จุด peering) จะติด และถ้ามีอุบัติเหตุ ระบบนำทางก็จะพาอ้อมไปทางไกล

สาเหตุของแลคในชั้นนี้

ความหน่วงในการแพร่สัญญาณ (ระยะทางจริง) Propagation delay

แม้แต่แสงในสายไฟเบอร์ก็เดินทางได้แค่ประมาณ 200,000 กิโลเมตรใน 1 วินาที เซิร์ฟเวอร์ที่อยู่ไกลจะดีแค่ไหนก็ยังช้า

ทำไม: เซิร์ฟเวอร์อยู่ไกล (เซิร์ฟเวอร์ต่างประเทศ, อีกทวีป) → ผลคือ: เวลาไปกลับเพิ่มขึ้นตามระยะทาง (อย่างน้อย 10 ms ต่อ 1,000 กิโลเมตร) → บนหน้าจอ: ทุกการกระทำมีอินพุตดีเลย์คงที่ และเสียเปรียบในการตัดสินผล

อาการ: อินพุตดีเลย์ · ผู้รับผิดชอบหลัก อินฟราเซิร์ฟเวอร์ (ทีมอินฟรา) · ร่วมกับ อินฟราเครือข่าย (ทีมอินฟรา), พัฒนาเซิร์ฟเวอร์ (ทีมพัฒนาเกม)

อินเทอร์เน็ตดาวเทียม (วงโคจรต่ำ/ค้างฟ้า) Satellite internet (LEO, GEO)

อินเทอร์เน็ตดาวเทียมต้องส่งสัญญาณขึ้นไปในอวกาศแล้วกลับลงมา ดาวเทียมวงโคจรค้างฟ้าแค่ช่วงขึ้นลงนี้ก็ใช้เวลาไปกลับเกิน 0.5 วินาทีแล้ว ส่วนดาวเทียมวงโคจรต่ำอย่าง Starlink ปกติเร็ว แต่ตอนที่ระบบจัดเส้นทางใหม่ ความหน่วงจะแกว่งและบางครั้งขาดไปชั่วครู่

ทำไม: เชื่อมต่อจากบ้าน เรือ หรือเครื่องบิน ผ่านอินเทอร์เน็ตดาวเทียมวงโคจรค้างฟ้าหรือวงโคจรต่ำ หรือผ่าน Wi-Fi บนเครื่องบินที่ใช้ดาวเทียม → ผลคือ: วงโคจรค้างฟ้าอยู่สูงประมาณ 36,000 กิโลเมตร ระยะทางขึ้นลงจึงยาวอยู่แล้ว ส่วนวงโคจรต่ำจัดเส้นทางระหว่างอุปกรณ์ผู้ใช้ ดาวเทียม และสถานีภาคพื้นดินใหม่เป็นรอบสั้น ๆ และตอนนั้นจะเกิดความหน่วงหรือแพ็กเก็ตหายชั่วครู่ → บนหน้าจอ: วงโคจรค้างฟ้ามีอินพุตดีเลย์มากในทุกการกระทำ ส่วนวงโคจรต่ำปกติดี แต่กระตุกหรือวาร์ปเป็นช่วงห่างเท่า ๆ กัน

อาการ: อินพุตดีเลย์, กระตุก, วาร์ป · ผู้รับผิดชอบหลัก ภายนอก (ภายนอก) · ร่วมกับ พัฒนาไคลเอนต์ (ทีมพัฒนาเกม), พัฒนาเซิร์ฟเวอร์ (ทีมพัฒนาเกม)

เส้นทางวิ่งอ้อม Suboptimal routing

เพราะสัญญาการเชื่อมต่อระหว่าง ISP แม้เซิร์ฟเวอร์จะอยู่ใกล้ แพ็กเก็ตก็ยังวิ่งอ้อมไปทางไกล

ทำไม: ISP ของเรากับ ISP ฝั่งเซิร์ฟเวอร์ไม่ได้เชื่อมต่อกันโดยตรง → ผลคือ: วิ่งผ่านประเทศอื่นหรือเมืองอื่น ระยะทางและจำนวนอุปกรณ์ที่ผ่านจึงเพิ่มขึ้น → บนหน้าจอ: เฉพาะผู้เล่นของ ISP บางค่ายที่ปิงสูงผิดปกติ

อาการ: อินพุตดีเลย์ · ผู้รับผิดชอบหลัก อินฟราเครือข่าย (ทีมอินฟรา) · ร่วมกับ ภายนอก (ภายนอก)

จุด peering แออัดช่วงพีค Peak-hour congestion at peering

ช่วงหัวค่ำราว 21:00–23:00 ทราฟฟิกวิดีโอพุ่งขึ้นมาก จุดเชื่อมต่อระหว่าง ISP (peering) จึงมักแออัด

ทำไม: ช่วงหัวค่ำมีการสตรีมและดาวน์โหลดพร้อมกันจำนวนมาก → ผลคือ: เกิดคิวและแพ็กเก็ตหายที่จุด peering → บนหน้าจอ: เฉพาะช่วงหัวค่ำที่ผู้เล่นของ ISP บางค่ายกระตุกหรือวาร์ป

อาการ: กระตุก, วาร์ป, ดีดกลับ · ผู้รับผิดชอบหลัก อินฟราเครือข่าย (ทีมอินฟรา) · ร่วมกับ ภายนอก (ภายนอก)

เคเบิลใต้น้ำ/วงจรต่างประเทศขัดข้อง Submarine cable fault

เมื่อเคเบิลใต้น้ำขาด ทราฟฟิกต้องอ้อมไปเส้นทางไกลหลายสัปดาห์ (นานสุดหลายเดือน) จนกว่าจะซ่อมเสร็จ และวงจรที่เหลืออยู่ก็แออัด

ทำไม: เคเบิลขาดหรืออุปกรณ์เสีย → ผลคือ: ทราฟฟิกไปกองที่เส้นทางอ้อมที่ไกลและวงจรที่เหลืออยู่ → บนหน้าจอ: ผู้เล่นที่เชื่อมต่อจากต่างประเทศปิงพุ่งและแพ็กเก็ตหาย ต่อเนื่องหลายวันถึงหลายสัปดาห์

อาการ: อินพุตดีเลย์, วาร์ป · ผู้รับผิดชอบหลัก ภายนอก (ภายนอก) · ร่วมกับ อินฟราเครือข่าย (ทีมอินฟรา)

เส้นทาง BGP เปลี่ยนและ converge ใหม่ Route change / BGP convergence

เมื่อข้อมูลเส้นทางของอินเทอร์เน็ตเปลี่ยน แพ็กเก็ตจะหายในช่วงไม่กี่วินาทีถึงหลายสิบวินาที (บางกรณีหลายนาที) ที่เส้นทางกำลัง converge ใหม่

ทำไม: ข้อมูลเส้นทางในช่วงเครือข่ายของ ISP รายใดรายหนึ่งเปลี่ยน → ผลคือ: แพ็กเก็ตหายไปไม่กี่วินาทีถึงหลายสิบวินาที หรือสลับไปใช้เส้นทางใหม่ → บนหน้าจอ: จู่ ๆ ก็ค้างไปไม่กี่วินาที แล้วค่าปิงเปลี่ยนไป (เช่น 40 → 70 ms)

อาการ: ค้าง, วาร์ป · ผู้รับผิดชอบหลัก อินฟราเครือข่าย (ทีมอินฟรา) · ร่วมกับ พัฒนาเซิร์ฟเวอร์ (ทีมพัฒนาเกม), ภายนอก (ภายนอก)

เส้นทาง ECMP เส้นหนึ่งเสีย ECMP / link bundle member fault

ISP และดาต้าเซ็นเตอร์มีหลายเส้นทางไปยังปลายทางเดียวกัน และกำหนดเส้นทางหนึ่งเส้นให้แต่ละการเชื่อมต่อ ถ้าเสียแค่เส้นเดียว คนที่ถูกจัดให้ใช้เส้นนั้นจะแลคอยู่ตลอด

ทำไม: ในช่วงที่รวมหลายลิงก์เข้าด้วยกัน มีลิงก์หรืออุปกรณ์ตัวหนึ่งเสียหรือแออัด → ผลคือ: เส้นทางถูกกำหนดจากค่า hash ของ IP และพอร์ต การเชื่อมต่อที่ถูกจัดให้ใช้เส้นนั้นเท่านั้นที่เจอแพ็กเก็ตหายและความหน่วง → บนหน้าจอ: ภูมิภาคเดียวกัน ISP เดียวกัน แต่บางคนวาร์ปอยู่เรื่อย ๆ บางครั้งเชื่อมต่อใหม่แล้วกลับมาปกติ

อาการ: วาร์ป, ดีดกลับ, กระตุก · ผู้รับผิดชอบหลัก อินฟราเครือข่าย (ทีมอินฟรา) · ร่วมกับ พัฒนาเซิร์ฟเวอร์ (ทีมพัฒนาเกม), ภายนอก (ภายนอก)

ISP จำกัดความเร็วและจัดการทราฟฟิก Traffic shaping, data caps

ในแพ็กเกจที่ใช้ดาต้าเกินโควตาแล้ว หรือแพ็กเกจที่มีการจัดการทราฟฟิกบางประเภท แพ็กเก็ตจะถูกหน่วงหรือถูกทิ้ง

ทำไม: ถูกจำกัดความเร็วหลังดาต้าในแพ็กเกจหมด หรือถูกจำกัดทราฟฟิกบางประเภท → ผลคือ: แพ็กเก็ตต้องรอคิวหรือถูกทิ้ง → บนหน้าจอ: แลคหลังใช้งานไปถึงปริมาณหนึ่ง โดยเฉพาะบนมือถือ

อาการ: อินพุตดีเลย์, วาร์ป · ผู้รับผิดชอบหลัก ภายนอก (ภายนอก) · ร่วมกับ พัฒนาเซิร์ฟเวอร์ (ทีมพัฒนาเกม), อินฟราเครือข่าย (ทีมอินฟรา)

การจำกัด UDP และตรวจแพ็กเก็ตระดับประเทศหรือ ISP UDP blocking, throttling and inspection by networks

บางเครือข่ายบล็อก IP หรือพอร์ต UDP บางตัว หรือจำกัดความเร็ว UDP และอุปกรณ์ตรวจแพ็กเก็ตจะกรองโปรโตคอลที่ไม่รู้จักทิ้ง เกมที่สื่อสารด้วย UDP จึงเชื่อมต่อในเครือข่ายนั้นไม่ได้หรือหลุดบ่อย

ทำไม: เชื่อมต่อจากเครือข่าย ISP บางรายที่จำกัดความเร็ว UDP หรือจากเครือข่ายที่มีอุปกรณ์ตรวจทราฟฟิก (เพื่อเซ็นเซอร์) ระดับประเทศหรือ ISP → ผลคือ: บล็อก IP หรือพอร์ต UDP บางตัว, จำกัดความเร็ว UDP ในช่วงที่แออัด, กรองพอร์ตหรือโปรโตคอลที่ไม่อยู่ในรายการอนุญาตทิ้ง หรือปล่อยผ่านแค่ไม่กี่แพ็กเก็ตแรกแล้วบล็อก → บนหน้าจอ: เฉพาะผู้เล่นในบางประเทศหรือบาง ISP ที่เข้าเกมไม่ได้หรือโหลดไม่จบ, เข้าได้แล้วหลุดในไม่ช้า, วาร์ปเพราะแพ็กเก็ตหายในช่วงที่แออัด

อาการ: เข้าเกมไม่ได้/โหลดไม่จบ, หลุด, วาร์ป · ผู้รับผิดชอบหลัก ภายนอก (ภายนอก) · ร่วมกับ พัฒนาไคลเอนต์ (ทีมพัฒนาเกม), พัฒนาเซิร์ฟเวอร์ (ทีมพัฒนาเกม), อินฟราเครือข่าย (ทีมอินฟรา)

คุณภาพสายเน็ตแย่ Faulty last-mile line / modem

ขั้วต่อหลวม สายเก่า หรือโมเด็มผิดปกติ ทำให้แพ็กเก็ตหายอย่างต่อเนื่องและเน็ตขาดเป็นระยะ

ทำไม: สายเคเบิลเสียหาย, ขั้วต่อหลวม, โมเด็มหรืออุปกรณ์ไฟเบอร์ (ONU) ผิดปกติ → ผลคือ: แพ็กเก็ตถูกทิ้งเพราะบิตผิดพลาด และบางครั้งเน็ตขาดไปหลายวินาทีถึงราว 1 นาทีระหว่างเชื่อมต่อใหม่ → บนหน้าจอ: แพ็กเก็ตหายเล็กน้อยต่อเนื่อง บางครั้งค้างไปหลายวินาทีหรือหลุด

อาการ: วาร์ป, ค้าง, หลุด · ผู้รับผิดชอบหลัก ภายนอก (ภายนอก)

DNS ขัดข้อง/ช้า DNS failure / slowness

ถ้า DNS ซึ่งแปลงชื่อเซิร์ฟเวอร์เป็น IP ช้าหรือล้มเหลว เกมจะหาเซิร์ฟเวอร์ล็อกอินหรือเซิร์ฟเวอร์แพตช์ไม่เจอ

ทำไม: DNS ของ ISP ขัดข้องหรือตั้งค่าผิด → ผลคือ: หา IP ของเซิร์ฟเวอร์ล็อกอินหรือเซิร์ฟเวอร์แพตช์ไม่เจอ → บนหน้าจอ: กดเชื่อมต่อแล้วรอนาน หรือเข้าเกมไม่ได้ คนที่เข้าเกมอยู่แล้วไม่เป็นอะไร

อาการ: เข้าเกมไม่ได้/โหลดไม่จบ · ผู้รับผิดชอบหลัก ภายนอก (ภายนอก) · ร่วมกับ พัฒนาไคลเอนต์ (ทีมพัฒนาเกม)

ลิงก์ที่ใช้ร่วมกันเต็มเพราะ DDoS DDoS saturating shared links

การโจมตีปริมาณมหาศาลที่พุ่งเป้ามาที่บริษัทเกม หรือที่อื่นในเครือข่ายเดียวกัน ทำให้ลิงก์ที่ใช้ร่วมกันเต็ม

ทำไม: มีทราฟฟิกโจมตีปริมาณมหาศาล → ผลคือ: ทราฟฟิกปกติที่ใช้ลิงก์เดียวกันก็ถูกเบียดและถูกทิ้งไปด้วย → บนหน้าจอ: หลายคนวาร์ป หลุด หรือเข้าเกมไม่ได้พร้อมกัน

อาการ: วาร์ป, หลุด, เข้าเกมไม่ได้/โหลดไม่จบ · ผู้รับผิดชอบหลัก อินฟราเครือข่าย (ทีมอินฟรา) · ร่วมกับ ภายนอก (ภายนอก)

IP ที่ ISP ให้ใช้ร่วมกัน (CGNAT) Carrier-grade NAT

เครือข่ายมือถือและ ISP บางรายให้ผู้ใช้บริการหลายรายใช้ IP เดียวร่วมกัน และลบ mapping ของการเชื่อมต่อที่ idle ภายในเวลาสั้น ๆ

ทำไม: อุปกรณ์ของ ISP ดูแลตารางเซสชันของผู้ใช้บริการจำนวนมหาศาล → ผลคือ: ตารางเซสชันมีขีดจำกัด และ idle timeout สั้น → บนหน้าจอ: อยู่เฉย ๆ แล้วหลุด, false positive ที่ทำให้คนที่ใช้ IP เดียวกันถูกบล็อกไปพร้อมกัน

อาการ: หลุด, เข้าเกมไม่ได้/โหลดไม่จบ · ผู้รับผิดชอบหลัก พัฒนาไคลเอนต์ (ทีมพัฒนาเกม) · ร่วมกับ พัฒนาเซิร์ฟเวอร์ (ทีมพัฒนาเกม), อินฟราเครือข่าย (ทีมอินฟรา)

เชื่อมต่อผ่าน VPN/โปรแกรมลดปิง VPN / game accelerator detour

เมื่อเปิด VPN หรือโปรแกรมลดปิง แพ็กเก็ตจะวิ่งผ่านเซิร์ฟเวอร์ตัวกลาง (relay) ของบริษัทนั้น ถ้าเซิร์ฟเวอร์ตัวกลางอยู่ไกลหรือแออัด ก็กลับยิ่งช้าลง

ทำไม: VPN หรือโปรแกรมลดปิงส่งแพ็กเก็ตเกมทั้งหมดอ้อมไปที่เซิร์ฟเวอร์ตัวกลาง → ผลคือ: ระยะทางและความแออัดถึงเซิร์ฟเวอร์ตัวกลางเพิ่มเข้ามา และเฮดเดอร์ของ tunnel ทำให้ MTU (ขนาดแพ็กเก็ตที่ส่งได้ในครั้งเดียว) ลดลงด้วย → บนหน้าจอ: ปิงสูงขึ้นและแพ็กเก็ตหาย, ถูกบล็อกไปพร้อมกับคนที่ใช้ IP ตัวกลางเดียวกันจนเข้าเกมไม่ได้

อาการ: อินพุตดีเลย์, วาร์ป, เข้าเกมไม่ได้/โหลดไม่จบ · ผู้รับผิดชอบหลัก ภายนอก (ภายนอก) · ร่วมกับ อินฟราเครือข่าย (ทีมอินฟรา), พัฒนาเซิร์ฟเวอร์ (ทีมพัฒนาเกม)

อุปกรณ์เครือข่ายในดาต้าเซ็นเตอร์

ก่อนถึงเซิร์ฟเวอร์ แพ็กเก็ตจะผ่านเราเตอร์, อุปกรณ์ป้องกัน DDoS, ไฟร์วอลล์, โหลดบาลานเซอร์ และสวิตช์ตามลำดับ ปกติช่วงนี้ใช้เวลาไม่ถึง 1 ms แต่ถ้าอุปกรณ์ตัวใดตัวหนึ่งเต็มหรือขัดข้อง ผู้เล่นหลายพันคนทั้งเซิร์ฟเวอร์จะได้รับผลกระทบพร้อมกัน

อุปกรณ์แต่ละตัวมีหน้าที่ต่างกัน เราเตอร์กำหนดเส้นทาง อุปกรณ์ป้องกัน DDoS กรองทราฟฟิกโจมตีออก ไฟร์วอลล์ปล่อยผ่านเฉพาะการเชื่อมต่อที่อนุญาตและติดตามการเชื่อมต่อทั้งหมดด้วยตารางเซสชัน โหลดบาลานเซอร์กระจายการเชื่อมต่อที่เข้ามาไปยังเซิร์ฟเวอร์หลายเครื่อง และสวิตช์เชื่อมเซิร์ฟเวอร์เข้าด้วยกัน

จุดอ่อนร่วมของอุปกรณ์เหล่านี้คือขนาดตารางและขนาดบัฟเฟอร์ ถ้าตารางเซสชันของไฟร์วอลล์เต็ม ก็รับการเชื่อมต่อใหม่ไม่ได้ โหลดบาลานเซอร์จะลบการเชื่อมต่อที่ idle ทิ้งหลังผ่านไประยะหนึ่ง และบัฟเฟอร์เล็ก ๆ ของสวิตช์จะล้นในเวลาไม่ถึง 1 ms เมื่อเซิร์ฟเวอร์หลายเครื่องส่งแพ็กเก็ตถึงผู้เล่นหลายพันคนในจังหวะเดียวกัน (ตอนเวิลด์บอสเกิด) และในช่วงไม่กี่วินาทีที่อุปกรณ์ตัวหนึ่งเสียแล้วสลับไปใช้ตัวสำรอง (failover) ทุกคนจะค้าง

เปรียบเทียบ

ทางเข้าดาต้าเซ็นเตอร์คือจุดตรวจค้นและประตูขึ้นเครื่องที่สนามบิน จุดตรวจค้น (ไฟร์วอลล์) ให้ผ่านเฉพาะคนที่มีชื่ออยู่ในรายชื่อ และถ้าช่องรายชื่อเต็มก็รับเพิ่มไม่ได้ เจ้าหน้าที่ประตู (โหลดบาลานเซอร์) จะนับผู้โดยสารที่นั่งเงียบอยู่นาน ๆ ว่า “ออกไปแล้ว” และลบชื่อออกจากรายชื่อ

สาเหตุของแลคในชั้นนี้

ตารางเซสชันของไฟร์วอลล์เต็ม Firewall session table exhaustion

ไฟร์วอลล์บันทึกทุกการเชื่อมต่อที่ปล่อยผ่านไว้ในตารางเซสชันเพื่อติดตาม เมื่อตารางเต็มก็รับการเชื่อมต่อใหม่ไม่ได้

ทำไม: จำนวนเซสชันชนขีดจำกัดเพราะการเชื่อมต่อทะลักหรือถูกโจมตี → ผลคือ: ไม่มีช่องว่างให้บันทึกการเชื่อมต่อใหม่ จึงถูกปฏิเสธ → บนหน้าจอ: คนที่พยายามเข้าใหม่เข้าเกมไม่ได้หรือโหลดไม่จบ และการเชื่อมต่อเดิมบางส่วนก็หลุด

อาการ: เข้าเกมไม่ได้/โหลดไม่จบ, หลุด · ผู้รับผิดชอบหลัก อินฟราเครือข่าย (ทีมอินฟรา) · ร่วมกับ พัฒนาเซิร์ฟเวอร์ (ทีมพัฒนาเกม), พัฒนาไคลเอนต์ (ทีมพัฒนาเกม)

การอ้อมผ่านระบบป้องกัน DDoS และ false positive DDoS scrubbing latency, false positives

เมื่อเบี่ยงทราฟฟิกไป scrubbing center เพื่อกันการโจมตี เส้นทางจะยาวขึ้น และบางครั้งระบบเข้าใจผิดว่าผู้เล่นปกติเป็นการโจมตีแล้วบล็อก

ทำไม: หลังตรวจพบการโจมตี (หรือตลอดเวลา) ทราฟฟิกขาเข้าถูกเบี่ยงไป scrubbing center → ผลคือ: เส้นทางยาวขึ้น และแพ็กเก็ตปกติบางส่วนถูกตัดสินว่าเป็นการโจมตี → บนหน้าจอ: ปิงสูงขึ้นทั้งหมด, เฉพาะบางพื้นที่หรือบาง ISP ที่เข้าเกมไม่ได้

อาการ: อินพุตดีเลย์, เข้าเกมไม่ได้/โหลดไม่จบ, วาร์ป · ผู้รับผิดชอบหลัก อินฟราเครือข่าย (ทีมอินฟรา) · ร่วมกับ พัฒนาเซิร์ฟเวอร์ (ทีมพัฒนาเกม)

idle timeout ของโหลดบาลานเซอร์ Load balancer idle timeout

โหลดบาลานเซอร์ลบการเชื่อมต่อที่ idle ทิ้งหลังผ่านไประยะหนึ่ง ฝั่งเกมยังคิดว่าการเชื่อมต่อยังอยู่ จนกระทั่งหลุด

ทำไม: ผู้เล่นไม่ส่งแพ็กเก็ตใด ๆ อยู่พักหนึ่ง (เปิดหน้าต่างสนทนา, ไม่อยู่หน้าจอ) → ผลคือ: โหลดบาลานเซอร์เก็บกวาดการเชื่อมต่อที่ idle (ค่าเริ่มต้นที่พบบ่อย 60–350 วินาที) → บนหน้าจอ: หลุดทันทีที่กลับมาขยับอีกครั้ง

อาการ: หลุด · ผู้รับผิดชอบหลัก อินฟราเครือข่าย (ทีมอินฟรา) · ร่วมกับ พัฒนาไคลเอนต์ (ทีมพัฒนาเกม), พัฒนาเซิร์ฟเวอร์ (ทีมพัฒนาเกม)

connection tracking ของ security group บนคลาวด์หมดอายุ Cloud security group connection tracking timeout

ไฟร์วอลล์ที่ผูกกับเซิร์ฟเวอร์บนคลาวด์ (security group) ก็ติดตามการเชื่อมต่อเช่นกัน และรายการที่ติดตามของการเชื่อมต่อที่ idle จะหมดอายุหลังเวลาที่กำหนด แม้เป็นเซิร์ฟเวอร์ที่ผู้เล่นเชื่อมต่อตรงโดยไม่มีโหลดบาลานเซอร์ ผู้เล่นที่อยู่เฉย ๆ ก็อาจหลุดได้

ทำไม: security group ถูกตั้งค่าในแบบที่ติดตามการเชื่อมต่อของเกม (อนุญาตเฉพาะบาง IP, จำกัดกฎขาออก, ผ่าน NLB ฯลฯ) → ผลคือ: รายการที่ติดตามของการเชื่อมต่อที่ idle อยู่พักหนึ่งหมดอายุ แล้ว security group ทิ้งแพ็กเก็ตที่มาหลังจากนั้นเงียบ ๆ → บนหน้าจอ: กลับมาขยับหลังจากไม่อยู่หน้าจอ แล้วไม่มีการตอบสนองจนหลุด โปรแกรมเซิร์ฟเวอร์ไม่รู้ตัวอยู่นาน

อาการ: หลุด · ผู้รับผิดชอบหลัก อินฟราเซิร์ฟเวอร์ (ทีมอินฟรา) · ร่วมกับ พัฒนาไคลเอนต์ (ทีมพัฒนาเกม), พัฒนาเซิร์ฟเวอร์ (ทีมพัฒนาเกม)

ขีดจำกัดการเชื่อมต่อ/พอร์ตของ NAT gateway บนคลาวด์ Cloud NAT gateway connection / port limits

การเชื่อมต่อที่เซิร์ฟเวอร์ใน private subnet ออกไปภายนอก (ระบบยืนยันตัวตนของแพลตฟอร์ม, ระบบชำระเงิน, API ภายนอก) จะผ่าน NAT gateway ซึ่งแปลง IP และพอร์ตก่อนส่งออก ถ้าการเชื่อมต่อพร้อมกันไปยังปลายทางเดียวกันเกินขีดจำกัดพอร์ตของ gateway การเชื่อมต่อใหม่จะล้มเหลว

ทำไม: เซิร์ฟเวอร์เปิดการเชื่อมต่อสั้น ๆ จำนวนมากไปยัง IP ภายนอกเดียวกัน เช่น ระบบยืนยันตัวตนของแพลตฟอร์มหรือระบบชำระเงิน หรือเปิดการเชื่อมต่อทิ้งไว้นาน → ผลคือ: NAT gateway จัดสรรพอร์ตต้นทางสำหรับปลายทางนั้นเพิ่มไม่ได้ การเชื่อมต่อใหม่จึงล้มเหลว → บนหน้าจอ: ในเกมปกติดี แต่เฉพาะฟีเจอร์ที่เรียกภายนอก เช่น ล็อกอิน ชำระเงิน หรือแจกของรางวัล ที่ล้มเหลวหรือช้า (เข้าเกมไม่ได้/โหลดไม่จบ, กดไม่ติด/โรลแบ็ค)

อาการ: เข้าเกมไม่ได้/โหลดไม่จบ, กดไม่ติด/โรลแบ็ค · ผู้รับผิดชอบหลัก อินฟราเครือข่าย (ทีมอินฟรา) · ร่วมกับ พัฒนาเซิร์ฟเวอร์ (ทีมพัฒนาเกม)

โหลดบาลานเซอร์กระจายไม่เท่ากัน/health check ตัดสินผิด LB imbalance, bad health checks

การเชื่อมต่อไปกองที่เซิร์ฟเวอร์เครื่องเดียว หรือยังส่งคนไปเซิร์ฟเวอร์ที่ตายแล้วอยู่เรื่อย ๆ

ทำไม: กฎการกระจายไม่เหมาะ หรือ health check มองไม่เห็นสถานะจริง → ผลคือ: เซิร์ฟเวอร์เครื่องเดียวโหลดเกิน หรือพยายามเชื่อมต่อไปเซิร์ฟเวอร์ที่ตายแล้ว → บนหน้าจอ: เฉพาะบางแชนแนลหรือบางคนที่เป็นสโลว์โมชั่น, เข้าเกมไม่ได้หรือโหลดไม่จบ

อาการ: สโลว์โมชั่น, เข้าเกมไม่ได้/โหลดไม่จบ · ผู้รับผิดชอบหลัก อินฟราเครือข่าย (ทีมอินฟรา) · ร่วมกับ พัฒนาเซิร์ฟเวอร์ (ทีมพัฒนาเกม)

microburst ที่สวิตช์ Switch microburst drops

ถ้าเซิร์ฟเวอร์หลายเครื่องส่งแพ็กเก็ตถึงผู้เล่นหลายพันคนพร้อมกันในชั่วขณะเดียว บัฟเฟอร์ขนาดเล็กของพอร์ตสวิตช์ที่ทราฟฟิกนั้นไหลมารวมกันจะล้นภายในไม่ถึง 1 ms

ทำไม: เวิลด์บอสเกิดหรือมีสกิลขนาดใหญ่ หรือทิกของเซิร์ฟเวอร์หลายเครื่องตรงกันพอดีจนส่งออกพร้อมกัน → ผลคือ: บัฟเฟอร์ (หลายร้อย KB ถึงหลาย MB ต่อพอร์ต) ตรงจุดที่หลายพอร์ตไหลเข้าพอร์ตเดียว หรือจุดที่ข้อมูลไหลจากพอร์ตเร็วไปพอร์ตช้า เต็มในชั่วขณะ → บนหน้าจอ: แพ็กเก็ตบางส่วนถูกทิ้ง หลายคนวาร์ปหรือสกิลไม่ออกพร้อมกัน

อาการ: วาร์ป, กดไม่ติด/โรลแบ็ค · ผู้รับผิดชอบหลัก พัฒนาเซิร์ฟเวอร์ (ทีมพัฒนาเกม) · ร่วมกับ อินฟราเครือข่าย (ทีมอินฟรา), อินฟราเซิร์ฟเวอร์ (ทีมอินฟรา)

ลิงก์ของดาต้าเซ็นเตอร์เต็ม Uplink saturation

ถ้าการแจกจ่ายแพตช์ การส่ง log หรือการสำรองข้อมูล ใช้ลิงก์เดียวกับเกม ลิงก์จะเต็ม

ทำไม: การส่งข้อมูลขนาดใหญ่กินลิงก์เดียวกัน → ผลคือ: คิวในลิงก์และแพ็กเก็ตหายเพิ่มขึ้น → บนหน้าจอ: ปิงสูงขึ้นทั้งเซิร์ฟเวอร์และวาร์ป

อาการ: อินพุตดีเลย์, วาร์ป · ผู้รับผิดชอบหลัก อินฟราเครือข่าย (ทีมอินฟรา) · ร่วมกับ อินฟราเซิร์ฟเวอร์ (ทีมอินฟรา)

การ failover ของอุปกรณ์เครือข่าย Network device failover

ระหว่างไม่กี่วินาทีที่เราเตอร์หรือไฟร์วอลล์ตัวหนึ่งเสียแล้วสลับไปใช้อุปกรณ์สำรอง (failover) ทุกคนจะค้างพร้อมกัน

ทำไม: อุปกรณ์เสียหรืออยู่ระหว่างซ่อมบำรุง จึงสลับไปใช้อุปกรณ์สำรอง → ผลคือ: การสลับใช้เวลาหลายวินาที ถ้าข้อมูลเซสชันไม่ได้ซิงก์ไว้ การเชื่อมต่อจะถูกรีเซ็ต → บนหน้าจอ: ผู้เล่นทุกคนในเซิร์ฟเวอร์ค้างพร้อมกัน, หลุดจำนวนมาก

อาการ: ค้าง, หลุด · ผู้รับผิดชอบหลัก อินฟราเครือข่าย (ทีมอินฟรา) · ร่วมกับ พัฒนาเซิร์ฟเวอร์ (ทีมพัฒนาเกม), พัฒนาไคลเอนต์ (ทีมพัฒนาเกม)

สายเสีย/พอร์ตมี error Bad cable / optics (CRC errors)

ถ้าโมดูลออปติกหรือสายเสีย แพ็กเก็ตที่ผ่านเส้นทางนั้นจะเสียหายในสัดส่วนคงที่

ทำไม: บิตผิดพลาดเพราะโมดูลออปติกหรือสายเสีย → ผลคือ: อุปกรณ์ทิ้งแพ็กเก็ตที่เสียหายเงียบ ๆ → บนหน้าจอ: เฉพาะเซิร์ฟเวอร์และผู้เล่นบางส่วนที่ใช้เส้นทางนั้น แพ็กเก็ตหายต่อเนื่องจนวาร์ปหรือดีดกลับ

อาการ: วาร์ป, ดีดกลับ · ผู้รับผิดชอบหลัก อินฟราเครือข่าย (ทีมอินฟรา)

MTU ไม่ตรงกัน (เฉพาะแพ็กเก็ตใหญ่ที่หาย) MTU black hole

ถ้า MTU (ขนาดที่ส่งได้ในครั้งเดียว) ของช่วงกลางทางลดลง แต่ข้อความแจ้งว่าขนาดเกินถูกบล็อก แพ็กเก็ตใหญ่จะหายไปเรื่อย ๆ

ทำไม: MTU ลดลงในช่วง tunnel หรือ VPN → ผลคือ: ข้อความแจ้งว่าขนาดเกิน (ICMP) ถูกไฟร์วอลล์บล็อก ฝั่งส่งจึงไม่รู้ → บนหน้าจอ: ค้างเฉพาะตอนเปิดหน้าจอที่ข้อมูลเยอะ เช่น กระเป๋าหรือรายชื่อตัวละคร แล้วหลุด

อาการ: ค้าง, หลุด, เข้าเกมไม่ได้/โหลดไม่จบ · ผู้รับผิดชอบหลัก อินฟราเครือข่าย (ทีมอินฟรา) · ร่วมกับ อินฟราเซิร์ฟเวอร์ (ทีมอินฟรา), พัฒนาเซิร์ฟเวอร์ (ทีมพัฒนาเกม)

การ์ดเครือข่ายของเซิร์ฟเวอร์ (NIC)

การ์ดเครือข่ายที่เสียบอยู่ในเซิร์ฟเวอร์รับแพ็กเก็ตหลายแสนถึงหลายล้านตัวต่อวินาทีแล้วส่งต่อให้ CPU ถ้าการประมวลผลตรงนี้ไม่ทัน โปรแกรมเซิร์ฟเวอร์จะทำแพ็กเก็ตหายไปโดยไม่รู้ด้วยซ้ำว่ามีแพ็กเก็ตมาถึง

NIC ใส่แพ็กเก็ตที่มาถึงลงใน ring buffer (receive buffer ที่วนใช้สล็อตจำนวนคงที่) ตามลำดับ แล้วแจ้ง CPU ว่า “มีแพ็กเก็ตมาแล้ว” (อินเทอร์รัปต์) CPU ดึงแพ็กเก็ตจาก ring buffer ส่งต่อให้ OS ถ้าแพ็กเก็ตเข้ามาเร็วกว่าที่ CPU ดึงออก สล็อตจะเต็มหมด และแพ็กเก็ตที่ตามมาจะถูกทิ้ง เป็นแลคที่หายาก เพราะมีแค่ตัวเลขในสถิติของการ์ดเครือข่าย (ethtool -S) ที่เพิ่มขึ้นเงียบ ๆ โดยไม่มี error ใดเหลือใน log ของเซิร์ฟเวอร์เกม

NIC รุ่นใหม่มี receive queue (ring buffer) หลายชุดและกระจายการแจ้งไปหลายคอร์ CPU ได้ (RSS) แต่ถ้าไม่ได้ตั้งค่า หรือทราฟฟิกไปกองที่คิวเดียว คอร์เดียวจะเต็ม 100% จนเป็นคอขวด ถ้าเป็นเซิร์ฟเวอร์บนคลาวด์ ยังมีขีดจำกัดจำนวนแพ็กเก็ตต่อวินาที แบนด์วิดท์ และจำนวนการเชื่อมต่อแยกต่างหากอยู่ด้านหน้า NIC ส่วนที่เกินจะถูกทิ้งก่อนถึงเซิร์ฟเวอร์ และไม่ปรากฏในเมตริกปกติอย่าง CPU หรือ ring buffer ถ้าเป็น AWS จะเหลือร่องรอยเฉพาะในสถิติของไดรเวอร์ ENA (pps_allowance_exceeded ฯลฯ ใน ethtool -S)

เปรียบเทียบ

NIC คือตู้จดหมายของคอนโด ring buffer คือจำนวนช่องในตู้ และอินเทอร์รัปต์คือกริ่งของบุรุษไปรษณีย์ ถ้าจดหมายหลั่งไหลเข้ามาแต่มีคนเก็บอยู่คนเดียว ช่องจะล้นจนจดหมายตกพื้น RSS คือการมีคนเก็บหลายคน

สาเหตุของแลคในชั้นนี้

อินเทอร์รัปต์ของ NIC กระจุกที่คอร์เดียว Single-queue NIC / no RSS

ถ้า NIC ส่งอินเทอร์รัปต์แจ้งว่าแพ็กเก็ตมาถึงไปที่คอร์ CPU เพียงคอร์เดียว คอร์นั้นจะกลายเป็นคอขวด

ทำไม: มี receive queue เดียว หรือปิด RSS ที่กระจายไปหลายคอร์ → ผลคือ: คอร์หนึ่งขึ้นถึง 100% จนดึงแพ็กเก็ตออกมาไม่ทัน → บนหน้าจอ: ตอนคนแห่มารวมกัน ทั้งเซิร์ฟเวอร์มีแพ็กเก็ตหายและความหน่วง (วาร์ป, อินพุตดีเลย์)

อาการ: วาร์ป, ดีดกลับ, อินพุตดีเลย์ · ผู้รับผิดชอบหลัก อินฟราเซิร์ฟเวอร์ (ทีมอินฟรา)

ring buffer ไม่พอ RX ring buffer overflow

ถ้า ring buffer ที่ NIC ใช้พักแพ็กเก็ตไว้ชั่วคราวมีขนาดเล็ก เมื่อแพ็กเก็ตทะลักเข้ามาในชั่วขณะ บัฟเฟอร์จะล้นและแพ็กเก็ตถูกทิ้ง

ทำไม: ring buffer เล็กตามค่าเริ่มต้น (256–2,048 สล็อต แล้วแต่ไดรเวอร์) → ผลคือ: ตอนเกิด burst บัฟเฟอร์ล้นก่อนที่ CPU จะดึงออกไป → บนหน้าจอ: แพ็กเก็ตหายเฉพาะตอน burst (วาร์ป, สกิลไม่ออก) ไม่มีร่องรอยใน log ของเซิร์ฟเวอร์เกม

อาการ: วาร์ป, กดไม่ติด/โรลแบ็ค · ผู้รับผิดชอบหลัก อินฟราเซิร์ฟเวอร์ (ทีมอินฟรา)

interrupt coalescing มากเกินไป Interrupt coalescing

การรวบแพ็กเก็ตไว้แล้วแจ้งทีเดียวช่วยลดภาระ CPU แต่ก็ทำให้ช้าลงตามเวลาที่ใช้รวบ

ทำไม: NIC รวบแพ็กเก็ตตามเวลาหรือจำนวนที่กำหนดแล้วจึงแจ้ง → ผลคือ: แพ็กเก็ตต้องรอระหว่างรวบ → บนหน้าจอ: ความหน่วงเพิ่มขึ้นเล็กน้อย ปกติน้อยมาก แต่ถ้าตั้งมากเกินไปจะถึงระดับ ms

อาการ: อินพุตดีเลย์ · ผู้รับผิดชอบหลัก อินฟราเซิร์ฟเวอร์ (ทีมอินฟรา)

เกินขีดจำกัด PPS ของคลาวด์ Cloud PPS / bandwidth allowance

เซิร์ฟเวอร์บนคลาวด์แต่ละประเภทมีขีดจำกัดจำนวนแพ็กเก็ตต่อวินาทีและแบนด์วิดท์ ถ้าเกินจะถูกทิ้งเงียบ ๆ

ทำไม: ผู้เล่นออนไลน์พร้อมกันเพิ่มขึ้นจนจำนวนแพ็กเก็ตต่อวินาทีเกินขีดจำกัดของอินสแตนซ์ → ผลคือ: เครือข่ายของคลาวด์ทิ้งส่วนที่เกิน → บนหน้าจอ: วาร์ปหรือสกิลไม่ออกจากแพ็กเก็ตหายที่หาสาเหตุไม่เจอ ขณะที่ CPU ของเซิร์ฟเวอร์ยังว่าง

อาการ: วาร์ป, กดไม่ติด/โรลแบ็ค · ผู้รับผิดชอบหลัก อินฟราเซิร์ฟเวอร์ (ทีมอินฟรา) · ร่วมกับ พัฒนาเซิร์ฟเวอร์ (ทีมพัฒนาเกม), ภายนอก (ภายนอก)

แบนด์วิดท์ NIC เต็ม NIC bandwidth saturation

ถ้าใช้การ์ด 1 Gbps หรือ 10 Gbps จนถึงขีดจำกัด transmit queue จะยาวขึ้น และสุดท้ายแพ็กเก็ตจะถูกทิ้ง

ทำไม: broadcast เพิ่มขึ้นจนปริมาณส่งชนขีดจำกัดของการ์ด → ผลคือ: transmit queue ยาวขึ้น และเมื่อล้นก็ทิ้ง → บนหน้าจอ: ความหน่วงและแพ็กเก็ตหายทั้งเซิร์ฟเวอร์ (อินพุตดีเลย์, วาร์ป)

อาการ: อินพุตดีเลย์, วาร์ป · ผู้รับผิดชอบหลัก พัฒนาเซิร์ฟเวอร์ (ทีมพัฒนาเกม) · ร่วมกับ อินฟราเซิร์ฟเวอร์ (ทีมอินฟรา)

โอเวอร์เฮดจาก virtualization/noisy neighbor Noisy neighbors in virtualization

ถ้า VM อื่นที่อยู่บนเครื่องจริงเครื่องเดียวกันใช้เครือข่ายหรือ CPU มาก การประมวลผลของเซิร์ฟเวอร์เราจะถูกเลื่อนออกไปอย่างไม่สม่ำเสมอ

ทำไม: VM อื่นบนเครื่องจริงเครื่องเดียวกันใช้ทรัพยากรมาก → ผลคือ: การประมวลผลแพ็กเก็ตของ VM เราล่าช้าอย่างไม่สม่ำเสมอ → บนหน้าจอ: เกิดจิตเตอร์ (ช่วงเวลาที่แพ็กเก็ตมาถึงไม่สม่ำเสมอ) เป็นครั้งคราวโดยไม่มีสาเหตุชัดเจน จนภาพกระตุก

อาการ: กระตุก · ผู้รับผิดชอบหลัก อินฟราเซิร์ฟเวอร์ (ทีมอินฟรา) · ร่วมกับ ภายนอก (ภายนอก)

การซ่อมบำรุงโฮสต์คลาวด์/live migration Cloud host maintenance / live migration

เมื่อผู้ให้บริการคลาวด์ซ่อมบำรุงเครื่องจริง (โฮสต์) จะย้าย VM ไปโฮสต์อื่น (live migration) หรือหยุด VM ชั่วคราว ระหว่างนั้นทั้งเซิร์ฟเวอร์จะหยุด และถ้าหยุดนาน การเชื่อมต่อจะขาด

ทำไม: ผู้ให้บริการย้าย VM ไปโฮสต์อื่นหรือพักการทำงานชั่วคราว เพราะซ่อมบำรุงโฮสต์หรือคาดว่าฮาร์ดแวร์จะเสีย → ผลคือ: ระหว่างย้าย CPU หน่วยความจำ และเครือข่ายช้าลง และช่วงท้าย VM จะหยุดสนิทชั่วครู่ (ไม่ถึง 1 วินาทีจนถึงราว 30 วินาที ขึ้นกับผู้ให้บริการและวิธี) → บนหน้าจอ: ทุกคนในเซิร์ฟเวอร์ค้างพร้อมกันแล้วกรอเร็วหรือวาร์ป ถ้าหยุดนานกว่าไทม์เอาต์ จะหลุดจำนวนมาก

อาการ: ค้าง, กรอเร็ว, วาร์ป, หลุด · ผู้รับผิดชอบหลัก อินฟราเซิร์ฟเวอร์ (ทีมอินฟรา) · ร่วมกับ พัฒนาเซิร์ฟเวอร์ (ทีมพัฒนาเกม), ภายนอก (ภายนอก)

ปัญหาไดรเวอร์/เฟิร์มแวร์ของ NIC NIC hang / reset

ระหว่างที่การ์ดหยุดแล้วเริ่มใหม่เพราะบั๊กของไดรเวอร์หรือฟีเจอร์ทำงานผิดพลาด การรับส่งทั้งหมดจะขาด

ทำไม: บั๊กของไดรเวอร์, ฟีเจอร์ offload ทำงานผิดพลาด → ผลคือ: NIC หยุดแล้วเริ่มใหม่ (หลายวินาที) → บนหน้าจอ: ทุกคนในเซิร์ฟเวอร์นั้นค้างพร้อมกันแล้ววาร์ปหรือหลุด

อาการ: ค้าง, หลุด · ผู้รับผิดชอบหลัก อินฟราเซิร์ฟเวอร์ (ทีมอินฟรา)

ดีเลย์จากการรอรวมแพ็กเก็ตของ GRO/LRO GRO/LRO batching

เป็นฟีเจอร์ที่รวมหลายแพ็กเก็ตเป็นก้อนเดียวเพื่อลดภาระ CPU บางการตั้งค่าทำให้แพ็กเก็ตเกมขนาดเล็กต้องรอแพ็กเก็ตถัดไปที่จะรวมด้วยสักครู่

ทำไม: NIC และเคอร์เนลรวมแพ็กเก็ตที่มาถึงแล้วประมวลผลพร้อมกัน → ผลคือ: ถ้าเปิดการรวมในฮาร์ดแวร์ (LRO) หรือตั้งเวลารอรวมไว้ จะรอแพ็กเก็ตถัดไปสักครู่ → บนหน้าจอ: ความหน่วงเพิ่มขึ้นเล็กน้อย (ส่วนใหญ่ไม่เกินหลายสิบ µs)

อาการ: อินพุตดีเลย์ · ผู้รับผิดชอบหลัก อินฟราเซิร์ฟเวอร์ (ทีมอินฟรา)

OS ของเซิร์ฟเวอร์ (เคอร์เนล)

เคอร์เนล Linux หรือ Windows ของเซิร์ฟเวอร์ทำหน้าที่รับการเชื่อมต่อ จัดการบัฟเฟอร์ซ็อกเก็ต และแบ่ง CPU กับหน่วยความจำให้โปรแกรมเซิร์ฟเวอร์เกม ค่าเริ่มต้นส่วนใหญ่เป็นค่าแบบระมัดระวังที่ตั้งให้เหมาะกับงานทั่วไป จึงมักไม่เหมาะกับเซิร์ฟเวอร์เกมที่มีคนหลายหมื่นคนเชื่อมต่ออยู่นาน ๆ

เมื่อมีการเชื่อมต่อใหม่เข้ามา เคอร์เนลจะใส่คำขอไว้ในคิวรอเชื่อมต่อ (backlog) แล้วเซิร์ฟเวอร์เกมจะรับไปทีละรายการ ถ้าคิวเต็ม Linux จะทิ้งคำขอใหม่เงียบ ๆ ส่วน Windows จะตอบกลับว่าปฏิเสธ บน Linux การเชื่อมต่อแต่ละรายการต้องใช้ file descriptor (fd) หนึ่งตัว ซึ่งเป็นหมายเลขที่ผูกกับไฟล์หรือการเชื่อมต่อที่เปิดอยู่ และจำนวน fd ที่โปรเซสหนึ่งถือได้ก็มีขีดจำกัด ทันทีหลังปิดปรับปรุง เมื่อคนหลายหมื่นคนกดปุ่มเชื่อมต่อพร้อมกัน คิวรอเชื่อมต่อและ fd จะหมดก่อนอย่างอื่น

เมื่อหน่วยความจำไม่พอ เคอร์เนลยังย้ายข้อมูลออกไปไว้บนดิสก์ (swap ถ้าเปิดไว้) และถ้าหมดจริง ๆ Linux จะเลือกโปรเซสที่ใช้หน่วยความจำมากที่สุดแล้วบังคับปิด (OOM killer) เซิร์ฟเวอร์เกมมักเป็นโปรเซสที่ใช้หน่วยความจำมากที่สุดในเครื่องนั้น จึงเป็นเป้าแรกที่ถูกปิด ถ้าตั้งขีดจำกัดหน่วยความจำให้คอนเทนเนอร์ไว้ ต่อให้ทั้งเครื่องยังมีที่ว่าง เรื่องเดียวกันก็จะเกิดทันทีที่ชนขีดจำกัด เรื่องที่ดูไม่เกี่ยวกับเกม เช่น การซิงก์เวลา (NTP), งานตามกำหนดเวลา, ขีดจำกัด CPU ของคอนเทนเนอร์ และ CPU steal ของ VM (เวลาที่ต้องรอขณะ VM อื่นใช้ CPU จริงอยู่) ก็ทำให้เซิร์ฟเวอร์หยุดชะงักเป็นพัก ๆ หรือทำให้ตัวจับเวลาคลาดเคลื่อนได้

เปรียบเทียบ

OS ของเซิร์ฟเวอร์คือประตูทางเข้าและสำนักงานของสวนสนุก ถ้าทุกคนแห่มาตอนสวนเปิด (ปิดปรับปรุงเสร็จ) แถวหน้าประตู (backlog) จะล้น และถ้าริสต์แบนด์ที่ต้องแจก (file descriptor) หมด ก็เข้าไม่ได้อีก

สาเหตุของแลคในชั้นนี้

คิวรอเชื่อมต่อ (backlog) ล้น Listen backlog / SYN queue overflow

ทันทีหลังปิดปรับปรุง ถ้าผู้เล่นหลายหมื่นคนเชื่อมต่อพร้อมกัน คิวรอเชื่อมต่อ (backlog) ของเคอร์เนลจะล้น และความพยายามเชื่อมต่อจะถูกทิ้ง

ทำไม: พอปิดปรับปรุงเสร็จ การเชื่อมต่อก็ทะลักเข้ามาเร็วกว่าที่เซิร์ฟเวอร์เกมรับด้วย accept ทัน → ผลคือ: คิวรอเชื่อมต่อของเคอร์เนล (backlog คือค่าที่น้อยกว่าระหว่างค่าที่โค้ดเซิร์ฟเวอร์ส่งให้ listen กับเพดานของเคอร์เนล) เต็ม → บนหน้าจอ: ความพยายามเชื่อมต่อถูกทิ้งและลองใหม่วนไปเรื่อย ๆ จนเข้าเกมไม่ได้/โหลดไม่จบ

อาการ: เข้าเกมไม่ได้/โหลดไม่จบ · ผู้รับผิดชอบหลัก พัฒนาเซิร์ฟเวอร์ (ทีมพัฒนาเกม) · ร่วมกับ อินฟราเซิร์ฟเวอร์ (ทีมอินฟรา), พัฒนาไคลเอนต์ (ทีมพัฒนาเกม)

ขีดจำกัด file descriptor File descriptor limit (ulimit)

ทุกการเชื่อมต่อต้องใช้ file descriptor (fd คือหมายเลขที่ OS กำหนดให้ไฟล์หรือซ็อกเก็ตที่เปิดอยู่) แต่จำนวน fd ที่หนึ่งโปรเซสเปิดได้มีจำกัด

ทำไม: จำนวนผู้เล่นออนไลน์พร้อมกันแตะขีดจำกัด file descriptor ของโปรเซส → ผลคือ: เซิร์ฟเวอร์รับการเชื่อมต่อใหม่ไม่ได้ (Too many open files) การเปิดไฟล์ log และการเชื่อมต่อ DB ก็ล้มเหลวไปด้วย → บนหน้าจอ: พอถึงจำนวนคนค่าหนึ่งพอดีก็ไม่มีใครเข้าได้อีก: เข้าเกมไม่ได้/โหลดไม่จบ

อาการ: เข้าเกมไม่ได้/โหลดไม่จบ · ผู้รับผิดชอบหลัก อินฟราเซิร์ฟเวอร์ (ทีมอินฟรา) · ร่วมกับ พัฒนาเซิร์ฟเวอร์ (ทีมพัฒนาเกม)

บัฟเฟอร์ซ็อกเก็ตของเคอร์เนลไม่พอ Small socket buffers

ถ้าบัฟเฟอร์ส่งและรับเล็ก เมื่อทราฟฟิกมาเป็น burst แพ็กเก็ตที่รับทาง UDP จะถูกทิ้ง ส่วนการส่งทาง TCP จะติดเพราะบัฟเฟอร์ไม่มีที่ว่าง

ทำไม: SO_SNDBUF และ SO_RCVBUF ใช้ค่าเริ่มต้นหรือเล็กเกินไป → ผลคือ: ระหว่าง burst หรือช่วงที่เธรดรับหยุดชั่วครู่ receive buffer ของ UDP ล้นจนแพ็กเก็ตถูกทิ้ง ส่วน TCP ต้องรอเพราะ send buffer ไม่มีที่ว่าง → บนหน้าจอ: วาร์ป (UDP แพ็กเก็ตหาย) หรือกรอเร็ว (TCP รอ)

อาการ: วาร์ป, กรอเร็ว · ผู้รับผิดชอบหลัก อินฟราเซิร์ฟเวอร์ (ทีมอินฟรา) · ร่วมกับ พัฒนาเซิร์ฟเวอร์ (ทีมพัฒนาเกม)

เธรดมากเกินไปและ context switch Thread oversubscription, context switching

ถ้ารันเธรดมากกว่าจำนวนคอร์มาก ๆ OS จะเสีย CPU ไปกับการสลับให้เธรดผลัดกันทำงานอย่างเดียว

ทำไม: มีเธรดหลายร้อยถึงหลายพันตัว เช่น สร้างเธรดหนึ่งตัวต่อหนึ่งการเชื่อมต่อ → ผลคือ: ต้นทุน context switch (การสลับเธรดที่กำลังรัน) และ cache miss เพิ่มขึ้น → บนหน้าจอ: CPU ยุ่งแต่ throughput ต่ำ ทิกไม่สม่ำเสมอ จึงกระตุกและเป็นสโลว์โมชั่น

อาการ: กระตุก, สโลว์โมชั่น · ผู้รับผิดชอบหลัก พัฒนาเซิร์ฟเวอร์ (ทีมพัฒนาเกม) · ร่วมกับ อินฟราเซิร์ฟเวอร์ (ทีมอินฟรา)

CPU steal (VM) CPU steal time

ระหว่างที่เครื่องจริง (ไฮเปอร์ไวเซอร์) ยก CPU time ของ VM ไปให้ VM ตัวอื่นชั่วครู่ (CPU steal) เซิร์ฟเวอร์เกมจะหยุดชะงัก

ทำไม: VM ตัวอื่นบนโฮสต์เดียวกันใช้ CPU มาก → ผลคือ: VM ของเราไม่ได้รับ CPU ครั้งละไม่กี่ ms ถึงหลายสิบ ms → บนหน้าจอ: เวลาต่อทิกพุ่งโดยหาสาเหตุไม่เจอ: กระตุก, ค้าง

อาการ: กระตุก, ค้าง · ผู้รับผิดชอบหลัก อินฟราเซิร์ฟเวอร์ (ทีมอินฟรา) · ร่วมกับ ภายนอก (ภายนอก)

CPU throttling ของคอนเทนเนอร์ (CFS quota) Container CPU throttling (CFS quota)

ถ้าตั้งขีดจำกัด CPU ให้คอนเทนเนอร์ เมื่อใช้โควตาหมดภายในรอบที่กำหนด (ปกติ 100 ms) คอนเทนเนอร์จะถูกบังคับหยุดตลอดเวลาที่เหลือของรอบนั้น (throttling)

ทำไม: ตั้งขีดจำกัด CPU (limit) ให้คอนเทนเนอร์เซิร์ฟเวอร์เกมไว้ เช่น ใน Kubernetes → ผลคือ: ตอนที่งานคำนวณทิกมากระจุกกัน โควตาหมดจนต้องหยุดหลายสิบ ms รอรอบถัดไป → บนหน้าจอ: CPU เฉลี่ยต่ำ แต่ทิกพุ่งเป็นรอบ: กระตุก, สโลว์โมชั่น

อาการ: กระตุก, สโลว์โมชั่น · ผู้รับผิดชอบหลัก อินฟราเซิร์ฟเวอร์ (ทีมอินฟรา) · ร่วมกับ พัฒนาเซิร์ฟเวอร์ (ทีมพัฒนาเกม)

ความหน่วงพุ่งจากการจัดการพลังงานของเซิร์ฟเวอร์ (C-state/การปรับความถี่) CPU power management latency (C-states, frequency scaling)

คอร์ CPU ที่ว่างจะเข้าโหมดประหยัดพลังงานระดับลึก (C-state) และลดความถี่ลงเพื่อประหยัดไฟ เมื่อแพ็กเก็ตหรือตัวจับเวลามาถึง ต้องใช้เวลาตื่นและเพิ่มความถี่ การประมวลผลแพ็กเก็ตเล็ก ๆ จึงมีดีเลย์เพิ่มขึ้น

ทำไม: นโยบายปรับความถี่ของ OS (governor) หรือการตั้งค่าพลังงานใน BIOS ยอมให้ใช้ C-state ระดับลึกและความถี่ต่ำ → ผลคือ: คอร์ที่ว่างอยู่ตื่นช้าสูงสุดหลายร้อย µs ทุกครั้งที่ออกจากโหมดประหยัดพลังงานระดับลึก และถ้าความถี่ถูกตรึงไว้ต่ำ การคำนวณทิกเองก็ช้าลง → บนหน้าจอ: ปกติรู้สึกได้ยาก แต่ถ้าเรียกกันระหว่างเซิร์ฟเวอร์บ่อย ดีเลย์จะสะสมเป็นอินพุตดีเลย์ที่กลับหนักขึ้นตอนเซิร์ฟเวอร์ว่าง ถ้าความถี่ถูกตรึงไว้ต่ำ ตอนคนแห่มารวมกันทิกจะตามไม่ทันจนเป็นสโลว์โมชั่น

อาการ: อินพุตดีเลย์, สโลว์โมชั่น · ผู้รับผิดชอบหลัก อินฟราเซิร์ฟเวอร์ (ทีมอินฟรา)

OOM killer Out-of-memory killer

เมื่อหน่วยความจำหมด Linux จะเลือกโปรเซสที่ใช้หน่วยความจำมากที่สุดแล้วบังคับปิด ซึ่งส่วนใหญ่คือเซิร์ฟเวอร์เกม

ทำไม: หน่วยความจำหมดเพราะรั่วหรือใช้พุ่ง หรือคอนเทนเนอร์แตะขีดจำกัดหน่วยความจำ → ผลคือ: เคอร์เนลบังคับปิดโปรเซสเซิร์ฟเวอร์เกม → บนหน้าจอ: ทุกคนในเซิร์ฟเวอร์นั้นหลุดพร้อมกัน ความคืบหน้าล่าสุดอาจโดนโรลแบ็ค

อาการ: หลุด, กดไม่ติด/โรลแบ็ค · ผู้รับผิดชอบหลัก พัฒนาเซิร์ฟเวอร์ (ทีมพัฒนาเกม) · ร่วมกับ อินฟราเซิร์ฟเวอร์ (ทีมอินฟรา)

หยุดชะงักจาก memory reclaim และ compaction Memory compaction / reclaim stalls (THP)

โปรเซสจะหยุดชะงักระหว่างที่ OS ทำ compaction หน่วยความจำเพื่อสร้างเพจขนาดใหญ่ (huge page) หรือเรียกคืนหน่วยความจำให้ว่าง (reclaim)

ทำไม: หน่วยความจำว่างเหลือน้อย หรือฟีเจอร์ huge page (THP) สั่ง compaction หน่วยความจำ → ผลคือ: เธรดที่ขอหน่วยความจำต้องรอจนการ reclaim หรือ compaction เสร็จ → บนหน้าจอ: เซิร์ฟเวอร์ค้างเป็นพัก ๆ แบบไม่สม่ำเสมอ (ไม่กี่ ms ถึงหลายร้อย ms)

อาการ: ค้าง, กระตุก · ผู้รับผิดชอบหลัก อินฟราเซิร์ฟเวอร์ (ทีมอินฟรา) · ร่วมกับ พัฒนาเซิร์ฟเวอร์ (ทีมพัฒนาเกม)

นาฬิการะบบกระโดด (NTP step) Wall-clock jump (NTP step)

ถ้านาฬิกาของเซิร์ฟเวอร์ถูกปรับไปข้างหน้าหรือถอยหลังหลายวินาทีในครั้งเดียว ตัวจับเวลาที่อิงนาฬิการะบบจะทำงานพร้อมกันรวดเดียวหรือหยุดไป

ทำไม: การซิงก์เวลาปรับนาฬิกาครั้งใหญ่ในครั้งเดียว → ผลคือ: ตัวจับเวลาทำงานรวดเดียวหรือหยุด และตัดสินไทม์เอาต์ผิด → บนหน้าจอ: บัฟและคูลดาวน์ผิดปกติ, หลุดพร้อมกันหลายคน, กรอเร็ว

อาการ: กรอเร็ว, หลุด, กดไม่ติด/โรลแบ็ค · ผู้รับผิดชอบหลัก พัฒนาเซิร์ฟเวอร์ (ทีมพัฒนาเกม) · ร่วมกับ อินฟราเซิร์ฟเวอร์ (ทีมอินฟรา)

งานตามกำหนดเวลา (scheduled job) Cron jobs (log rotation, backup, scans)

งานบีบอัด log, การสำรองข้อมูล และการสแกนความปลอดภัยที่รันเวลาเดิมทุกวันจะกิน CPU และดิสก์

ทำไม: งานของ OS เริ่มรันตามเวลาที่กำหนด → ผลคือ: แย่ง CPU และดิสก์กับเซิร์ฟเวอร์เกม → บนหน้าจอ: กระตุกและสโลว์โมชั่นในเวลาเดิม เช่น 04:00 ทุกวัน

อาการ: กระตุก, สโลว์โมชั่น · ผู้รับผิดชอบหลัก อินฟราเซิร์ฟเวอร์ (ทีมอินฟรา)

ประสิทธิภาพเปลี่ยนไปหลังอัปเดต OS/เคอร์เนล/ไดรเวอร์/เฟิร์มแวร์ Performance regression after OS / kernel / driver / firmware update

โค้ดเกมเหมือนเดิม แต่เซิร์ฟเวอร์ช้าลงตั้งแต่อัปเดต OS, เคอร์เนล, ไดรเวอร์ หรือเฟิร์มแวร์ การอัปเดตอาจเปลี่ยนค่าเริ่มต้น, scheduler, การป้องกันช่องโหว่ CPU (mitigations) และพฤติกรรมของไดรเวอร์

ทำไม: แพตช์ความปลอดภัยตามรอบหรืออิมเมจเซิร์ฟเวอร์ใหม่ทำให้เคอร์เนล ไดรเวอร์ หรือเฟิร์มแวร์เปลี่ยน → ผลคือ: ค่าเริ่มต้นหรือ scheduler เปลี่ยน หรือเปิด mitigation ตัวใหม่ งานเดิมจึงใช้ CPU time มากขึ้น และลำดับที่เธรดได้รับ CPU เปลี่ยนไป → บนหน้าจอ: เซิร์ฟเวอร์ที่เคยลื่นช้าลงนิดหน่อยตลอดเวลาตั้งแต่วันที่อัปเดต: อินพุตดีเลย์ และพอคนแห่มารวมกันก็กระตุกและเป็นสโลว์โมชั่น

อาการ: อินพุตดีเลย์, กระตุก, สโลว์โมชั่น · ผู้รับผิดชอบหลัก อินฟราเซิร์ฟเวอร์ (ทีมอินฟรา)

ตาราง conntrack ของเซิร์ฟเวอร์เต็ม conntrack table full

เมื่อตาราง connection tracking (conntrack) ที่ไฟร์วอลล์ของ Linux ใช้บันทึกทุกการเชื่อมต่อแตะขีดจำกัด แพ็กเก็ตใหม่จะถูกทิ้ง

ทำไม: การเชื่อมต่อทะลักและการเชื่อมต่อสั้น ๆ ซ้ำไปมาทำให้รายการในตารางเพิ่มขึ้น → ผลคือ: ตารางเต็ม การเชื่อมต่อใหม่และแพ็กเก็ตบางส่วนถูกทิ้ง → บนหน้าจอ: เข้าเกมไม่ได้ และวาร์ปเพราะแพ็กเก็ตหายโดยไม่รู้สาเหตุ

อาการ: เข้าเกมไม่ได้/โหลดไม่จบ, วาร์ป · ผู้รับผิดชอบหลัก อินฟราเซิร์ฟเวอร์ (ทีมอินฟรา) · ร่วมกับ พัฒนาเซิร์ฟเวอร์ (ทีมพัฒนาเกม), พัฒนาไคลเอนต์ (ทีมพัฒนาเกม)

ephemeral port หมดในการเชื่อมต่อระหว่างเซิร์ฟเวอร์ Ephemeral port exhaustion (TIME_WAIT)

ถ้าเซิร์ฟเวอร์เกมเปิดและปิดการเชื่อมต่อสั้น ๆ ไปยัง DB หรือเซิร์ฟเวอร์อื่นบ่อย ๆ การเชื่อมต่อที่ปิดแล้วจะยังถือครองพอร์ตอยู่สักพัก จนเปิดการเชื่อมต่อใหม่ไม่ได้

ทำไม: เปิดและปิดการเชื่อมต่อใหม่ทุกคำขอ → ผลคือ: ฝั่งที่ปิดก่อนจะถือครองพอร์ตไว้ประมาณ 60 วินาทีบน Linux (TIME_WAIT) จนพอร์ตที่ใช้ได้หมด → บนหน้าจอ: คำขอภายในล้มเหลว: บันทึกข้อมูลไม่สำเร็จ, ฟีเจอร์ทำงานผิดพลาด

อาการ: กดไม่ติด/โรลแบ็ค, เข้าเกมไม่ได้/โหลดไม่จบ · ผู้รับผิดชอบหลัก พัฒนาเซิร์ฟเวอร์ (ทีมพัฒนาเกม) · ร่วมกับ อินฟราเซิร์ฟเวอร์ (ทีมอินฟรา)

ซ็อกเก็ตและโปรโตคอล: TCP, UDP และ socket option

ส่วนที่กำหนดว่าเกมรับส่งข้อมูลผ่านเครือข่ายอย่างไร ต่อให้ใช้เน็ตเดียวกัน แพ็กเก็ตหายหนึ่งตัวอาจจบแค่ “สะดุดนิดเดียว” หรือกลายเป็น “ค้าง 1 วินาทีแล้วกรอเร็ว” ก็ได้ ขึ้นอยู่กับว่าใช้โปรโตคอลไหนและเปิด socket option อย่างไร

TCP รับประกันว่าจะส่งครบทุกตัวตามลำดับที่ส่ง แลกกับการที่ถ้าหายหนึ่งตัว ก็จะรอจนกว่าจะได้รับใหม่ โดยไม่ส่งตัวที่มาถึงทีหลังให้เกมเลย UDP ไม่รับประกันอะไรเลย ตัวที่มาถึงจะส่งให้ทันทีโดยไม่รอ แต่ตัวที่หาย เกมต้องจัดการเอง เกมที่เน้นแอ็กชันจึงสร้างความน่าเชื่อถือบน UDP เองเท่าที่จำเป็น (reliable UDP) ส่วน MMO จำนวนมากเลือกใช้ TCP ที่เขียนง่ายกว่าและยอมรับจุดอ่อนนั้น

socket option คือการตั้งค่ารายละเอียดของพฤติกรรมเหล่านี้ เช่น จะรวบแพ็กเก็ตเล็ก ๆ ไว้ส่งทีเดียวหรือไม่ (TCP_NODELAY), จะให้บัฟเฟอร์รับส่งใหญ่แค่ไหน (SO_SNDBUF, SO_RCVBUF), จะรู้ว่าการเชื่อมต่อตายเมื่อไร (SO_KEEPALIVE, TCP_USER_TIMEOUT) และจะทำอย่างไรกับข้อมูลที่เหลือตอนปิด (SO_LINGER) ค่าเริ่มต้นส่วนใหญ่ตั้งไว้ให้ส่งข้อมูลก้อนใหญ่ด้วยแพ็กเก็ตน้อย ๆ อย่างมีประสิทธิภาพ จึงมักเสียเปรียบสำหรับเกมที่รับส่งแพ็กเก็ตเล็ก ๆ บ่อย ๆ

ประเด็นสำคัญ

TCP ส่งข้อมูลที่ได้รับให้เกมตามลำดับที่ส่งมาเท่านั้น ถ้าแพ็กเก็ตหมายเลข 17 หาย แม้หมายเลข 18–30 จะมาถึงแล้ว ทั้งหมดก็ต้องรอจนกว่าหมายเลข 17 จะมาอีกครั้ง (HOL blocking) ส่วน UDP ส่งต่อทันทีที่มาถึง ต่อให้หมายเลข 17 ไม่มาเลย ตัวที่เหลือก็ยังถูกประมวลผลทันเวลา

การส่งซ้ำเกิดขึ้นเพราะอะไร (Wi-Fi, ความแออัด, MTU black hole, การส่งซ้ำโดยไม่จำเป็น ฯลฯ) และวิธีหาสาเหตุ อธิบายแยกตามสาเหตุไว้ในบท 06 การส่งซ้ำของ TCP

สาเหตุของแลคในชั้นนี้

TCP HOL blocking Head-of-line blocking

เพื่อรักษาลำดับข้อมูล TCP จะไม่ส่งแพ็กเก็ตที่มาถึงทีหลังให้เกม จนกว่าจะได้รับแพ็กเก็ตที่หายไปหนึ่งตัวนั้นอีกครั้ง

ทำไม: แพ็กเก็ตหายไปหนึ่งตัว → ผลคือ: แพ็กเก็ตที่ตามมามาถึงแล้ว แต่ต้องรออยู่ใน receive buffer → บนหน้าจอ: ค้างไปก่อนแล้วปล่อยออกมารวดเดียวเป็นอาการกรอเร็ว

อาการ: ค้าง, กรอเร็ว · ผู้รับผิดชอบหลัก พัฒนาเซิร์ฟเวอร์ (ทีมพัฒนาเกม) · ร่วมกับ พัฒนาไคลเอนต์ (ทีมพัฒนาเกม)

TCP RTO และ exponential backoff RTO and exponential backoff

ทุกครั้งที่การส่งซ้ำล้มเหลวอีก เวลารอจะเพิ่มเป็นสองเท่า การเชื่อมต่อที่ขาดไปแป๊บเดียวจึงกลายเป็นการหยุดยาว

ทำไม: การเชื่อมต่อขาดไปแป๊บหนึ่ง การส่งซ้ำจึงล้มเหลวติดต่อกัน → ผลคือ: เวลารอก่อนลองครั้งถัดไปเพิ่มเป็นสองเท่าทุกครั้ง เช่น 0.3 → 0.6 → 1.2 → 2.4 วินาที (ที่ปิง 100 ms) → บนหน้าจอ: เน็ตขาดแค่ 1 วินาที แต่เกมค้างเกิน 2 วินาที ถ้าขาดนานกว่านั้นสุดท้ายก็หลุด

อาการ: ค้าง, หลุด · ผู้รับผิดชอบหลัก พัฒนาเซิร์ฟเวอร์ (ทีมพัฒนาเกม) · ร่วมกับ พัฒนาไคลเอนต์ (ทีมพัฒนาเกม)

อัลกอริทึม Nagle + delayed ACK Nagle + delayed ACK (TCP_NODELAY off)

อัลกอริทึม Nagle ที่รวบรวมแพ็กเก็ตเล็กก่อนส่ง กับ delayed ACK ที่ส่ง ACK ช้า ทำงานประกบกัน ทุกครั้งที่เขียนข้อความแบ่งเป็นหลายส่วนจึงดีเลย์ 40–200 ms

ทำไม: เขียนข้อความเล็ก ๆ แบ่งเป็นหลายส่วนโดยไม่ได้เปิด TCP_NODELAY → ผลคือ: ฝั่งส่งรอ ACK ส่วนฝั่งรับส่ง ACK ช้า → บนหน้าจอ: ปิงต่ำ แต่ทุกการกระทำตอบสนองช้าสม่ำเสมอ: อินพุตดีเลย์

อาการ: อินพุตดีเลย์ · ผู้รับผิดชอบหลัก พัฒนาเซิร์ฟเวอร์ (ทีมพัฒนาเกม) · ร่วมกับ พัฒนาไคลเอนต์ (ทีมพัฒนาเกม)

การส่งแบบ blocking เพราะไคลเอนต์ช้า Blocking send on a full socket

ถ้า send buffer ของผู้เล่นคนหนึ่งที่เน็ตช้าเต็ม แล้วเซิร์ฟเวอร์ส่งแบบ blocking (การส่งที่ฟังก์ชันจะไม่คืนค่าจนกว่าบัฟเฟอร์จะมีที่ว่าง) เธรดของเซิร์ฟเวอร์จะต้องรอผู้เล่นคนนั้นคนเดียว

ทำไม: send buffer ของไคลเอนต์ที่ช้าเต็ม → ผลคือ: เป็นการส่งแบบ blocking เธรดของเซิร์ฟเวอร์จึงรอจนบัฟเฟอร์มีที่ว่าง → บนหน้าจอ: ทุกคนที่เธรดนั้นดูแลค้างหรือเป็นสโลว์โมชั่น

อาการ: ค้าง, สโลว์โมชั่น · ผู้รับผิดชอบหลัก พัฒนาเซิร์ฟเวอร์ (ทีมพัฒนาเกม)

นโยบายจัดการไคลเอนต์ที่ช้า (slow consumer) Slow-consumer policy

เมื่อข้อมูลที่ต้องส่งให้ไคลเอนต์ใดกองสะสมเรื่อย ๆ เซิร์ฟเวอร์จะทิ้งอัปเดตเก่าหรือตัดการเชื่อมต่อ

ทำไม: เน็ตของไคลเอนต์รับไม่ทันปริมาณที่เซิร์ฟเวอร์ส่ง → ผลคือ: เซิร์ฟเวอร์ทิ้งอัปเดตเก่า หรือตัดการเชื่อมต่อเมื่อเกินขีดจำกัด → บนหน้าจอ: เฉพาะคนนั้นวาร์ปหรือหลุด

อาการ: วาร์ป, หลุด · ผู้รับผิดชอบหลัก พัฒนาเซิร์ฟเวอร์ (ทีมพัฒนาเกม)

keepalive ค่าเริ่มต้น 2 ชั่วโมง TCP keepalive defaults

ถ้าอีกฝั่งหายไปโดยไม่ส่งสัญญาณปิด TCP จะรู้ตัวหลังผ่านไปนานมาก keepalive (ฟีเจอร์ของ TCP ที่ตรวจว่าการเชื่อมต่อที่ idle ยังอยู่หรือไม่) ปิดไว้เป็นค่าเริ่มต้น และต่อให้เปิด ก็ต้อง idle ครบ 2 ชั่วโมงก่อนจึงเริ่มตรวจ

ทำไม: ไคลเอนต์หายไปโดยไม่ส่งสัญญาณปิด เพราะเครื่องดับหรือเน็ตขาด → ผลคือ: เซิร์ฟเวอร์ถือว่าการเชื่อมต่อยังอยู่ (keepalive ค่าเริ่มต้น 7,200 วินาที ถ้ามีข้อมูลที่กำลังส่งอยู่ จะใช้ประมาณ 15 นาทีกว่าจะเลิกส่งซ้ำ) → บนหน้าจอ: เหลือตัวผีของตัวละครอยู่ในเกม และพอเชื่อมต่อใหม่ก็เจอข้อผิดพลาด “ล็อกอินอยู่แล้ว”

อาการ: เข้าเกมไม่ได้/โหลดไม่จบ, มองไม่เห็น/ตัวผี · ผู้รับผิดชอบหลัก พัฒนาเซิร์ฟเวอร์ (ทีมพัฒนาเกม) · ร่วมกับ พัฒนาไคลเอนต์ (ทีมพัฒนาเกม), อินฟราเซิร์ฟเวอร์ (ทีมอินฟรา)

IP fragmentation ของแพ็กเก็ต UDP IP fragmentation of large UDP

แพ็กเก็ต UDP ที่ใหญ่กว่า MTU (ขนาดสูงสุดที่ส่งได้ในครั้งเดียว) จะถูกแบ่งเป็นชิ้น (fragmentation) ที่ชั้น IP และถ้า fragment หายไปแค่ชิ้นเดียว ทั้งแพ็กเก็ตจะถูกทิ้ง

ทำไม: สแนปช็อตในที่ที่คนเยอะมีขนาดเกิน 1,500 ไบต์ → ผลคือ: ถูกแบ่งเป็นหลาย fragment ตอนส่ง ถ้าหายแม้แต่ชิ้นเดียวก็ทิ้งทั้งหมด → บนหน้าจอ: ยิ่งแพ็กเก็ตใหญ่ อัตราแพ็กเก็ตหายยิ่งสูงขึ้นหลายเท่า วาร์ปเฉพาะในที่ที่คนเยอะ

อาการ: วาร์ป · ผู้รับผิดชอบหลัก พัฒนาเซิร์ฟเวอร์ (ทีมพัฒนาเกม)

การตั้งค่าการส่งซ้ำของ reliable UDP Reliable-UDP tuning (KCP, ENet…)

ถ้ากฎการส่งซ้ำที่สร้างเองบน UDP ระมัดระวังเกินไป การกู้คืนจะช้า ถ้าดุดันเกินไปก็จะยิ่งทำให้เน็ตอุดตัน

ทำไม: ช่วงห่าง จำนวนครั้ง และขนาด window ของการส่งซ้ำไม่เหมาะกับเน็ต → ผลคือ: กู้คืนช้า หรือส่งซ้ำซ้อนจนความแออัดแย่ลง → บนหน้าจอ: สกิลไม่ออก, กรอเร็ว, แลคหนักขึ้นตอนเครือข่ายแออัด

อาการ: กดไม่ติด/โรลแบ็ค, กรอเร็ว · ผู้รับผิดชอบหลัก พัฒนาเซิร์ฟเวอร์ (ทีมพัฒนาเกม) · ร่วมกับ พัฒนาไคลเอนต์ (ทีมพัฒนาเกม)

slow start หลัง idle Slow start after idle

เมื่อการเชื่อมต่อ idle ไปสักพัก TCP จะลด congestion window (ปริมาณที่ส่งได้ในครั้งเดียว) กลับลงมา พอต้องส่งข้อมูลก้อนใหญ่ขึ้นมากะทันหันจึงต้องแบ่งส่งหลายรอบ

ทำไม: ส่งข้อมูลก้อนใหญ่ผ่านการเชื่อมต่อที่ idle อยู่ เช่น ตอนเข้าเมือง → ผลคือ: congestion window ลดลงแล้ว จึงต้องแบ่งส่งเป็นหลายรอบเวลาไปกลับ → บนหน้าจอ: หลังเข้าพื้นที่ ตัวละครและ NPC รอบตัวโผล่ช้าไปเท่ากับเวลาไปกลับไม่กี่รอบ (ยิ่งเซิร์ฟเวอร์อยู่ไกลยิ่งเห็นชัด)

อาการ: อินพุตดีเลย์, มองไม่เห็น/ตัวผี · ผู้รับผิดชอบหลัก อินฟราเซิร์ฟเวอร์ (ทีมอินฟรา) · ร่วมกับ พัฒนาเซิร์ฟเวอร์ (ทีมพัฒนาเกม)

อัตราการส่งดิ่งลงเพราะ congestion control Congestion control backoff

TCP มองว่าแพ็กเก็ตหายคือสัญญาณของความแออัด จึงลดอัตราการส่งลง 30–50% และตอบสนองแบบเดียวกันแม้แพ็กเก็ตหายเพราะ Wi-Fi

ทำไม: มีแพ็กเก็ตหายเล็กน้อยบน Wi-Fi หรือเน็ตตอนที่มีข้อมูลต้องส่งมาก → ผลคือ: TCP ลดอัตราการส่งลงมากแล้วค่อย ๆ ฟื้นตัว (CUBIC ซึ่งเป็นค่าเริ่มต้นของ Linux และ Windows ลด 30%) → บนหน้าจอ: ในที่ที่คนเยอะ อัปเดตตามไม่ทัน: กรอเร็ว, อินพุตดีเลย์

อาการ: กรอเร็ว, อินพุตดีเลย์ · ผู้รับผิดชอบหลัก อินฟราเซิร์ฟเวอร์ (ทีมอินฟรา) · ร่วมกับ พัฒนาเซิร์ฟเวอร์ (ทีมพัฒนาเกม)

ข้อมูลสุดท้ายหายเพราะปิดการเชื่อมต่อแบบบังคับ (RST) SO_LINGER, abrupt RST

ถ้าเซิร์ฟเวอร์ตัดการเชื่อมต่อแบบกะทันหัน ข้อความแจ้งสุดท้ายหรือสัญญาณว่าบันทึกข้อมูลเสร็จที่ส่งออกไปจะหายไป

ทำไม: เซิร์ฟเวอร์ปิดการเชื่อมต่อแบบบังคับ (RST) เกิดเมื่อตั้ง SO_LINGER เป็น 0 วินาที หรือปิดทั้งที่ยังอ่านข้อมูลที่รับมาไม่หมด → ผลคือ: เหตุผลที่ถูกเตะออก (kick) และข้อมูลสุดท้ายที่ยังส่งไม่ถึงถูกทิ้ง → บนหน้าจอ: ขึ้นข้อความ “การเชื่อมต่อถูกตัดเนื่องจากข้อผิดพลาดที่ไม่ทราบสาเหตุ” โดยไม่รู้เหตุผล

อาการ: หลุด · ผู้รับผิดชอบหลัก พัฒนาเซิร์ฟเวอร์ (ทีมพัฒนาเกม)

โครงสร้างแบบ blocking I/O Blocking I/O model

โครงสร้างที่เธรดทำอย่างอื่นไม่ได้ระหว่างรอซ็อกเก็ตตัวเดียว จะทำให้ทั้งระบบช้าลงเมื่อคนเพิ่มขึ้น

ทำไม: ใช้วิธีที่เธรดรอการอ่านและเขียนของแต่ละการเชื่อมต่อ → ผลคือ: ดีเลย์ของการเชื่อมต่อหนึ่งลามไปยังการเชื่อมต่ออื่นในเธรดเดียวกัน → บนหน้าจอ: ยิ่งผู้เล่นออนไลน์เพิ่มขึ้น ทุกคนยิ่งเจอสโลว์โมชั่นและอินพุตดีเลย์

อาการ: สโลว์โมชั่น, อินพุตดีเลย์ · ผู้รับผิดชอบหลัก พัฒนาเซิร์ฟเวอร์ (ทีมพัฒนาเกม)

การกระจายของ SO_REUSEPORT ไม่สมดุล SO_REUSEPORT imbalance, stuck worker

เมื่อหลายโปรเซสแบ่งกันรับพอร์ตเดียวกัน เคอร์เนลจะกำหนดโปรเซสที่รับแต่ละการเชื่อมต่อด้วย hash ของที่อยู่ และไม่เปลี่ยนอีก ถ้าโปรเซสตัวใดหยุด เฉพาะคนที่ถูกจัดให้โปรเซสนั้นจะต้องรอ

ทำไม: เกตเวย์หรือเซิร์ฟเวอร์ล็อกอินรันหลายโปรเซสด้วย SO_REUSEPORT → ผลคือ: แม้โปรเซสหนึ่งหยุดเพราะ GC หรือโหลดเกิน การเชื่อมต่อใหม่และแพ็กเก็ต UDP ที่ถูกจัดให้โปรเซสนั้นก็ไม่ย้ายไปโปรเซสอื่น → บนหน้าจอ: บางคนเท่านั้นที่เข้าเกมไม่ได้หรือค้าง ตอนรีสตาร์ตที่จำนวนโปรเซสเปลี่ยน เซสชัน UDP บางส่วนจะหลุด

อาการ: เข้าเกมไม่ได้/โหลดไม่จบ, ค้าง, หลุด · ผู้รับผิดชอบหลัก พัฒนาเซิร์ฟเวอร์ (ทีมพัฒนาเกม) · ร่วมกับ อินฟราเซิร์ฟเวอร์ (ทีมอินฟรา)

ข้อผิดพลาด WSAECONNRESET บนซ็อกเก็ต UDP ของ Windows WSAECONNRESET on a Windows UDP socket

ถ้าเซิร์ฟเวอร์ Windows ส่ง UDP ไปหาไคลเอนต์ที่ออกไปแล้ว จะได้ข้อความแจ้ง “ไม่พบพอร์ต” (ICMP) กลับมา ข้อความนั้นทำให้การเรียกรับครั้งถัดไปจบด้วยข้อผิดพลาด และถ้าโค้ดเซิร์ฟเวอร์ถือว่าข้อผิดพลาดนี้คือซ็อกเก็ตเสีย ทุกคนที่ใช้ซ็อกเก็ตนั้นจะได้รับผลกระทบ

ทำไม: ยังส่ง UDP ไปยังที่อยู่ของไคลเอนต์ที่เพิ่งออกไปเรื่อย ๆ จนได้ข้อความแจ้ง “ไม่พบพอร์ต” (ICMP) กลับมา → ผลคือ: Windows ทำให้การเรียกรับครั้งถัดไปจบด้วยข้อผิดพลาด WSAECONNRESET (10054) แล้วโค้ดเซิร์ฟเวอร์หยุดรับหรือปิดซ็อกเก็ต → บนหน้าจอ: ทุกคนที่ใช้ซ็อกเก็ตนั้นค้างหรือหลุดพร้อมกัน

อาการ: หลุด, ค้าง · ผู้รับผิดชอบหลัก พัฒนาเซิร์ฟเวอร์ (ทีมพัฒนาเกม)

โปรเซสเกมฝั่งเซิร์ฟเวอร์: ทิกและเธรด

โปรแกรมที่คำนวณลอจิกของเกมจริง ๆ การเคลื่อนที่ การต่อสู้ AI ของมอนสเตอร์ การคำนวณระยะมองเห็น และ broadcast ต้องเสร็จภายใน “ทิก” เดียว ยิ่งคนมารวมกันที่จุดเดียวมากเท่าไร การคำนวณระยะมองเห็นและแพ็กเก็ตที่ต้องส่งก็เพิ่มขึ้นเป็นกำลังสองของจำนวนคน

เซิร์ฟเวอร์คำนวณสถานะเกมตามช่วงห่างระหว่างทิกที่กำหนด ถ้าเป็นเซิร์ฟเวอร์ 20 ทิก คือทุก 50 ms ภายในเวลานั้นต้องใช้อินพุตของผู้เล่นทุกคน ขยับมอนสเตอร์ คำนวณว่าใครมองเห็นใคร (ระยะมองเห็น, AOI) แล้วส่งสิ่งที่เปลี่ยนไปให้ทุกคนที่มองเห็น 50 ms นี้คืองบเวลาต่อทิก ถ้าเกินงบ ทิกถัดไปจะช้าลง เซิร์ฟเวอร์ที่เดินเวลาในเกมเท่าเดิมทุกทิก เวลาทั้งหมดในเกมจะไหลช้าลง (สโลว์โมชั่น) ส่วนเซิร์ฟเวอร์ที่เดินเวลารวดเดียวตามเวลาจริงที่ผ่านไป จะรักษาความเร็วไว้ได้ แต่แพ็กเก็ตจะห่างขึ้นจนภาพกระตุกและวาร์ป ไม่ว่าแบบไหน การตอบสนองก็ช้าลง ถ้าเธรดเกมเธรดเดียวดูแลทั้งเซิร์ฟเวอร์ (แชนแนล) ทุกคนในเซิร์ฟเวอร์นั้นจะเจอพร้อมกัน ถ้าแยกเธรดตามพื้นที่ คนในพื้นที่นั้นจะเจอด้วยกัน

ปัญหาคือจำนวนคน ถ้าเทียบกันทุกคู่ 100 คนต้องตรวจราว 1 หมื่นครั้ง และ 1,000 คนต้องตรวจราว 1 ล้านครั้งในทุกทิก เซิร์ฟเวอร์จึงแบ่งแมพเป็นตาราง (grid) แล้วเทียบเฉพาะเซลล์ที่อยู่ใกล้กัน แต่เมื่อทุกคนมารวมกันใกล้เซลล์เดียว อย่างเวิลด์บอส ศึกชิงปราสาท หรืออีเวนต์ที่ลานกลางเมือง ตารางก็ช่วยได้น้อยลง ปริมาณการคำนวณและข้อมูลที่ต้องส่งจะพุ่งขึ้นมาก ถ้ายังมีล็อกที่หลายเธรดรอข้อมูลเดียวกัน และ synchronous call ที่รอคำตอบจาก DB กลางทิกเพิ่มเข้ามา ระหว่างที่รอ ทุกคนที่เธรดนั้นดูแลจะหยุดชะงักไปพร้อมกัน

เปรียบเทียบ

ทิกของเซิร์ฟเวอร์คือจังหวะของวาทยกร ยิ่งนักดนตรีในวง (ผู้เล่น) มากขึ้น โน้ตที่ต้องดูแลในหนึ่งจังหวะก็ยิ่งมาก และถ้าพลาดจังหวะ ทั้งเพลงจะช้าลง ถ้ามีใครไปหาโน้ตหนึ่งแผ่นที่ห้องเก็บของ (DB) ทุกคนก็ต้องรอคนนั้น

สาเหตุของแลคในชั้นนี้

ทิกเกินงบเวลา Tick overrun

ถ้างานในหนึ่งทิกเกินงบเวลา รอบทิกของเซิร์ฟเวอร์จะยืดออก และทั้งพื้นที่จะช้าลงหรือกระตุก

ทำไม: งานในหนึ่งทิก (เช่น 50 ms) เกินงบเวลา → ผลคือ: สถานะเกมที่ควรคำนวณ 20 ครั้งใน 1 วินาที ถูกคำนวณแค่ 8 ครั้ง → บนหน้าจอ: ทั้งพื้นที่เป็นสโลว์โมชั่น (แล้วแต่การออกแบบเซิร์ฟเวอร์ อาจเป็นอาการกระตุก), สกิลตอบสนองช้า

อาการ: สโลว์โมชั่น, อินพุตดีเลย์, กระตุก · ผู้รับผิดชอบหลัก พัฒนาเซิร์ฟเวอร์ (ทีมพัฒนาเกม) · ร่วมกับ อินฟราเซิร์ฟเวอร์ (ทีมอินฟรา)

การคำนวณระยะมองเห็น (AOI) พุ่ง (N²) Area-of-interest explosion

ถ้าเปรียบเทียบทุกคนกับทุกคนเพื่อหาว่าใครมองเห็นใคร เมื่อจำนวนคนเพิ่มเป็น 10 เท่า การคำนวณจะเพิ่มเป็น 100 เท่า

ทำไม: เทียบระยะห่างระหว่างตัวละครทุกตัว หรือแม้แบ่งเป็นตาราง (grid) แล้วก็ยังมีคนหลายร้อยคนกระจุกรอบเซลล์เดียว → ผลคือ: 100 คนเทียบประมาณ 1 หมื่นครั้ง 1,000 คนเทียบประมาณ 1 ล้านครั้ง → บนหน้าจอ: ในที่ที่คนกระจุกตัว เช่น เวิลด์บอสหรือศึกชิงปราสาท ทิกพุ่ง: สโลว์โมชั่น, กระตุก

อาการ: สโลว์โมชั่น, กระตุก · ผู้รับผิดชอบหลัก พัฒนาเซิร์ฟเวอร์ (ทีมพัฒนาเกม)

ปริมาณ broadcast พุ่ง Broadcast fan-out (N×N)

ถ้าส่งการเคลื่อนไหวของผู้เล่นหนึ่งคนไปให้ทุกคนที่มองเห็น จะเกิดอัปเดตที่ต้องส่งเท่ากับจำนวนคนที่รวมตัวยกกำลังสอง

ทำไม: ส่งการเปลี่ยนแปลงของผู้เล่นหนึ่งคนไปให้ทุกคนที่มองเห็น → ผลคือ: ถ้า 1,000 คนมองเห็นกันทั้งหมด จะมีอัปเดต 1 ล้านรายการทุกทิก → บนหน้าจอ: คิวส่งและแบนด์วิดท์เต็ม เกิดดีเลย์และแพ็กเก็ตหาย (อินพุตดีเลย์, กรอเร็ว, วาร์ป)

อาการ: อินพุตดีเลย์, วาร์ป, กรอเร็ว · ผู้รับผิดชอบหลัก พัฒนาเซิร์ฟเวอร์ (ทีมพัฒนาเกม) · ร่วมกับ อินฟราเซิร์ฟเวอร์ (ทีมอินฟรา)

พื้นที่ที่รันบนเธรดเดียวโหลดเกิน (hotspot) Single-threaded hot zone

ในโครงสร้างที่แต่ละพื้นที่มีเธรดดูแลตัวเดียว ถ้าคนแห่ไปที่จุดเดียว คอร์ตัวนั้นตัวเดียวจะขึ้นไป 100%

ทำไม: เธรดหนึ่งตัวดูแลหนึ่งพื้นที่ (แชนแนล) → ผลคือ: ถ้าคนแห่ไปที่จุดเดียว คอร์นั้นเต็มอยู่ตัวเดียว ส่วนคอร์อื่นยังว่าง → บนหน้าจอ: แลคเฉพาะพื้นที่นั้น พื้นที่อื่นปกติ

อาการ: สโลว์โมชั่น, อินพุตดีเลย์ · ผู้รับผิดชอบหลัก พัฒนาเซิร์ฟเวอร์ (ทีมพัฒนาเกม) · ร่วมกับ อินฟราเซิร์ฟเวอร์ (ทีมอินฟรา)

การแย่งล็อก Lock contention

ถ้าหลายเธรดรอล็อกตัวเดียวเพื่อเขียนข้อมูลเดียวกัน ต่อให้เพิ่มเธรด ก็ทำงานได้ทีละตัวเท่านั้น

ทำไม: หลายเธรดใช้ข้อมูลที่ใช้ร่วมกันพร้อมกัน เช่น ตลาดประมูลหรือคลังกิลด์ → ผลคือ: เธรดอื่นต้องรอจนเธรดที่ถือล็อกทำงานเสร็จ → บนหน้าจอ: ช้าเฉพาะบางฟีเจอร์ ถ้าหนัก ทิกทั้งหมดก็ช้าไปด้วย

อาการ: อินพุตดีเลย์, ค้าง · ผู้รับผิดชอบหลัก พัฒนาเซิร์ฟเวอร์ (ทีมพัฒนาเกม)

เดดล็อก Deadlock

ถ้าสองเธรดต่างรอล็อกที่อีกฝ่ายถืออยู่ ทั้งคู่จะหยุดไปตลอดกาล

ทำไม: เธรด A ถือล็อก 1 แล้วรอล็อก 2 ส่วน B ถือล็อก 2 แล้วรอล็อก 1 → ผลคือ: ทั้งคู่หยุดไปตลอดกาล และเธรดที่เกี่ยวข้องก็หยุดตามกันเป็นทอด ๆ → บนหน้าจอ: ทั้งเซิร์ฟเวอร์ค้าง และเมื่อ watchdog รีสตาร์ต ทุกคนก็หลุด

อาการ: ค้าง, หลุด · ผู้รับผิดชอบหลัก พัฒนาเซิร์ฟเวอร์ (ทีมพัฒนาเกม)

synchronous call บนเธรดเกม Synchronous DB / file I/O on the game loop

ถ้ารอการตอบกลับจาก DB หรือการเขียนไฟล์กลางทิก การดำเนินเกมทั้งหมดบนเซิร์ฟเวอร์จะหยุดไปเท่ากับเวลานั้น

ทำไม: ทิกต้องรอการอ่านหรือบันทึก DB, การเขียน log หรือการเรียก API ภายนอก → ผลคือ: ถ้า DB ใช้ 100 ms ทิกก็หยุด 100 ms → บนหน้าจอ: ทุกครั้งที่ DB หรือดิสก์ช้าลง ทั้งฟิลด์จะหยุดแวบ

อาการ: ค้าง, กระตุก · ผู้รับผิดชอบหลัก พัฒนาเซิร์ฟเวอร์ (ทีมพัฒนาเกม)

ข้อความสะสมในคิว Mailbox / job queue backlog

ถ้าคำขอเข้ามาเร็วกว่าความเร็วในการประมวลผลจนกองในคิว คำขอที่อยู่ท้ายคิวจะถูกประมวลผลอีกหลายวินาทีต่อมาหรือถูกทิ้ง

ทำไม: คำขอมาถึงเร็วกว่าความเร็วในการประมวลผล → ผลคือ: คิวยาวขึ้น และถ้าเกินขีดจำกัดก็ทิ้ง → บนหน้าจอ: สกิลและเทรดตอบสนองช้า หรือกดไม่ติด

อาการ: อินพุตดีเลย์, กดไม่ติด/โรลแบ็ค · ผู้รับผิดชอบหลัก พัฒนาเซิร์ฟเวอร์ (ทีมพัฒนาเกม)

ตัวจับเวลาทำงานพร้อมกันจำนวนมาก Synchronized timers

ถ้าการรีสปอนของมอนสเตอร์ทั้งหมด การหมดอายุของบัฟทั้งหมด และของรางวัลตอนตรงชั่วโมงมารวมในทิกเดียวกัน ทิกนั้นจะหนักขึ้นหลายสิบเท่า

ทำไม: ตัวจับเวลาของการรีสปอน การหมดอายุ ของรางวัล และการบันทึกอัตโนมัติถูกตั้งไว้เวลาเดียวกัน → ผลคือ: ทิกนั้นทิกเดียวมีงานมากกว่าปกติหลายสิบเท่า → บนหน้าจอ: หยุดแวบหนึ่งครั้งทุกเวลาที่กำหนด

อาการ: ค้าง, กระตุก · ผู้รับผิดชอบหลัก พัฒนาเซิร์ฟเวอร์ (ทีมพัฒนาเกม)

การคำนวณ pathfinding พุ่ง Pathfinding storms

ถ้ามอนสเตอร์หลายร้อยตัวไล่ตามผู้เล่นและคำนวณเส้นทางพร้อมกัน จะกิน CPU มาก

ทำไม: มอนสเตอร์จำนวนมากไล่ตามพร้อมกันจากการลากมอนหรือการ spawn ครั้งใหญ่ → ผลคือ: มอนสเตอร์แต่ละตัวคำนวณ pathfinding ของตัวเอง → บนหน้าจอ: สโลว์โมชั่นเฉพาะในจุดฟาร์มนั้น

อาการ: สโลว์โมชั่น · ผู้รับผิดชอบหลัก พัฒนาเซิร์ฟเวอร์ (ทีมพัฒนาเกม)

ต้นทุนของ serialization และการบีบอัด Serialization / compression cost

การแปลงข้อมูลที่จะส่งเป็นไบต์และบีบอัดก็ใช้ CPU เช่นกัน และเมื่อคนเยอะ ต้นทุนนี้จะพุ่งสูง

ทำไม: แปลง struct เป็นไบต์และบีบอัดทุกครั้งที่อัปเดต → ผลคือ: ต้นทุนเพิ่มตามจำนวนคนยกกำลังสอง → บนหน้าจอ: การส่งช้าลงจนเกิดอินพุตดีเลย์

อาการ: อินพุตดีเลย์ · ผู้รับผิดชอบหลัก พัฒนาเซิร์ฟเวอร์ (ทีมพัฒนาเกม)

เซิร์ฟเวอร์แครช Server process crash

ถ้าโปรเซสเซิร์ฟเวอร์ตายเพราะข้อผิดพลาดที่ไม่ได้จัดการ ทุกคนในเซิร์ฟเวอร์นั้นจะหลุดพร้อมกัน

ทำไม: ข้อผิดพลาดร้ายแรง เช่น อ้างถึงสิ่งที่ไม่มีอยู่ (null reference), ข้อมูลผิดพลาด หรือหน่วยความจำไม่พอ → ผลคือ: โปรเซสเซิร์ฟเวอร์ (หรือโซน) จบการทำงาน → บนหน้าจอ: ทุกคนหลุดพร้อมกัน ความคืบหน้าหลังการบันทึกครั้งล่าสุดอาจโดนโรลแบ็ค

อาการ: หลุด, กดไม่ติด/โรลแบ็ค · ผู้รับผิดชอบหลัก พัฒนาเซิร์ฟเวอร์ (ทีมพัฒนาเกม) · ร่วมกับ อินฟราเซิร์ฟเวอร์ (ทีมอินฟรา)

thread pool หมด Thread pool starvation

ถ้า worker thread ที่ประมวลผลงานติดอยู่กับงานช้าทั้งหมด คำขอใหม่จะต้องรอไปเรื่อย ๆ อย่างไม่มีกำหนด

ทำไม: worker thread ติดรอการตอบกลับจาก API ภายนอกหรือ DB → ผลคือ: ไม่มีเธรดว่างรับคำขอใหม่ → บนหน้าจอ: บางฟีเจอร์ เช่น ล็อกอินหรือร้านค้า โหลดไม่จบ

อาการ: เข้าเกมไม่ได้/โหลดไม่จบ, อินพุตดีเลย์, ค้าง · ผู้รับผิดชอบหลัก พัฒนาเซิร์ฟเวอร์ (ทีมพัฒนาเกม)

ลูปไม่รู้จบ/ลอจิกทำงานไม่หยุด Infinite loop / runaway logic

ถ้าบั๊กทำให้ทิกหนึ่งไม่จบ เซิร์ฟเวอร์จะหยุด และ watchdog จะบังคับรีสตาร์ต

ทำไม: ลูปไม่จบเพราะเงื่อนไขผิด หรือ recursion ไม่หยุด → ผลคือ: ทิกไม่จบ เซิร์ฟเวอร์จึงหยุด → บนหน้าจอ: ค้างแล้วทุกคนก็หลุด

อาการ: ค้าง, หลุด · ผู้รับผิดชอบหลัก พัฒนาเซิร์ฟเวอร์ (ทีมพัฒนาเกม)

การต่อสู้ที่รุมเป้าหมายเดียว (เวิลด์บอส) Hot entity / combat event fan-out

ถ้าคนหลายร้อยคนตีบอสตัวเดียวพร้อมกัน การคำนวณของบอสตัวนั้นจะกระจุกที่จุดเดียว และข้อมูลการโจมตีจะถูกส่งให้ทุกคนที่มองเห็น

ทำไม: คนหลายร้อยคนใช้สกิล บัฟ และดีบัฟใส่บอสตัวเดียวไม่หยุด → ผลคือ: การคำนวณ HP รายการ aggro และดีบัฟของบอสกระจุกที่จุดเดียว และทุกครั้งที่โจมตี จะส่งแพ็กเก็ตตัวเลขดาเมจและเอฟเฟกต์ให้ทุกคนที่มองเห็น → บนหน้าจอ: สกิลเข้าช้า ตัวเลขดาเมจขึ้นมารวดเดียว และเป็นสโลว์โมชั่นเฉพาะรอบบอส

อาการ: อินพุตดีเลย์, กรอเร็ว, สโลว์โมชั่น · ผู้รับผิดชอบหลัก พัฒนาเซิร์ฟเวอร์ (ทีมพัฒนาเกม)

spawn ทะลักเมื่อเข้าพื้นที่คนหนาแน่น Spawn burst when entering a crowd

ถ้าเทเลพอร์ตเข้าเมืองที่คนแน่น เซิร์ฟเวอร์ต้องส่งรูปลักษณ์ อุปกรณ์ และสถานะของคนหลายร้อยคนที่เพิ่งมองเห็นไปพร้อมกันรวดเดียว

ทำไม: โผล่ในที่ที่คนเยอะทันทีจากการเทเลพอร์ต ล็อกอิน หรือย้ายแชนแนล → ผลคือ: สร้างข้อมูลทั้งหมดของคนหลายร้อยคนแล้วส่งในครั้งเดียว และ PC ของเราก็โหลดทั้งหมดในครั้งเดียว → บนหน้าจอ: ค้างไปชั่วครู่หลังมาถึง, ตัวละครทยอยโผล่ช้าทีละตัว, อินพุตดีเลย์

อาการ: ค้าง, อินพุตดีเลย์, กรอเร็ว · ผู้รับผิดชอบหลัก พัฒนาเซิร์ฟเวอร์ (ทีมพัฒนาเกม) · ร่วมกับ พัฒนาไคลเอนต์ (ทีมพัฒนาเกม)

เอนทิตีสะสม (ไอเทม/ซัมมอนที่ไม่ถูกเก็บกวาด) Entity / timer buildup over uptime

ถ้าไอเทมที่ตกบนพื้นซึ่งควรหายไปแล้ว ซัมมอน และตัวจับเวลาที่จบแล้วไม่ถูกเก็บกวาดจนกองสะสม ยิ่งเปิดเซิร์ฟเวอร์ไว้นาน งานในแต่ละทิกก็ยิ่งเพิ่ม

ทำไม: ไอเทมบนพื้น ซัมมอน ตัวจับเวลาที่หมดอายุ และข้อมูลปาร์ตี้ที่ว่างไม่ถูกลบตามเวลา → ผลคือ: รายการที่ต้องไล่ทุกทิกยาวขึ้นทุกวัน → บนหน้าจอ: ทันทีหลังปิดปรับปรุงยังปกติ แต่ผ่านไปไม่กี่วัน เฉพาะเซิร์ฟเวอร์หรือพื้นที่นั้นก็ค่อย ๆ อืดขึ้นเรื่อย ๆ

อาการ: สโลว์โมชั่น, กระตุก, อินพุตดีเลย์ · ผู้รับผิดชอบหลัก พัฒนาเซิร์ฟเวอร์ (ทีมพัฒนาเกม)

แพตช์ทำให้รูปแบบทราฟฟิกเปลี่ยน Patch changes traffic pattern

ถ้าคอนเทนต์ เอฟเฟกต์ หรือรายการที่ต้องซิงก์ใหม่ทำให้ขนาดและความถี่ของแพ็กเก็ตเพิ่ม เซิร์ฟเวอร์ที่เคยลื่นจะเริ่มชนขีดจำกัด MTU แบนด์วิดท์ และจำนวนแพ็กเก็ตหลังลงแพตช์

ทำไม: แพตช์เพิ่มเอฟเฟกต์สกิล รายการที่ต้องซิงก์ หรือข้อมูลไอเทมใหม่ แพ็กเก็ตจึงใหญ่ขึ้นหรือถี่ขึ้น → ผลคือ: แพ็กเก็ตใหญ่เกิน MTU จนถูกแบ่งชิ้น และปริมาณที่เพิ่มขึ้นไปชนแบนด์วิดท์ ขีดจำกัด PPS ของคลาวด์ และ send buffer → บนหน้าจอ: ตั้งแต่ลงแพตช์ ที่ที่คนเยอะมีอาการวาร์ป สกิลไม่ออก และอินพุตดีเลย์ ฝั่งอินฟราไม่ได้เปลี่ยนอะไร แต่แพ็กเก็ตหายเพิ่มขึ้น

อาการ: วาร์ป, กดไม่ติด/โรลแบ็ค, อินพุตดีเลย์ · ผู้รับผิดชอบหลัก พัฒนาเซิร์ฟเวอร์ (ทีมพัฒนาเกม) · ร่วมกับ อินฟราเซิร์ฟเวอร์ (ทีมอินฟรา), อินฟราเครือข่าย (ทีมอินฟรา)

หน่วยความจำ

ทุกอย่างที่เซิร์ฟเวอร์จำไว้ ทั้งตัวละคร มอนสเตอร์ ไอเทม และแมพ อยู่ในหน่วยความจำ หน่วยความจำเองเร็ว แต่จะเกิดแลคทันทีที่หยุดเพราะ GC (การเก็บคืนหน่วยความจำที่ไม่ใช้แล้ว), รั่วไหลทีละน้อย (memory leak) หรือไม่พอจนเกิด swap (การย้ายหน่วยความจำบางส่วนไปไว้บนดิสก์)

ภาษาที่จัดการหน่วยความจำอัตโนมัติอย่าง Java, C# และ Go จะมี garbage collector (GC) คอยรวบรวมหน่วยความจำที่ใช้แล้วทิ้งกลับคืนมา บางวิธีของ GC จะหยุดทุกเธรดชั่วครู่ ถ้า GC ทั้ง heap (พื้นที่หน่วยความจำที่โปรแกรมขอจัดสรรมาใช้ระหว่างรัน) ในครั้งเดียว ยิ่งมีข้อมูลที่ยังใช้อยู่มากก็ยิ่งนาน จนถึงหลายร้อย ms หรือหลายวินาที GC รุ่นใหม่อย่าง ZGC ลดการหยุดลงเหลือต่ำกว่า 1 ms แลกกับการใช้ CPU และหน่วยความจำมากขึ้น เซิร์ฟเวอร์ C++ ไม่มี GC แต่ต้องเจอกับหน่วยความจำรั่วที่หน่วยความจำซึ่งลืมคืนสะสมขึ้นเรื่อย ๆ และ fragmentation ที่พื้นที่ว่างแตกเป็นชิ้นเล็ก ๆ จนใช้ก้อนใหญ่ไม่ได้ ถึงจะมี GC ถ้าอ็อบเจกต์ที่ใช้เสร็จแล้วยังถูกอ้างอิงอยู่ที่ใดที่หนึ่ง ก็รั่วได้เหมือนกัน

อีกเหตุผลที่หน่วยความจำช้าลงคือลำดับชั้นหน่วยความจำ (อุปกรณ์เก็บข้อมูลอยู่ห่างจาก CPU แค่ไหน) แคชที่อยู่ติด CPU ใช้ 1 ns, RAM ใช้ 100 ns และการอ่านหน่วยความจำที่ถูกย้ายไปไว้บนดิสก์ (swap) กลับมา ใช้เวลามากกว่า RAM เกิน 1,000 เท่า ลองดูตาราง “ตัวเลขที่ควรรู้” ด้านล่างที่ยืดความต่างนี้ให้เป็นเวลาในสเกลของคน

เปรียบเทียบ

หน่วยความจำคือโต๊ะทำงานของเชฟ ถ้าวัตถุดิบอยู่ใกล้มือ (แคช) ก็เร็ว ถ้าต้องเดินไปตู้เย็น (RAM) ก็ช้าลงหน่อย และถ้าโต๊ะเต็มจนต้องเอาวัตถุดิบไปไว้ในโกดัง (ดิสก์, swap) ทุกครั้งที่หยิบก็จะนานมาก ระหว่างล้างจาน (GC) ต้องหยุดทำอาหาร

สาเหตุของแลคในชั้นนี้

GC ของเซิร์ฟเวอร์หยุดทั้งระบบ Stop-the-world GC pause

ระหว่างที่เซิร์ฟเวอร์ Java หรือ C# หยุดทุกเธรดเพื่อเก็บคืน garbage (stop-the-world) ทั้งเซิร์ฟเวอร์จะหยุดชะงักไปด้วย

ทำไม: heap เต็ม GC จึงเริ่มทำงาน → ผลคือ: หยุดเธรดเกมทั้งหมดแล้วเก็บคืน (ยิ่งมีข้อมูลที่ยังใช้อยู่มาก ยิ่งนาน) → บนหน้าจอ: ทุกคนบนเซิร์ฟเวอร์ค้างพร้อมกันแล้วตามด้วยอาการกรอเร็ว

อาการ: ค้าง, กรอเร็ว · ผู้รับผิดชอบหลัก พัฒนาเซิร์ฟเวอร์ (ทีมพัฒนาเกม) · ร่วมกับ อินฟราเซิร์ฟเวอร์ (ทีมอินฟรา)

GC pause ของสคริปต์เอนจิน Scripting VM GC (Lua, etc.)

แม้เซิร์ฟเวอร์จะเขียนด้วย C++ แต่ถ้ารันเควสต์, AI และสกิลด้วยสคริปต์อย่าง Lua ระหว่างที่ GC ของสคริปต์เอนจินทำงาน โซนนั้นจะหยุดชะงัก

ทำไม: สคริปต์เอนจินของแต่ละโซนรันเควสต์, AI และอีเวนต์ พร้อมสร้างออบเจ็กต์ชั่วคราวจำนวนมาก → ผลคือ: ถ้า GC ของสคริปต์เอนจินเก็บคืนทีละมาก ๆ ทิกของโซนนั้นจะหยุดชะงัก → บนหน้าจอ: หยุดแวบเป็นรอบเฉพาะในบางโซนหรือระหว่างบางอีเวนต์

อาการ: กระตุก, ค้าง · ผู้รับผิดชอบหลัก พัฒนาเซิร์ฟเวอร์ (ทีมพัฒนาเกม)

การจัดสรรหน่วยความจำพุ่ง Allocation storms

ถ้าสร้างออบเจ็กต์ชั่วคราวจำนวนมากระหว่างอีเวนต์ GC จะทำงานบ่อยกว่าปกติมาก

ทำไม: ออบเจ็กต์ชั่วคราวพุ่งจากไอเทมดรอป, log การต่อสู้ และของรางวัลอีเวนต์ → ผลคือ: GC ทำงานบ่อยขึ้นหลายเท่า และออบเจ็กต์ที่ยังไม่ทันถูกทิ้งย้ายไปอยู่ใน Old generation ทำให้ Full GC มาเร็วขึ้นด้วย → บนหน้าจอ: หยุดแวบเป็นรอบเฉพาะช่วงอีเวนต์

อาการ: กระตุก, ค้าง · ผู้รับผิดชอบหลัก พัฒนาเซิร์ฟเวอร์ (ทีมพัฒนาเกม)

หน่วยความจำรั่ว Memory leak

หน่วยความจำที่ไม่ถูกคืนค่อย ๆ สะสม จนหลายวันต่อมานำไปสู่ GC ทำงานไม่หยุด, สวอป หรือโปรเซสถูกบังคับปิด

ทำไม: ข้อมูลตัวละครที่ออกจากเกมไปแล้วและ event handler ไม่ถูกคืนหน่วยความจำ → ผลคือ: หน่วยความจำว่างลดลงเรื่อย ๆ ตลอดหลายวัน → บนหน้าจอ: หลังปิดปรับปรุงใหม่ ๆ ยังปกติ ยิ่งผ่านไปหลายวันยิ่งแลค สุดท้ายเซิร์ฟเวอร์ล่ม

อาการ: สโลว์โมชั่น, ค้าง, หลุด · ผู้รับผิดชอบหลัก พัฒนาเซิร์ฟเวอร์ (ทีมพัฒนาเกม) · ร่วมกับ อินฟราเซิร์ฟเวอร์ (ทีมอินฟรา)

GC thrashing (heap เหลือที่ว่างไม่พอ) GC thrashing (heap nearly full)

เมื่อข้อมูลที่ยังใช้อยู่ (live data) เข้าใกล้ขีดจำกัดของ heap แม้ GC ทำงานก็แทบไม่มีอะไรให้เก็บคืน GC จึงวนทำงานซ้ำไม่หยุด

ทำไม: ผู้เล่นเพิ่มขึ้นช่วงอีเวนต์หรือหน่วยความจำรั่ว ทำให้ข้อมูลที่ยังใช้อยู่เต็มจนใกล้ขีดจำกัดของ heap → ผลคือ: GC เก็บคืนได้นิดเดียวจึงต้องทำ Full GC อีกทันที และ GC ใช้ CPU ไปเกือบทั้งหมด → บนหน้าจอ: ทั้งเซิร์ฟเวอร์เป็นสโลว์โมชั่นสลับกับค้างซ้ำ ๆ อยู่หลายนาที แล้วปิดตัวเพราะหน่วยความจำไม่พอ

อาการ: สโลว์โมชั่น, ค้าง, หลุด · ผู้รับผิดชอบหลัก พัฒนาเซิร์ฟเวอร์ (ทีมพัฒนาเกม) · ร่วมกับ อินฟราเซิร์ฟเวอร์ (ทีมอินฟรา)

สวอป Swapping

ถ้าหน่วยความจำไม่พอจน OS ย้ายบางส่วนไปไว้บนดิสก์ ทุกครั้งที่ใช้หน่วยความจำส่วนนั้นต้องรอดิสก์ที่ช้ากว่าเกิน 1,000 เท่า

ทำไม: หน่วยความจำที่ใช้เกิน RAM จริง → ผลคือ: OS ย้ายบางส่วนไปไว้บนดิสก์และอ่านกลับเมื่อต้องใช้ → บนหน้าจอ: เวลาต่อทิกพุ่งเป็นหลายร้อย ms ผู้เล่นทุกคนบนเซิร์ฟเวอร์เจอสโลว์โมชั่นและค้าง

อาการ: สโลว์โมชั่น, ค้าง · ผู้รับผิดชอบหลัก อินฟราเซิร์ฟเวอร์ (ทีมอินฟรา) · ร่วมกับ พัฒนาเซิร์ฟเวอร์ (ทีมพัฒนาเกม)

แคชมิส CPU cache misses

ถ้าข้อมูลกระจัดกระจายอยู่ทั่วหน่วยความจำ CPU ต้องไปรอถึง RAM ที่ช้าทุกครั้ง

ทำไม: ออบเจ็กต์กระจัดกระจายเชื่อมกันด้วยพอยน์เตอร์ และถูกเข้าถึงแบบไม่เรียงลำดับ → ผลคือ: ไม่มีข้อมูลในแคชของ CPU จึงต้องอ่านจาก RAM ทุกครั้ง (ช้ากว่าราว 100 เท่า) → บนหน้าจอ: งานเท่าเดิมแต่ต้นทุนต่อทิกเพิ่มเป็นหลายเท่า ถ้าหนักจะเป็นสโลว์โมชั่น

อาการ: สโลว์โมชั่น · ผู้รับผิดชอบหลัก พัฒนาเซิร์ฟเวอร์ (ทีมพัฒนาเกม)

หน่วยความจำแตกกระจาย (fragmentation) Heap fragmentation

ถ้าจัดสรรและคืนหน่วยความจำซ้ำไปมาจนพื้นที่ว่างแตกเป็นชิ้นเล็ก ๆ โปรเซสจะถือครองหน่วยความจำมากกว่าที่ใช้จริงมาก

ทำไม: หลายเธรดจัดสรรและคืนหน่วยความจำขนาดไม่เท่ากันเป็นเวลานาน → ผลคือ: พื้นที่ว่างกระจายเป็นชิ้นเล็ก ๆ คืนให้ OS ไม่ได้ ปริมาณการใช้จึงเพิ่มขึ้นเรื่อย ๆ เหมือนหน่วยความจำรั่ว → บนหน้าจอ: ยิ่งเปิดไว้นานยิ่งช้าลงเพราะสวอปและหน่วยความจำไม่พอ แล้วถูกบังคับปิด

อาการ: สโลว์โมชั่น, หลุด · ผู้รับผิดชอบหลัก พัฒนาเซิร์ฟเวอร์ (ทีมพัฒนาเกม)

หน่วยความจำ NUMA ฝั่งไกล (remote) Remote NUMA access

บนเซิร์ฟเวอร์ที่มี CPU สองตัว ถ้าใช้หน่วยความจำที่ต่ออยู่กับ CPU อีกฝั่ง การเข้าถึงจะช้าลง

ทำไม: เธรดกับหน่วยความจำถูกวางไว้คนละซ็อกเก็ต CPU → ผลคือ: การเข้าถึงหน่วยความจำช้าลง (1.5–2 เท่า แล้วแต่เครื่อง) → บนหน้าจอ: สเปกเดียวกันแต่ประสิทธิภาพต่างกันในแต่ละโปรเซส

อาการ: สโลว์โมชั่น · ผู้รับผิดชอบหลัก อินฟราเซิร์ฟเวอร์ (ทีมอินฟรา)

ดิสก์

log, การเซฟตัวละคร, ข้อมูลแมพ และไฟล์ DB อยู่บนดิสก์ทั้งหมด ดิสก์ช้ากว่าหน่วยความจำหลายร้อยเท่า (SSD) ถึง 100,000 เท่า (HDD) ถ้าโครงสร้างของเซิร์ฟเวอร์เกมต้องรอดิสก์ ทันทีที่ดิสก์ยุ่ง เกมก็จะหยุดไปด้วย

ประสิทธิภาพของดิสก์วัดด้วย “อ่านเขียนได้กี่ครั้งใน 1 วินาที” (IOPS) HDD รุ่นเก่าได้ราว 150 ครั้ง ส่วน SSD ได้หลายหมื่นถึงหลายแสนครั้ง ดิสก์บนคลาวด์มีขีดจำกัดตามเงินที่จ่าย (gp3 พื้นฐานของ AWS คือ 3,000 ครั้ง) ดิสก์คลาวด์บางประเภทและสเปกเซิร์ฟเวอร์ขนาดเล็กให้ burst credit ที่ทำงานได้เร็วกว่าปกติชั่วคราว แต่ถ้าช่วงที่ยุ่งยาวนาน เครดิตจะหมดและความเร็วจะตกลงทันที การแจ้งปัญหาว่า “ทุกเย็นพอผ่านไปไม่กี่ชั่วโมงก็แลค” มีลักษณะแบบนี้

หัวใจคือใครเป็นฝ่ายรอ การเขียนไฟล์ปกติ OS จะรับไว้ในหน่วยความจำก่อนแล้วค่อยเขียนลงดิสก์ทีหลัง จึงมักเสร็จทันที ปัญหาเกิดเมื่อสั่งให้รอ “จนกว่าจะเขียนลงดิสก์จริง” (fsync) หรือเมื่อหน่วยความจำที่ OS รับไว้ได้เต็มแล้ว ตอนนั้นถ้าเธรดเกมรอเอง (synchronous) เมื่อดิสก์ช้าไป 100 ms ทิกก็หยุดชะงัก 100 ms ถ้าโยนการเขียนไปให้เธรดแยก (async) เกมจะไม่หยุด แต่ถ้าเซิร์ฟเวอร์ล่มกะทันหัน ข้อมูลที่ยังเขียนไม่ทันอาจหายไป (กดไม่ติด/โรลแบ็ค)

เปรียบเทียบ

ดิสก์คือโกดัง และ IOPS คือจำนวนประตูโกดัง ถ้าประตูน้อย คนที่ขนของเข้าออกก็ต้องต่อแถว burst credit คือแรงที่ใช้วิ่งสุดกำลังได้ชั่วครู่ พอหมดแรงก็กลับไปเดินตามปกติ

สาเหตุของแลคในชั้นนี้

การเขียน log แบบ synchronous Synchronous logging

ถ้าเธรดเกมต้องรอให้ดิสก์เขียนเสร็จทุกครั้งที่เขียน log หนึ่งบรรทัด พอดิสก์ยุ่ง เกมก็หยุดเดินไปด้วย

ทำไม: เขียน log การต่อสู้และการเทรดลงไฟล์โดยตรงจากเธรดเกม → ผลคือ: ถ้าสั่งให้เขียนลงดิสก์แบบแน่นอน (fsync) หรือบัฟเฟอร์การเขียนของ OS (page cache) เต็มถึงขีดจำกัด ตอนที่ดิสก์ยุ่ง การเขียนหนึ่งครั้งจะใช้เวลาหลายสิบ ms → บนหน้าจอ: หยุดแวบในการต่อสู้ที่มี log เยอะ

อาการ: กระตุก, ค้าง · ผู้รับผิดชอบหลัก พัฒนาเซิร์ฟเวอร์ (ทีมพัฒนาเกม) · ร่วมกับ อินฟราเซิร์ฟเวอร์ (ทีมอินฟรา)

fsync ทะลัก fsync storms

ถ้าสั่งให้เขียนข้อมูลลงดิสก์ “แบบแน่นอน” แต่ละครั้งจะใช้เวลา 0.1 ms ถึงหลายสิบ ms แล้วแต่ดิสก์ และถ้ามาพร้อมกันมาก คิวจะยาวขึ้น

ทำไม: คำขอเขียนแบบแน่นอนมากระจุกกันจากการเซฟตามรอบและผู้เล่นแห่ล็อกเอาต์ → ผลคือ: คิวดิสก์ยาวขึ้น → บนหน้าจอ: แลคทุกครั้งที่ถึงเวลาเซฟ, ล็อกเอาต์และย้ายแชนแนลช้า

อาการ: กระตุก, อินพุตดีเลย์ · ผู้รับผิดชอบหลัก พัฒนาเซิร์ฟเวอร์ (ทีมพัฒนาเกม) · ร่วมกับ อินฟราเซิร์ฟเวอร์ (ทีมอินฟรา), อินฟรา DB (ทีมอินฟรา)

burst credit ของดิสก์คลาวด์หมด Burst credit depletion

ดิสก์คลาวด์บางประเภทและเซิร์ฟเวอร์สเปกเล็กมี burst credit ที่ให้ทำงานเร็วกว่าประสิทธิภาพพื้นฐานได้ชั่วคราว ถ้าช่วงที่ยุ่งยาวนานจนเครดิตหมด ความเร็วจะตกลงกะทันหัน

ทำไม: ใช้งานเกินประสิทธิภาพพื้นฐานเป็นเวลานาน → ผลคือ: burst credit หมด ประสิทธิภาพจึงดิ่งลงไปเหลือระดับพื้นฐาน → บนหน้าจอ: ทุกเย็นพอผ่านไปไม่กี่ชั่วโมงก็เริ่มแลค

อาการ: กระตุก, สโลว์โมชั่น, อินพุตดีเลย์ · ผู้รับผิดชอบหลัก อินฟราเซิร์ฟเวอร์ (ทีมอินฟรา) · ร่วมกับ อินฟรา DB (ทีมอินฟรา)

ขีดจำกัด IOPS/คิวดิสก์อิ่มตัว IOPS limit / queue saturation

ถ้าคำขอเกินจำนวนที่ดิสก์ประมวลผลได้ใน 1 วินาที คิวจะยาวขึ้นและดีเลย์พุ่งสูง

ทำไม: คำขออ่านและเขียนเข้าใกล้ขีดความสามารถของดิสก์ → ผลคือ: คิวยาวขึ้น (มักพุ่งเมื่ออัตราการใช้งานเกิน 90%) → บนหน้าจอ: เซฟและโหลดช้า ถ้าเป็น synchronous call เกมจะค้าง

อาการ: อินพุตดีเลย์, ค้าง · ผู้รับผิดชอบหลัก อินฟราเซิร์ฟเวอร์ (ทีมอินฟรา) · ร่วมกับ พัฒนาเซิร์ฟเวอร์ (ทีมพัฒนาเกม), อินฟรา DB (ทีมอินฟรา)

ดิสก์เต็ม Disk full

ถ้า log และ dump สะสมจนดิสก์เต็ม การเขียนจะล้มเหลว และถ้าไม่ได้เตรียมรับมือไว้ เซิร์ฟเวอร์จะล่ม

ทำไม: log, dump และไฟล์ชั่วคราวสะสมจนถึง 100% → ผลคือ: เขียนไม่สำเร็จ ถ้าไม่มีการจัดการข้อผิดพลาดจะแครช ถ้ามีก็เซฟไม่สำเร็จ → บนหน้าจอ: หลุด, ความคืบหน้าถูกโรลแบ็ค

อาการ: หลุด, กดไม่ติด/โรลแบ็ค · ผู้รับผิดชอบหลัก อินฟราเซิร์ฟเวอร์ (ทีมอินฟรา) · ร่วมกับ อินฟรา DB (ทีมอินฟรา), พัฒนาเซิร์ฟเวอร์ (ทีมพัฒนาเกม)

งานสำรองข้อมูล/บีบอัด/สแกน Backup / compression / scans

ถ้าการสำรองข้อมูลตอนเช้ามืด, การบีบอัด log หรือการสแกนความปลอดภัยยึดดิสก์ไว้ การอ่านและเขียนของเซิร์ฟเวอร์เกมจะล่าช้า

ทำไม: งานสำรองข้อมูลหรือบีบอัดที่ตั้งเวลาไว้เริ่มทำงาน → ผลคือ: ใช้แบนด์วิดท์และ IOPS ของดิสก์ไปเกือบหมด → บนหน้าจอ: แลคเวลาเดิมทุกวัน

อาการ: กระตุก, อินพุตดีเลย์ · ผู้รับผิดชอบหลัก อินฟราเซิร์ฟเวอร์ (ทีมอินฟรา) · ร่วมกับ อินฟรา DB (ทีมอินฟรา)

lazy loading บนเซิร์ฟเวอร์ Lazy loading on the server

ถ้าเซิร์ฟเวอร์อ่านข้อมูลดันเจี้ยนหรือแมพจากดิสก์ตอนที่ถูกขอเป็นครั้งแรก ทุกคนจะหยุดไปตลอดทิกนั้น

ทำไม: มีคนเข้าดันเจี้ยนหรือพื้นที่นั้นเป็นคนแรก → ผลคือ: เซิร์ฟเวอร์อ่านข้อมูลจากดิสก์ในเธรดเกม → บนหน้าจอ: ทุกคนบนเซิร์ฟเวอร์นั้นค้างไปครู่หนึ่ง

อาการ: ค้าง · ผู้รับผิดชอบหลัก พัฒนาเซิร์ฟเวอร์ (ทีมพัฒนาเกม) · ร่วมกับ อินฟราเซิร์ฟเวอร์ (ทีมอินฟรา)

การเขียน core dump Core dump writing

ตอนเซิร์ฟเวอร์ล่ม ต้องเขียนหน่วยความจำหลาย GB ลงดิสก์ บางครั้งการรีสตาร์ตจึงล่าช้าไปหลายนาที

ทำไม: เซิร์ฟเวอร์แครช จึงเขียนหน่วยความจำทั้งหมดลงไฟล์ → ผลคือ: รีสตาร์ตไม่ได้ระหว่างเขียนหลาย GB → บนหน้าจอ: เซิร์ฟเวอร์ล่มจนผู้เล่นหลุด แล้วเข้าเกมไม่ได้อยู่นาน

อาการ: เข้าเกมไม่ได้/โหลดไม่จบ · ผู้รับผิดชอบหลัก อินฟราเซิร์ฟเวอร์ (ทีมอินฟรา) · ร่วมกับ พัฒนาเซิร์ฟเวอร์ (ทีมพัฒนาเกม)

ดีเลย์จาก seek ของ HDD HDD seek latency

HDD ต้องเลื่อนหัวอ่านไปบนจานแม่เหล็ก (seek) การอ่านเขียนข้อมูลที่กระจัดกระจายจึงใช้เวลาเกือบ 10 ms ต่อครั้ง

ทำไม: ใช้ HDD บนเซิร์ฟเวอร์เก่าหรือสตอเรจราคาถูก → ผลคือ: อ่านเขียนแบบกระจัดกระจายครั้งละประมาณ 10 ms → บนหน้าจอ: เซฟและโหลดช้าโดยรวม

อาการ: อินพุตดีเลย์ · ผู้รับผิดชอบหลัก อินฟราเซิร์ฟเวอร์ (ทีมอินฟรา) · ร่วมกับ อินฟรา DB (ทีมอินฟรา), พัฒนาเซิร์ฟเวอร์ (ทีมพัฒนาเกม)

ฐานข้อมูล

ที่เก็บสิ่งที่ห้ามหายเด็ดขาด เช่น ตัวละคร ไอเทม เงินในเกม และประวัติการเทรด เมื่อ DB ช้า การต่อสู้ยังปกติ แต่ไอเทมเข้าช้า เทรดล้มเหลว และล็อกอินไม่จบ ถ้าโครงสร้างของเซิร์ฟเวอร์เกมต้องรอ DB ทั้งฟิลด์จะหยุด

เซิร์ฟเวอร์เกมเปิดการเชื่อมต่อกับ DB ไว้ล่วงหน้าจำนวนหนึ่ง (connection pool) แล้วผลัดกันใช้ ถ้าคิวรี (คำขอที่ส่งไปยัง DB) ตัวหนึ่งใช้เวลานาน การเชื่อมต่อนั้นก็ถูกใช้อยู่ตลอด และถ้าการเชื่อมต่อใน pool ถูกใช้หมด คำขอที่เหลือต้องรอในคิว สาเหตุที่พบบ่อยที่ทำให้คิวรีช้ามีสองอย่าง คือไม่มีอินเด็กซ์ (ดัชนีท้ายเล่มของหนังสือ) จึงต้องอ่านทั้งตาราง (full table scan) หรือคำขอหลายรายการพยายามแก้แถวเดียวกันพร้อมกันจนต้องรอล็อก

DB ใช้กลไกหลายอย่างเพื่อให้เชื่อถือได้และรับคำขอได้มาก ได้แก่ replica ที่แบ่งรับงานอ่าน, DB สำรองที่สลับไปใช้เมื่อขัดข้อง และ checkpoint ที่เขียนส่วนที่เปลี่ยนลงดิสก์รวดเดียวเป็นรอบ ๆ ถ้า checkpoint กระจุกตัวจะช้าลงชั่วครู่ ถ้า replica ตามไม่ทัน จะเกิดอาการกดไม่ติด/โรลแบ็คแบบ “ไอเทมที่เพิ่งซื้อไม่ขึ้น” และถ้าสลับไป DB สำรองขณะที่ replication ยังตามไม่ทัน จะเกิดแบบ “ล็อกอินเข้ามาแล้วย้อนกลับไปเป็นสถานะเมื่อครู่” ถ้าเซิร์ฟเวอร์เกมเซฟตัวละครทุก ๆ ไม่กี่นาที เมื่อเซิร์ฟเวอร์ล่มก็จะกลายเป็น “ย้อนกลับไปเมื่อ 10 นาทีก่อน”

เปรียบเทียบ

DB คือเคาน์เตอร์ธนาคาร จำนวนช่องบริการ (connection pool) มีจำกัด ถ้าคำขอหนึ่งต้องค้นสมุดบัญชีทั้งเล่ม (full table scan) คำขอที่ต่อท้ายทั้งหมดก็ต้องรอ และถ้าทุกคนพยายามเปิดตู้นิรภัยใบเดียวกัน (hot row) ก็เข้าได้ทีละคน

สาเหตุของแลคในชั้นนี้

คิวรีที่ไม่มีอินเด็กซ์ Missing index / full table scan

ถ้าไม่มีอินเด็กซ์ การหาแถวที่ตรงเงื่อนไขต้องอ่านทั้งตาราง (full table scan)

ทำไม: deploy ฟีเจอร์ใหม่ที่เพิ่มการค้นหาด้วยเงื่อนไขที่ไม่มีอินเด็กซ์ → ผลคือ: สแกนทุกแถวหลายล้านแถว คิวรีเดียวใช้เวลาหลายร้อย ms ถึงหลายวินาที → บนหน้าจอ: กล่องจดหมายและประวัติการเทรดโหลดช้า, connection ถูกถือครองไว้จนคำขออื่นต้องรอไปด้วย

อาการ: อินพุตดีเลย์, เข้าเกมไม่ได้/โหลดไม่จบ · ผู้รับผิดชอบหลัก พัฒนาเซิร์ฟเวอร์ (ทีมพัฒนาเกม) · ร่วมกับ อินฟรา DB (ทีมอินฟรา)

แย่งล็อกบน hot row Hot row lock contention

ถ้าทุกคนพยายามแก้แถวเดียวกัน (โกดังกิลด์, ไอเทมฮิตในตลาดประมูล, ตัวนับของทั้งเซิร์ฟเวอร์) จะได้ล็อกทีละคนเท่านั้น

ทำไม: การแก้ไขมากระจุกที่แถวเดียวกันเพราะอีเวนต์หรือไอเทมฮิต → ผลคือ: คำขอต้องรอจนกว่าจะได้ล็อก → บนหน้าจอ: เทรดไม่สำเร็จ, “โปรดลองใหม่ภายหลัง”, ไทม์เอาต์

อาการ: กดไม่ติด/โรลแบ็ค, อินพุตดีเลย์ · ผู้รับผิดชอบหลัก พัฒนาเซิร์ฟเวอร์ (ทีมพัฒนาเกม) · ร่วมกับ อินฟรา DB (ทีมอินฟรา)

เดดล็อกใน DB Database deadlock

ถ้าสองทรานแซกชัน (งาน DB ที่ประมวลผลเป็นชุดเดียว) ต่างรอแถวที่อีกฝ่ายล็อกไว้ DB จะบังคับยกเลิกฝั่งหนึ่ง

ทำไม: การเทรด A ล็อกตามลำดับไอเทม→เงินในเกม ส่วน B ล็อกตามลำดับเงินในเกม→ไอเทม → ผลคือ: DB ตรวจพบเดดล็อกแล้วโรลแบ็คฝั่งหนึ่ง → บนหน้าจอ: เทรดและคราฟต์ล้มเหลวเป็นบางครั้ง, ไอเทมย้อนกลับ

อาการ: กดไม่ติด/โรลแบ็ค, อินพุตดีเลย์ · ผู้รับผิดชอบหลัก พัฒนาเซิร์ฟเวอร์ (ทีมพัฒนาเกม) · ร่วมกับ อินฟรา DB (ทีมอินฟรา)

connection pool หมด Connection pool exhaustion

จำนวนการเชื่อมต่อที่เปิดไว้กับ DB มีจำกัด ถ้าคิวรีที่ช้าถือครองการเชื่อมต่อไว้ คำขอที่เหลือต้องรอ

ทำไม: ทุกการเชื่อมต่อถูกใช้หมดเพราะคิวรีช้าหรือคำขอทะลัก → ผลคือ: คำขอใหม่ต้องรอจนกว่าจะมีการเชื่อมต่อว่าง → บนหน้าจอ: ล็อกอินโหลดไม่จบ, เซฟช้า, ไทม์เอาต์

อาการ: เข้าเกมไม่ได้/โหลดไม่จบ, อินพุตดีเลย์ · ผู้รับผิดชอบหลัก พัฒนาเซิร์ฟเวอร์ (ทีมพัฒนาเกม) · ร่วมกับ อินฟรา DB (ทีมอินฟรา)

การทำสำเนาข้อมูลล่าช้า Replication lag

การเขียนไปที่ DB หลัก ส่วนการอ่านทำจาก replica ถ้า replica ตามไม่ทัน ข้อมูลที่เพิ่งเขียนจะยังมองไม่เห็น

ทำไม: การเขียนกระจุกที่ DB หลักจน replica ตามหลังไปหลายวินาที → ผลคือ: อ่านข้อมูลที่เพิ่งเซฟจาก replica แล้วยังไม่มี → บนหน้าจอ: ไอเทมที่เพิ่งซื้อไม่ขึ้น, ราคาในตลาดซื้อขายเป็นค่าเก่า, บั๊กแจกของซ้ำ

อาการ: กดไม่ติด/โรลแบ็ค · ผู้รับผิดชอบหลัก อินฟรา DB (ทีมอินฟรา) · ร่วมกับ พัฒนาเซิร์ฟเวอร์ (ทีมพัฒนาเกม)

checkpoint/log flush Checkpoint / log flush stalls

จังหวะที่ DB เขียนส่วนที่เปลี่ยนแปลงในหน่วยความจำลงดิสก์รวดเดียวตามรอบ คิวรีจะช้าลง

ทำไม: ส่วนที่เปลี่ยนแปลงสะสมแล้วถูกเขียนลงดิสก์ตามรอบ → ผลคือ: ช่วงนั้นดิสก์ยุ่ง คิวรีจึงช้า → บนหน้าจอ: เซฟและโหลดช้าลงเป็นรอบ

อาการ: อินพุตดีเลย์, กระตุก · ผู้รับผิดชอบหลัก อินฟรา DB (ทีมอินฟรา)

cold cache (หลังรีสตาร์ตใหม่ ๆ) Cold buffer pool after restart

เมื่อรีสตาร์ต DB แคชในหน่วยความจำจะว่างเปล่า ช่วงหนึ่งข้อมูลทุกอย่างที่ดึงจึงต้องอ่านจากดิสก์

ทำไม: รีสตาร์ต DB ตอนปิดปรับปรุง → ผลคือ: ข้อมูลที่ใช้บ่อยไม่อยู่ในหน่วยความจำ จึงต้องอ่านจากดิสก์ → บนหน้าจอ: หลังปิดปรับปรุงใหม่ ๆ ล็อกอินและโหลดช้าอยู่พักหนึ่ง

อาการ: เข้าเกมไม่ได้/โหลดไม่จบ, อินพุตดีเลย์ · ผู้รับผิดชอบหลัก อินฟรา DB (ทีมอินฟรา) · ร่วมกับ พัฒนาเซิร์ฟเวอร์ (ทีมพัฒนาเกม)

คนแห่ล็อกอินและคิวรี N+1 Login storm, N+1 queries

ถ้าการโหลดตัวละครหนึ่งตัวต้องดึงข้อมูลแยกกันหลายสิบครั้ง ผู้เล่นหลายหมื่นคนที่ล็อกอินพร้อมกันจะกลายเป็นคิวรีหลายล้านคิวรี

ทำไม: ตอนโหลดตัวละคร ดึงไอเทม สกิล และเควสต์แยกกันทีละอย่าง → ผลคือ: หลังปิดปรับปรุงใหม่ ๆ คนล็อกอินพร้อมกันจนคิวรีพุ่ง → บนหน้าจอ: ล็อกอินโหลดไม่จบ, การเซฟของคนที่กำลังเล่นอยู่ก็ล่าช้าไปด้วย

อาการ: เข้าเกมไม่ได้/โหลดไม่จบ, อินพุตดีเลย์ · ผู้รับผิดชอบหลัก พัฒนาเซิร์ฟเวอร์ (ทีมพัฒนาเกม) · ร่วมกับ อินฟรา DB (ทีมอินฟรา)

งาน batch ขนาดใหญ่ Batch jobs during service

ถ้ารันการสรุปอันดับ, การส่งจดหมายในเกมแบบรวดเดียว หรือการล้างข้อมูลเก่าระหว่างให้บริการ งานเหล่านี้จะยึดล็อกและดิสก์ไว้

ทำไม: รันงานขนาดใหญ่ในช่วงเวลาให้บริการ → ผลคือ: ล็อกเป็นช่วงกว้าง, ถือครองดิสก์และ CPU → บนหน้าจอ: เทรดและเซฟล้มเหลวในบางช่วงเวลา, โหลดช้า

อาการ: อินพุตดีเลย์, กดไม่ติด/โรลแบ็ค · ผู้รับผิดชอบหลัก พัฒนาเซิร์ฟเวอร์ (ทีมพัฒนาเกม) · ร่วมกับ อินฟรา DB (ทีมอินฟรา)

failover ของ DB Database failover

ระหว่างที่ DB หลักล่มและสลับไปใช้ DB สำรอง จะเขียนข้อมูลไม่ได้ และข้อมูลช่วงท้ายที่ยัง replicate ไม่ทันอาจหายไป

ทำไม: DB หลักขัดข้อง DB สำรองจึงถูกยกขึ้นเป็นตัวหลัก (promote) → ผลคือ: ระหว่างสลับ เขียนไม่ได้หลายวินาทีถึงไม่กี่นาที ถ้าเป็น asynchronous replication ข้อมูลที่ยังไม่ได้ replicate อาจหาย → บนหน้าจอ: เซฟไม่สำเร็จทั้งหมดชั่วครู่, ไอเทมและค่าประสบการณ์ถูกโรลแบ็ค

อาการ: กดไม่ติด/โรลแบ็ค, ค้าง, หลุด, เข้าเกมไม่ได้/โหลดไม่จบ · ผู้รับผิดชอบหลัก อินฟรา DB (ทีมอินฟรา) · ร่วมกับ พัฒนาเซิร์ฟเวอร์ (ทีมพัฒนาเกม)

ความคืบหน้าหายเพราะรอบเซฟยาว Periodic save window

ถ้าเซฟแค่ทุกไม่กี่นาทีเพื่อลดโหลด เมื่อเซิร์ฟเวอร์ล่มในระหว่างนั้น ความคืบหน้าจะหายไป

ทำไม: เซฟสถานะตัวละครทุกไม่กี่นาที → ผลคือ: เซิร์ฟเวอร์แครชหรือขัดข้องในระหว่างนั้น → บนหน้าจอ: เข้าเกมใหม่แล้วกลับไปเป็นสถานะเมื่อไม่กี่นาทีก่อน (โรลแบ็ค)

อาการ: กดไม่ติด/โรลแบ็ค · ผู้รับผิดชอบหลัก พัฒนาเซิร์ฟเวอร์ (ทีมพัฒนาเกม) · ร่วมกับ อินฟรา DB (ทีมอินฟรา)

แคชสแตมปีด Cache stampede / thundering herd

ถ้าแคชของข้อมูลยอดนิยมหมดอายุพร้อมกัน คำขอหลายพันรายการจะแห่ไปที่ DB พร้อมกัน

ทำไม: ข้อมูลยอดนิยมที่เก็บใน Redis หรือที่อื่นหมดอายุพร้อมกัน → ผลคือ: คำขอที่พยายามสร้างข้อมูลเดียวกันใหม่แห่ไปที่ DB พร้อมกัน → บนหน้าจอ: DB โหลดเกิน หลายฟีเจอร์จึงช้าลงหรือค้างต่อ ๆ กันไป

อาการ: อินพุตดีเลย์, ค้าง, เข้าเกมไม่ได้/โหลดไม่จบ · ผู้รับผิดชอบหลัก พัฒนาเซิร์ฟเวอร์ (ทีมพัฒนาเกม) · ร่วมกับ อินฟรา DB (ทีมอินฟรา)

ทรานแซกชันที่เปิดทิ้งไว้นาน Long-running transaction / MVCC purge lag

ถ้าทรานแซกชันหนึ่งเปิดทิ้งไว้นาน จะถือล็อกไว้ตลอด และ DB ล้างข้อมูลเวอร์ชันเก่า (purge) ไม่ได้ ทั้งระบบจึงค่อย ๆ ช้าลง

ทำไม: เปิดทรานแซกชันไว้แล้วรอคำตอบจากเซิร์ฟเวอร์อื่น หรือรันคิวรีสรุปผลยาว ๆ บน DB หลักระหว่างให้บริการ → ผลคือ: ล็อกที่ถือไว้ไม่ถูกปล่อย และข้อมูลเวอร์ชันเก่าที่ต้องล้างสะสมเรื่อย ๆ → บนหน้าจอ: ฟีเจอร์ที่ใช้แถวนั้นไทม์เอาต์, การเซฟและการอ่านข้อมูลโดยรวมช้าลงตลอดหลายชั่วโมง

อาการ: อินพุตดีเลย์, กดไม่ติด/โรลแบ็ค · ผู้รับผิดชอบหลัก พัฒนาเซิร์ฟเวอร์ (ทีมพัฒนาเกม) · ร่วมกับ อินฟรา DB (ทีมอินฟรา)

คำสั่ง Redis ที่ช้า Redis blocking commands (single-threaded)

Redis ประมวลผลคำสั่งทีละคำสั่ง คำสั่งที่ช้าเพียงคำสั่งเดียวจึงขวางทุกคำขอที่ตามมา

ทำไม: ใช้ KEYS ค้นทั้งหมดระหว่างให้บริการ, อ่านหรือลบอันดับหรือลิสต์ที่มีสมาชิกหลายล้านตัวทั้งก้อน → ผลคือ: คำขออื่นทั้งหมดต้องรอจนคำสั่งนั้นเสร็จ (หลายสิบ ms ถึงหลายวินาที) → บนหน้าจอ: ฟีเจอร์ที่ใช้เซสชัน อันดับ และแคชหยุดแวบพร้อมกัน, ล็อกอินช้า

อาการ: ค้าง, อินพุตดีเลย์, เข้าเกมไม่ได้/โหลดไม่จบ · ผู้รับผิดชอบหลัก พัฒนาเซิร์ฟเวอร์ (ทีมพัฒนาเกม) · ร่วมกับ อินฟรา DB (ทีมอินฟรา)

คิวรีช้าเพราะ query plan เปลี่ยน Query plan regression (stats, parameter sniffing)

โค้ดเหมือนเดิม แต่ถ้า DB เปลี่ยนวิธีประมวลผลคิวรีเดิม (query plan) คิวรีที่เมื่อวานใช้ 2 ms วันนี้อาจกลายเป็นหลายร้อย ms

ทำไม: สถิติอัปเดตอัตโนมัติ, DB รีสตาร์ต หรือการกระจายของข้อมูลเปลี่ยน ทำให้ DB วาง query plan ใหม่ → ผลคือ: DB เลือก plan ที่ไม่ใช้อินเด็กซ์ คิวรีเดิมจึงช้าลงหลายสิบถึงหลายร้อยเท่า และ connection ถูกถือครองไว้ → บนหน้าจอ: ไม่มี deploy อะไรเลย แต่การโหลดของบางฟีเจอร์ช้าลงทันที และคำขออื่นต้องรอไปด้วย

อาการ: อินพุตดีเลย์, เข้าเกมไม่ได้/โหลดไม่จบ · ผู้รับผิดชอบหลัก อินฟรา DB (ทีมอินฟรา) · ร่วมกับ พัฒนาเซิร์ฟเวอร์ (ทีมพัฒนาเกม)

ล็อกจากการเปลี่ยนสคีมา (DDL) ระหว่างให้บริการ Schema change lock (DDL / metadata lock)

ถ้าเพิ่มคอลัมน์หรืออินเด็กซ์ให้ตารางระหว่างให้บริการ ล็อกที่ต้องใช้เพียงชั่วครู่ตัวเดียวอาจทำให้ทุกคำขอที่ใช้ตารางนั้นต้องรอ

ทำไม: เพิ่มคอลัมน์หรืออินเด็กซ์ให้ตารางที่ใช้งานอยู่ผ่าน hotfix → ผลคือ: การเปลี่ยนสคีมารอทรานแซกชันยาวที่เปิดอยู่ก่อน และทุกคำขอที่ตามมาต้องรอการเปลี่ยนสคีมานั้น → บนหน้าจอ: ฟีเจอร์ที่ใช้ตารางนั้น (กระเป๋า, จดหมาย ฯลฯ) ค้างทั้งหมดแล้วไทม์เอาต์

อาการ: อินพุตดีเลย์, กดไม่ติด/โรลแบ็ค, เข้าเกมไม่ได้/โหลดไม่จบ · ผู้รับผิดชอบหลัก อินฟรา DB (ทีมอินฟรา) · ร่วมกับ พัฒนาเซิร์ฟเวอร์ (ทีมพัฒนาเกม)

สถาปัตยกรรมเซิร์ฟเวอร์และการดูแลระบบ

MMO ในปัจจุบันส่วนใหญ่มีโครงสร้างที่เซิร์ฟเวอร์ล็อกอิน, เกตเวย์, ฟิลด์, ดันเจี้ยน, แชต, ปาร์ตี้, ตลาดประมูล, แคช และ DB เรียกใช้กันไปมา ถ้าจุดหนึ่งขัดข้อง จะลามไปยังจุดที่เชื่อมต่อกัน และงานดูแลระบบอย่างการ deploy การขยายระบบ และการปิดปรับปรุงก็ทำให้เกิดแลคได้

การแยกเซิร์ฟเวอร์ช่วยกันไม่ให้ความขัดข้องจุดเดียวลามไปทั้งระบบ แต่ก็ทำให้เกิด call chain (การที่เซิร์ฟเวอร์เรียกเซิร์ฟเวอร์ต่อกันเป็นทอด ๆ) เช่น เซิร์ฟเวอร์เกมเรียกเซิร์ฟเวอร์ตลาดประมูล แล้วเซิร์ฟเวอร์ตลาดประมูลเรียกแคชและ DB อีกที ถ้าเซิร์ฟเวอร์ปลาย chain ช้าลง เซิร์ฟเวอร์ข้างหน้าจะถือครองเธรดและการเชื่อมต่อไว้ระหว่างรอคำตอบ จนในที่สุดฟีเจอร์ที่ดูไม่เกี่ยวข้องก็หยุดไปด้วย เรียกว่า cascading failure ซึ่งป้องกันการลามได้ด้วยไทม์เอาต์และ circuit breaker (กลไกที่ตัดการเรียกที่ล้มเหลวต่อเนื่องไว้ชั่วคราว)

งานดูแลระบบก็เป็นสาเหตุของแลค การรีสตาร์ตระหว่าง deploy อัปเดต, เวลาไม่กี่นาทีที่ใช้เพิ่มเซิร์ฟเวอร์อัตโนมัติเมื่อคนแห่มา, ขั้นตอนย้ายตัวละครไปเซิร์ฟเวอร์อื่นตอนย้ายโซน และภาระที่มองไม่เห็นจากบอทและมาโคร ล้วนดูเป็น “แลค” ในสายตาผู้เล่น

เปรียบเทียบ

สถาปัตยกรรมเซิร์ฟเวอร์คือบริษัทที่หลายแผนกส่งเอกสารอนุมัติต่อกัน ถ้าแผนกท้ายสายอนุมัติ (DB) ช้าลงแผนกเดียว แผนกข้างหน้าก็ต้องถือเอกสารต่อแถว จนสุดท้ายงานทั้งบริษัทหยุด ไทม์เอาต์คือกฎ “ถ้าเกิน 10 นาทีไม่มีคำตอบ ให้ตีกลับไปก่อน” ส่วนเบรกเกอร์คือกฎ “ถ้าถูกตีกลับต่อเนื่อง ช่วงหนึ่งจะไม่ส่งเอกสารไปแผนกนั้นอีกและตีกลับทันที”

สาเหตุของแลคในชั้นนี้

การผ่านเกตเวย์/พร็อกซี Gateway / proxy hop

ถ้าวางเซิร์ฟเวอร์คั่นกลางระหว่างไคลเอนต์กับเซิร์ฟเวอร์เกม ทุกครั้งที่ผ่านจะมีเวลาประมวลผลเพิ่มขึ้น และเซิร์ฟเวอร์ตัวนั้นจะกลายเป็น single point of failure (จุดเดียวที่ล่มแล้วกระทบทั้งระบบ)

ทำไม: โครงสร้างแบบ ไคลเอนต์ ↔ เกตเวย์ ↔ เซิร์ฟเวอร์เกม → ผลคือ: มีเวลาประมวลผลและเวลารอที่เซิร์ฟเวอร์คั่นกลางเพิ่มเข้ามา ถ้าโหลดเกินจะกระทบทุกคน → บนหน้าจอ: ปิงสูงขึ้นทั้งหมด ถ้าเกตเวย์ล่ม ผู้เล่นทุกคนที่ผ่านเกตเวย์นั้นหลุด

อาการ: อินพุตดีเลย์, หลุด · ผู้รับผิดชอบหลัก พัฒนาเซิร์ฟเวอร์ (ทีมพัฒนาเกม) · ร่วมกับ อินฟราเซิร์ฟเวอร์ (ทีมอินฟรา), พัฒนาไคลเอนต์ (ทีมพัฒนาเกม)

ย้ายโซน (ส่งต่อตัวละครข้ามเซิร์ฟเวอร์) Zone / server handoff

เมื่อเข้าพื้นที่หรือดันเจี้ยนอื่น ขั้นตอนส่งข้อมูลตัวละครไปยังเซิร์ฟเวอร์อื่นทำให้เกิดดีเลย์และความล้มเหลว

ทำไม: เข้าดันเจี้ยนหรือข้ามทวีป ทำให้เซิร์ฟเวอร์ที่ดูแลตัวละครเปลี่ยนไป → ผลคือ: บันทึก → ส่งต่อ → โหลด ถ้าเซิร์ฟเวอร์ปลายทางแน่นหรือไม่มีอินสแตนซ์ดันเจี้ยนว่าง ต้องรอ → บนหน้าจอ: โหลดนาน, เข้าไม่สำเร็จ, หลุดระหว่างย้าย

อาการ: เข้าเกมไม่ได้/โหลดไม่จบ, ค้าง, หลุด, ดีดกลับ · ผู้รับผิดชอบหลัก พัฒนาเซิร์ฟเวอร์ (ทีมพัฒนาเกม) · ร่วมกับ อินฟราเซิร์ฟเวอร์ (ทีมอินฟรา)

ความล้มเหลวแบบลูกโซ่ Cascading failure

เมื่อเซอร์วิสหนึ่งช้าลง เซิร์ฟเวอร์ที่เรียกใช้เซอร์วิสนั้นจะติดอยู่กับการรอคำตอบ จนฟีเจอร์ที่ไม่เกี่ยวข้องก็หยุดทำงานไปด้วย

ทำไม: เซอร์วิสหนึ่ง เช่น DB หรือระบบยืนยันตัวตน ช้าลง → ผลคือ: เธรดและการเชื่อมต่อของเซิร์ฟเวอร์ที่เรียกใช้ติดอยู่กับการรอคำตอบ และการลองส่งคำขอที่ล้มเหลวใหม่ยิ่งเพิ่มโหลด → บนหน้าจอ: ฟีเจอร์ที่ดูไม่เกี่ยวข้องก็ช้าลงหรือค้างไปหมด

อาการ: ค้าง, อินพุตดีเลย์, เข้าเกมไม่ได้/โหลดไม่จบ · ผู้รับผิดชอบหลัก พัฒนาเซิร์ฟเวอร์ (ทีมพัฒนาเกม) · ร่วมกับ อินฟราเครือข่าย (ทีมอินฟรา)

เซิร์ฟเวอร์เสริมขัดข้อง Auxiliary service outage

ถ้าเซิร์ฟเวอร์ที่รันแยกจากเซิร์ฟเวอร์เกม เช่น แชต, ปาร์ตี้ หรือตลาดประมูล ขัดข้อง จะใช้งานไม่ได้เฉพาะฟีเจอร์นั้น

ทำไม: เซิร์ฟเวอร์เฉพาะฟีเจอร์ช้าลงหรือล่ม → ผลคือ: เฉพาะคำขอของฟีเจอร์นั้นที่ไม่ได้รับคำตอบ → บนหน้าจอ: แชตไม่ได้, เชิญปาร์ตี้แล้วไม่มีอะไรเกิดขึ้น, ตลาดซื้อขายโหลดไม่จบ (การต่อสู้ปกติ)

อาการ: กดไม่ติด/โรลแบ็ค, เข้าเกมไม่ได้/โหลดไม่จบ · ผู้รับผิดชอบหลัก พัฒนาเซิร์ฟเวอร์ (ทีมพัฒนาเกม) · ร่วมกับ อินฟราเซิร์ฟเวอร์ (ทีมอินฟรา)

การ deploy และรีสตาร์ต Deploy / rolling restart

ถ้ารีสตาร์ตเซิร์ฟเวอร์เพื่ออัปเดตโดยไม่ย้ายการเชื่อมต่อ ผู้เล่นที่อยู่บนเซิร์ฟเวอร์นั้นจะหลุด และการบันทึกข้อมูลก่อนปิดกับการเชื่อมต่อใหม่จะทะลักเข้ามาพร้อมกัน

ทำไม: deploy hotfix โดยรีสตาร์ตเซิร์ฟเวอร์ทีละเครื่องตามลำดับ → ผลคือ: ปิดเซิร์ฟเวอร์โดยไม่ย้ายการเชื่อมต่อไปเซิร์ฟเวอร์อื่น การบันทึกของผู้เล่นทุกคนบนเซิร์ฟเวอร์นั้นจึงไปกองที่ DB → บนหน้าจอ: หลุดโดยไม่มีประกาศ, คนแห่เชื่อมต่อใหม่

อาการ: หลุด, เข้าเกมไม่ได้/โหลดไม่จบ, อินพุตดีเลย์ · ผู้รับผิดชอบหลัก พัฒนาเซิร์ฟเวอร์ (ทีมพัฒนาเกม) · ร่วมกับ อินฟราเซิร์ฟเวอร์ (ทีมอินฟรา)

autoscaling เพิ่มเครื่องไม่ทัน Autoscaling lag

เมื่อคนแห่เข้ามา ระบบจะเพิ่มเซิร์ฟเวอร์อัตโนมัติ แต่ต้องใช้เวลาเตรียมหลายนาที และระหว่างนั้นเซิร์ฟเวอร์เดิมรับโหลดเกิน

ทำไม: อีเวนต์เริ่มแล้วผู้เล่นแห่เชื่อมต่อเข้ามา → ผลคือ: กว่าเซิร์ฟเวอร์ใหม่จะเปิดและพร้อมใช้ต้องใช้เวลาหลายนาที → บนหน้าจอ: ไม่กี่นาทีแรกหลังอีเวนต์เริ่ม เกิดสโลว์โมชั่นและเข้าเกมไม่ได้

อาการ: สโลว์โมชั่น, เข้าเกมไม่ได้/โหลดไม่จบ · ผู้รับผิดชอบหลัก อินฟราเซิร์ฟเวอร์ (ทีมอินฟรา) · ร่วมกับ พัฒนาเซิร์ฟเวอร์ (ทีมพัฒนาเกม)

log และระบบมอนิเตอร์โหลดเกิน Logging / monitoring overhead

เมื่อเกิดเหตุขัดข้อง log จะพุ่งขึ้นมาก และเซิร์ฟเวอร์ที่ส่ง log แบบ synchronous จะยิ่งช้าลงเพราะ log

ทำไม: เกิด error แล้วปริมาณ log และเมตริกที่ส่งออกพุ่ง → ผลคือ: log collector รับไม่ทัน เซิร์ฟเวอร์ที่ส่งแบบ synchronous ต้องรอ → บนหน้าจอ: ตอนเกิดเหตุขัดข้อง อาการกระตุกและค้างยิ่งหนักขึ้นเพราะ log

อาการ: กระตุก, ค้าง · ผู้รับผิดชอบหลัก พัฒนาเซิร์ฟเวอร์ (ทีมพัฒนาเกม) · ร่วมกับ อินฟราเซิร์ฟเวอร์ (ทีมอินฟรา)

นาฬิการะหว่างเซิร์ฟเวอร์ไม่ตรงกัน Clock skew between servers

ถ้านาฬิกาของแต่ละเซิร์ฟเวอร์ต่างกันเล็กน้อย การตัดสินคูลดาวน์, บัฟ และเวลาเริ่มอีเวนต์จะไม่ตรงกันระหว่างเซิร์ฟเวอร์

ทำไม: นาฬิกาของเซิร์ฟเวอร์ที่การซิงก์เวลาหยุดทำงาน ห่างจากเซิร์ฟเวอร์อื่นหลายร้อย ms ถึงหลายวินาที → ผลคือ: ถ้าส่งเวลาสัมบูรณ์ เช่น เวลาที่บัฟหมด ข้ามเซิร์ฟเวอร์ การตัดสินจะคลาดเคลื่อน → บนหน้าจอ: ย้ายเซิร์ฟเวอร์แล้วบัฟหายไป หรือคูลดาวน์กลับมานับใหม่

อาการ: กดไม่ติด/โรลแบ็ค · ผู้รับผิดชอบหลัก อินฟราเซิร์ฟเวอร์ (ทีมอินฟรา) · ร่วมกับ พัฒนาเซิร์ฟเวอร์ (ทีมพัฒนาเกม)

มาโครและบอทมากเกินไป Bots and macros

บอทส่งคำขอถี่กว่าคนมาก จึงกินกำลังประมวลผลของเซิร์ฟเวอร์

ทำไม: บอทจำนวนมากเชื่อมต่อเข้ามา ฟาร์มมอน, เดิน และเทรดซ้ำไม่หยุด → ผลคือ: ปริมาณงานที่เซิร์ฟเวอร์ต้องประมวลผลและโหลดของ DB เพิ่มขึ้น → บนหน้าจอ: บางจุดฟาร์มหรือทั้งเซิร์ฟเวอร์ช้าลง (สโลว์โมชั่น, อินพุตดีเลย์)

อาการ: สโลว์โมชั่น, อินพุตดีเลย์ · ผู้รับผิดชอบหลัก พัฒนาเซิร์ฟเวอร์ (ทีมพัฒนาเกม) · ร่วมกับ อินฟราเครือข่าย (ทีมอินฟรา)

การพึ่งพาเซอร์วิสภายนอก External dependencies (auth, billing, platform)

ถ้าเซอร์วิสภายนอก เช่น ล็อกอินผ่านแพลตฟอร์ม, การชำระเงิน หรือการยืนยันตัวตน ช้าหรือหยุดทำงาน ผู้เล่นจะติดอยู่ที่ขั้นตอนนั้น

ทำไม: เซอร์วิสยืนยันตัวตนหรือชำระเงินภายนอกขัดข้องหรือช้า → ผลคือ: รอคำตอบอยู่ที่ขั้นตอนนั้น → บนหน้าจอ: ล็อกอินไม่ได้, ชำระเงินไม่สำเร็จ คนที่อยู่ในเกมแล้วเล่นได้ปกติ

อาการ: เข้าเกมไม่ได้/โหลดไม่จบ, กดไม่ติด/โรลแบ็ค · ผู้รับผิดชอบหลัก ภายนอก (ภายนอก) · ร่วมกับ พัฒนาเซิร์ฟเวอร์ (ทีมพัฒนาเกม)

matchmaking/การจัดรีเจียนผิดพลาด Wrong region assignment (matchmaking / GeoDNS)

ถ้าถูกจัดไปเซิร์ฟเวอร์ในรีเจียนที่ไกลแทนรีเจียนที่ใกล้ ผู้เล่นคนนั้นจะปิงสูงตลอดแม้เน็ตจะปกติ

ทำไม: ข้อมูล GeoIP ผิด, VPN, จัดทั้งปาร์ตี้ตามปิงเฉลี่ยของสมาชิก, กฎที่ขยายไปถึงรีเจียนไกลเมื่อคนไม่พอ, การจัดตามตำแหน่งของ DNS resolver → ผลคือ: มีรีเจียนที่ใกล้อยู่แล้ว แต่ไปเชื่อมต่อกับเซิร์ฟเวอร์ในรีเจียนข้ามทะเล → บนหน้าจอ: ในเกมที่มีเซิร์ฟเวอร์หลายรีเจียน เราคนเดียว (หรือแค่ปาร์ตี้เรา) ปิงสูงตลอด และมีอินพุตดีเลย์, โดนดีดกลับ, สกิลไม่ออก

อาการ: อินพุตดีเลย์, ดีดกลับ, กดไม่ติด/โรลแบ็ค · ผู้รับผิดชอบหลัก พัฒนาเซิร์ฟเวอร์ (ทีมพัฒนาเกม) · ร่วมกับ พัฒนาไคลเอนต์ (ทีมพัฒนาเกม), อินฟราเครือข่าย (ทีมอินฟรา), ภายนอก (ภายนอก)

ใบรับรอง TLS หมดอายุ/ตั้งค่าผิด TLS certificate expiry / misconfiguration

ถ้าใบรับรองของเซิร์ฟเวอร์ล็อกอิน, API หรือแพตช์หมดอายุ หรือขาดใบรับรองกลาง (intermediate certificate) ไคลเอนต์ที่เชื่อมต่อใหม่ตั้งแต่วินาทีนั้นจะเชื่อมต่อ TLS ไม่สำเร็จ

ทำไม: ใบรับรองพ้นวันหมดอายุ, เซิร์ฟเวอร์ส่งใบรับรองมาโดยไม่มีใบรับรองกลาง หรือวันที่/เวลาในเครื่องผู้เล่นผิด → ผลคือ: ไคลเอนต์ตรวจใบรับรองไม่ผ่านแล้วตัดการเชื่อมต่อ TLS → บนหน้าจอ: เข้าเกมไม่ได้/โหลดไม่จบที่ขั้นตอนล็อกอินหรือแพตช์, ใช้ไม่ได้เฉพาะฟีเจอร์ HTTPS อย่างร้านค้า คนที่เชื่อมต่ออยู่แล้วส่วนใหญ่เล่นได้ปกติ

อาการ: เข้าเกมไม่ได้/โหลดไม่จบ, กดไม่ติด/โรลแบ็ค · ผู้รับผิดชอบหลัก อินฟราเครือข่าย (ทีมอินฟรา) · ร่วมกับ อินฟราเซิร์ฟเวอร์ (ทีมอินฟรา), พัฒนาไคลเอนต์ (ทีมพัฒนาเกม)

คิวล็อกอินชนเพดานและ grace period ตอนเชื่อมต่อใหม่ไม่พอ Login queue cap / no reconnect grace

เมื่อคนแห่เข้ามาทันทีหลังเปิดตัวเกมหรือปิดปรับปรุง คิวล็อกอินจะชนเพดานจนปฏิเสธคนที่จะเข้าคิวใหม่ และผู้เล่นที่รออยู่ถ้าหลุดไปแป๊บเดียวก็เสียลำดับคิวแล้วต้องกลับไปต่อท้าย

ทำไม: คนที่จะเข้าเกมมีมากกว่าที่เซิร์ฟเวอร์ล็อกอินรับได้ในครั้งเดียว จึงต้องมีคิว และถ้าคิวยาวเกินไปก็ปฏิเสธการเข้าคิวใหม่เพื่อปกป้องเซิร์ฟเวอร์ → ผลคือ: ยิ่งคิวยาว เวลารอยิ่งนาน และระหว่างนั้นถ้า Wi-Fi หรือเครือข่ายมือถือหลุดแค่แป๊บเดียวก็เสียลำดับคิว → บนหน้าจอ: เข้าเกมไม่ได้/โหลดไม่จบ, เกมปิดตัวพร้อม error ระหว่างรอ, ต้องกลับไปรอท้ายคิวใหม่

อาการ: เข้าเกมไม่ได้/โหลดไม่จบ, หลุด · ผู้รับผิดชอบหลัก พัฒนาเซิร์ฟเวอร์ (ทีมพัฒนาเกม) · ร่วมกับ พัฒนาไคลเอนต์ (ทีมพัฒนาเกม), อินฟราเซิร์ฟเวอร์ (ทีมอินฟรา)

T1เครื่องมือ

ตัวช่วยวิเคราะห์อาการ

เมื่อได้รับการแจ้งแลค ลองเลือกแค่สามอย่าง คือ “ใคร, เมื่อไร และลักษณะอาการแบบไหน” ระบบจะแสดงสาเหตุในคู่มือนี้ที่เข้าข่ายที่สุดเรียงตามคะแนน ผลนี้ยังใช้ยืนยันสาเหตุไม่ได้ แต่พอสำหรับตัดสินใจว่าจะถามทีมไหนก่อน

T2เครื่องมือ

ชี้สาเหตุจากข้อมูลที่วัดได้

เมื่อได้รับการแจ้งปัญหาหรือ alert ให้ไล่แคบลงตามลำดับ ขอบเขต → ช่วงเวลา → ชั้น ความผิดปกติกระจุกอยู่ที่ไหนเป็นตัวแบ่งผู้รับผิดชอบที่สำคัญที่สุด เกิดพร้อมกับอะไรช่วยจำกัดสาเหตุให้แคบลง และเมตริกของชั้นไหนผิดปกติใช้ยืนยัน ถ้าใช้ “บนกราฟ” และ “วิธียืนยัน” ที่มีอยู่ในการ์ดสาเหตุทุกใบควบคู่กัน ก็จะเลือกสาเหตุที่น่าสงสัยจากรูปแบบกราฟ และหาจุดที่ต้องดูได้ทันที

ลำดับการชี้สาเหตุ

1 ขอบเขต

ความผิดปกติกระจุกอยู่ที่ไหน

  • บางประเทศ/บาง ISP (ASN) → ทีมอินฟราเครือข่าย ภายนอกISP
  • บางเซิร์ฟเวอร์/แชนแนล/โซน → ถ้าเมตริกของโฮสต์ปกติ ทีมพัฒนาเกมเซิร์ฟเวอร์ ถ้าผิดปกติ ทีมอินฟราเครื่องเซิร์ฟเวอร์/OS
  • บาง OS/อุปกรณ์/บิลด์ → ทีมพัฒนาเกมไคลเอนต์
  • คนเดียวหรือบ้านเดียว → ภายนอกสภาพแวดล้อมของผู้เล่น (ถ้าหลายคนมีลักษณะเดียวกัน ทีมพัฒนาเกมไคลเอนต์)
  • ทั้งหมดพร้อมกัน → ทรัพยากรที่ใช้ร่วมกัน (DB, โหลดบาลานเซอร์, เกตเวย์) หรือ deploy ที่เพิ่งปล่อยออกไป
2 ช่วงเวลา

เกิดพร้อมกับอะไร

3 ชั้น

เมตริกของชั้นไหนผิดปกติ

  1. เครือข่าย: RTT, แพ็กเก็ตหาย, อัตราการส่งซ้ำ, error และ discard ของอินเทอร์เฟซ
  2. โฮสต์: CPU รายคอร์, CPU steal, softirq, discard ของ NIC, memory pressure
  3. เซิร์ฟเวอร์เกม: เวลาต่อทิก, คิวรับของซ็อกเก็ต (Recv-Q), CPU รายเธรด, GC log
  4. DB: ความหน่วงของคิวรี, การรอล็อก, replication lag
  5. ไคลเอนต์: เฟรมไทม์, net graph, รายงานแครช

ตารางสัญญาณ

สิ่งที่ต้องดูถ้าเห็นแบบนี้เรียกใครก่อน
คิวรับของซ็อกเก็ตเซิร์ฟเวอร์ (Recv-Q)โปรเซสเซิร์ฟเวอร์อ่านไม่ทันจนสะสมทีมพัฒนาเกมเซิร์ฟเวอร์ (ทิกหยุดชะงัก/GC/ล็อก)
การส่งซ้ำและ RTT แยกตามการเชื่อมต่อเฉพาะบางการเชื่อมต่อ กระจุกที่บาง ASNทีมอินฟราเครือข่าย ภายนอกISP/เน็ตของผู้เล่น
ทุกการเชื่อมต่อของโฮสต์เดียวทีมอินฟราเครื่องเซิร์ฟเวอร์/OS (NIC/เคอร์เนล)
การส่งซ้ำและแบนด์วิดท์ของทั้งเซิร์ฟเวอร์เพิ่มขึ้นทันทีหลังลงแพตช์ขนาดหรือความถี่ของแพ็กเก็ตเปลี่ยนทีมพัฒนาเกมเซิร์ฟเวอร์ ทีมอินฟราเครือข่าย (MTU/ขีดจำกัด)
CPU steal/throttling/softirq/discard ของ NICเพิ่มขึ้นทีมอินฟราเครื่องเซิร์ฟเวอร์/OS
เธรดเดียวเต็ม 100%, การรอใน run queue, GC pauseเพิ่มขึ้นทีมพัฒนาเกมเซิร์ฟเวอร์
ความหน่วงของ DB เพิ่มขึ้น แต่จำนวนคิวรีเท่าเดิมIOPS/ล็อก/งานอื่นทีมอินฟราเครื่อง DB
จำนวนหรือรูปแบบคิวรีของ DB เปลี่ยนหลังลงแพตช์N+1, คิวรีใหม่ทีมพัฒนาเกมเซิร์ฟเวอร์
synthetic monitoring จากจุดวัดในต่างประเทศ (RTT/แพ็กเก็ตหาย)แย่ทีมอินฟราเครือข่าย ภายนอกISP
synthetic monitoring ปกติ แต่ฝั่งผู้เล่นแย่สภาพแวดล้อมของผู้เล่นหรือไคลเอนต์ภายนอกสภาพแวดล้อมของผู้เล่น ทีมพัฒนาเกมไคลเอนต์
การกระจายของสาเหตุที่หลุดheartbeat ไทม์เอาต์↑ / RST↑ / เซิร์ฟเวอร์ตัดเอง↑NAT/เส้นทาง / อุปกรณ์ / เซิร์ฟเวอร์
รอบที่แม่นยำ (ตรงชั่วโมง, ทุก N นาที)งานตามกำหนดเวลา/การสำรองข้อมูล/GC/อีเวนต์ทีมที่ตั้งตารางนั้น

หาสาเหตุจากรูปแบบกราฟ

แค่รู้ว่ากราฟมอนิเตอร์มีรูปแบบแบบไหน ตัวเลือกก็ลดลงได้มาก ด้านล่างรวบรวมสาเหตุที่ทำให้เกิดแต่ละรูปแบบไว้ทั้ง 13 แบบ ภาพเล็กในการ์ดสาเหตุก็ใช้รูปแบบเดียวกัน เส้นทึบคือเมตริกหลักที่ต้องดู เส้นประคือเมตริกที่ดูประกอบ (จำนวนผู้เล่น, การรอ, error ฯลฯ) และเส้นประจาง ๆ คือระดับปกติ

พุ่งเป็นรอบ

ปกติอยู่ต่ำ แล้วพุ่งขึ้นเป็นระยะเท่า ๆ กัน เช่น ทุกไม่กี่วินาที ทุกไม่กี่นาที หรือตรงต้นชั่วโมง

GC ฝั่งไคลเอนต์, การตรวจของโมดูลความปลอดภัยเกม (anti-cheat), การสแกน Wi-Fi เบื้องหลัง, งานตามกำหนดเวลา (scheduled job), ตัวจับเวลาทำงานพร้อมกันจำนวนมาก, GC ของเซิร์ฟเวอร์หยุดทั้งระบบ, GC pause ของสคริปต์เอนจิน, fsync ทะลัก, งานสำรองข้อมูล/บีบอัด/สแกน, checkpoint/log flush, งาน batch ขนาดใหญ่, แคชสแตมปีด

พุ่งแบบสุ่มเป็นครั้งคราว

พุ่งขึ้นอย่างไม่เป็นจังหวะ แล้วไม่นานก็กลับมาปกติ

เฟรมไทม์พุ่ง, extrapolation มากเกินไป (dead reckoning), client-side prediction ไม่ตรงกับเซิร์ฟเวอร์, fixed timestep ไล่ตามไม่ทันจนบานปลาย, โปรเซสเบื้องหลังแย่ง CPU, หน่วยความจำฝั่งไคลเอนต์ไม่พอและ swap, โหมดประหยัดพลังงานของ NIC และปัญหาไดรเวอร์, Wi-Fi ถูกรบกวนหรือสัญญาณอ่อน, สลับ 5G↔LTE บ่อย (บริเวณขอบพื้นที่ 5G), คุณภาพสายเน็ตแย่, ring buffer ไม่พอ, โอเวอร์เฮดจาก virtualization/noisy neighbor, บัฟเฟอร์ซ็อกเก็ตของเคอร์เนลไม่พอ, CPU steal (VM), หยุดชะงักจาก memory reclaim และ compaction, นาฬิการะบบกระโดด (NTP step), การส่งแบบ blocking เพราะไคลเอนต์ช้า, การตั้งค่าการส่งซ้ำของ reliable UDP, ข้อมูลสุดท้ายหายเพราะปิดการเชื่อมต่อแบบบังคับ (RST), synchronous call บนเธรดเกม, การเขียน log แบบ synchronous, lazy loading บนเซิร์ฟเวอร์, เดดล็อกใน DB, คำสั่ง Redis ที่ช้า, log และระบบมอนิเตอร์โหลดเกิน, lockstep ต้องรอผู้เล่นที่ช้าที่สุด, rollback netcode คาดการณ์พลาด, เล่นทันทีที่มาถึงโดยไม่มี timestamp, เซิร์ฟเวอร์ตรวจสอบเข้มงวดเกินไป, การคำนวณเส้นทางไม่ตรงกันในการซิงก์คำสั่ง, ลำดับการลงทะเบียนระยะมองเห็นพันกัน, สแนปช็อตตั้งต้น (baseline) หาย, ข้อความแจ้งการออกไปหาย (ตัวผี), สับสนจากการนำ entity ID กลับมาใช้ซ้ำ, การส่งซ้ำโดยไม่จำเป็นเพราะความหน่วงพุ่ง

ขึ้นเป็นขั้นบันไดจากจุดหนึ่ง

ขยับขึ้นไปหนึ่งขั้นแล้วอยู่ที่ระดับนั้นต่อไป โดยเริ่มจากเวลาที่ชัดเจน เช่น ตอนลงแพตช์ เปลี่ยนคอนฟิก หรือเปลี่ยนเส้นทาง

เคเบิลใต้น้ำ/วงจรต่างประเทศขัดข้อง, เส้นทาง BGP เปลี่ยนและ converge ใหม่, การอ้อมผ่านระบบป้องกัน DDoS และ false positive, ประสิทธิภาพเปลี่ยนไปหลังอัปเดต OS/เคอร์เนล/ไดรเวอร์/เฟิร์มแวร์, แพตช์ทำให้รูปแบบทราฟฟิกเปลี่ยน, คิวรีที่ไม่มีอินเด็กซ์, คิวรีช้าเพราะ query plan เปลี่ยน, ล็อกจากการเปลี่ยนสคีมา (DDL) ระหว่างให้บริการ, การพึ่งพาเซอร์วิสภายนอก, เส้นทางเปลี่ยน/เส้นทาง ECMP ที่เสีย

ค่อย ๆ สูงขึ้น

ไต่ขึ้นทีละนิดตลอดหลายชั่วโมงหรือหลายวัน ยิ่งเปิดไว้นานยิ่งสูง

ซิงก์นาฬิกาคลาดเคลื่อน, เวลาแบบ float สูญเสียความแม่นยำ, หน่วยความจำรั่วฝั่งไคลเอนต์, โหมดประหยัดพลังงานและ thermal throttling, หน่วยความจำรั่ว, สวอป, หน่วยความจำแตกกระจาย (fragmentation), ดิสก์เต็ม, ทรานแซกชันที่เปิดทิ้งไว้นาน, นาฬิการะหว่างเซิร์ฟเวอร์ไม่ตรงกัน

สูงเฉพาะบางช่วงเวลา

นูนขึ้นเป็นเนินเฉพาะช่วงเวลาเดิมของทุกวัน เช่น ช่วงพีคหัวค่ำ

ช่องสัญญาณ Wi-Fi แออัด, จุด peering แออัดช่วงพีค, false positive ของการตรวจสอบที่กระจุกกับผู้ใช้บาง ISP, คิวคอขวดล้น (แพ็กเก็ตหายจากความแออัด)

สูงตามจำนวนคนและโหลด

เมื่อผู้เล่นออนไลน์พร้อมกันหรือคนที่รวมตัวอยู่จุดเดียวเพิ่มขึ้น กราฟจะชันขึ้นเร็วกว่าจำนวนคนที่เพิ่ม

ภาระการเรนเดอร์ตัวละครจำนวนมาก, คอขวดการประมวลผลแพ็กเก็ตบนเมนเธรด, receive buffer ล้น, แอปอื่นในเครื่องเดียวกันแย่งแบนด์วิดท์, bufferbloat (คิวในเราเตอร์), microburst ที่สวิตช์, เธรดมากเกินไปและ context switch, CPU throttling ของคอนเทนเนอร์ (CFS quota), IP fragmentation ของแพ็กเก็ต UDP, โครงสร้างแบบ blocking I/O, ทิกเกินงบเวลา, การคำนวณระยะมองเห็น (AOI) พุ่ง (N²), ปริมาณ broadcast พุ่ง, พื้นที่ที่รันบนเธรดเดียวโหลดเกิน (hotspot), การแย่งล็อก, การคำนวณ pathfinding พุ่ง, ต้นทุนของ serialization และการบีบอัด, การต่อสู้ที่รุมเป้าหมายเดียว (เวิลด์บอส), การจัดสรรหน่วยความจำพุ่ง, แย่งล็อกบน hot row, การทำสำเนาข้อมูลล่าช้า, การผ่านเกตเวย์/พร็อกซี, ย้ายโซน (ส่งต่อตัวละครข้ามเซิร์ฟเวอร์), งบการส่ง/ลำดับความสำคัญต่อการเชื่อมต่อ, บัฟเฟอร์ตื้นล้นเพราะ burst จากฝั่งส่ง, duplex ไม่ตรงกัน

ชนเพดานแล้วแบนราบ

throughput หรือจำนวนการเชื่อมต่อแตะค่าหนึ่งแล้วขึ้นต่อไม่ได้ จากนั้นการรอและ error จะเพิ่มขึ้น

หน่วยความจำกราฟิก (VRAM) ไม่พอ, เราเตอร์สเปกไม่พอหรือร้อนเกิน, ISP จำกัดความเร็วและจัดการทราฟฟิก, ลิงก์ที่ใช้ร่วมกันเต็มเพราะ DDoS, ตารางเซสชันของไฟร์วอลล์เต็ม, ขีดจำกัดการเชื่อมต่อ/พอร์ตของ NAT gateway บนคลาวด์, ลิงก์ของดาต้าเซ็นเตอร์เต็ม, อินเทอร์รัปต์ของ NIC กระจุกที่คอร์เดียว, เกินขีดจำกัด PPS ของคลาวด์, แบนด์วิดท์ NIC เต็ม, ขีดจำกัด file descriptor, ตาราง conntrack ของเซิร์ฟเวอร์เต็ม, ephemeral port หมดในการเชื่อมต่อระหว่างเซิร์ฟเวอร์, ข้อความสะสมในคิว, thread pool หมด, GC thrashing (heap เหลือที่ว่างไม่พอ), burst credit ของดิสก์คลาวด์หมด, ขีดจำกัด IOPS/คิวดิสก์อิ่มตัว, connection pool หมด, ความล้มเหลวแบบลูกโซ่, คิวล็อกอินชนเพดานและ grace period ตอนเชื่อมต่อใหม่ไม่พอ, หน่วยความจำ/VRAM ไม่พอจนสตรีมล้มเหลว, policer ทิ้งส่วนที่เกิน, เครื่องเซิร์ฟเวอร์ฝั่งรับทิ้งแพ็กเก็ต, ไฟร์วอลล์/connection tracking ทิ้งแพ็กเก็ต, อุปกรณ์กลางทางเกินขีดความสามารถ (ไฟร์วอลล์/IPS/ระบบป้องกัน DDoS)

สูงตลอดตั้งแต่แรก

ไม่พุ่งขึ้นลง แต่อยู่ที่ค่าสูงตลอด เป็นกรณีที่มาจากโครงสร้าง เช่น ระยะทาง เส้นทาง หรือการออกแบบ

ไม่มี interpolation buffer หรือสั้นเกินไป, V-Sync และคิวเรนเดอร์, ความละเอียดของตัวจับเวลา, ดีเลย์จากจอ, อุปกรณ์อินพุต และ frame generation, ความหน่วงในการแพร่สัญญาณ (ระยะทางจริง), เส้นทางวิ่งอ้อม, interrupt coalescing มากเกินไป, ดีเลย์จากการรอรวมแพ็กเก็ตของ GRO/LRO, ความหน่วงพุ่งจากการจัดการพลังงานของเซิร์ฟเวอร์ (C-state/การปรับความถี่), อัลกอริทึม Nagle + delayed ACK, แคชมิส, ดีเลย์จาก seek ของ HDD, แสดงผลหลังเซิร์ฟเวอร์ตอบเท่านั้น (แบบ request-response), โปรโตคอลที่ต้องไปกลับตามลำดับหลายรอบ (chatty), ไม่มีการกดสกิลล่วงหน้า, client authoritative, รอทิกซ้อนสองชั้น, อัตราส่งสแนปช็อตต่ำ, fast retransmit โดยไม่จำเป็นเพราะแพ็กเก็ตสลับลำดับ, ค่า RTO ไม่เหมาะกับสภาพแวดล้อม, อุปกรณ์กลางทางตัด TCP option ทิ้ง

สูงเฉพาะบางกลุ่ม

ส่วนใหญ่ปกติ มีเพียงผู้เล่น พื้นที่ ISP หรืออุปกรณ์บางกลุ่มที่สูงแยกออกมา

สตอเรจช้าจนสตรีมแอสเซ็ตไม่ทัน, ไคลเอนต์แครช, โปรแกรมความปลอดภัยตรวจแพ็กเก็ต, โปรแกรมโอเวอร์เลย์รบกวน, ดีเลย์จากการเปลี่ยนสถานะ RRC (โหมดประหยัดพลังงานของวิทยุมือถือ), สัญญาณมือถืออ่อนหรืออยู่ในจุดอับสัญญาณ, ข้อจำกัดของ Wi-Fi สาธารณะและเครือข่ายบริษัท, อินเทอร์เน็ตดาวเทียม (วงโคจรต่ำ/ค้างฟ้า), เส้นทาง ECMP เส้นหนึ่งเสีย, การจำกัด UDP และตรวจแพ็กเก็ตระดับประเทศหรือ ISP, DNS ขัดข้อง/ช้า, เชื่อมต่อผ่าน VPN/โปรแกรมลดปิง, โหลดบาลานเซอร์กระจายไม่เท่ากัน/health check ตัดสินผิด, สายเสีย/พอร์ตมี error, MTU ไม่ตรงกัน (เฉพาะแพ็กเก็ตใหญ่ที่หาย), นโยบายจัดการไคลเอนต์ที่ช้า (slow consumer), keepalive ค่าเริ่มต้น 2 ชั่วโมง, slow start หลัง idle, การกระจายของ SO_REUSEPORT ไม่สมดุล, หน่วยความจำ NUMA ฝั่งไกล (remote), มาโครและบอทมากเกินไป, matchmaking/การจัดรีเจียนผิดพลาด, ช่วงเวลาตัดสินสั้นจนปิงกินหมด, การตัดสินผลที่ไม่มี lag compensation, lag compensation มากเกินไป, โครงสร้างแบบโฮสต์ (หัวห้อง), แสดงผลล่วงหน้าแล้วเซิร์ฟเวอร์ปฏิเสธ, คนเน็ตช้าขยับแบบกรอเร็วบนจอคนอื่น, อาการกรอเร็วจากเซิร์ฟเวอร์ที่ประมวลผลทันทีที่ได้รับ, ขนาดบัฟเฟอร์อินพุตของผู้เล่นแต่ละคน, เพื่อนในปาร์ตี้ที่ช้าหนึ่งคนกับกิมมิกบอส, สิทธิ์ควบคุมมอนสเตอร์อยู่ที่ไคลเอนต์ที่ช้า, ข้อมูลของตัวละครบางตัวใหญ่เกินไป, แชนแนล/อินสแตนซ์/phasing ต่างกัน, ทิ้งข้อความแจ้งการปรากฏตัวที่มาถึงระหว่างโหลด, พอร์ต UDP แบบตายตัวชนกัน, บั๊กแยกเซสชันด้วย IP/ID เครื่อง, การจำกัดการเปิดหลายไคลเอนต์, การเข้าถึงไฟล์แคช/แอสเซ็ตพร้อมกันจนชนกัน, ตัวเลือกการแสดงผลต่างกัน, เวอร์ชันไคลเอนต์/ข้อมูลไม่ตรงกัน, เอนทิตีถูกพักไว้เพราะประมาณเวลาคลาดเคลื่อน, แพ็กเก็ตหายช่วงไร้สาย, ข้อผิดพลาดทางกายภาพ (สายเสีย/โมดูลออปติก/คอนเน็กเตอร์), MTU black hole (เฉพาะแพ็กเก็ตใหญ่ที่หายซ้ำแล้วซ้ำอีก), ACK มาช้าหรือหาย (อัปโหลดเต็ม)

ขาดช่วงแล้วมารวดเดียว

ปริมาณที่รับได้เป็น 0 อยู่ช่วงหนึ่ง แล้วไหลเข้ามาพร้อมกันในทีเดียว

จำกัดการประมวลผลเมื่อย่อหรือสลับหน้าต่าง, handover ระหว่างเสาสัญญาณ (ขณะเดินทาง), การซ่อมบำรุงโฮสต์คลาวด์/live migration, ปัญหาไดรเวอร์/เฟิร์มแวร์ของ NIC, TCP HOL blocking, TCP RTO และ exponential backoff, จำกัดการประมวลผลของหน้าต่างเบื้องหลัง, thin stream กู้คืนช้า, zero window (การหยุดที่ดูเหมือนการส่งซ้ำ)

การเชื่อมต่อหลุดพร้อมกัน

จำนวนผู้เชื่อมต่อดิ่งลง หรือจำนวนการหลุดพุ่งขึ้นในชั่วพริบตา

สลับแอปมือถือไปเบื้องหลัง, สลับ Wi-Fi ↔ LTE/5G, NAT mapping หมดอายุ, IP ที่ ISP ให้ใช้ร่วมกัน (CGNAT), idle timeout ของโหลดบาลานเซอร์, connection tracking ของ security group บนคลาวด์หมดอายุ, การ failover ของอุปกรณ์เครือข่าย, OOM killer, ข้อผิดพลาด WSAECONNRESET บนซ็อกเก็ต UDP ของ Windows, เดดล็อก, เซิร์ฟเวอร์แครช, ลูปไม่รู้จบ/ลอจิกทำงานไม่หยุด, การเขียน core dump, failover ของ DB, ความคืบหน้าหายเพราะรอบเซฟยาว, เซิร์ฟเวอร์เสริมขัดข้อง, การ deploy และรีสตาร์ต, ใบรับรอง TLS หมดอายุ/ตั้งค่าผิด, mapping ของ NAT/โหลดบาลานเซอร์หมดอายุกลางการเชื่อมต่อ

พุ่งทันทีหลังเปิดเซิร์ฟ/ปิดปรับปรุง

พุ่งสูงทันทีหลังเปิดเซิร์ฟเวอร์หรือเริ่มอีเวนต์ แล้วค่อย ๆ ลดลง

โหลดแบบ synchronous และคอมไพล์ shader บนเมนเธรด, คิวรอเชื่อมต่อ (backlog) ล้น, spawn ทะลักเมื่อเข้าพื้นที่คนหนาแน่น, cold cache (หลังรีสตาร์ตใหม่ ๆ), คนแห่ล็อกอินและคิวรี N+1, autoscaling เพิ่มเครื่องไม่ทัน, ข้อมูลการปรากฏตัวที่ทะลักมาหลังเข้าโซนหาย, การส่งซ้ำคำขอเชื่อมต่อ (SYN)

ตรวจได้มากแค่ไหนโดยไม่ต้องใช้โค้ดเกม

นับวิธีตรวจที่ง่ายที่สุดของแต่ละสาเหตุ เครื่องมืออินฟราคือการตรวจด้วยเครื่องมือของ OS, เครือข่าย, คลาวด์ และ DB รวมถึงตัวเลือกตอนเริ่มรันไทม์ (GC log ฯลฯ) จึงไม่ต้องแก้โค้ดเกม log/เมตริกของเกมคือสิ่งที่จะเห็นได้ก็ต่อเมื่อเกมบันทึกไว้ เช่น เวลาต่อทิก และสาเหตุที่หลุด ชั้นไหนมีช่องนี้มาก ก็ยิ่งเป็นเหตุผลให้ขอทีมพัฒนาเกมเพิ่มการวัด (instrumentation)

วิธีอ่านตัวเลข

ค่าเฉลี่ยกลบค่าที่พุ่ง ในเซิร์ฟเวอร์ 20 ทิกต่อวินาที ถ้าทิกช้าแค่ 1% ทุกคนจะหยุดแวบราวทุก 5 วินาที แต่เวลาต่อทิกเฉลี่ยแทบไม่เปลี่ยน จึงต้องดูเปอร์เซ็นไทล์ด้วย p50 (ค่ามัธยฐาน) คือค่าที่ครึ่งหนึ่งเร็วกว่านี้ ส่วน p99 คือค่าที่อยู่ราวครั้งที่ช้าที่สุด 1 ครั้งใน 100 ครั้ง สิ่งที่ผู้เล่นจำได้ว่าเป็น “แลค” มักอยู่ฝั่ง p99

ช่วงเวลารวมค่าก็กลบค่าที่พุ่งเช่นกัน ในกราฟค่าเฉลี่ย 1 นาที การหยุด 1 วินาทีจะจางลงเหลือ 1/60 เวลาหาการหยุด ให้ดูค่าสูงสุดหรือ p99 ของกราฟเดียวกัน และช่วงเวลาที่สั้นกว่าประกอบด้วย

จิตเตอร์คือความไม่สม่ำเสมอของช่วงเวลาที่แพ็กเก็ตมาถึง แม้ปิงเฉลี่ยจะต่ำ ถ้าจิตเตอร์สูง interpolation buffer จะว่างจนเกิดอาการกระตุกหรือวาร์ป

วิธีวัดวัดอะไรข้อควรระวัง
ping (ICMP)เวลาไปกลับถึงอุปกรณ์เราเตอร์หรือเซิร์ฟเวอร์อาจประมวลผลการตอบ ICMP ช้าหรือจำกัดจำนวน จึงอาจได้ค่าต่างจากแพ็กเก็ตเกม ถ้าถูกบล็อกจะไม่มีการตอบเลย
mtr·tracerouteความหน่วงและแพ็กเก็ตหายราย hopถ้ามีอุปกรณ์ตรงกลางตัวเดียวที่แพ็กเก็ตหายสูง แต่ hop หลังจากนั้นปกติ มีโอกาสสูงที่อุปกรณ์นั้นแค่ลดการตอบ ICMP การหายที่ต่อเนื่องไปถึงปลายทางเท่านั้นคือการหายจริง
TCP RTT (rtt ใน ss -ti)เวลาไปกลับที่เคอร์เนลวัดให้แต่ละการเชื่อมต่อเป็นค่าจากการเชื่อมต่อของเกมจริง จึงเชื่อถือได้มากที่สุด ดูแยกรายผู้เล่นได้จากฝั่งเซิร์ฟเวอร์
ปิงในเกมเวลาไปกลับที่เกมวัดด้วยข้อความของตัวเองถ้าวัดในลูปของเกม จะมีเวลารอเฟรมและทิกปนอยู่ แม้เน็ตปกติ ถ้าเซิร์ฟเวอร์หรือ PC ยุ่งก็จะสูงขึ้น

สิ่งที่ทำได้ทันที และสิ่งที่ควรเพิ่มในโค้ดเกม

ไม่ต้องใช้โค้ดเกม
  • เพิ่มมิติข้อมูล: เติมประเทศและ ISP (ASN) ให้ IP ไคลเอนต์ใน log การเชื่อมต่อและ log ของโหลดบาลานเซอร์ เพื่อให้เห็นกรณี “เฉพาะต่างประเทศ” หรือ “เฉพาะบาง ISP”
  • คุณภาพการเชื่อมต่อ: เก็บ RTT และการส่งซ้ำของแต่ละการเชื่อมต่อจากเซิร์ฟเวอร์ด้วย ss -ti หรือเครื่องมือ eBPF แล้วดูแยกตาม ASN
  • ดูภายในเซิร์ฟเวอร์จากภายนอก: คิวของซ็อกเก็ต, CPU รายเธรด (pidstat -t), การรอใน run queue, GC log ที่เปิดได้ด้วยตัวเลือกตอนเริ่มรันอย่างเดียว
  • วัดเส้นทาง: synthetic monitoring จากฝั่งประเทศหรือ ISP เป้าหมาย (RIPE Atlas, เซิร์ฟเวอร์สำหรับวัดในรีเจียนของคลาวด์) และ mtr
  • บันทึกการเปลี่ยนแปลง: แสดง deploy, แพตช์, การเปลี่ยนคอนฟิก และงานด้านเครือข่ายเป็นเส้นแนวตั้งบนกราฟทุกตัว นี่คือจุดเริ่มต้นในการชี้ว่าเป็นปัญหา “หลังลงแพตช์” หรือไม่
เพิ่มในโค้ดเกมให้น้อยที่สุด
  • รายงานสรุปจากไคลเอนต์: ทุก 30–60 วินาที ส่ง RTT p50/p95, จิตเตอร์, แพ็กเก็ตหาย, FPS, จำนวนครั้งที่เฟรมไทม์พุ่ง, บิลด์, เซิร์ฟเวอร์/แชนแนล
  • เมตริกทิกของเซิร์ฟเวอร์: เวลาต่อทิก p50/p99, จำนวนครั้งที่ทิกเกินงบ, จำนวนผู้เล่นแยกตามโซน, คิวส่งแยกตามการเชื่อมต่อ
  • รหัสสาเหตุที่หลุด: ใช้รหัสเดียวกันทั้งสองฝั่งสำหรับ heartbeat ไทม์เอาต์, RST, เซิร์ฟเวอร์ตัดเอง, ยืนยันตัวตนล้มเหลว และปิดปรับปรุง
  • session ID และเวลา: ทุก log มี ID ของเซสชัน ตัวละคร และเซิร์ฟเวอร์ พร้อมเวลา UTC ที่ซิงก์แล้ว
  • ปุ่มแจ้งแลค: ส่ง RTT, FPS และช่วงที่ทิกขาดหายใน 60 วินาทีล่าสุด ขึ้นไปพร้อม session ID
T3เครื่องมือ

กรณีศึกษาและขั้นตอน

รวบรวมลำดับการตรวจสอบของสองสถานการณ์ที่เจอบ่อย และกรณีเหตุขัดข้องจริงที่ผู้พัฒนาต้นทางและผู้ให้บริการเกมเปิดเผยเอง แต่ละขั้นตอนและแต่ละกรณีเชื่อมไปยังการ์ดสาเหตุที่เกี่ยวข้อง

ขั้นตอนตามสถานการณ์

แลคหลังลงแพตช์

ใช้เมื่อการแจ้งปัญหาแลคเพิ่มขึ้นตั้งแต่แพตช์หรือ deploy ครั้งใดครั้งหนึ่ง เช่น มีการแจ้งว่า “ผิดปกติตั้งแต่อัปเดตรอบนี้” เข้ามาหลายราย หรือกราฟขึ้นเป็นขั้นบันไดจากเวลาหนึ่งแล้วคงอยู่ที่ระดับนั้น

  1. ระบุเวลาเริ่มให้แน่ชัด แล้วรวบรวมการเปลี่ยนแปลงทั้งหมดก่อนและหลังเวลานั้น: หาเวลาที่การแจ้งปัญหาเริ่มเข้ามามาก และเวลาที่กราฟขึ้นเป็นขั้นบันได แล้วจดการเปลี่ยนแปลงที่ออกไปก่อนและหลังช่วงนั้นให้ครบ ดูให้ครอบคลุมทั้งแพตช์ไคลเอนต์, การ deploy เซิร์ฟเวอร์, การเปลี่ยนคอนฟิก, การเปลี่ยนสคีมา DB (DDL) และการรีสตาร์ต, งานเครือข่ายและไฟร์วอลล์ และการเปลี่ยนอินฟรา (ประเภทอินสแตนซ์, เคอร์เนล, ไดรเวอร์) ถ้าใช้ฟีเจอร์ annotation ของเครื่องมือมอนิเตอร์ขีดเส้นแนวตั้งไว้บนทุกกราฟทุกครั้งที่ deploy ขั้นตอนนี้จะเสร็จได้เร็ว ถ้าแพตช์เกมกับงานอินฟราออกไปในรอบปิดปรับปรุงเดียวกัน ให้เก็บไว้เป็นสาเหตุที่เป็นไปได้ทั้งคู่ เรียกใครก่อน: ทั้งทีมพัฒนาเกมและทีมอินฟราที่ออกการเปลี่ยนแปลงนั้น
  2. แยกขอบเขต: บิลด์, อุปกรณ์, เซิร์ฟเวอร์ และพื้นที่: ดูว่าความผิดปกติกระจุกอยู่ในมิติไหน แล้วเริ่มสงสัยตามนี้: ถ้าแย่เฉพาะผู้เล่นที่ใช้บิลด์ใหม่ คือไคลเอนต์ ถ้าแย่เฉพาะบาง OS, การ์ดจอ หรืออุปกรณ์ คือประสิทธิภาพไคลเอนต์หรือไดรเวอร์ ถ้าแย่เฉพาะบางเซิร์ฟเวอร์ แชนแนล หรือโซน คือเซิร์ฟเวอร์ ถ้าแย่เฉพาะบางประเทศหรือบาง ISP คือเส้นทางเครือข่าย ถ้าทุกคนแย่พร้อมกัน คือทรัพยากรที่ใช้ร่วมกัน (DB, โหลดบาลานเซอร์, เกตเวย์) หรือการ deploy เซิร์ฟเวอร์ที่เพิ่งออกไป ถ้า telemetry ของไคลเอนต์มีเลขบิลด์ ให้วางปิง, FPS, frame spike และจำนวนครั้งที่หลุดของบิลด์เก่ากับบิลด์ใหม่ไว้เทียบกัน ถ้าปิงเท่าเดิมแต่ FPS แย่ลงอย่างเดียว น่าจะเป็นที่ประสิทธิภาพไคลเอนต์มากกว่าเครือข่าย เรียกใครก่อน: ถ้ากระจุกตามบิลด์หรืออุปกรณ์ คือทีมพัฒนาเกม (ไคลเอนต์) ถ้ากระจุกตามเซิร์ฟเวอร์หรือแชนแนล คือทีมพัฒนาเกม (เซิร์ฟเวอร์) เมื่อเมตริกของโฮสต์ปกติ และทีมอินฟรา (เครื่องเซิร์ฟเวอร์/OS) เมื่อผิดปกติ ถ้ากระจุกตามประเทศหรือ ISP คือทีมอินฟรา (เครือข่าย)
  3. เทียบเวอร์ชันใหม่กับเวอร์ชันเก่าในช่วงเวลาเดียวกัน: ถ้าเทียบแค่ก่อนกับหลัง deploy การเปลี่ยนแปลงตามวันในสัปดาห์ ช่วงเวลา และอีเวนต์จะปนเข้ามาจนตัดสินได้ยาก ถ้าทำได้ ให้ลงเวอร์ชันใหม่บนเซิร์ฟเวอร์บางส่วน (canary) ก่อน แล้วเทียบกับเซิร์ฟเวอร์เวอร์ชันเก่าในช่วงเวลาเดียวกัน (กลุ่มควบคุม) ทั้งเวลาต่อทิก p50 และ p99, จำนวนทิกที่เกินงบ, CPU, หน่วยความจำ และอัตรา error ถ้า deploy ไปทั้งหมดแล้ว ให้เทียบกับวันเดียวกันของสัปดาห์ก่อนในช่วงเวลาเดียวกัน ถ้าดูแค่ค่าเฉลี่ยรวมของทุกเซิร์ฟเวอร์ ปัญหาในบางเซิร์ฟเวอร์หรือบางโซนจะถูกกลบ จึงต้องแยกดูตามเซิร์ฟเวอร์และโซน เรียกใครก่อน: ทีมพัฒนาเกม (เซิร์ฟเวอร์)
  4. เทียบ fingerprint ของทราฟฟิกก่อนและหลังแพตช์: แม้ไม่รู้โค้ดเซิร์ฟเวอร์ ก็ตรวจได้จากค่าที่เห็นฝั่งเครือข่ายว่าแพตช์เปลี่ยนรูปแบบทราฟฟิกหรือไม่ ให้เทียบก่อนและหลังทั้งจำนวนแพ็กเก็ตต่อวินาทีต่อผู้เล่น (pps) และจำนวนไบต์, ขนาดแพ็กเก็ตเฉลี่ยและสูงสุด, จำนวนการเชื่อมต่อ และขนาด burst ขาออกที่ส่งออกไปพร้อมกันทุกทิก ถ้าแพ็กเก็ต UDP เริ่มใหญ่เกิน path MTU (ปกติ 1,500 ไบต์) จะเกิด IP fragmentation แค่ fragment เดียวหาย แพ็กเก็ตทั้งก้อนก็หาย และ NAT หรือไฟร์วอลล์บางตัวก็ทิ้ง fragment ไปเลย ผู้เล่นที่เส้นทางผ่านช่วงที่ MTU เล็ก (tunnel, VPN) จะเจอเฉพาะแพ็กเก็ตใหญ่ที่หายไป ถ้า pps เพิ่มขึ้น ให้ดูว่าชนขีดจำกัด PPS ของอินสแตนซ์บนคลาวด์ หรือขีดจำกัดการประมวลผลของไฟร์วอลล์และอุปกรณ์ป้องกัน DDoS หรือไม่ เรียกใครก่อน: ถ้า fingerprint เปลี่ยน ให้แนบหลักฐานส่งทีมพัฒนาเกม (เซิร์ฟเวอร์) ถ้า fingerprint เท่าเดิมแต่แพ็กเก็ตหายและการส่งซ้ำเพิ่มขึ้นอย่างเดียว คือทีมอินฟรา (เครือข่าย)
  5. เทียบประเภทและจำนวนครั้งของคิวรี DB ก่อนและหลังแพตช์: ถ้าความหน่วงของ DB สูงขึ้น ให้ดูก่อนว่าจำนวนคิวรี (QPS) สูงขึ้นด้วยหรือไม่ pg_stat_statements ของ PostgreSQL และสรุปแบบ digest ของ MySQL Performance Schema จะรวมคิวรีที่ต่างกันแค่ค่าไว้เป็นประเภทเดียว แล้วสะสมจำนวนครั้งที่รันและเวลารวมไว้ให้ เมื่อเทียบรายการคิวรีอันดับต้น ๆ ก่อนและหลังแพตช์ จะเห็นคิวรีที่เพิ่งเกิดขึ้นใหม่, คิวรีที่จำนวนครั้งเพิ่มขึ้นหลายเท่า (N+1) และคิวรีที่อ่านทั้งตารางโดยไม่ใช้อินเด็กซ์ (ใน MySQL ดูคอลัมน์ SUM_NO_INDEX_USED) เรียกใครก่อน: ถ้า QPS หรือรูปแบบคิวรีเปลี่ยน คือทีมพัฒนาเกม (เซิร์ฟเวอร์) ถ้าคิวรีเหมือนเดิมแต่ความหน่วงเพิ่มขึ้นอย่างเดียว คือทีมอินฟรา (DB: query plan, IOPS, ล็อก)
  6. แยกชั้นด้วยเมตริกของโฮสต์และโปรเซสเซิร์ฟเวอร์: ใช้ค่าที่เห็นจาก OS โดยไม่ต้องใช้โค้ด แยกว่าปัญหาอยู่ภายในตัวเซิร์ฟเวอร์หรือที่โฮสต์ ถ้า receive queue (Recv-Q) ของซ็อกเก็ตเซิร์ฟเวอร์สะสม แปลว่าโปรเซสเซิร์ฟเวอร์อ่านไม่ทัน (ทิกหยุดชะงัก, GC, ล็อก) ถ้าเธรดเดียวใช้ CPU 100% คือคอขวดที่เธรดเดียว ถ้าเวลาหยุดใน GC log เพิ่มขึ้น แปลว่ารูปแบบการใช้หน่วยความจำเปลี่ยนไป ดูด้วยว่า deploy ไปทั้งที่ยังตั้ง log level ไว้สูงจนการเขียน log เพิ่มขึ้นหรือไม่ ในทางกลับกัน ถ้า CPU steal, throttling หรือ NIC drop เพิ่มขึ้น ให้ดูอินฟราที่เปลี่ยนในเวลาเดียวกัน (ประเภทอินสแตนซ์, เคอร์เนล, ขีดจำกัดของคอนเทนเนอร์) เรียกใครก่อน: ถ้าเป็นสัญญาณจากภายในโปรเซส คือทีมพัฒนาเกม (เซิร์ฟเวอร์) ถ้าเป็นสัญญาณจากโฮสต์ คือทีมอินฟรา (เครื่องเซิร์ฟเวอร์/OS)
  7. ย้อนกลับเพื่อยืนยัน แล้วบันทึกผลไว้: ย้อนการเปลี่ยนแปลงที่น่าสงสัยที่สุดเฉพาะบางเซิร์ฟเวอร์หรือบางผู้เล่น (โรลแบ็ค, ปิด feature flag) หรือคืนคอนฟิกเป็นค่าเดิม แล้วดูว่าอาการหายไปด้วยหรือไม่ ถ้าดีขึ้นเฉพาะฝั่งที่ย้อนกลับ ก็ยืนยันสาเหตุได้ การย้อนกลับเองก็อาจทำให้ช้าไปชั่วครู่จากการรีสตาร์ตและ cold cache ถ้าไม่เร่งด่วนให้ทำในช่วงที่คนน้อย บันทึกผลพร้อม ID สาเหตุลงในบันทึกเหตุขัดข้อง และย้ายขีดจำกัดของขนาดแพ็กเก็ต, จำนวนคิวรี และเวลาต่อทิก ไปเป็นรายการตรวจก่อน deploy ของแพตช์ถัดไป เรียกใครก่อน: ทีมที่ออกการเปลี่ยนแปลงนั้น

การขยายบริการไปประเทศหรือภูมิภาคใหม่

ใช้เมื่อเปิดให้บริการในประเทศใหม่ หรือเพิ่มรีเจียนหรือดาต้าเซ็นเตอร์ใหม่ ใช้ได้ทั้งตอนตรวจก่อนเปิด และตอนแยกแยะการแจ้งปัญหาแบบ “ในเกาหลีปกติดี มีแต่ผู้เล่นประเทศใหม่ที่แลค”

  1. วัดคุณภาพเส้นทางแยกตาม ISP ท้องถิ่นก่อนเปิดบริการ: วัดการกระจายของเวลาไปกลับ (RTT), จิตเตอร์ และแพ็กเก็ตหาย จาก ISP หลัก (ASN) แต่ละรายของประเทศเป้าหมายไปยังที่ตั้งเซิร์ฟเวอร์เกมที่เป็นตัวเลือก ค่าเฉลี่ยตัวเดียวจะกลบความต่างระหว่าง ISP จึงต้องดูค่ามัธยฐานและเปอร์เซ็นไทล์ที่ 95 แยกตาม ISP และแยกช่วงพีคหัวค่ำกับช่วงเช้ามืด เครือข่ายวัดผลสาธารณะ RIPE Atlas ให้เลือกประเทศหรือ ASN แล้วส่ง ping และ traceroute จาก probe ทั่วโลกได้ หรือจะเปิด VM ชั่วคราวในรีเจียนที่เป็นตัวเลือกเพื่อวัดก็ได้ อุปกรณ์ระหว่างทางบางตัวจำกัดการตอบ ICMP ถ้าเป็นไปได้จึงควรวัดด้วยโปรโตคอลและพอร์ตเดียวกับเกมด้วย ถ้ามีแต่ ISP บางรายที่อ้อมผ่านเมืองที่อยู่ไกลผิดปกติ คือปัญหา peering หรือเส้นทาง ISP เลือกเส้นทางโดยดูต้นทุนก่อนความหน่วง ปลายทางที่อยู่ใกล้จึงอาจวิ่งอ้อมไปไกลได้ เรียกใครก่อน: ทีมอินฟรา (เครือข่าย) ถ้าเส้นทางเป็นเรื่องฝั่ง ISP คือภายนอก (ISP/IX)
  2. เทียบค่าที่วัดได้กับขีดจำกัดที่การออกแบบเกมรับได้: นำ RTT และจิตเตอร์ที่วัดได้ไปเทียบกับช่วงเวลาตัดสินของเกม (เวลาตอบสนองสำหรับการหลบหรือการแพร์รี), ขีดจำกัดของ lag compensation, ความยาวของ interpolation buffer และขนาดบัฟเฟอร์อินพุต ตัวอย่างเช่น ถ้าช่วงเวลาตัดสินการแพร์รีคือ 0.2 วินาที ผู้เล่นที่ใช้ ISP ซึ่งดีเลย์ไปกลับรวมกับ interpolation buffer นานกว่านั้น จะช้าเกินไปแม้กดทันเวลา ถ้าขยาย lag compensation เพื่อชดเชย คราวนี้ฝั่งที่โดนโจมตีจะแจ้งเข้ามามากขึ้นว่า “หลบหลังกำแพงแล้วยังโดน” ถ้ามี ISP จำนวนมากที่เกินขีดจำกัด ทีมอินฟราควรพิจารณาวางรีเจียนหรือ edge PoP ให้ใกล้ขึ้น ส่วนทีมพัฒนาเกมควรทบทวนค่าการตัดสินผล, interpolation และ lag compensation บท “รูปแบบการซิงก์” ในคู่มือนี้ใช้เป็นตารางอ้างอิงได้ เรียกใครก่อน: ทีมพัฒนาเกม (เซิร์ฟเวอร์/ไคลเอนต์: ขีดจำกัดของการออกแบบ), ทีมอินฟรา (เครือข่าย: ที่ตั้งรีเจียนและ PoP)
  3. ตรวจ MTU และดูว่า UDP ผ่านได้หรือไม่: ตรวจว่าแพ็กเก็ตที่ใหญ่ที่สุดของเกมผ่านเครือข่ายท้องถิ่นไปได้ครบทั้งก้อนหรือไม่ ส่งปิงที่ตั้งแฟล็ก Don't Fragment (DF) ไปหลายขนาดเพื่อวัด path MTU และดูว่ามีช่วงที่เล็กกว่า 1,500 ไบต์ เช่น PPPoE, tunnel หรือเครือข่ายมือถือหรือไม่ มาตรฐานสำหรับการส่งแบบ datagram อย่าง UDP (RFC 8899) แนะนำขนาดพื้นฐานที่ผ่านเส้นทางส่วนใหญ่ได้บน IPv4 ไว้ที่ 1,200 ไบต์ ถ้าแพ็กเก็ตใหญ่สุดของเกมเกินค่านี้ ให้ตกลงกับทีมพัฒนาเกมว่าจะลดขนาดหรือแบ่งส่ง ตรวจด้วยว่า Wi-Fi สาธารณะ เครือข่ายบริษัท หรือ ISP บางรายบล็อก UDP หรือพอร์ตเกม หรือจำกัดความเร็วหรือไม่ และดูว่ามีเส้นทางสำรองไว้ใช้ตอนโดนบล็อก (TCP, พอร์ต 443) หรือไม่ เรียกใครก่อน: ทีมอินฟรา (เครือข่าย) และทีมพัฒนาเกม (เซิร์ฟเวอร์: ขนาดแพ็กเก็ต)
  4. วัด idle timeout ของ NAT/CGNAT แล้วปรับช่วงห่างของ heartbeat ให้เข้ากัน: วัดว่าเราเตอร์ตามบ้านและเครือข่ายมือถือ (CGNAT) ในประเทศนั้นลบ mapping ของการเชื่อมต่อ UDP ที่ idle ภายในเวลาเท่าไร ในการทดสอบแต่ละรอบ ให้อุปกรณ์ทดสอบส่งแพ็กเก็ตหนึ่งตัวไปยังเซิร์ฟเวอร์เพื่อสร้าง mapping จากนั้นอุปกรณ์ไม่ส่งอะไรเลย แล้วให้เซิร์ฟเวอร์ส่งแพ็กเก็ตกลับไปหาอุปกรณ์เมื่อครบเวลาที่กำหนดไว้ (30 วินาที, 60 วินาที, 120 วินาที …) เวลาที่อุปกรณ์เริ่มไม่ได้รับแพ็กเก็ตนั้นคือ idle timeout ของเครือข่ายนั้น มาตรฐาน (RFC 4787) กำหนดว่า UDP mapping ต้องไม่หมดอายุก่อน 2 นาที และแนะนำค่าเริ่มต้นตั้งแต่ 5 นาทีขึ้นไป แต่ค่าจริงต่างกันมากตามอุปกรณ์ และบางอุปกรณ์ลบเร็วกว่านั้น mapping จะต่ออายุได้แน่นอนก็ต่อเมื่อมีแพ็กเก็ตขาออกจากอุปกรณ์เท่านั้น ไคลเอนต์จึงต้องเป็นฝ่ายส่ง heartbeat และต้องดูว่าช่วงห่างนั้นไม่เกินครึ่งหนึ่งของค่าที่สั้นที่สุดในบรรดาค่าที่วัดได้ และ idle timeout ของโหลดบาลานเซอร์และ security group บนคลาวด์ เรียกใครก่อน: ทีมพัฒนาเกม (ไคลเอนต์: ช่วงห่างของ heartbeat, เซิร์ฟเวอร์: ค่าไทม์เอาต์), ทีมอินฟรา (คอนฟิกโหลดบาลานเซอร์และ security group)
  5. ตรวจบริการภายนอกและอุปกรณ์ความปลอดภัยที่ทราฟฟิกในประเทศนั้นต้องผ่าน: ตรวจว่าการล็อกอินผ่านแพลตฟอร์มท้องถิ่น, การชำระเงิน และการยืนยันตัวตนตอบกลับด้วยความเร็วปกติหรือไม่, DNS ท้องถิ่นแปลงที่อยู่ของเซิร์ฟเวอร์ล็อกอินและเซิร์ฟเวอร์แพตช์ได้ถูกต้องหรือไม่ และ CDN ส่งแพตช์จากจุดให้บริการที่อยู่ใกล้ประเทศนั้นหรือไม่ ดูว่าช่วง IP ของประเทศใหม่ไม่ติดกฎบล็อกตามประเทศหรือการจำกัดอัตราของระบบป้องกัน DDoS และไฟร์วอลล์ โดยเฉพาะช่วง IP ของ CGNAT ที่ผู้ใช้บริการหลายรายใช้ IP เดียวร่วมกัน ต้องไม่ถูกบล็อกไปทั้งก้อน เรียกใครก่อน: ทีมอินฟรา (อุปกรณ์ความปลอดภัย, DNS, CDN), ภายนอก (แพลตฟอร์ม, ผู้ให้บริการชำระเงิน, ISP)
  6. หลังเปิดบริการ ให้แยกดูตามประเทศและ ASN: ใส่ประเทศและ ASN ให้ IP ของไคลเอนต์ใน log การเชื่อมต่อและ log ของโหลดบาลานเซอร์ แล้วดู RTT, การส่งซ้ำ, จำนวนครั้งที่หลุด และเหตุผลที่หลุด (heartbeat ไทม์เอาต์, RST, เซิร์ฟเวอร์เตะออก) แยกตามประเทศและ ISP ฐานข้อมูลฟรีอย่าง MaxMind GeoLite ASN แปลง IP เป็น ASN และชื่อองค์กรได้ และให้เก็บ IP แบบย่อเป็นระดับ /24 หรือ ASN ตามกฎข้อมูลส่วนบุคคลของประเทศนั้น ถ้ากระจุกอยู่ที่ ASN เดียว ให้ดูเส้นทางของ ISP นั้น (ทีมอินฟรา, ภายนอก) ถ้าแย่ทั้งประเทศใหม่ ให้ดูระยะทางและขีดจำกัดของการออกแบบ (ทีมอินฟรา, ทีมพัฒนาเกม) ถ้าแย่เฉพาะช่วงหัวค่ำ ให้ดู peering ที่แออัดก่อน ถ้าผู้เล่นบางส่วนปิงสูงตลอด ให้ดูร่วมกับทีมพัฒนาเกม (เซิร์ฟเวอร์) ว่าถูกจัดไปรีเจียนที่ไกลเพราะ GeoIP ผิด, VPN หรือการจัดรีเจียนตามหัวปาร์ตี้หรือไม่ ถ้า synthetic monitoring ปกติแต่ค่าที่ผู้เล่นจริงเจอแย่ แปลว่าเป็นที่สภาพแวดล้อมของผู้เล่นหรือฝั่งไคลเอนต์
  7. ตรวจผลกระทบที่ผู้เล่นจากพื้นที่ไกลมีต่อผู้เล่นคนอื่น: เมื่อผู้เล่นที่เชื่อมต่อจากที่ไกลมีมากขึ้น ผลกระทบไม่ได้หยุดอยู่แค่หน้าจอของคนนั้น อินพุตของคนที่ช้ามาถึงเป็นก้อน ตัวละครของคนนั้นจึงขยับแบบกรอเร็วบนหน้าจอคนอื่น และติดการตรวจความเร็วหรือคูลดาวน์ของเซิร์ฟเวอร์ จนเกิดอาการดีดกลับหรือสกิลถูกปฏิเสธ ในกิมมิคที่ต้องเล่นเป็นปาร์ตี้ การตอบสนองที่ช้าของคนเดียวทำให้ทั้งปาร์ตี้ล้มเหลว และในแบบ lockstep ทุกคนต้องรอคนที่ช้าที่สุด หลังเปิดประเทศใหม่ ให้ดูว่าผู้เล่นเดิมแจ้งปัญหาแบบ “เห็นตัวละครบางตัวผิดปกติ” มากขึ้นหรือไม่ แล้วตกลงกับทีมพัฒนาเกมเรื่องบัฟเฟอร์อินพุต, ค่าที่ยอมให้คลาดเคลื่อนในการตรวจ และการแยกพื้นที่ในการจับคู่ เรียกใครก่อน: ทีมพัฒนาเกม (เซิร์ฟเวอร์)

กรณีเหตุขัดข้องจริง

เลือกเฉพาะการวิเคราะห์หลังเกิดเหตุ (postmortem) ที่บริษัทเกมและบริษัทอินฟราเปิดเผยเอง สรุปเขียนภายในขอบเขตที่ต้นฉบับระบุไว้ รายละเอียดความเป็นมาให้ดูที่ต้นฉบับ

CCP Games 2014: เซิร์ฟเวอร์ EVE Online โหลดเกินในศึกกองยานขนาดใหญ่ที่ HED-GP

ในศึกกองยานขนาดใหญ่ที่ระบบดาว HED-GP ซึ่งบทวิเคราะห์ย้อนหลังเมื่อมกราคม 2014 กล่าวถึง เซิร์ฟเวอร์โหลดเกินอย่างหนัก Time Dilation (ฟีเจอร์ที่ทำให้เวลาในเกมช้าลงเมื่อโหลดเกิน) ลดลงไปแตะขีดต่ำสุดที่ 10% จนทั้งสนามรบกลายเป็นสโลว์โมชั่น แต่หลังจากนั้นโหลดก็ยังสะสมต่อไป ความล่าช้าของงานที่ประมวลผลการหยุดและการทำงานซ้ำของโมดูล (Dogma Lateness) สูงสุดถึง 193 วินาทีในเวลาเกม หรือประมาณ 32 นาทีในเวลาจริง ส่วนศึก 6VDT เมื่อกรกฎาคม 2013 ซึ่งมีขนาดใกล้เคียงกัน สูงสุดอยู่ที่ 42 วินาที (เวลาจริงประมาณ 7 นาที) CCP ออกตัวไว้ก่อนว่ายังไม่แน่ใจ เพราะเครื่องมือวิเคราะห์ประสิทธิภาพก็เพิ่มโหลดในตัวเอง จึงไม่ได้รันในสถานการณ์แบบนี้ แล้วชี้สาเหตุที่น่าจะเป็นไว้สองอย่าง อย่างแรกคือโหลดที่ประมวลผลไม่ทันสะสมต่อเนื่องเมื่อการรบยืดเยื้อ อย่างที่สองคือการใช้โดรนที่เพิ่มขึ้น จำนวนโดรนที่ถูกปล่อยออกมาระหว่างการรบ (ไม่นับซ้ำ) อยู่ที่ 21,123 ลำใน 6VDT และ 38,852 ลำใน HED-GP มากกว่ากัน 84% การส่งข้อมูลที่ต้องแจ้งการกระทำของคนหนึ่งให้ทุกคนที่มองเห็นรู้ จะเพิ่มขึ้นตามกำลังสองของจำนวนคน (O(n²)) และโดรนสร้างข้อความต่อการโจมตีหนึ่งครั้งมากกว่า นอกจากนี้โค้ดที่โดรนใช้เลือกเป้าหมายก็มักไล่ดูเป้าหมายที่โจมตีได้ทั้งหมดในสนามรบเดียวกัน ต้นทุนจึงเพิ่มขึ้นเกือบเป็น n²

เมื่อปริมาณงานในพื้นที่เดียวที่คนแห่มารวมกันเกินขีดจำกัด ทั้งพื้นที่นั้นจะเป็นสโลว์โมชั่น และยิ่งการรบยืดเยื้อ งานที่ประมวลผลไม่ทันก็ยิ่งสะสมจนอินพุตดีเลย์มากขึ้น สัญญาณที่ใช้ยืนยันคือเวลาต่อทิก, ปริมาณงานที่กองรอ, จำนวนผู้เล่น และจำนวนเอนทิตีของเซิร์ฟเวอร์ (โหนด) ที่ดูแลพื้นที่นั้น โดยมีจุดสังเกตคือพื้นที่อื่นยังปกติ ผู้รับผิดชอบหลักคือทีมพัฒนาเกม (เซิร์ฟเวอร์) จุดที่ต้องแก้คือขอบเขตของผู้รับที่ต้องแจ้งการกระทำหนึ่งครั้ง และต้นทุนการค้นหาเป้าหมายของ AI การออกแบบที่ทำให้เวลาในเกมช้าลงกำจัดการโหลดเกินไม่ได้ แต่ทำให้ทุกคนช้าลงในอัตราเดียวกัน จึงป้องกันไม่ให้การกระทำบางอย่างถูกเลื่อนออกไปไม่รู้จบ ต้นฉบับ

Riot Games 2015: ทราฟฟิก League of Legends ที่วิ่งอ้อมไปไกล และ Riot Direct

บทความเทคนิคที่ Riot Games อธิบายว่าทำไมอินเทอร์เน็ตจึงไม่เหมาะกับเกมแบบเรียลไทม์ ทราฟฟิกจริงที่ผู้เล่น League of Legends แจ้งเข้ามาควรวิ่งจากซานฟรานซิสโกไปพอร์ตแลนด์โดยตรง แต่อ้อมผ่านลอสแอนเจลิส, เดนเวอร์ และซีแอตเทิล จึงใช้เวลา 70 ms ทั้งที่ถ้าไปตรงใช้แค่ 14 ms Riot อธิบายว่าเมื่อเราเตอร์ล้นจนแพ็กเก็ตถูกทิ้ง จะเห็นแชมเปี้ยนตัวอื่นกระโดดไปมาบนหน้าจอ และ projectile ดูเหมือนวาร์ป Riot ชี้ว่าต้นเหตุคือเส้นทางและเราเตอร์ ผู้ให้บริการแบ็กโบนและ ISP ให้ความสำคัญกับต้นทุนก่อนความหน่วง จึงส่งทราฟฟิกตามเส้นทางที่ถูกที่สุด และเมื่อเส้นทางที่ BGP เลือกอ้อมไปไกล จำนวนเราเตอร์ที่ต้องผ่านก็เพิ่มขึ้น เราเตอร์รับภาระประมวลผลตามจำนวนแพ็กเก็ตโดยไม่ขึ้นกับขนาด แพ็กเก็ตเกมมีขนาดราว 55 ไบต์ ถ้าปริมาณข้อมูลเท่ากัน จำนวนแพ็กเก็ตจะมากกว่าแพ็กเก็ต 1,500 ไบต์ถึง 27 เท่า และเติมบัฟเฟอร์ขาเข้าของเราเตอร์ได้เร็วขึ้นตามไปด้วย Riot อธิบายว่าเราเตอร์จำนวนมากเลือกทิ้งแพ็กเก็ต UDP ก่อนเมื่อโหลดเกิน ทางแก้ของ Riot คือสร้างเครือข่ายของตัวเองชื่อ Riot Direct โดยวางเราเตอร์ไว้ที่ศูนย์กลางอินเทอร์เน็ตขนาดใหญ่ 10 แห่งในสหรัฐฯ และเชื่อมต่อตรง (peering) กับ ISP ให้ได้มากที่สุด ตามบทความตอนที่ 2 สัดส่วนผู้เล่นที่เล่นด้วยปิงต่ำกว่า 80 ms เพิ่มจาก 31% เป็น 50% ในเวลา 9 เดือนเศษ และหลังย้ายเซิร์ฟเวอร์เกมไปชิคาโก ก็กลายเป็น 80% ภายในคืนเดียว

ถ้าในประเทศเดียวกัน มีแต่ผู้เล่นที่ใช้ ISP บางรายที่ปิงสูงผิดปกติ ให้สงสัยเส้นทาง สัญญาณที่ใช้ยืนยันคือการกระจายของ RTT แยกตาม ISP (ASN) และเมืองที่ทราฟฟิกผ่านซึ่งเห็นใน traceroute ผู้รับผิดชอบหลักคือทีมอินฟรา (เครือข่าย) แก้ด้วยการทำ peering ตรงกับ ISP, การเชื่อมต่อ IX และการเลือกที่ตั้งเซิร์ฟเวอร์ ส่วนนโยบายเส้นทางฝั่ง ISP ต้องประสานกับภายนอก (ISP) กรณีนี้ยังแสดงให้เห็นว่าแค่ย้ายเซิร์ฟเวอร์ไปใกล้จุดศูนย์กลางของกลุ่มผู้เล่นก็ได้ผลมาก ต้นฉบับ

Riot Games 2020: โฮสต์ edge ของเซิร์ฟเวอร์ League of Legends ยุโรปและบราซิลโหลดเกิน

ปลายเดือนกุมภาพันธ์ 2020 เซิร์ฟเวอร์ EUW, EUNE และ BR ของ League of Legends ขัดข้องหลายครั้ง จำนวนเกมที่เริ่มใหม่ลดลงอย่างมาก บริการ backend อย่างระบบจับคู่และเซิร์ฟเวอร์เกมมีสถานะปกติทั้งหมด แต่แทบไม่มีทราฟฟิกเข้ามา Riot เลื่อนกำหนดการโหมดทัวร์นาเมนต์ (Clash) ออกไปหนึ่งสัปดาห์ เพื่อไม่ให้เปิดบนคลัสเตอร์ที่อาจไม่เสถียร บทวิเคราะห์ย้อนหลังไม่ได้ระบุว่าเหตุขัดข้องแต่ละครั้งกินเวลานานเท่าไร มีสามเรื่องเกิดซ้อนกัน คำขอที่ส่งไปยังบริการหนึ่งถูกสร้างผิดรูปแบบ ในบางกรณีจึงล้มเหลวและถูกลองใหม่ไม่หยุด จนปริมาณคำขอพุ่งสูง ปัญหาความเข้ากันได้ที่รู้กันอยู่แล้วระหว่างระบบคอนเทนเนอร์กับเวอร์ชัน OS ทำให้หน่วยความจำภายใน OS รั่วอยู่ การอัปเกรดเสร็จแค่ประมาณ 60% ของสภาพแวดล้อมคอนเทนเนอร์ทั้งหมดของ Riot และคลัสเตอร์ยุโรปกับละตินอเมริกายังอยู่ระหว่างดำเนินการ คอนเทนเนอร์ edge ที่รับทราฟฟิกจากอินเทอร์เน็ต กรองแล้วส่งต่อไปยัง backend ถูกจัดวางให้อยู่แยกกันภายในชาร์ด (กลุ่มเซิร์ฟเวอร์) เดียวกัน แต่ไม่มีกลไกกันไว้ระหว่างต่างชาร์ด ทุกครั้งที่ขัดข้อง คอนเทนเนอร์ edge ของอย่างน้อยสามชาร์ดจึงไปกระจุกอยู่บนโฮสต์เครื่องเดียว การลองใหม่ที่พุ่งสูงมาซ้อนทับที่โฮสต์นั้น และหน่วยความจำรั่วก็ทำให้โฮสต์นั้นหยุดทำงาน

ถ้าบริการ backend ทุกตัวตอบว่า “ปกติดี แต่ไม่มีทราฟฟิกเข้ามา” ให้ดูส่วนที่อยู่ด้านหน้า (edge, เกตเวย์, โหลดบาลานเซอร์) สัญญาณที่ใช้ยืนยันคือจำนวนการเชื่อมต่อขาเข้าแยกตามโฮสต์ว่ากระจุกอยู่ที่บางเครื่องหรือไม่ และอัตราความล้มเหลวและการลองใหม่ของคำขอบางประเภท ผู้รับผิดชอบหลักคือทีมพัฒนาเกม (เซิร์ฟเวอร์: คำขอที่ผิดรูปแบบและวิธีลองใหม่) ส่วนกฎการจัดวางคอนเทนเนอร์, การอัปเกรด OS และ alert เมื่อโหลดกระจุก เป็นหน้าที่ของทีมอินฟรา (เครื่องเซิร์ฟเวอร์/OS) Riot แก้โค้ดของคำขอ ปรับไม่ให้การลองใหม่พุ่งขึ้นฉับพลัน และตั้ง alert เมื่อโหลดกระจุกไว้จนกว่าจะพัฒนาการกระจายข้ามชาร์ดเสร็จ ต้นฉบับ

Riot Games 2021: League of Legends EUW ขัดข้อง 5 ชั่วโมง: DB เสริมตัวเดียวทำให้ทั้งเซิร์ฟเวอร์หยุด

วันที่ 22 มกราคม 2021 เซิร์ฟเวอร์ EUW ของ League of Legends ทำงานผิดปกตินานกว่า 5 ชั่วโมงเล็กน้อย เมตริกจำนวนผู้เล่นที่ล็อกอินอยู่และจำนวนผู้เล่นที่อยู่ในเกมขาดหายไปพร้อมกัน และในช่วงระหว่างการรีสตาร์ตสองครั้ง แม้จำนวนคนล็อกอินจะเพิ่มขึ้น แต่แทบไม่มีเกมไหนเริ่มได้เลย เซิร์ฟเวอร์หลักของ DB ที่ดูแลฟีเจอร์ที่ไม่สำคัญเกิดฮาร์ดแวร์เสีย และ DB นั้นไม่ได้ตั้งค่า failover อัตโนมัติไปยังเซิร์ฟเวอร์สำรอง แม้ connection pool จะแยกกันตาม DB แต่ทุก pool ใช้ thread pool เดียวกัน งานที่ส่งไปยัง DB ที่เสียไม่จบและถือครองเธรดไว้ จนเธรดที่ทั้งระบบต้องใช้หมด ท่ามกลาง alert ที่ถาโถมเข้ามา ทีมสงสัยการโจมตีเครือข่ายแบบมุ่งร้ายที่เพิ่งเจอเมื่อไม่นานมานี้และงานฮาร์ดแวร์ในภูมิภาคอื่นก่อน กว่าจะเห็น alert ของ DB ที่เสียก็ผ่านไปราว 1 ชั่วโมง ระบบทั้งหมดรันอยู่ใน JVM เดียว เมื่อ GC หยุดโปรเซสครั้งละหลายวินาทีท่ามกลางโหลดจากการเชื่อมต่อใหม่หลังรีสตาร์ต การเก็บเมตริกก็ขาดช่วงไปมาก คิวล็อกอินก็ไม่ได้จำกัดตามค่าที่ตั้งไว้ คนจึงไหลเข้ามาไม่สม่ำเสมอ

แม้แต่ DB เสริมตัวเดียวที่คิดว่าไม่สำคัญ ก็ทำให้ทั้งระบบหยุดได้ผ่านทรัพยากรที่ใช้ร่วมกันอย่าง thread pool สัญญาณที่ใช้ยืนยันคือจำนวนคำขอที่รออยู่แยกตาม DB, อัตราการใช้งาน thread pool และสัดส่วนจำนวนเกมที่เริ่มได้ซึ่งต่ำเกินไปเมื่อเทียบกับจำนวนการล็อกอิน ผู้รับผิดชอบคือทีมพัฒนาเกม (เซิร์ฟเวอร์: แยก thread pool และตั้งไทม์เอาต์) และทีมอินฟรา (DB: failover อัตโนมัติ) เมื่อ alert ถาโถมเข้ามา มักสงสัยปัญหาที่เพิ่งเจอ (เช่น การโจมตี) ก่อน จึงควรตัดทีละอย่างตามลำดับการชี้สาเหตุ (ขอบเขต → ช่วงเวลา → ชั้น) หลังรีสตาร์ตให้ดูด้วยว่าคิวล็อกอินจำกัดคนที่เข้ามาตามค่าที่ตั้งไว้หรือไม่ ต้นฉบับ

Roblox 2021: Roblox ล่ม 73 ชั่วโมง: การแย่งทรัพยากรในคลัสเตอร์ service discovery (Consul)

เหตุเริ่มในช่วงบ่ายวันที่ 28 ตุลาคม 2021 (เวลาแปซิฟิก) จาก CPU โหลดสูงบนเซิร์ฟเวอร์ Consul เครื่องหนึ่ง เวลา 16:35 จำนวนผู้เล่นที่ออนไลน์อยู่ลดลงเหลือครึ่งหนึ่งของปกติ แล้วบริการทั้งหมดก็หยุด ผู้เล่นทุกคนกลับเข้ามาได้อีกครั้งก็ต่อเมื่อ 16:45 วันที่ 31 ตุลาคม รวมเวลา 73 ชั่วโมงนับจากเริ่มเกิดเหตุ Roblox ระบุว่ามีผู้ใช้งาน 50 ล้านคนทุกวัน Roblox ใช้ HashiCorp Consul ทำ service discovery (ให้บริการต่าง ๆ หาที่อยู่ของกันและกัน), health check และ KV store โดยคลัสเตอร์ Consul ชุดเดียวรับหลายเวิร์กโหลดร่วมกัน ต้นเหตุมีสองอย่าง อย่างแรก ฟีเจอร์ streaming ตัวใหม่ของ Consul ที่ทยอยขยายการใช้งานมาหลายเดือน ถูกเปิดใช้กับบริการ routing ทราฟฟิกด้วยในวันก่อนเกิดเหตุ พร้อมเพิ่มจำนวนโหนดของบริการนั้นอีก 50% ภายใต้โหลดที่ทั้งอ่านและเขียนสูงมาก ฟีเจอร์นี้ทำให้เกิดการแย่งทรัพยากรที่ใช้ร่วมกันตัวหนึ่ง (Go channel) และเซิร์ฟเวอร์แบบ dual socket (NUMA) ที่มีคอร์มากกว่าซึ่งเปลี่ยนเข้าไประหว่างเกิดเหตุ ยิ่งทำให้การแย่งรุนแรงขึ้น อย่างที่สอง การจัดการรายการหน้าว่าง (freelist) ของ BoltDB ที่ Consul ใช้เก็บ Raft log ช้าลงอย่างผิดปกติ ทุกครั้งที่เพิ่มข้อมูลไม่เกิน 16 kB จะเขียนลงดิสก์ 7.8 MB ค่ามัธยฐานของความหน่วงในการเขียน KV ซึ่งปกติต่ำกว่า 300 ms กลายเป็น 2 วินาที และบนเซิร์ฟเวอร์ leader ที่ช้ายังพบ zero window ที่บัฟเฟอร์ TCP เต็มด้วย ระบบ telemetry พึ่ง Consul อยู่ เมตริกที่ต้องใช้หาสาเหตุจึงหายไปด้วย

เมื่อระบบพื้นฐานที่หลายบริการพึ่งพาร่วมกัน (service discovery, ที่เก็บคอนฟิก, ระบบยืนยันตัวตน) ช้าลง ทุกฟีเจอร์จะหยุดพร้อมกัน สัญญาณที่ใช้ยืนยันคือความหน่วงในการเขียน, การเปลี่ยน leader และ CPU ของระบบนั้น รวมถึงการเปลี่ยนคอนฟิกก่อนเกิดเหตุ ผู้รับผิดชอบมีทั้งทีมพัฒนาเกม (เซิร์ฟเวอร์) และทีมอินฟรา (เครื่องเซิร์ฟเวอร์/OS) ต้องแยกระบบมอนิเตอร์ไม่ให้พึ่งระบบที่ถูกเฝ้าดู จึงจะยังเห็นเมตริกได้ระหว่างเกิดเหตุ ตอนกู้คืน แคชยังว่างอยู่ ถ้ารับคนเข้ามาพร้อมกันทั้งหมดระบบอาจล่มซ้ำ Roblox จึงใช้ DNS คุมสัดส่วนผู้เล่นที่ให้เข้ามา และเพิ่มทีละประมาณ 10% ต้นฉบับ

Square Enix 2021: FINAL FANTASY XIV แออัดช่วงเปิดตัวภาคเสริม และ error ในคิวล็อกอิน

ตั้งแต่ช่วง early access ของภาคเสริม Endwalker ในเดือนธันวาคม 2021 ทุกเวิลด์แออัดอย่างหนัก คิวล็อกอินยาวขึ้น และมักเกิด Error 2002 ตอนล็อกอินจากหน้าเลือกตัวละครหรือระหว่างรอในคิว นอกจากนี้ยังมีบางเวิลด์และบางโซนล่ม (Error 3001) และคิวไทม์เอาต์ (Error 4004) ด้วย ณ วันที่ประกาศ 11 ธันวาคม ซึ่งเป็นวันที่ 8 ของ early access ความแออัดก็ยังไม่หมดไป Error 2002 เกิดได้สองกรณี กรณีแรกคือเมื่อจำนวนคนรอในแต่ละ logical data center เกิน 17,000 คน ซึ่งเป็นเพดานที่ตั้งไว้เพื่อกันไม่ให้คิวยาวจนเซิร์ฟเวอร์ล็อกอินล่ม ในกรณีนี้ไคลเอนต์จะถูกปิดไปเลย วันที่ 7 ธันวาคม มีการนำเครื่องสำรองสำหรับงานพัฒนามาเสริมเซิร์ฟเวอร์ล็อบบี้เพื่อยกเพดานขึ้น error นี้จึงลดลง แต่คิวกลับยาวขึ้น กรณีที่สองคือเมื่อเน็ตของผู้เล่นที่กำลังรอไม่เสถียร พอรอนานขึ้น การเชื่อมต่อที่ขาดไปชั่วครู่เพราะแพ็กเก็ตหายบนเส้นทางอินเทอร์เน็ตหรือ Wi-Fi ไม่เสถียรก็เกิดบ่อยขึ้น เซิร์ฟเวอร์ล็อบบี้จะรอการเชื่อมต่อใหม่ให้ราวหลายสิบวินาทีถึง 1 นาที ถ้าเชื่อมต่อกลับมาได้ในช่วงนั้นจะได้ต่อคิวจากตำแหน่งเดิม แต่ถ้าเกินจะต้องไปต่อท้ายคิว Square Enix ระบุว่าการแจ้งปัญหาส่วนใหญ่เป็นกรณีนี้ นอกจากนี้ยังเพิ่มเวิลด์ทันทีไม่ได้เพราะขาดแคลนเซมิคอนดักเตอร์

ยิ่งคิวยาว เน็ตที่ขาดช่วงสั้น ๆ ของผู้เล่นที่กำลังรอก็ยิ่งกลายเป็น error ในการเชื่อมต่อ แม้ความแออัดจะเท่ากัน error ก็ไปกระจุกที่คนที่ใช้ Wi-Fi หรือเน็ตไม่เสถียร จนกลายเป็นปัญหาที่ “เกิดกับบางคนเท่านั้น” สัญญาณที่ใช้ยืนยันคือความยาวคิว, เวลารอ และสัดส่วนการหลุดระหว่างรอคิวเมื่อแยกตามเหตุผลที่หลุด ผู้รับผิดชอบหลักคือทีมพัฒนาเกม (เซิร์ฟเวอร์: เพดานคิวและช่วงผ่อนผันการเชื่อมต่อใหม่) ส่วนการเพิ่มเซิร์ฟเวอร์ล็อบบี้และเซิร์ฟเวอร์เวิลด์ ทีมอินฟราทำร่วมกัน ถ้าตั้งช่วงผ่อนผันการเชื่อมต่อใหม่ให้นานพอ จะลดโอกาสที่เน็ตขาดช่วงสั้น ๆ ของผู้เล่นลุกลามจนเสียลำดับคิว ต้นฉบับ

Cloudflare 2020: คอนฟิกแบ็กโบนของ Cloudflare ผิดพลาด ทำให้ทราฟฟิกบางเมืองหายไป

เป็นเหตุขัดข้องด้านอินฟราประเภทที่เกมได้รับผลกระทบไปด้วย เพราะเกมจำนวนมากฝากเว็บ, API และการป้องกัน DDoS ไว้กับผู้ให้บริการ CDN วันที่ 17 กรกฎาคม 2020 ตั้งแต่ 21:12 ถึง 21:39 (UTC) รวม 27 นาที ทราฟฟิกรวมของเครือข่าย Cloudflare ลดลงประมาณ 50% ผลกระทบจำกัดอยู่ที่จุดให้บริการในบางเมืองของสหรัฐฯ, ยุโรป, รัสเซีย และบราซิลที่เชื่อมกับแบ็กโบน ส่วนจุดอื่นยังปกติ ช่วงแบ็กโบนนิวอาร์ก–ชิคาโกขัดข้อง ทำให้ช่วงแอตแลนตา–วอชิงตันแออัด วิศวกรจึงแก้คอนฟิกเราเตอร์เพื่อลดทราฟฟิกแบ็กโบนที่แอตแลนตา สิ่งที่ต้องทำคือปิดทั้งรายการนโยบาย (term) แต่กลับปิดแค่เงื่อนไขข้างใน (prefix-list) เราเตอร์แอตแลนตาจึงประกาศเส้นทาง BGP ทั้งหมดด้วยลำดับความสำคัญที่สูงกว่า (local-preference 200) ไปทั่วแบ็กโบน ขณะที่แต่ละจุดให้บริการตั้งลำดับความสำคัญของเส้นทางไปยังเซิร์ฟเวอร์ของตัวเองไว้ที่ 100 ทราฟฟิกของทุกจุดที่เชื่อมกับแบ็กโบนจึงแห่ไปที่แอตแลนตา แอตแลนตาโหลดเกิน ส่วนจุดที่ได้รับผลกระทบแทบไม่เหลือทราฟฟิกให้ประมวลผล เมื่อถอดเราเตอร์แอตแลนตาออกจากแบ็กโบน ระบบก็กลับมาปกติ Cloudflare ระบุว่าเหตุนี้ไม่เกี่ยวกับการโจมตีหรือการเจาะระบบ

ถ้าผู้เล่นในบางเมืองหรือบางพื้นที่เท่านั้นเจออาการหลุดหรือเข้าเกมไม่ได้/โหลดไม่จบพร้อมกัน ขณะที่ที่อื่นปกติ ให้สงสัยการเปลี่ยนคอนฟิกเส้นทางที่เพิ่งทำไปก่อน บนกราฟ CPU และทราฟฟิกจะพุ่งที่จุดเดียว ส่วนจุดที่ได้รับผลกระทบกลับดิ่งลงเกือบ 0 ผู้รับผิดชอบหลักคือทีมอินฟรา (เครือข่าย) ถ้าเป็นเหตุขัดข้องฝั่งผู้ให้บริการก็เป็นภายนอก Cloudflare ตัดสินใจตั้งเพดานจำนวนเส้นทางที่รับได้ (maximum-prefix) บนเซสชัน BGP ของแบ็กโบน และปรับลำดับความสำคัญไม่ให้จุดหนึ่งดึงทราฟฟิกของจุดอื่นไปได้ ต้นฉบับ

Fastly 2021: Fastly CDN เกิด error ทั่วโลก

เป็นเหตุขัดข้องด้านอินฟราประเภทที่เกมได้รับผลกระทบไปด้วย เพราะเกมจำนวนมากส่งไฟล์แพตช์, launcher และหน้าเว็บผ่าน CDN วันที่ 8 มิถุนายน 2021 ตั้งแต่ 09:47 (UTC) เครือข่ายของ Fastly 85% ตอบกลับเป็น error ภายใน 49 นาที เครือข่าย 95% กลับมาปกติ และเหตุขัดข้องคลี่คลายเมื่อ 12:35 ซอฟต์แวร์ที่เริ่ม deploy เมื่อ 12 พฤษภาคม มีบั๊กที่จะทำงานเมื่อคอนฟิกบางแบบของลูกค้าเจอเงื่อนไขบางอย่าง วันที่ 8 มิถุนายน ลูกค้ารายหนึ่งส่งการเปลี่ยนคอนฟิกที่ถูกต้องตามปกติขึ้นไป ทำให้เงื่อนไขนั้นครบพอดี Fastly ตรวจพบความผิดปกติภายใน 1 นาที และเมื่อหาคอนฟิกของลูกค้าที่เป็นต้นเหตุเจอแล้วปิดไป ระบบก็เริ่มกลับมา การ deploy ตัวแก้บั๊กเริ่มในวันเดียวกันเวลา 17:25

แม้โค้ดที่ deploy ไปหลายสัปดาห์แล้ว ถ้าเจอเงื่อนไขที่เกิดยาก ก็กลายเป็นเหตุขัดข้องทั่วโลกได้ในพริบตา สัญญาณที่ใช้ยืนยันฝั่งเกมคืออัตรา HTTP error ของคำขอแพตช์, launcher และเว็บที่สูงขึ้นพร้อมกันทุกภูมิภาค และหน้าสถานะของผู้ให้บริการ CDN จุดสังเกตคือ ถ้าการเชื่อมต่อเกมที่เข้าอยู่แล้วไม่ผ่าน CDN ก็ยังปกติ มีแค่การเชื่อมต่อใหม่, การดาวน์โหลดแพตช์ และการล็อกอินผ่านเว็บที่ใช้ไม่ได้ ผู้รับผิดชอบหลักคือภายนอก (ผู้ให้บริการ CDN) ส่วนทีมพัฒนาเกมและทีมอินฟราควรเตรียมเส้นทางสำรองไว้ เช่น ใช้ CDN ตั้งแต่สองเจ้าขึ้นไป หรือดึงตรงจากเซิร์ฟเวอร์ต้นทาง ต้นฉบับ

Meta 2021: คำสั่งเดียวบนแบ็กโบนของ Facebook ทำให้ DNS หายไปด้วย

เป็นเหตุขัดข้องด้านอินฟราที่ใช้เป็นบทเรียนกับเครือข่ายและ DNS ของบริษัทเกมเองได้ตรง ๆ วันที่ 4 ตุลาคม 2021 บริการของ Facebook (ปัจจุบันคือ Meta) เข้าใช้งานไม่ได้ทั่วโลก แบ็กโบนที่เชื่อมดาต้าเซ็นเตอร์ขาดทั้งหมด และฝั่งอินเทอร์เน็ตหาเซิร์ฟเวอร์ DNS ของ Facebook ไม่เจอ บทวิเคราะห์ย้อนหลังไม่ได้ระบุว่าเหตุขัดข้องกินเวลานานเท่าไร ระหว่างงานบำรุงรักษาตามรอบ คำสั่งที่รันเพื่อตรวจความจุของแบ็กโบนทั่วโลกกลับตัดการเชื่อมต่อทั้งหมดของแบ็กโบนโดยไม่ได้ตั้งใจ และเครื่องมือ audit ที่ควรกันคำสั่งแบบนี้ก็กันไม่ได้เพราะมีบั๊ก เซิร์ฟเวอร์ DNS ในจุดให้บริการขนาดเล็กถูกออกแบบให้ถือว่าตัวเองผิดปกติและถอนการประกาศเส้นทาง BGP เองเมื่อสื่อสารกับดาต้าเซ็นเตอร์ไม่ได้ จึงเข้าถึงจากอินเทอร์เน็ตไม่ได้ทั้งที่เซิร์ฟเวอร์ DNS ยังทำงานอยู่ ทั้งเส้นทางเข้าถึงปกติและการเข้าถึงแบบ out-of-band ขาดหมด เครื่องมือภายในก็ใช้ DNS ไม่ได้ จึงต้องส่งวิศวกรไปที่ดาต้าเซ็นเตอร์ด้วยตัวเอง และขั้นตอนด้านความปลอดภัยทำให้ใช้เวลานานขึ้นอีก ตอนกู้คืน การใช้ไฟฟ้าของแต่ละดาต้าเซ็นเตอร์ลดลงไปหลายสิบ MW ทีมประเมินว่าถ้าคืนโหลดทั้งหมดพร้อมกัน อาจเสี่ยงตั้งแต่ระบบไฟฟ้าไปจนถึงแคช จึงค่อย ๆ เพิ่มโหลดทีละขั้น

ถ้าเกิดอาการเข้าเกมไม่ได้/โหลดไม่จบพร้อมกันในทุกพื้นที่และทุก ISP ให้ดู DNS และเส้นทาง BGP ก่อนเซิร์ฟเวอร์เกม ตรวจจากนอกบริษัทได้ด้วยการคิวรี DNS จากภายนอกและข้อมูลเส้นทาง BGP ที่เปิดเผยสาธารณะ ผู้รับผิดชอบหลักคือทีมอินฟรา (เครือข่าย) ควรตรวจล่วงหน้าว่าเส้นทางเข้าถึงแบบ out-of-band ที่จะใช้ตอนเกิดเหตุและเครื่องมือภายในไม่ได้พึ่ง DNS และเครือข่ายชุดเดียวกัน และตอนกู้คืนให้ค่อย ๆ เพิ่มโหลดทีละขั้นเพื่อไม่ให้การเชื่อมต่อใหม่ทะลักเข้ามาพร้อมกัน ต้นฉบับ

AWS 2021: เครือข่ายภายในของ AWS us-east-1 แออัด

เป็นเหตุขัดข้องด้านอินฟราประเภทที่เกมได้รับผลกระทบไปด้วย เพราะเกมจำนวนมากวางเซิร์ฟเวอร์, ระบบล็อกอิน และข้อมูลไว้บนคลาวด์สาธารณะ วันที่ 7 ธันวาคม 2021 เวลา 7:30 (เวลามาตรฐานแปซิฟิก) เครือข่ายภายในของรีเจียนเวอร์จิเนียเหนือ (us-east-1) เริ่มแออัด ตั้งแต่ 7:33 error และความหน่วงของ EC2 API เพิ่มขึ้นจนเปิดอินสแตนซ์ใหม่ได้ยาก (การเปิดอินสแตนซ์กลับมาได้เมื่อ 14:40) ตามมาด้วยล็อกอินคอนโซลไม่สำเร็จ, เปลี่ยนคอนฟิก Route 53 ไม่ได้ และเมตริก CloudWatch ล่าช้าและหายไปบางส่วน อุปกรณ์เครือข่ายกลับมาปกติเต็มที่เมื่อ 14:22 อินสแตนซ์ EC2 ที่รันอยู่แล้วและการตอบ DNS ที่มีอยู่เดิมไม่ได้รับผลกระทบ งานอัตโนมัติที่เพิ่มความจุของบริการหนึ่งบนเครือข่ายหลัก ทำให้ไคลเอนต์จำนวนมากบนเครือข่ายภายในทำงานแบบที่ไม่คาดคิด จนความพยายามเชื่อมต่อพุ่งสูง อุปกรณ์ที่เชื่อมเครือข่ายภายในกับเครือข่ายหลักรับไม่ไหว การสื่อสารจึงล่าช้า และความล่าช้านั้นก็ยิ่งเพิ่มความพยายามเชื่อมต่อและการลองใหม่ ความแออัดจึงลากยาว ไคลเอนต์มีกลไก backoff ที่เพิ่มช่วงห่างระหว่างคำขอเมื่อแออัดแบบนี้ แต่ข้อบกพร่องที่แฝงอยู่ทำให้กลไกนั้นทำงานไม่ถูกต้อง ระบบมอนิเตอร์ภายในก็พึ่งเครือข่ายเดียวกัน ทีมปฏิบัติการจึงไม่มีเมตริกแบบเรียลไทม์ให้ดู ต้องอาศัย log ในการรับมือ

ถ้าการลองใหม่ไม่ยืดช่วงห่างออกไป ความแออัดสั้น ๆ ก็กลายเป็นเหตุขัดข้องที่ยาวหลายชั่วโมง มองจากฝั่งเกม แม้เซิร์ฟเวอร์เกมที่รันอยู่แล้วจะปกติ แต่การเพิ่มเซิร์ฟเวอร์ใหม่ (autoscaling), ล็อกอิน, การจับคู่ และการชำระเงินที่ใช้ API ของคลาวด์ รวมถึงระบบมอนิเตอร์ ก็อาจใช้ไม่ได้ไปพร้อมกัน สัญญาณที่ใช้ยืนยันคือหน้าสถานะของผู้ให้บริการคลาวด์, อัตรา error ของ API คลาวด์ และการเปิดอินสแตนซ์ที่ล้มเหลว ผู้รับผิดชอบหลักคือภายนอก (ผู้ให้บริการคลาวด์) ทีมพัฒนาเกมควรใส่ exponential backoff ที่สุ่มช่วงห่างและจำกัดจำนวนครั้งให้การลองใหม่ทุกจุด ส่วนทีมอินฟราควรเตรียมความจุสำรองที่พอรับมือได้แม้เพิ่มเครื่องไม่ได้ และทางเลือกในรีเจียนอื่น ต้นฉบับ

Cloudflare 2025: DNS สาธารณะ 1.1.1.1 ของ Cloudflare ขัดข้อง

เป็นเหตุขัดข้องของ DNS resolver สาธารณะที่ผู้เล่นตั้งค่าเองในอุปกรณ์หรือเราเตอร์ เป็นประเภทที่ทุกเกมและทุกบริการใช้ไม่ได้พร้อมกันเฉพาะกับผู้เล่นที่ใช้ค่านั้น วันที่ 14 กรกฎาคม 2025 ตั้งแต่ 21:52 ถึง 22:54 (UTC) รวม 62 นาที resolver 1.1.1.1 ไม่ตอบกลับทั่วโลก Cloudflare ระบุว่าสำหรับผู้ใช้จำนวนมาก นี่หมายถึงแทบใช้บริการอินเทอร์เน็ตอะไรไม่ได้เลย การคิวรีผ่าน UDP, TCP และ DNS over TLS ได้รับผลกระทบ ส่วน DNS over HTTPS ซึ่งเชื่อมต่อด้วยชื่อโดเมนค่อนข้างเสถียร วันที่ 6 มิถุนายน ระหว่างเตรียม service topology (คอนฟิกที่กำหนดว่าจะประกาศช่วง IP จากจุดให้บริการใด) ของบริการอื่นที่จะใช้ในอนาคต ช่วง IP ของ resolver 1.1.1.1 ถูกผูกรวมเข้าไปในคอนฟิกนั้นโดยไม่ตั้งใจ วันที่ 14 กรกฎาคม เมื่อมีการเปลี่ยนคอนฟิกของบริการนั้น จุดที่ประกาศช่วง IP ของ resolver ก็ลดจากทุกจุดเหลือเพียงจุดเดียวซึ่งออฟไลน์อยู่ เส้นทาง BGP จึงถูกถอนทั่วโลก การเปลี่ยนแปลงนี้กระจายไปทุกดาต้าเซ็นเตอร์ทันทีโดยไม่ผ่าน canary deploy เมื่อย้อนคอนฟิกกลับเวลา 22:20 ทราฟฟิกกลับมาราว 77% แต่ระหว่างนั้นคอนฟิก IP ที่จำเป็นบนเซิร์ฟเวอร์ edge ราว 23% ถูกลบไป ต้องตั้งค่าใหม่ จึงกลับมาปกติเมื่อ 22:54 Cloudflare ระบุว่าเป็นความผิดพลาดของคอนฟิกภายใน และไม่เกี่ยวกับการโจมตีหรือ BGP hijacking

ถ้าเซิร์ฟเวอร์เกมและผู้เล่นคนอื่นปกติ แต่ผู้เล่นบางส่วนเท่านั้นเจออาการเข้าเกมไม่ได้/โหลดไม่จบกับเซิร์ฟเวอร์ล็อกอินหรือเซิร์ฟเวอร์แพตช์ ให้สงสัย DNS ที่ผู้เล่นกลุ่มนั้นใช้ จุดสังเกตคือเซสชันที่เชื่อมต่ออยู่แล้วยังอยู่ได้ มีแค่การเชื่อมต่อใหม่ที่ล้มเหลว ให้ผู้เล่นลองเปลี่ยนการตั้งค่า DNS หรือคิวรีที่อยู่เซิร์ฟเวอร์เองดู ก็แยกได้ทันที ผู้รับผิดชอบหลักคือภายนอก (ผู้ดูแล DNS/ISP) ถ้าทีมพัฒนาเกม (ไคลเอนต์) แยกข้อความแจ้งกรณีแปลงชื่อเป็น IP ไม่สำเร็จออกจาก error อื่น ฝ่ายบริการลูกค้าก็ชี้สาเหตุได้ทันที ต้นฉบับ

AWS 2025: DNS ของ DynamoDB ใน AWS us-east-1 ขัดข้อง และการกู้คืนที่ยืดเยื้อ

เป็นเหตุขัดข้องด้านอินฟราประเภทที่เกมได้รับผลกระทบไปด้วย เพราะเกมจำนวนมากวางเซิร์ฟเวอร์, ระบบล็อกอิน และข้อมูลไว้บนคลาวด์สาธารณะ ตั้งแต่ 23:48 วันที่ 19 ตุลาคม 2025 ถึง 14:20 วันที่ 20 (เวลาออมแสงแปซิฟิก) รีเจียนเวอร์จิเนียเหนือได้รับผลกระทบต่อเนื่องเป็นสามช่วง error ของ DynamoDB API เพิ่มขึ้นจนถึง 2:40 วันที่ 20, การเปิดอินสแตนซ์ EC2 ใหม่ล้มเหลวตั้งแต่ 2:25 ถึง 10:36 (ปัญหาการเชื่อมต่อของอินสแตนซ์ใหม่บางตัวคลี่คลายเมื่อ 13:50) และ error การเชื่อมต่อของ Network Load Balancer (NLB) บางตัวเพิ่มขึ้นตั้งแต่ 5:30 ถึง 14:09 ระบบอัตโนมัติที่จัดการ DNS ของ DynamoDB มี race condition แฝงอยู่ ในบรรดาตัวรันที่นำแผน DNS ไปใช้ (DNS Enactor) ซึ่งทำงานอยู่ใน availability zone ต่างกัน มีตัวหนึ่งที่ช้าผิดปกติแล้วเขียนแผนเก่าทับแผนใหม่ ทันทีหลังจากนั้น งานเก็บกวาดของตัวรันอีกตัวก็ลบแผนเก่านั้นทิ้ง DNS record ของ endpoint ระดับรีเจียน (dynamodb.us-east-1.amazonaws.com) จึงกลายเป็นค่าว่าง ระบบอัตโนมัติแก้เองไม่ได้ ต้องให้คนกู้คืนด้วยมือ ระบบจัดการเซิร์ฟเวอร์จริง (physical server) ของ EC2 พึ่ง DynamoDB อยู่ ระหว่างนั้น lease ที่ถือไว้กับเซิร์ฟเวอร์จริงแต่ละเครื่องจึงหมดอายุ หลัง DynamoDB กลับมา เซิร์ฟเวอร์จริงมีมากเกินไป งานต่อ lease ใหม่จึงไทม์เอาต์ก่อนเสร็จ และงานที่ต้องลองใหม่ก็สะสมขึ้นอีก จนตกอยู่ในสภาพ “ล่มเพราะความแออัด (congestive collapse)” การตั้งค่าเครือข่ายของอินสแตนซ์ที่เพิ่งเปิดกระจายไปช้า health check ของ NLB จึงสลับไปมาระหว่างสำเร็จกับล้มเหลว และแม้แต่โหนดที่ปกติก็ถูกถอดออกจาก DNS แล้วกลับเข้ามาซ้ำไปมา

DNS record ที่ผิดพลาดเพียงจุดเดียวลุกลามไปยังบริการอื่นที่พึ่งบริการนั้น และแม้ต้นเหตุจะคลี่คลายแล้ว งานที่กองรออยู่กับ health check ที่แกว่งไปมาก็ทำให้การกู้คืนใช้เวลาอีกหลายชั่วโมง มองจากฝั่งเกม เซิร์ฟเวอร์ที่รันอยู่แล้วอาจยังไหว แต่เปิดเซิร์ฟเวอร์ใหม่ไม่ได้ autoscaling จึงหยุด และเมื่อ health check แกว่ง โหลดบาลานเซอร์ก็อาจถอดเซิร์ฟเวอร์ที่ปกติออก สัญญาณที่ใช้ยืนยันคือหน้าสถานะของคลาวด์, อัตรา error ของ API บริการแบบ managed, การเปิดอินสแตนซ์ที่ล้มเหลว และจำนวน target ที่ปกติของโหลดบาลานเซอร์ ผู้รับผิดชอบหลักคือภายนอก (ผู้ให้บริการคลาวด์) ส่วนทีมอินฟราควรจำกัดจำนวนเซิร์ฟเวอร์ที่ถูกถอดออกพร้อมกันเพราะ health check ล้มเหลว และเตรียมทางเลือกในรีเจียนอื่น ต้นฉบับ

T4เครื่องมือ

แนวทางการแจ้งปัญหาแลค

ส่วนที่ทีมพัฒนาเกมและทีมอินฟราใช้เวลานานที่สุดในการหาสาเหตุ คือการหาว่า “เมื่อไร ที่ไหน และใคร” ถ้ากรอกรายการด้านล่างให้ครบ ก็จะหาช่วงเวลานั้นใน log และกราฟได้ทันที

T5เครื่องมือ

อภิธานศัพท์

คำที่มักได้ยินเมื่อคุยกับทีมพัฒนาเกมและทีมอินฟรา ลองพิมพ์ภาษาไทยหรือภาษาอังกฤษในช่องค้นหา

ปิง Ping, RTT
เวลาที่สัญญาณจากเราเดินทางไปถึงเซิร์ฟเวอร์แล้วกลับมา (ไปกลับ) ปิงที่เกมแสดงบางครั้งก็รวมเวลารอประมวลผลบนเซิร์ฟเวอร์ไว้ด้วย
ความหน่วง Latency
เวลาตั้งแต่แพ็กเก็ตออกเดินทางจนถึงปลายทาง มักหมายถึงทางเดียว จึงประมาณครึ่งหนึ่งของปิง
จิตเตอร์ Jitter
ความไม่สม่ำเสมอของช่วงเวลาที่แพ็กเก็ตมาถึง ต่อให้ปิงเฉลี่ยเท่ากัน ถ้าจิตเตอร์สูง ภาพก็จะกระตุก
แพ็กเก็ต Packet
ก้อนข้อมูลที่ส่งผ่านเครือข่ายในครั้งเดียว ปกติใหญ่สุด 1,500 ไบต์ ส่วนอัปเดตของเกมมีขนาดหลักสิบถึงหลักร้อยไบต์
แพ็กเก็ตหาย Packet loss
แพ็กเก็ตที่ส่งออกไปแล้วหายระหว่างทาง ไปไม่ถึงปลายทาง เกมที่ใช้ TCP แค่ 1% ก็รู้สึกได้ว่าหยุดแวบทุกไม่กี่วินาทีถึงสิบกว่าวินาที ส่วนเกม UDP ที่มี interpolation และส่งอินพุตซ้ำ อาจกลบได้ถึงระดับไม่กี่เปอร์เซ็นต์
แบนด์วิดท์ Bandwidth
ปริมาณข้อมูลสูงสุดที่ลิงก์ส่งได้ใน 1 วินาที (Mbps) เป็นคนละเรื่องกับว่าข้อมูลไปถึงเร็วแค่ไหน (ความหน่วง)
ทิก Tick
หน่วยที่เซิร์ฟเวอร์คำนวณสถานะเกมหนึ่งรอบ เซิร์ฟเวอร์ 20 ทิกคำนวณ 20 ครั้งใน 1 วินาที หรือทุก 50 ms
ทิกเรต Tick rate
จำนวนทิกใน 1 วินาที ยิ่งสูงยิ่งตอบสนองเร็ว แต่ค่าเซิร์ฟเวอร์และปริมาณข้อมูลที่ส่งก็เพิ่มขึ้น บางเกมตั้งความถี่ในการส่งแพ็กเก็ตให้ต่ำกว่าทิกเรตเพื่อประหยัดปริมาณข้อมูล
งบเวลาต่อทิก Tick budget
เวลาสูงสุดที่ต้องทำหนึ่งทิกให้เสร็จ ถ้าเกิน ทิกถัดไปจะช้าลงและช่วงห่างระหว่างทิกจะยืดออก
FPS Frames per second
จำนวนครั้งที่วาดภาพใน 1 วินาที ที่ 60 FPS หนึ่งเฟรมใช้เวลา 16.7 ms
เฟรมไทม์ Frame time
เวลาที่ใช้วาดหนึ่งเฟรม เฟรมที่พุ่งเป็นบางครั้งส่งผลต่อความรู้สึกมากกว่า FPS เฉลี่ย
สแนปช็อต Snapshot
สรุป “สถานะเกมตอนนี้” ที่เซิร์ฟเวอร์ส่งมาทุกทิก มีตำแหน่ง, HP, สถานะ ฯลฯ ส่วนใหญ่จะคัดส่งเฉพาะส่วนที่ต่างจากสถานะที่ฝั่งรับมีอยู่แล้ว (delta compression)
อินเทอร์โพเลชัน Interpolation
เทคนิคที่วาดเชื่อมระหว่างสแนปช็อตสองอันที่ได้รับ ให้ภาพดูลื่น แลกกับการแสดงภาพย้อนหลังไปเล็กน้อย
บัฟเฟอร์อินเทอร์โพเลชัน Interpolation buffer
เวลาที่จงใจวาดภาพช้าลงเพื่อทำ interpolation เป็นเวลาเผื่อสำหรับดูดซับจิตเตอร์และแพ็กเก็ตหายหนึ่งสองตัว ปกติเป็น 2 เท่าของระยะห่างระหว่างแพ็กเก็ต (ถ้ารับ 20 ครั้งใน 1 วินาทีคือ 100 ms) บางเกมจะเพิ่มค่านี้เองเมื่อจิตเตอร์สูงขึ้น
เอ็กซ์ทราโพเลชัน Extrapolation, Dead reckoning
เทคนิคที่เมื่อไม่มีแพ็กเก็ตใหม่ จะใช้ความเร็วล่าสุดคาดเดาว่าวัตถุจะไปอยู่ตรงไหนแล้ววาดตามนั้น ถ้าเดาผิดจะดูเหมือนวาร์ป เกมจำนวนมากจึงเดาไว้แค่ราว 0.25 วินาทีแล้วหยุด (ค่าเริ่มต้นของ Source Engine คือ 0.25 วินาที)
การคาดการณ์ฝั่งไคลเอนต์ Client-side prediction
เทคนิคที่ขยับตัวละครของเราให้เห็นก่อน โดยไม่รอเซิร์ฟเวอร์ยืนยัน
การแก้ตำแหน่งตามเซิร์ฟเวอร์ Reconciliation
เมื่อผลจากเซิร์ฟเวอร์มาถึง จะเทียบกับที่คาดการณ์ไว้แล้วแก้ตำแหน่งตัวละครของเราให้ถูก โดยเริ่มจากตำแหน่งที่เซิร์ฟเวอร์ยืนยันแล้ว และนำอินพุตที่ยังไม่ได้รับการยืนยันมาคำนวณซ้ำ ถ้าต่างกันมากจะเห็นเป็นอาการดีดกลับ
การชดเชยแลค Lag compensation
เทคนิคที่เซิร์ฟเวอร์ย้อนเวลากลับไปยังจังหวะที่ผู้โจมตีเห็นอยู่ แล้วค่อยตรวจว่าโดนหรือไม่ มีการจำกัดระยะย้อนสูงสุดไว้ เพื่อไม่ให้ฝ่ายที่โดนยิงรู้สึกไม่ยุติธรรม เกมยิงแข่งขันมักตั้งไว้ราว 0.2–0.25 วินาที และบางกรณีย้อนได้ถึง 1 วินาทีเหมือนค่าเริ่มต้นของ Source Engine
เซิร์ฟเวอร์ผู้ตัดสิน Authoritative server
การออกแบบให้เซิร์ฟเวอร์เป็นผู้ตัดสินผลสุดท้ายแต่ผู้เดียว ช่วยกันการโกงได้ แต่ทุกผลลัพธ์ต้องรอไปกลับเซิร์ฟเวอร์ จึงต้องใช้ prediction และการแสดงผลล่วงหน้ากลบเวลารอ
ล็อกสเต็ป Deterministic lockstep
วิธีที่ทุกคนส่งกันแค่อินพุต แล้วคำนวณเหมือนกันทุกประการในเทิร์นเดียวกัน อินพุตจะมีดีเลย์คงที่ติดมาด้วย และถ้าอินพุตของใครสักคนมาช้า ทุกคนต้องรอ
บัฟเฟอร์อินพุตฝั่งเซิร์ฟเวอร์ Server-side input buffer
บัฟเฟอร์ที่เซิร์ฟเวอร์เก็บอินพุตของแต่ละคนไว้เล็กน้อย แล้วดึงมาใช้ทีละหนึ่งอินพุตต่อทิก คนที่จิตเตอร์สูงก็ดูลื่นในสายตาคนอื่น แต่การกระทำของคนนั้นจะถูกยืนยันบนเซิร์ฟเวอร์ช้าลงตามไปด้วย
ลิสเซินเซิร์ฟเวอร์ Listen server
วิธีที่ PC ของผู้เล่นคนหนึ่งทั้งเล่นเกมและทำหน้าที่เป็นเซิร์ฟเวอร์ไปพร้อมกัน หัวห้องจะมีปิงเป็น 0 แต่ถ้าเน็ตหรือ PC ของหัวห้องช้า ทุกคนจะแลคไปด้วย
เฟสซิง Phasing
ฟีเจอร์ที่แสดง NPC และภูมิประเทศต่างกันตามความคืบหน้าของเควสต์ แม้จะอยู่ที่เดียวกัน ถ้าความคืบหน้าของสองตัวละครต่างกัน การที่ฝั่งหนึ่งไม่เห็น NPC ถือเป็นเรื่องปกติ
โรลแบ็คเน็ตโค้ด Rollback netcode (GGPO)
วิธีที่คาดเดาอินพุตของอีกฝ่ายแล้วเดินเกมไปก่อน ถ้าอินพุตจริงไม่ตรงกับที่เดา จะย้อนกลับไปยังเฟรมในอดีตแล้วคำนวณใหม่ นิยมใช้ในเกมต่อสู้ คำนี้เป็นคนละความหมายกับการโรลแบ็คของ DB
การกดล่วงหน้า Input buffer, spell queue
การรับอินพุตถัดไปที่กดไว้ก่อนคูลดาวน์หรือท่าจะจบเล็กน้อย แล้วสั่งให้ทำงานทันทีที่จบ ช่วยไม่ให้มีเวลาไปกลับแทรกอยู่ระหว่างคอมโบ
การแสดงผลล่วงหน้า Client-side feedback
การเล่นแอนิเมชัน เสียง และเอฟเฟกต์ไปก่อนโดยไม่รอเซิร์ฟเวอร์ยืนยัน เฉพาะผลที่ต้องยืนยัน เช่น ดาเมจหรือของรางวัล จึงรอคำตอบจากเซิร์ฟเวอร์ ถ้าเซิร์ฟเวอร์ปฏิเสธ ต้องย้อนสิ่งที่แสดงไปแล้วกลับคืน
TCP Transmission Control Protocol
โปรโตคอลที่ส่งข้อมูลให้ครบและตามลำดับ ถ้าแพ็กเก็ตหาย จะไม่ส่งแพ็กเก็ตถัดไปให้เกมจนกว่าจะได้แพ็กเก็ตที่หายกลับมา
UDP User Datagram Protocol
โปรโตคอลที่ส่งตามที่ได้รับโดยไม่รับประกันอะไรเลย ไม่ต้องรอ แต่เกมต้องจัดการเรื่องแพ็กเก็ตหายและลำดับเอง
UDP แบบเชื่อถือได้ Reliable UDP (KCP, ENet…)
วิธีที่เขียนการส่งซ้ำและการรับประกันลำดับเองบน UDP เฉพาะเท่าที่จำเป็น
HOL blocking Head-of-line blocking
ปรากฏการณ์ที่ตัวหน้าสุดติดอยู่ ทำให้ทุกตัวที่ตามหลังต้องรอ เป็นต้นเหตุของอาการกรอเร็วใน TCP
RTO Retransmission timeout
ตัวจับเวลาส่งซ้ำ คือเวลาที่ TCP รอก่อนจะตัดสินว่าแพ็กเก็ตหายแล้วส่งใหม่ Linux ใช้ปิง + 200 ms ขึ้นไป และเพิ่มเป็นสองเท่าทุกครั้งที่ล้มเหลว
อัลกอริทึม Nagle Nagle’s algorithm
ฟีเจอร์ของ TCP ที่เก็บข้อมูลเล็ก ๆ ไว้จนกว่า ACK ของข้อมูลก่อนหน้าจะมาถึง แล้วส่งรวดเดียวเพื่อลดจำนวนแพ็กเก็ต ในเกมส่วนใหญ่ควรปิด
TCP_NODELAY TCP_NODELAY
socket option สำหรับปิดอัลกอริทึม Nagle ข้อความเล็ก ๆ จะถูกส่งทันที
ACK แบบหน่วงเวลา Delayed ACK
ฟีเจอร์ที่ส่งการยืนยันว่าได้รับข้อมูลช้าลงเล็กน้อย โดยรวมไปกับข้อมูลอื่น Linux ปกติใช้ 40 ms (สูงสุด 200 ms) ส่วน Windows รุ่นเก่าใช้ 200 ms และรุ่นใหม่ใช้ 40 ms
บัฟเฟอร์ซ็อกเก็ต SO_SNDBUF / SO_RCVBUF
ขนาดพื้นที่รอส่งและรอรับที่ OS กันไว้ให้แต่ละซ็อกเก็ต เล็กเกินไปจะล้น ใหญ่เกินไปข้อมูลเก่าจะกองรออยู่
keepalive SO_KEEPALIVE
ฟีเจอร์ของ TCP ที่ตรวจว่าการเชื่อมต่อที่ idle อยู่ยังใช้ได้หรือไม่ ค่าเริ่มต้นปิดอยู่ และต่อให้เปิด ค่าเริ่มต้นก็จะตรวจหลังผ่านไป 2 ชั่วโมง
RST TCP reset
สัญญาณของ TCP ที่ตัดการเชื่อมต่อทันที ข้อมูลที่ยังส่งไม่ออกจะถูกทิ้ง
ฮาร์ตบีต Heartbeat
สัญญาณ “ยังอยู่” ที่เกมส่งเองเป็นระยะ ใช้ตรวจจับการเชื่อมต่อที่ขาดไปแล้ว และรักษาการเชื่อมต่อในอุปกรณ์ระหว่างทางไว้
ไทม์เอาต์ Timeout
เกณฑ์ที่ถือว่าล้มเหลวถ้าไม่มีการตอบกลับภายในเวลานี้ สั้นเกินไปจะตัดสินผิด ยาวเกินไปจะตรวจพบช้า
NAT Network Address Translation
ฟีเจอร์ที่เราเตอร์ใช้พาอุปกรณ์หลายเครื่องในบ้านออกอินเทอร์เน็ตด้วย public IP เดียว และบันทึกการเชื่อมต่อไว้ในตาราง NAT
CGNAT Carrier-grade NAT
NAT ขนาดใหญ่ที่ ISP ใช้แบ่ง IP เดียวให้ลูกค้าหลายราย
MTU Maximum Transmission Unit
ขนาดแพ็กเก็ตใหญ่สุดที่ส่งได้ในครั้งเดียว ปกติ 1,500 ไบต์ และจะเล็กกว่านั้นในช่วงที่ผ่าน VPN หรือ PPPoE
บัฟเฟอร์โบลต Bufferbloat
ปรากฏการณ์ที่อุปกรณ์เก็บคิวไว้ยาวเกินไป จนความหน่วงเพิ่มเป็นหลายร้อย ms
SQM Smart Queue Management (fq_codel, CAKE)
ฟีเจอร์ของเราเตอร์ที่รักษาคิวให้สั้นและปล่อยข้อมูลแต่ละ flow อย่างเป็นธรรม เป็นวิธีแก้ bufferbloat
QoS Quality of Service
ฟีเจอร์ที่ให้ลำดับความสำคัญกับทราฟฟิกสำคัญเพื่อให้ส่งออกไปก่อน
พีริง Peering
จุดที่ ISP เชื่อมเครือข่ายของกันและกัน มักแออัดช่วงหัวค่ำ
BGP Border Gateway Protocol
กติกาที่ ISP ใช้บอกกันว่าจะส่งข้อมูลไปทางเส้นทางไหนบนอินเทอร์เน็ต ถ้าเปลี่ยน เส้นทางและปิงก็เปลี่ยนตาม
DDoS Distributed Denial of Service
การโจมตีที่ส่งทราฟฟิกปริมาณมหาศาลจากหลายแหล่งเพื่อทำให้บริการใช้การไม่ได้
สครับบิงเซ็นเตอร์ DDoS scrubbing center
จุดให้บริการของผู้ให้บริการป้องกัน DDoS ที่รับทราฟฟิกซึ่งมุ่งไปยังเซิร์ฟเวอร์ไว้ก่อนระหว่างถูกโจมตี กรองการโจมตีออก แล้วส่งต่อเฉพาะทราฟฟิกปกติ ถ้าจุดนี้อยู่ไกล เส้นทางก็จะยาวขึ้น
ไฟร์วอลล์ Firewall
อุปกรณ์หรือโปรแกรมที่ยอมให้เฉพาะการเชื่อมต่อที่อนุญาตผ่านไปได้ และติดตามการเชื่อมต่อด้วยตารางเซสชัน
โหลดบาลานเซอร์ Load balancer
อุปกรณ์ที่กระจายการเชื่อมต่อขาเข้าไปยังเซิร์ฟเวอร์หลายเครื่อง
ตารางเซสชัน Session table, conntrack
ตารางที่อุปกรณ์หรือ OS ใช้ติดตามการเชื่อมต่อที่มีอยู่ในขณะนั้น ขนาดมีขีดจำกัด
ไมโครเบิสต์ Microburst
ปรากฏการณ์ที่ค่าเฉลี่ยต่ำ แต่ทราฟฟิกกระจุกตัวในช่วงสั้นมาก ๆ ไม่ถึง 1 ms
NIC Network Interface Card
การ์ดเครือข่ายของเซิร์ฟเวอร์
ริงบัฟเฟอร์ Ring buffer
บัฟเฟอร์ที่เก็บแพ็กเก็ตที่ NIC รับมาไว้จนกว่า CPU จะดึงไปประมวลผล ใช้สล็อตจำนวนคงที่วนซ้ำไปเรื่อย ๆ เมื่อสล็อตเต็มหมด แพ็กเก็ตใหม่จะถูกทิ้ง
อินเทอร์รัปต์ Interrupt
สัญญาณที่อุปกรณ์ใช้บอก CPU ว่า “มีงานเข้ามาแล้ว”
RSS Receive Side Scaling
ฟีเจอร์ของ NIC ที่กระจายแพ็กเก็ตที่ได้รับไปยังหลาย receive queue เพื่อให้ CPU หลายคอร์ช่วยกันประมวลผล
PPS Packets per second
จำนวนแพ็กเก็ตต่อวินาที เซิร์ฟเวอร์เกมมักชนขีดจำกัดของตัวเลขนี้ก่อนแบนด์วิดท์
เคอร์เนล Kernel
แกนกลางของระบบปฏิบัติการ ดูแลเครือข่าย หน่วยความจำ และการแบ่ง CPU
backlog Listen backlog
คิวที่คำขอเชื่อมต่อใหม่รออยู่ระหว่างที่เซิร์ฟเวอร์ยังไม่รับไป เมื่อคิวเต็ม Linux จะทิ้งคำขอใหม่เงียบ ๆ ส่วน Windows จะส่งการตอบกลับว่าปฏิเสธ
TIME_WAIT TIME_WAIT
สถานะที่ฝั่งซึ่งปิดการเชื่อมต่อก่อนจะเก็บคู่พอร์ตนั้นไว้ชั่วครู่ (Linux 60 วินาที) เผื่อแพ็กเก็ตที่มาถึงช้า
CPU steal Steal time
เวลาที่ VM อยากใช้ CPU แต่ต้องรอ เพราะเครื่องจริงกำลังให้ CPU กับ VM ตัวอื่น ดูได้จากค่า st ใน top
CPU throttling CFS throttling
กลไกที่บังคับให้คอนเทนเนอร์หยุดจนถึงรอบถัดไป เมื่อใช้ CPU ครบโควตา (quota) ภายในรอบเวลาที่กำหนด (CFS period ปกติ 100 ms)
ไฟล์ดิสคริปเตอร์ File descriptor
หมายเลข (fd) ที่ติดกับไฟล์หรือการเชื่อมต่อแต่ละตัวที่โปรเซสเปิดไว้ จำนวนมีขีดจำกัด
เธรด Thread
หน่วยงานย่อยที่ทำงานอย่างอิสระภายในโปรแกรม หลายเธรดทำงานพร้อมกันได้
การสลับคอนเท็กซ์ Context switch
การที่ CPU สลับจากเธรดที่กำลังทำงานไปเป็นอีกเธรดหนึ่ง มีต้นทุน
ล็อก Lock, Mutex
กลไกที่กันไม่ให้หลายเธรดใช้ข้อมูลที่ใช้ร่วมกันพร้อมกัน ให้ใช้ได้ทีละเธรด
เดดล็อก Deadlock
สภาพที่เธรดต่าง ๆ รอล็อกที่อีกฝ่ายถืออยู่ จนหยุดนิ่งไปตลอดกาล
เธรดพูล Thread pool
กลุ่ม worker thread ที่สร้างเตรียมไว้ล่วงหน้า ถ้าทุกตัวไม่ว่าง งานใหม่ต้องรอ
I/O แบบอะซิงโครนัส epoll, IOCP, io_uring
วิธีที่ไม่รอ I/O แต่ไปทำงานอื่นก่อน แล้วรับแจ้งเตือนเมื่อ I/O เสร็จ
AOI Area of Interest
“ระยะที่มองเห็นได้” ของผู้เล่นแต่ละคน ส่งเฉพาะความเปลี่ยนแปลงภายในระยะนี้เพื่อลดปริมาณข้อมูล เพื่อลดต้นทุนการหาว่าใครอยู่ในระยะบ้าง ปกติจะแบ่งแมพเป็นตาราง (grid) แล้วดูเฉพาะเซลล์ที่อยู่ใกล้
บรอดแคสต์ Broadcast, fan-out
การส่งความเปลี่ยนแปลงหนึ่งอย่างไปให้ทุกคนที่มองเห็นได้ ถ้าคนที่รวมตัวกันต่างมองเห็นกันหมด ปริมาณที่ต้องส่งจะเพิ่มตามกำลังสองของจำนวนคน
GC Garbage collection
ฟีเจอร์ที่เก็บคืนหน่วยความจำที่ใช้แล้วทิ้งโดยอัตโนมัติ ระหว่าง GC โปรแกรมอาจหยุดทำงาน
ฮีป Heap
พื้นที่หน่วยความจำที่โปรแกรมขอจัดสรรมาใช้ตามต้องการระหว่างทำงาน
หน่วยความจำรั่ว Memory leak
บั๊กที่ไม่คืนหน่วยความจำที่ใช้เสร็จแล้ว ทำให้การใช้งานเพิ่มขึ้นเรื่อย ๆ ต่อให้มี GC ก็เกิดได้ ถ้ายังมีที่ใดอ้างอิงอ็อบเจกต์ที่ใช้เสร็จแล้วอยู่
สวอป Swap, paging
การย้ายหน่วยความจำบางส่วนไปไว้บนดิสก์เมื่อแรมไม่พอ ถ้าจะกลับมาใช้หน่วยความจำส่วนนั้นอีก จะช้ากว่าแรมกว่า 1,000 เท่า
OOM killer Out-of-memory killer
ฟีเจอร์ของ Linux ที่เมื่อหน่วยความจำหมด จะเลือกโปรเซสที่ใช้หน่วยความจำมากที่สุดแล้วบังคับปิด สำหรับคอนเทนเนอร์ แค่แตะขีดจำกัดหน่วยความจำก็ทำงานแล้ว
แคชมิส Cache miss
การที่ข้อมูลไม่อยู่ในแคชที่ใกล้ CPU ต้องไปอ่านถึงหน่วยความจำที่ช้ากว่า
IOPS I/O operations per second
จำนวนครั้งที่ดิสก์อ่านหรือเขียนได้ใน 1 วินาที ดิสก์บนคลาวด์มีขีดจำกัดตามที่จ่ายเงิน
fsync fsync
คำสั่งที่รอจนกว่าข้อมูลจะถูกบันทึกลงดิสก์จริง ๆ การเขียนปกติจะไปอยู่ในหน่วยความจำของ OS ก่อนแล้วค่อยถูกเขียนลงดิสก์ภายหลัง ถ้าไฟดับระหว่างนั้น ข้อมูลอาจหายได้ fsync ปลอดภัยแต่ช้า
เบิสต์เครดิต Burst credits
เครดิตสะสมที่ทำให้ดิสก์หรือเซิร์ฟเวอร์บนคลาวด์ทำงานได้เกินประสิทธิภาพพื้นฐานชั่วคราว เมื่อใช้จนหมดจะตกกลับไปที่ประสิทธิภาพพื้นฐาน
อินเด็กซ์ Index
ดัชนีของ DB ถ้าไม่มี ต้องอ่านทั้งตาราง
ฟูลสแกน Full table scan
คิวรีที่ไล่อ่านทุกแถวในตารางโดยไม่ใช้อินเด็กซ์
แผนการรันคิวรี Query plan
วิธีที่ DB เลือกว่าจะรันคิวรีตามลำดับใดและใช้อินเด็กซ์ใด ต่อให้โค้ดไม่เปลี่ยน ถ้า DB เปลี่ยนแผน คิวรีเดิมก็อาจช้าลงกะทันหันได้
ทรานแซกชัน Transaction
งานใน DB ที่ผูกรวมเป็นหนึ่งเดียวแบบ “สำเร็จทั้งหมดหรือไม่สำเร็จเลย” การเทรดต้องทำในทรานแซกชันเสมอ ระหว่างที่ยังไม่จบจะล็อกแถวที่แก้ไว้ จึงยิ่งสั้นยิ่งดี
คอนเนกชันพูล Connection pool
กลุ่มการเชื่อมต่อ DB ที่เปิดเตรียมไว้ล่วงหน้า ถ้าถูกใช้หมด คำขอใหม่ต้องรอ
ฮอตโรว์ Hot row
แถวเดียวที่คำขอจำนวนมากพยายามแก้พร้อมกัน เป็นต้นเหตุของการแย่งล็อก
การทำสำเนาข้อมูลล่าช้า Replication lag
ระยะเวลาที่ DB สำเนาตามไม่ทัน DB หลัก
โรลแบ็ค Rollback
การที่การบันทึกถูกยกเลิกแล้วกลับไปเป็นสถานะก่อนหน้า ผู้เล่นจะรู้สึกว่า “ไอเทมหาย”
แคช Cache (Redis etc.)
การคัดลอกข้อมูลที่ใช้บ่อยไปเก็บในที่ที่เร็ว ช่วยลดโหลดของ DB
เช็กพอยต์ Checkpoint
งานที่ DB เขียนการเปลี่ยนแปลงที่สะสมไว้ในหน่วยความจำลงดิสก์ทีเดียวเป็นระยะ ขณะนั้นการบันทึกและการดึงข้อมูลอาจช้าลงชั่วครู่
เฟลโอเวอร์ Failover
การสลับไปใช้ฝั่งสำรองเมื่อเซิร์ฟเวอร์หลักหรือ DB หลักล่ม ระหว่างสลับจะบันทึกไม่ได้ชั่วครู่ และถ้า replication ตามไม่ทัน ข้อมูลช่วงสุดท้ายอาจหาย
MVCC Multi-version concurrency control
วิธีที่ DB เก็บเวอร์ชันเก่าไว้ชั่วคราว เพื่อไม่ให้คนอ่านกับคนแก้ขวางกัน ถ้ามีทรานแซกชันที่เปิดทิ้งไว้นาน เวอร์ชันเก่าจะสะสมจนช้าลง
แคชสแตมปีด Cache stampede
ปรากฏการณ์ที่แคชว่างลงพร้อมกัน จนคำขอแห่ไปที่ต้นทาง (DB)
เกตเวย์ Gateway
เซิร์ฟเวอร์ตัวกลางที่รับการเชื่อมต่อจากไคลเอนต์แล้วส่งต่อไปยังเซิร์ฟเวอร์เกมด้านหลัง
เซอร์กิตเบรกเกอร์ Circuit breaker
กลไกที่ตัดการเรียกเซอร์วิสที่ล้มเหลวต่อเนื่องไว้ชั่วคราวและตอบกลับเป็นล้มเหลวทันที เพื่อกัน cascading failure พอผ่านไปสักพักจะลองเรียกหนึ่งสองครั้ง ถ้ากลับมาใช้ได้แล้วจึงเปิดให้เรียกตามปกติอีกครั้ง
ความล้มเหลวแบบลูกโซ่ Cascading failure
การที่เหตุขัดข้องในจุดหนึ่งลามไปยังเซอร์วิสอื่นตาม call chain
ออโตสเกลลิง Autoscaling
ฟีเจอร์ที่เพิ่มหรือลดจำนวนเซิร์ฟเวอร์อัตโนมัติตามโหลด การเพิ่มเครื่องต้องใช้เวลา
วอตช์ด็อก Watchdog
ตัวจับเวลาที่เฝ้าดูว่าเซิร์ฟเวอร์หยุดทำงานหรือไม่ ถ้าลูปของเกมหยุดนานเกินเวลาที่กำหนด (หลายวินาทีถึงหลายสิบวินาที) จะบันทึกสถานะ (dump) ไว้ แล้วบังคับปิดเซิร์ฟเวอร์เพื่อให้เปิดใหม่
อัตราการใช้งาน Utilization
สัดส่วนเวลาที่ worker (ตัวที่ประมวลผลคำขอ เช่น คอร์ CPU, เธรด หรือการเชื่อมต่อ DB) ไม่ว่าง ถ้าเกิน 80–90% การรอจะเพิ่มขึ้นอย่างรวดเร็ว
p99 99th percentile
ค่าที่ 99 ครั้งใน 100 ครั้งเร็วกว่านี้ และราว 1 ครั้งช้ากว่านี้ สะท้อนแลคที่ผู้เล่นรู้สึกได้ดีกว่าค่าเฉลี่ย
V-Sync Vertical sync
ฟีเจอร์ที่ส่งเฟรมออกให้ตรงกับรอบการรีเฟรชของจอ ช่วยแก้ภาพฉีก (screen tearing) แต่ทำให้เกิดอินพุตดีเลย์ และถ้า FPS ตกต่ำกว่าอัตรารีเฟรช ภาพอาจกระตุกเพราะสลับไปมาระหว่าง 60 กับ 30
อัตรารีเฟรชแบบแปรผัน VRR, G-Sync, FreeSync
ฟีเจอร์ที่ให้จอเปลี่ยนภาพตามจังหวะที่เฟรมพร้อม ช่วยลดอาการกระตุกจากการสลับไปมาระหว่าง 60 กับ 30 ของ V-Sync และลดอินพุตดีเลย์
แอนตี้ชีต Anti-cheat
โมดูลความปลอดภัยที่ป้องกันการแฮ็กเกม ถ้าการตรวจสอบเป็นระยะหรือ heartbeat กับเซิร์ฟเวอร์ล้มเหลว ก็อาจทำให้เกิดอาการกระตุกหรือหลุดได้
โอเวอร์เลย์ Overlay
ฟีเจอร์ที่โปรแกรมแชต โปรแกรมอัดหน้าจอ หรือโปรแกรมแสดง FPS วาดภาพซ้อนทับบนหน้าจอเกม โปรแกรมเหล่านี้แทรกเข้าไปในขั้นตอนการวาดภาพของเกม จึงทำให้กระตุกได้
การคอมไพล์เชเดอร์ Shader compilation
งานแปลงโปรแกรมเอฟเฟกต์กราฟิกให้ใช้กับ GPU ได้ ถ้าไม่ทำไว้ล่วงหน้า ภาพจะหยุดแวบตอนเห็นเอฟเฟกต์นั้นครั้งแรก และเมื่ออัปเดตไดรเวอร์การ์ดจอ ผลที่เก็บไว้จะใช้ไม่ได้ ต้องคอมไพล์ใหม่
เมนเธรด Main thread, Game thread
เธรดหลักของเกมที่ไล่ทำการคำนวณกติกาเกมและการเตรียมภาพตามลำดับ ถ้ามีงานใดใช้เวลานานแม้แค่งานเดียว ภาพจะหยุดไปตลอดช่วงนั้น
ความละเอียดของตัวจับเวลา Timer resolution
ช่วงเวลาสั้นที่สุดที่ระบบปฏิบัติการปลุกโปรแกรมที่หลับอยู่ได้ ค่าเริ่มต้นของ Windows คือ 15.6 ms ถ้าโปรแกรมไม่เปลี่ยนค่านี้เอง ต่อให้สั่ง “ปลุกในอีก 1 ms” ก็จะตื่นช้ากว่านั้น
การลดความเร็วเพราะความร้อน Thermal throttling
ฟีเจอร์ป้องกันที่ลดความเร็ว CPU/GPU ลงเองเมื่อเครื่องร้อน บนมือถือเกิดบ่อยหลังเล่นเกมไปหลายนาทีถึงหลายสิบนาที
VRAM Video memory
หน่วยความจำเฉพาะบนการ์ดจอ เท็กซ์เจอร์และโมเดลจะถูกโหลดขึ้นไปไว้ที่นี่ก่อนวาด ถ้าไม่พอ จะต้องรับส่งกับหน่วยความจำของ PC ผ่านเส้นทางที่ช้า ภาพจึงกระตุก
เน็ตกราฟ Net graph
หน้าจอสำหรับพัฒนาและดีบั๊กที่แสดงปิง แพ็กเก็ตหาย FPS และทิกเป็นกราฟแบบเรียลไทม์บนหน้าจอเกม ถ้าติดมาในคลิปแจ้งแลคด้วย จะหาสาเหตุได้ง่ายขึ้นมาก
อัตราการส่งซ้ำ Retransmission rate
สัดส่วนแพ็กเก็ต TCP ที่ต้องส่งซ้ำจากที่ส่งไปทั้งหมด ไม่มีเกณฑ์ที่เป็นทางการ แต่ถ้าค่าเฉลี่ยของทั้งเซิร์ฟเวอร์ต่ำกว่า 0.1% ถือว่าอยู่ในเกณฑ์ดี และถ้าเกิน 1% ผู้เล่นจำนวนมากจะเริ่มรู้สึกแลค ควรดูด้วยว่าเพิ่มขึ้นจากค่าปกติกี่เท่า
SACK Selective ACK
ฟีเจอร์ของ TCP ที่ฝั่งรับแจ้งอย่างละเอียดว่า “ได้ช่วงนี้แล้ว ขาดแค่ส่วนนี้” ต่อให้หายหลายตัวก็กู้คืนได้ในรอบเดียว
RACK-TLP Recent ACK, Tail Loss Probe
ฟีเจอร์ของ TCP ที่ใช้เวลาเป็นเกณฑ์ตัดสินว่าแพ็กเก็ตหาย และถ้าไม่มี ACK อยู่ช่วงหนึ่ง จะส่งแพ็กเก็ตสุดท้ายซ้ำอีกครั้งเพื่อเร่งการกู้คืน เป็นค่าเริ่มต้นใน Linux และ Android รุ่นใหม่ ฝั่ง Windows เปิด TLP และ RACK เป็นค่าเริ่มต้นตั้งแต่ Windows 10 (1607) และ Server 2016 ส่วน RACK รุ่นใหม่ที่กู้คืนได้แม้แต่การส่งซ้ำที่หายไป มีตั้งแต่ Server 2022 ทำงานเฉพาะการเชื่อมต่อที่เปิด SACK
การส่งซ้ำโดยไม่จำเป็น Spurious retransmission
การส่งซ้ำแพ็กเก็ตที่ไม่ได้หาย แค่มาช้าหรือมาสลับลำดับจนถูกเข้าใจว่าหาย ทำให้เปลืองแบนด์วิดท์และลดอัตราการส่งลงโดยไม่จำเป็น
ซีโรวินโดว์ Zero window
สถานะที่บัฟเฟอร์ฝั่งรับเต็มจนแจ้งว่า “หยุดส่งก่อน” ดูเหมือนการส่งซ้ำ แต่เน็ตไม่ได้มีปัญหา โปรแกรมฝั่งรับแค่อ่านข้อมูลไม่ทัน
thin stream Thin stream
การเชื่อมต่อที่ส่งแพ็กเก็ตเล็ก ๆ ห่าง ๆ แบบเกม สัญญาณสำหรับ fast retransmit สะสมได้ยาก เมื่อแพ็กเก็ตหายจึงหยุดนาน
โพลิเซอร์ Policer
วิธีจำกัดความเร็วที่ทิ้งแพ็กเก็ตส่วนที่เกินความเร็วที่กำหนดทันทีโดยไม่พักไว้ในคิว ส่วนวิธีที่พักไว้ในคิวแล้วค่อย ๆ ปล่อยออกเรียกว่า shaper
เพซซิง Pacing
การกระจายแพ็กเก็ตที่จะส่งให้ออกไปอย่างสม่ำเสมอตามเวลา แทนการส่งออกไปทีเดียวทั้งหมด ช่วยกันไม่ให้บัฟเฟอร์ขนาดเล็กล้น
ECN Explicit Congestion Notification
ฟีเจอร์ที่เมื่อเครือข่ายแออัด จะติดเครื่องหมาย “แออัด” ให้แพ็กเก็ตแทนการทิ้ง เพื่อให้ฝั่งส่งลดความเร็วลง บอกความแออัดได้โดยไม่ต้องมีแพ็กเก็ตหาย ต้องรองรับทั้งปลายทั้งสองฝั่งและอุปกรณ์ในช่วงที่แออัดจึงจะได้ผล
MSS Maximum Segment Size
ขนาดข้อมูลสูงสุดที่ TCP ใส่ในแพ็กเก็ตหนึ่ง ปกติ 1,460 ไบต์ ถ้าลดลงให้เข้ากับช่วงที่ผ่านอุโมงค์ (tunnel) จะป้องกัน MTU black hole ได้
แฮนด์โอเวอร์ Handover
การที่มือถือซึ่งกำลังเคลื่อนที่เปลี่ยนสถานีฐานที่เชื่อมต่ออยู่
เปอร์เซ็นไทล์ Percentile (p50, p95, p99)
ค่าที่อยู่ในตำแหน่งกี่เปอร์เซ็นต์เมื่อเรียงค่าจากน้อยไปมาก p50 คือค่ามัธยฐาน p99 คือค่าที่อยู่แถว ๆ ครั้งที่ช้าที่สุด 1 ครั้งใน 100 ครั้ง ช่วยเผยค่าที่พุ่งซึ่งค่าเฉลี่ยซ่อนไว้
ความหน่วงส่วนหาง Tail latency
ความหน่วงนาน ๆ ที่เกิดเป็นบางครั้ง ทั้งที่ส่วนใหญ่เร็ว แทบไม่ปรากฏในค่าเฉลี่ย แต่เป็นส่วนที่ผู้เล่นจำได้ว่าแลค
การวัดแบบสังเคราะห์ Synthetic monitoring
การใช้อุปกรณ์หรือเซิร์ฟเวอร์สำหรับวัดส่ง ping, traceroute ฯลฯ จากจุดที่กำหนดเป็นระยะแทนผู้เล่นจริง เพื่อวัดคุณภาพเส้นทาง RIPE Atlas เป็นเครื่องมือสาธารณะที่เป็นที่รู้จัก
ช่วงเวลารวมค่า Aggregation interval
จุดหนึ่งบนกราฟรวมค่าของกี่วินาทีหรือกี่นาที ยิ่งช่วงยาว ค่าที่พุ่งสั้น ๆ จะถูกเฉลี่ยจนจางลง
การวิเคราะห์หลังเกิดเหตุ Postmortem
เอกสารที่สรุปหลังเหตุขัดข้องจบลงว่าเกิดอะไรขึ้น ทำไมจึงเกิด และจะเปลี่ยนอะไร เขียนขึ้นเพื่อป้องกันไม่ให้เกิดซ้ำมากกว่าเพื่อหาคนผิด
C-state CPU idle state
สถานะประหยัดพลังงานที่ CPU เข้าไปเมื่อว่าง ยิ่งลึกยิ่งประหยัดไฟ แต่ต้องใช้เวลาตื่นกลับมานานขึ้น
ไลฟ์ไมเกรชัน Live migration
การที่คลาวด์ย้าย VM ที่กำลังทำงานอยู่ไปยังโฮสต์อื่น เช่น เพื่อซ่อมบำรุงโฮสต์ ขณะย้ายอาจหยุดไปชั่วครู่
SNAT Source NAT
NAT ที่เปลี่ยน IP ต้นทางของแพ็กเก็ตขาออกเป็น public IP จำนวนพอร์ตที่ public IP หนึ่งตัวใช้ได้มีขีดจำกัด ถ้าใช้หมด การเชื่อมต่อใหม่จะล้มเหลว
NAT เกตเวย์ NAT gateway
อุปกรณ์บนคลาวด์ที่ให้เซิร์ฟเวอร์ในเครือข่ายส่วนตัวใช้ public IP ร่วมกันหนึ่งตัวเวลาออกอินเทอร์เน็ต จำนวนการเชื่อมต่อพร้อมกันต่อปลายทางมีขีดจำกัด
อินเทอร์เน็ตดาวเทียมวงโคจรต่ำ LEO satellite internet
อินเทอร์เน็ตที่เชื่อมต่อผ่านดาวเทียมจำนวนมากที่ความสูงหลักร้อยถึงหลักพันกิโลเมตร ความหน่วงสั้นกว่าดาวเทียมวงโคจรค้างฟ้ามาก แต่เมื่อเปลี่ยนดาวเทียมที่เชื่อมต่อ ความหน่วงอาจพุ่งขึ้นได้
GeoIP IP geolocation
ฐานข้อมูลที่ใช้ IP address ประมาณประเทศ เมือง และ ISP บางรายการผิดหรือล้าสมัย จึงอาจเป็นสาเหตุให้ถูกส่งไปเซิร์ฟเวอร์ในภูมิภาคที่อยู่ไกล
ใบรับรอง TLS TLS certificate
เอกสารอิเล็กทรอนิกส์ที่ยืนยันว่าเซิร์ฟเวอร์เป็นตัวจริง มีวันหมดอายุ ถ้าหมดอายุ การเชื่อมต่อแบบเข้ารหัสจะล้มเหลวจนเข้าใช้งานไม่ได้
การสร้างเฟรม Frame generation
เทคโนโลยีที่การ์ดจอแทรกเฟรมที่คาดการณ์ขึ้นระหว่างเฟรมที่วาดจริง เพื่อเพิ่ม FPS ภาพลื่นขึ้น แต่ความหน่วงตั้งแต่อินพุตจนถึงภาพบนจออาจเพิ่มขึ้น
T6เครื่องมือ

เอกสารอ้างอิง

แหล่งที่มาของตัวเลข ค่าเริ่มต้น และคำอธิบายพฤติกรรมในคู่มือนี้ รวบรวมเฉพาะแหล่งอ้างอิงที่เชื่อถือได้ เช่น เอกสารมาตรฐาน (RFC), เอกสารเคอร์เนลและ OS, เอกสารทางการของคลาวด์ เอนจิน และ DB, การบรรยายและงานวิจัย การ์ดสาเหตุและ “แหล่งอ้างอิง” ท้ายแต่ละบทก็ลิงก์ไปยังเอกสารชุดเดียวกัน เมื่อเวอร์ชันเปลี่ยน ค่าเริ่มต้นก็อาจเปลี่ยนตาม ก่อนนำไปใช้จริงให้ตรวจเอกสารของเวอร์ชันที่ใช้อยู่

เอกสาร 616 รายการ จากผู้เผยแพร่ 83 ราย ดูรายการได้ที่ เอกสารอ้างอิงในฉบับข้อความ