Bản gốc: https://jungrok5.github.io/mmo-lag-anatomy/vi/ Trang nguyên nhân: https://jungrok5.github.io/mmo-lag-anatomy/vi/c/[ID nguyên nhân].html # Sách trắng lag game: dữ liệu tri thức Tạo tự động (2026-10-03, `node tools/export.cjs`). Đừng sửa tay; hãy sửa src/js/ rồi xuất lại. 228 nguyên nhân, 135 thuật ngữ. Nguyên nhân được tham chiếu bằng **ID**. Thêm `#c-ID` vào sau địa chỉ trang để mở thẳng thẻ nguyên nhân đó. ## Mã phụ trách | Mã | Đội | Phụ trách | Phạm vi | |---|---|---|---| | cli | Đội phát triển game | Phát triển client | Code client game: khung hình, GC, loading; nội suy, ngoại suy, dự đoán; xử lý mạng phía client (gồm gửi heartbeat và tự động kết nối lại) | | srv | Đội phát triển game | Phát triển server | Code server game: tick, thread, lock; thiết kế đồng bộ; xử lý kết nối (vòng lặp accept, tham số listen); phản hồi heartbeat và dọn các kết nối đã đứt; tùy chọn socket; thiết kế query và transaction | | net | Đội hạ tầng | Hạ tầng mạng | Đường truyền và thiết bị mạng trong IDC (switch, router, tường lửa, bộ cân bằng tải, chống DDoS); network ACL, định tuyến VPC và bộ cân bằng tải trên cloud; nhà mạng và peering | | sys | Đội hạ tầng | Hạ tầng server | Phần cứng server và instance cloud (gồm security group và theo dõi kết nối), cấu hình OS và kernel, NIC, môi trường triển khai và giám sát | | dba | Đội hạ tầng | Hạ tầng DB | Server DB và storage; cấu hình, replication và sao lưu DB; server cache | | ext | Bên ngoài | Bên ngoài | PC và mạng gia đình của người chơi, chặng của nhà mạng (ngoài phạm vi hợp đồng của chúng ta), nhà cung cấp cloud. Vì không tự sửa được nên xử lý bằng cách hướng dẫn, gửi yêu cầu hoặc workaround | Với mỗi nguyên nhân, “Phụ trách chính” là nơi loại bỏ nguyên nhân gốc; “Phối hợp” là nơi cũng có việc thực tế phải làm. ## Triệu chứng - **Giật khựng** (`stutter`, tên gọi khác: giật, giật lag, khựng, cảm giác như tụt FPS): Chuyển động không mượt, cứ khựng lại một chút rồi chạy tiếp, lặp đi lặp lại. Nếu ping vẫn ổn thì nhiều khả năng do khung hình trên PC của bạn (client, OS); nếu ping lúc cao lúc thấp thì nhiều khả năng là jitter của Wi-Fi hoặc đường truyền. Tuy vậy, chỉ số ping trong game thường được đo bên trong vòng lặp game chạy theo từng khung hình, nên khi frame time vọt lên thì con số ping cũng có thể nhảy theo. - **Dịch chuyển tức thời** (`teleport`, tên gọi khác: warp, teleport, đứng im rồi nhảy sang chỗ khác): Nhân vật nhảy thẳng tới một vị trí ở xa mà không thấy quá trình di chuyển. Thường có nghĩa là gói tin bị gián đoạn một lúc. Hãy xem mất gói, đường truyền đứt trong chốc lát, server đứng, ngoại suy sai. Nếu mọi người khác đều ổn mà chỉ một người bị nhảy thì nghi ngờ đường truyền của người đó trước. - **Kéo ngược** (`rubber`, tên gọi khác: bị kéo lại, giật lùi, rubber banding, bị trả về vị trí cũ): Nhân vật của bạn đang đi tới thì bị kéo về chỗ vừa đi qua. Màn hình của bạn (dự đoán) và phán định của server không khớp. Có thể input của bạn không tới được server (mất gói), bước kiểm tra di chuyển của server đã cắt bớt quãng đường, hoặc hai bên tính di chuyển khác nhau. - **Tua nhanh** (`burst`, tên gọi khác: dồn cục, như tua video, xử lý dồn một lúc): Màn hình đang đứng bỗng chạy lại, và mọi chuyển động, đòn đánh, sát thương bị dồn lại trôi qua thật nhanh cùng một lúc. Gói tin bị dồn ứ ở đâu đó rồi được giải phóng cùng lúc. Điển hình là chờ truyền lại TCP, server tính dồn để đuổi kịp, hoặc client xử lý không kịp. - **Quay chậm** (`slowmo`, tên gọi khác: slow motion, cả thế giới chậm lại, mọi thứ ì ạch): Mọi thứ chuyển động chậm. Việc tung skill và quái di chuyển trông như bị kéo dài ra. Tùy thiết kế server, tốc độ có thể vẫn giữ nguyên nhưng hiện ra thành giật khựng hoặc dịch chuyển tức thời. Server không kịp xử lý xong mỗi tick đúng hạn. Đường truyền vẫn ổn nên ping đo bên ngoài game không đổi; ping trong game có thể tăng nhẹ nếu có lẫn thời gian chờ xử lý của server. Hãy xem số người tăng đột biến, tính toán tầm nhìn, broadcast, thiếu bộ nhớ. - **Trễ thao tác** (`delay`, tên gọi khác: input lag, delay, phản hồi chậm, thiếu cảm giác tay): Từ lúc bấm đến lúc thấy kết quả mất một khoảng thời gian. Bản thân màn hình có thể vẫn mượt. Thời gian khứ hồi (ping) dài, hoặc có hàng đợi bị dồn ở đâu đó. Hãy xem khoảng cách, hàng đợi của router, Nagle (tính năng của TCP gom các gói nhỏ lại rồi mới gửi), hàng đợi của server. Nếu ping thấp mà lúc nào cũng ì thì hãy xem phía PC của bạn như V-Sync hay FPS thấp, hoặc thiết kế bắt mọi hành động phải chờ server xác nhận (chương về cơ chế đồng bộ). - **Đứng hình** (`freeze`, tên gọi khác: đơ, treo, không phản hồi): Mọi thứ trên màn hình dừng lại một lúc (0,5 giây đến vài giây) rồi chạy tiếp. Server đứng toàn bộ (GC, deadlock, gọi đồng bộ), đường truyền đứt trong chốc lát, hoặc PC của bạn bị đứng. - **Nuốt thao tác·rollback** (`dropped`, tên gọi khác: nuốt skill, vật phẩm bị trả lại, giao dịch thất bại): Hành động rõ ràng đã làm lại bị coi như chưa từng xảy ra, hoặc kết quả bị đảo ngược một lúc lâu sau. Yêu cầu bị mất (mất gói, tràn hàng đợi), server phán định khác với màn hình của bạn (lệch thời điểm phán định, bị từ chối sau khi đã hiển thị trước), hoặc lưu dữ liệu thất bại giữa chừng (khóa hoặc sự cố DB, server crash). - **Mất kết nối** (`disconnect`, tên gọi khác: văng game, rớt mạng, “Đã mất kết nối với máy chủ”): Đang chơi thì kết nối bị ngắt, game quay về màn hình đăng nhập hoặc cửa sổ kết nối lại. Trong suốt thời gian timeout không có gói tin nào. Hãy xem đường truyền đứt lâu, idle timeout, server crash hoặc khởi động lại, server hay PC của bạn đứng lâu hơn timeout (loading lâu). Nếu game tự tắt mà không có thông báo thì hãy xem việc client bị buộc thoát (crash, thiếu bộ nhớ) trước khi xem kết nối. - **Không vào được·kẹt loading** (`noconnect`, tên gọi khác: không đăng nhập được, loading mãi không xong): Không vào được game, hoặc bị kẹt ở màn hình loading hay màn hình vào game. Các nơi tiếp nhận kết nối mới (hàng đợi kết nối của server, tường lửa, server đăng nhập, DB) đã đầy. Đặc biệt hay gặp ngay sau bảo trì. - **Không hiển thị·đối tượng ma** (`invisible`, tên gọi khác: không thấy NPC, nhân vật vô hình, quái đã chết vẫn đứng đó): NPC, quái hay người chơi lẽ ra phải có thì chỉ riêng màn hình của bạn không có, hoặc đối tượng đã biến mất vẫn còn trên màn hình của riêng bạn. Tình trạng này thường do thiếu một gói tin hoặc vẽ thất bại, ít liên quan đến tốc độ. Hãy xem khác kênh hoặc khác phasing, mất thông báo xuất hiện hay rời đi, bị hủy trong lúc loading, tải asset thất bại. Manh mối quyết định là khi đi ra khỏi tầm nhìn rồi quay lại thì đối tượng có hiện ra hay không. ## Bốn yếu tố - **Độ trễ** (`lat`, Latency): Mọi gói tin đều đến muộn một khoảng gần như cố định do khoảng cách, hàng đợi và thời gian xử lý. Cách game xử lý: Game dùng dự đoán và hiển thị trước để cho thấy hành động của bạn ngay, còn phán định thì server quay ngược về quá khứ để tính cho khớp (bù trễ). - **Jitter** (`jit`, Jitter): Trung bình vẫn ổn nhưng có gói đến sớm, có gói đến muộn. Thủ phạm thường là Wi-Fi, đường truyền bị nghẽn hoặc CPU quá bận. Cách game xử lý: Game gom một ít gói vào bộ đệm nội suy rồi lấy ra vẽ với tốc độ đều. Jitter lớn hơn bộ đệm thì không che được nữa. - **Mất gói** (`loss`, Packet loss): Hàng đợi bị tràn, nhiễu sóng hoặc thiết bị lỗi làm rơi gói tin. Đường truyền đứt trong chốc lát cũng là mất nhiều gói liên tiếp. Cách game xử lý: Game dùng UDP lấp chỗ trống bằng nội suy và ngoại suy, còn input của bạn được gửi lặp lại để mất một hai gói vẫn đủ. TCP thì giữ lại các gói phía sau, không chuyển cho game cho đến khi nhận lại được gói bị mất. - **Ngưng trệ** (`stall`, Stall): Tick của server bị chậm hoặc dừng hẳn (GC, lock, gọi đồng bộ, quá tải), hoặc khung hình trên PC của bạn đứng lại. Hiện tượng này xảy ra cả khi đường truyền hoàn toàn bình thường. Cách game xử lý: Game tính dồn phần việc tồn đọng trong lúc dừng để đuổi kịp, bỏ qua luôn phần đó, hoặc để thời gian trong game trôi chậm lại. ## Nguyên nhân ### L1 Tiến trình game phía client (16 nguyên nhân) #### cg-hitch · Frame time vọt lên · Frame hitch Việc tính toán một khung hình mất thời gian gấp nhiều lần bình thường nên màn hình dừng lại trong chốc lát. - Vì sao → Dẫn đến → Trên màn hình: Hiệu ứng skill tăng đột biến, spawn hàng loạt, cập nhật toàn bộ UI dồn vào cùng một khung hình → Không xong kịp trong 16,7 ms, mất tới 50–300 ms → Màn hình khựng lại, sang khung hình sau mọi nhân vật cùng nhảy tới vị trí mới một lượt - Triệu chứng: Giật khựng, Đứng hình / Yếu tố: Ngưng trệ - Ai gặp: Chỉ mình tôi / Khi nào: Khi đông người, Khi làm một thao tác nhất định, Thỉnh thoảng bất chợt - Phụ trách chính: Phát triển client (Đội phát triển game) - Việc cần làm (Đội phát triển game): Chia việc nặng ra nhiều khung hình, dùng profiler tìm khung hình vọt lên, đặt giới hạn số hiệu ứng. - Con số tham khảo: Ở 60 FPS, một khung hình dài 16,7 ms. Chỉ cần một khung hình vượt quá 50 ms là người chơi đã dễ cảm thấy “bị giật”. - Trên đồ thị: Thỉnh thoảng vọt lên bất chợt (Frame time) - Chỗ cần xem: Dùng PresentMon ghi lại frame time (FrameTime) và thời gian CPU, GPU dùng cho khung hình đó (CPUBusy, GPUBusy) trong lúc chơi. Trên di động: chỉ số phiên chậm (slow session) và render chậm (slow rendering) của Android vitals - Đúng nếu: frame time vốn quanh 16,7 ms vọt lên quá 50 ms đúng lúc có hiệu ứng skill, spawn hàng loạt hoặc cập nhật toàn bộ UI, còn ping lúc đó không đổi - Loại trừ nếu: frame time đều nhưng chỉ nhân vật khác bị khựng → phía mạng, ví dụ “Bộ đệm nội suy không có hoặc quá ngắn”. Các lần vọt lên cách đều nhau → xem “Thu gom rác (GC) phía client” trước - Cách kiểm tra: Kiểm tra ở môi trường phía người chơi - Nguồn: - [Slow rendering](https://developer.android.com/topic/performance/vitals/render) · Android (Google) · Muốn đạt 60 FPS thì phải vẽ một khung hình trong 16 ms; trễ hơn thì khung hình bị bỏ qua, trông như giật (jank) - [Slow Sessions (games only)](https://developer.android.com/topic/performance/issues/slow-session) · Android (Google) · Android vitals coi khung hình game vượt 50 ms (20 FPS) hoặc 34 ms (30 FPS) là khung hình chậm - [PresentMon Capture Application (README-CaptureApplication.md)](https://raw.githubusercontent.com/GameTechDev/PresentMon/main/README-CaptureApplication.md) · Intel · FrameTime (thời gian CPU giữa hai khung hình), CPUBusy và GPUBusy (thời gian CPU, GPU dùng để tạo khung hình đó) #### cg-gc · Thu gom rác (GC) phía client · Client GC (Unity C#, Unreal, Lua) Trong lúc thu hồi bộ nhớ đã dùng xong (rác), cả game dừng lại. Đặc trưng là giật khựng theo khoảng cách đều đặn. - Vì sao → Dẫn đến → Trên màn hình: Mỗi khung hình đều tạo rồi bỏ các chuỗi, mảng, list tạm → Rác dồn lại thì GC dừng main thread để thu hồi → Cứ vài giây đến vài chục giây lại giật khựng đều đặn - Triệu chứng: Giật khựng, Đứng hình / Yếu tố: Ngưng trệ - Ai gặp: Chỉ mình tôi / Khi nào: Theo chu kỳ đều, Khi đông người - Phụ trách chính: Phát triển client (Đội phát triển game) - Việc cần làm (Đội phát triển game): Giảm cấp phát (tránh nối chuỗi, LINQ, capture biến trong lambda), dùng object pool, bật incremental GC (mặc định từ Unity 2020), chủ động chạy GC vào lúc dừng cũng không sao, như màn hình loading. - Con số tham khảo: Thường mỗi lần mất vài ms đến 100 ms; trên điện thoại cấu hình thấp hoặc game dùng nhiều bộ nhớ thì lâu hơn (trong thí nghiệm khoảng 150–170 ms). GC của Unity quét toàn bộ heap mỗi lần chạy, nên game đang dùng càng nhiều bộ nhớ thì mỗi lần dừng càng lâu. - Trên đồ thị: Vọt lên theo chu kỳ (Frame time, thời điểm GC chạy) - Chỗ cần xem: Marker GC.Collect, GC.Alloc trong Unity Profiler trên bản build development. Với Unreal: stat GC và stat Hitches (ghi log các khung hình vượt ngưỡng đặt bằng t.HitchFrameTimeThreshold) - Đúng nếu: khung hình nào vọt lên cũng có đoạn GC.Collect dài xấp xỉ mức vọt, cách nhau đều đặn vài giây đến vài chục giây. Ở nơi đông người, GC.Alloc mỗi khung hình tăng lên - Loại trừ nếu: khung hình vọt lên không có đoạn GC → xem “Tải đồng bộ trên main thread và biên dịch shader” hoặc “Tải render khi đông người” - Cách kiểm tra: Cần log và chỉ số của server, client game - Tìm hiểu thêm: Hiện tượng này phổ biến ở client viết bằng C# như Unity. Thủ phạm chính là code tạo mới chuỗi log chiến đấu, số sát thương, chữ trên UI ở mỗi khung hình. Nếu chỉ giật ở nơi đông người, nghĩa là có đoạn code sinh rác tăng theo số người. Incremental GC chia việc thu gom thành từng phần nhỏ ở mỗi khung hình (mặc định của Unity là 3 ms), nhưng nếu rác sinh ra nhanh hơn tốc độ thu gom thì cuối cùng game vẫn dừng một lượt. Unreal Engine cũng có GC riêng để dọn các game object không còn dùng. Tùy phiên bản engine và cấu hình, với thiết lập mặc định GC này chạy khoảng 1 phút một lần và có thể gây khựng cách nhau 1 phút. Client viết luật game bằng script như Lua còn có thêm GC riêng của script đó. - Nguồn: - [Garbage collection modes](https://docs.unity3d.com/Manual/performance-incremental-garbage-collection.html) · Unity · Incremental GC là mặc định và thu gom rải ra nhiều khung hình; nếu tắt, main thread dừng trong lúc quét toàn bộ heap, có thể tới hàng trăm ms - [Scripting.GarbageCollector.CollectIncremental](https://docs.unity3d.com/ScriptReference/Scripting.GarbageCollector.CollectIncremental.html) · Unity · Thời gian mỗi lượt của incremental GC (time slice) có mục tiêu mặc định là 3 ms (incrementalTimeSliceNanoseconds) - [Garbage Collection Settings in the Unreal Engine Project Settings](https://dev.epicgames.com/documentation/en-us/unreal-engine/garbage-collection-settings-in-the-unreal-engine-project-settings) · Epic Games · Thiết lập cho GC của Unreal chạy theo khoảng cách cố định (Time Between Purging Pending Kill Objects, tính bằng giây). Tài liệu này không ghi con số mặc định - [Profiler markers reference](https://docs.unity3d.com/Manual/profiler-markers.html) · Unity · GC.Collect: khoảng thời gian code chương trình dừng trong lúc thu gom rác (dưới 1 ms đến hàng trăm ms); GC.Alloc: cấp phát trên managed heap - [Stat Commands in Unreal Engine](https://dev.epicgames.com/documentation/en-us/unreal-engine/stat-commands-in-unreal-engine) · Epic Games · stat GC (thống kê thu gom rác), stat Hitches (ghi log các khung hình vượt t.HitchFrameTimeThreshold) #### cg-sync-load · Tải đồng bộ trên main thread và biên dịch shader · Synchronous asset load, shader compile Ngay trước khi vẽ khu vực, quái hay hiệu ứng lần đầu xuất hiện, game phải đọc file và tạo shader nên bị dừng. - Vì sao → Dẫn đến → Trên màn hình: Vào khu vực mới, skill, trang bị hoặc quái lần đầu xuất hiện → Main thread phải chờ đọc file và biên dịch shader → Chỉ đứng hình 0,1–1 giây ở lần đầu, từ lần thứ hai trở đi thì bình thường - Triệu chứng: Đứng hình, Giật khựng / Yếu tố: Ngưng trệ - Ai gặp: Chỉ mình tôi / Khi nào: Khi di chuyển hoặc chuyển bản đồ, Khi làm một thao tác nhất định - Phụ trách chính: Phát triển client (Đội phát triển game) - Việc cần làm (Đội phát triển game): Tải bất đồng bộ, tải sẵn trước (prewarm), biên dịch shader trước ở màn hình loading hoặc lần chạy đầu tiên, tận dụng màn hình loading. - Con số tham khảo: Biên dịch một shader mất vài chục ms, lâu thì trên 100 ms. Tải texture mất vài chục đến vài trăm ms tùy tốc độ ổ lưu trữ. - Trên đồ thị: Tăng vọt ngay sau đăng nhập hoặc bảo trì (Số lần khung hình vọt lên (ngay sau bản cập nhật hoặc cập nhật driver)) - Chỗ cần xem: Dùng PresentMon ghi lại cùng một lộ trình hai lần, so sánh lần đầu với lần thứ hai. Trên bản build development: với Unreal, bật r.PSOPrecache.Validation rồi xem stat PSOPrecache và dòng “PSO PRECACHING MISS” trong log; với Unity, xem đoạn loading và shader của khung hình vọt lên trong Timeline của Profiler - Đúng nếu: chỉ vọt lên 0,1–1 giây ở chỗ lần đầu tới hoặc skill lần đầu dùng, lần thứ hai thì hết. Báo cáo dồn dập ngay sau bản cập nhật hoặc cập nhật driver đồ họa rồi giảm dần - Loại trừ nếu: lần nào tới cùng một chỗ cũng vọt lên thì không phải lỗi shader cache. Chỉ lặp lại mỗi lần di chuyển trên PC có ổ lưu trữ chậm → “Ổ lưu trữ chậm khiến streaming asset không theo kịp”; bộ nhớ GPU chuyên dụng đầy → “Thiếu bộ nhớ đồ họa (VRAM)” - Cách kiểm tra: Cần log và chỉ số của server, client game - Tìm hiểu thêm: Trên PC, shader đã tạo một lần sẽ được driver đồ họa lưu vào shader cache để dùng lại. Vì vậy, ngay sau khi cập nhật driver đồ họa hoặc game ra bản cập nhật, cache này bị vô hiệu, và cả những người trước đó chơi mượt cũng bị giật lại một thời gian. Báo cáo điển hình là “sau bản cập nhật, cứ tới chỗ nào lần đầu là khựng”. - Nguồn: - [Shader loading](https://docs.unity3d.com/Manual/shader-loading.html) · Unity · Lần đầu dùng một shader variant, driver đồ họa phải tạo bản dành cho GPU nên có thể dừng thấy rõ; bản đã tạo được cache nên không dừng lại nữa - [Optimizing Rendering With PSO Caches in Unreal Engine](https://dev.epicgames.com/documentation/en-us/unreal-engine/optimizing-rendering-with-pso-caches-in-unreal-engine) · Epic Games · Tạo pipeline state (PSO) vào đúng lúc cần có thể mất trên 100 ms, nên phải tạo sẵn trước - [Direct3D 12 Return Codes](https://learn.microsoft.com/en-us/windows/win32/direct3d12/d3d12-graphics-reference-returnvalues) · Microsoft · D3D12_ERROR_DRIVER_VERSION_MISMATCH: không dùng lại được PSO cache tạo bằng phiên bản driver khác (phải biên dịch lại sau khi cập nhật driver) - [PresentMon Capture Application (README-CaptureApplication.md)](https://raw.githubusercontent.com/GameTechDev/PresentMon/main/README-CaptureApplication.md) · Intel · Ghi lại thời gian từng khung hình bằng FrameTime (thời gian CPU giữa hai khung hình) - [PSO Precaching for Unreal Engine](https://dev.epicgames.com/documentation/en-us/unreal-engine/pso-precaching-for-unreal-engine) · Epic Games · Bật r.PSOPrecache.Validation thì xem được thống kê PSO bị bỏ sót bằng stat PSOPrecache và log ghi “PSO PRECACHING MISS”; tạo PSO lúc runtime vượt ngưỡng mặc định 20 ms thì bị tính là hitch #### cg-asset-stream · Ổ lưu trữ chậm khiến streaming asset không theo kịp · Slow storage stalls asset streaming Trên ổ lưu trữ chậm như HDD, tốc độ đọc texture và model của thế giới mở không theo kịp tốc độ di chuyển, nên đối tượng hiện ra muộn hoặc game giật vì phải chờ đọc xong. - Vì sao → Dẫn đến → Trên màn hình: Di chuyển nhanh bằng thú cưỡi hoặc teleport, hay đi vào nơi đông người, nên cần cùng lúc nhiều texture và model mới → Ổ lưu trữ chậm như HDD không đọc kịp tốc độ cần thiết nên yêu cầu đọc dồn lại, một số phần loading khiến main thread phải chờ tới khi xong → Texture mờ một lúc, nhà cửa và nhân vật hiện ra muộn; lúc chờ đọc thì giật khựng hoặc đứng hình - Triệu chứng: Không hiển thị·đối tượng ma, Giật khựng, Đứng hình / Yếu tố: Ngưng trệ - Ai gặp: Chỉ mình tôi / Khi nào: Khi di chuyển hoặc chuyển bản đồ, Khi đông người - Phụ trách chính: Phát triển client (Đội phát triển game) / Phối hợp: Bên ngoài (Bên ngoài) - Việc cần làm (Đội phát triển game): Streaming bất đồng bộ để main thread không phải chờ đọc, đọc trước dựa trên hướng và tốc độ di chuyển, hiển thị trước bản độ phân giải thấp (mipmap) và model đơn giản rồi thay sau, khi streaming không theo kịp lúc di chuyển nhanh thì tạm giới hạn tốc độ di chuyển hoặc dùng màn hình loading, ghi rõ có cần SSD hay không trong cấu hình tối thiểu và cấu hình đề nghị. - Việc cần làm (Bên ngoài): Hướng dẫn người chơi cài game lên SSD, kiểm tra xem trên cùng ổ đĩa có đang tải file hoặc phần mềm diệt virus đang quét hay không. - Con số tham khảo: Theo Microsoft, ổ cứng HDD đời cũ đọc được vài chục MB trong 1 giây, NVMe SSD đọc được vài GB trong 1 giây, còn game thế hệ trước dùng khoảng 50 MB trong 1 giây cho streaming. Game được thiết kế dựa trên tốc độ SSD và đọc nhiều hơn thế rất nhiều, khi chạy trên HDD thì việc đọc dễ không theo kịp tốc độ di chuyển. - Trên đồ thị: Chỉ một phần cao (Số lần khung hình vọt lên (theo loại ổ lưu trữ), độ trễ đọc đĩa) - Chỗ cần xem: Vừa di chuyển nhanh vừa ghi đồng thời PhysicalDisk\Avg. Disk sec/Read (thời gian trung bình của một lần đọc), Current Disk Queue Length trong Performance Monitor của Windows và frame time của PresentMon. Trên bản build development: stat Streaming, stat AsyncLoad của Unreal; cảnh báo AssetBundle.asset/allAssets trong Unity Profiler (yêu cầu kết quả trước khi tải xong nên main thread phải chờ) - Đúng nếu: khi di chuyển nhanh, độ trễ đọc đĩa và hàng đợi tăng vọt, cùng lúc đó khung hình vọt lên hoặc texture, đối tượng hiện muộn. Chạy cùng cảnh đó trên SSD thì hết - Loại trừ nếu: ổ đĩa rảnh mà texture vẫn mờ → “Thiếu bộ nhớ đồ họa (VRAM)”; từ lần thứ hai tới cùng chỗ thì bình thường → “Tải đồng bộ trên main thread và biên dịch shader” - Cách kiểm tra: Kiểm tra ở môi trường phía người chơi - Tìm hiểu thêm: Nếu chỉ dừng ở lần đầu rồi sau đó bình thường thì gần với “Tải đồng bộ trên main thread và biên dịch shader”; nếu chỉ lặp lại mỗi khi di chuyển trên PC có ổ lưu trữ chậm thì là nguyên nhân này. Khi thiếu bộ nhớ đồ họa, texture bị hạ xuống rồi nạp lại (“Thiếu bộ nhớ đồ họa (VRAM)”) và cũng trông mờ, nên hãy xem cùng lúc thời gian chờ đọc đĩa và mức dùng bộ nhớ đồ họa. Tính năng quét thời gian thực của phần mềm diệt virus chen vào mỗi lần game mở file cũng có thể làm việc đọc chậm thêm. - Nguồn: - [DirectStorage is coming to PC](https://devblogs.microsoft.com/directx/directstorage-is-coming-to-pc/) · Microsoft · HDD đời cũ đọc vài chục MB mỗi giây, NVMe SSD vài GB mỗi giây, ngân sách streaming asset của game thế hệ trước khoảng 50 MB mỗi giây; game thế giới mở vừa di chuyển vừa đọc rồi bỏ cảnh vật ở xa theo thời gian thực - [Texture and mesh loading](https://docs.unity3d.com/Manual/LoadingTextureandMeshData.html) · Unity · Upload đồng bộ đọc và đẩy dữ liệu lên trong một khung hình trên main thread nên gây dừng thấy rõ; upload bất đồng bộ streaming rải ra nhiều khung hình - [Texture Streaming Overview for Unreal Engine](https://dev.epicgames.com/documentation/en-us/unreal-engine/texture-streaming-overview-for-unreal-engine) · Epic Games · Streamer tăng giảm độ phân giải texture (mip) theo góc nhìn; phần lớn tính toán chạy trên worker thread bất đồng bộ, mip đang hiện trên màn hình được tải trước - [Profiler markers reference](https://docs.unity3d.com/Manual/profiler-markers.html) · Unity · Cảnh báo AssetBundle.asset/allAssets: yêu cầu kết quả trước khi tải xong nên main thread dừng lại chờ - [Stat Commands in Unreal Engine](https://dev.epicgames.com/documentation/en-us/unreal-engine/stat-commands-in-unreal-engine) · Epic Games · stat Streaming (bộ nhớ và số lượng texture streaming), stat AsyncLoad (thống kê tải bất đồng bộ) - [Windows Performance Monitor Disk Counters Explained](https://learn.microsoft.com/en-us/archive/blogs/askcore/windows-performance-monitor-disk-counters-explained) · Microsoft · Avg. Disk sec/Read là thời gian trung bình để hoàn tất một lần đọc (độ trễ I/O), Current Disk Queue Length là độ dài hàng đợi đĩa tại thời điểm đo - [PresentMon Capture Application (README-CaptureApplication.md)](https://raw.githubusercontent.com/GameTechDev/PresentMon/main/README-CaptureApplication.md) · Intel · Ghi lại thời gian từng khung hình bằng FrameTime (thời gian CPU giữa hai khung hình) - [About regular quick and full scans with Microsoft Defender Antivirus](https://learn.microsoft.com/en-us/defender-endpoint/schedule-antivirus-scans) · Microsoft · Tính năng bảo vệ thời gian thực quét file mỗi lần file được mở và đóng #### cg-crowd · Tải render khi đông người · Render/animation cost of crowds Khi hàng trăm người cùng hiện trên một màn hình như lúc công thành hay đánh world boss, riêng chi phí vẽ đã vượt quá sức máy. - Vì sao → Dẫn đến → Trên màn hình: Hàng trăm người và hiệu ứng chồng lên nhau trên một màn hình → Chi phí hoạt ảnh, bóng đổ, bảng tên, hiệu ứng tăng theo số người → FPS tụt từ 60 → 15, mọi chuyển động đều giật khựng và thao tác cũng bị trễ - Triệu chứng: Giật khựng, Trễ thao tác / Yếu tố: Ngưng trệ - Ai gặp: Một địa điểm hoặc kênh, Chỉ mình tôi / Khi nào: Khi đông người - Phụ trách chính: Phát triển client (Đội phát triển game) - Việc cần làm (Đội phát triển game): Giảm chi tiết theo khoảng cách (LOD), giới hạn số người hiển thị, tùy chọn giảm hiệu ứng, giảm tần suất cập nhật hoạt ảnh. - Con số tham khảo: Dù 1 nhân vật chỉ tốn 0,02–0,1 ms, 300 nhân vật đã là 6–30 ms. Ngân sách một khung hình ở 60 FPS là 16,7 ms, nên riêng phần này đã chiếm hơn 1/3, nhiều thì vượt luôn ngân sách. - Trên đồ thị: Tăng theo số người và tải (Frame time, số nhân vật trên màn hình) - Chỗ cần xem: So sánh frame time và CPUBusy, GPUBusy của PresentMon trước và sau công thành hoặc world boss. Trên bản build development: stat Unit của Unreal (thời gian game thread, render thread, GPU) - Đúng nếu: số người trên màn hình càng tăng thì frame time càng tăng theo, bật giới hạn số người hiển thị hoặc tùy chọn giảm hiệu ứng là đỡ ngay - Loại trừ nếu: vọt lên bất kể số người → “Frame time vọt lên” hoặc “Thu gom rác (GC) phía client”. FPS bình thường mà chỉ chuyển động của người khác bị trễ dần → “Nghẽn xử lý gói tin trên main thread” - Cách kiểm tra: Kiểm tra ở môi trường phía người chơi - Nguồn: - [Introduction to level of detail](https://docs.unity3d.com/Manual/LevelOfDetail.html) · Unity · Không có LOD thì vật thể trông nhỏ trên màn hình vẫn được vẽ với cùng độ phức tạp; LOD giảm tải việc vẽ - [Animation Budget Allocator in Unreal Engine](https://dev.epicgames.com/documentation/en-us/unreal-engine/animation-budget-allocator-in-unreal-engine) · Epic Games · Giảm linh động tần suất cập nhật hoạt ảnh (tick) của skeletal mesh để giữ thời gian dành cho hoạt ảnh trong ngân sách - [PresentMon Capture Application (README-CaptureApplication.md)](https://raw.githubusercontent.com/GameTechDev/PresentMon/main/README-CaptureApplication.md) · Intel · Dùng FrameTime, CPUBusy, GPUBusy để phân biệt CPU hay GPU đang làm chậm khung hình - [Stat Commands in Unreal Engine](https://dev.epicgames.com/documentation/en-us/unreal-engine/stat-commands-in-unreal-engine) · Epic Games · stat Unit: tổng frame time và thời gian game thread, render thread, GPU #### cg-net-mainthread · Nghẽn xử lý gói tin trên main thread · Network processing on the main thread Nếu mỗi khung hình chỉ xử lý một lượng gói tin cố định, số gói dồn tới sẽ liên tục bị đẩy sang khung hình sau. - Vì sao → Dẫn đến → Trên màn hình: Ở nơi đông người, mỗi giây có hàng nghìn bản cập nhật đổ về → Main thread chạm giới hạn xử lý mỗi khung hình nên đọc không hết → Chuyển động của người khác hiện ra ngày càng trễ và dồn cục - Triệu chứng: Tua nhanh, Trễ thao tác / Yếu tố: Ngưng trệ, Độ trễ - Ai gặp: Một địa điểm hoặc kênh, Chỉ mình tôi / Khi nào: Khi đông người - Phụ trách chính: Phát triển client (Đội phát triển game) / Phối hợp: Phát triển server (Đội phát triển game) - Việc cần làm (Đội phát triển game): Client: nhận và giải mã trên thread riêng, gộp các bản cập nhật vị trí cũ của cùng một đối tượng và chỉ áp dụng bản mới nhất. Server: ở nơi đông người, gửi cập nhật của nhân vật ở xa thưa hơn để giảm lượng dữ liệu gửi. - Con số tham khảo: Gói tin chưa xử lý mà dồn lại thì chỉ vài giây là đã trễ tới 1 giây. - Trên đồ thị: Tăng theo số người và tải (Số gói nhận chưa xử lý, độ trễ từ lúc nhận đến lúc áp dụng) - Chỗ cần xem: Ghi log số gói client để lại chưa xử lý ở mỗi khung hình và độ trễ từ lúc gói tới đến lúc áp dụng vào game, rồi đối chiếu với số người xung quanh - Đúng nếu: ở nơi đông người, số gói tồn và độ trễ áp dụng tăng liên tục, trong khi ping và khoảng cách gửi của server cùng lúc đó vẫn bình thường - Loại trừ nếu: không có độ trễ áp dụng nhưng bản thân gói tin tới muộn → đoạn mạng. Frame time tăng mạnh → “Tải render khi đông người” - Cách kiểm tra: Cần log và chỉ số của server, client game - Nguồn: - [Actor Priority in Unreal Engine](https://dev.epicgames.com/documentation/en-us/unreal-engine/actor-priority-in-unreal-engine) · Epic Games · Khi thiếu băng thông, thay vì replicate mọi actor mỗi lần, engine xếp ưu tiên theo khoảng cách tới người xem và thời gian kể từ lần replicate cuối - [Replication Graph in Unreal Engine](https://dev.epicgames.com/documentation/en-us/unreal-engine/replication-graph-in-unreal-engine) · Epic Games · Game có nhiều người kết nối và nhiều đối tượng cần replicate (như MMORPG) phải gom theo vị trí và chỉ gửi đối tượng cần thiết thì mới tránh được nghẽn CPU server #### cg-no-buffer · Bộ đệm nội suy không có hoặc quá ngắn · Missing/short interpolation buffer Nếu vẽ ngay khi nhận gói tin từ server, jitter (độ dao động của khoảng cách giữa các lần gói tin đến) lộ nguyên trên màn hình. - Vì sao → Dẫn đến → Trên màn hình: Vẽ ngay vị trí vừa nhận, hoặc bộ đệm ngắn hơn jitter → Gói tới muộn bao lâu thì dừng bấy lâu, gói dồn tới bao nhiêu thì nhảy bấy nhiêu → Nhân vật khác di chuyển khựng khựng - Triệu chứng: Giật khựng / Yếu tố: Jitter - Ai gặp: Chỉ mình tôi / Khi nào: Luôn luôn - Phụ trách chính: Phát triển client (Đội phát triển game) / Phối hợp: Phát triển server (Đội phát triển game) - Việc cần làm (Đội phát triển game): Client: dùng bộ đệm nội suy, tự động điều chỉnh độ dài bộ đệm theo tình trạng đường truyền. Server: áp dụng bù trễ (phán định có quay ngược) để phán định vẫn đúng khi người chơi bắn vào hình ảnh quá khứ trễ đúng bằng bộ đệm. - Con số tham khảo: Thường đặt bộ đệm bằng khoảng 2 lần khoảng cách gửi gói của server (nhận 20 lần trong 1 giây thì là 100 ms). - Trên đồ thị: Luôn cao ngay từ đầu (Khoảng cách gói tin đến, số lần bộ đệm nội suy bị cạn) - Chỗ cần xem: Ghi lại trên client phân bố khoảng cách đến của gói tin từ server, và số khung hình bị dừng hoặc chuyển sang ngoại suy vì không có snapshot kế tiếp để nội suy - Đúng nếu: độ dao động khoảng cách đến thường xuyên lớn hơn độ dài bộ đệm nội suy, mỗi lần như vậy bộ đệm cạn và nhân vật khác khựng lại. Tăng bộ đệm thì giảm - Loại trừ nếu: bộ đệm đủ mà vẫn khựng → kiểm tra xem bản thân khoảng cách gửi của server có thất thường không (tick bị trễ) - Cách kiểm tra: Cần log và chỉ số của server, client game - Tìm hiểu thêm: Tăng bộ đệm thì chuyển động mượt hơn, nhưng bạn cũng thấy đối thủ ở trạng thái quá khứ lâu hơn chừng ấy. Vì vậy, phán định đòn đánh dùng kèm bù trễ: server quay ngược về “quá khứ mà người đó đang thấy” để kiểm tra. - Nguồn: - [Interpolation and extrapolation (Netcode for Entities 6.5)](https://docs.unity3d.com/Packages/com.unity.netcode@6.5/manual/interpolation.html) · Unity · Nội suy có bộ đệm: cố ý vẽ trễ để chờ gói tới muộn; bộ đệm càng lớn càng chính xác nhưng độ trễ cũng tăng tương ứng - [Struct ClientTickRate (Netcode for Entities 6.5)](https://docs.unity3d.com/Packages/com.unity.netcode@6.5/api/Unity.NetCode.ClientTickRate.html) · Unity · Giá trị mặc định của bộ đệm nội suy InterpolationTimeNetTicks = 2 (bằng 2 lần gửi của server) - [Physics (Netcode for Entities 6.5)](https://docs.unity3d.com/Packages/com.unity.netcode@6.5/manual/physics.html) · Unity · Bù trễ: server tìm lại thế giới va chạm mà client đang thấy ở tick đó để phán định trúng hay trượt #### cg-extrap · Ngoại suy quá mức (dead reckoning) · Over-extrapolation / dead reckoning Trong lúc không có gói tin, client cho đối tượng tiếp tục di chuyển theo vận tốc cuối cùng, rồi khi biết là sai thì kéo lại. - Vì sao → Dẫn đến → Trên màn hình: Không nhận được gói tin nên tiếp tục cho di chuyển theo hướng và vận tốc cuối cùng → Trên thực tế đối thủ đã dừng lại hoặc đổi hướng → Nhân vật đối thủ đi một đoạn dài rồi vụt về vị trí thật, hoặc đi xuyên tường. Nếu khoảng cách gói tin đến lúc dài lúc ngắn, nhân vật cứ vượt lên trước rồi bị kéo lại, trông như đang rung - Triệu chứng: Dịch chuyển tức thời, Giật khựng / Yếu tố: Mất gói, Jitter - Ai gặp: Chỉ mình tôi / Khi nào: Thỉnh thoảng bất chợt - Phụ trách chính: Phát triển client (Đội phát triển game) - Việc cần làm (Đội phát triển game): Giới hạn thời gian ngoại suy (ví dụ 200–250 ms), khi sai thì đưa về vị trí đúng một cách mượt mà. - Con số tham khảo: Với tốc độ 6 m/s, chỉ cần sai 300 ms là lệch 1,8 m. - Trên đồ thị: Thỉnh thoảng vọt lên bất chợt (Thời gian ngoại suy, khoảng cách hiệu chỉnh vị trí) - Chỗ cần xem: Ghi lại thời gian nhân vật khác được vẽ bằng ngoại suy và khoảng cách sửa vị trí sau khi gói mới tới - Đúng nếu: ở mỗi đoạn mất gói, thời gian ngoại suy kéo dài không giới hạn, sau đó khoảng cách hiệu chỉnh lên tới vài m - Loại trừ nếu: ngoại suy đã được cắt ngắn mà vẫn dịch chuyển tức thời → bản thân mất gói và độ trễ đã lớn, xem phía đường truyền và tuyến đường - Cách kiểm tra: Cần log và chỉ số của server, client game - Nguồn: - [Interpolation and extrapolation (Netcode for Entities 6.5)](https://docs.unity3d.com/Packages/com.unity.netcode@6.5/manual/interpolation.html) · Unity · Ngoại suy (snapshot kế tiếp không tới đúng lúc thì tiếp tục di chuyển theo cùng hướng và vận tốc) hay sai nên cần giới hạn (Unity mặc định 20 tick, khoảng 1/3 giây ở 60 Hz) - [Peeking into VALORANT's Netcode](https://technology.riotgames.com/news/peeking-valorants-netcode) · Riot Games · Đoán để lấp dữ liệu tới muộn hoặc bị thiếu, nếu đoán sai thì lệch với server và nhân vật bị sửa vị trí kiểu nhảy cóc hoặc trượt đi #### cg-predict · Dự đoán phía client sai lệch · Prediction mismatch / reconciliation Client của bạn đã cho nhân vật di chuyển trước, nhưng nếu server tính ra kết quả khác thì nhân vật của bạn bị kéo đi. - Vì sao → Dẫn đến → Trên màn hình: Client cho di chuyển trước khi server xác nhận (dự đoán) → Server tính va chạm, tốc độ di chuyển, buff theo cách khác, hoặc không nhận được lệnh → Khi xác nhận về, nhân vật của bạn bị kéo ngược về sau - Triệu chứng: Kéo ngược / Yếu tố: Mất gói, Độ trễ - Ai gặp: Chỉ mình tôi / Khi nào: Khi di chuyển hoặc chuyển bản đồ, Thỉnh thoảng bất chợt - Phụ trách chính: Phát triển client (Đội phát triển game) / Phối hợp: Phát triển server (Đội phát triển game) - Việc cần làm (Đội phát triển game): Client: dùng chung code di chuyển với server, gửi lặp input (dư thừa), hiệu chỉnh mượt mà. Server: dùng chung code di chuyển với client, lọc input trùng theo số thứ tự input và chỉ xử lý một lần. - Con số tham khảo: Khoảng cách bị kéo là “thời gian lệch × tốc độ di chuyển”. Chỉ mất vài lệnh cũng đã 1–3 m. - Trên đồ thị: Thỉnh thoảng vọt lên bất chợt (Số lần hiệu chỉnh theo server (dự đoán sai)) - Chỗ cần xem: Ghi lại số lần và khoảng cách hiệu chỉnh vị trí mà server gửi. Với Unreal: đếm lần server hiệu chỉnh bằng ClientAdjustPosition; với Unity Netcode for Entities: đếm lần phải quay lại tính lại vì dự đoán sai - Đúng nếu: hiệu chỉnh dồn vào lúc người chơi báo kéo ngược, khoảng cách hiệu chỉnh lặp lại ở mức lớn với một số buff, địa hình hoặc skill di chuyển nhất định - Loại trừ nếu: hiệu chỉnh chỉ dồn vào lúc mất gói nhiều → mất gói input (đường truyền). Không có hiệu chỉnh mà chỉ nhân vật khác trông bị kéo đi → “Ngoại suy quá mức (dead reckoning)” - Cách kiểm tra: Cần log và chỉ số của server, client game - Nguồn: - [Introduction to prediction (Netcode for Entities 6.5)](https://docs.unity3d.com/Packages/com.unity.netcode@6.5/manual/intro-to-prediction.html) · Unity · Client và server dự đoán bằng cùng code mô phỏng; khi khác trạng thái server (dự đoán sai) thì quay lại tính lại và người chơi thấy được việc hiệu chỉnh - [Use the command stream to handle user inputs (Netcode for Entities 6.5)](https://docs.unity3d.com/Packages/com.unity.netcode@6.5/manual/command-stream.html) · Unity · Để phòng mất gói, gửi kèm input của vài tick trước cùng với input mới nhất - [Understanding Networked Movement in the Character Movement Component for Unreal Engine](https://dev.epicgames.com/documentation/en-us/unreal-engine/understanding-networked-movement-in-the-character-movement-component-for-unreal-engine) · Epic Games · Client di chuyển trước, server tái hiện cùng chuyển động đó; nếu vị trí khác nhau thì hiệu chỉnh bằng ClientAdjustPosition và áp dụng lại các chuyển động đã lưu #### cg-fixed-step · Fixed timestep chạy bù mất kiểm soát · Fixed-timestep catch-up / spiral of death Sau một lần dừng, game dồn phần tính toán bị tồn lại để chạy bù, và chính phần tính toán đó lại làm game chậm tiếp. - Vì sao → Dẫn đến → Trên màn hình: Mô phỏng game chạy theo bước thời gian cố định, rồi bị dừng một lần → Dồn các bước bị tồn vào một khung hình để tính → Các khung hình dài nối đuôi nhau gây giật, hoặc chạm giới hạn khiến cả thế giới chậm lại - Triệu chứng: Giật khựng, Tua nhanh, Quay chậm / Yếu tố: Ngưng trệ - Ai gặp: Chỉ mình tôi / Khi nào: Thỉnh thoảng bất chợt, Khi đông người - Phụ trách chính: Phát triển client (Đội phát triển game) - Việc cần làm (Đội phát triển game): Giới hạn số bước chạy bù mỗi khung hình, phần thời gian còn dư xử lý bằng nội suy. - Trên đồ thị: Thỉnh thoảng vọt lên bất chợt (Frame time, số bước cố định mỗi khung hình) - Chỗ cần xem: Trong profiler của bản build development, xem cùng lúc số bước cố định chạy trong một khung hình (với Unity là số marker của giai đoạn FixedUpdate như FixedBehaviourUpdate) và frame time - Đúng nếu: sau một khung hình dài là chuỗi khung hình dài chạy nhiều bước liền nhau; khi chạm giới hạn (Maximum Allowed Timestep của Unity) thì thời gian trong game trôi chậm hơn thực tế - Loại trừ nếu: khung hình dài chỉ xảy ra một lần rồi thôi → “Frame time vọt lên” hoặc “Thu gom rác (GC) phía client” - Cách kiểm tra: Cần log và chỉ số của server, client game - Tìm hiểu thêm: Phần tính vật lý của Unity (FixedUpdate) là ví dụ điển hình của bước cố định (mặc định 0,02 giây, 50 lần trong 1 giây). Maximum Allowed Timestep trong thiết lập Time (thời gian tối đa được chạy bù trong một khung hình, mặc định khoảng 0,33 giây) chính là giới hạn chạy bù. Khung hình dài hơn mức này thì phần thời gian vượt bị bỏ, và đồng hồ game chậm hơn thực tế đúng chừng ấy. - Nguồn: - [Set fixed timestep to optimize physics simulation frequency](https://docs.unity3d.com/Manual/physics-optimization-cpu-frequency.html) · Unity · Fixed Timestep mặc định 0,02 giây (50 lần trong 1 giây); khung hình dài thì phải chạy nhiều bước vật lý trong một khung hình nên tải tăng - [Handling variation in time](https://docs.unity3d.com/Manual/time-handling-variations.html) · Unity · Maximum Allowed Timestep mặc định 1/3 giây (0.3333333); dù dừng 1 giây, thời gian game chỉ trôi 0,333 giây. Đây là giới hạn chặn vòng luẩn quẩn các bước chạy bù lại làm game chậm thêm - [Profiler markers reference](https://docs.unity3d.com/Manual/profiler-markers.html) · Unity · FixedBehaviourUpdate: đoạn chạy MonoBehaviour.FixedUpdate; các marker vật lý được gọi trong giai đoạn FixedUpdate #### cg-clock · Sai lệch đồng bộ đồng hồ · Clock sync error Nếu giờ server mà client ước tính bị sai, thời điểm nội suy và việc phán định hồi chiêu sẽ lệch. - Vì sao → Dẫn đến → Trên màn hình: Chỉ đồng bộ giờ server một lần lúc kết nối, ping thay đổi cũng để nguyên → Thời điểm nội suy và thời điểm hết hồi chiêu lệch với server → Đối thủ thỉnh thoảng khựng lại, hết hồi chiêu rồi mà skill vẫn bị từ chối - Triệu chứng: Giật khựng, Nuốt thao tác·rollback / Yếu tố: Độ trễ - Ai gặp: Chỉ mình tôi / Khi nào: Càng chạy lâu càng nặng, Thỉnh thoảng bất chợt - Phụ trách chính: Phát triển client (Đội phát triển game) - Việc cần làm (Đội phát triển game): Đồng bộ thời gian định kỳ (đo thời gian khứ hồi rồi hiệu chỉnh), chỉnh dần dần thay vì đổi đột ngột, đo thời gian trôi qua bằng monotonic clock thay vì giờ của PC. - Trên đồ thị: Tăng dần (Sai số của giờ server ước tính) - Chỗ cần xem: Ghi định kỳ chênh lệch giữa giờ server do client ước tính và giờ server (số tick) mà server gửi kèm trong gói tin - Đúng nếu: sai số lớn dần theo thời gian sau khi kết nối, hoặc nhảy vọt một lần đúng lúc đồng hồ PC được chỉnh, và quanh lúc đó báo cáo skill bị từ chối hoặc bị khựng tăng lên - Loại trừ nếu: sai số vẫn nhỏ mà skill vẫn bị từ chối → phía phán định của server hoặc độ trễ - Cách kiểm tra: Cần log và chỉ số của server, client game - Tìm hiểu thêm: Nếu đo thời gian trôi qua bằng ngày giờ của PC (wall clock), thì đúng lúc Windows chỉnh đồng hồ theo giờ Internet hoặc người dùng đổi giờ, thời gian trong game sẽ nhảy. Thời gian trôi qua phải đo bằng đồng hồ không bao giờ chạy lùi (monotonic clock, ví dụ Stopwatch). - Nguồn: - [RFC 5905: Network Time Protocol Version 4: Protocol and Algorithms Specification](https://www.rfc-editor.org/rfc/rfc5905) · IETF · Cách tính độ trễ khứ hồi và sai lệch đồng hồ từ bốn mốc thời gian của yêu cầu và phản hồi - [Acquiring high-resolution time stamps](https://learn.microsoft.com/en-us/windows/win32/sysinfo/acquiring-high-resolution-time-stamps) · Microsoft · QueryPerformanceCounter (Stopwatch dùng cái này) là đồng hồ đo thời gian trôi qua, không đồng bộ với giờ bên ngoài; chỉ dùng giờ hệ thống khi cần giờ UTC - [Time synchronization (Netcode for Entities 6.5)](https://docs.unity3d.com/Packages/com.unity.netcode@6.5/manual/time-synchronization.html) · Unity · Ước tính giờ server từ thời gian khứ hồi, rồi chỉnh dần tốc độ chạy của đồng hồ cho khớp thay vì đổi giờ đột ngột #### cg-float-time · Mất độ chính xác thời gian kiểu float · Float time precision loss on long sessions Nếu game lưu thời gian bằng kiểu số thực độ chính xác thấp (float), game bật càng lâu thì độ phân giải thời gian (khoảng chênh nhỏ nhất còn phân biệt được) càng kém, khiến chuyển động và hiệu ứng bị rung. - Vì sao → Dẫn đến → Trên màn hình: Cộng dồn thời gian kể từ lúc bật game vào biến float, hoặc truyền thẳng giá trị đó cho shader → Bật càng lâu thì khoảng chênh nhỏ nhất mà float biểu diễn được càng lớn → Chỉ client bật liên tục mấy ngày mới bị rung nhân vật, hoạt ảnh và hiệu ứng chuyển động; khởi động lại là hết - Triệu chứng: Giật khựng / Yếu tố: Jitter - Ai gặp: Chỉ mình tôi / Khi nào: Càng chạy lâu càng nặng - Phụ trách chính: Phát triển client (Đội phát triển game) - Việc cần làm (Đội phát triển game): Lưu thời gian trôi qua bằng double (64 bit) hoặc số nguyên, định kỳ quay vòng giá trị thời gian truyền cho shader, chạy test tự động kéo dài nhiều ngày. - Con số tham khảo: Float 32 bit chỉ có hơn 7 chữ số có nghĩa, nên sau một ngày bật máy (khoảng 86.400 giây), độ phân giải thời gian đã thô tới khoảng 8 ms, cỡ nửa khung hình ở 60 FPS (16,7 ms); sau một tuần là khoảng 60 ms, lớn hơn cả một khung hình. - Trên đồ thị: Tăng dần (Báo cáo rung theo thời gian client đã bật) - Chỗ cần xem: Khi nhận báo cáo rung thì hỏi kèm client đã bật bao lâu, so sánh trước và sau khi khởi động lại. Phía phát triển: test bằng cách đặt giá trị thời gian game như thể đã chạy vài ngày - Đúng nếu: chỉ rung ở client đã bật mấy ngày, khởi động lại là hết, bật càng lâu càng nặng - Loại trừ nếu: vừa bật đã rung → “Bộ đệm nội suy không có hoặc quá ngắn” hoặc “Độ phân giải timer” - Cách kiểm tra: Kiểm tra ở môi trường phía người chơi - Tìm hiểu thêm: Lỗi này hay gặp nhất ở game MMO di động, nơi người chơi bật auto rồi treo máy nhiều ngày liền. Time.time của Unity cũng là float, nên Unity có thêm Time.timeAsDouble kiểu double và khuyên dùng giá trị này. - Nguồn: - [Time.timeAsDouble](https://docs.unity3d.com/ScriptReference/Time-timeAsDouble.html) · Unity · Phiên bản double của Time.time; chạy càng lâu càng chính xác hơn float, nên hầu hết trường hợp đều khuyên dùng - [Floating-point numeric types (C# reference)](https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/builtin-types/floating-point-numeric-types) · Microsoft · Độ chính xác của float khoảng 6–9 chữ số, double khoảng 15–17 chữ số #### cg-vsync · V-Sync và hàng đợi render · V-Sync, render queue GPU xếp vài khung hình đã vẽ vào hàng đợi rồi mới đẩy ra theo chu kỳ của màn hình, và trong lúc đó thao tác bị trễ. - Vì sao → Dẫn đến → Trên màn hình: Driver đồ họa xếp trước 1–3 khung hình vào hàng đợi → Thao tác mất thêm chừng ấy thời gian mới hiện lên màn hình → Ping thấp nhưng điều khiển thấy nặng và ì - Triệu chứng: Trễ thao tác, Giật khựng / Yếu tố: Độ trễ - Ai gặp: Chỉ mình tôi / Khi nào: Luôn luôn - Phụ trách chính: Phát triển client (Đội phát triển game) / Phối hợp: Bên ngoài (Bên ngoài) - Việc cần làm (Đội phát triển game): Hỗ trợ chế độ độ trễ thấp, giảm hàng đợi khung hình, cung cấp tùy chọn giới hạn FPS thấp hơn tần số quét một chút, trên điện thoại thì bật tính năng frame pacing. - Việc cần làm (Bên ngoài): Hướng dẫn người chơi dùng màn hình tần số quét biến thiên (VRR) kèm giới hạn FPS thấp hơn tần số quét một chút, và bật chế độ độ trễ thấp của driver đồ họa. - Con số tham khảo: Ở 60 Hz, mỗi khung hình là 16,7 ms. Nếu CPU chạy nhanh hơn GPU hoặc chu kỳ màn hình khiến hàng đợi ba khung hình (mặc định của DirectX 11) đầy, độ trễ tăng thêm 50 ms. Với V-Sync bộ đệm đôi, khung hình mất 17 ms phải chờ tới lần làm mới màn hình kế tiếp (33,3 ms), và trong lúc đó khung hình trước được hiển thị thêm một lần. - Trên đồ thị: Luôn cao ngay từ đầu (Độ trễ từ thao tác đến màn hình) - Chỗ cần xem: So sánh MsClickToPhotonLatency, MsAllInputToPhotonLatency (từ lúc nhập bằng chuột, bàn phím đến lúc đẩy ra màn hình) và DisplayLatency của PresentMon khi lần lượt đổi V-Sync, chế độ độ trễ thấp, giới hạn FPS. MsPCLatency (từ lúc PC nhận input đến lúc gửi ra màn hình) chỉ được ghi khi game phát sự kiện PC Latency - Đúng nếu: khi bật V-Sync hoặc không giới hạn FPS, độ trễ này tăng thêm một, hai khung hình (vài chục ms), và giảm khi dùng chế độ độ trễ thấp hoặc giới hạn FPS thấp hơn tần số quét một chút. Ping không đổi - Loại trừ nếu: độ trễ bên trong PC thấp mà điều khiển vẫn trễ → “Độ trễ của màn hình, thiết bị nhập và tạo khung hình”; ping cao → phía mạng - Cách kiểm tra: Kiểm tra ở môi trường phía người chơi - Tìm hiểu thêm: V-Sync (đồng bộ dọc) là thiết lập chỉ đẩy khung hình mới ra đúng lúc màn hình đổi hình. Hiện tượng xé hình biến mất, nhưng thao tác bị trễ đúng bằng thời gian chờ thời điểm đó, và khi FPS tụt dưới 60 thì game nhảy qua lại giữa 60 và 30, gây giật khựng. Màn hình tần số quét biến thiên đổi hình theo lúc khung hình sẵn sàng, nên giảm được thời gian chờ này. Trên điện thoại cũng xảy ra chuyện tương tự. Game 30 FPS mà không đẩy khung hình ra đều đặn trên màn hình 60 Hz thì dù trung bình vẫn là 30 FPS, mỗi khung hình lại hiển thị lúc dài lúc ngắn như 49 ms, 16 ms, 33 ms và gây giật khựng (ví dụ trong tài liệu phát triển Android). Có thể giảm hiện tượng này bằng thư viện frame pacing của Android (làm đều khoảng cách xuất khung hình) hoặc tùy chọn tương tự của engine. - Nguồn: - [IDXGIDevice1::SetMaximumFrameLatency](https://learn.microsoft.com/en-us/windows/win32/api/dxgi/nf-dxgi-idxgidevice1-setmaximumframelatency) · Microsoft · Số khung hình driver được xếp vào hàng đợi mặc định là 3 (1–16) - [Reduce latency with DXGI 1.3 swap chains](https://learn.microsoft.com/en-us/windows/uwp/gaming/reduce-latency-with-dxgi-1-3-swap-chains) · Microsoft · Present bị chặn cho tới khi hàng đợi trống, nên từ lúc vẽ xong đến lúc hiển thị phải chờ thêm gần một khung hình; giảm được bằng waitable swap chain - [Frame Pacing library](https://developer.android.com/games/sdk/frame-pacing) · Android (Google) · Trên màn hình 60 Hz, không có khung hình mới thì khung hình trước được hiển thị lại; ví dụ game 30 FPS có frame time lúc dài lúc ngắn như 49 ms, 16 ms, 33 ms - [PresentMon Capture Application (README-CaptureApplication.md)](https://raw.githubusercontent.com/GameTechDev/PresentMon/main/README-CaptureApplication.md) · Intel · MsPCLatency (từ lúc PC nhận input đến lúc gửi ra màn hình), MsClickToPhotonLatency (từ cú click chuột đến màn hình), MsAllInputToPhotonLatency (từ input bàn phím, chuột đến màn hình), DisplayLatency (từ lúc nộp khung hình đến lúc đẩy ra màn hình) - [PresentMon Console Application (README-ConsoleApplication.md)](https://raw.githubusercontent.com/GameTechDev/PresentMon/main/README-ConsoleApplication.md) · Intel · MsPCLatency chỉ được ghi khi ứng dụng phát sự kiện PC Latency (--track_pc_latency); MsAllInputToPhotonLatency tính theo input bàn phím, chuột #### cg-leak · Rò rỉ bộ nhớ phía client · Client memory leak Game bật càng lâu thì bộ nhớ dùng càng tăng, game chậm dần rồi cuối cùng bị tắt cưỡng bức. - Vì sao → Dẫn đến → Trên màn hình: Texture, UI, hiệu ứng không được giải phóng khi chuyển qua lại giữa các bản đồ → GC chạy dày hơn, OS thiếu bộ nhớ nên phải dùng swap → Sau vài giờ chơi, game giật khựng ngày càng nhiều rồi bị tắt cưỡng bức (người chơi thấy giống mất kết nối) - Triệu chứng: Giật khựng, Mất kết nối / Yếu tố: Ngưng trệ - Ai gặp: Chỉ mình tôi / Khi nào: Càng chạy lâu càng nặng - Phụ trách chính: Phát triển client (Đội phát triển game) - Việc cần làm (Đội phát triển game): Đo mức dùng bộ nhớ mỗi lần chuyển bản đồ, tìm và sửa texture, UI, hiệu ứng không được giải phóng, chạy test tự động dài giờ (soak test). - Trên đồ thị: Tăng dần (Bộ nhớ của tiến trình game) - Chỗ cần xem: Ghi Process(game)\Private Bytes bằng Performance Monitor trong vài giờ. Trên di động: lý do thoát (REASON_LOW_MEMORY) trong ApplicationExitInfo của Android và báo cáo jetsam của iOS - Đúng nếu: mỗi lần chuyển qua lại giữa các bản đồ, bộ nhớ tăng lên mà không giảm xuống; bật càng lâu thì giật khựng và bị tắt càng nhiều - Loại trừ nếu: bộ nhớ ổn định, chỉ bị rung khi bật lâu → “Mất độ chính xác thời gian kiểu float” - Cách kiểm tra: Kiểm tra ở môi trường phía người chơi - Tìm hiểu thêm: Điện thoại chủ yếu cầm cự bằng cách nén bộ nhớ. Nếu vẫn thiếu, OS tắt game ngay lập tức (văng game). Máy càng ít RAM thì càng bị tắt sớm. - Nguồn: - [Memory allocation among processes](https://developer.android.com/topic/performance/memory-management) · Android (Google) · Android nén bộ nhớ vào zRAM để cầm cự, thiếu nữa thì low memory killer kết thúc tiến trình; ứng dụng đang hiện ở foreground bị tắt thì trông như crash - [Identifying high-memory use with jetsam event reports](https://developer.apple.com/documentation/xcode/identifying-high-memory-use-with-jetsam-event-reports) · Apple · iOS buộc tắt ứng dụng (jetsam) khi áp lực bộ nhớ không giảm; ứng dụng nào vượt giới hạn bộ nhớ dành cho nó thì bị đưa vào diện bị tắt - [Find user-mode memory leaks with Performance Monitor (PerfMon)](https://learn.microsoft.com/en-us/windows-hardware/drivers/debugger/using-performance-monitor-to-find-a-user-mode-memory-leak) · Microsoft · Ghi lâu Process > Private Bytes (bộ nhớ riêng tiến trình đã cấp phát) và Virtual Bytes; nếu chỉ tăng mãi thì là rò rỉ - [ApplicationExitInfo](https://developer.android.com/reference/android/app/ApplicationExitInfo) · Android (Google) · REASON_LOW_MEMORY: low memory killer của hệ thống đã kết thúc tiến trình ứng dụng (máy không hỗ trợ thì báo là REASON_SIGNALED, SIGKILL) #### cg-crash · Client bị crash · Client crash Game tắt vì một lỗi không được xử lý. Người chơi thấy giống mất kết nối, nhưng server vẫn bình thường. - Vì sao → Dẫn đến → Trên màn hình: Tham chiếu null, thiếu bộ nhớ, lỗi driver đồ họa → Tiến trình game bị kết thúc đột ngột → Báo cáo “bị văng game”. Cùng lúc đó người khác vẫn chơi bình thường - Triệu chứng: Mất kết nối / Yếu tố: Ngưng trệ - Ai gặp: Chỉ mình tôi / Khi nào: Khi làm một thao tác nhất định, Thỉnh thoảng bất chợt - Phụ trách chính: Phát triển client (Đội phát triển game) / Phối hợp: Bên ngoài (Bên ngoài) - Việc cần làm (Đội phát triển game): Thu thập crash report, thống kê theo thiết bị và driver, sửa lỗi xảy ra nhiều nhất trước. - Việc cần làm (Bên ngoài): Nếu tập trung ở một phiên bản driver đồ họa nhất định, hướng dẫn người chơi cập nhật driver. - Trên đồ thị: Chỉ một phần cao (Số lần crash (theo thiết bị, driver đồ họa, bản build)) - Chỗ cần xem: Crash report và tỷ lệ crash trên Android vitals theo thiết bị, driver, bản build. Trên PC người chơi: Event ID 1000 trong log Application của Event Viewer (tên module gây lỗi) và dòng “Display driver stopped responding and has recovered” - Đúng nếu: có bản ghi crash đúng lúc người chơi báo mất kết nối, còn người chơi khác trên cùng server lúc đó vẫn bình thường. Tập trung ở một thiết bị, phiên bản driver hoặc module nhất định - Loại trừ nếu: không có bản ghi crash mà chỉ mất kết nối → “Ánh xạ NAT hết hạn” hoặc phía đường truyền - Cách kiểm tra: Cần log và chỉ số của server, client game - Nguồn: - [Crashes](https://developer.android.com/topic/performance/vitals/crash) · Android (Google) · Crash là khi ứng dụng kết thúc bất ngờ do exception không được xử lý hoặc signal (như SIGSEGV); thống kê bằng Android vitals trong Play Console - [WDDM Support for Timeout Detection and Recovery (TDR)](https://learn.microsoft.com/en-us/windows-hardware/drivers/display/timeout-detection-and-recovery) · Microsoft · Nếu GPU không xong việc trong 2 giây (mặc định), Windows reset driver đồ họa và GPU - [The application or service crashing behavior troubleshooting guidance](https://learn.microsoft.com/en-us/troubleshoot/windows-server/performance/troubleshoot-application-service-crashing-behavior) · Microsoft · Event ID 1000 trong log Application là bản ghi crash thực sự, chứa tên ứng dụng lỗi và tên module lỗi (Faulting module name) #### cg-anticheat · Kiểm tra của module bảo mật game (anti-cheat) · Anti-cheat scan and heartbeat Module bảo mật chạy cùng game để chống hack sẽ kiểm tra định kỳ. Nếu lần kiểm tra nặng, hoặc heartbeat (tín hiệu định kỳ báo vẫn còn sống) trao đổi với server bảo mật bị trễ, game sẽ giật khựng hoặc mất kết nối. - Vì sao → Dẫn đến → Trên màn hình: Module bảo mật định kỳ quét bộ nhớ game, các chương trình đang chạy và driver → Trong lúc quét, game thread bị dừng, hoặc heartbeat không gửi đi kịp → Khựng theo khoảng cách đều, nặng thì mất kết nối kèm thông báo lỗi bảo mật - Triệu chứng: Giật khựng, Đứng hình, Mất kết nối / Yếu tố: Ngưng trệ - Ai gặp: Chỉ mình tôi / Khi nào: Theo chu kỳ đều, Ngay sau đăng nhập hoặc bảo trì, Thỉnh thoảng bất chợt - Phụ trách chính: Phát triển client (Đội phát triển game) / Phối hợp: Phát triển server (Đội phát triển game) - Việc cần làm (Đội phát triển game): Client: chạy các lần kiểm tra nặng bên ngoài game thread và chia nhỏ ra, so sánh thống kê giật khựng và văng game theo phiên bản module bảo mật (nếu dồn lại ngay sau bản cập nhật thì báo cho nhà cung cấp module bảo mật). Server: chấp nhận heartbeat trễ một, hai lần. - Con số tham khảo: Lần kiểm tra nhẹ thường mất chưa tới 1 ms, nhưng lần kiểm tra nặng chạy trên game thread, tùy cách cài đặt, có thể chiếm vài chục đến vài trăm ms mỗi lần. - Trên đồ thị: Vọt lên theo chu kỳ (Frame time, số lần bị anti-cheat kick) - Chỗ cần xem: Đo khoảng cách giữa các lần vọt lên trong frame time của PresentMon, thống kê lý do kick của anti-cheat mà server nhận được (với EOS là AuthenticationFailed / Authentication Timed Out… trong ClientActionReason) theo phiên bản module bảo mật và cấu hình máy - Đúng nếu: những lần dừng ngắn lặp lại theo khoảng cách đều bất kể tình huống trong game; ngay sau khi module bảo mật cập nhật, ở một cấu hình nhất định số lần giật khựng và bị kick vì hết thời gian xác thực tăng lên - Loại trừ nếu: khoảng cách như nhau ở mọi cấu hình bất kể phiên bản module bảo mật → “Thu gom rác (GC) phía client” hoặc “Tiến trình chạy nền chiếm giữ CPU” - Cách kiểm tra: Cần log và chỉ số của server, client game - Tìm hiểu thêm: Module bảo mật cài sâu vào OS dưới dạng driver nên đôi khi xung đột với phần mềm diệt virus, overlay hoặc module bảo mật của game khác. Nếu ngay sau khi module bảo mật cập nhật mà báo cáo giật khựng và văng game chỉ dồn về từ một cấu hình nhất định, hãy nghi nguyên nhân này trước. - Nguồn: - [Using the Anti-Cheat Interfaces](https://dev.epicgames.com/docs/epic-online-services/trust-and-safety/anti-cheat-interfaces/using-anti-cheat) · Epic Games · Nếu server không nhận được message anti-cheat của client trong thời gian quy định (RegisterTimeout) thì kick vì hết thời gian xác thực (nguyên nhân hay gặp là client bị treo do loading); nếu bản cập nhật module gần đây gây lỗi thì quay về module cũ - [PresentMon Capture Application (README-CaptureApplication.md)](https://raw.githubusercontent.com/GameTechDev/PresentMon/main/README-CaptureApplication.md) · Intel · Ghi lại thời gian từng khung hình bằng FrameTime (thời gian CPU giữa hai khung hình) ### L2 OS và thiết bị phía client (15 nguyên nhân) #### co-background · Tiến trình chạy nền chiếm giữ CPU · Background CPU contention Khi việc quét virus, Windows Update, phần mềm livestream hay video trên trình duyệt chiếm các core, game thread không được cấp CPU và phải chờ. - Vì sao → Dẫn đến → Trên màn hình: Chương trình khác chiếm giữ core CPU trong thời gian dài → Game thread phải chờ được lập lịch → Khung hình bị trễ, việc xử lý gói tin đã nhận cũng trễ theo - Triệu chứng: Giật khựng, Tua nhanh / Yếu tố: Ngưng trệ - Ai gặp: Chỉ mình tôi / Khi nào: Thỉnh thoảng bất chợt, Theo chu kỳ đều - Phụ trách chính: Bên ngoài (Bên ngoài) / Phối hợp: Phát triển client (Đội phát triển game) - Việc cần làm (Đội phát triển game): Điều chỉnh mức ưu tiên của game thread, ghi kèm mức dùng CPU của toàn PC vào log lúc xảy ra giật để phân biệt có phải do chương trình khác hay không. - Việc cần làm (Bên ngoài): Hướng dẫn người chơi bật Game Mode của Windows, tắt các chương trình không cần thiết khi chơi (quét virus, Windows Update, phần mềm livestream, video trên trình duyệt). - Con số tham khảo: Windows thường giao core cho mỗi thread vài ms đến vài chục ms một lần. Chỉ cần bị lỡ một lượt lập lịch là mất một khung hình. - Trên đồ thị: Thỉnh thoảng vọt lên bất chợt (Mức dùng CPU của toàn PC, frame time) - Chỗ cần xem: Ghi cột CPU ở tab Processes của Task Manager và Processor Information(_Total)\% Processor Time trong Performance Monitor cùng với frame time của PresentMon. Nghi phần mềm diệt virus thì ghi bằng New-MpPerformanceRecording rồi dùng Get-MpPerformanceReport xem file, tiến trình nào tốn nhiều thời gian quét - Đúng nếu: đúng lúc giật, mức dùng CPU của chương trình khác (quét virus, cập nhật, phần mềm livestream) vọt lên, hoặc file trong thư mục game nằm trong nhóm tốn thời gian quét nhiều nhất. Tắt chương trình đó hoặc đưa vào danh sách loại trừ thì hết - Loại trừ nếu: mức dùng CPU thấp mà cả màn hình vẫn khựng và tiếng bị rè → “Tiết kiệm điện NIC và lỗi driver” (độ trễ DPC) - Cách kiểm tra: Kiểm tra ở môi trường phía người chơi - Tìm hiểu thêm: Windows ưu tiên hơn một chút cho chương trình ở cửa sổ đang hiện phía trước (foreground), nhưng khi số việc cần chạy nhiều hơn số core thì game vẫn phải chờ. Với phần mềm diệt virus, tính năng “giám sát thời gian thực” còn hay chen vào hơn cả việc dùng CPU. Nó quét mỗi lần game mở file, nên những lần dừng lúc đọc asset kéo dài hơn. - Nguồn: - [Multitasking](https://learn.microsoft.com/en-us/windows/win32/procthread/multitasking) · Microsoft · Windows cấp cho mỗi thread một time slice, dùng hết thì chuyển sang thread tiếp theo; time slice khoảng 20 ms (tùy OS và CPU) - [Priority Boosts](https://learn.microsoft.com/en-us/windows/win32/procthread/priority-boosts) · Microsoft · Tiến trình của cửa sổ đang ở phía trước (foreground) được nâng mức ưu tiên lên bằng hoặc cao hơn tiến trình chạy nền - [About regular quick and full scans with Microsoft Defender Antivirus](https://learn.microsoft.com/en-us/defender-endpoint/schedule-antivirus-scans) · Microsoft · Tính năng bảo vệ thời gian thực quét mỗi lần mở, đóng file và mỗi lần mở thư mục - [Network-Related Performance Counters](https://learn.microsoft.com/en-us/windows-server/networking/technologies/network-subsystem/net-sub-performance-counters) · Microsoft · Processor Information: counter % Processor Time - [Performance analyzer for Microsoft Defender Antivirus](https://learn.microsoft.com/en-us/defender-endpoint/tune-performance-defender-antivirus) · Microsoft · Ghi bằng New-MpPerformanceRecording và dùng Get-MpPerformanceReport xem các file, đường dẫn, tiến trình ảnh hưởng nhiều nhất tới thời gian quét - [PresentMon Capture Application (README-CaptureApplication.md)](https://raw.githubusercontent.com/GameTechDev/PresentMon/main/README-CaptureApplication.md) · Intel · Ghi lại thời gian từng khung hình bằng FrameTime (thời gian CPU giữa hai khung hình) #### co-power · Chế độ tiết kiệm điện và bóp xung do nhiệt · Power saving, thermal throttling Chế độ pin của laptop, chế độ tiết kiệm pin của điện thoại hay máy nóng lên đều làm tốc độ CPU, GPU giảm. Đặc trưng của quá nhiệt là lúc đầu vẫn ổn, chơi một lúc lâu mới bắt đầu chậm. - Vì sao → Dẫn đến → Trên màn hình: Đang ở chế độ pin hoặc tiết kiệm điện, hoặc máy nóng lên → Xung nhịp CPU, GPU bị hạ 30–50% tùy thiết bị → Với chế độ tiết kiệm điện thì ngay khi bật game, với quá nhiệt thì sau khi chơi vài phút đến khoảng 20 phút, FPS tụt và bị giật khựng - Triệu chứng: Giật khựng, Trễ thao tác / Yếu tố: Ngưng trệ - Ai gặp: Chỉ mình tôi / Khi nào: Càng chạy lâu càng nặng, Luôn luôn - Phụ trách chính: Bên ngoài (Bên ngoài) / Phối hợp: Phát triển client (Đội phát triển game) - Việc cần làm (Đội phát triển game): Tự động điều chỉnh tùy chọn đồ họa, kiểm soát nhiệt bằng giới hạn FPS, dựa vào mức nhiệt OS báo (thermalState của iOS, API trạng thái nhiệt của Android) để hạ tùy chọn trước, đánh dấu trong file thực thi để laptop có hai chip đồ họa dùng card đồ họa rời (export NvOptimusEnablement, AmdPowerXpressRequestHighPerformance). - Việc cần làm (Bên ngoài): Hướng dẫn người chơi tắt chế độ tiết kiệm điện, khuyên cắm sạc khi chơi trên laptop. Với báo cáo “laptop cấu hình tốt mà FPS thấp”, hướng dẫn kiểm tra game đang chạy trên chip đồ họa nào và chỉ định GPU hiệu năng cao cho game trong phần cài đặt Graphics của Windows. - Trên đồ thị: Tăng dần (FPS, xung nhịp CPU và GPU) - Chỗ cần xem: Ghi CPUFrequency, GPUFrequency, CPUTemperature, GPUTemperature của PresentMon cùng frame time trong 20–30 phút, và xem cột GPU engine ở tab Processes của Task Manager để biết game chạy trên chip đồ họa nào. Trên di động: ghi API nhiệt của Android (getThermalHeadroom, trạng thái nhiệt) và thermalState của iOS cùng với FPS - Đúng nếu: FPS giảm từ lúc xung nhịp tụt sau khi nhiệt độ tăng, hoặc xung nhịp chỉ thấp khi ở chế độ pin hay tiết kiệm điện. Hoặc game đang chạy trên đồ họa tích hợp - Loại trừ nếu: xung nhịp và nhiệt độ không đổi mà FPS vẫn tụt → “Tiến trình chạy nền chiếm giữ CPU” hoặc “Rò rỉ bộ nhớ phía client” - Cách kiểm tra: Kiểm tra ở môi trường phía người chơi - Tìm hiểu thêm: Laptop có hai chip đồ họa đôi khi chạy game bằng đồ họa tích hợp (onboard) chậm hơn để tiết kiệm điện. Với báo cáo “laptop cấu hình tốt mà FPS thấp”, hãy kiểm tra trước xem game đang chạy trên chip đồ họa nào. - Nguồn: - [Thermal API](https://developer.android.com/games/optimize/adpf/thermal) · Android (Google) · Thiết bị chỉ giữ hiệu năng cao trong thời gian giới hạn, sau đó bị bóp xung do nhiệt; khuyến nghị theo dõi trạng thái nhiệt để giảm tải trước - [thermalState](https://developer.apple.com/documentation/foundation/processinfo/thermalstate-swift.property) · Apple · Mức nhiệt hiện tại do iOS báo; mức càng cao thì ứng dụng càng phải giảm dùng tài nguyên - [Selecting the Best Graphics Device to Run a 3D Intensive Application](https://gpuopen.com/learn/amdpowerxpressrequesthighperformance/) · AMD · Trên laptop có hai chip đồ họa, game 60 FPS có thể tụt còn 30 FPS nếu chạy bằng đồ họa tích hợp; export AmdPowerXpressRequestHighPerformance để chọn card đồ họa rời - [PresentMon Capture Application (README-CaptureApplication.md)](https://raw.githubusercontent.com/GameTechDev/PresentMon/main/README-CaptureApplication.md) · Intel · Ghi CPUFrequency, GPUFrequency (xung nhịp), CPUTemperature, GPUTemperature (nhiệt độ) theo từng khung hình - [GPUs in the task manager](https://devblogs.microsoft.com/directx/gpus-in-the-task-manager/) · Microsoft · Task Manager có các cột hiển thị mức dùng GPU theo tiến trình và giá trị đó thuộc GPU, engine nào #### co-timer · Độ phân giải timer · Timer resolution (Windows 15.6ms) Timer mặc định của Windows có bước 15,6 ms, nên lệnh “chỉ nghỉ 1 ms” thực tế kéo dài tới chu kỳ timer kế tiếp, lâu nhất tới 15,6 ms. - Vì sao → Dẫn đến → Trên màn hình: Giới hạn khung hình và gửi gói tin được cài đặt bằng Sleep (chờ một chút) → OS chỉ đánh thức theo bước 15,6 ms → Khoảng cách giữa các khung hình và giữa các lần gửi input lúc dài lúc ngắn - Triệu chứng: Giật khựng / Yếu tố: Jitter - Ai gặp: Chỉ mình tôi / Khi nào: Luôn luôn - Phụ trách chính: Phát triển client (Đội phát triển game) - Việc cần làm (Đội phát triển game): Dùng timer độ phân giải cao, điều nhịp khung hình (pacing) dựa trên event hoặc V-Sync thay vì chờ bằng Sleep. - Con số tham khảo: Với bước 15,6 ms thì không thể canh đúng khoảng 16,7 ms, nên khoảng cách khung hình nhảy qua lại giữa 15,6 ms và 31,2 ms. - Trên đồ thị: Luôn cao ngay từ đầu (Phân bố khoảng cách khung hình) - Chỗ cần xem: Phân bố MsBetweenPresents (khoảng cách khung hình) của PresentMon và mục “Platform Timer Resolution” trong báo cáo powercfg /energy (tiến trình đã đổi độ phân giải timer) - Đúng nếu: khoảng cách khung hình dồn vào các bội số của 15,6 ms như 15,6 ms và 31,2 ms, và game không yêu cầu độ phân giải timer cao hơn - Loại trừ nếu: khoảng cách phân tán đều → xem “Tiến trình chạy nền chiếm giữ CPU” hoặc tải khung hình - Cách kiểm tra: Kiểm tra ở môi trường phía người chơi - Tìm hiểu thêm: Ở Windows đời cũ, chỉ cần một chương trình đổi timer thành 1 ms là mọi chương trình đều được áp dụng. Vì thế từng có lời đồn “bật trình duyệt thì game mượt hơn”. Từ bản Windows 10 2004, thiết lập này chỉ áp dụng cho chương trình yêu cầu, còn Windows 11 có thể bỏ qua yêu cầu của cửa sổ đang thu nhỏ hoặc bị che hoàn toàn và không phát âm thanh. - Nguồn: - [_WDF_TIMER_CONFIG (wdftimer.h)](https://learn.microsoft.com/en-us/windows-hardware/drivers/ddi/wdftimer/ns-wdftimer-_wdf_timer_config) · Microsoft · Độ chính xác của timer thông thường bằng khoảng cách tick đồng hồ hệ thống, mặc định 15,6 ms; timer độ phân giải cao là 1 ms - [timeBeginPeriod function (timeapi.h)](https://learn.microsoft.com/en-us/windows/win32/api/timeapi/nf-timeapi-timebeginperiod) · Microsoft · Trước bản Windows 10 2004 là thiết lập toàn cục, từ bản đó trở đi chỉ áp dụng cho tiến trình yêu cầu; Windows 11 không bảo đảm độ phân giải cao cho tiến trình có cửa sổ bị che hoặc thu nhỏ - [CreateWaitableTimerExW function (synchapi.h)](https://learn.microsoft.com/en-us/windows/win32/api/synchapi/nf-synchapi-createwaitabletimerexw) · Microsoft · Cờ cho waitable timer độ phân giải cao CREATE_WAITABLE_TIMER_HIGH_RESOLUTION - [PresentMon Capture Application (README-CaptureApplication.md)](https://raw.githubusercontent.com/GameTechDev/PresentMon/main/README-CaptureApplication.md) · Intel · MsBetweenPresents: thời gian giữa lần gọi Present() này và lần gọi trước (ms) - [Results for the Idle Energy Efficiency Assessment](https://learn.microsoft.com/en-us/windows-hardware/test/assessments/results-for-the-idle-energy-efficiency-assessment) · Microsoft · Độ phân giải timer hệ thống mặc định 15,6 ms; tìm tiến trình đã đổi độ phân giải timer ở mục “Platform Timer Resolution” trong báo cáo năng lượng - [Powercfg command-line options](https://learn.microsoft.com/en-us/windows-hardware/design/device-experiences/powercfg-command-line-options) · Microsoft · powercfg /energy: phân tích hệ thống và tạo báo cáo năng lượng (HTML) #### co-mobile-bg · Ứng dụng di động chuyển xuống chạy nền · App suspended in background Khi bạn tạm chuyển ra khỏi game để xem thông báo, vài giây sau OS tạm dừng (suspend) ứng dụng, và trong lúc đó server ngắt kết nối của bạn. - Vì sao → Dẫn đến → Trên màn hình: Chuyển ra khỏi game để xem tin nhắn hoặc nghe điện thoại → Engine game dừng xử lý game, OS cũng nhanh chóng dừng ứng dụng và kết nối mạng → Quay lại thì đã mất kết nối, phải kết nối lại - Triệu chứng: Mất kết nối / Yếu tố: Ngưng trệ, Mất gói - Ai gặp: Chỉ mình tôi / Khi nào: Sau khi để yên một lúc, Khi làm một thao tác nhất định - Phụ trách chính: Phát triển client (Đội phát triển game) / Phối hợp: Phát triển server (Đội phát triển game) - Việc cần làm (Đội phát triển game): Client: khi quay lại, kết nối lại ngay bằng session token (nối tiếp mà không cần đăng nhập lại) thay vì chờ kết nối đã đứt, rồi nhận trạng thái mới nhất một lần để đồng bộ. Server: khi mất heartbeat thì dọn kết nối nhưng vẫn giữ session nhân vật trong một khoảng ân hạn ngắn (không đá ra ngay), nếu người chơi kết nối lại trong khoảng đó thì nối tiếp bằng session token. - Con số tham khảo: Engine game thường dừng ngay khi bị đưa xuống nền. iOS tạm dừng ứng dụng sau vài giây, xin thêm thời gian thì thường cũng chỉ trong vòng vài chục giây; Android 14 trở lên đóng băng (freeze) ứng dụng đã rời màn hình sau khoảng 10 giây. - Trên đồ thị: Rớt kết nối hàng loạt (Số lần mất kết nối (heartbeat timeout), bản ghi ứng dụng tạm dừng) - Chỗ cần xem: Đối chiếu theo session ID thời điểm ứng dụng tạm dừng và quay lại trong log client (Unity là OnApplicationPause) với lý do, thời điểm ngắt kết nối phía server. Với Android, xem thêm lý do kết thúc tiến trình ghi trong ApplicationExitInfo (như REASON_LOW_MEMORY) - Đúng nếu: client chuyển sang tạm dừng ngay trước khi server ngắt vì heartbeat timeout, và kết nối lại ngay sau khi quay lại - Loại trừ nếu: mất kết nối ngay cả khi game đang mở ở foreground → “Ánh xạ NAT hết hạn”, “IP dùng chung của nhà mạng (CGNAT)”, “Chuyển đổi Wi-Fi ↔ LTE hoặc 5G” - Cách kiểm tra: Cần log và chỉ số của server, client game - Tìm hiểu thêm: Khi thiếu bộ nhớ, điện thoại có thể tắt hẳn game đang chạy nền. Đó là lý do game khởi động lại từ đầu sau khi bạn mở camera hoặc ứng dụng thanh toán, xác thực rồi quay lại. Máy cấu hình càng thấp càng hay gặp. - Nguồn: - [Extending your app’s background execution time](https://developer.apple.com/documentation/uikit/extending-your-app-s-background-execution-time) · Apple · Khi xuống nền, applicationDidEnterBackground có 5 giây rồi ứng dụng bị tạm dừng; cần thêm thì xin thời gian bằng beginBackgroundTask (thời gian còn lại xem ở backgroundTimeRemaining) - [Cached apps freezer](https://source.android.com/docs/core/perf/cached-apps-freezer) · Android (Google) · Android 14 trở lên đóng băng tiến trình ứng dụng ở trạng thái cached sau 10 giây; khi bị đóng băng, mọi thread đều dừng - [Application.runInBackground](https://docs.unity3d.com/ScriptReference/Application-runInBackground.html) · Unity · Mặc định là false nên game dừng khi chạy nền; trên Android, cứ chạy nền là dừng bất kể thiết lập, còn iOS bỏ qua thiết lập này - [MonoBehaviour.OnApplicationPause(bool)](https://docs.unity3d.com/ScriptReference/MonoBehaviour.OnApplicationPause.html) · Unity · Khi ứng dụng bị tạm dừng hoặc chạy lại, gửi OnApplicationPause(true/false) tới mọi MonoBehaviour - [ApplicationExitInfo](https://developer.android.com/reference/android/app/ApplicationExitInfo) · Android (Google) · REASON_LOW_MEMORY: low memory killer của hệ thống đã kết thúc tiến trình ứng dụng (máy không hỗ trợ thì báo là REASON_SIGNALED, SIGKILL) #### co-netswitch · Chuyển đổi Wi-Fi ↔ LTE hoặc 5G · Network switch changes IP Khi bạn ra khỏi nhà, Wi-Fi mất và máy chuyển sang LTE hoặc 5G, địa chỉ IP của bạn thay đổi nên kết nối hiện có không còn dùng được. - Vì sao → Dẫn đến → Trên màn hình: Sóng Wi-Fi yếu đi nên máy chuyển sang mạng di động → Địa chỉ IP của bạn đổi, nên kết nối đã thiết lập bằng địa chỉ cũ không trao đổi dữ liệu được nữa → Đứng hình một chút rồi mất kết nối hoặc phải kết nối lại - Triệu chứng: Đứng hình, Mất kết nối / Yếu tố: Mất gói - Ai gặp: Chỉ mình tôi / Khi nào: Khi di chuyển hoặc chuyển bản đồ - Phụ trách chính: Phát triển server (Đội phát triển game) / Phối hợp: Phát triển client (Đội phát triển game), Hạ tầng mạng (Đội hạ tầng) - Việc cần làm (Đội phát triển game): Server: dùng session token để nhận lại đúng người chơi đó dù địa chỉ đã đổi và dọn ngay kết nối của địa chỉ cũ, cân nhắc giao thức vẫn giữ được kết nối khi địa chỉ đổi (như connection migration của QUIC). Client: phát hiện chuyển mạng thì kết nối lại ngay bằng session token thay vì chờ heartbeat timeout. - Việc cần làm (Đội hạ tầng): Nếu dùng connection migration của QUIC, cấu hình bộ cân bằng tải chọn server theo connection ID thay vì địa chỉ và cổng (chọn theo địa chỉ và cổng thì gói tin có địa chỉ mới sẽ đi tới server khác). - Trên đồ thị: Rớt kết nối hàng loạt (Số lần mất kết nối và kết nối lại, IP bị đổi khi kết nối lại) - Chỗ cần xem: Tìm trong log kết nối của server các lần cùng một session token kết nối lại từ IP khác, rồi đối chiếu với thời điểm callback đổi mạng mặc định của client (registerDefaultNetworkCallback) - Đúng nếu: IP kết nối lại ngay sau khi mất kết nối đổi từ dải IP của Wi-Fi (đường truyền gia đình) sang dải IP của nhà mạng di động hoặc ngược lại, và ngay trước đó có callback chuyển mạng - Loại trừ nếu: IP không đổi mà vẫn mất kết nối → “Handover trạm phát sóng (khi đang di chuyển)” hoặc “Sóng di động yếu hoặc vùng lõm sóng” - Cách kiểm tra: Cần log và chỉ số của server, client game - Nguồn: - [Read network state](https://developer.android.com/training/basics/network-ops/reading-network-state) · Android (Google) · Khi mạng mặc định đổi, kết nối mới đi qua mạng mới còn kết nối trên mạng cũ cuối cùng bị ngắt cưỡng bức; phát hiện việc chuyển mạng bằng registerDefaultNetworkCallback - [RFC 9000: QUIC: A UDP-Based Multiplexed and Secure Transport](https://www.rfc-editor.org/rfc/rfc9000) · IETF · Nhờ connection ID, kết nối vẫn được giữ dù địa chỉ IP và cổng đổi (mục 9); bộ cân bằng tải chỉ phân tải theo địa chỉ và cổng có thể gửi gói tin có địa chỉ mới tới server khác (mục 5.2.3) - [RFC 9293: Transmission Control Protocol (TCP)](https://www.rfc-editor.org/rfc/rfc9293) · IETF · Kết nối TCP được nhận diện bằng cặp socket (địa chỉ và cổng) ở hai đầu #### co-security · Phần mềm bảo mật kiểm tra gói tin · Antivirus / firewall inspection Khi phần mềm diệt virus hay tường lửa kiểm tra mọi gói tin, độ trễ tăng lên; nếu quá tay, chúng còn nhầm game là tấn công và chặn lại. - Vì sao → Dẫn đến → Trên màn hình: Phần mềm bảo mật kiểm tra từng gói tin gửi và nhận → Mỗi gói bị cộng thêm độ trễ, kiểm tra không kịp thì gói bị bỏ → Ping nhảy thất thường hoặc bị chặn kết nối - Triệu chứng: Giật khựng, Không vào được·kẹt loading / Yếu tố: Jitter, Mất gói - Ai gặp: Chỉ mình tôi / Khi nào: Luôn luôn, Ngay sau đăng nhập hoặc bảo trì - Phụ trách chính: Bên ngoài (Bên ngoài) / Phối hợp: Phát triển client (Đội phát triển game) - Việc cần làm (Đội phát triển game): Duy trì danh sách tương thích với phần mềm bảo mật, thêm ngoại lệ cho game vào Windows Firewall khi cài đặt. - Việc cần làm (Bên ngoài): Hướng dẫn người chơi thêm game vào danh sách ngoại lệ của phần mềm bảo mật; nếu phần mềm nhầm game là tấn công thì yêu cầu hãng bảo mật sửa lỗi nhận diện nhầm (false positive). - Con số tham khảo: Khi bình thường, việc kiểm tra gói tin thường mất chưa tới 1 ms. Vấn đề xảy ra khi module kiểm tra bị dồn việc hoặc có bug, hoặc khi nó nhầm lưu lượng của game là tấn công. - Trên đồ thị: Chỉ một phần cao (RTT, kết nối thất bại (theo người chơi)) - Chỗ cần xem: So sánh sau khi tạm tắt phần mềm bảo mật hoặc thêm game vào ngoại lệ. Trên Windows, bật Audit Filtering Platform Connection và Audit Filtering Platform Packet Drop trong audit policy thì Security log ghi 5157 (chặn kết nối) và 5152 (chặn gói tin); xem số gói bị bỏ bằng WFPv4\Packets Discarded/sec trong Performance Monitor - Đúng nếu: có bản ghi chặn kết nối hoặc gói tin tới địa chỉ server game, hoặc tắt phần mềm bảo mật thì hết ping nhảy và lỗi không vào được - Loại trừ nếu: thiết bị khác trong cùng nhà cũng bị y hệt, không liên quan tới phần mềm bảo mật → phía router hoặc đường truyền - Cách kiểm tra: Kiểm tra ở môi trường phía người chơi - Nguồn: - [About Windows Filtering Platform](https://learn.microsoft.com/en-us/windows/win32/fwp/about-windows-filtering-platform) · Microsoft · Kiến trúc cho phép hoặc chặn gói tin bằng hook và filter engine trong network stack của Windows; hãng bảo mật bên thứ ba có thể gắn module lọc riêng (callout) vào - [Windows Firewall Rules](https://learn.microsoft.com/en-us/windows/security/operating-system-security/network-security/windows-firewall/rules) · Microsoft · Mặc định chặn kết nối đi vào nên ứng dụng cần quy tắc ngoại lệ, và thường trình cài đặt của ứng dụng sẽ tạo quy tắc này - [Address false positives/negatives in Microsoft Defender for Endpoint](https://learn.microsoft.com/en-us/defender-endpoint/defender-endpoint-false-positives-negatives) · Microsoft · Quy trình thêm ngoại lệ và gửi mẫu cho Microsoft phân tích khi chương trình bình thường bị nhầm là mối đe dọa (false positive) - [5157(F): The Windows Filtering Platform has blocked a connection.](https://learn.microsoft.com/en-us/previous-versions/windows/it-pro/windows-10/security/threat-protection/auditing/event-5157) · Microsoft · Sự kiện 5157: Windows Filtering Platform đã chặn một kết nối (Audit Filtering Platform Connection) - [Audit Filtering Platform Packet Drop](https://learn.microsoft.com/en-us/previous-versions/windows/it-pro/windows-10/security/threat-protection/auditing/audit-filtering-platform-packet-drop) · Microsoft · Sự kiện 5152: Windows Filtering Platform đã chặn một gói tin - [Network-Related Performance Counters](https://learn.microsoft.com/en-us/windows-server/networking/technologies/network-subsystem/net-sub-performance-counters) · Microsoft · WFPv4, WFPv6: counter Packets Discarded/sec #### co-rcvbuf · Tràn bộ đệm nhận · Socket receive buffer overflow Khi game bận và lấy gói tin ra khỏi socket (giao diện gửi nhận qua mạng do OS cung cấp) chậm, bộ đệm của OS bị tràn. - Vì sao → Dẫn đến → Trên màn hình: Khung hình bị trễ nên game đọc socket muộn → Bộ đệm nhận của OS đầy: với UDP thì gói bị bỏ, với TCP thì cửa sổ nhận bị thu hẹp khiến bên gửi phải dừng → Dịch chuyển tức thời (UDP) hoặc tua nhanh (TCP) - Triệu chứng: Dịch chuyển tức thời, Tua nhanh / Yếu tố: Mất gói, Ngưng trệ - Ai gặp: Chỉ mình tôi / Khi nào: Khi đông người - Phụ trách chính: Phát triển client (Đội phát triển game) - Việc cần làm (Đội phát triển game): Dùng thread riêng để nhận, điều chỉnh kích thước bộ đệm (SO_RCVBUF). - Con số tham khảo: Bộ đệm nhận mặc định từ vài chục đến vài trăm KB tùy OS và cấu hình. Ở nơi đông người, lượng cập nhật có thể lên tới vài trăm KB trong 1 giây. - Trên đồ thị: Tăng theo số người và tải (Số gói UDP bị bỏ ở bộ đệm nhận, frame time) - Chỗ cần xem: Ghi Microsoft Winsock BSP\Dropped Datagrams (số gói UDP bị bỏ vì bộ đệm nhận của socket không đủ) và UDPv4\Datagrams Received Errors trong Performance Monitor của Windows cùng với frame time; phía game đếm các chỗ bị hụt trong số thứ tự gói đã nhận - Đúng nếu: Dropped Datagrams tăng ở nơi đông người hoặc ngay sau một khung hình dài, cùng lúc số thứ tự trong game bị hụt. Cùng thời điểm đó đường truyền không mất gói - Loại trừ nếu: Dropped Datagrams không đổi mà số thứ tự vẫn bị hụt → mất gói trên đường đi - Cách kiểm tra: Kiểm tra ở môi trường phía người chơi - Nguồn: - [socket(7) — Linux manual page](https://man7.org/linux/man-pages/man7/socket.7.html) · Linux man-pages · SO_RCVBUF là kích thước tối đa của bộ đệm nhận socket; giá trị mặc định do rmem_default, giá trị tối đa do rmem_max quyết định (Android cũng dùng kernel Linux) - [SOL_SOCKET Socket Options (Winsock2.h)](https://learn.microsoft.com/en-us/windows/win32/winsock/sol-socket-socket-options) · Microsoft · SO_RCVBUF trên Windows: vùng bộ đệm dành riêng cho việc nhận ở mỗi socket - [RFC 9293: Transmission Control Protocol (TCP)](https://www.rfc-editor.org/rfc/rfc9293) · IETF · Trường window của TCP là số byte bên nhận còn nhận thêm được; nếu bằng 0, bên gửi chỉ gửi zero window probe và chờ - [Low Latency Workloads Management and Operations](https://learn.microsoft.com/en-us/previous-versions/windows/it-pro/windows-server-2012-R2-and-2012/hh997022(v=ws.11)) · Microsoft · Dropped Datagrams và Dropped Datagrams/sec trong bộ counter Microsoft Winsock BSP: số gói UDP bị bỏ vì tới nhanh hơn tốc độ ứng dụng xử lý hoặc vì bộ đệm socket nhận không đủ - [Network-Related Performance Counters](https://learn.microsoft.com/en-us/windows-server/networking/technologies/network-subsystem/net-sub-performance-counters) · Microsoft · Counter UDPv4, UDPv6: Datagrams Received Errors; Microsoft Winsock BSP: Dropped Datagrams #### co-swap · Thiếu bộ nhớ và swap phía client · Paging / swap on client Khi mở vài chục tab trình duyệt cùng lúc với game, OS đẩy một phần bộ nhớ của game xuống ổ đĩa. - Vì sao → Dẫn đến → Trên màn hình: Tổng RAM không đủ → OS chuyển phần bộ nhớ game chưa dùng tới xuống ổ đĩa → Lúc cần dùng lại phần đó, game đứng hình vài chục đến vài trăm ms tùy ổ lưu trữ - Triệu chứng: Đứng hình, Giật khựng / Yếu tố: Ngưng trệ - Ai gặp: Chỉ mình tôi / Khi nào: Khi di chuyển hoặc chuyển bản đồ, Thỉnh thoảng bất chợt - Phụ trách chính: Bên ngoài (Bên ngoài) / Phối hợp: Phát triển client (Đội phát triển game) - Việc cần làm (Đội phát triển game): Giảm mức dùng bộ nhớ, hiện cảnh báo khi bộ nhớ còn trống ít. - Việc cần làm (Bên ngoài): Thông báo cấu hình tối thiểu cho người chơi, hướng dẫn tắt chương trình khác (như tab trình duyệt) khi chơi. - Trên đồ thị: Thỉnh thoảng vọt lên bất chợt (Hard page fault, mức dùng bộ nhớ) - Chỗ cần xem: Ghi Memory\Pages Input/sec (số page đọc từ ổ đĩa để giải quyết hard page fault) trong Performance Monitor và mức dùng bộ nhớ, commit ở tab Performance của Task Manager cùng với frame time - Đúng nếu: đúng lúc dừng, Pages Input/sec vọt lên và bộ nhớ gần đầy. Đóng trình duyệt hay chương trình khác thì hết - Loại trừ nếu: bộ nhớ còn trống và Pages Input/sec yên ắng → “Tải đồng bộ trên main thread và biên dịch shader” hoặc “Ổ lưu trữ chậm khiến streaming asset không theo kịp” - Cách kiểm tra: Kiểm tra ở môi trường phía người chơi - Nguồn: - [Introduction to the page file](https://learn.microsoft.com/en-us/troubleshoot/windows-client/performance/introduction-to-the-page-file) · Microsoft · Page file là một file trên ổ đĩa, dùng để đẩy các page bộ nhớ đã bị sửa nhưng ít dùng ra khỏi RAM - [Working Set](https://learn.microsoft.com/en-us/windows/win32/memory/working-set) · Microsoft · Truy cập page không có trong RAM là page fault; hard fault chỉ giải quyết được bằng cách đọc từ ổ đĩa, như từ page file - [Chapter 12 - Detecting Memory Bottlenecks](https://learn.microsoft.com/en-us/previous-versions/cc749872(v=technet.10)) · Microsoft · Memory\Pages Input/sec: số page đọc từ ổ đĩa để giải quyết page fault (hard page fault) #### co-vram · Thiếu bộ nhớ đồ họa (VRAM) · VRAM over-commit Nếu tùy chọn đồ họa cần nhiều bộ nhớ hơn bộ nhớ của card đồ họa, OS phải đẩy texture ra bộ nhớ của PC rồi lấy lại, gây giật khựng. - Vì sao → Dẫn đến → Trên màn hình: Tùy chọn texture cao, đủ loại trang bị và hiệu ứng ở nơi đông người làm đầy bộ nhớ card đồ họa → OS chuyển texture chưa dùng tới sang bộ nhớ PC, khi cần lại lấy về qua bus PCIe chậm → Mỗi khi có cảnh mới hoặc nhân vật mới xuất hiện thì khựng, texture mờ một lúc - Triệu chứng: Giật khựng, Đứng hình / Yếu tố: Ngưng trệ - Ai gặp: Chỉ mình tôi / Khi nào: Khi đông người, Khi di chuyển hoặc chuyển bản đồ - Phụ trách chính: Phát triển client (Đội phát triển game) / Phối hợp: Bên ngoài (Bên ngoài) - Việc cần làm (Đội phát triển game): Đặt tùy chọn mặc định theo dung lượng bộ nhớ card đồ họa, tự động hạ chất lượng texture khi vượt ngân sách bộ nhớ, đơn giản hóa texture nhân vật ở nơi đông người. - Việc cần làm (Bên ngoài): Hướng dẫn người chơi hạ tùy chọn texture; nếu mở hai client thì hạ tùy chọn thấp hơn nữa. - Con số tham khảo: Bộ nhớ card đồ họa đọc được hàng trăm GB trong 1 giây, còn bus PCIe nối với bộ nhớ PC chỉ khoảng 16–64 GB trong 1 giây tùy thế hệ, chậm hơn trên mười lần. - Trên đồ thị: Chạm giới hạn rồi đi ngang (Bộ nhớ GPU chuyên dụng, bộ nhớ GPU dùng chung) - Chỗ cần xem: Đồ thị bộ nhớ GPU chuyên dụng và bộ nhớ GPU dùng chung ở mục GPU trong tab Performance của Task Manager (có thể thêm cột theo tiến trình ở tab Details), xem cùng frame time của PresentMon - Đúng nếu: trong lúc bộ nhớ GPU chuyên dụng chạm trần và đi ngang còn bộ nhớ GPU dùng chung tăng lên, game khựng liên tục; hạ tùy chọn texture thì hết - Loại trừ nếu: bộ nhớ chuyên dụng còn trống → “Ổ lưu trữ chậm khiến streaming asset không theo kịp” hoặc “Tải đồng bộ trên main thread và biên dịch shader” - Cách kiểm tra: Kiểm tra ở môi trường phía người chơi - Tìm hiểu thêm: Trong mục GPU của Task Manager trên Windows, nếu “Bộ nhớ GPU chuyên dụng” (Dedicated GPU memory) đầy và “Bộ nhớ GPU dùng chung” (Shared GPU memory) tăng lên thì máy đang ở tình trạng này. Mở hai client trên cùng một PC thì đầy nhanh hơn (xem mục “Streaming thất bại do thiếu bộ nhớ hoặc VRAM”). - Nguồn: - [Residency](https://learn.microsoft.com/en-us/windows/win32/direct3d12/residency) · Microsoft · Mỗi tiến trình có ngân sách bộ nhớ đồ họa được dùng; vượt quá thì kernel chuyển một phần heap của GPU rời sang bộ nhớ PC (đây là biện pháp cuối cùng, nên khuyến nghị tự quản lý ngân sách) - [GPUs in the task manager](https://devblogs.microsoft.com/directx/gpus-in-the-task-manager/) · Microsoft · Trong Task Manager, bộ nhớ GPU chuyên dụng là VRAM của card đồ họa, bộ nhớ GPU dùng chung là bộ nhớ PC mà GPU và CPU cùng dùng - [CUDA C++ Best Practices Guide](https://docs.nvidia.com/cuda/cuda-c-best-practices-guide/index.html) · NVIDIA · Băng thông bộ nhớ đồ họa (V100: 898 GB/s) lớn hơn nhiều so với PCIe x16 thế hệ 3 (16 GB/s), nên khuyến nghị giảm truyền dữ liệu qua lại với bộ nhớ PC - [PresentMon Capture Application (README-CaptureApplication.md)](https://raw.githubusercontent.com/GameTechDev/PresentMon/main/README-CaptureApplication.md) · Intel · Ghi lại thời gian từng khung hình bằng FrameTime (thời gian CPU giữa hai khung hình) #### co-wifi-scan · Wi-Fi quét nền · Periodic Wi-Fi background scan OS định kỳ chuyển qua các kênh để dò Wi-Fi xung quanh, và trong lúc đó việc truyền dữ liệu tạm dừng. - Vì sao → Dẫn đến → Trên màn hình: OS hoặc driver dò tìm Wi-Fi xung quanh theo chu kỳ → Trong lúc dò, việc gửi nhận tạm dừng → Ping vọt lên theo khoảng cách đều chính xác (ví dụ cứ 60 giây một lần) - Triệu chứng: Giật khựng, Dịch chuyển tức thời / Yếu tố: Jitter - Ai gặp: Chỉ mình tôi / Khi nào: Theo chu kỳ đều - Phụ trách chính: Bên ngoài (Bên ngoài) / Phối hợp: Phát triển client (Đội phát triển game) - Việc cần làm (Đội phát triển game): Khi đang chơi, yêu cầu chế độ giảm việc dò sóng (Android: chế độ Wi-Fi độ trễ thấp WIFI_MODE_FULL_LOW_LATENCY; Windows: chế độ media streaming của WlanSetInterface. Tùy thiết bị và driver có thể không có tác dụng). - Việc cần làm (Bên ngoài): Hướng dẫn người chơi dùng mạng dây, chỉnh thiết lập dịch vụ vị trí và tự động dò Wi-Fi, cập nhật driver không dây. - Con số tham khảo: Thường mỗi lần vài chục đến vài trăm ms. Nếu quá đều đặn, hãy nghi nguyên nhân này trước. - Trên đồ thị: Vọt lên theo chu kỳ (RTT tới router) - Chỗ cần xem: Trong lúc chơi, chạy ping /t tới địa chỉ router (Default Gateway trong ipconfig) vài phút và đo khoảng cách giữa các lần vọt lên. Đổi sang mạng dây rồi đo lại như vậy - Đúng nếu: ping tới router vọt lên vài chục đến vài trăm ms theo khoảng cách đều chính xác (ví dụ 60 giây), dùng mạng dây thì hết - Loại trừ nếu: khoảng cách giữa các lần vọt lên không đều → “Nhiễu và sóng Wi-Fi yếu”. Tới router vẫn bình thường, chỉ đoạn phía sau vọt lên → đoạn đường truyền hoặc nhà mạng - Cách kiểm tra: Kiểm tra ở môi trường phía người chơi - Nguồn: - [WDI low latency connection quality](https://learn.microsoft.com/en-us/windows-hardware/drivers/network/wdi-low-latency-connection-quality) · Microsoft · Việc dò sóng và roaming đưa chip không dây ra khỏi kênh đang kết nối, nên chế độ độ trễ thấp giới hạn việc dò và thời gian rời kênh - [WlanSetInterface function (wlanapi.h)](https://learn.microsoft.com/en-us/windows/win32/api/wlanapi/nf-wlanapi-wlansetinterface) · Microsoft · API bật tắt dò nền (wlan_intf_opcode_background_scan_enabled) và chế độ media streaming trên Windows - [Wi-Fi low-latency mode](https://source.android.com/docs/core/connect/wifi-low-latency) · Android (Google) · Chế độ độ trễ thấp tắt tiết kiệm điện Wi-Fi; việc tối ưu thiết lập dò sóng và roaming tùy vào cách cài đặt của nhà sản xuất thiết bị - [ping](https://learn.microsoft.com/en-us/windows-server/administration/windows-commands/ping) · Microsoft · /t: gửi echo request liên tục cho tới khi dừng - [ipconfig](https://learn.microsoft.com/en-us/windows-server/administration/windows-commands/ipconfig) · Microsoft · Chạy không có tham số thì hiển thị địa chỉ IPv4, IPv6 và default gateway của từng adapter #### co-driver · Tiết kiệm điện NIC và lỗi driver · NIC power saving, driver bugs Nếu card mạng LAN hoặc chip Wi-Fi vào trạng thái tiết kiệm điện giữa các gói tin, việc kích hoạt lại sẽ tốn thời gian. - Vì sao → Dẫn đến → Trên màn hình: Tính năng tiết kiệm điện của thiết bị mạng đang bật hoặc driver đã cũ → Trễ khi đánh thức (wake-up), thỉnh thoảng thiết bị tự khởi động lại → Độ trễ thất thường, đôi khi đứng hình vài giây - Triệu chứng: Giật khựng, Đứng hình / Yếu tố: Jitter, Mất gói - Ai gặp: Chỉ mình tôi / Khi nào: Sau khi để yên một lúc, Thỉnh thoảng bất chợt - Phụ trách chính: Bên ngoài (Bên ngoài) / Phối hợp: Phát triển client (Đội phát triển game) - Việc cần làm (Đội phát triển game): Client Android yêu cầu chế độ Wi-Fi độ trễ thấp (WIFI_MODE_FULL_LOW_LATENCY) khi đang chơi để tắt tiết kiệm điện Wi-Fi. - Việc cần làm (Bên ngoài): Hướng dẫn người chơi cập nhật driver mạng, tắt tiết kiệm điện của thiết bị mạng trong Device Manager; nếu cả màn hình khựng và tiếng bị rè thì dùng LatencyMon tìm driver gây ra. - Trên đồ thị: Thỉnh thoảng vọt lên bất chợt (Thời gian DPC và ISR, RTT tới router) - Chỗ cần xem: Ghi bằng Windows Performance Recorder (WPR), tìm driver chạy lâu (cột Module) trong đồ thị DPC/ISR của Windows Performance Analyzer (WPA), và kiểm tra thiết lập Power Management (tiết kiệm điện) của network adapter trong Device Manager - Đúng nếu: đúng lúc khựng, DPC và ISR của driver mạng kéo dài vài ms, hoặc tắt tiết kiệm điện thì hết độ trễ thất thường - Loại trừ nếu: DPC ngắn và tắt tiết kiệm điện vẫn vậy → “Nhiễu và sóng Wi-Fi yếu” hoặc “Wi-Fi quét nền” - Cách kiểm tra: Kiểm tra ở môi trường phía người chơi - Tìm hiểu thêm: Khi driver chiếm giữ CPU lâu để xử lý ngắt (trên Windows gọi là độ trễ DPC), trong lúc đó game thread cũng không dùng được core. Khi đó dù mức dùng CPU thấp, cả màn hình vẫn khựng và tiếng cũng bị rè. Có thể dùng công cụ như LatencyMon để tìm driver thủ phạm; driver Wi-Fi và LAN là nguyên nhân hay gặp. - Nguồn: - [Introduction to NDIS Selective Suspend](https://learn.microsoft.com/en-us/windows-hardware/drivers/network/ndis-selective-suspend) · Microsoft · Windows có thể đưa network adapter đang rảnh vào trạng thái tiêu thụ điện thấp (selective suspend) - [Guidelines for Writing DPC Routines](https://learn.microsoft.com/en-us/windows-hardware/drivers/kernel/guidelines-for-writing-dpc-routines) · Microsoft · Trong lúc DPC chạy, mọi thread trên core đó đều dừng, nên khuyến nghị mỗi lần không quá 100 µs - [Wi-Fi low-latency mode](https://source.android.com/docs/core/connect/wifi-low-latency) · Android (Google) · Ở chế độ Wi-Fi độ trễ thấp của Android, framework chủ động tắt tiết kiệm điện Wi-Fi - [CPU Analysis](https://learn.microsoft.com/en-us/windows-hardware/test/wpt/cpu-analysis) · Microsoft · Đồ thị DPC/ISR của WPA: thời gian của từng đoạn DPC, ISR chạy liền mạch và module (Module) chứa hàm đó #### co-other-apps · Ứng dụng khác trên cùng thiết bị chiếm giữ băng thông · Other apps saturating the link Khi đồng bộ cloud, tải file dung lượng lớn hay tải bản cập nhật game chạy trên cùng PC, gói tin của game phải chờ trong hàng đợi. - Vì sao → Dẫn đến → Trên màn hình: Ứng dụng khác dùng tối đa chiều tải lên hoặc tải xuống → Gói tin game dồn vào hàng đợi của PC và router → Ping tăng vọt, trễ thao tác, tua nhanh - Triệu chứng: Trễ thao tác, Tua nhanh / Yếu tố: Độ trễ, Jitter - Ai gặp: Chỉ mình tôi, Cùng một nhà / Khi nào: Thỉnh thoảng bất chợt - Phụ trách chính: Bên ngoài (Bên ngoài) / Phối hợp: Phát triển client (Đội phát triển game) - Việc cần làm (Đội phát triển game): Launcher và patcher của chúng ta dừng hoặc giới hạn tốc độ tải nền khi đang chơi. - Việc cần làm (Bên ngoài): Hướng dẫn người chơi giới hạn tốc độ tải, tắt tự động cập nhật khi đang chơi. - Trên đồ thị: Tăng theo số người và tải (RTT, lưu lượng gửi nhận của PC) - Chỗ cần xem: Ghi Network Interface\Bytes Sent/sec, Bytes Received/sec trong Performance Monitor cùng với ping. Cách làm giống bài test bufferbloat: để ping chạy rồi cố ý truyền một lượng dữ liệu lớn - Đúng nếu: trong lúc tải xuống hoặc tải lên gần chạm tốc độ đường truyền, ping tăng vài chục đến vài trăm ms, dừng truyền thì trở lại ngay - Loại trừ nếu: lưu lượng gửi nhận của PC thấp mà ping vẫn tăng → “Bufferbloat (hàng đợi của router)” do thiết bị khác trong nhà, hoặc đoạn nhà mạng - Cách kiểm tra: Kiểm tra ở môi trường phía người chơi - Nguồn: - [Introduction](https://www.bufferbloat.net/projects/bloat/wiki/Introduction/) · Bufferbloat.net · Khi thiết bị mạng như router giữ quá nhiều dữ liệu trong hàng đợi, độ trễ vọt lên rất cao (bufferbloat) - [Delivery Optimization reference](https://learn.microsoft.com/en-us/windows/deployment/do/waas-delivery-optimization-reference) · Microsoft · Tải Windows Update (Delivery Optimization) mặc định tự điều chỉnh theo băng thông khả dụng, và có thể đặt giới hạn băng thông cho việc tải ở nền và ở foreground - [Network-Related Performance Counters](https://learn.microsoft.com/en-us/windows-server/networking/technologies/network-subsystem/net-sub-performance-counters) · Microsoft · Network Interface: counter Bytes Received/sec, Bytes Sent/sec - [Tests for Bufferbloat](https://www.bufferbloat.net/projects/bloat/wiki/Tests_for_Bufferbloat/) · Bufferbloat.net · Để ping chạy rồi đo tốc độ cho đầy đường truyền; nếu ping tăng thì là bufferbloat #### co-unfocused · Giới hạn xử lý khi cửa sổ bị thu nhỏ hoặc mất focus · Minimized / unfocused window throttling Khi bạn chuyển sang cửa sổ khác hoặc thu nhỏ game, cả game lẫn Windows đều cho game chạy chậm lại để tiết kiệm điện. Lúc quay lại, gói tin tồn đọng dồn tới một lượt, hoặc kết nối đã bị ngắt. - Vì sao → Dẫn đến → Trên màn hình: Nhấn Alt+Tab sang cửa sổ khác hoặc thu nhỏ game → Trong lúc không hiển thị, game hạ FPS xuống rất thấp hoặc dừng hẳn, Windows cũng hạ mức ưu tiên của chương trình không hiển thị → Vừa quay lại thì tua nhanh, nếu thu nhỏ lâu thì mất kết nối - Triệu chứng: Tua nhanh, Giật khựng, Mất kết nối / Yếu tố: Ngưng trệ - Ai gặp: Chỉ mình tôi / Khi nào: Khi làm một thao tác nhất định, Sau khi để yên một lúc - Phụ trách chính: Phát triển client (Đội phát triển game) - Việc cần làm (Đội phát triển game): Dù cửa sổ không hiển thị, vẫn nhận gói tin và gửi heartbeat trên thread riêng, kiểm tra thiết lập “chạy nền” (Run In Background) của engine, khi quay lại thì đồng bộ về trạng thái mới nhất một lần. - Con số tham khảo: Khi cửa sổ không hiển thị mà FPS bị hạ còn 5–10, mỗi khung hình dài 100–200 ms. Game xử lý gói tin theo từng khung hình sẽ đọc gói tin trễ đúng chừng ấy. - Trên đồ thị: Đứt quãng rồi dồn về (Khoảng cách khung hình (trước và sau khi chuyển cửa sổ), số gói đã xử lý) - Chỗ cần xem: Để PresentMon chạy rồi thử Alt+Tab hoặc thu nhỏ, xem khoảng cách khung hình khi cửa sổ không hiển thị. Ghi thời điểm đổi focus cửa sổ vào log game để đối chiếu với lý do mất kết nối - Đúng nếu: trong lúc cửa sổ không hiển thị, khoảng cách khung hình tăng lên trên 100 ms hoặc không còn được ghi, lúc quay lại thì xử lý dồn một lượt các gói tồn đọng và tua nhanh. Thu nhỏ lâu thì mất kết nối do heartbeat timeout - Loại trừ nếu: cửa sổ vẫn mở phía trước mà vẫn bị như vậy → “Tiến trình chạy nền chiếm giữ CPU” hoặc phía mạng - Cách kiểm tra: Kiểm tra ở môi trường phía người chơi - Tìm hiểu thêm: Windows 11 không bảo đảm timer 1 ms cho chương trình có cửa sổ bị thu nhỏ hoặc bị che hoàn toàn và không phát âm thanh. Trên laptop chạy pin, Windows hạ các chương trình đó xuống tốc độ tiết kiệm điện nhất, và với CPU có nhiều loại core thì có thể cho chạy trên core hiệu suất (efficiency core) chậm hơn. Nếu trong hai client trên cùng PC chỉ client chạy nền bị bất thường, hãy xem thêm mục “Giới hạn xử lý ở cửa sổ chạy nền”. - Nguồn: - [Quality of Service](https://learn.microsoft.com/en-us/windows/win32/procthread/quality-of-service) · Microsoft · Chương trình có cửa sổ không nhìn thấy và không phát âm thanh được xếp vào Low QoS; khi chạy pin thì được lập lịch với tốc độ CPU tiết kiệm nhất và trên efficiency core - [timeBeginPeriod function (timeapi.h)](https://learn.microsoft.com/en-us/windows/win32/api/timeapi/nf-timeapi-timebeginperiod) · Microsoft · Windows 11 không bảo đảm độ phân giải timer cao hơn mặc định cho tiến trình có cửa sổ bị che hoặc thu nhỏ - [Application.runInBackground](https://docs.unity3d.com/ScriptReference/Application-runInBackground.html) · Unity · Giá trị mặc định của Unity là false nên khi cửa sổ xuống nền, vòng lặp game dừng lại - [PresentMon Capture Application (README-CaptureApplication.md)](https://raw.githubusercontent.com/GameTechDev/PresentMon/main/README-CaptureApplication.md) · Intel · MsBetweenPresents: thời gian giữa lần gọi Present() này và lần gọi trước (ms) #### co-overlay · Phần mềm overlay can thiệp vào game · Overlays and screen hooks Phần mềm nhắn tin, launcher, quay màn hình, hiển thị FPS chen vào quá trình render của game (hooking) để vẽ UI của chúng đè lên màn hình game. Mỗi khung hình phải làm thêm việc, và đôi khi xung đột với game gây khựng hoặc làm game bị tắt đột ngột. - Vì sao → Dẫn đến → Trên màn hình: Overlay của phần mềm nhắn tin, launcher game, công cụ card đồ họa hoặc phần mềm quay màn hình đang bật → Mỗi lần khung hình được đẩy ra màn hình, overlay chen vào để vẽ đè UI của nó → Khung hình trễ đi một chút, lúc có thông báo hiện lên thì khựng, hoặc lỗi đồ họa, game bị tắt đột ngột (người chơi thấy giống mất kết nối) - Triệu chứng: Giật khựng, Đứng hình, Mất kết nối / Yếu tố: Ngưng trệ - Ai gặp: Chỉ mình tôi / Khi nào: Luôn luôn, Thỉnh thoảng bất chợt - Phụ trách chính: Bên ngoài (Bên ngoài) / Phối hợp: Phát triển client (Đội phát triển game) - Việc cần làm (Đội phát triển game): Thu thập kèm danh sách overlay đang chạy trong crash report và log giật. - Việc cần làm (Bên ngoài): Khi có báo cáo, hướng dẫn người chơi tắt hết overlay rồi thử lại. - Trên đồ thị: Chỉ một phần cao (Frame time, số lần crash (người chơi bật overlay)) - Chỗ cần xem: Tắt hết overlay rồi so sánh frame time của PresentMon ở cùng một cảnh; nếu có crash thì xem tên module lỗi (Faulting module name) trong Event ID 1000 của Event Viewer - Đúng nếu: tắt overlay thì hết khựng và lỗi đồ họa, hoặc module lỗi trong crash là DLL của phần mềm overlay - Loại trừ nếu: tắt hết overlay vẫn vậy → driver đồ họa hoặc “Client bị crash” - Cách kiểm tra: Kiểm tra ở môi trường phía người chơi - Tìm hiểu thêm: Khi chỉ một số người bị giật khựng hoặc văng game mà cấu hình máy không giải thích được, hãy nghi trước tiên xung đột giữa overlay và module bảo mật game. - Nguồn: - [Steam Overlay (Steamworks Documentation)](https://partner.steamgames.com/doc/features/overlay) · Valve · Steam Overlay tự động hook vào game chạy qua Steam; do cách làm này, lỗi bộ nhớ trong cách game dùng API render có thể lộ ra và gây crash - [PresentMon Capture Application (README-CaptureApplication.md)](https://raw.githubusercontent.com/GameTechDev/PresentMon/main/README-CaptureApplication.md) · Intel · Ghi lại thời gian từng khung hình bằng FrameTime (thời gian CPU giữa hai khung hình) - [The application or service crashing behavior troubleshooting guidance](https://learn.microsoft.com/en-us/troubleshoot/windows-server/performance/troubleshoot-application-service-crashing-behavior) · Microsoft · Event ID 1000 trong log Application chứa tên module lỗi (Faulting module name); đôi khi module của Windows bị ghi là module lỗi vì một module khác làm hỏng bộ nhớ #### co-display-input · Độ trễ của màn hình, thiết bị nhập và tạo khung hình · Display, input device and frame generation latency Nếu ping bình thường mà điều khiển vẫn thấy nặng, có thể phần xử lý hình ảnh của TV, tay cầm không dây hoặc tính năng tạo khung hình đang cộng thêm độ trễ giữa thao tác và màn hình. - Vì sao → Dẫn đến → Trên màn hình: Chế độ game của TV đang tắt, dùng tay cầm Bluetooth hoặc không dây, hoặc bật tạo khung hình (frame generation của DLSS, FSR) → TV xuất khung hình muộn vì bận xử lý chất lượng hình ảnh, input không dây tới muộn do chu kỳ truyền và nhiễu, còn tạo khung hình phải chờ khung hình kế tiếp rồi mới tạo khung hình chen giữa → Ping và FPS đều đẹp nhưng bấm xong phải một lúc mới thấy trên màn hình, tức là trễ thao tác - Triệu chứng: Trễ thao tác / Yếu tố: Độ trễ - Ai gặp: Chỉ mình tôi / Khi nào: Luôn luôn - Phụ trách chính: Bên ngoài (Bên ngoài) / Phối hợp: Phát triển client (Đội phát triển game) - Việc cần làm (Đội phát triển game): Để tạo khung hình là tùy chọn và ghi chú rằng bật lên có thể làm tăng trễ thao tác, khi dùng tạo khung hình thì tích hợp kèm tính năng độ trễ thấp của hãng đồ họa (NVIDIA Reflex, AMD Anti-Lag 2), hiển thị trong game độ trễ từ thao tác đến màn hình phía PC, bản build cho Android TV và set-top box gọi Window.setPreferMinimalPostProcessing(true) để yêu cầu TV bật chế độ độ trễ thấp (ALLM). - Việc cần làm (Bên ngoài): Hướng dẫn người chơi bật chế độ game (ALLM) của TV hoặc màn hình, dùng tay cầm có dây và tắt tạo khung hình khi chơi nội dung đối kháng, để thiết bị Bluetooth ở gần và dùng Wi-Fi 5 GHz. - Con số tham khảo: Riêng việc gửi một khung hình đã mất 16,7 ms ở màn hình 60 Hz và 8,3 ms ở 120 Hz. Tay cầm Xbox đời cũ đọc và gửi input 8 ms một lần. Độ trễ do xử lý hình ảnh của TV khác nhau tùy thiết bị nên khó nói bằng một con số; chế độ game là thiết lập giảm phần xử lý này. Với tạo khung hình, AMD khuyến nghị chỉ dùng khi tốc độ khung hình trước khi tạo từ 60 FPS trở lên. - Trên đồ thị: Luôn cao ngay từ đầu (Độ trễ từ thao tác đến màn hình) - Chỗ cần xem: So sánh MsAllInputToPhotonLatency của PresentMon (từ input bàn phím, chuột đến lúc đẩy ra màn hình) khi bật và tắt tạo khung hình, xem FrameType (chỉ được ghi khi driver hoặc SDK báo) để biết có khung hình chen giữa được tạo ra hay không. Giá trị này không gồm đoạn không dây của tay cầm và phần xử lý bên trong TV, nên phần đó thì so sánh bằng cách lần lượt đổi sang chế độ game của TV và tay cầm có dây - Đúng nếu: ping bình thường, tắt tạo khung hình thì độ trễ từ thao tác đến màn hình giảm, hoặc đổi sang chế độ game của TV hay tay cầm có dây thì cảm giác trễ biến mất - Loại trừ nếu: đổi hết các thiết lập này vẫn vậy và ping cao hoặc nhảy → phía mạng. Độ trễ phía PC cao do V-Sync hoặc hàng đợi khung hình → “V-Sync và hàng đợi render” - Cách kiểm tra: Kiểm tra ở môi trường phía người chơi - Tìm hiểu thêm: Độ trễ mạng thể hiện qua ping, còn độ trễ này thì ping không đo được. Vì vậy đây là chỗ cần kiểm tra đầu tiên khi có báo cáo “ping thấp mà vẫn lag”. Tạo khung hình làm con số FPS trên màn hình tăng khoảng gấp đôi, nhưng để tạo khung hình chen giữa thì phải chờ khung hình thật kế tiếp, nên thời gian từ thao tác đến lúc hiện lên màn hình tăng lên (AMD cho biết độ trễ tăng là do thiết kế). Thiết bị Bluetooth dùng cùng dải 2,4 GHz với Wi-Fi, nên khi bị nhiễu, input có thể bị ngắt quãng hoặc giật. Về V-Sync và hàng đợi render làm tăng độ trễ bên trong PC, hãy xem mục “V-Sync và hàng đợi render”. - Nguồn: - [Auto Low Latency Mode (ALLM)](https://www.hdmi.org/spec21sub/autolowlatencymode) · HDMI Licensing Administrator · ALLM cho phép thiết bị tự động chuyển màn hình sang chế độ độ trễ thấp (thường gọi là chế độ game); ở chế độ này, TV tắt một số xử lý hình ảnh để giảm độ trễ - [Xbox Series X: What’s the Deal with Latency?](https://news.xbox.com/en-us/2020/03/16/xbox-series-x-latency/) · Microsoft · Trễ thao tác là tổng độ trễ trên đường tay cầm→console→HDMI→TV; tay cầm đời cũ đọc và gửi input 8 ms một lần; thời gian gửi một khung hình qua HDMI là 16,6 ms ở 60 Hz và 8,3 ms ở 120 Hz; ALLM tự động chuyển TV sang chế độ game - [AMD FSR 3 game integrations out now + more details for developers](https://gpuopen.com/news/fsr3-in-games-technical-details/) · AMD · Nội suy khung hình làm tăng độ trễ do thiết kế; khuyến nghị dùng khi tốc độ khung hình trước nội suy từ 60 trở lên; đầu vào 60 FPS cho đầu ra tối đa 120 FPS - [AMD FSR Frame Generation](https://gpuopen.com/amd-fsr-framegeneration/) · AMD · Tạo khung hình được khuyến nghị khi trước nội suy đạt từ 60 FPS (nên tránh dưới 30 FPS); AMD Radeon Anti-Lag 2 căn chỉnh công việc của CPU và GPU để giảm độ trễ hệ thống - [NVIDIA DLSS](https://developer.nvidia.com/rtx/dlss) · NVIDIA · Tạo khung hình của DLSS được thiết kế để giữ độ phản hồi khi dùng cùng NVIDIA Reflex (tính năng độ trễ thấp) - [Resolve Wi-Fi and Bluetooth issues caused by wireless interference](https://support.apple.com/en-us/102319) · Apple · Nhiễu sóng không dây gây ngắt quãng và giảm hiệu năng ở thiết bị Wi-Fi, Bluetooth; Bluetooth và Wi-Fi dùng chung dải 2,4 GHz - [PresentMon Capture Application (README-CaptureApplication.md)](https://raw.githubusercontent.com/GameTechDev/PresentMon/main/README-CaptureApplication.md) · Intel · MsAllInputToPhotonLatency (độ trễ từ input đến màn hình), DisplayLatency (từ lúc nộp khung hình đến lúc đẩy ra màn hình), FrameType (phân biệt khung hình do ứng dụng vẽ và khung hình do driver, SDK nội suy) - [PresentMon Console Application (README-ConsoleApplication.md)](https://raw.githubusercontent.com/GameTechDev/PresentMon/main/README-ConsoleApplication.md) · Intel · MsAllInputToPhotonLatency tính theo input bàn phím, chuột; FrameType chỉ được ghi khi ứng dụng hoặc driver phát sự kiện Intel-PresentMon (--track_frame_type) - [Window.setPreferMinimalPostProcessing](https://developer.android.com/reference/android/view/Window#setPreferMinimalPostProcessing(boolean)) · Android (Google) · Cửa sổ cần độ trễ thấp như game sẽ yêu cầu màn hình xử lý hình ảnh ở mức tối thiểu; nếu kết nối HDMI thì gửi tín hiệu ALLM và Game Content Type để chuyển TV sang chế độ độ trễ thấp ### L3 Mạng gia đình (10 nguyên nhân) #### hn-wifi · Nhiễu và sóng Wi-Fi yếu · Wi-Fi interference, weak signal Khi sóng yếu hoặc bị nhiễu, đoạn không dây phải gửi lại nhiều lần nên thời điểm gói tin đến lúc nhanh lúc chậm. - Vì sao → Dẫn đến → Trên màn hình: Chất lượng sóng giảm do tường, khoảng cách, lò vi sóng, Bluetooth, router nhà hàng xóm → Truyền thất bại ở đoạn không dây → truyền lại nhiều lần → Gói tin đến lúc nhanh lúc chậm (jitter) nên nhân vật khựng khựng, nặng thì mất gói và dịch chuyển tức thời - Triệu chứng: Giật khựng, Dịch chuyển tức thời, Kéo ngược / Yếu tố: Jitter, Mất gói - Ai gặp: Chỉ mình tôi, Cùng một nhà / Khi nào: Thỉnh thoảng bất chợt, Luôn luôn - Phụ trách chính: Bên ngoài (Bên ngoài) / Phối hợp: Phát triển client (Đội phát triển game) - Việc cần làm (Đội phát triển game): Tự động điều chỉnh độ dài bộ đệm nội suy theo tình trạng đường truyền, hiển thị trạng thái mạng trên màn hình khi jitter hoặc mất gói lớn. - Việc cần làm (Bên ngoài): Hướng dẫn người chơi dùng mạng dây, dùng băng tần 5 GHz hoặc 6 GHz, đổi vị trí đặt router. - Con số tham khảo: Mỗi lần truyền lại tốn thêm khoảng 1–4 ms. Khi sóng yếu, thiết bị phải gửi lại nhiều lần ở tốc độ thấp và còn chờ kênh trống, nên có lúc vọt lên 50–200 ms. Cái bẫy là ping trung bình trông vẫn bình thường. - Trên đồ thị: Thỉnh thoảng vọt lên bất chợt (RTT tới router) - Chỗ cần xem: Chạy ping /t tới địa chỉ router (Default Gateway trong ipconfig) vài phút, xem cường độ sóng và kênh của router nhà mình bằng netsh wlan show networks mode=bssid. Đổi sang mạng dây ở cùng vị trí để so sánh - Đúng nếu: ngay từ đoạn tới router, ping đã vọt lên thất thường vài chục đến vài trăm ms, thỉnh thoảng mất gói, cường độ sóng thấp. Dùng mạng dây hoặc ngồi gần router thì hết - Loại trừ nếu: tới router thì ổn định, chỉ đoạn phía sau vọt lên → đoạn đường truyền hoặc nhà mạng. Chỉ vọt lên theo khoảng cách đều → “Wi-Fi quét nền” - Cách kiểm tra: Kiểm tra ở môi trường phía người chơi - Tìm hiểu thêm: Trong hệ thống Wi-Fi mesh, nếu các router (node) nối với nhau bằng sóng không dây (wireless backhaul), node chuyển tiếp không thể gửi trong lúc đang nhận và phải chia cơ hội truyền với các chặng trước và sau dùng cùng kênh, nên lúc đông thiết bị thông lượng có thể giảm và độ trễ tăng thêm. Sản phẩm có băng tần không dây riêng cho backhaul có thể đỡ hơn, còn nối các node bằng dây (Ethernet) thì đoạn này không còn đi qua sóng không dây. Bộ chuyển đổi mạng qua đường dây điện (PLC) cũng dùng cơ chế kiểm tra môi trường truyền có trống không rồi mới gửi (CSMA/CA) như Wi-Fi, và chất lượng thay đổi liên tục theo nhiễu do thiết bị điện gia dụng phát ra và việc bật tắt các thiết bị đó, nên có thể gây truyền lại và jitter. - Sự cố thực tế: ffxiv-2021 - Nguồn: - [Resolve Wi-Fi and Bluetooth issues caused by wireless interference](https://support.apple.com/en-us/102319) · Apple · Nguồn nhiễu như lò vi sóng, điện thoại không dây; Wi-Fi và Bluetooth dùng chung dải 2,4 GHz; khuyến nghị chuyển sang 5 GHz và chọn kênh ít nhiễu - [RFC 8325: Mapping Diffserv to IEEE 802.11](https://www.rfc-editor.org/rfc/rfc8325) · IETF · CSMA/CA của 802.11: chỉ gửi khi kênh trống; kênh bận thì hoãn tới khi trống rồi chờ thêm một khoảng backoff ngẫu nhiên - [Ending the Anomaly: Achieving Low Latency and Airtime Fairness in WiFi (USENIX ATC 2017)](https://www.usenix.org/system/files/conference/atc17/atc17-hoiland-jorgensen.pdf) · USENIX · Hàng đợi mặc định của Wi-Fi khi có tải gây độ trễ hàng trăm ms; thiết bị kết nối ở tốc độ thấp (sóng yếu) dù dùng FQ-CoDel vẫn có trung vị trên 200 ms - [ping](https://learn.microsoft.com/en-us/windows-server/administration/windows-commands/ping) · Microsoft · /t: gửi echo request liên tục cho tới khi dừng - [ipconfig](https://learn.microsoft.com/en-us/windows-server/administration/windows-commands/ipconfig) · Microsoft · Chạy không có tham số thì hiển thị địa chỉ IPv4, IPv6 và default gateway của từng adapter - [Wireless network connectivity issues troubleshooting](https://learn.microsoft.com/en-us/troubleshoot/windows-client/networking/wireless-network-connectivity-issues-troubleshooting) · Microsoft · netsh wlan show networks mode=bssid: hiển thị BSSID, cường độ sóng, kênh và chuẩn không dây của từng mạng Wi-Fi nhìn thấy được - [Capacity of Ad Hoc Wireless Networks (MobiCom 2001)](https://pdos.csail.mit.edu/papers/grid:mobicom01/paper.pdf) · ACM · Chuyển tiếp nhiều chặng qua sóng 802.11 thì node không gửi được trong lúc đang nhận và các chặng trước sau gây nhiễu cho nhau, nên thông lượng của tuyến chuyển tiếp nối tiếp về lý thuyết giảm còn 1/3 (trong mô phỏng khoảng 1/7) - [Electri-Fi Your Data: Measuring and Combining Power-Line Communications with WiFi (IMC 2015)](https://conferences.sigcomm.org/imc/2015/papers/p325.pdf) · ACM · Thiết bị truyền thông qua đường dây điện thương mại (IEEE 1901, HomePlug AV) gửi bằng CSMA/CA tương tự Wi-Fi, có thể mất công bằng trong thời gian ngắn làm jitter tăng; chất lượng kênh thay đổi theo nhiễu do thiết bị gia dụng phát ra và việc bật tắt các thiết bị đó (theo đơn vị vài phút đến vài giờ) #### hn-channel · Kênh Wi-Fi bị nghẽn · Crowded Wi-Fi channel Ở nơi có hàng chục router như chung cư, các thiết bị phải chia nhau cùng một kênh nên phải chờ tới lượt truyền. - Vì sao → Dẫn đến → Trên màn hình: Hàng chục router dùng cùng một kênh 2,4 GHz → Muốn truyền thì phải chờ thiết bị khác truyền xong và kênh trống → Buổi tối khi mọi người về nhà, jitter (độ dao động của khoảng cách giữa các lần gói tin đến) tăng lên gây giật khựng - Triệu chứng: Giật khựng, Trễ thao tác / Yếu tố: Jitter, Độ trễ - Ai gặp: Cùng một nhà / Khi nào: Giờ cao điểm buổi tối - Phụ trách chính: Bên ngoài (Bên ngoài) / Phối hợp: Phát triển client (Đội phát triển game) - Việc cần làm (Đội phát triển game): Tự động tăng độ dài bộ đệm nội suy khi jitter tăng. - Việc cần làm (Bên ngoài): Hướng dẫn người chơi dùng băng tần 5 GHz hoặc 6 GHz, chọn kênh ít đông hơn, hoặc dùng mạng dây. - Trên đồ thị: Chỉ cao vào một khung giờ (RTT và jitter tới router) - Chỗ cần xem: Xem kênh và cường độ sóng của các mạng Wi-Fi xung quanh bằng netsh wlan show networks mode=bssid, đo ping tới router vào buổi tối và ban ngày để so sánh - Đúng nếu: bắt được nhiều router xung quanh trên cùng kênh 2,4 GHz, jitter tới router chỉ tăng vào buổi tối. Chuyển sang 5 GHz, 6 GHz hoặc kênh ít đông hơn thì giảm - Loại trừ nếu: vọt lên bất kể khung giờ → “Nhiễu và sóng Wi-Fi yếu”. Tới router vẫn ổn, chỉ đoạn phía sau xấu đi vào buổi tối → “Nghẽn peering vào giờ cao điểm” - Cách kiểm tra: Kiểm tra ở môi trường phía người chơi - Nguồn: - [Recommended settings for Wi-Fi routers and access points](https://support.apple.com/en-us/102766) · Apple · Router và thiết bị khác dùng cùng kênh là nguồn nhiễu; với 2,4 GHz khuyến nghị độ rộng kênh 20 MHz; 5 GHz và 6 GHz ít phải lo nhiễu hơn - [RFC 8325: Mapping Diffserv to IEEE 802.11](https://www.rfc-editor.org/rfc/rfc8325) · IETF · 802.11 hoãn truyền khi kênh bận cho tới lúc trống, rồi gửi sau một khoảng backoff ngẫu nhiên (CSMA/CA) - [Wireless network connectivity issues troubleshooting](https://learn.microsoft.com/en-us/troubleshoot/windows-client/networking/wireless-network-connectivity-issues-troubleshooting) · Microsoft · netsh wlan show networks mode=bssid: hiển thị BSSID, cường độ sóng, kênh và chuẩn không dây của từng mạng Wi-Fi nhìn thấy được - [ping](https://learn.microsoft.com/en-us/windows-server/administration/windows-commands/ping) · Microsoft · /t: gửi echo request liên tục cho tới khi dừng #### hn-bufferbloat · Bufferbloat (hàng đợi của router) · Bufferbloat Khi ai đó trong nhà tải video lên hoặc tải file lớn xuống, hàng đợi của router dồn lượng gói tin tương đương hàng trăm ms, và gói tin game cũng phải chờ phía sau. - Vì sao → Dẫn đến → Trên màn hình: Người nhà tải video lên hoặc sao lưu lên cloud, bạn đang livestream, hoặc tải file dung lượng lớn làm đầy đường truyền → Router hoặc modem giữ các gói bị dư trong một hàng đợi lớn → Gói tin game cũng phải chờ cuối hàng đợi nên ping tăng vọt lên hàng trăm ms - Triệu chứng: Trễ thao tác, Tua nhanh, Dịch chuyển tức thời / Yếu tố: Độ trễ, Jitter - Ai gặp: Cùng một nhà, Chỉ mình tôi / Khi nào: Thỉnh thoảng bất chợt, Giờ cao điểm buổi tối - Phụ trách chính: Bên ngoài (Bên ngoài) / Phối hợp: Phát triển client (Đội phát triển game) - Việc cần làm (Đội phát triển game): Khi ping đột ngột tăng lên hàng trăm ms, hiển thị trạng thái mạng trên màn hình (gợi ý khả năng có truyền tải dung lượng lớn trên cùng đường truyền). - Việc cần làm (Bên ngoài): Hướng dẫn người chơi dùng router có SQM (fq_codel, CAKE) hoặc QoS, đặt tốc độ SQM bằng 90–95% tốc độ đường truyền (có như vậy hàng đợi mới hình thành bên trong router và SQM mới có tác dụng), giới hạn tốc độ upload. - Con số tham khảo: Với đường truyền upload 10 Mbps và bộ đệm 1 MB, hàng đợi có thể dài tới 800 ms. - Trên đồ thị: Tăng theo số người và tải (RTT, lưu lượng upload và download của đường truyền) - Chỗ cần xem: Để ping chạy rồi đo tốc độ cho đầy đường truyền, hoặc dùng trang web test đo độ trễ khi có tải (theo hướng dẫn của Bufferbloat.net). Xem cùng lưu lượng upload, download trên trang quản trị router - Đúng nếu: trong lúc upload hoặc download làm đầy đường truyền, ping tăng lên hàng trăm ms và trở lại khi truyền xong (độ trễ khi có tải vượt 50 ms thì đáng nghi). Bật SQM thì hết - Loại trừ nếu: đường truyền đang rảnh mà ping vẫn vọt lên → “Nhiễu và sóng Wi-Fi yếu” hoặc “Chất lượng đường truyền kém” - Cách kiểm tra: Kiểm tra ở môi trường phía người chơi - Tìm hiểu thêm: Chiều tải lên (upload) đặc biệt dễ bị nghẽn, vì đường truyền cáp và di động thường có upload hẹp hơn download rất nhiều. Ở nhà có cáp quang dư dả, đoạn Wi-Fi lại trở thành điểm nghẽn, và chuyện tương tự xảy ra ở hàng đợi không dây của router. Gói tin game nhỏ nên gần như không tốn băng thông, nhưng vẫn phải chờ trong hàng đợi như mọi gói khác. Nếu chỉ chiều upload bị nghẽn thì chỉ thao tác của bạn bị trễ, còn chuyển động của người khác vẫn bình thường. Trên điện thoại, việc sao lưu ảnh hay cập nhật ứng dụng trên chính máy đó làm đầy hàng đợi của modem điện thoại và trạm phát sóng, gây ra hiện tượng tương tự. - Nguồn: - [Setting up SQM for CeroWrt 3.10](https://www.bufferbloat.net/projects/cerowrt/wiki/Setting_up_SQM_for_CeroWrt_310/) · Bufferbloat.net · Phải hạ tốc độ SQM xuống 95% tốc độ đo thực tế (85% nếu tính theo tốc độ quảng cáo) để kéo điểm nghẽn từ thiết bị nhà mạng vào bên trong router thì mới có tác dụng - [SQM (Smart Queue Management)](https://openwrt.org/docs/guide-user/network/traffic-shaping/sqm) · OpenWrt · Nhập tốc độ download, upload bằng 90% giá trị đo thực tế; thuật toán hàng đợi khuyến nghị cake (CPU yếu thì fq_codel) - [Ending the Anomaly: Achieving Low Latency and Airtime Fairness in WiFi (USENIX ATC 2017)](https://www.usenix.org/system/files/conference/atc17/atc17-hoiland-jorgensen.pdf) · USENIX · Khi đoạn Wi-Fi bị đầy, hàng đợi không dây của router gây độ trễ hàng trăm ms; sửa hàng đợi không dây thì độ trễ khi có tải giảm còn khoảng 1/10 - [Tests for Bufferbloat](https://www.bufferbloat.net/projects/bloat/wiki/Tests_for_Bufferbloat/) · Bufferbloat.net · Để ping chạy rồi đo tốc độ cho đầy đường truyền, ping tăng thì là bufferbloat; độ trễ khi có tải vượt 50 ms (hoặc dưới hạng B) thì nên khắc phục #### hn-nat · Ánh xạ NAT hết hạn · NAT mapping timeout Router xóa khỏi bảng NAT các kết nối nhàn rỗi không có gói tin qua lại trong một thời gian. Đây là nguyên nhân phổ biến khiến người chơi đứng yên một lúc, vừa di chuyển là mất kết nối. - Vì sao → Dẫn đến → Trên màn hình: Router ghi kết nối “thiết bị bên trong ↔ server bên ngoài” vào bảng NAT (bảng chuyển đổi địa chỉ) → Không có gói tin trong một thời gian thì bị xóa khỏi bảng (với UDP thường là 30–120 giây) → Gói tin từ server không vào được trong nhà nên mất kết nối - Triệu chứng: Mất kết nối / Yếu tố: Mất gói - Ai gặp: Chỉ mình tôi, Cùng một nhà / Khi nào: Sau khi để yên một lúc - Phụ trách chính: Phát triển client (Đội phát triển game) / Phối hợp: Phát triển server (Đội phát triển game) - Việc cần làm (Đội phát triển game): Client: gửi heartbeat với khoảng cách không quá một nửa idle timeout ngắn nhất (ánh xạ UDP chỉ chắc chắn được làm mới bởi gói đi ra từ trong nhà, nên client phải là bên gửi), mất kết nối thì tự kết nối lại. Server: phản hồi heartbeat, không nhận được trong một khoảng thời gian thì chủ động dọn kết nối trước; khi ánh xạ bị xóa khiến địa chỉ và cổng bên ngoài đổi, dùng session token (mã xác nhận nhận được lúc kết nối) để xác nhận vẫn là người chơi đó và nối tiếp. - Trên đồ thị: Rớt kết nối hàng loạt (Số lần mất kết nối (heartbeat timeout), thời gian nhàn rỗi trước khi mất kết nối) - Chỗ cần xem: Thu thập lý do ngắt kết nối phía server và thời gian đã trôi qua kể từ gói tin cuối cùng trên kết nối đó trước khi ngắt (thời gian nhàn rỗi), rồi xem phân bố. Để thử nghiệm, tăng dần khoảng cách gửi gói UDP lên 30 giây, 60 giây, 120 giây và đo xem ở khoảng nào thì không còn phản hồi - Đúng nếu: chỉ kết nối đang nhàn rỗi mới bị ngắt, và thời gian nhàn rỗi dồn ngay sau một giá trị nhất định trong khoảng 30–120 giây. Rút khoảng cách heartbeat ngắn hơn giá trị đó thì hết - Loại trừ nếu: đang di chuyển cũng mất kết nối → phía đường truyền hoặc tuyến đường. Chỉ ở một nhà mạng di động nhất định mà dồn vào giá trị ngắn → “IP dùng chung của nhà mạng (CGNAT)” - Cách kiểm tra: Cần log và chỉ số của server, client game - Nguồn: - [RFC 4787: Network Address Translation (NAT) Behavioral Requirements for Unicast UDP](https://www.rfc-editor.org/rfc/rfc4787) · IETF · Timer ánh xạ UDP không được hết hạn trước 2 phút, khuyến nghị mặc định từ 5 phút; làm mới bằng gói đi ra từ bên trong là bắt buộc, làm mới bằng gói đi vào từ bên ngoài là tùy chọn - [An Experimental Study of Home Gateway Characteristics (IMC 2010)](https://conferences.sigcomm.org/imc/2010/papers/p260.pdf) · ACM · Đo 34 mẫu router gia đình: ánh xạ UDP được giữ 30–691 giây, trung vị 90 giây, hơn một nửa dưới 2 phút; với TCP trung vị khoảng 60 phút - [RFC 9308: Applicability of the QUIC Transport Protocol](https://www.rfc-editor.org/rfc/rfc9308) · IETF · Trên Internet có NAT dọc đường, keep-alive khoảng 30 giây một lần là hợp lý; dày hơn thì lãng phí lưu lượng và điện năng #### hn-router · Router yếu hoặc quá nhiệt · Router CPU / session table exhaustion Khi một router giá rẻ phải gánh hàng chục thiết bị và hàng nghìn kết nối, bản thân router không xử lý nổi. - Vì sao → Dẫn đến → Trên màn hình: Hàng chục thiết bị, P2P và torrent mở hàng nghìn kết nối → CPU và bảng phiên của router bị bão hòa → Xử lý gói tin bị trễ, mất gói, kết nối mới thất bại - Triệu chứng: Giật khựng, Không vào được·kẹt loading, Mất kết nối / Yếu tố: Mất gói, Jitter - Ai gặp: Cùng một nhà / Khi nào: Càng chạy lâu càng nặng, Thỉnh thoảng bất chợt - Phụ trách chính: Bên ngoài (Bên ngoài) - Việc cần làm (Bên ngoài): Hướng dẫn người chơi khởi động lại router (tạm thời), thay router, tắt bớt chương trình mở nhiều kết nối (P2P, torrent). - Trên đồ thị: Chạm giới hạn rồi đi ngang (CPU và số kết nối của router, RTT tới router) - Chỗ cần xem: Xem mức dùng CPU, số kết nối (phiên), số thiết bị đang kết nối trên trang quản trị router (nếu router hỗ trợ), so sánh ping tới chính router trước và sau khi khởi động lại - Đúng nếu: khi số kết nối nhiều, ngay từ đoạn tới router ping đã vọt lên hoặc mất gói, kết nối mới thất bại. Khởi động lại thì ổn một thời gian rồi lại xấu đi - Loại trừ nếu: tới router vẫn bình thường, chỉ đoạn phía sau xấu → đoạn đường truyền hoặc nhà mạng - Cách kiểm tra: Kiểm tra ở môi trường phía người chơi - Nguồn: - [An Experimental Study of Home Gateway Characteristics (IMC 2010)](https://conferences.sigcomm.org/imc/2010/papers/p260.pdf) · ACM · Số kết nối TCP router gia đình cho phép tới một cổng server là từ 16 đến khoảng 1.024 (trung vị 135); thiết bị giá rẻ đôi khi chỉ đạt thông lượng vài Mbps - [Netfilter Conntrack Sysfs variables](https://docs.kernel.org/networking/nf_conntrack-sysctl.html) · Linux kernel · Số mục tối đa của bảng theo dõi kết nối trên Linux (nf_conntrack_max) và thời gian giữ mặc định theo từng trạng thái #### hn-handover · Handover trạm phát sóng (khi đang di chuyển) · Cellular handover Khi di chuyển bằng xe buýt hay tàu điện ngầm, kết nối bị gián đoạn trong lúc máy đổi trạm phát sóng. - Vì sao → Dẫn đến → Trên màn hình: Di chuyển nên trạm phát sóng đang kết nối thay đổi → Thường chỉ gián đoạn vài chục ms, nhưng nếu sóng kém khiến chuyển trạm thất bại thì kết nối có thể bị ngắt vài trăm ms đến vài giây → Đứng hình rồi dịch chuyển tức thời, lâu thì mất kết nối - Triệu chứng: Đứng hình, Dịch chuyển tức thời, Mất kết nối / Yếu tố: Mất gói - Ai gặp: Chỉ mình tôi / Khi nào: Khi di chuyển hoặc chuyển bản đồ - Phụ trách chính: Bên ngoài (Bên ngoài) / Phối hợp: Phát triển client (Đội phát triển game), Phát triển server (Đội phát triển game) - Việc cần làm (Đội phát triển game): Client: timeout chịu được gián đoạn ngắn, kết nối lại nhanh. Server: timeout đủ dài để không đá người chơi ra ngay khi mất kết nối vài giây, khi kết nối lại thì nối tiếp vào cùng session. - Việc cần làm (Bên ngoài): Giải thích cho người chơi rằng mất kết nối khi đang di chuyển (xe buýt, tàu điện ngầm) là do chuyển trạm phát sóng. - Trên đồ thị: Đứt quãng rồi dồn về (Số gói nhận được, RTT) - Chỗ cần xem: Xác nhận lúc báo mất kết nối người chơi có đang di chuyển (xe buýt, tàu điện ngầm) không, xem thời điểm gián đoạn nhận trong log client cùng thay đổi loại mạng và sóng - Đúng nếu: chỉ khi di chuyển mới có khoảng trống nhận vài trăm ms đến vài giây rồi gói dồn tới, đứng yên thì không tái hiện - Loại trừ nếu: đứng yên vẫn vậy → “Sóng di động yếu hoặc vùng lõm sóng” hoặc “Chuyển qua lại 5G↔LTE liên tục (vùng rìa phủ sóng 5G)” - Cách kiểm tra: Kiểm tra ở môi trường phía người chơi - Nguồn: - [Report ITU-R M.2134: Requirements related to technical performance for IMT-Advanced radio interface(s)](https://www.itu.int/pub/R-REP-M.2134-2008) · ITU · Yêu cầu về thời gian không trao đổi được dữ liệu trong lúc handover: cùng tần số 27,5 ms, khác tần số 40–60 ms - [Understanding Operational 5G: A First Measurement Study on Its Coverage, Performance and Energy Consumption (SIGCOMM 2020)](https://xyzhang.ucsd.edu/papers/DXu_SIGCOMM20_5Gmeasure.pdf) · ACM · Độ trễ handover đo thực tế trên mạng thương mại: 4G↔4G trung bình 30 ms, giữa các cell 5G (NSA) trung bình 108 ms #### hn-rrc · Trễ chuyển trạng thái RRC (chế độ tiết kiệm điện của mạng di động) · Radio state promotion (RRC) Khi không có dữ liệu trong một lúc, điện thoại hạ kết nối sóng xuống trạng thái tiêu thụ điện thấp, và đến gói tin kế tiếp phải kích hoạt lại nên bị trễ. - Vì sao → Dẫn đến → Trên màn hình: Không có dữ liệu một lúc thì điện thoại chuyển kết nối sóng sang trạng thái tiết kiệm điện → Muốn gửi gói tiếp theo phải kích hoạt lại kết nối → Riêng thao tác đầu tiên sau một lúc đứng yên bị trễ rõ rệt - Triệu chứng: Trễ thao tác / Yếu tố: Độ trễ - Ai gặp: Chỉ mình tôi / Khi nào: Sau khi để yên một lúc - Phụ trách chính: Phát triển client (Đội phát triển game) - Việc cần làm (Đội phát triển game): Gửi gói nhẹ định kỳ để giữ trạng thái hoạt động (đánh đổi với pin). - Con số tham khảo: LTE thường chuyển sang tiết kiệm điện sau khoảng 10 giây không có dữ liệu, và kích hoạt lại mất vài chục đến vài trăm ms (ví dụ đo thực tế: khoảng 0,3–0,6 giây). 3G mất trên 1 giây. - Trên đồ thị: Chỉ một phần cao (RTT của yêu cầu đầu tiên sau khi nhàn rỗi (di động)) - Chỗ cần xem: Chia RTT trong game theo khoảng cách với lần truyền trước đó. Trên di động, so sánh RTT của gói đầu tiên gửi sau hơn 10 giây nghỉ với RTT của các gói gửi liên tiếp - Đúng nếu: trên mạng di động, chỉ gói đầu tiên sau khi nghỉ bị trễ vài trăm ms, các gói gửi ngay sau đó bình thường. Trên Wi-Fi không có khác biệt - Loại trừ nếu: gửi liên tiếp vẫn trễ → phía sóng, đường truyền hoặc tuyến đường - Cách kiểm tra: Cần log và chỉ số của server, client game - Nguồn: - [An In-depth Study of LTE: Effect of Network Protocol and Application Behavior on Performance (SIGCOMM 2013)](https://conferences.sigcomm.org/sigcomm/2013/papers/sigcomm/p363.pdf) · ACM · Mạng LTE được đo có timer chuyển sang tiết kiệm điện (tail) 10 giây, độ trễ kích hoạt lại từ tiết kiệm điện có trung vị 435 ms (25–75%: 319–558 ms); 3G khoảng 1,5–2 giây - [Optimize network access](https://developer.android.com/develop/connectivity/network-ops/network-access-optimization) · Android (Google) · Độ trễ chuyển trạng thái sóng và thời gian tail khác nhau tùy công nghệ vô tuyến (3G, LTE, 5G) và cấu hình nhà mạng; ví dụ 3G: từ công suất thấp→công suất tối đa khoảng 1,5 giây, từ chờ→công suất tối đa trên 2 giây - [Report ITU-R M.2134: Requirements related to technical performance for IMT-Advanced radio interface(s)](https://www.itu.int/pub/R-REP-M.2134-2008) · ITU · Yêu cầu độ trễ control plane khi chuyển từ trạng thái chờ lên trạng thái hoạt động: dưới 100 ms (không tính paging và đoạn hữu tuyến) #### hn-weak-cell · Sóng di động yếu hoặc vùng lõm sóng · Weak cellular signal Trong thang máy, tầng hầm hay sâu bên trong tòa nhà, số lần truyền lại tăng, tốc độ giảm và cuối cùng mất kết nối. - Vì sao → Dẫn đến → Trên màn hình: Di chuyển vào nơi sóng yếu → Truyền lại trên đoạn không dây tăng, tốc độ giảm, mất sóng chốc lát → Jitter và mất gói gây giật khựng, dịch chuyển tức thời, cuối cùng mất kết nối - Triệu chứng: Giật khựng, Dịch chuyển tức thời, Mất kết nối / Yếu tố: Jitter, Mất gói - Ai gặp: Chỉ mình tôi / Khi nào: Khi di chuyển hoặc chuyển bản đồ - Phụ trách chính: Bên ngoài (Bên ngoài) / Phối hợp: Phát triển client (Đội phát triển game) - Việc cần làm (Đội phát triển game): Hoàn thiện quy trình kết nối lại, hiển thị chất lượng mạng. - Việc cần làm (Bên ngoài): Giải thích cho người chơi rằng lỗi xảy ra ở nơi sóng yếu (thang máy, tầng hầm, sâu trong tòa nhà). - Trên đồ thị: Chỉ một phần cao (RTT, mất gói (theo người chơi di động)) - Chỗ cần xem: Xác nhận vị trí lúc báo mất kết nối (thang máy, tầng hầm, trong tòa nhà) và vạch sóng trên điện thoại, lặp lại cùng thao tác ở nơi sóng tốt để so sánh - Đúng nếu: chỉ ở nơi sóng yếu RTT và mất gói mới tăng và bị mất kết nối, chuyển tới nơi sóng tốt thì hết - Loại trừ nếu: sóng tốt mà vẫn vậy → đoạn nhà mạng hoặc phía server - Cách kiểm tra: Kiểm tra ở môi trường phía người chơi - Nguồn: - [An In-depth Study of LTE: Effect of Network Protocol and Application Behavior on Performance (SIGCOMM 2013)](https://conferences.sigcomm.org/sigcomm/2013/papers/sigcomm/p363.pdf) · ACM · LTE che giấu mất gói ở đoạn không dây bằng truyền lại ở tầng vật lý và tầng MAC; băng thông khả dụng thay đổi mạnh theo từng giây tùy cường độ sóng và các yếu tố khác #### hn-5g-flip · Chuyển qua lại 5G↔LTE liên tục (vùng rìa phủ sóng 5G) · 5G NSA / LTE switching Ở trong tòa nhà có sóng 5G yếu hoặc vùng rìa phủ sóng 5G, điện thoại liên tục chuyển qua lại giữa 5G và LTE, và mỗi lần chuyển ping lại vọt lên hoặc kết nối bị ngắt chốc lát. - Vì sao → Dẫn đến → Trên màn hình: Đang ở nơi sóng 5G lúc có lúc không (trong tòa nhà, rìa vùng phủ 5G) → Điện thoại liên tục chuyển giữa 5G và LTE, mỗi lần chuyển có một khoảng gián đoạn ngắn → Dù đứng yên ping vẫn vọt lên bất thường, thỉnh thoảng đứng hình hoặc dịch chuyển tức thời - Triệu chứng: Giật khựng, Dịch chuyển tức thời, Đứng hình / Yếu tố: Jitter, Mất gói - Ai gặp: Chỉ mình tôi / Khi nào: Thỉnh thoảng bất chợt, Khi di chuyển hoặc chuyển bản đồ - Phụ trách chính: Bên ngoài (Bên ngoài) / Phối hợp: Phát triển client (Đội phát triển game) - Việc cần làm (Đội phát triển game): Tự động tăng độ dài bộ đệm nội suy khi jitter tăng, ghi kèm thay đổi loại mạng (5G, LTE) vào log lúc xảy ra lag để phân biệt nguyên nhân. - Việc cần làm (Bên ngoài): Hướng dẫn người chơi chuyển sang chế độ ưu tiên LTE trong cài đặt để so sánh, khuyên dùng Wi-Fi. - Con số tham khảo: Mỗi lần chuyển mất vài chục đến vài trăm ms. 5G tại Hàn Quốc phần lớn dùng cơ chế gộp chung với LTE (NSA), nên riêng phần 5G dễ lúc kết nối lúc rớt. - Trên đồ thị: Thỉnh thoảng vọt lên bất chợt (RTT, thay đổi loại mạng (5G, LTE)) - Chỗ cần xem: Chuyển điện thoại sang ưu tiên LTE rồi so sánh ở cùng vị trí. Chắc chắn hơn nếu client ghi thay đổi biểu tượng mạng trong TelephonyDisplayInfo của Android (như OVERRIDE_NETWORK_TYPE_NR_NSA) cùng với RTT - Đúng nếu: thời điểm RTT vọt lên trùng với thời điểm biểu tượng 5G↔LTE đổi, ở chế độ ưu tiên LTE thì không còn vọt lên - Loại trừ nếu: biểu tượng mạng không đổi mà vẫn vọt lên → “Sóng di động yếu hoặc vùng lõm sóng” hoặc phía đường truyền - Cách kiểm tra: Kiểm tra ở môi trường phía người chơi - Nguồn: - [Understanding Operational 5G: A First Measurement Study on Its Coverage, Performance and Energy Consumption (SIGCOMM 2020)](https://xyzhang.ucsd.edu/papers/DXu_SIGCOMM20_5Gmeasure.pdf) · ACM · 5G NSA giao phần điều khiển cho LTE, nên khi đổi cell 5G, máy bỏ 5G, đi qua LTE rồi mới kết nối lại: trung bình 108 ms (4G→5G 80 ms); ngay sau lần chuyển có 5G, thông lượng TCP giảm 73–83% - [5G 통신서비스 품질평가 결과 발표](https://www.korea.kr/briefing/policyBriefingView.do?newsId=156404679) · 과학기술정보통신부 · Theo công bố năm 2020 thì 5G tại Hàn Quốc đang được cung cấp theo cơ chế NSA, còn việc chuyển sang SA mới ở giai đoạn dự kiến - [TelephonyDisplayInfo](https://developer.android.com/reference/android/telephony/TelephonyDisplayInfo) · Android (Google) · OVERRIDE_NETWORK_TYPE_NR_NSA: biểu tượng mạng khi máy đang kết nối LTE và có thể hoặc đang kết nối kép (EN-DC) với 5G (NR) #### hn-captive · Giới hạn của Wi-Fi công cộng và mạng công ty · Captive portal, restrictive network Trang đăng nhập của Wi-Fi quán cà phê hoặc tường lửa công ty chặn kết nối của game. - Vì sao → Dẫn đến → Trên màn hình: Chưa xác thực ở trang đăng nhập, hoặc tường lửa chặn cổng game hay UDP → Bị chặn ngay từ lần thử kết nối, hoặc chỉ một phần đi qua được → Không vào được, hoặc đăng nhập được nhưng không vào được game - Triệu chứng: Không vào được·kẹt loading / Yếu tố: Mất gói - Ai gặp: Chỉ mình tôi / Khi nào: Ngay sau đăng nhập hoặc bảo trì - Phụ trách chính: Bên ngoài (Bên ngoài) / Phối hợp: Phát triển client (Đội phát triển game), Phát triển server (Đội phát triển game) - Việc cần làm (Đội phát triển game): Client: khi bị chặn thì hiện thông báo lý do (chưa xác thực trang đăng nhập, UDP bị chặn…), UDP bị chặn thì tự động chuyển sang đường thay thế. Server: cung cấp đường thay thế như TCP 443. - Việc cần làm (Bên ngoài): Hướng dẫn người chơi xác thực ở trang đăng nhập trước khi dùng Wi-Fi công cộng, và dùng mạng khác ở nơi bị chặn như mạng công ty. - Trên đồ thị: Chỉ một phần cao (Số lần kết nối thất bại (theo mạng)) - Chỗ cần xem: Nhờ người chơi bị lỗi thử kết nối bằng mạng khác như dữ liệu di động, xem trong log kết nối của server gói UDP đầu tiên có tới không và kết nối qua đường thay thế TCP 443 có được không - Đúng nếu: chỉ thất bại ở một mạng Wi-Fi nhất định (quán cà phê, công ty), sang mạng khác là vào ngay. Chưa xác thực trang đăng nhập, hoặc chỉ riêng UDP không tới được server - Loại trừ nếu: mạng nào cũng thất bại → phía tài khoản, server hoặc “DNS lỗi hoặc chậm”. Thất bại trên toàn bộ một quốc gia hoặc nhà mạng → “Hạn chế UDP và kiểm tra gói tin ở cấp quốc gia hoặc nhà mạng” - Cách kiểm tra: Kiểm tra ở môi trường phía người chơi - Nguồn: - [RFC 8952: Captive Portal Architecture](https://www.rfc-editor.org/rfc/rfc8952) · IETF · Captive portal: mạng hạn chế truy cập cho tới khi người dùng đáp ứng yêu cầu như đồng ý điều khoản hoặc xác thực - [RFC 9308: Applicability of the QUIC Transport Protocol](https://www.rfc-editor.org/rfc/rfc9308) · IETF · Theo nghiên cứu đo đạc, 3–5% mạng chặn toàn bộ UDP, nên ứng dụng dựa trên UDP cần chuẩn bị đường thay thế qua TCP (TLS) ### L4 Đường truyền Internet (14 nguyên nhân) #### isp-distance · Độ trễ lan truyền (khoảng cách vật lý) · Propagation delay Ngay cả ánh sáng trong cáp quang cũng chỉ đi được khoảng 200.000 km trong 1 giây. Server ở xa thì dù tốt đến đâu vẫn chậm. - Vì sao → Dẫn đến → Trên màn hình: Server ở xa (server nước ngoài, ở châu lục khác) → Thời gian khứ hồi tăng theo khoảng cách (tối thiểu 10 ms cho mỗi 1.000 km) → Mọi hành động đều bị trễ thao tác ở một mức cố định, chịu bất lợi khi phán định - Triệu chứng: Trễ thao tác / Yếu tố: Độ trễ - Ai gặp: Một khu vực hoặc nhà mạng / Khi nào: Luôn luôn - Phụ trách chính: Hạ tầng server (Đội hạ tầng) / Phối hợp: Hạ tầng mạng (Đội hạ tầng), Phát triển server (Đội phát triển game) - Việc cần làm (Đội phát triển game): Code không sửa được định luật vật lý nên chỉ có thể giảm nhẹ, cho người chơi chọn khu vực để vào server gần, dùng bù trễ (quay ngược) để giảm bất lợi khi phán định. - Việc cần làm (Đội hạ tầng): Thiết bị server·OS: đặt server riêng cho từng khu vực có nhiều người chơi. Mạng: đặt điểm kết nối (edge) gần người chơi, chọn đường truyền và tuyến đường ít đi vòng. - Con số tham khảo: Seoul–Tokyo khoảng 30 ms, Seoul–Singapore khoảng 75 ms, Seoul–miền Tây nước Mỹ khoảng 140 ms, Seoul–châu Âu khoảng 230–270 ms (khứ hồi, theo tuyến đường thực tế). Với châu Âu, gần như không có tuyến cáp lớn nào chạy theo đường thẳng, nên dữ liệu phải vòng qua Đông Nam Á và Suez hoặc qua Mỹ; vì vậy độ trễ dài hơn nhiều so với khoảng cách. - Trên đồ thị: Luôn cao ngay từ đầu (RTT (theo quốc gia, khu vực)) - Chỗ cần xem: Gắn quốc gia vào IP kết nối để xem phân bố RTT theo quốc gia, rồi đo ping và traceroute tới server từ VM ở region cloud của khu vực đó hoặc từ probe RIPE Atlas (chọn theo quốc gia, ASN) - Đúng nếu: RTT của quốc gia ở xa luôn cao bất kể khung giờ, và giá trị đó gần với độ trễ tối thiểu tính theo khoảng cách (10 ms khứ hồi cho mỗi 1.000 km) cũng như số liệu độ trễ công khai - Loại trừ nếu: cao hơn nhiều so với mức khoảng cách giải thích được → xem “Định tuyến đi đường vòng”; chỉ tăng vào buổi tối → xem “Nghẽn peering vào giờ cao điểm” - Cách kiểm tra: Kiểm tra bằng công cụ hạ tầng (không cần code game) - Sự cố thực tế: riot-direct-2015 - Nguồn: - [ITU-T G.114: One-way transmission time](https://www.itu.int/rec/T-REC-G.114-200305-I/en) · ITU · Giá trị hoạch định cho độ trễ lan truyền trên cáp quang là 5 µs/km (khoảng 200.000 km mỗi giây, 1.000 km khứ hồi mất 10 ms) - [Azure network round-trip latency statistics](https://learn.microsoft.com/en-us/azure/networking/azure-network-latency) · Microsoft Azure · Trung vị thời gian khứ hồi đo thực tế tính từ Seoul (Korea Central): Tokyo 30 ms, Singapore 68 ms, miền Tây nước Mỹ 124–136 ms, châu Âu 234–244 ms - [AAE-1 & SMW5 cable cuts impact millions of users across multiple countries](https://blog.cloudflare.com/aae-1-smw5-cable-cuts/) · Cloudflare · Lưu lượng giữa châu Âu và châu Á thường đi qua các tuyến cáp quang biển chạy qua Ai Cập (Suez) - [Probe Selection (RIPE Atlas REST API)](https://atlas.ripe.net/docs/apis/rest-api-manual/measurements/creating-measurements/probe-selection/) · RIPE NCC · Chọn probe cho phép đo RIPE Atlas theo quốc gia, khu vực, ASN hoặc dải địa chỉ rồi chạy ping, traceroute #### isp-satellite · Internet vệ tinh (quỹ đạo thấp, địa tĩnh) · Satellite internet (LEO, GEO) Với Internet vệ tinh, sóng phải đi lên vũ trụ rồi quay về. Vệ tinh địa tĩnh chỉ riêng thời gian khứ hồi đã hơn 0,5 giây. Vệ tinh quỹ đạo thấp như Starlink bình thường thì nhanh, nhưng vào lúc tuyến đường được phân lại, độ trễ dao động và đôi khi kết nối đứt quãng trong chốc lát. - Vì sao → Dẫn đến → Trên màn hình: Kết nối bằng Internet vệ tinh địa tĩnh hoặc quỹ đạo thấp, hoặc bằng Wi-Fi trên máy bay dùng vệ tinh, khi ở nhà, trên tàu hay trên máy bay → Vệ tinh địa tĩnh ở độ cao khoảng 36.000 km nên bản thân quãng đường đi và về đã dài; vệ tinh quỹ đạo thấp phân lại tuyến thiết bị đầu cuối–vệ tinh–trạm mặt đất theo chu kỳ ngắn, và vào lúc đó độ trễ tăng, mất gói trong chốc lát → Vệ tinh địa tĩnh gây trễ thao tác lớn ở mọi hành động; vệ tinh quỹ đạo thấp bình thường vẫn ổn nhưng cứ cách một khoảng đều lại giật khựng hoặc dịch chuyển tức thời - Triệu chứng: Trễ thao tác, Giật khựng, Dịch chuyển tức thời / Yếu tố: Độ trễ, Jitter, Mất gói - Ai gặp: Chỉ mình tôi, Cùng một nhà, Một khu vực hoặc nhà mạng / Khi nào: Luôn luôn, Theo chu kỳ đều - Phụ trách chính: Bên ngoài (Bên ngoài) / Phối hợp: Phát triển client (Đội phát triển game), Phát triển server (Đội phát triển game) - Việc cần làm (Đội phát triển game): Client: tự động kéo dài bộ đệm nội suy theo jitter, gửi lặp input để chịu được mất gói ngắn, hiển thị chất lượng kết nối. Server: tính đến độ trễ của đường truyền vệ tinh khi đặt khung phán định và giới hạn bù trễ, dùng timeout không đẩy người chơi ra ngay chỉ vì một khoảng trống quanh 1 giây. - Việc cần làm (Bên ngoài): Thông báo cho người chơi rằng Internet vệ tinh có thể có độ trễ lớn hoặc vọt lên theo chu kỳ, khuyên dùng mạng có dây mặt đất cho nội dung đối kháng nếu có thể. - Con số tham khảo: Với vệ tinh địa tĩnh (độ cao 36.000 km), riêng thời gian sóng đi qua không gian đã là 260 ms một chiều, nên khứ hồi vượt 520 ms (ITU-T G.114). Với Starlink quỹ đạo thấp, theo tài liệu chính thức (giá trị trung bình mỗi 15 giây), trung vị giờ cao điểm tại Mỹ là 33 ms và ngay cả 1% tệ nhất (p99) cũng dưới 65 ms (năm 2024). Còn trong nghiên cứu đo đạc, cứ 15 giây tuyến đường lại được phân lại; vào lúc đó độ trễ thay đổi và xuất hiện những lần đứt quãng ngắn dưới 1 giây. Trong phép đo Internet trên máy bay năm 2018, độ trễ khứ hồi của kiểu kết nối qua vệ tinh trung bình là 750 ms. - Trên đồ thị: Chỉ một phần cao (RTT, jitter (theo ASN của nhà cung cấp Internet vệ tinh)) - Chỗ cần xem: Xem ASN của IP kết nối có thuộc nhà cung cấp Internet vệ tinh không, rồi vẽ riêng phân bố RTT và chuỗi thời gian của người chơi dùng nhà cung cấp đó. Đo ping liên tục vài phút tới server từ probe RIPE Atlas thuộc ASN đó, hoặc nhờ người chơi để ping chạy liên tục và đo khoảng cách giữa các lần vọt lên - Đúng nếu: nhà cung cấp vệ tinh địa tĩnh có RTT luôn trên 500 ms; nhà cung cấp quỹ đạo thấp bình thường vài chục ms nhưng cứ khoảng 15 giây RTT lại đổi hoặc đứt quãng ngắn - Loại trừ nếu: ASN không thuộc nhà cung cấp vệ tinh nhưng RTT luôn cao → xem “Độ trễ lan truyền (khoảng cách vật lý)” hoặc “Định tuyến đi đường vòng”; vọt lên thất thường → phía Wi-Fi hoặc sóng di động - Cách kiểm tra: Kiểm tra bằng công cụ hạ tầng (không cần code game) - Tìm hiểu thêm: Vệ tinh quỹ đạo thấp ở gần (một chặng của Starlink chỉ mất 1,8–3,6 ms), nên độ trễ bình thường có thể tương đương đường truyền mặt đất. Đổi lại, nếu điểm ra Internet từ trạm mặt đất (PoP) ở xa server game thì tuyến đường dài thêm tương ứng, và nếu phải đi vòng qua liên kết laser giữa các vệ tinh thì độ trễ còn cộng thêm. Theo nghiên cứu đo đạc, dao động chu kỳ 15 giây là do việc phân lại tuyến đường xảy ra cùng lúc trên toàn thế giới, độc lập với việc chuyển giữa các vệ tinh. Wi-Fi trên máy bay có độ trễ khác nhau rất nhiều tùy công nghệ (vệ tinh hay trạm phát sóng mặt đất); nếu dùng vệ tinh địa tĩnh thì cũng có thời gian khứ hồi dài như trên. - Nguồn: - [ITU-T G.114: One-way transmission time](https://www.itu.int/rec/T-REC-G.114-200305-I/en) · ITU · Giá trị hoạch định độ trễ lan truyền một chiều của chặng vệ tinh: độ cao 400 km là 12 ms, 14.000 km là 110 ms, 36.000 km (địa tĩnh) là 260 ms - [Improving Starlink’s Latency](https://starlink.com/public-files/StarlinkLatency.pdf) · Starlink · Trung vị giờ cao điểm tại Mỹ từ 48,5 ms→33 ms, 1% chậm nhất (p99) từ trên 150 ms→dưới 65 ms (năm 2024), lan truyền qua một chặng vệ tinh 1,8–3,6 ms, đi vòng qua liên kết laser thì độ trễ tăng thêm, khoảng cách từ trạm mặt đất tới điểm truy cập Internet (PoP) cũng là một yếu tố gây trễ - [A Multifaceted Look at Starlink Performance (WWW 2024)](https://www.nitindermohan.com/documents/2024/pubs/starlinkWWW2024.pdf) · ACM · Starlink phân lại tuyến đường mỗi 15 giây vào cùng một thời điểm trên toàn thế giới; tại ranh giới đó độ trễ và thông lượng dao động, xuất hiện những lần đứt quãng ngắn dưới 1 giây (nguyên nhân nằm ngoài việc chuyển giữa các vệ tinh), độ trễ chặng thiết bị đầu cuối↔vệ tinh↔trạm mặt đất khoảng 40 ms - [Mile High WiFi: A First Look at In-Flight Internet Connectivity (WWW 2018)](https://aqualab.cs.northwestern.edu/publication/2018/jrula-www18/) · ACM · Đo Internet trên máy bay trong 45 giờ: độ trễ khứ hồi trung bình của kiểu trạm phát sóng mặt đất là 200 ms, kiểu vệ tinh là 750 ms; tỷ lệ mất gói trung vị của kiểu vệ tinh là 7% - [Probe Selection (RIPE Atlas REST API)](https://atlas.ripe.net/docs/apis/rest-api-manual/measurements/creating-measurements/probe-selection/) · RIPE NCC · Chọn probe cho phép đo RIPE Atlas theo quốc gia, khu vực, ASN hoặc dải địa chỉ rồi chạy ping, traceroute #### isp-routing · Định tuyến đi đường vòng · Suboptimal routing Do hợp đồng kết nối giữa các nhà mạng, dữ liệu tới cả server ở gần cũng phải đi vòng qua nơi xa. - Vì sao → Dẫn đến → Trên màn hình: Nhà mạng của bạn và nhà mạng phía server không kết nối trực tiếp với nhau → Đi qua nước khác hoặc thành phố khác nên quãng đường và số thiết bị tăng lên → Chỉ người chơi của một nhà mạng nhất định có ping cao bất thường - Triệu chứng: Trễ thao tác / Yếu tố: Độ trễ - Ai gặp: Một khu vực hoặc nhà mạng / Khi nào: Luôn luôn - Phụ trách chính: Hạ tầng mạng (Đội hạ tầng) / Phối hợp: Bên ngoài (Bên ngoài) - Việc cần làm (Đội hạ tầng): Kết nối với nhiều nhà mạng (multihoming), giám sát ping theo từng nhà mạng để tìm nhà mạng đang đi vòng, thương lượng với nhà mạng để điều chỉnh tuyến đường. - Việc cần làm (Bên ngoài): Yêu cầu nhà mạng liên quan điều chỉnh tuyến đường. - Con số tham khảo: Dù trong cùng một nước, ping vẫn có thể chênh nhau gấp hai, ba lần tùy tuyến đường. - Trên đồ thị: Luôn cao ngay từ đầu (RTT (theo nhà mạng, ASN)) - Chỗ cần xem: So sánh RTT theo nhà mạng (ASN), dùng traceroute, mtr từ probe RIPE Atlas của nhà mạng chậm hoặc do người chơi gửi về để xem tuyến đường đi qua nước nào, thành phố nào. Đo riêng IPv4 và IPv6 (mtr -4, -6) - Đúng nếu: cùng khu vực nhưng chỉ một nhà mạng luôn cao, và tuyến đường có chặng đi qua nước khác hoặc thành phố xa. Hoặc chỉ một hệ địa chỉ (IPv4 hoặc IPv6) cao - Loại trừ nếu: mọi nhà mạng đều cao tương tự → xem “Độ trễ lan truyền (khoảng cách vật lý)”; chỉ cao vào buổi tối → xem “Nghẽn peering vào giờ cao điểm” - Cách kiểm tra: Kiểm tra bằng công cụ hạ tầng (không cần code game) - Tìm hiểu thêm: IPv4 và IPv6 được định tuyến riêng, nên với cùng một server, có thể chỉ một bên đi vòng xa và bị chậm (phép đo của APNIC năm 2016: trong cùng một nhà mạng xuất hiện riêng những nhóm người dùng có IPv6 chậm hơn IPv4 lần lượt 15 ms, 25 ms hoặc 75 ms). Ứng dụng dùng Happy Eyeballs (RFC 8305), tức cơ chế dùng bên nào kết nối được trước giữa IPv6 và IPv4, sẽ thử IPv6 trước; nếu IPv6 kết nối được trong 250 ms theo giá trị khuyến nghị thì không thử IPv4 nữa. Vì vậy dù phía IPv6 chậm hơn một chút, ứng dụng vẫn dễ kết nối theo đường đó. Nếu chỉ một nhà mạng có ping cao, hãy đo riêng IPv4 và IPv6. - Sự cố thực tế: riot-direct-2015 - Nguồn: - [Quantifying the Causes of Path Inflation (SIGCOMM 2003)](https://conferences.sigcomm.org/sigcomm/2003/papers/p113-spring.pdf) · ACM · Phân tích 65 ISP: chính sách peering giữa các ISP và định tuyến liên miền làm tuyến đường dài ra đáng kể - [The Internet at the Speed of Light (HotNets 2014)](https://conferences.sigcomm.org/hotnets/2014/papers/hotnets-XIII-final111.pdf) · ACM · Tuyến đường router thực tế dài gấp khoảng 1,5 lần (trung vị) so với cáp quang theo đường thẳng; có cả trường hợp gói tin giữa hai điểm gần nhau lại đi vòng sang bên kia địa cầu (hairpinning) - [Probe Selection (RIPE Atlas REST API)](https://atlas.ripe.net/docs/apis/rest-api-manual/measurements/creating-measurements/probe-selection/) · RIPE NCC · Chọn probe cho phép đo RIPE Atlas theo quốc gia, khu vực, ASN hoặc dải địa chỉ rồi chạy ping, traceroute - [mtr(8) manual page source](https://raw.githubusercontent.com/traviscross/mtr/master/man/mtr.8.in) · mtr · Đo tuyến đường chỉ bằng IPv4 hoặc chỉ bằng IPv6 với -4, -6 - [RFC 8305: Happy Eyeballs Version 2: Better Connectivity Using Concurrency](https://www.rfc-editor.org/rfc/rfc8305) · IETF · Địa chỉ hoặc hệ địa chỉ (IPv4, IPv6) có thể bị chặn, hỏng hoặc chậm tùy mạng; thử IPv6 trước và chờ 250 ms theo khuyến nghị trước lần thử kết nối tiếp theo - [IPv6 Performance – Revisited](https://blog.apnic.net/2016/08/22/ipv6-performance-revisited/) · APNIC · So sánh thời gian khứ hồi IPv6 và IPv4 của cùng người dùng dual-stack: tùy mạng truy cập mà gói IPv6 có thể được xử lý hoàn toàn khác, nên trong cùng một nhà mạng xuất hiện những nhóm có IPv6 chậm hơn 15 ms, 25 ms và 75 ms #### isp-peak · Nghẽn peering vào giờ cao điểm · Peak-hour congestion at peering Khoảng 9–11 giờ tối, lưu lượng video tăng vọt nên đoạn kết nối giữa các nhà mạng (peering) dễ bị nghẽn. - Vì sao → Dẫn đến → Trên màn hình: Buổi tối lưu lượng streaming và tải xuống dồn đến → Hàng đợi dồn lên và mất gói ở đoạn peering → Chỉ vào buổi tối, người chơi của một nhà mạng nhất định bị giật khựng hoặc dịch chuyển tức thời - Triệu chứng: Giật khựng, Dịch chuyển tức thời, Kéo ngược / Yếu tố: Jitter, Mất gói, Độ trễ - Ai gặp: Một khu vực hoặc nhà mạng / Khi nào: Giờ cao điểm buổi tối - Phụ trách chính: Hạ tầng mạng (Đội hạ tầng) / Phối hợp: Bên ngoài (Bên ngoài) - Việc cần làm (Đội hạ tầng): Tăng kết nối trực tiếp với nhà mạng đó, đi vòng tránh tuyến bị nghẽn, giám sát mất gói và ping buổi tối theo từng nhà mạng. - Việc cần làm (Bên ngoài): Yêu cầu nhà mạng đó mở rộng dung lượng đoạn peering. - Trên đồ thị: Chỉ cao vào một khung giờ (RTT, mất gói (theo nhà mạng)) - Chỗ cần xem: Vẽ RTT và mất gói theo nhà mạng (ASN) theo từng khung giờ, rồi lấy kết quả mtr vào buổi tối và ban ngày từ probe RIPE Atlas của nhà mạng đó hoặc từ người chơi để xem mất gói bắt đầu từ chặng nào - Đúng nếu: chỉ một nhà mạng có RTT và mất gói tăng vào khoảng 9–11 giờ tối mỗi ngày, và trong mtr, mất gói và độ trễ kéo dài từ đoạn kết nối giữa các nhà mạng đến tận đích - Loại trừ nếu: mọi nhà mạng cùng tăng → phía đường truyền hoặc server của chúng ta; chỉ một hộ gia đình bị tệ vào buổi tối → xem “Kênh Wi-Fi bị nghẽn” - Cách kiểm tra: Kiểm tra bằng công cụ hạ tầng (không cần code game) - Nguồn: - [Inferring Persistent Interdomain Congestion (SIGCOMM 2018)](https://www.caida.org/catalog/papers/2018_inferring_persistent_interdomain_congestion/inferring_persistent_interdomain_congestion.pdf) · ACM · Nghẽn lặp lại ở một số đoạn kết nối giữa các nhà mạng, độ trễ tăng vào giờ cao điểm mỗi ngày, tỷ lệ mất gói cũng tăng trong khung giờ nghẽn - [Probe Selection (RIPE Atlas REST API)](https://atlas.ripe.net/docs/apis/rest-api-manual/measurements/creating-measurements/probe-selection/) · RIPE NCC · Chọn probe cho phép đo RIPE Atlas theo quốc gia, khu vực, ASN hoặc dải địa chỉ rồi chạy ping, traceroute #### isp-cable · Sự cố cáp quang biển, đường truyền quốc tế · Submarine cable fault Khi cáp quang biển bị đứt, dữ liệu phải đi vòng theo tuyến xa trong nhiều tuần (có khi nhiều tháng) cho đến khi sửa xong, và các đường truyền còn lại bị nghẽn. - Vì sao → Dẫn đến → Trên màn hình: Đứt cáp hoặc hỏng thiết bị → Lưu lượng dồn sang tuyến đường vòng xa và các đường truyền còn lại → Người chơi kết nối từ nước ngoài bị ping tăng vọt và mất gói kéo dài từ vài ngày đến vài tuần - Triệu chứng: Trễ thao tác, Dịch chuyển tức thời / Yếu tố: Độ trễ, Mất gói - Ai gặp: Một khu vực hoặc nhà mạng / Khi nào: Luôn luôn - Phụ trách chính: Bên ngoài (Bên ngoài) / Phối hợp: Hạ tầng mạng (Đội hạ tầng) - Việc cần làm (Đội hạ tầng): Chuẩn bị đường truyền đi theo tuyến khác, chuyển lưu lượng sang tuyến đó khi có sự cố. - Việc cần làm (Bên ngoài): Thông báo cho người chơi ở nước ngoài về nguyên nhân và thời điểm dự kiến khôi phục, đề nghị nhà cung cấp đường truyền xác nhận lịch sửa chữa. - Trên đồ thị: Tăng như bậc thang từ một thời điểm (RTT (theo quốc gia ở nước ngoài)) - Chỗ cần xem: Tìm thời điểm RTT và mất gói tăng trên đồ thị theo quốc gia, đối chiếu với bản tổng hợp sự cố Internet của Cloudflare Radar và thông báo của đơn vị vận hành cáp quang biển, rồi dùng traceroute kiểm tra xem tuyến đường có vòng qua châu lục khác không - Đúng nếu: từ một thời điểm, RTT của một khu vực nước ngoài nhất định tăng lên một bậc và giữ nguyên trong vài ngày đến vài tuần, cùng thời gian đó có báo cáo sự cố cáp. Tuyến đường chuyển sang một đường vòng xa khác với thường ngày - Loại trừ nếu: trở lại bình thường trong vài ngày và không có báo cáo sự cố → xem “Thay đổi tuyến BGP và hội tụ” hoặc đoạn mạng của nhà mạng - Cách kiểm tra: Kiểm tra bằng công cụ hạ tầng (không cần code game) - Nguồn: - [AAE-1 & SMW5 cable cuts impact millions of users across multiple countries](https://blog.cloudflare.com/aae-1-smw5-cable-cuts/) · Cloudflare · Sửa cáp quang biển cần điều tàu sửa chữa đến nên thường mất vài ngày đến vài tuần (trường hợp Tonga là 38 ngày); khi đứt cáp, độ trễ và mất gói trên tuyến châu Âu–châu Á tăng - [Q2 2024 Internet disruption summary](https://blog.cloudflare.com/q2-2024-internet-disruption-summary/) · Cloudflare · Cáp ở Biển Đỏ bị hư hỏng vào tháng 2 năm 2024 đến tháng 7 vẫn đang sửa (vùng xung đột); các sự cố đứt cáp EASSy và Seacom vào tháng 5 được khôi phục sau 19 ngày - [Q1 2024 Internet disruption summary](https://blog.cloudflare.com/q1-2024-internet-disruption-summary/) · Cloudflare · Sự cố đứt cáp ở Tây Phi (ngày 14 tháng 3) được khôi phục sau 3–6 tuần; trong thời gian đó lưu lượng được chuyển sang các tuyến cáp khác #### isp-bgp · Thay đổi tuyến BGP và hội tụ · Route change / BGP convergence Khi thông tin định tuyến trên Internet thay đổi, gói tin bị mất trong vài giây đến vài chục giây (hiếm khi vài phút) cho đến khi định tuyến hội tụ trở lại. - Vì sao → Dẫn đến → Trên màn hình: Thông tin định tuyến ở đoạn mạng của một nhà mạng nào đó thay đổi → Trong vài giây đến vài chục giây, gói tin biến mất hoặc chuyển sang tuyến mới → Đột nhiên đứng hình vài giây, sau đó chỉ số ping đổi hẳn (ví dụ 40 → 70 ms) - Triệu chứng: Đứng hình, Dịch chuyển tức thời / Yếu tố: Mất gói, Độ trễ - Ai gặp: Một khu vực hoặc nhà mạng / Khi nào: Thỉnh thoảng bất chợt - Phụ trách chính: Hạ tầng mạng (Đội hạ tầng) / Phối hợp: Phát triển server (Đội phát triển game), Bên ngoài (Bên ngoài) - Việc cần làm (Đội phát triển game): Timeout chịu được gián đoạn ngắn (không cắt ngay kết nối chỉ vì nó im lặng vài giây). - Việc cần làm (Đội hạ tầng): Giám sát tuyến đường (theo dõi thay đổi tuyến và ping của dải IP của chúng ta), với sự cố trên đường truyền của chúng ta thì dùng BFD để phát hiện trong vòng 1 giây và chuyển đường (hold time mặc định của BGP là 90–180 giây), nếu tuyến đường đổi sang đường xa mà không quay lại thì chuyển lưu lượng sang đường truyền khác. - Việc cần làm (Bên ngoài): Với đoạn mạng có tuyến đường thay đổi thường xuyên, yêu cầu nhà mạng quản lý đoạn đó tìm nguyên nhân. - Trên đồ thị: Tăng như bậc thang từ một thời điểm (RTT, tuyến đường traceroute) - Chỗ cần xem: So sánh tuyến đường traceroute, mtr trước và sau thời điểm RTT thay đổi, và xem lịch sử thay đổi tuyến BGP của dải địa chỉ (prefix) của chúng ta bằng RIPEstat BGPlay - Đúng nếu: đứng hình vài giây rồi RTT chuyển sang một mức khác, cùng thời điểm đó có BGP update và AS path thay đổi - Loại trừ nếu: không có lịch sử thay đổi tuyến mà chỉ tăng vào buổi tối → xem “Nghẽn peering vào giờ cao điểm”; chỉ một số kết nối bị tệ → xem “Một đường ECMP bị lỗi” - Cách kiểm tra: Kiểm tra bằng công cụ hạ tầng (không cần code game) - Sự cố thực tế: cloudflare-2020, meta-2021, cloudflare-dns-2025 - Nguồn: - [RFC 4271: A Border Gateway Protocol 4 (BGP-4)](https://www.rfc-editor.org/rfc/rfc4271) · IETF · Giá trị mặc định khuyến nghị cho hold time của BGP là 90 giây (nếu trong khoảng này không nhận được bản tin nào từ phía bên kia thì phiên bị cắt) - [BGP updates in 2024](https://blog.apnic.net/2025/01/07/bgp-updates-in-2024/) · APNIC · Thời gian trung bình mỗi ngày để tuyến đường bất ổn trở lại ổn định: 25–35 giây (IPv4), 40–50 giây (IPv6) - [Delayed Internet Routing Convergence (SIGCOMM 2000)](https://conferences.sigcomm.org/sigcomm/2000/conf/paper/sigcomm2000-5-2.pdf) · ACM · Sau sự cố tuyến đường, việc hội tụ có thể mất đến vài phút, trong thời gian đó mất gói và độ trễ tăng (số liệu đo năm 2000) - [BGPlay (RIPEstat Data API)](https://stat.ripe.net/docs/data-api/api-endpoints/bgplay) · RIPE NCC · Hiển thị tuyến BGP của dải địa chỉ (prefix) tại thời điểm bắt đầu, các BGP update quan sát được trong khoảng thời gian đó và thông tin các AS trên tuyến đường #### isp-ecmp · Một đường ECMP bị lỗi · ECMP / link bundle member fault Nhà mạng và trung tâm dữ liệu thường có nhiều đường cùng đi tới một đích, và mỗi kết nối được gán cố định vào một đường. Nếu chỉ một đường bị hỏng, chỉ những người được gán vào đường đó liên tục bị lag. - Vì sao → Dẫn đến → Trên màn hình: Trong một đoạn gộp nhiều đường truyền, một đường truyền hoặc một thiết bị bị lỗi hay bị nghẽn → Đường đi được chọn theo tổ hợp địa chỉ và cổng (hash), nên chỉ những kết nối được gán vào đường đó bị mất gói và trễ → Cùng khu vực, cùng nhà mạng nhưng chỉ một số người liên tục bị dịch chuyển tức thời. Đôi khi kết nối lại là hết - Triệu chứng: Dịch chuyển tức thời, Kéo ngược, Giật khựng / Yếu tố: Mất gói, Jitter - Ai gặp: Chỉ mình tôi, Một khu vực hoặc nhà mạng / Khi nào: Luôn luôn - Phụ trách chính: Hạ tầng mạng (Đội hạ tầng) / Phối hợp: Phát triển server (Đội phát triển game), Bên ngoài (Bên ngoài) - Việc cần làm (Đội phát triển game): Ghi thống kê mất gói và truyền lại theo từng kết nối để có thể trích ra IP, cổng và thời điểm của người bị ảnh hưởng (TCP thì dùng số lần truyền lại trong TCP_INFO, UDP thì tính từ số thứ tự của các gói bị thiếu). - Việc cần làm (Đội hạ tầng): Thu thập IP, cổng và thời điểm của người bị ảnh hưởng rồi gửi cho nhà mạng hoặc trung tâm dữ liệu, giám sát mất gói theo từng đường, đo tuyến đường bằng đúng giao thức và cổng của game (mtr --tcp hoặc --udp cùng --port), nếu đường đó nằm trên thiết bị của chúng ta thì gỡ đường truyền hoặc thiết bị lỗi ra khỏi nhóm. - Việc cần làm (Bên ngoài): Yêu cầu nhà mạng kiểm tra và thay đường bị lỗi, hướng dẫn người chơi tạm tránh bằng cách kết nối lại (trường hợp kết nối lại làm đổi cổng). - Con số tham khảo: Nếu có 4 đường, chỉ khoảng 1/4 người chơi bị ảnh hưởng. Phép đo ping có thể đi theo đường khác với game nên vẫn cho kết quả bình thường. - Trên đồ thị: Chỉ một phần cao (Mất gói, truyền lại theo kết nối (theo IP, cổng)) - Chỗ cần xem: Chia mất gói và truyền lại theo kết nối theo IP và cổng nguồn; chạy mtr ở chế độ UDP (-u) tới cổng game (-P) với cổng nguồn cố định (-L), rồi lặp lại nhiều lần với các cổng nguồn khác nhau. Nếu chỉ dùng -P mà không có -L, cổng nguồn đổi theo từng lần gửi và kết quả của nhiều đường bị trộn lẫn - Đúng nếu: trong cùng khu vực và nhà mạng, chỉ một số tổ hợp cổng nguồn (hoặc địa chỉ) nhất định liên tục mất gói, và khi kết nối lại làm đổi cổng thì hết - Loại trừ nếu: đổi cổng nào cũng tệ như nhau → nghẽn hoặc sự cố trên toàn bộ một đoạn mạng - Cách kiểm tra: Kiểm tra bằng công cụ hạ tầng (không cần code game) - Tìm hiểu thêm: Để gói tin của một kết nối không bị đảo thứ tự, các thiết bị dùng ECMP và LAG cố định đường đi cho từng kết nối theo giá trị tính từ địa chỉ và cổng (tùy cấu hình thiết bị, có thể chỉ tính từ địa chỉ). Ở nơi chỉ dùng địa chỉ, kết nối lại vẫn đi cùng đường nên không cải thiện. Vì vậy khi các báo cáo kiểu “ping bình thường mà game lại lag” và “kết nối lại thì hết” đến cùng lúc, hãy nghi ngờ nguyên nhân này. - Nguồn: - [RFC 7424: Mechanisms for Optimizing Link Aggregation Group (LAG) and Equal-Cost Multipath (ECMP) Component Link Utilization in Networks](https://www.rfc-editor.org/rfc/rfc7424) · IETF · LAG và ECMP chọn một đường truyền cho mỗi luồng dựa trên hash của các trường header để giữ thứ tự gói (nhiều luồng ánh xạ vào một đường truyền) - [RFC 2991: Multipath Issues in Unicast and Multicast Next-Hop Selection](https://www.rfc-editor.org/rfc/rfc2991) · IETF · Tiêu chí phân biệt luồng khác nhau tùy cách hiện thực (chỉ địa chỉ đích, cặp địa chỉ, hoặc cả cổng); khi có nhiều đường, kết quả ping, traceroute khó tin cậy - [mtr(8) manual page source](https://raw.githubusercontent.com/traviscross/mtr/master/man/mtr.8.in) · mtr · Các tùy chọn -u (UDP), -P (cổng đích), -L (cổng nguồn UDP); nếu chỉ dùng -P, số thứ tự lần gửi được đưa vào cổng nguồn nên cổng nguồn đổi theo từng lần gửi #### isp-shaping · Nhà mạng giới hạn tốc độ và quản lý lưu lượng · Traffic shaping, data caps Khi dùng vượt dung lượng data, hoặc với gói cước có quản lý một số loại lưu lượng, gói tin bị làm chậm hoặc bị bỏ. - Vì sao → Dẫn đến → Trên màn hình: Bị giới hạn tốc độ sau khi dùng hết data của gói cước, hoặc một số loại lưu lượng bị hạn chế → Gói tin phải chờ hoặc bị bỏ → Lag sau khi dùng đến một mức nhất định, nhất là trên di động - Triệu chứng: Trễ thao tác, Dịch chuyển tức thời / Yếu tố: Độ trễ, Mất gói - Ai gặp: Chỉ mình tôi, Một khu vực hoặc nhà mạng / Khi nào: Luôn luôn, Giờ cao điểm buổi tối - Phụ trách chính: Bên ngoài (Bên ngoài) / Phối hợp: Phát triển server (Đội phát triển game), Hạ tầng mạng (Đội hạ tầng) - Việc cần làm (Đội phát triển game): Giảm lưu lượng của game (nén, chỉ gửi những gì cần). - Việc cần làm (Đội hạ tầng): Nếu lưu lượng game chỉ bị làm chậm hoặc bị bỏ ở một nhà mạng nhất định, thu thập dữ liệu rồi escalate lên nhà mạng đó. - Việc cần làm (Bên ngoài): Hướng dẫn người chơi kiểm tra xem đã dùng hết data hay đang bị giới hạn tốc độ không và có ứng dụng khác trên cùng điện thoại đang dùng mạng không, yêu cầu nhà mạng xác nhận có hạn chế lưu lượng game hay không. - Con số tham khảo: Ở Hàn Quốc, gói cước di động khi dùng hết data thường bị giới hạn còn 1–5 Mbps, gói cước rẻ còn vài trăm kbps. Bản thân game dùng ít data, nhưng nếu ứng dụng khác trên cùng điện thoại dùng mạng thì hàng đợi sẽ hình thành trước thiết bị giới hạn tốc độ. - Trên đồ thị: Chạm giới hạn rồi đi ngang (Thông lượng, RTT) - Chỗ cần xem: Nhờ người chơi xem data còn lại và tình trạng giới hạn tốc độ trong ứng dụng của nhà mạng, rồi đo tốc độ để biết tốc độ tối đa. Phía server thì so sánh mất gói và RTT theo nhà mạng - Đúng nếu: thông lượng dừng ở một mức như 1–5 Mbps hay vài trăm kbps và không tăng thêm, từ lúc đó hễ ứng dụng khác trên cùng điện thoại dùng mạng là RTT và mất gói tăng. Nạp thêm data hoặc chuyển sang Wi-Fi thì hết - Loại trừ nếu: không bị giới hạn tốc độ nhưng chỉ một nhà mạng bị tệ → xem “Nghẽn peering vào giờ cao điểm” hoặc “Định tuyến đi đường vòng” - Cách kiểm tra: Kiểm tra ở môi trường phía người chơi - Nguồn: - [SKT, 고객 선택권과 혜택 강화한 신규 5G 요금제 출시](https://news.sktelecom.com/180213) · SK텔레콤 · Ví dụ về kiểm soát tốc độ sau khi dùng hết data cơ bản của gói cước 5G: tối đa 400 kbps, 1 Mbps, 3 Mbps - [SKT, 요금제 개편](https://news.sktelecom.com/225487) · SK텔레콤 · Dùng hết data được cấp vẫn tiếp tục dùng được với tốc độ tối đa 400 kbps (chương trình “data an tâm cho toàn dân”) #### isp-udp-block · Hạn chế UDP và kiểm tra gói tin ở cấp quốc gia hoặc nhà mạng · UDP blocking, throttling and inspection by networks Một số mạng chặn một số địa chỉ và cổng UDP nhất định hoặc giới hạn tốc độ UDP, còn thiết bị kiểm tra gói tin thì lọc bỏ các giao thức mà nó không nhận ra. Game giao tiếp bằng UDP sẽ không kết nối được hoặc hay bị mất kết nối trên các mạng đó. - Vì sao → Dẫn đến → Trên màn hình: Kết nối từ mạng của một số nhà mạng có giới hạn tốc độ UDP, hoặc từ mạng có thiết bị kiểm tra lưu lượng (kiểm duyệt) ở cấp quốc gia hoặc nhà mạng → Chặn một số địa chỉ và cổng UDP nhất định, giới hạn tốc độ UDP vào giờ đông, lọc bỏ các cổng và giao thức không có trong danh sách cho phép, hoặc chỉ cho vài gói đầu tiên đi qua rồi chặn → Chỉ người chơi ở một số quốc gia hoặc nhà mạng nhất định gặp tình trạng không vào được·kẹt loading, vào được rồi lại sớm mất kết nối, hoặc dịch chuyển tức thời do mất gói vào giờ đông - Triệu chứng: Không vào được·kẹt loading, Mất kết nối, Dịch chuyển tức thời / Yếu tố: Mất gói - Ai gặp: Một khu vực hoặc nhà mạng / Khi nào: Ngay sau đăng nhập hoặc bảo trì, Luôn luôn, Giờ cao điểm buổi tối - Phụ trách chính: Bên ngoài (Bên ngoài) / Phối hợp: Phát triển client (Đội phát triển game), Phát triển server (Đội phát triển game), Hạ tầng mạng (Đội hạ tầng) - Việc cần làm (Đội phát triển game): Client: nếu UDP không kết nối được trong vài giây thì tự động chuyển sang đường dự phòng TCP 443 (TLS), phát hiện cả trường hợp lúc đầu kết nối được rồi sớm bị ngắt để thử lại bằng đường dự phòng, ghi log kết nối qua đường nào. Server: nhận cùng giao thức game qua cả TCP 443 (TLS), điều chỉnh timeout vì đường dự phòng có thể làm tăng độ trễ. - Việc cần làm (Đội hạ tầng): Trước khi mở thêm quốc gia ở nước ngoài, đo khả năng UDP tới được và mất gói giờ cao điểm trên mạng của các nhà mạng tại chỗ, đặt relay hoặc gateway nhận đường dự phòng TCP 443 gần khu vực đó, giám sát tỷ lệ kết nối thành công của UDP và TCP theo quốc gia, ASN, với nhà mạng đã xác nhận giới hạn tốc độ UDP thì thu thập dữ liệu rồi escalate. - Việc cần làm (Bên ngoài): Hỏi nhà mạng hoặc cơ quan liên quan về tiêu chí hạn chế UDP và khả năng nới lỏng, hướng dẫn người chơi thử kết nối từ mạng khác để so sánh. - Con số tham khảo: Theo các phép đo được một tài liệu IETF trích dẫn, 3–5% mạng chặn hoàn toàn UDP. Khi Google xem lại kết quả sử dụng QUIC (chạy trên UDP) năm 2016, Google thấy 4,4% client không dùng được vì UDP hoặc QUIC bị chặn hoặc path MTU quá nhỏ, phần lớn ở sau tường lửa doanh nghiệp, và không thấy trường hợp nào cả một nhà mạng chặn. 0,3% nằm trong những mạng mà mất gói tăng mạnh vào giờ cao điểm, có vẻ do giới hạn tốc độ UDP; con số này đã giảm từ 1% năm 2015 nhờ đề nghị các nhà mạng xử lý. - Trên đồ thị: Chỉ một phần cao (Tỷ lệ kết nối UDP thành công (theo quốc gia, ASN)) - Chỗ cần xem: Tách tỷ lệ kết nối UDP thành công và tỷ lệ thành công của đường dự phòng TCP 443 theo quốc gia, ASN. Từ VM cloud hoặc PC của người chơi trong mạng của nhà mạng đó, thử kết nối riêng tới cổng UDP của game và tới TCP 443, rồi so sánh mtr -u -P (cổng game) với mtr -T -P 443 để xem phản hồi biến mất từ chặng nào - Đúng nếu: chỉ ở một quốc gia hoặc ASN nhất định, UDP không có phản hồi đầu tiên hoặc bị ngắt sau vài giây, trong khi TCP 443 từ cùng nơi đó vẫn bình thường. Nếu là giới hạn tốc độ thì mất gói UDP chỉ tăng rõ vào giờ cao điểm, còn TCP ít bị ảnh hưởng hơn - Loại trừ nếu: TCP cũng thất bại → sự cố tuyến đường, bị chặn IP, hoặc xem “DNS lỗi hoặc chậm”; mọi quốc gia đều như nhau → cấu hình server hoặc tường lửa của chúng ta; chỉ mất gói khi lượng truyền tức thời lớn, bất kể UDP hay TCP → xem “Policer bỏ phần vượt mức” - Cách kiểm tra: Kiểm tra bằng công cụ hạ tầng (không cần code game) - Tìm hiểu thêm: Theo tài liệu khảo sát của IRTF, thiết bị kiểm tra gói tin có thể chọn và chặn luồng UDP theo địa chỉ, cổng, giao thức, hoặc chặn mọi thứ ngoài các giao thức được cho phép (danh sách cho phép). Nếu thiết bị chỉ nhìn vài trường của gói tin để quyết định, chỉ cần giao thức thay đổi một chút là có thể bị chặn. Thời kỳ đầu của QUIC, có một tường lửa sau khi 1 bit trong header thay đổi thì cho vài gói đầu đi qua rồi chặn các gói sau, khiến logic chuyển sang TCP của client không hoạt động được. Khi mở thêm quốc gia ở nước ngoài, vấn đề này có thể lộ ra qua các báo cáo kiểu “trong nước thì ổn, nhưng một số nhà mạng ở nước đó không vào được”. Nếu chỉ bị chặn trên mạng của một địa điểm như quán cà phê hay công ty, hãy xem mục “Giới hạn của Wi-Fi công cộng và mạng công ty”. - Nguồn: - [RFC 9308: Applicability of the QUIC Transport Protocol](https://www.rfc-editor.org/rfc/rfc9308) · IETF · Theo các nghiên cứu đo đạc, 3–5% mạng chặn hoàn toàn UDP nên ứng dụng dựa trên UDP phải chấp nhận kết nối thất bại hoặc có đường dự phòng TCP (TLS); tường lửa có thể chặn các cổng không gắn với dịch vụ đã đăng ký - [The QUIC Transport Protocol: Design and Internet-Scale Deployment (SIGCOMM 2017)](https://static.googleusercontent.com/media/research.google.com/en//pubs/archive/46403.pdf) · ACM · Năm 2016: 4,4% client không dùng được QUIC trên UDP (UDP hoặc QUIC bị chặn hoặc path MTU nhỏ, chủ yếu sau tường lửa doanh nghiệp, không ghi nhận trường hợp cả nhà mạng chặn), 0,3% nằm trong mạng có vẻ giới hạn tốc độ UDP (mất gói tăng giờ cao điểm, giảm từ 1% năm 2015 sau khi đề nghị nhà mạng xử lý), trường hợp tường lửa chỉ cho vài gói đầu đi qua sau khi 1 bit header thay đổi rồi chặn phần sau, vô hiệu hóa logic dự phòng TCP - [RFC 9505: A Survey of Worldwide Censorship Techniques](https://www.rfc-editor.org/rfc/rfc9505) · IRTF · Thiết bị kiểm tra trong mạng có thể chọn và chặn luồng TCP, UDP theo địa chỉ, cổng, giao thức (đã ghi nhận việc chặn endpoint UDP với QUIC); cách chặn mọi thứ ngoài giao thức được cho phép dẫn đến chặn quá mức; cách giới hạn tốc độ của một số loại lưu lượng nhất định cũng được dùng - [mtr(8) manual page source](https://raw.githubusercontent.com/traviscross/mtr/master/man/mtr.8.in) · mtr · Gửi UDP bằng -u, TCP SYN bằng -T và đặt cổng đích bằng -P, để đo tuyến đường bằng đúng giao thức và cổng của game #### isp-line · Chất lượng đường truyền kém · Faulty last-mile line / modem Đầu nối tiếp xúc kém, dây cũ hoặc modem trục trặc gây mất gói đều đặn và làm đường truyền bị ngắt theo chu kỳ. - Vì sao → Dẫn đến → Trên màn hình: Cáp hỏng, tiếp xúc kém, modem hoặc thiết bị đầu cuối quang (ONT) trục trặc → Gói tin bị bỏ do lỗi bit; thỉnh thoảng đường truyền bị ngắt vài giây đến khoảng 1 phút để kết nối lại → Mất gói ít nhưng đều đặn, thỉnh thoảng đứng hình vài giây hoặc mất kết nối - Triệu chứng: Dịch chuyển tức thời, Đứng hình, Mất kết nối / Yếu tố: Mất gói - Ai gặp: Cùng một nhà / Khi nào: Thỉnh thoảng bất chợt - Phụ trách chính: Bên ngoài (Bên ngoài) - Việc cần làm (Bên ngoài): Hướng dẫn người chơi kiểm tra xem game khác và cuộc gọi video có bị ngắt không; nếu có, liên hệ nhà mạng yêu cầu kiểm tra đường truyền. - Trên đồ thị: Thỉnh thoảng vọt lên bất chợt (Tỷ lệ mất gói, lịch sử kết nối lại của đường truyền) - Chỗ cần xem: Đo mất gói tới chặng đầu tiên của nhà mạng trong vài phút bằng pathping (hoặc mtr), và xem thời điểm kết nối lại trong lịch sử kết nối Internet (WAN) trên trang quản trị router - Đúng nếu: ngay cả khi đường truyền rảnh vẫn mất gói đều đặn từ chặng đầu tiên của nhà mạng, và thời điểm kết nối lại trong log router trùng với lúc đứng hình, mất kết nối. Game khác và cuộc gọi video cũng bị ngắt cùng lúc - Loại trừ nếu: mất gói bắt đầu từ chặng không dây tới router → xem “Nhiễu và sóng Wi-Fi yếu”; bắt đầu từ chặng xa trong mạng nhà mạng → tuyến đường của nhà mạng - Cách kiểm tra: Kiểm tra ở môi trường phía người chơi - Nguồn: - [RFC 3635: Definitions of Managed Objects for the Ethernet-like Interface Types](https://www.rfc-editor.org/rfc/rfc3635) · IETF · Khung không qua được bước kiểm tra khung (FCS) được đếm là lỗi FCS (dot3StatsFCSErrors) và cộng vào lỗi đầu vào (ifInErrors) - [Understanding and Mitigating Packet Corruption in Data Center Networks (SIGCOMM 2017)](https://www.microsoft.com/en-us/research/wp-content/uploads/2017/06/CorrOpt_SIGCOMM2017.pdf) · ACM · Nguyên nhân chính gây hỏng gói là module quang lỗi, sợi quang bị hư, đầu nối bẩn và lắp đặt sai; tỷ lệ mất gói do hỏng gói giữ đều bất kể mức sử dụng - [pathping](https://learn.microsoft.com/en-us/windows-server/administration/windows-commands/pathping) · Microsoft · Gửi ping tới từng chặng trong một khoảng thời gian, tính tỷ lệ mất gói theo từng router và liên kết để cho thấy mất gói xảy ra ở chặng nào #### isp-dns · DNS lỗi hoặc chậm · DNS failure / slowness Nếu DNS, dịch vụ đổi tên server thành địa chỉ, bị chậm hoặc lỗi thì game không tìm được server đăng nhập hay server cập nhật. - Vì sao → Dẫn đến → Trên màn hình: DNS của nhà mạng gặp sự cố hoặc cấu hình sai → Không tìm được địa chỉ của server đăng nhập, server cập nhật → Bấm nút kết nối rồi chờ rất lâu hoặc không vào được. Người đã vào game vẫn bình thường - Triệu chứng: Không vào được·kẹt loading / Yếu tố: Độ trễ, Mất gói - Ai gặp: Một khu vực hoặc nhà mạng, Chỉ mình tôi / Khi nào: Ngay sau đăng nhập hoặc bảo trì - Phụ trách chính: Bên ngoài (Bên ngoài) / Phối hợp: Phát triển client (Đội phát triển game) - Việc cần làm (Đội phát triển game): Cache địa chỉ (nhớ địa chỉ server của lần kết nối thành công gần nhất), chuẩn bị nhiều DNS (một nơi lỗi thì tra lại bằng DNS khác). - Việc cần làm (Bên ngoài): Hướng dẫn người chơi thử đổi DNS sang nơi khác, chẳng hạn DNS công cộng. - Trên đồ thị: Chỉ một phần cao (Số lần đăng nhập thất bại (theo nhà mạng), thời gian tra DNS) - Chỗ cần xem: Dùng Resolve-DnsName -Server (hoặc nslookup) hỏi tên server đăng nhập lần lượt ở DNS của nhà mạng và DNS công cộng, rồi so sánh thời gian phản hồi và kết quả - Đúng nếu: chỉ DNS của nhà mạng không phản hồi hoặc phản hồi rất lâu, đổi sang DNS công cộng thì vào được ngay. Người chơi đã vào game vẫn bình thường - Loại trừ nếu: DNS nào cũng trả địa chỉ ngay mà vẫn không kết nối được → phía tuyến đường, tường lửa hoặc server - Cách kiểm tra: Kiểm tra ở môi trường phía người chơi - Sự cố thực tế: meta-2021, cloudflare-dns-2025, aws-2025 - Nguồn: - [Cloudflare 1.1.1.1 Incident on July 14, 2025](https://blog.cloudflare.com/cloudflare-1-1-1-1-incident-on-july-14-2025/) · Cloudflare · Khi một resolver DNS công cộng ngừng hoạt động 62 phút, với người dùng không tra được tên thì gần như mọi dịch vụ Internet đều không dùng được - [RFC 8767: Serving Stale Data to Improve DNS Resiliency](https://www.rfc-editor.org/rfc/rfc8767) · IETF · Cách chống chịu sự cố bằng việc tiếp tục dùng bản ghi cache đã hết hạn khi không liên lạc được server DNS có thẩm quyền (serve-stale) - [Resolve-DnsName](https://learn.microsoft.com/en-us/powershell/module/dnsclient/resolve-dnsname) · Microsoft · Tra tên bằng server DNS được chỉ định qua -Server - [nslookup](https://learn.microsoft.com/en-us/windows-server/administration/windows-commands/nslookup) · Microsoft · Lệnh tra tên trực tiếp với server DNS #### isp-ddos-path · Đường truyền dùng chung bị DDoS làm bão hòa · DDoS saturating shared links Các cuộc tấn công quy mô lớn nhắm vào công ty game, hoặc vào nơi khác trong cùng mạng, làm đầy đường truyền dùng chung. - Vì sao → Dẫn đến → Trên màn hình: Lưu lượng tấn công khổng lồ xuất hiện → Cả lưu lượng hợp lệ dùng chung đường truyền cũng bị dồn ứ và bị bỏ → Nhiều người cùng lúc bị dịch chuyển tức thời, mất kết nối hoặc không vào được - Triệu chứng: Dịch chuyển tức thời, Mất kết nối, Không vào được·kẹt loading / Yếu tố: Mất gói, Độ trễ - Ai gặp: Cả server, Một khu vực hoặc nhà mạng / Khi nào: Thỉnh thoảng bất chợt, Khi đông người - Phụ trách chính: Hạ tầng mạng (Đội hạ tầng) / Phối hợp: Bên ngoài (Bên ngoài) - Việc cần làm (Đội hạ tầng): Dùng dịch vụ chống DDoS, chuyển hướng lưu lượng khi bị tấn công, giấu địa chỉ server (đặt sau thiết bị chống DDoS, không để lộ địa chỉ thật). - Việc cần làm (Bên ngoài): Nếu tấn công nhắm vào nơi khác trong cùng mạng, yêu cầu nhà mạng chặn ở đoạn phía trên (upstream). - Trên đồ thị: Chạm giới hạn rồi đi ngang (Lưu lượng nhận của đường truyền (bps, pps), gói bị bỏ ở interface) - Chỗ cần xem: Xem lưu lượng nhận và số gói bị bỏ ở interface của đường truyền và thiết bị của chúng ta, cùng log phát hiện tấn công của dịch vụ chống DDoS, đặt cạnh thời điểm các lần mất kết nối dồn lại - Đúng nếu: lưu lượng nhận bám sát dung lượng đường truyền rồi đi ngang, gói bị bỏ tăng, và cùng lúc người chơi ở nhiều khu vực, nhà mạng đồng loạt bị dịch chuyển tức thời hoặc mất kết nối - Loại trừ nếu: đường truyền còn dư mà chỉ một số nhà mạng bị tệ → nghẽn hoặc vấn đề tuyến đường ở đoạn mạng của nhà mạng - Cách kiểm tra: Kiểm tra bằng công cụ hạ tầng (không cần code game) - Nguồn: - [Infrastructure layer attacks](https://docs.aws.amazon.com/whitepapers/latest/aws-best-practices-ddos-resiliency/infrastructure-layer-attacks.html) · AWS · Các cuộc tấn công quy mô lớn như phản xạ UDP, SYN flood làm tràn dung lượng mạng hoặc giữ chặt tài nguyên của tường lửa, bộ cân bằng tải - [Obfuscating AWS resources (BP1, BP4, BP5)](https://docs.aws.amazon.com/whitepapers/latest/aws-best-practices-ddos-resiliency/obfuscating-aws-resources-bp1-bp4-bp5.html) · AWS · Đặt dịch vụ edge như CloudFront, bộ cân bằng tải trước server gốc để giảm việc bị lộ trực tiếp - [RFC 7999: BLACKHOLE Community](https://www.rfc-editor.org/rfc/rfc7999) · IETF · BLACKHOLE community, dùng BGP để báo nhà mạng lân cận bỏ lưu lượng đi tới một địa chỉ nhất định #### isp-cgnat · IP dùng chung của nhà mạng (CGNAT) · Carrier-grade NAT Mạng di động và một số nhà mạng cho nhiều thuê bao dùng chung một IP, và xóa ánh xạ (mapping) của kết nối nhàn rỗi sau một thời gian ngắn. - Vì sao → Dẫn đến → Trên màn hình: Thiết bị của nhà mạng quản lý bảng phiên của rất nhiều thuê bao → Giới hạn bảng phiên, idle timeout ngắn → Để yên một lúc thì mất kết nối; những người dùng chung IP bị chặn cùng lúc do phát hiện nhầm - Triệu chứng: Mất kết nối, Không vào được·kẹt loading / Yếu tố: Mất gói - Ai gặp: Một khu vực hoặc nhà mạng / Khi nào: Sau khi để yên một lúc - Phụ trách chính: Phát triển client (Đội phát triển game) / Phối hợp: Phát triển server (Đội phát triển game), Hạ tầng mạng (Đội hạ tầng) - Việc cần làm (Đội phát triển game): Client: gửi heartbeat từ client với khoảng cách không quá một nửa idle timeout ngắn nhất (mạng di động có khi chỉ khoảng 30 giây), vì ánh xạ CGNAT của nhà mạng chỉ chắc chắn được làm mới bởi gói đi từ trong ra, mất kết nối thì tự động kết nối lại. Server: phản hồi heartbeat, nếu một thời gian không nhận được thì chủ động dọn kết nối trước, dùng session token để nối tiếp đúng người chơi đó dù ánh xạ đổi làm địa chỉ và cổng khác đi, thận trọng với chính sách chặn theo IP vì nhiều người có thể dùng chung một IP (xét kèm tiêu chí tài khoản, thiết bị). - Việc cần làm (Đội hạ tầng): Điều chỉnh giới hạn số kết nối trên mỗi IP và số kết nối mới mỗi giây của tường lửa, thiết bị chống DDoS cho phù hợp với IP dùng chung của nhà mạng (nâng ngưỡng hoặc miễn trừ cho dải địa chỉ của nhà mạng di động). - Con số tham khảo: Idle timeout UDP của mạng di động có khi chỉ khoảng 30 giây. - Trên đồ thị: Rớt kết nối hàng loạt (Số lần mất kết nối (heartbeat timeout), thời gian nhàn rỗi trước khi bị ngắt (theo nhà mạng)) - Chỗ cần xem: Trong log kết nối, xem số tài khoản cùng kết nối từ một IP và nhà mạng (ASN), rồi gom theo nhà mạng thời gian nhàn rỗi của các kết nối bị ngắt sau khi nhàn rỗi. Phía người chơi thì xem địa chỉ Internet (WAN) trên trang quản trị router - Đúng nếu: ở dải địa chỉ của nhà mạng di động, nhiều tài khoản cùng kết nối từ một IP, và thời gian nhàn rỗi trước khi bị ngắt dồn vào mức ngắn khoảng 30–60 giây. Địa chỉ WAN của router nằm trong 100.64.0.0/10 (dải địa chỉ dùng chung cho NAT của nhà mạng) hoặc khác với địa chỉ server nhìn thấy - Loại trừ nếu: dồn vào người dùng router gia đình bất kể nhà mạng → xem “Ánh xạ NAT hết hạn” - Cách kiểm tra: Cần log và chỉ số của server, client game - Nguồn: - [A Multi-perspective Analysis of Carrier-Grade NAT Deployment (IMC 2016)](https://www.icir.org/vern/papers/cgn-imc16.pdf) · ACM · Thời gian giữ ánh xạ UDP của các NAT được đo là 10–200 giây, 74% không quá 1 phút; trung vị của CGN là 65 giây ở mạng di động, 35 giây ở mạng cố định - [RFC 6888: Common Requirements for Carrier-Grade NATs (CGNs)](https://www.rfc-editor.org/rfc/rfc6888) · IETF · CGN phải hỗ trợ giới hạn số cổng ngoài trên mỗi thuê bao và giới hạn tốc độ tạo ánh xạ mới - [RFC 6269: Issues with IP Address Sharing](https://www.rfc-editor.org/rfc/rfc6269) · IETF · Khi nhiều người dùng chung một địa chỉ, việc chặn theo IP (penalty box) chặn luôn cả các thuê bao khác cùng địa chỉ - [RFC 6598: IANA-Reserved IPv4 Prefix for Shared Address Space](https://www.rfc-editor.org/rfc/rfc6598) · IETF · 100.64.0.0/10 là dải địa chỉ dùng chung giữa thiết bị NAT của nhà mạng (CGN) và router của thuê bao #### isp-vpn · Đi qua VPN hoặc phần mềm tăng tốc game · VPN / game accelerator detour Khi bật VPN hoặc phần mềm tăng tốc game, gói tin sẽ đi qua server trung chuyển của công ty đó. Nếu server trung chuyển ở xa hoặc đông, kết nối lại chậm hơn cả khi không bật. - Vì sao → Dẫn đến → Trên màn hình: VPN hoặc phần mềm tăng tốc chuyển toàn bộ gói tin của game qua server trung chuyển → Cộng thêm khoảng cách tới server trung chuyển và tình trạng nghẽn ở đó; header của tunnel còn làm MTU (kích thước gói tối đa gửi được trong một lần) nhỏ đi → Ping tăng và mất gói; bị chặn cùng với những người dùng chung địa chỉ trung chuyển nên không vào được - Triệu chứng: Trễ thao tác, Dịch chuyển tức thời, Không vào được·kẹt loading / Yếu tố: Độ trễ, Mất gói - Ai gặp: Chỉ mình tôi / Khi nào: Luôn luôn, Ngay sau đăng nhập hoặc bảo trì - Phụ trách chính: Bên ngoài (Bên ngoài) / Phối hợp: Hạ tầng mạng (Đội hạ tầng), Phát triển server (Đội phát triển game) - Việc cần làm (Đội phát triển game): Giữ gói UDP ở mức 1.200 byte trở xuống (để không bị phân mảnh dù header của tunnel làm MTU nhỏ đi), khi chặn theo IP thì tính đến địa chỉ trung chuyển dùng chung của VPN và phần mềm tăng tốc, đồng thời xét kèm tiêu chí tài khoản, thiết bị. - Việc cần làm (Đội hạ tầng): Nếu có nhiều người chơi ở nước ngoài, tự đặt điểm kết nối ở gần họ; với nhà mạng có nhiều báo cáo “bật phần mềm tăng tốc thì đỡ hơn”, kiểm tra tuyến đường. - Việc cần làm (Bên ngoài): Hướng dẫn người chơi tắt VPN hoặc phần mềm tăng tốc để so sánh. - Con số tham khảo: Server trung chuyển ở gần thì cộng thêm vài ms, đi vòng qua nước khác thì cộng thêm vài chục ms đến hơn 100 ms. - Trên đồ thị: Chỉ một phần cao (RTT (theo người chơi), nhà cung cấp của IP kết nối) - Chỗ cần xem: Xem ASN của IP kết nối có thuộc nhà cung cấp VPN, phần mềm tăng tốc hoặc hosting không, và nhờ người chơi tắt VPN hoặc phần mềm tăng tốc rồi so sánh ping và traceroute - Đúng nếu: chỉ khi bật VPN hoặc phần mềm tăng tốc thì RTT và mất gói tăng hoặc bị chặn kết nối, và traceroute cho thấy chặng đi qua server trung chuyển - Loại trừ nếu: bật hay tắt đều như nhau → đường truyền hoặc đoạn mạng nhà mạng; bật lên lại tốt hơn → vấn đề ở tuyến đường gốc của nhà mạng (“Định tuyến đi đường vòng”, “Nghẽn peering vào giờ cao điểm”) - Cách kiểm tra: Kiểm tra ở môi trường phía người chơi - Tìm hiểu thêm: Ngược lại, khi tuyến đường của nhà mạng tệ, phần mềm tăng tốc có thể đi theo tuyến tốt hơn và làm ping giảm. Báo cáo “bật phần mềm tăng tốc thì đỡ hơn” là manh mối cho vấn đề tuyến đường của nhà mạng như định tuyến đi đường vòng hay nghẽn buổi tối. - Nguồn: - [RFC 4459: MTU and Fragmentation Issues with In-the-Network Tunneling](https://www.rfc-editor.org/rfc/rfc4459) · IETF · Vấn đề phân mảnh và path MTU phát sinh khi header đóng gói của tunnel giữa mạng làm giảm kích thước gửi được - [RFC 8899: Packetization Layer Path MTU Discovery for Datagram Transports](https://www.rfc-editor.org/rfc/rfc8899) · IETF · Khuyến nghị 1.200 byte làm kích thước an toàn cơ bản (BASE_PLPMTU) cho truyền datagram như UDP - [Azure network round-trip latency statistics](https://learn.microsoft.com/en-us/azure/networking/azure-network-latency) · Microsoft Azure · Mức độ trễ khứ hồi cộng thêm khi đi qua điểm kết nối ở nước khác: Seoul–Tokyo 30 ms, Seoul–Hồng Kông 39 ms, Seoul–Singapore 68 ms ### L5 Thiết bị mạng trung tâm dữ liệu (11 nguyên nhân) #### dc-firewall · Bảng phiên của tường lửa bị đầy · Firewall session table exhaustion Tường lửa ghi mọi kết nối đã cho đi qua vào bảng phiên để theo dõi. Khi bảng đầy, tường lửa không nhận thêm được kết nối mới. - Vì sao → Dẫn đến → Trên màn hình: Số phiên chạm giới hạn do kết nối dồn dập hoặc bị tấn công → Không còn mục trống để ghi kết nối mới nên kết nối bị từ chối → Người đang cố vào game thì không vào được·kẹt loading, một số kết nối hiện có cũng bị mất kết nối - Triệu chứng: Không vào được·kẹt loading, Mất kết nối / Yếu tố: Mất gói - Ai gặp: Cả server / Khi nào: Ngay sau đăng nhập hoặc bảo trì, Khi đông người - Phụ trách chính: Hạ tầng mạng (Đội hạ tầng) / Phối hợp: Phát triển server (Đội phát triển game), Phát triển client (Đội phát triển game) - Việc cần làm (Đội phát triển game): Server: dùng hệ thống hàng chờ đăng nhập để điều tiết lượng kết nối dồn đến cùng lúc, tái sử dụng kết nối thay vì mở kết nối ngắn lặp đi lặp lại, chủ động dọn các kết nối đã mất heartbeat (để kết nối chết không chiếm bảng phiên quá lâu). Client: gửi heartbeat với khoảng cách không quá một nửa idle timeout ngắn nhất, mất kết nối thì tự động kết nối lại nhưng tăng dần khoảng chờ giữa các lần thử và rải ngẫu nhiên (để không dồn lại cùng lúc). - Việc cần làm (Đội hạ tầng): Tăng kích thước bảng phiên, dọn nhanh các kết nối kết thúc sớm (rút ngắn timeout của phiên đã đóng), khi giảm idle timeout của phiên thì báo giá trị đó cho đội phát triển game để chỉnh khoảng cách heartbeat, chặn tấn công, cảnh báo theo mức sử dụng số phiên. - Trên đồ thị: Chạm giới hạn rồi đi ngang (Số phiên của tường lửa, số kết nối mới thất bại) - Chỗ cần xem: Vẽ số phiên đồng thời của tường lửa cùng với giới hạn phiên, và tìm trong log thiết bị các bản ghi bỏ gói vì không tạo được phiên. Với tường lửa Linux, so sánh nf_conntrack_count với nf_conntrack_max và tìm “nf_conntrack: table full, dropping packet” trong dmesg; với instance AWS, xem conntrack_allowance_exceeded trong ethtool -S - Đúng nếu: từ lúc số phiên đi ngang ở mức giới hạn, số kết nối mới thất bại tăng, đồng thời bản ghi tạo phiên thất bại hoặc bộ đếm gói bị bỏ cũng tăng - Loại trừ nếu: số phiên còn cách xa giới hạn mà vẫn không vào được → xem “Tràn hàng đợi kết nối (backlog)” hoặc phía server đăng nhập; chỉ kết nối nhàn rỗi bị ngắt → xem “Hết hạn theo dõi kết nối của security group trên cloud” - Cách kiểm tra: Kiểm tra bằng công cụ hạ tầng (không cần code game) - Nguồn: - [Netfilter Conntrack Sysfs variables](https://docs.kernel.org/networking/nf_conntrack-sysctl.html) · Linux kernel · Số mục tối đa của bảng theo dõi kết nối (nf_conntrack_max), thời gian giữ kết nối đang đóng (TIME_WAIT, FIN_WAIT mặc định 120 giây), TCP đã thiết lập mặc định 5 ngày, số mục hiện tại (nf_conntrack_count) - [Amazon EC2 security group connection tracking](https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/security-group-connection-tracking.html) · AWS · Vượt số kết nối có thể theo dõi trên mỗi instance thì gói của kết nối mới bị bỏ; kết nối nhàn rỗi có thể làm cạn bảng theo dõi - [Infrastructure layer attacks](https://docs.aws.amazon.com/whitepapers/latest/aws-best-practices-ddos-resiliency/infrastructure-layer-attacks.html) · AWS · Các cuộc tấn công như SYN flood giữ chặt tài nguyên của server, tường lửa, bộ cân bằng tải - [net/netfilter/nf_conntrack_core.c (Linux v6.12)](https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/net/netfilter/nf_conntrack_core.c?h=v6.12) · Linux kernel · Khi bảng theo dõi kết nối đầy, kernel ghi “nf_conntrack: table full, dropping packet” và bỏ gói của kết nối mới - [Monitor network performance for ENA settings on your EC2 instance](https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/monitoring-network-performance-ena.html) · AWS · conntrack_allowance_exceeded: số gói bị bỏ do vượt giới hạn theo dõi kết nối của instance, xem bằng ethtool -S #### dc-ddos · Đi qua hệ thống chống DDoS, chặn nhầm · DDoS scrubbing latency, false positives Để chặn tấn công, lưu lượng được chuyển qua trung tâm lọc DDoS nên tuyến đường dài ra, và đôi khi người chơi bình thường bị nhầm là kẻ tấn công và bị chặn. - Vì sao → Dẫn đến → Trên màn hình: Sau khi phát hiện tấn công (hoặc thường trực), lưu lượng đi vào được chuyển vòng qua trung tâm lọc DDoS → Tuyến đường dài ra, một số gói hợp lệ bị xác định là tấn công → Ping chung tăng, chỉ một số khu vực hoặc nhà mạng không vào được - Triệu chứng: Trễ thao tác, Không vào được·kẹt loading, Dịch chuyển tức thời / Yếu tố: Độ trễ, Mất gói - Ai gặp: Cả server, Một khu vực hoặc nhà mạng / Khi nào: Khi đông người, Thỉnh thoảng bất chợt - Phụ trách chính: Hạ tầng mạng (Đội hạ tầng) / Phối hợp: Phát triển server (Đội phát triển game) - Việc cần làm (Đội phát triển game): Tổng hợp đặc điểm lưu lượng game (cổng, kích thước gói, số gói mỗi giây) để chia sẻ với đội hạ tầng, giữ gói UDP ở mức 1.200 byte trở xuống. - Việc cần làm (Đội hạ tầng): Quy tắc chống DDoS phù hợp với đặc điểm lưu lượng game, điểm lọc DDoS theo khu vực, giảm kích thước gói TCP ở chặng tunnel (điều chỉnh MSS), kiểm tra chặn nhầm qua tỷ lệ kết nối thất bại theo khu vực, nhà mạng. - Con số tham khảo: Nếu điểm lọc DDoS ở cùng nước thì cộng thêm vài ms, nếu đi qua điểm ở nước khác thì cộng thêm 30–100 ms hoặc hơn. Thông thường chỉ chiều vào đi vòng, còn phản hồi của server đi thẳng ra ngoài. Nếu lưu lượng đã lọc được trả về qua tunnel thì kích thước gửi được trong một lần (MTU) cũng giảm, có thể dẫn đến vấn đề chỉ gói lớn bị mất. - Trên đồ thị: Tăng như bậc thang từ một thời điểm (RTT (ping), tỷ lệ kết nối thất bại theo khu vực, nhà mạng) - Chỗ cần xem: Đặt lịch sử bắt đầu, kết thúc chuyển hướng (lọc DDoS) và log chặn của thiết bị, dịch vụ chống DDoS lên cùng trục thời gian với đồ thị RTT và tỷ lệ kết nối thất bại theo khu vực, nhà mạng. Từ khu vực có vấn đề, dùng mtr, traceroute kiểm tra xem tuyến đường có chen thêm điểm lọc DDoS không - Đúng nếu: RTT tăng một bậc đúng lúc bật chuyển hướng và giữ nguyên, tắt đi thì trở lại. Hoặc log chặn có địa chỉ của người chơi bình thường và chỉ khu vực, nhà mạng đó có tỷ lệ kết nối thất bại tăng - Loại trừ nếu: RTT tăng vào lúc không có lịch sử chuyển hướng hay chặn → xem “Định tuyến đi đường vòng” hoặc “Thay đổi tuyến BGP và hội tụ”; chỉ gói lớn bị mất → xem “MTU không khớp (chỉ mất gói lớn)” - Cách kiểm tra: Kiểm tra bằng công cụ hạ tầng (không cần code game) - Nguồn: - [Maximum transmission unit and maximum segment size](https://developers.cloudflare.com/magic-transit/reference/mtu-mss/) · Cloudflare · Lưu lượng đi vào sau khi lọc được chuyển qua tunnel GRE (MTU 1.476), phản hồi đi ra thì đi thẳng ra Internet (DSR), khuyến nghị giới hạn TCP MSS ở mức 1.436 trở xuống, nếu không chỉnh thì gói lớn bị bỏ hoặc bị phân mảnh - [Azure network round-trip latency statistics](https://learn.microsoft.com/en-us/azure/networking/azure-network-latency) · Microsoft Azure · Mức độ trễ khứ hồi theo vị trí điểm kết nối: Seoul–khu vực Busan 8 ms, Seoul–Tokyo 30 ms, Seoul–Singapore 68 ms #### dc-lb-idle · Idle timeout của bộ cân bằng tải · Load balancer idle timeout Bộ cân bằng tải xóa kết nối nhàn rỗi sau một khoảng thời gian nhất định. Game vẫn coi là kết nối còn đó cho đến lúc bị mất kết nối. - Vì sao → Dẫn đến → Trên màn hình: Người chơi một lúc không gửi gói nào (đang ở cửa sổ chat, rời máy) → Bộ cân bằng tải dọn kết nối nhàn rỗi (giá trị mặc định thường gặp là 60–350 giây) → Mất kết nối ngay khi di chuyển lại - Triệu chứng: Mất kết nối / Yếu tố: Mất gói - Ai gặp: Chỉ mình tôi, Cả server / Khi nào: Sau khi để yên một lúc - Phụ trách chính: Hạ tầng mạng (Đội hạ tầng) / Phối hợp: Phát triển client (Đội phát triển game), Phát triển server (Đội phát triển game) - Việc cần làm (Đội phát triển game): Client: gửi heartbeat với khoảng cách không quá một nửa idle timeout ngắn nhất (nếu đứng sau ALB 60 giây thì tối đa 30 giây), mất kết nối thì tự động kết nối lại. Server: phản hồi heartbeat, nếu một thời gian không nhận được thì chủ động dọn kết nối trước, dùng session token để nối tiếp phiên. - Việc cần làm (Đội hạ tầng): Kiểm tra idle timeout của các bộ cân bằng tải trên tuyến đường rồi chia sẻ với đội phát triển game, tăng lên nếu cần. - Con số tham khảo: Giá trị mặc định: AWS ALB là 60 giây, NLB là 350 giây với TCP và 120 giây với UDP, Azure Load Balancer là 4 phút với TCP. Giá trị TCP của ALB và NLB có thể đổi, nhưng 120 giây UDP của NLB thì không. Khi hết thời gian, ALB đóng cả kết nối phía server, còn NLB xóa âm thầm nên dễ để lại kết nối mà server không hề biết. - Trên đồ thị: Rớt kết nối hàng loạt (Số lần mất kết nối, thời gian nhàn rỗi trước khi bị ngắt) - Chỗ cần xem: Kiểm tra giá trị cấu hình idle timeout của bộ cân bằng tải trên tuyến đường, và gom thời gian từ gói cuối cùng tới lúc bị ngắt của từng kết nối đã mất. Với AWS NLB, xem thêm TCP_ELB_Reset_Count (số RST do bộ cân bằng tải gửi) trên CloudWatch - Đúng nếu: thời gian nhàn rỗi của các kết nối bị ngắt dồn ngay sau giá trị cấu hình (ALB 60 giây, NLB TCP 350 giây…), và tái hiện được khi để yên lâu hơn thời gian đó rồi mới di chuyển. Với NLB, TCP_ELB_Reset_Count tăng vào thời điểm đó - Loại trừ nếu: bị ngắt không liên quan tới thời gian nhàn rỗi → không phải nguyên nhân này; server nhận kết nối trực tiếp không qua bộ cân bằng tải mà vẫn dồn quanh 350 giây → xem “Hết hạn theo dõi kết nối của security group trên cloud”; nằm ở phía router nhà người chơi → xem “Ánh xạ NAT hết hạn” - Cách kiểm tra: Kiểm tra bằng công cụ hạ tầng (không cần code game) - Nguồn: - [Edit attributes for your Application Load Balancer](https://docs.aws.amazon.com/elasticloadbalancing/latest/application/edit-load-balancer-attributes.html) · AWS · Idle timeout của ALB mặc định 60 giây (1–4.000 giây); nếu kết nối phía client hoặc phía target im lặng trong khoảng này, bộ cân bằng tải đóng kết nối - [Network Load Balancers](https://docs.aws.amazon.com/elasticloadbalancing/latest/network/network-load-balancers.html) · AWS · Idle timeout TCP của NLB mặc định 350 giây (60–6.000 giây); quá thời gian thì chỉ ngừng theo dõi, sau đó nếu có dữ liệu đến thì gửi RST; luồng UDP 120 giây không đổi được - [Configure load balancer TCP reset and idle timeout](https://learn.microsoft.com/en-us/azure/load-balancer/load-balancer-tcp-idle-timeout) · Microsoft Azure · Idle timeout của Azure Load Balancer mặc định 4 phút (4–100 phút), quá thời gian thì không bảo đảm giữ phiên, TCP reset là cấu hình tùy chọn - [CloudWatch metrics for your Network Load Balancer](https://docs.aws.amazon.com/elasticloadbalancing/latest/network/load-balancer-cloudwatch-metrics.html) · AWS · TCP_ELB_Reset_Count: số gói RST do bộ cân bằng tải tạo ra và gửi đi #### dc-cloud-conntrack · Hết hạn theo dõi kết nối của security group trên cloud · Cloud security group connection tracking timeout Tường lửa gắn với server cloud (security group) cũng theo dõi kết nối, và mục theo dõi của kết nối nhàn rỗi sẽ hết hạn sau một khoảng thời gian định sẵn. Ngay cả với server nhận kết nối trực tiếp không qua bộ cân bằng tải, người chơi để yên một lúc cũng có thể bị mất kết nối. - Vì sao → Dẫn đến → Trên màn hình: Security group được cấu hình theo cách có theo dõi kết nối game (chỉ cho phép một số địa chỉ, hạn chế quy tắc chiều ra, đi qua NLB…) → Mục theo dõi của kết nối đã nhàn rỗi một lúc bị hết hạn, và các gói đến sau đó bị security group âm thầm bỏ → Rời máy một lúc rồi quay lại di chuyển thì không có phản hồi, sau đó mất kết nối. Chương trình server rất lâu sau mới biết - Triệu chứng: Mất kết nối / Yếu tố: Mất gói - Ai gặp: Chỉ mình tôi, Cả server / Khi nào: Sau khi để yên một lúc - Phụ trách chính: Hạ tầng server (Đội hạ tầng) / Phối hợp: Phát triển client (Đội phát triển game), Phát triển server (Đội phát triển game) - Việc cần làm (Đội phát triển game): Client: gửi heartbeat với khoảng cách không quá một nửa idle timeout ngắn nhất (TCP 350 giây thì tối đa 175 giây, UDP stream 180 giây thì tối đa 90 giây), mất kết nối thì tự động kết nối lại. Server: phản hồi heartbeat, nếu một thời gian không nhận được thì chủ động dọn kết nối trước, dùng session token để nối tiếp phiên. - Việc cần làm (Đội hạ tầng): Kiểm tra thời gian theo dõi kết nối của instance (TcpEstablishedTimeout) và tăng nếu cần (UDP tối đa 180 giây nên không tăng được), xem xét cấu hình security group không tạo theo dõi (cổng game cho phép mọi địa chỉ, quy tắc chiều ra cho phép toàn bộ; kết nối qua NLB vẫn bị theo dõi), thử để nhàn rỗi khi chuyển sang instance thế hệ mới. - Con số tham khảo: Theo AWS, các loại instance Nitro v6 mặc định xóa mục theo dõi của kết nối TCP nhàn rỗi sau 350 giây (các loại khác là 5 ngày). Với UDP, mặc định là 180 giây cho luồng có yêu cầu và phản hồi qua lại nhiều lần (stream), 30 giây cho luồng chỉ đi một chiều hoặc chỉ có một lần yêu cầu và phản hồi. - Trên đồ thị: Rớt kết nối hàng loạt (Số lần mất kết nối, thời gian nhàn rỗi trước khi bị ngắt) - Chỗ cần xem: Kiểm tra cấu hình thời gian theo dõi kết nối của instance và quy tắc security group (có phải cấu hình tạo theo dõi không), rồi gom thời gian nhàn rỗi của các kết nối bị ngắt. Ngay sau khi bị ngắt, dùng ss -tnoi trên server xem kết nối đó có còn ở trạng thái ESTABLISHED, timer truyền lại (timer:(on,…)) đang chạy và backoff tăng dần không - Đúng nếu: thời gian nhàn rỗi của các kết nối bị ngắt dồn ngay sau mức TCP 350 giây, UDP stream 180 giây, UDP một chiều 30 giây, và socket phía server vẫn ở ESTABLISHED mà không phát hiện kết nối đã đứt (nếu server có dữ liệu cần gửi thì chỉ lặp lại truyền lại) - Loại trừ nếu: security group có cấu hình không theo dõi (cổng game cho phép mọi địa chỉ, quy tắc chiều ra cho phép toàn bộ, không đi qua NLB) → không phải nguyên nhân này; có đi qua NLB → so sánh giá trị với “Idle timeout của bộ cân bằng tải” - Cách kiểm tra: Kiểm tra bằng công cụ hạ tầng (không cần code game) - Nguồn: - [Amazon EC2 security group connection tracking](https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/security-group-connection-tracking.html) · AWS · Theo dõi TCP nhàn rỗi mặc định 350 giây (Nitro v6, loại khác 432.000 giây = 5 ngày), UDP một chiều 30 giây, stream 180 giây (tối đa 180); quy tắc cho phép mọi địa chỉ thì không theo dõi; kết nối qua NLB luôn bị theo dõi - [Update the TCP idle timeout for your Network Load Balancer listener](https://docs.aws.amazon.com/elasticloadbalancing/latest/network/update-idle-timeout.html) · AWS · Nếu idle timeout của NLB dài hơn thời gian theo dõi kết nối của instance đích, phía instance sẽ âm thầm bỏ trạng thái kết nối trước - [ss(8) — Linux manual page](https://man7.org/linux/man-pages/man8/ss.8.html) · iproute2 · timer:(on,…) của -o là timer truyền lại, backoff của -i là số lần thời gian chờ truyền lại đã được nhân đôi #### dc-nat-gateway · Giới hạn kết nối và cổng của NAT gateway trên cloud · Cloud NAT gateway connection / port limits Các kết nối từ server trong subnet riêng đi ra ngoài (xác thực nền tảng, thanh toán, API bên ngoài) được NAT gateway đổi địa chỉ và cổng rồi gửi đi. Nếu số kết nối đồng thời tới cùng một đích vượt giới hạn cổng của gateway, kết nối mới sẽ thất bại. - Vì sao → Dẫn đến → Trên màn hình: Các server mở nhiều kết nối ngắn hoặc giữ kết nối lâu tới cùng một địa chỉ bên ngoài như xác thực nền tảng, thanh toán → NAT gateway không cấp thêm được cổng nguồn cho đích đó nên kết nối mới thất bại → Trong game vẫn bình thường, nhưng riêng các tính năng gọi ra ngoài như đăng nhập, thanh toán, phát thưởng thất bại hoặc chậm (không vào được·kẹt loading, nuốt thao tác·rollback) - Triệu chứng: Không vào được·kẹt loading, Nuốt thao tác·rollback / Yếu tố: Mất gói, Độ trễ - Ai gặp: Chỉ một tính năng, Cả server / Khi nào: Ngay sau đăng nhập hoặc bảo trì, Giờ cao điểm buổi tối, Khi đông người - Phụ trách chính: Hạ tầng mạng (Đội hạ tầng) / Phối hợp: Phát triển server (Đội phát triển game) - Việc cần làm (Đội phát triển game): Tái sử dụng kết nối tới API bên ngoài (HTTP keep-alive, connection pool) và không mở kết nối mới cho mỗi yêu cầu, với kết nối nhàn rỗi để lại trong pool thì gửi keepalive với khoảng cách ngắn hơn idle timeout của NAT (AWS 350 giây) hoặc chủ động đóng trước, khi thất bại thì tăng dần khoảng chờ giữa các lần thử lại và rải ngẫu nhiên, ghi tỷ lệ thất bại và độ trễ theo từng lời gọi ra ngoài. - Việc cần làm (Đội hạ tầng): Gắn thêm địa chỉ IP cho NAT gateway (NAT gateway công cộng của AWS mặc định chỉ gắn được tối đa 2 Elastic IP, muốn nhiều hơn phải yêu cầu tăng hạn mức), chia gateway theo availability zone và subnet, đặt cảnh báo cho chỉ số cấp phát cổng thất bại (AWS ErrorPortAllocation, trạng thái Failed của SNAT Connection Count trên Azure, OUT_OF_RESOURCES của dropped_sent_packets_count trên Google Cloud), với Google Cloud NAT thì tăng số cổng tối thiểu cho mỗi VM hoặc dùng cấp phát cổng động. - Con số tham khảo: AWS NAT gateway với một địa chỉ IP mở được tối đa 55.000 kết nối đồng thời tới cùng một đích (IP, cổng, giao thức), và có thể gắn tới 8 IP để tăng thêm. Kết nối im lặng 350 giây bị xóa, sau đó gói gửi qua kết nối này sẽ nhận về RST. Azure NAT Gateway có 64.512 cổng SNAT cho mỗi IP công cộng (tối đa 16 IP). Google Cloud NAT chia 64.512 cổng của mỗi NAT IP cho từng VM, nhưng số cổng tối thiểu mặc định cho mỗi VM là 64 (cấp phát tĩnh), nên với cấu hình mặc định, một VM thường chỉ mở được 64 kết nối đồng thời tới cùng một đích. - Trên đồ thị: Chạm giới hạn rồi đi ngang (Số kết nối đồng thời của NAT gateway, số lần cấp phát cổng thất bại) - Chỗ cần xem: Với AWS, xem các chỉ số NAT gateway trên CloudWatch là ErrorPortAllocation, ActiveConnectionCount, PacketsDropCount (Azure: SNAT Connection Count lọc theo trạng thái Failed và Dropped Packets; Google Cloud: dropped_sent_packets_count với reason OUT_OF_RESOURCES) đặt cạnh thời điểm các lời gọi ra ngoài của server game thất bại - Đúng nếu: vào lúc lời gọi ra ngoài thất bại, ErrorPortAllocation (Azure: SNAT Connection Count ở trạng thái Failed, Google Cloud: gói bị bỏ với OUT_OF_RESOURCES) lớn hơn 0, và các lỗi dồn vào lời gọi tới một hai đích có nhiều kết nối như server xác thực, thanh toán - Loại trừ nếu: cấp phát cổng thất bại bằng 0 nhưng connect của server game thất bại với EADDRNOTAVAIL và TIME_WAIT gần chạm dải cổng tạm → xem “Cạn cổng tạm (ephemeral port) ở kết nối giữa các server”; kết nối được nhưng chỉ phản hồi chậm → xem “Phụ thuộc service bên ngoài” - Cách kiểm tra: Kiểm tra bằng công cụ hạ tầng (không cần code game) - Tìm hiểu thêm: Khác với “Cạn cổng tạm (ephemeral port) ở kết nối giữa các server”, vốn là tình trạng cổng tạm của một server bị cạn, giới hạn này nằm ở NAT gateway và được các server phía sau gateway dùng chung (Google Cloud NAT chia cho từng VM). Nếu TIME_WAIT và dải cổng tạm phía server vẫn còn dư mà chỉ lời gọi ra ngoài thất bại thì là trường hợp này. Cổng của kết nối đã đóng cũng không được dùng lại ngay cho cùng đích (Azure có thời gian cooldown, Google Cloud không dùng được trong thời gian TIME_WAIT), nên càng lặp lại kết nối ngắn thì càng nhanh chạm giới hạn. - Nguồn: - [NAT gateway basics](https://docs.aws.amazon.com/vpc/latest/userguide/nat-gateway-basics.html) · AWS · Mỗi địa chỉ IPv4 mở được 55.000 kết nối đồng thời tới cùng một đích (IP, cổng, giao thức đích), gắn tới 8 IP để tăng thêm (Elastic IP của NAT gateway công cộng mặc định là 2, tăng bằng yêu cầu nâng hạn mức), băng thông tự tăng từ 5 lên 100 Gbps và thông lượng tự tăng từ 1 triệu lên 10 triệu gói mỗi giây, vượt giới hạn đó thì gói bị bỏ - [NAT gateway metrics and dimensions](https://docs.aws.amazon.com/vpc/latest/userguide/metrics-dimensions-nat-gateway.html) · AWS · ErrorPortAllocation: số lần không cấp phát được cổng nguồn (lớn hơn 0 nghĩa là quá nhiều kết nối đồng thời), ActiveConnectionCount, IdleTimeoutCount (kết nối bị dọn do nhàn rỗi 350 giây), PacketsDropCount - [Troubleshoot NAT gateways](https://docs.aws.amazon.com/vpc/latest/userguide/nat-gateway-troubleshooting.html) · AWS · Nhàn rỗi 350 giây thì kết nối hết hạn và gửi tiếp sẽ nhận RST, khuyến nghị keepalive ngắn hơn 350 giây, chạm giới hạn kết nối thì thêm gateway theo availability zone, thêm IP hoặc giảm số kết nối - [Source Network Address Translation (SNAT) with Azure NAT Gateway](https://learn.microsoft.com/en-us/azure/nat-gateway/nat-gateway-snat) · Microsoft Azure · Mỗi IP công cộng có 64.512 cổng SNAT (tối đa 16 IP), mỗi kết nối tới cùng một đích cần một cổng khác nhau, cổng đã đóng phải qua cooldown trước khi dùng lại cho cùng đích - [Metrics and alerts for Azure NAT Gateway](https://learn.microsoft.com/en-us/azure/nat-gateway/nat-metrics) · Microsoft Azure · SNAT Connection Count lọc theo trạng thái Failed lớn hơn 0 thì có khả năng đã cạn cổng SNAT, Dropped Packets - [IP addresses and ports](https://cloud.google.com/nat/docs/ports-and-addresses) · Google Cloud · Mỗi NAT IP có 64.512 cổng riêng cho TCP và riêng cho UDP, số cổng tối thiểu mỗi VM mặc định 64 (cấp phát tĩnh), 32 (cấp phát động), số cổng dành cho VM giới hạn số kết nối đồng thời tới cùng một đích, kết nối đã đóng không dùng được trong thời gian TIME_WAIT - [Logs and metrics](https://cloud.google.com/nat/docs/monitoring) · Google Cloud · reason OUT_OF_RESOURCES của dropped_sent_packets_count: gói bị bỏ vì thiếu NAT IP hoặc cổng #### dc-lb-imbalance · Bộ cân bằng tải phân bổ lệch, health check đánh giá sai · LB imbalance, bad health checks Kết nối dồn vào một server, hoặc người chơi cứ bị gửi tới server đã chết. - Vì sao → Dẫn đến → Trên màn hình: Quy tắc phân bổ không phù hợp, hoặc health check không phản ánh trạng thái thực → Chỉ một server bị quá tải, hoặc người chơi cố kết nối tới server đã chết → Chỉ một số kênh hoặc một số người bị quay chậm, không vào được·kẹt loading - Triệu chứng: Quay chậm, Không vào được·kẹt loading / Yếu tố: Ngưng trệ, Mất gói - Ai gặp: Một địa điểm hoặc kênh / Khi nào: Ngay sau đăng nhập hoặc bảo trì, Khi đông người - Phụ trách chính: Hạ tầng mạng (Đội hạ tầng) / Phối hợp: Phát triển server (Đội phát triển game) - Việc cần làm (Đội phát triển game): Hiện thực health check trả lời yêu cầu kiểm tra của bộ cân bằng tải dựa trên trạng thái game thực (tick có chạy không, kết nối DB), gửi kèm giá trị tải của server. - Việc cần làm (Đội hạ tầng): Đổi health check sang cách xác nhận phản hồi thực của game, phân bổ theo tải server, giám sát chênh lệch số kết nối giữa các server. - Trên đồ thị: Chỉ một phần cao (Số kết nối, mức sử dụng CPU theo server) - Chỗ cần xem: Vẽ chồng số kết nối (ss -s) và mức sử dụng CPU của từng server sau bộ cân bằng tải trên cùng một đồ thị, và so sánh trạng thái health của target trên bộ cân bằng tải (AWS: HealthyHostCount, UnHealthyHostCount trên CloudWatch) với trạng thái thực của server game - Đúng nếu: chỉ một hai server có số kết nối, CPU cao hơn hẳn các server khác, hoặc server đã dừng tick vẫn giữ trạng thái health “bình thường” và tiếp tục nhận kết nối mới - Loại trừ nếu: số kết nối giữa các server đồng đều mà chỉ một kênh chậm → tải bên trong kênh đó (“Quá tải khu vực chạy trên một thread (hotspot)”) - Cách kiểm tra: Kiểm tra bằng công cụ hạ tầng (không cần code game) - Sự cố thực tế: aws-2025 - Nguồn: - [Load Balancing in the Datacenter](https://sre.google/sre-book/load-balancing-datacenter/) · Google · Round robin đơn giản khiến mức dùng CPU giữa các tác vụ chênh nhau tới 2 lần, phân bổ có trọng số trong đó backend gửi kèm thông tin tải trong phản hồi và health check, trạng thái lame duck báo không nhận thêm yêu cầu - [Health checks for Network Load Balancer target groups](https://docs.aws.amazon.com/elasticloadbalancing/latest/network/target-group-health-checks.html) · AWS · Health check mặc định cách 30 giây, lỗi 2 lần thì bị loại; dịch vụ UDP được kiểm tra bằng health check TCP hay HTTP nên khuyến nghị cấu hình sao cho phản ánh trạng thái dịch vụ thực - [CloudWatch metrics for your Network Load Balancer](https://docs.aws.amazon.com/elasticloadbalancing/latest/network/load-balancer-cloudwatch-metrics.html) · AWS · HealthyHostCount, UnHealthyHostCount: số target được xác định là bình thường, bất thường #### dc-microburst · Microburst ở switch · Switch microburst drops Khi nhiều server cùng lúc gửi dồn gói tin tới hàng nghìn người, bộ đệm nhỏ của cổng switch nơi lưu lượng đó hội tụ sẽ tràn trong chưa tới 1 ms. - Vì sao → Dẫn đến → Trên màn hình: World boss xuất hiện, skill diện rộng, hoặc tick của nhiều server trùng đúng một thời điểm nên gửi dồn một lượt → Bộ đệm (vài trăm KB đến vài MB mỗi cổng) ở nơi nhiều cổng dồn vào một cổng, hoặc nơi chuyển từ cổng nhanh sang cổng chậm, bị đầy trong tích tắc → Một phần gói tin bị bỏ, nhiều người cùng lúc bị dịch chuyển tức thời hoặc nuốt skill - Triệu chứng: Dịch chuyển tức thời, Nuốt thao tác·rollback / Yếu tố: Mất gói - Ai gặp: Một địa điểm hoặc kênh / Khi nào: Khi đông người - Phụ trách chính: Phát triển server (Đội phát triển game) / Phối hợp: Hạ tầng mạng (Đội hạ tầng), Hạ tầng server (Đội hạ tầng) - Việc cần làm (Đội phát triển game): Chia đều việc gửi trong một tick (pacing), cho thời điểm bắt đầu tick của các server lệch nhau một chút. - Việc cần làm (Đội hạ tầng): Mạng: dùng switch có bộ đệm lớn, phân tán lưu lượng (bố trí server trải ra nhiều switch và cổng), giám sát bộ đếm gói bị bỏ theo từng cổng switch. Thiết bị server·OS: đặt trần tốc độ gửi cho toàn server (shaper tc của Linux). - Con số tham khảo: Lượng dữ liệu một cổng 10 Gbps đẩy ra được trong 1 ms là khoảng 1,25 MB. Nếu lưu lượng của hai cổng cùng dồn vào một cổng, mỗi 1 ms sẽ tích thêm 1,25 MB. Dù mức sử dụng trung bình trong 1 giây chỉ 10%, ở thang 1 ms vẫn có thể tràn. - Trên đồ thị: Tăng theo số người và tải (Số gói bị bỏ ở chiều ra của cổng switch) - Chỗ cần xem: Thu thập bộ đếm gói bị bỏ ở chiều ra (ifOutDiscards, tùy thiết bị là output drops) của cổng switch nối với server và của cổng nơi lưu lượng đó hội tụ, với khoảng lấy mẫu ngắn nhất có thể, rồi đối chiếu với thời điểm boss xuất hiện, giao tranh lớn. Chỉ nhìn đồ thị mức sử dụng trung bình 1 giây, 1 phút sẽ không thấy được - Đúng nếu: mức sử dụng trung bình thấp nhưng mỗi lần người chơi dồn vào một chỗ thì gói bị bỏ ở chiều ra lại tăng, và lúc đó nhiều người chơi cùng báo bị dịch chuyển tức thời, nuốt skill - Loại trừ nếu: gói bị bỏ tăng đều trong khung giờ có mức sử dụng trung bình cao → xem “Đường truyền trung tâm dữ liệu bị bão hòa”; lỗi đầu vào (CRC) tăng → xem “Cáp hỏng và lỗi cổng” - Cách kiểm tra: Kiểm tra bằng công cụ hạ tầng (không cần code game) - Nguồn: - [High-Resolution Measurement of Data Center Microbursts (IMC 2017)](https://conferences.sigcomm.org/imc/2017/papers/imc17-final60.pdf) · ACM · Hơn 70% burst trong trung tâm dữ liệu kết thúc trong vài chục µs; ngay cả cổng có mức sử dụng trung bình khoảng 9% vẫn bị bỏ gói do burst - [Data Center TCP (DCTCP) (SIGCOMM 2010)](https://conferences.sigcomm.org/sigcomm/2010/papers/sigcomm/p63.pdf) · ACM · Switch phổ thông có bộ đệm nhỏ (48 cổng dùng chung 4 MB, một cổng dùng được tới khoảng 700 KB), nhiều luồng dồn vào một cổng trong khoảnh khắc ngắn sẽ gây mất gói - [RFC 2863: The Interfaces Group MIB](https://www.rfc-editor.org/rfc/rfc2863) · IETF · ifOutDiscards: số gói không có lỗi nhưng vẫn bị bỏ, không gửi ra được, vì các lý do như cần giải phóng bộ đệm #### dc-uplink · Đường truyền trung tâm dữ liệu bị bão hòa · Uplink saturation Nếu việc phân phối bản cập nhật, gửi log, sao lưu dùng chung đường truyền với game thì đường truyền bị đầy. - Vì sao → Dẫn đến → Trên màn hình: Truyền dữ liệu dung lượng lớn chiếm giữ cùng đường truyền → Hàng đợi trên đường truyền và mất gói tăng → Ping toàn server tăng và dịch chuyển tức thời - Triệu chứng: Trễ thao tác, Dịch chuyển tức thời / Yếu tố: Độ trễ, Mất gói - Ai gặp: Cả server / Khi nào: Theo chu kỳ đều, Khi đông người - Phụ trách chính: Hạ tầng mạng (Đội hạ tầng) / Phối hợp: Hạ tầng server (Đội hạ tầng) - Việc cần làm (Đội hạ tầng): Mạng: ưu tiên lưu lượng game (QoS), tách đường truyền cho truyền dữ liệu lớn, cảnh báo mức sử dụng đường truyền. Thiết bị server·OS: giới hạn tốc độ cho sao lưu, gửi log, triển khai và chạy vào giờ thấp điểm. - Trên đồ thị: Chạm giới hạn rồi đi ngang (Mức sử dụng đường truyền, RTT (ping)) - Chỗ cần xem: Đặt mức sử dụng (tính từ SNMP ifHCInOctets, ifHCOutOctets) và số gói bị bỏ ở chiều ra (ifOutDiscards) của interface đường truyền trung tâm dữ liệu (uplink) lên cùng trục thời gian với lịch sao lưu, triển khai, gửi log - Đúng nếu: mức sử dụng đường truyền bám sát giới hạn băng thông rồi đi ngang, đúng lúc đó RTT và gói bị bỏ trên toàn server tăng, và thời điểm này trùng với tác vụ truyền dữ liệu lớn - Loại trừ nếu: mức sử dụng tính theo phút còn cách xa giới hạn mà vẫn có gói bị bỏ → xem “Microburst ở switch” - Cách kiểm tra: Kiểm tra bằng công cụ hạ tầng (không cần code game) - Nguồn: - [RFC 4594: Configuration Guidelines for DiffServ Service Classes](https://www.rfc-editor.org/rfc/rfc4594) · IETF · Chia lưu lượng tương tác thời gian thực như game và truyền dữ liệu lớn như sao lưu vào các lớp dịch vụ khác nhau để xử lý - [RFC 7567: IETF Recommendations Regarding Active Queue Management](https://www.rfc-editor.org/rfc/rfc7567) · IETF · Khi lượng dữ liệu vào thiết bị vượt tốc độ đẩy ra, hàng đợi dồn lên, và hàng đợi quá mức là nguyên nhân chính gây độ trễ - [RFC 2863: The Interfaces Group MIB](https://www.rfc-editor.org/rfc/rfc2863) · IETF · ifHCInOctets, ifHCOutOctets: số byte nhận và gửi qua interface (64 bit), ifOutDiscards: số gói bị bỏ vì không gửi ra được #### dc-failover · Failover thiết bị mạng · Network device failover Khi một router hay tường lửa hỏng và chuyển sang thiết bị dự phòng (failover), mọi người đều bị đứng hình trong vài giây chuyển đổi đó. - Vì sao → Dẫn đến → Trên màn hình: Chuyển sang thiết bị dự phòng do thiết bị hỏng hoặc bảo trì → Chuyển đổi mất vài giây, nếu thông tin phiên không được đồng bộ thì kết nối bị reset → Mọi người chơi của server cùng lúc đứng hình, mất kết nối hàng loạt - Triệu chứng: Đứng hình, Mất kết nối / Yếu tố: Mất gói - Ai gặp: Cả server / Khi nào: Thỉnh thoảng bất chợt - Phụ trách chính: Hạ tầng mạng (Đội hạ tầng) / Phối hợp: Phát triển server (Đội phát triển game), Phát triển client (Đội phát triển game) - Việc cần làm (Đội phát triển game): Server: timeout chịu được gián đoạn ngắn (vài giây), dù bị ngắt thì khi kết nối lại vẫn dùng session token để nối tiếp phiên. Client: mất kết nối thì tự động kết nối lại (rải ngẫu nhiên khoảng chờ giữa các lần thử để không dồn lại cùng lúc). - Việc cần làm (Đội hạ tầng): Dự phòng kép có chia sẻ trạng thái kết nối, phát hiện hỏng trong vòng 1 giây bằng BFD, thử nghiệm chuyển đổi định kỳ. - Con số tham khảo: Nếu thiết bị phát hiện hỏng ngay thì việc chuyển đổi mất khoảng 1–3 giây. Nếu không có cơ chế phát hiện lỗi nhanh (BFD) mà chỉ dựa vào timer mặc định của BGP, tuyến đường có thể bị đứt 90–180 giây cho tới khi thiết bị lân cận phát hiện. - Trên đồ thị: Rớt kết nối hàng loạt (Số kết nối, lưu lượng gửi nhận toàn server) - Chỗ cần xem: Xem log sự kiện của router, tường lửa (thay đổi vai trò VRRP, phiên BFD hay BGP bị down, bản ghi failover) cùng với số kết nối và lưu lượng gửi nhận toàn server tại cùng thời điểm - Đúng nếu: vào thời điểm chuyển đổi trong log thiết bị, lưu lượng của mọi server phía sau thiết bị đó về 0 trong vài giây hoặc số kết nối cùng tụt xuống - Loại trừ nếu: chỉ kết nối của một server tụt → xem “Server crash” hoặc “Lỗi driver hoặc firmware NIC”; log thiết bị sạch và server bị dừng là một máy ảo trên cloud → xem “Bảo trì host cloud và live migration” - Cách kiểm tra: Kiểm tra bằng công cụ hạ tầng (không cần code game) - Nguồn: - [RFC 5880: Bidirectional Forwarding Detection (BFD)](https://www.rfc-editor.org/rfc/rfc5880) · IETF · Cơ chế Hello của giao thức định tuyến cần hơn 1 giây để phát hiện hỏng, nên BFD được tạo ra để phát hiện trong thời gian ngắn hơn - [RFC 7938: Use of BGP for Routing in Large-Scale Data Centers](https://www.rfc-editor.org/rfc/rfc7938) · IETF · Nếu chỉ dựa vào keepalive của BGP thì hội tụ chậm; nếu nhận tín hiệu link down ngay và cắt phiên thì phát hiện ở mức ms và hội tụ lại - [RFC 5798: Virtual Router Redundancy Protocol (VRRP) Version 3 for IPv4 and IPv6](https://www.rfc-editor.org/rfc/rfc5798) · IETF · Bản tin quảng bá VRRP mặc định 1 giây, thiết bị dự phòng tiếp nhận vai trò khi quảng bá bị gián đoạn quá khoảng 3 lần chu kỳ (với cấu hình mặc định là hơn 3 giây một chút) #### dc-bad-cable · Cáp hỏng và lỗi cổng · Bad cable / optics (CRC errors) Nếu module quang hoặc cáp bị lỗi, gói tin đi qua đường đó bị hỏng theo một tỷ lệ nhất định. - Vì sao → Dẫn đến → Trên màn hình: Lỗi bit do module quang hoặc cáp bị lỗi → Gói bị hỏng bị thiết bị âm thầm bỏ → Chỉ một số server hoặc người chơi đi qua đường đó bị mất gói đều đặn, gây dịch chuyển tức thời hoặc kéo ngược - Triệu chứng: Dịch chuyển tức thời, Kéo ngược / Yếu tố: Mất gói - Ai gặp: Một địa điểm hoặc kênh / Khi nào: Luôn luôn - Phụ trách chính: Hạ tầng mạng (Đội hạ tầng) - Việc cần làm (Đội hạ tầng): Giám sát và cảnh báo bộ đếm lỗi cổng (CRC), thay linh kiện như module quang, cáp, gỡ liên kết có vấn đề ra để đi vòng cho đến khi thay xong. - Trên đồ thị: Chỉ một phần cao (Số lỗi CRC theo cổng, tỷ lệ mất gói theo server và tuyến đường) - Chỗ cần xem: Xem bộ đếm CRC ở cả hai đầu liên kết. Với switch: lỗi FCS (dot3StatsFCSErrors) và lỗi đầu vào (ifInErrors) của cổng; với server: crc trong RX errors của ip -s -s link (thống kê kernel rx_crc_errors) - Đúng nếu: lỗi CRC của một cổng tăng đều bất kể lưu lượng hay khung giờ, và chỉ server, người chơi đi qua cổng đó bị mất gói - Loại trừ nếu: không có lỗi CRC mà chỉ gói bị bỏ ở chiều ra tăng → nghẽn (“Microburst ở switch”, “Đường truyền trung tâm dữ liệu bị bão hòa”) - Cách kiểm tra: Kiểm tra bằng công cụ hạ tầng (không cần code game) - Nguồn: - [Understanding and Mitigating Packet Corruption in Data Center Networks (SIGCOMM 2017)](https://www.microsoft.com/en-us/research/wp-content/uploads/2017/06/CorrOpt_SIGCOMM2017.pdf) · ACM · Phân tích 350.000 liên kết trong trung tâm dữ liệu: nguyên nhân hỏng gói là module quang lỗi, sợi quang bị hư, đầu nối bẩn; tỷ lệ hỏng giữ đều bất kể mức sử dụng; gỡ liên kết có vấn đề để sửa trong khi vẫn giữ đủ số đường - [RFC 3635: Definitions of Managed Objects for the Ethernet-like Interface Types](https://www.rfc-editor.org/rfc/rfc3635) · IETF · Bộ đếm lỗi kiểm tra khung FCS (dot3StatsFCSErrors); lỗi này được cộng vào lỗi đầu vào (ifInErrors) - [Interface statistics](https://docs.kernel.org/networking/statistics.html) · Linux kernel · rx_crc_errors: số gói nhận bị lỗi CRC, xem theo từng loại lỗi bằng ip -s -s link #### dc-mtu · MTU không khớp (chỉ mất gói lớn) · MTU black hole Khi MTU (kích thước gửi được trong một lần) của một chặng ở giữa nhỏ đi mà thông báo vượt kích thước lại bị chặn, chỉ các gói lớn liên tục bị mất. - Vì sao → Dẫn đến → Trên màn hình: MTU nhỏ đi ở chặng tunnel hoặc VPN → Thông báo vượt kích thước (ICMP) bị tường lửa chặn nên bên gửi không biết → Chỉ khi mở màn hình lớn như túi đồ, danh sách nhân vật thì đứng hình rồi mất kết nối - Triệu chứng: Đứng hình, Mất kết nối, Không vào được·kẹt loading / Yếu tố: Mất gói - Ai gặp: Một khu vực hoặc nhà mạng, Chỉ mình tôi / Khi nào: Khi làm một thao tác nhất định, Ngay sau đăng nhập hoặc bảo trì - Phụ trách chính: Hạ tầng mạng (Đội hạ tầng) / Phối hợp: Hạ tầng server (Đội hạ tầng), Phát triển server (Đội phát triển game) - Việc cần làm (Đội phát triển game): Nếu muốn giảm trực tiếp từ phía server thì đặt kích thước segment tối đa (TCP_MAXSEG) cho socket, vì chỉ chia nhỏ message trong code game thì không tránh được; giữ gói UDP ở mức 1.200 byte trở xuống. - Việc cần làm (Đội hạ tầng): Mạng: giảm kích thước gói TCP ở chặng tunnel (điều chỉnh MSS), cho phép thông báo vượt kích thước (ICMP) trên tường lửa và network ACL của cloud. Thiết bị server·OS: cho phép thông báo vượt kích thước (ICMP) cả trên tường lửa server và security group của cloud, bật dò MTU của kernel server (tcp_mtu_probing=1), đây là lưới an toàn cuối cùng vì chỉ hoạt động sau khi đã đứng vài giây. - Con số tham khảo: Thường là 1.500 byte, đi qua tunnel thì giảm còn khoảng 1.400. - Trên đồ thị: Chỉ một phần cao (Mất kết nối theo khu vực, nhà mạng, phản hồi lớn thất bại) - Chỗ cần xem: Từ PC của người chơi gặp vấn đề, gửi ping bật cờ không phân mảnh (DF) tới server với nhiều kích thước khác nhau. Windows: ping /f /l 1472 SERVER_IP, Linux: ping -M do -s 1472 SERVER_IP (1.472 là MTU 1.500 trừ 20 byte header IP và 8 byte header ICMP). Giảm dần kích thước để tìm kích thước lớn nhất đi qua được, và kiểm tra security group, tường lửa phía server có cho phép thông báo vượt kích thước ICMP (Fragmentation Needed) không - Đúng nếu: ping nhỏ đi được nhưng ping DF 1.472 byte thất bại (không phản hồi hoặc báo lỗi cần phân mảnh), và kích thước lớn nhất đi qua được nhỏ, khoảng 1.400. Người chơi cùng khu vực chỉ đứng hình khi mở màn hình lớn - Loại trừ nếu: ping DF 1.472 byte vẫn đi tốt → không phải vấn đề path MTU; ping nhỏ cũng không đi → bản thân ICMP bị chặn, cách này không kết luận được - Cách kiểm tra: Kiểm tra ở môi trường phía người chơi - Nguồn: - [RFC 2923: TCP Problems with Path MTU Discovery](https://www.rfc-editor.org/rfc/rfc2923) · IETF · Nếu tường lửa chặn ICMP (Fragmentation Needed), dò path MTU thất bại và chỉ gói lớn liên tục bị mất (black hole); ping và giao tiếp nhỏ vẫn chạy nên khó chẩn đoán - [Maximum transmission unit and maximum segment size](https://developers.cloudflare.com/magic-transit/reference/mtu-mss/) · Cloudflare · Path MTU trên Internet là 1.500, đi qua tunnel GRE thì 1.476, khuyến nghị giới hạn TCP MSS ở mức 1.436 trở xuống - [IP Sysctl](https://docs.kernel.org/networking/ip-sysctl.html) · Linux kernel · tcp_mtu_probing=1 bình thường tắt, chỉ bật dò path MTU của TCP khi phát hiện ICMP black hole - [ping(8) — Linux manual page](https://man7.org/linux/man-pages/man8/ping.8.html) · iputils · -M do bật cờ DF và từ chối gói lớn hơn path MTU, -s chỉ định kích thước dữ liệu (mặc định 56 byte, cộng 8 byte header ICMP) - [ping](https://learn.microsoft.com/en-us/windows-server/administration/windows-commands/ping) · Microsoft · /f bật cờ DF để tìm vấn đề path MTU, /l chỉ định kích thước dữ liệu ### L6 Card mạng server (9 nguyên nhân) #### nic-irq · Ngắt NIC dồn vào một core · Single-queue NIC / no RSS Nếu NIC chỉ gửi ngắt báo gói tin đến cho một core CPU, core đó sẽ thành điểm nghẽn. - Vì sao → Dẫn đến → Trên màn hình: Chỉ có một hàng đợi nhận, hoặc RSS (phân tán ra nhiều core) đang tắt → Một core lên 100% nên không lấy gói tin ra kịp → Khi đông người, toàn server bị mất gói và trễ (dịch chuyển tức thời, trễ thao tác) - Triệu chứng: Dịch chuyển tức thời, Kéo ngược, Trễ thao tác / Yếu tố: Mất gói, Độ trễ - Ai gặp: Cả server / Khi nào: Khi đông người - Phụ trách chính: Hạ tầng server (Đội hạ tầng) - Việc cần làm (Đội hạ tầng): Cấu hình RSS (NIC phân tán), RPS (kernel phân tán), phân tán ngắt ra nhiều core, với UDP thì cấu hình để chia hàng đợi dựa cả trên cổng (rx-flow-hash udp4 sdfn của ethtool -N), tách core xử lý ngắt khỏi core chạy thread tick của game, giám sát %soft theo từng core. - Con số tham khảo: Lượng một core xử lý được qua kernel vào khoảng vài trăm nghìn gói mỗi giây, tùy kích thước gói và cấu hình. Nếu xem mức sử dụng theo từng core mà phần xử lý nhận (%soft của mpstat) chỉ dồn vào một core thì là trường hợp này. - Trên đồ thị: Chạm giới hạn rồi đi ngang (%soft theo core, số gói nhận mỗi giây) - Chỗ cần xem: Dùng mpstat -P ALL 1 xem %soft (tỷ lệ xử lý ngắt mềm) của từng core, xem /proc/interrupts để biết ngắt của từng hàng đợi NIC đi tới core nào, ethtool -l để xem số hàng đợi, ethtool -S để xem số gói theo từng hàng đợi (tên khác nhau tùy driver) - Đúng nếu: chỉ một core có %soft bám sát 100%, các core còn lại rảnh, ngắt và gói tin dồn vào một hàng đợi. Từ lúc đó số gói nhận mỗi giây không tăng thêm được - Loại trừ nếu: %soft trải đều trên nhiều core → không phải nguyên nhân này; CPU rảnh mà vẫn mất gói → xem “Vượt giới hạn PPS trên cloud” hoặc “Ring buffer không đủ” - Cách kiểm tra: Kiểm tra bằng công cụ hạ tầng (không cần code game) - Tìm hiểu thêm: Dù có nhiều hàng đợi, nếu phần lớn lưu lượng đến từ vài địa chỉ như gateway hay proxy thì vẫn dồn vào một hàng đợi. Với UDP, cấu hình mặc định của NIC đôi khi chỉ dựa vào địa chỉ để chia hàng đợi, phải đổi sang dựa cả trên cổng thì mới phân tán đều. - Nguồn: - [Scaling in the Linux Networking Stack](https://docs.kernel.org/networking/scaling.html) · Linux kernel · RSS (NIC phân tán vào nhiều hàng đợi nhận), RPS (kernel phân tán), cấu hình cho mỗi hàng đợi một ngắt riêng và chia ra nhiều core, khuyến nghị RSS nếu xử lý ngắt nhận là điểm nghẽn - [How to receive a million packets per second](https://blog.cloudflare.com/how-to-receive-a-million-packets/) · Cloudflare · Số đo cho thấy khi một hàng đợi nhận chỉ đi tới một core, core đó nghẽn ở khoảng 350.000–430.000 gói mỗi giây; trường hợp NIC hash UDP chỉ theo địa chỉ IP nên dồn vào một hàng đợi - [ethtool(8) — Linux manual page](https://man7.org/linux/man-pages/man8/ethtool.8.html) · ethtool · Tùy chọn ethtool -N rx-flow-hash udp4 để đưa cả cổng (f, n) vào hash UDP - [mpstat(1) — Linux manual page](https://man7.org/linux/man-pages/man1/mpstat.1.html) · sysstat · %soft: tỷ lệ thời gian CPU dùng để xử lý ngắt mềm, -P ALL để xem theo từng core #### nic-ring · Ring buffer không đủ · RX ring buffer overflow Nếu ring buffer, nơi NIC tạm giữ gói tin, quá nhỏ thì khi gói dồn đến trong tích tắc, bộ đệm sẽ tràn và gói bị bỏ. - Vì sao → Dẫn đến → Trên màn hình: Ring buffer để ở giá trị mặc định nhỏ (256–2.048 slot tùy driver) → Khi có burst, bộ đệm tràn trước khi CPU kịp lấy gói ra → Chỉ mất gói vào lúc burst (dịch chuyển tức thời, nuốt skill). Log của server game không để lại dấu vết - Triệu chứng: Dịch chuyển tức thời, Nuốt thao tác·rollback / Yếu tố: Mất gói - Ai gặp: Cả server / Khi nào: Khi đông người - Phụ trách chính: Hạ tầng server (Đội hạ tầng) - Việc cần làm (Đội hạ tầng): Tăng ring buffer (ethtool -G), giám sát bộ đếm gói bị bỏ (drop), như rx_missed_errors trong ethtool -S (tên khác nhau tùy driver). - Con số tham khảo: Nếu 1 triệu gói dồn đến mỗi giây thì 1.024 slot sẽ đầy trong khoảng 1 ms. Trong khoảng đó chỉ cần CPU đến muộn một lần là tràn. Phần lớn NIC có thể tăng lên vài nghìn slot. - Trên đồ thị: Thỉnh thoảng vọt lên bất chợt (Bộ đếm gói nhận bị bỏ của NIC) - Chỗ cần xem: Thu thập với khoảng ngắn bộ đếm gói nhận bị bỏ trong ethtool -S (rx_missed_errors, rx_fifo_errors…, tên khác nhau tùy driver) và missed trong ip -s -s link, rồi dùng ethtool -g xem kích thước ring hiện tại và giá trị tối đa - Đúng nếu: bộ đếm gói bị bỏ tăng vào lúc burst, và kích thước ring hiện tại nhỏ hơn nhiều so với tối đa. Tăng ring lên thì gói bị bỏ giảm - Loại trừ nếu: bộ đếm gói bị bỏ không đổi mà vẫn mất gói → bước tiếp theo trong kernel (“Bộ đệm socket của kernel quá nhỏ”) hoặc đoạn mạng; %soft của một core ở mức 100% → xem “Ngắt NIC dồn vào một core” - Cách kiểm tra: Kiểm tra bằng công cụ hạ tầng (không cần code game) - Nguồn: - [Linux Base Driver for Intel(R) Ethernet Network Connection (e1000)](https://docs.kernel.org/networking/device_drivers/ethernet/intel/e1000.html) · Linux kernel · Descriptor nhận (slot của ring) của e1000 mặc định 256, có thể tăng tới 4.096 - [drivers/net/ethernet/intel/ice/ice.h (Linux v6.12)](https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/drivers/net/ethernet/intel/ice/ice.h?h=v6.12) · Linux kernel · Descriptor nhận của driver ice mặc định 2.048, tối đa 8.160 - [Interface statistics](https://docs.kernel.org/networking/statistics.html) · Linux kernel · Gói bị thiết bị bỏ do thiếu bộ đệm được tính vào rx_missed_errors, thống kê riêng của driver xem bằng ethtool -S - [ethtool(8) — Linux manual page](https://man7.org/linux/man-pages/man8/ethtool.8.html) · ethtool · -g xem kích thước ring (giá trị hiện tại và tối đa), -G để đổi, -S xem thống kê theo driver #### nic-coalesce · Gộp ngắt quá mức · Interrupt coalescing Để giảm tải cho CPU, NIC gom gói tin lại rồi báo một lần, nên gói bị trễ đúng bằng thời gian gom. - Vì sao → Dẫn đến → Trên màn hình: NIC gom đủ một khoảng thời gian hoặc số lượng gói nhất định rồi mới báo → Gói tin phải chờ trong lúc gom → Độ trễ tăng nhẹ. Thường nhỏ, nhưng nếu quá mức thì lên tới mức ms - Triệu chứng: Trễ thao tác / Yếu tố: Độ trễ - Ai gặp: Cả server / Khi nào: Luôn luôn - Phụ trách chính: Hạ tầng server (Đội hạ tầng) - Việc cần làm (Đội hạ tầng): Gộp ngắt thích ứng (adaptive), chỉnh về giá trị phù hợp cho server game (ethtool -C). - Con số tham khảo: Thường vài chục đến vài trăm µs. Với game thì thường không đáng kể, nhưng nếu cấu hình quá mức thì tăng lên tới ms. - Trên đồ thị: Luôn cao ngay từ đầu (Thời gian khứ hồi trong cùng trung tâm dữ liệu) - Chỗ cần xem: Dùng ethtool -c xem cấu hình gộp ngắt hiện tại (adaptive-rx, rx-usecs, rx-frames), và so sánh thời gian khứ hồi ping với server khác trong cùng trung tâm dữ liệu trước và sau khi đổi cấu hình - Đúng nếu: rx-usecs được đặt lớn, từ vài trăm µs trở lên, và giảm giá trị này thì thời gian khứ hồi trong cùng trung tâm dữ liệu giảm tương ứng - Loại trừ nếu: giảm rồi mà thời gian khứ hồi vẫn như cũ → không phải nguyên nhân này - Cách kiểm tra: Kiểm tra bằng công cụ hạ tầng (không cần code game) - Nguồn: - [Linux Driver for Intel(R) Ethernet Network Connection (e1000e)](https://docs.kernel.org/networking/device_drivers/ethernet/intel/e1000e.html) · Linux kernel · Mặc định là điều tiết ngắt thích ứng, 4.000–20.000 lần mỗi giây (khoảng cách 50–250 µs); giảm ngắt thì tiết kiệm CPU nhưng độ trễ tăng - [Linux Base Driver for Intel(R) Ethernet Network Connection (e1000)](https://docs.kernel.org/networking/device_drivers/ethernet/intel/e1000.html) · Linux kernel · RxIntDelay có thể làm chậm ngắt nhận theo đơn vị 1,024 µs, tối đa 65.535 (khoảng 67 ms); tăng giá trị thì độ trễ nhận tăng - [ethtool(8) — Linux manual page](https://man7.org/linux/man-pages/man8/ethtool.8.html) · ethtool · Các cấu hình adaptive-rx, rx-usecs, rx-frames của ethtool -C #### nic-cloud-pps · Vượt giới hạn PPS trên cloud · Cloud PPS / bandwidth allowance Server cloud có giới hạn số gói mỗi giây và băng thông theo từng loại instance, vượt quá thì gói bị bỏ âm thầm. - Vì sao → Dẫn đến → Trên màn hình: Số người chơi đồng thời tăng làm số gói mỗi giây vượt giới hạn của instance → Mạng của cloud bỏ phần vượt → Mất gói không rõ nguyên nhân gây dịch chuyển tức thời hoặc nuốt skill. CPU server vẫn dư - Triệu chứng: Dịch chuyển tức thời, Nuốt thao tác·rollback / Yếu tố: Mất gói - Ai gặp: Cả server / Khi nào: Khi đông người, Giờ cao điểm buổi tối - Phụ trách chính: Hạ tầng server (Đội hạ tầng) / Phối hợp: Phát triển server (Đội phát triển game), Bên ngoài (Bên ngoài) - Việc cần làm (Đội phát triển game): Gộp gói (gom message của một tick vào một gói), tránh gửi quá thường xuyên những gói rất nhỏ. - Việc cần làm (Đội hạ tầng): Kiểm tra bộ đếm vượt giới hạn (AWS: pps_allowance_exceeded, conntrack_allowance_exceeded…) và đặt cảnh báo, chuyển sang instance lớn hơn, tránh giới hạn theo dõi kết nối bằng cấu hình security group không tạo theo dõi. - Việc cần làm (Bên ngoài): Hỏi nhà cung cấp cloud về giới hạn số gói mỗi giây và giới hạn theo dõi kết nối theo từng loại instance. - Con số tham khảo: Giới hạn khác nhau theo kích thước instance, và giới hạn số gói mỗi giây thường không được công bố. “Tối đa 10 Gbps” của instance nhỏ là tốc độ burst chỉ dùng được khi còn credit (thường 5–60 phút), tốc độ cơ bản thường ngày thấp hơn nhiều. - Trên đồ thị: Chạm giới hạn rồi đi ngang (Số gói mỗi giây, bộ đếm vượt allowance) - Chỗ cần xem: Thu thập với khoảng ngắn các bộ đếm ENA trong ethtool -S là pps_allowance_exceeded, bw_in_allowance_exceeded, bw_out_allowance_exceeded, conntrack_allowance_exceeded, rồi xem cùng với số gói mỗi giây. Cũng có thể dùng CloudWatch agent đẩy các bộ đếm này lên để đặt cảnh báo - Đúng nếu: vào lúc mất gói, bộ đếm vượt allowance tăng, và số gói mỗi giây dừng ở một mức nhất định không tăng thêm. CPU server vẫn dư - Loại trừ nếu: bộ đếm vượt giới hạn không đổi → không phải nguyên nhân này; %soft của một core ở mức 100% → xem “Ngắt NIC dồn vào một core” - Cách kiểm tra: Kiểm tra bằng công cụ hạ tầng (không cần code game) - Tìm hiểu thêm: conntrack_allowance_exceeded là trường hợp bảng theo dõi kết nối đầy nên kết nối mới bị bỏ. Nếu bảng vẫn còn chỗ mà chỉ kết nối nhàn rỗi bị hết hạn theo dõi rồi bị ngắt, hãy xem mục “Hết hạn theo dõi kết nối của security group trên cloud”. - Nguồn: - [Monitor network performance for ENA settings on your EC2 instance](https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/monitoring-network-performance-ena.html) · AWS · Mỗi instance có giới hạn băng thông, PPS, theo dõi kết nối; vượt thì gói được xếp vào hàng đợi rồi bị bỏ; các bộ đếm pps_allowance_exceeded, conntrack_allowance_exceeded - [Amazon EC2 instance network bandwidth](https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/ec2-instance-network-bandwidth.html) · AWS · “Tối đa N Gbps” của instance từ 16 vCPU trở xuống là burst dùng credit I/O mạng (thường 5–60 phút), hết credit thì quay về băng thông cơ sở #### nic-saturate · Băng thông NIC bị bão hòa · NIC bandwidth saturation Khi dùng tới giới hạn của card 1 Gbps hay 10 Gbps, hàng đợi gửi dài ra và cuối cùng gói bị bỏ. - Vì sao → Dẫn đến → Trên màn hình: Broadcast tăng làm lượng dữ liệu gửi chạm giới hạn của card → Hàng đợi gửi dài ra, tràn thì gói bị bỏ → Toàn server bị trễ và mất gói (trễ thao tác, dịch chuyển tức thời) - Triệu chứng: Trễ thao tác, Dịch chuyển tức thời / Yếu tố: Độ trễ, Mất gói - Ai gặp: Cả server / Khi nào: Khi đông người - Phụ trách chính: Phát triển server (Đội phát triển game) / Phối hợp: Hạ tầng server (Đội hạ tầng) - Việc cần làm (Đội phát triển game): Giảm lượng dữ liệu gửi (AOI, nén, chỉ gửi phần thay đổi). - Việc cần làm (Đội hạ tầng): Nâng cấp card (NIC nhanh hơn, trên cloud thì instance lớn hơn), cảnh báo mức sử dụng NIC. - Trên đồ thị: Chạm giới hạn rồi đi ngang (Lưu lượng gửi của NIC, gói gửi bị bỏ) - Chỗ cần xem: So sánh txkB/s và %ifutil (mức sử dụng so với tốc độ interface) của sar -n DEV 1 với tốc độ NIC và băng thông của instance, đồng thời xem TX dropped của ip -s link - Đúng nếu: lưu lượng gửi đi ngang quanh mức băng thông của NIC hoặc instance, từ lúc đó gói gửi bị bỏ và độ trễ toàn server tăng - Loại trừ nếu: băng thông còn dư → không phải nguyên nhân này; nhiều gói nhỏ mà vẫn mất gói → xem “Vượt giới hạn PPS trên cloud” - Cách kiểm tra: Kiểm tra bằng công cụ hạ tầng (không cần code game) - Nguồn: - [Interface statistics](https://docs.kernel.org/networking/statistics.html) · Linux kernel · tx_dropped: số gói bị bỏ trong lúc gửi do thiếu tài nguyên - [Amazon EC2 instance network bandwidth](https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/ec2-instance-network-bandwidth.html) · AWS · Băng thông instance dùng được phụ thuộc vào số vCPU (kích thước instance) - [sar(1) — Linux manual page](https://man7.org/linux/man-pages/man1/sar.1.html) · sysstat · rxkB/s, txkB/s và %ifutil (mức sử dụng so với tốc độ interface) của -n DEV #### nic-noisy · Overhead ảo hóa và noisy neighbor · Noisy neighbors in virtualization Nếu máy ảo khác trên cùng server vật lý dùng nhiều mạng và CPU, việc xử lý trên server của bạn bị chậm lại một cách thất thường. - Vì sao → Dẫn đến → Trên màn hình: Máy ảo khác trên cùng server vật lý dùng nhiều tài nguyên → Việc xử lý gói tin của máy ảo của bạn bị trễ thất thường → Không có nguyên nhân rõ ràng mà thỉnh thoảng xuất hiện jitter (độ dao động của khoảng cách giữa các lần gói tin đến), gây giật khựng - Triệu chứng: Giật khựng / Yếu tố: Jitter - Ai gặp: Cả server / Khi nào: Thỉnh thoảng bất chợt - Phụ trách chính: Hạ tầng server (Đội hạ tầng) / Phối hợp: Bên ngoài (Bên ngoài) - Việc cần làm (Đội hạ tầng): Dedicated host, instance có bảo đảm hiệu năng, instance bị jitter kéo dài thì dừng rồi khởi động lại để chuyển sang host khác. - Việc cần làm (Bên ngoài): Báo host có vấn đề cho nhà cung cấp cloud. - Trên đồ thị: Thỉnh thoảng vọt lên bất chợt (Jitter thời gian khứ hồi trong cùng trung tâm dữ liệu, %steal) - Chỗ cần xem: Ping liên tục tới server khác trong cùng trung tâm dữ liệu để ghi jitter của thời gian khứ hồi, rồi so sánh cùng %steal của mpstat với instance khác có cùng cấu hình - Đúng nếu: chỉ instance này có jitter thời gian khứ hồi hoặc %steal vọt lên thất thường, instance khác cùng cấu hình thì yên. Dừng rồi khởi động lại để chuyển sang host khác thì hết - Loại trừ nếu: mọi instance cùng cấu hình đều vọt lên như nhau → không phải vấn đề host; xem tải phía server game hoặc đoạn mạng - Cách kiểm tra: Kiểm tra bằng công cụ hạ tầng (không cần code game) - Nguồn: - [How EC2 instance stop and start works](https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/how-ec2-instance-stop-start-works.html) · AWS · Dừng rồi khởi động lại instance thì phần lớn trường hợp sẽ được chuyển sang host mới (trừ dedicated host) - [mpstat(1) — Linux manual page](https://man7.org/linux/man-pages/man1/mpstat.1.html) · sysstat · %steal: tỷ lệ thời gian CPU ảo này buộc phải chờ trong lúc hypervisor chạy CPU ảo khác #### nic-host-maintenance · Bảo trì host cloud và live migration · Cloud host maintenance / live migration Khi nhà cung cấp cloud bảo trì server vật lý (host), họ chuyển máy ảo sang host khác (live migration) hoặc tạm dừng máy ảo một lúc. Trong lúc đó cả server bị đứng, nếu thời gian dừng dài thì kết nối bị ngắt. - Vì sao → Dẫn đến → Trên màn hình: Nhà cung cấp chuyển máy ảo sang host khác hoặc tạm dừng một lúc do bảo trì host hay dự đoán hỏng hóc → Trong lúc chuyển, CPU, bộ nhớ, mạng chậm đi, và ở bước cuối máy ảo dừng hẳn trong chốc lát (từ dưới 1 giây đến khoảng 30 giây, tùy nhà cung cấp và cách làm) → Mọi người trên server cùng đứng hình rồi tua nhanh hoặc dịch chuyển tức thời; nếu thời gian dừng dài hơn timeout thì mất kết nối hàng loạt - Triệu chứng: Đứng hình, Tua nhanh, Dịch chuyển tức thời, Mất kết nối / Yếu tố: Ngưng trệ, Mất gói - Ai gặp: Cả server / Khi nào: Thỉnh thoảng bất chợt - Phụ trách chính: Hạ tầng server (Đội hạ tầng) / Phối hợp: Phát triển server (Đội phát triển game), Bên ngoài (Bên ngoài) - Việc cần làm (Đội phát triển game): Timeout chịu được dừng vài giây, đặt trần cho số tick chạy bù sau khi dừng, tính thời gian trôi qua bằng monotonic clock, có quy trình khi nhận thông báo bảo trì thì lưu tiến trình và chuyển người chơi sang server khác. - Việc cần làm (Đội hạ tầng): Đăng ký nhận và cảnh báo thông báo bảo trì (Google Cloud maintenance-event, AWS scheduled event và AWS Health, Azure Scheduled Events), khi nhận thông báo thì chủ động thay server vào giờ ít người chơi, đổi giờ bảo trì nếu nhà cung cấp cho phép (Azure Maintenance Configuration, AWS scheduled event tùy loại), đối chiếu lịch sử bảo trì với lịch sử sự cố. - Việc cần làm (Bên ngoài): Xác nhận với nhà cung cấp cloud lịch bảo trì và phạm vi ảnh hưởng, báo cáo nếu cùng một instance bị dừng lặp lại. - Con số tham khảo: Google Compute Engine cho biết thời gian dừng của live migration thường ngắn hơn 1 giây rất nhiều, và trong lúc dừng, đồng hồ hệ thống có thể nhảy tới trước tối đa 5 giây. Giá trị maintenance-event trong metadata đổi 60 giây trước khi chuyển (nếu trước đó đã truy vấn giá trị này ít nhất một lần). Với Azure, bảo trì không cần khởi động lại gần như luôn dừng dưới 10 giây, hiếm khi (với cỡ thông thường không quá một lần trong 18 tháng) dừng khoảng 30 giây, còn live migration thường không quá 5 giây. Azure Scheduled Events báo trước các lần dừng như vậy (Freeze) ít nhất 15 phút. Tuy nhiên nếu phần cứng host hỏng đột ngột thì việc khôi phục bắt đầu ngay mà không báo trước. - Trên đồ thị: Đứt quãng rồi dồn về (Số gói gửi nhận của server, khoảng cách tick) - Chỗ cần xem: Đối chiếu thời điểm dừng với bản ghi của nhà cung cấp. Google Cloud: compute.instances.migrateOnHostMaintenance trong audit log; AWS: scheduled event trong describe-instance-status, AWS Health; Azure: Microsoft.Compute/virtualMachines/liveMigration/action trong Activity Log và thời điểm chỉ số khả dụng VM (VmAvailabilityMetric) rơi về 0. Bên trong server, xem chỉ số và log có bị trống trong lúc dừng không, đồng hồ có nhảy ngay sau đó không (log đồng bộ thời gian) - Đúng nếu: thời điểm cả server dừng trùng với thời điểm bảo trì hoặc migration mà nhà cung cấp ghi lại, và trong vài giây đó mọi chỉ số, log bên trong server đều trống - Loại trừ nếu: không có trong bản ghi của nhà cung cấp và các lần dừng ngắn lặp lại thường xuyên → xem “CPU steal (máy ảo)”; log kernel có bản ghi reset NIC → xem “Lỗi driver hoặc firmware NIC” - Cách kiểm tra: Kiểm tra bằng công cụ hạ tầng (không cần code game) - Tìm hiểu thêm: AWS thông báo bằng scheduled event. system-reboot nghĩa là khởi động lại và chuyển sang host mới, system-maintenance nghĩa là có thể bị ảnh hưởng tạm thời do bảo trì mạng hoặc nguồn điện. Dù thời gian dừng chỉ vài giây, client không nhận được ACK cho các gói đã gửi tới server trong lúc đó sẽ nhân đôi dần thời gian chờ truyền lại, nên sau khi server chạy lại, kết nối TCP có thể còn đứng lâu hơn (“TCP RTO và exponential backoff”). Khi thức dậy sau lần dừng, đồng hồ nhảy và có thể dẫn tới “Đồng hồ hệ thống nhảy (NTP step)”, health check của bộ cân bằng tải cũng có thể thất bại khiến server đó tạm bị loại. Instance không thể chuyển (như instance bare metal của Google Cloud) sẽ bị dừng hoặc khởi động lại khi bảo trì. - Nguồn: - [Live migration process during maintenance events](https://cloud.google.com/compute/docs/instances/live-migration-process) · Google Cloud · Thời gian dừng của live migration thường ngắn hơn 1 giây rất nhiều, trong lúc dừng đồng hồ hệ thống nhảy tới trước tối đa 5 giây, trong lúc chuyển thì hiệu năng ổ đĩa, CPU, bộ nhớ, mạng tạm giảm, VM không live migration thì bị tắt khi bảo trì (instance bare metal không hỗ trợ live migration) - [Query metadata server for maintenance event notices](https://cloud.google.com/compute/docs/metadata/getting-live-migration-notice) · Google Cloud · Giá trị metadata maintenance-event đổi 60 giây trước live migration (với cấu hình live migration và đã truy vấn giá trị này ít nhất một lần kể từ lần bảo trì trước) - [Monitor and plan for a host maintenance event](https://cloud.google.com/compute/docs/instances/monitor-plan-host-maintenance-event) · Google Cloud · Khi bảo trì, audit log ghi lại sự kiện hệ thống compute.instances.migrateOnHostMaintenance - [Scheduled events for Amazon EC2 instances](https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/monitoring-instances-status-check_sched.html) · AWS · Các loại scheduled event (system-reboot là khởi động lại và chuyển sang host mới, system-maintenance là ảnh hưởng tạm thời do bảo trì mạng, nguồn điện), thông báo qua email và AWS Health, kiểm tra bằng describe-instance-status, tùy loại có thể đổi giờ - [Maintenance and updates](https://learn.microsoft.com/en-us/azure/virtual-machines/maintenance-and-updates) · Microsoft Azure · Bảo trì không khởi động lại gần như luôn dừng dưới 10 giây, hiếm khi (cỡ thông thường không quá một lần trong 18 tháng) khoảng 30 giây, live migration thường không quá 5 giây, đồng hồ tự đồng bộ sau khi dừng, kết nối TCP dài có thể bị ngắt hoặc phía bên kia truyền lại dữ liệu đã gửi tới VM đang dừng theo exponential backoff khiến việc khôi phục chậm hơn, health check của bộ cân bằng tải xác định bất thường trong khoảng 10 giây, kiểm tra bằng Microsoft.Compute/virtualMachines/liveMigration/action trong Activity Log và VmAvailabilityMetric (về 0 trong lúc dừng), chọn giờ áp dụng bằng Maintenance Configuration - [Scheduled Events for Linux VMs in Azure](https://learn.microsoft.com/en-us/azure/virtual-machines/linux/scheduled-events) · Microsoft Azure · Freeze (dừng vài giây, CPU và mạng có thể dừng) được báo trước ít nhất 15 phút; khi phần cứng host hỏng thì bắt đầu khôi phục ngay, không có thời gian báo trước #### nic-reset · Lỗi driver hoặc firmware NIC · NIC hang / reset Do lỗi driver hoặc tính năng hoạt động sai, card mạng bị treo và khởi động lại, trong lúc đó mọi việc gửi nhận đều bị ngắt. - Vì sao → Dẫn đến → Trên màn hình: Lỗi driver, tính năng offload hoạt động sai → NIC treo rồi khởi động lại (vài giây) → Mọi người trên server đó cùng đứng hình rồi dịch chuyển tức thời hoặc mất kết nối - Triệu chứng: Đứng hình, Mất kết nối / Yếu tố: Mất gói - Ai gặp: Cả server / Khi nào: Thỉnh thoảng bất chợt, Càng chạy lâu càng nặng - Phụ trách chính: Hạ tầng server (Đội hạ tầng) - Việc cần làm (Đội hạ tầng): Kiểm tra và cảnh báo các bản ghi “transmit queue … timed out”, “Link is Down” trong log kernel, cập nhật driver và firmware, tắt tính năng có vấn đề (offload…). - Trên đồ thị: Đứt quãng rồi dồn về (Số gói gửi nhận của server) - Chỗ cần xem: Dùng dmesg tìm trong log kernel các bản ghi “NETDEV WATCHDOG … transmit queue N timed out”, reset của driver, “Link is Down” và “Link is Up”, rồi xem số gói gửi nhận của server tại thời điểm đó - Đúng nếu: vào lúc dừng, log kernel có bản ghi timeout hàng đợi gửi hoặc link down, up, và trong vài giây đó số gói gửi nhận bằng 0 - Loại trừ nếu: log kernel sạch và cổng phía switch cũng bình thường → tiến trình server game bị dừng (“GC dừng toàn bộ server”, “Deadlock”) hoặc xem “Failover thiết bị mạng” - Cách kiểm tra: Kiểm tra bằng công cụ hạ tầng (không cần code game) - Nguồn: - [net/sched/sch_generic.c (Linux v6.12)](https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/net/sched/sch_generic.c?h=v6.12) · Linux kernel · Khi hàng đợi gửi bị treo, watchdog của kernel ghi “NETDEV WATCHDOG … transmit queue N timed out” và gọi hàm reset của driver - [drivers/net/ethernet/intel/ixgbe/ixgbe_main.c (Linux v6.12)](https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c?h=v6.12) · Linux kernel · Driver ixgbe reset adapter khi việc gửi bị treo, và ghi “NIC Link is Down” khi mất link - [ethtool(8) — Linux manual page](https://man7.org/linux/man-pages/man8/ethtool.8.html) · ethtool · Tùy chọn ethtool -K để tắt từng tính năng offload #### nic-offload · Độ trễ chờ gộp gói GRO/LRO · GRO/LRO batching Đây là tính năng gộp nhiều gói thành một để giảm tải cho CPU. Tùy cấu hình, gói game nhỏ đôi khi phải chờ một chút gói tiếp theo để gộp cùng. - Vì sao → Dẫn đến → Trên màn hình: NIC và kernel gộp các gói đã đến lại để xử lý → Nếu bật gộp bằng phần cứng (LRO) hoặc cấu hình thời gian chờ gộp, gói phải chờ một chút để đợi gói tiếp theo → Độ trễ tăng nhẹ (thường không quá vài chục µs) - Triệu chứng: Trễ thao tác / Yếu tố: Độ trễ - Ai gặp: Cả server / Khi nào: Luôn luôn - Phụ trách chính: Hạ tầng server (Đội hạ tầng) - Việc cần làm (Đội hạ tầng): Chỉnh cho phù hợp với lưu lượng game (tắt LRO, kiểm tra cấu hình thời gian chờ gộp), hiệu quả thường nhỏ nên kiểm tra sau các nguyên nhân khác. - Trên đồ thị: Luôn cao ngay từ đầu (Thời gian khứ hồi trong cùng trung tâm dữ liệu) - Chỗ cần xem: Dùng ethtool -k xem trạng thái lro, gro, kiểm tra giá trị gro_flush_timeout trong cấu hình sysfs của thiết bị, rồi so sánh thời gian khứ hồi của gói nhỏ trong cùng trung tâm dữ liệu trước và sau khi đổi - Đúng nếu: LRO đang bật hoặc gro_flush_timeout lớn hơn 0, và khi tắt hoặc đặt về 0 thì thời gian khứ hồi của gói nhỏ giảm - Loại trừ nếu: đổi rồi mà chênh lệch chỉ trong vòng vài µs → không phải nguyên nhân này - Cách kiểm tra: Kiểm tra bằng công cụ hạ tầng (không cần code game) - Nguồn: - [NAPI](https://docs.kernel.org/networking/napi.html) · Linux kernel · Đặt gro_flush_timeout lớn thì được gộp gói để xử lý, nhưng đổi lại sẽ phát sinh độ trễ khi tải thấp - [Linux Base Driver for the Intel(R) Ethernet 10 Gigabit PCI Express Adapters (ixgbe)](https://docs.kernel.org/networking/device_drivers/ethernet/intel/ixgbe.html) · Linux kernel · GRO là tính năng gộp lưu lượng nhận thành khối lớn để tiết kiệm CPU, phát triển từ LRO - [ethtool(8) — Linux manual page](https://man7.org/linux/man-pages/man8/ethtool.8.html) · ethtool · Cấu hình gro, lro on|off của ethtool -K ### L7 OS server (kernel) (14 nguyên nhân) #### so-backlog · Tràn hàng đợi kết nối (backlog) · Listen backlog / SYN queue overflow Ngay sau bảo trì, khi hàng chục nghìn người chơi kết nối cùng lúc, hàng đợi kết nối (backlog) của kernel bị tràn và các yêu cầu kết nối bị bỏ. - Vì sao → Dẫn đến → Trên màn hình: Vừa hết bảo trì, kết nối đổ về nhanh hơn tốc độ server game xử lý bằng accept → Hàng đợi kết nối của kernel (backlog, lấy giá trị nhỏ hơn giữa giá trị code server truyền cho listen và giới hạn của kernel) bị đầy → Yêu cầu kết nối bị bỏ, client thử lại hết lần này đến lần khác, dẫn đến không vào được·kẹt loading - Triệu chứng: Không vào được·kẹt loading / Yếu tố: Mất gói - Ai gặp: Cả server / Khi nào: Ngay sau đăng nhập hoặc bảo trì - Phụ trách chính: Phát triển server (Đội phát triển game) / Phối hợp: Hạ tầng server (Đội hạ tầng), Phát triển client (Đội phát triển game) - Việc cần làm (Đội phát triển game): Server: tăng giá trị listen truyền trong code, không để thread nhận kết nối bị dừng vì việc khác, thêm hệ thống hàng chờ đăng nhập. Client: tăng khoảng cách thử lại (rải ngẫu nhiên). - Việc cần làm (Đội hạ tầng): Tăng somaxconn của kernel (phải tăng cả giá trị listen trong code server mới có tác dụng), luôn bật SYN cookie, giám sát số lần tràn (TcpExtListenOverflows trong nstat). - Con số tham khảo: Giới hạn của kernel Linux (somaxconn) mặc định là 4.096 từ bản 5.4 (trước đó là 128), nhưng nếu code server truyền cho listen một giá trị nhỏ hơn thì giá trị đó là giới hạn. Khi hàng đợi đầy, Linux lặng lẽ bỏ yêu cầu kết nối mà không báo lỗi. OS phía client gửi lại vài lần, bắt đầu sau 1 giây, nên người chơi chỉ thấy loading kéo dài, không thấy báo “Kết nối thất bại”. Server Windows thì gửi lại phản hồi từ chối, nên client thấy “Kết nối thất bại” ngay. - Trên đồ thị: Tăng vọt ngay sau đăng nhập hoặc bảo trì (Số lần tràn hàng đợi kết nối (ListenOverflows), số lần thử kết nối) - Chỗ cần xem: Mức tăng của TcpExtListenOverflows và TcpExtListenDrops trong nstat -az; dùng ss -ltn so sánh Recv-Q (số kết nối đang chờ accept) với Send-Q (giới hạn backlog) của socket đang listen - Đúng nếu: ListenOverflows tăng vào lúc kết nối dồn về, và Recv-Q của socket đang listen dính sát giá trị Send-Q - Loại trừ nếu: ListenOverflows không đổi. Kết nối đã thiết lập nhưng loading không xong → “Đăng nhập ồ ạt và query N+1”; bị chặn đúng ở một mức số người cố định → “Giới hạn file descriptor” - Cách kiểm tra: Kiểm tra bằng công cụ hạ tầng (không cần code game) - Nguồn: - [listen(2) — Linux manual page](https://man7.org/linux/man-pages/man2/listen.2.html) · Linux man-pages · Backlog của listen vượt somaxconn thì bị cắt bớt mà không báo; somaxconn mặc định 4.096 (từ 5.4, trước đó là 128); khi hàng đợi đầy, yêu cầu có thể bị bỏ qua để client tự thử lại - [IP Sysctl](https://docs.kernel.org/networking/ip-sysctl.html) · Linux kernel · tcp_syn_retries: yêu cầu kết nối (SYN) được gửi lại nhiều lần, lần truyền lại đầu tiên chờ 1 giây; tcp_abort_on_overflow mặc định tắt (tràn cũng không gửi phản hồi từ chối); tcp_syncookies mặc định bật - [listen function (winsock2.h)](https://learn.microsoft.com/en-us/windows/win32/api/winsock2/nf-winsock2-listen) · Microsoft · Trên Windows, khi hàng đợi đầy, client nhận lỗi WSAECONNREFUSED - [SNMP counter](https://docs.kernel.org/networking/snmp_counter.html) · Linux kernel · TcpExtListenOverflows: số lần bỏ yêu cầu kết nối (SYN) vì hàng đợi accept đầy; khi đó TcpExtListenDrops cũng tăng theo - [net/ipv4/tcp_diag.c (Linux v6.12)](https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/net/ipv4/tcp_diag.c?h=v6.12) · Linux kernel · Giá trị Recv-Q và Send-Q trong ss: với socket đang listen là số kết nối chờ accept và giới hạn backlog; với socket đã kết nối là số byte ứng dụng chưa đọc và số byte đã gửi nhưng chưa nhận được ACK #### so-fd · Giới hạn file descriptor · File descriptor limit (ulimit) Mỗi kết nối cần một file descriptor (fd, con số OS gán cho mỗi file hoặc socket đang mở), nhưng số fd mà một tiến trình được mở có giới hạn. - Vì sao → Dẫn đến → Trên màn hình: Số người chơi đồng thời chạm giới hạn file descriptor của tiến trình → Server không nhận được kết nối mới (Too many open files). Việc mở file log và kết nối DB cũng thất bại theo → Từ đúng một mức số người nhất định trở đi, mọi người mới vào đều gặp không vào được·kẹt loading - Triệu chứng: Không vào được·kẹt loading / Yếu tố: Mất gói - Ai gặp: Cả server / Khi nào: Ngay sau đăng nhập hoặc bảo trì, Khi đông người - Phụ trách chính: Hạ tầng server (Đội hạ tầng) / Phối hợp: Phát triển server (Đội phát triển game) - Việc cần làm (Đội phát triển game): Luôn đóng socket khi kết thúc kết nối (tránh rò rỉ fd); khi accept thất bại với EMFILE (hết fd), tạm dừng nhận kết nối một lúc hoặc dùng một fd dự phòng giữ sẵn để nhận rồi đóng ngay (để server không tốn CPU xử lý lặp đi lặp lại cùng một thông báo kết nối mới). - Việc cần làm (Đội hạ tầng): Kiểm tra ulimit và cấu hình service (LimitNOFILE của systemd), cảnh báo khi gần chạm giới hạn. - Con số tham khảo: Trên Linux, nếu không cấu hình riêng cho service, giới hạn này vẫn thường là 1.024. Server game thường nâng lên hàng chục nghìn đến hàng trăm nghìn. Windows không có giới hạn mặc định thấp như vậy. - Trên đồ thị: Chạm giới hạn rồi đi ngang (Số fd đang mở của tiến trình, số kết nối đồng thời) - Chỗ cần xem: fd-nr (số file descriptor đang mở) của tiến trình server game qua pidstat -v, giới hạn số file mở trong /proc/PID/limits, và các lần accept thất bại (EMFILE, Too many open files) trong log server - Đúng nếu: số fd đi ngang ở đúng giá trị giới hạn, và từ lúc đó accept thất bại với EMFILE - Loại trừ nếu: số fd còn cách xa giới hạn. Yêu cầu kết nối bị kernel bỏ → “Tràn hàng đợi kết nối (backlog)”; vấn đề nằm ở theo dõi kết nối → “Bảng conntrack của server bị đầy” - Cách kiểm tra: Kiểm tra bằng công cụ hạ tầng (không cần code game) - Tìm hiểu thêm: Các kết nối chưa được nhận vẫn nằm nguyên trong hàng đợi kết nối (backlog) của kernel, nên tùy cách viết code, server có thể liên tục nhận thông báo “có kết nối mới” và tốn CPU vô ích. - Nguồn: - [systemd-system.conf(5) — Linux manual page](https://man7.org/linux/man-pages/man5/systemd-system.conf.5.html) · systemd · Giá trị mặc định DefaultLimitNOFILE của service là 1024:524288 (giới hạn mềm 1.024) - [accept(2) — Linux manual page](https://man7.org/linux/man-pages/man2/accept.2.html) · Linux man-pages · Khi tiến trình chạm giới hạn fd, accept thất bại với EMFILE - [Maximum Number of Sockets Supported](https://learn.microsoft.com/en-us/windows/win32/winsock/maximum-number-of-sockets-supported-2) · Microsoft · Winsock trên Windows chỉ giới hạn số socket theo bộ nhớ khả dụng - [pidstat(1) — Linux manual page](https://man7.org/linux/man-pages/man1/pidstat.1.html) · sysstat · fd-nr của -v: số file descriptor mà tiến trình đang mở - [proc_pid_limits(5) — Linux manual page](https://man7.org/linux/man-pages/man5/proc_pid_limits.5.html) · Linux man-pages · /proc/PID/limits chứa giá trị mềm và cứng của từng giới hạn tài nguyên theo tiến trình #### so-sockbuf · Bộ đệm socket của kernel quá nhỏ · Small socket buffers Khi bộ đệm gửi và nhận nhỏ, lúc lưu lượng dồn thành burst, gói UDP nhận về sẽ bị bỏ, còn việc gửi qua TCP bị chặn vì bộ đệm hết chỗ. - Vì sao → Dẫn đến → Trên màn hình: SO_SNDBUF và SO_RCVBUF để mặc định hoặc quá nhỏ → Khi có burst hoặc khi thread nhận dừng một chút, bộ đệm nhận UDP bị tràn và gói bị bỏ; TCP phải chờ vì bộ đệm gửi hết chỗ → Dịch chuyển tức thời (mất gói UDP) hoặc tua nhanh (TCP phải chờ) - Triệu chứng: Dịch chuyển tức thời, Tua nhanh / Yếu tố: Mất gói, Ngưng trệ - Ai gặp: Cả server / Khi nào: Khi đông người - Phụ trách chính: Hạ tầng server (Đội hạ tầng) / Phối hợp: Phát triển server (Đội phát triển game) - Việc cần làm (Đội phát triển game): Đặt kích thước bộ đệm (SO_SNDBUF, SO_RCVBUF) trong code cho phù hợp với lưu lượng; lưu ý với TCP, tự đặt kích thước sẽ tắt cơ chế tự điều chỉnh kích thước của Linux; không đặt quá lớn vì dữ liệu cũ sẽ dồn trong bộ đệm và làm tăng độ trễ; không để thread nhận bị dừng. - Việc cần làm (Đội hạ tầng): Điều chỉnh giới hạn của kernel (rmem_max, wmem_max; kích thước đặt trong code cũng không vượt được giá trị này) và giá trị mặc định (rmem_default), giám sát bộ đếm tràn bộ đệm (RcvbufErrors). - Con số tham khảo: Bộ đệm nhận UDP mặc định trên Linux khoảng 208 KB. Ngay cả gói nhỏ cũng chiếm bộ nhớ kernel nhiều hơn kích thước thật rất nhiều, nên chỉ vài chục đến vài trăm gói là đầy. Với server nhận 100.000 gói mỗi giây, thread nhận chỉ cần dừng vài ms là bộ đệm tràn. - Trên đồ thị: Thỉnh thoảng vọt lên bất chợt (Số lần tràn bộ đệm nhận UDP (UdpRcvbufErrors)) - Chỗ cần xem: Mức tăng UdpRcvbufErrors trong nstat -az và skmem trong ss -uamn (rb là kích thước bộ đệm nhận, d là số gói bị bỏ vì không đưa được vào socket); với TCP, xem trong skmem của ss -tm bộ nhớ chờ gửi (w) đã chạm kích thước bộ đệm gửi (tb) chưa - Đúng nếu: UdpRcvbufErrors (hoặc d của socket) tăng vào lúc có burst hoặc thread nhận bị dừng, và rb ở gần giá trị mặc định (khoảng 208 KB). Với TCP, w dính sát tb và send bị chặn - Loại trừ nếu: bộ đếm không đổi mà vẫn mất gói → vấn đề ở tầng NIC (“Ring buffer không đủ”) hoặc trên đường mạng - Cách kiểm tra: Kiểm tra bằng công cụ hạ tầng (không cần code game) - Nguồn: - [socket(7) — Linux manual page](https://man7.org/linux/man-pages/man7/socket.7.html) · Linux man-pages · Giá trị mặc định của SO_RCVBUF và SO_SNDBUF là rmem_default và wmem_default, giới hạn trên là rmem_max và wmem_max; kernel nhân đôi giá trị được đặt - [include/net/sock.h (Linux v6.18)](https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/include/net/sock.h?h=v6.18) · Linux kernel · Bộ đệm socket mặc định được định nghĩa bằng dung lượng của 256 gói 256 byte tính cả overhead của sk_buff (SKB_TRUESIZE(256)×256); frame nhỏ cũng bị tính bằng sk_buff+MTU (khoảng 208 KB là giá trị tính trên x86-64) - [IP Sysctl](https://docs.kernel.org/networking/ip-sysctl.html) · Linux kernel · tcp_rmem, tcp_wmem: tự đặt SO_RCVBUF hoặc SO_SNDBUF sẽ tắt cơ chế tự điều chỉnh kích thước của socket đó - [net/ipv4/udp.c (Linux v6.12)](https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/net/ipv4/udp.c?h=v6.12) · Linux kernel · Khi hàng đợi nhận UDP vượt kích thước bộ đệm socket, gói bị bỏ ngay và RcvbufErrors tăng - [net/ipv4/proc.c (Linux v6.12)](https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/net/ipv4/proc.c?h=v6.12) · Linux kernel · Tên bộ đếm mà nstat hiển thị: RcvbufErrors và SndbufErrors trong nhóm Udp - [ss(8) — Linux manual page](https://man7.org/linux/man-pages/man8/ss.8.html) · iproute2 · skmem của -m: rb là kích thước bộ đệm nhận, tb là kích thước bộ đệm gửi, w là bộ nhớ chờ gửi, d là số gói bị bỏ trước khi vào socket #### so-context · Quá nhiều thread và context switch · Thread oversubscription, context switching Chạy số thread nhiều hơn số core rất nhiều thì OS tốn CPU chỉ để cho chúng chạy luân phiên. - Vì sao → Dẫn đến → Trên màn hình: Có hàng trăm đến hàng nghìn thread, chẳng hạn mỗi kết nối một thread → Chi phí context switch (đổi thread đang chạy) tăng, cache miss cũng tăng → CPU bận nhưng thông lượng thấp, tick không đều, gây giật khựng và quay chậm - Triệu chứng: Giật khựng, Quay chậm / Yếu tố: Ngưng trệ, Jitter - Ai gặp: Cả server / Khi nào: Khi đông người - Phụ trách chính: Phát triển server (Đội phát triển game) / Phối hợp: Hạ tầng server (Đội hạ tầng) - Việc cần làm (Đội phát triển game): Số thread khớp với số core, dùng I/O bất đồng bộ (epoll, IOCP). - Việc cần làm (Đội hạ tầng): Giám sát số lần context switch và số thread chờ chạy (cs và r trong vmstat). - Con số tham khảo: Mỗi lần context switch mất vài µs, cộng thêm chi phí cache miss sau đó thì còn lớn hơn. - Trên đồ thị: Tăng theo số người và tải (Số context switch mỗi giây, số thread chờ chạy) - Chỗ cần xem: cs (số context switch mỗi giây) và r (số tác vụ đang chạy hoặc đang chờ CPU) trong vmstat 1, so với số core; context switch tự nguyện (cswch/s) và không tự nguyện (nvcswch/s) theo từng thread của server game qua pidstat -w -t - Đúng nếu: khi số người chơi đồng thời tăng, r vượt xa số core, cs cũng vọt lên theo, và có hàng trăm thread với nhiều context switch không tự nguyện - Loại trừ nếu: r vẫn bằng hoặc thấp hơn số core. Chỉ nhiều context switch tự nguyện → thread đang chờ lock hoặc I/O (“Tranh chấp lock”, “Kiến trúc I/O blocking”) - Cách kiểm tra: Kiểm tra bằng công cụ hạ tầng (không cần code game) - Nguồn: - [Quantifying The Cost of Context Switch (ExpCS 2007)](https://www.usenix.org/legacy/events/expcs07/papers/2-li.pdf) · ACM · Chi phí trực tiếp của một lần context switch khoảng 3,8 µs; chi phí gián tiếp tính cả ảnh hưởng tới cache từ vài µs đến hơn 1.000 µs (theo môi trường đo) - [vmstat(8) — Linux manual page](https://man7.org/linux/man-pages/man8/vmstat.8.html) · procps-ng · Các trường cs (số context switch mỗi giây) và r (số tiến trình đang chạy hoặc đang chờ chạy) - [I/O Completion Ports](https://learn.microsoft.com/en-us/windows/win32/fileio/i-o-completion-ports) · Microsoft · Xử lý nhiều I/O bất đồng bộ bằng thread pool tạo sẵn và IOCP, giữ số thread chạy đồng thời khớp với khả năng chạy song song của CPU - [pidstat(1) — Linux manual page](https://man7.org/linux/man-pages/man1/pidstat.1.html) · sysstat · Trong -w, cswch/s là context switch tự nguyện (tác vụ tự dừng để chờ tài nguyên), nvcswch/s là context switch không tự nguyện (bị buộc đổi vì đã dùng hết time slice); -t hiển thị theo từng thread #### so-steal · CPU steal (máy ảo) · CPU steal time Trong lúc server vật lý (hypervisor) tạm chuyển thời gian CPU của một máy ảo cho máy ảo khác (CPU steal), server game bị dừng. - Vì sao → Dẫn đến → Trên màn hình: Máy ảo khác trên cùng host dùng nhiều CPU → Máy ảo chạy server game mất lượt dùng CPU, mỗi lần vài ms đến vài chục ms → Thời gian tick vọt lên không rõ lý do, gây giật khựng và đứng hình - Triệu chứng: Giật khựng, Đứng hình / Yếu tố: Ngưng trệ - Ai gặp: Cả server / Khi nào: Thỉnh thoảng bất chợt - Phụ trách chính: Hạ tầng server (Đội hạ tầng) / Phối hợp: Bên ngoài (Bên ngoài) - Việc cần làm (Đội hạ tầng): Giám sát chỉ số steal (st trong top và vmstat), dùng core hoặc host riêng, tránh loại instance burstable (bị chậm khi hết CPU credit), với instance có steal cao kéo dài thì dừng rồi khởi động lại để chuyển sang host khác. - Việc cần làm (Bên ngoài): Báo cho nhà cung cấp cloud những host có steal cao kéo dài. - Trên đồ thị: Thỉnh thoảng vọt lên bất chợt (%steal, thời gian tick của server) - Chỗ cần xem: %steal từ mpstat -P ALL 1, đặt trên cùng trục thời gian với thời gian tick của server - Đúng nếu: %steal vọt lên cùng lúc với tick, và giảm sau khi dừng rồi khởi động lại để chuyển sang host khác - Loại trừ nếu: %steal gần 0 nhưng tick vẫn vọt lên → nguyên nhân nằm bên trong server game (“GC dừng toàn bộ server”, “Tranh chấp lock”). Nếu chạy trong container → “CPU throttling trong container (CFS quota)” - Cách kiểm tra: Kiểm tra bằng công cụ hạ tầng (không cần code game) - Nguồn: - [proc_stat(5) — Linux manual page](https://man7.org/linux/man-pages/man5/proc_stat.5.html) · Linux man-pages · steal: thời gian bị lấy mất vì hệ điều hành khác đang chạy trong môi trường ảo hóa - [Standard mode for burstable performance instances](https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/burstable-performance-instances-standard-mode.html) · AWS · Instance burstable dùng credit để chạy vượt hiệu năng cơ sở; khi hết credit, mức sử dụng CPU bị hạ về mức cơ sở - [How EC2 instance stop and start works](https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/how-ec2-instance-stop-start-works.html) · AWS · Dừng rồi khởi động lại instance thì phần lớn trường hợp instance được chuyển sang host mới - [mpstat(1) — Linux manual page](https://man7.org/linux/man-pages/man1/mpstat.1.html) · sysstat · %steal: tỷ lệ thời gian CPU ảo này buộc phải chờ trong lúc hypervisor chạy CPU ảo khác #### so-cpu-quota · CPU throttling trong container (CFS quota) · Container CPU throttling (CFS quota) Khi container bị đặt giới hạn CPU, ngay lúc dùng hết quota trong một chu kỳ cố định (thường là 100 ms), container bị buộc dừng trong phần thời gian còn lại của chu kỳ đó (throttling). - Vì sao → Dẫn đến → Trên màn hình: Container server game bị đặt giới hạn CPU (limit), ví dụ trên Kubernetes → Lúc tính toán tick dồn lại, container dùng hết quota và dừng vài chục ms cho đến chu kỳ sau → CPU trung bình thấp nhưng tick vọt lên theo chu kỳ, gây giật khựng và quay chậm - Triệu chứng: Giật khựng, Quay chậm / Yếu tố: Ngưng trệ, Jitter - Ai gặp: Cả server / Khi nào: Khi đông người, Thỉnh thoảng bất chợt - Phụ trách chính: Hạ tầng server (Đội hạ tầng) / Phối hợp: Phát triển server (Đội phát triển game) - Việc cần làm (Đội phát triển game): Chỉnh số worker thread khớp với giới hạn CPU (không để runtime tạo số thread bằng tổng số core của host). - Việc cần làm (Đội hạ tầng): Đặt giới hạn CPU rộng rãi, hoặc bỏ giới hạn và gán core riêng, giám sát số lần bị throttle (nr_throttled). - Con số tham khảo: Trên server giới hạn 2 core, nếu 8 thread cùng làm việc, quota của chu kỳ 100 ms sẽ hết sau 25 ms và server dừng 75 ms. - Trên đồ thị: Tăng theo số người và tải (Số lần bị throttle (nr_throttled), thời gian tick của server) - Chỗ cần xem: Mức tăng nr_throttled và throttled_usec (cgroup v1 là nr_throttled và throttled_time) trong cpu.stat của cgroup container, xem cùng thời gian tick của server - Đúng nếu: mức sử dụng CPU trung bình thấp hơn giới hạn nhưng nr_throttled và throttled_usec liên tục tăng, trùng với lúc tick vọt lên - Loại trừ nếu: nr_throttled không tăng. Bản thân máy ảo bị chậm lại → “CPU steal (máy ảo)” - Cách kiểm tra: Kiểm tra bằng công cụ hạ tầng (không cần code game) - Nguồn: - [CFS Bandwidth Control](https://docs.kernel.org/scheduler/sched-bwc.html) · Linux kernel · Dùng hết quota được cấp trong một chu kỳ thì thread dừng đến chu kỳ sau (throttling); chu kỳ mặc định 100 ms; thống kê nr_throttled - [Control Group v2](https://docs.kernel.org/admin-guide/cgroup-v2.html) · Linux kernel · cpu.max có dạng “$MAX $PERIOD” (quota, chu kỳ), giá trị mặc định là “max 100000” (chu kỳ 100 ms) - [Resource Management for Pods and Containers](https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/) · Kubernetes · CPU limit của container là giới hạn cứng mà kernel áp đặt bằng CPU throttling #### so-cstate · Độ trễ vọt lên do quản lý nguồn điện của server (C-state, điều chỉnh xung nhịp) · CPU power management latency (C-states, frequency scaling) Core CPU đang rảnh sẽ vào trạng thái tiết kiệm điện sâu (C-state) và hạ xung nhịp để tiết kiệm điện. Khi có gói tin hoặc timer đến, core cần thời gian để thức dậy và nâng xung nhịp, nên việc xử lý gói nhỏ bị cộng thêm độ trễ. - Vì sao → Dẫn đến → Trên màn hình: Chính sách điều chỉnh xung nhịp (governor) của OS hoặc cấu hình nguồn điện trong BIOS cho phép C-state sâu và xung nhịp thấp → Mỗi lần thức dậy từ trạng thái tiết kiệm điện sâu, core đang rảnh chậm tối đa vài trăm µs; nếu xung nhịp bị ghim ở mức thấp thì bản thân việc tính tick cũng chậm đi → Thường khó cảm nhận, nhưng khi có nhiều lời gọi giữa các server, độ trễ cộng dồn thành trễ thao tác, và lúc vắng người phản hồi lại chậm hơn. Nếu xung nhịp bị ghim thấp, lúc đông người tick bị dồn lại, gây quay chậm - Triệu chứng: Trễ thao tác, Quay chậm / Yếu tố: Độ trễ, Jitter, Ngưng trệ - Ai gặp: Cả server / Khi nào: Luôn luôn, Thỉnh thoảng bất chợt - Phụ trách chính: Hạ tầng server (Đội hạ tầng) - Việc cần làm (Đội hạ tầng): Chỉnh cấu hình nguồn điện của BIOS sang hướng hiệu năng, đặt governor của OS là performance (scaling_governor của cpufreq), giới hạn C-state sâu trên server nhạy với độ trễ (profile tuned latency-performance, /dev/cpu_dma_latency của PM QoS, tham số kernel intel_idle.max_cstate); sau khi đổi, so sánh cùng lúc thời gian khứ hồi trong trung tâm dữ liệu, jitter của thời gian tick và điện năng tiêu thụ. - Con số tham khảo: Theo bảng của driver intel_idle trong Linux 6.12, CPU server Intel cần 1–2 µs để thức dậy từ C1 (nông) và từ 133 µs (Skylake-SP) đến 290 µs (Sapphire Rapids) từ C6 (sâu). Một lần thì nhỏ, nhưng khi một yêu cầu đi qua nhiều server, độ trễ cộng dồn theo số server đó. Kernel càng dự đoán thời gian rảnh dài thì càng chọn trạng thái sâu, nên hiện tượng này xảy ra thường hơn ở server vắng, nơi gói tin chỉ lác đác đến. Governor powersave của cpufreq chung ghim xung nhịp ở mức thấp nhất trong khoảng cho phép (thuật toán cùng tên của intel_pstate thì điều chỉnh theo tải). - Trên đồ thị: Luôn cao ngay từ đầu (Thời gian khứ hồi trong cùng trung tâm dữ liệu, xung nhịp core) - Chỗ cần xem: Tỷ lệ thời gian mỗi core ở từng C-state và xung nhịp thực tế qua cpupower monitor; name, latency (số µs cần để thức dậy) và usage của từng state dưới /sys/devices/system/cpu/cpu0/cpuidle/; scaling_governor của cpufreq; profile hiện tại qua tuned-adm active - Đúng nếu: lúc vắng, core nằm lâu ở C-state sâu nhất hoặc xung nhịp bị ghim gần mức thấp nhất, và khi chuyển sang governor performance cùng C-state nông thì thời gian khứ hồi và jitter của các yêu cầu nhỏ giảm - Loại trừ nếu: sau khi đổi, chênh lệch chỉ trong khoảng vài chục µs → có thể bỏ qua nguyên nhân này. Vọt lên ở mức ms → “CPU steal (máy ảo)” hoặc tầng khác - Cách kiểm tra: Kiểm tra bằng công cụ hạ tầng (không cần code game) - Tìm hiểu thêm: Với server bare-metal trong IDC, hãy xem cấu hình nguồn điện của BIOS (firmware) cùng với cấu hình OS. Trên cloud, chỉ một số loại instance cho phép OS thay đổi C-state và xung nhịp; AWS mặc định ưu tiên hiệu năng tối đa nên phần lớn trường hợp có thể để nguyên. Profile tuned latency-performance trên các bản phân phối họ Red Hat đặt governor là performance và dùng PM QoS để chỉ cho phép C-state nông. Tắt tiết kiệm điện sẽ làm tăng điện năng tiêu thụ, nên chỉ áp dụng cho server nhạy với độ trễ. - Nguồn: - [CPU Idle Time Management](https://docs.kernel.org/admin-guide/pm/cpuidle.html) · Linux kernel · Mỗi trạng thái tiết kiệm điện có thời gian thức dậy (exit latency) và thời gian lưu lại tối thiểu (target residency), kernel chọn trạng thái sâu theo thời gian rảnh dự đoán; latency, usage và time của từng state trong sysfs; giới hạn trạng thái sâu bằng PM QoS (/dev/cpu_dma_latency) và intel_idle.max_cstate - [drivers/idle/intel_idle.c (Linux v6.12)](https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/drivers/idle/intel_idle.c?h=v6.12) · Linux kernel · Thời gian thức dậy theo C-state của CPU server Intel: Skylake-SP C1 2 µs, C1E 10 µs, C6 133 µs; Ice Lake C6 170 µs; Sapphire Rapids C1 1 µs, C6 290 µs - [CPU Performance Scaling](https://docs.kernel.org/admin-guide/pm/cpufreq.html) · Linux kernel · Xem và đổi governor bằng scaling_governor; performance yêu cầu xung nhịp cao nhất trong khoảng cho phép, powersave yêu cầu mức thấp nhất - [intel_pstate CPU Performance Scaling Driver](https://docs.kernel.org/admin-guide/pm/intel_pstate.html) · Linux kernel · Thuật toán powersave của intel_pstate khác với governor powersave chung: nó điều chỉnh theo tải (tương tự schedutil và ondemand) - [Chapter 2. Getting started with TuneD](https://docs.redhat.com/en/documentation/red_hat_enterprise_linux/9/html/monitoring_and_managing_system_status_and_performance/getting-started-with-tuned_monitoring-and-managing-system-status-and-performance) · Red Hat · Profile latency-performance tắt các tính năng tiết kiệm điện, đặt governor là performance và dùng PM QoS để chỉ cho phép C-state nông; xem profile hiện tại bằng tuned-adm active - [tools/power/cpupower/man/cpupower-monitor.1 (Linux v6.12)](https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/tools/power/cpupower/man/cpupower-monitor.1?h=v6.12) · Linux kernel · cpupower monitor: thống kê xung nhịp và trạng thái tiết kiệm điện theo từng core - [Processor state control for Amazon EC2 Linux instances](https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/processor_state_control.html) · AWS · Chỉ một số loại instance cho phép OS điều khiển C-state và P-state, và có thể đổi để giảm độ trễ; cấu hình mặc định cho hiệu năng tối đa nên phù hợp với phần lớn khối lượng công việc; Graviton chạy ở xung nhịp cố định nên OS không điều khiển #### so-oom · OOM killer · Out-of-memory killer Khi bộ nhớ cạn, Linux chọn tiến trình dùng nhiều bộ nhớ nhất và buộc kết thúc (kill) nó. Thường đó là server game. - Vì sao → Dẫn đến → Trên màn hình: Bộ nhớ cạn do rò rỉ hoặc tăng đột biến, hoặc container chạm giới hạn bộ nhớ → Kernel buộc kết thúc tiến trình server game → Mọi người trên server đó cùng mất kết nối, tiến độ gần nhất có thể bị rollback - Triệu chứng: Mất kết nối, Nuốt thao tác·rollback / Yếu tố: Ngưng trệ - Ai gặp: Cả server / Khi nào: Càng chạy lâu càng nặng, Khi đông người - Phụ trách chính: Phát triển server (Đội phát triển game) / Phối hợp: Hạ tầng server (Đội hạ tầng) - Việc cần làm (Đội phát triển game): Sửa rò rỉ, đặt ngưỡng trên cho mức dùng bộ nhớ và có quy trình lưu dữ liệu rồi tắt server đúng cách khi gần chạm ngưỡng. - Việc cần làm (Đội hạ tầng): Cảnh báo bộ nhớ, đặt giới hạn bộ nhớ của container khớp với mức dùng thực tế, điều chỉnh thứ tự tiến trình bị kill trước (oom_score_adj). - Con số tham khảo: Log kernel (dmesg) ghi lại “Out of memory: Killed process”, còn trên Kubernetes sẽ thấy trạng thái OOMKilled. Windows không có OOM killer; ở đó server thường chết vì lỗi khi cấp phát bộ nhớ thất bại. - Trên đồ thị: Rớt kết nối hàng loạt (Số kết nối, mức dùng bộ nhớ) - Chỗ cần xem: Bản ghi “Out of memory: Killed process” trong dmesg, trạng thái OOMKilled của pod nếu dùng Kubernetes, hoặc mức tăng oom_kill trong memory.events nếu dùng cgroup v2, đối chiếu với thời điểm mất kết nối - Đúng nếu: vào lúc kết nối rớt hàng loạt có bản ghi kill tiến trình server game, và ngay trước đó mức dùng bộ nhớ tăng lên chạm giới hạn - Loại trừ nếu: không có bản ghi OOM mà tiến trình vẫn chết → xem crash log và core dump như ở “Server crash” - Cách kiểm tra: Kiểm tra bằng công cụ hạ tầng (không cần code game) - Nguồn: - [mm/oom_kill.c (Linux v6.12)](https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/mm/oom_kill.c?h=v6.12) · Linux kernel · Tiến trình dùng nhiều bộ nhớ nhất nhận điểm cao nhất (có tính oom_score_adj); khi kill, kernel ghi “Out of memory: Killed process …” - [Assign Memory Resources to Containers and Pods](https://kubernetes.io/docs/tasks/configure-pod-container/assign-memory-resource/) · Kubernetes · Container dùng bộ nhớ vượt limit liên tục sẽ bị kết thúc, trạng thái hiển thị là OOMKilled - [Pushing the Limits of Windows: Virtual Memory](https://learn.microsoft.com/en-us/archive/blogs/markrussinovich/pushing-the-limits-of-windows-virtual-memory) · Microsoft · Trên Windows, khi chạm commit limit, các lần cấp phát commit bộ nhớ sẽ thất bại, có thể dẫn đến lỗi ứng dụng hoặc sự cố hệ thống - [Control Group v2](https://docs.kernel.org/admin-guide/cgroup-v2.html) · Linux kernel · oom_kill trong memory.events: số tiến trình trong cgroup này bị OOM killer kill #### so-reclaim · Dừng do thu hồi bộ nhớ và compaction · Memory compaction / reclaim stalls (THP) Tiến trình bị dừng trong lúc OS dồn bộ nhớ (compaction) để tạo huge page, hoặc thu hồi bộ nhớ để có thêm chỗ trống. - Vì sao → Dẫn đến → Trên màn hình: Bộ nhớ trống giảm, hoặc tính năng huge page (THP) kích hoạt compaction bộ nhớ → Thread xin bộ nhớ phải chờ đến khi thu hồi hoặc compaction xong → Server dừng thất thường (vài ms đến vài trăm ms) - Triệu chứng: Đứng hình, Giật khựng / Yếu tố: Ngưng trệ - Ai gặp: Cả server / Khi nào: Càng chạy lâu càng nặng, Thỉnh thoảng bất chợt - Phụ trách chính: Hạ tầng server (Đội hạ tầng) / Phối hợp: Phát triển server (Đội phát triển game) - Việc cần làm (Đội phát triển game): Giảm cấp phát bộ nhớ lớn trong lúc chạy (cấp phát sẵn lúc khởi động rồi dùng lại). - Việc cần làm (Đội hạ tầng): Cấu hình huge page (THP) chỉ dùng ở nơi cần (madvise), nâng ngưỡng bộ nhớ trống (vm.min_free_kbytes, v.v.). - Trên đồ thị: Thỉnh thoảng vọt lên bất chợt (Thời gian tick của server, PSI bộ nhớ) - Chỗ cần xem: some và full trong /proc/pressure/memory (tỷ lệ thời gian bị dừng để chờ bộ nhớ) và mức tăng compact_stall trong /proc/vmstat, xem cùng thời gian tick của server; kiểm tra cấu hình /sys/kernel/mm/transparent_hugepage/defrag - Đúng nếu: PSI bộ nhớ và compact_stall cùng tăng vào lúc tick vọt lên. defrag đặt là always - Loại trừ nếu: PSI và compact_stall không đổi. Mức dùng swap tăng → “Swap” - Cách kiểm tra: Kiểm tra bằng công cụ hạ tầng (không cần code game) - Nguồn: - [Transparent Hugepage Support](https://docs.kernel.org/admin-guide/mm/transhuge.html) · Linux kernel · Với defrag=always, khi cấp phát THP thất bại, kernel thu hồi và compaction bộ nhớ ngay tại chỗ nên tiến trình bị dừng; với madvise, chỉ vùng có yêu cầu mới làm vậy - [Documentation for /proc/sys/vm/](https://docs.kernel.org/admin-guide/sysctl/vm.html) · Linux kernel · min_free_kbytes: ngưỡng bộ nhớ trống tối thiểu (watermark) mà kernel giữ lại - [PSI - Pressure Stall Information](https://docs.kernel.org/accounting/psi.html) · Linux kernel · some (tỷ lệ thời gian một số tác vụ bị dừng để chờ bộ nhớ) và full (tỷ lệ thời gian mọi tác vụ đều bị dừng) trong /proc/pressure/memory #### so-timejump · Đồng hồ hệ thống nhảy (NTP step) · Wall-clock jump (NTP step) Khi đồng hồ server bị chỉnh tới hoặc lùi vài giây trong một lần, các timer dựa vào đồng hồ hệ thống sẽ kích hoạt dồn một lượt hoặc dừng hẳn. - Vì sao → Dẫn đến → Trên màn hình: Dịch vụ đồng bộ thời gian chỉnh đồng hồ một lần với biên độ lớn → Timer kích hoạt dồn một lượt hoặc dừng, timeout bị xác định sai → Buff và hồi chiêu bất thường, mất kết nối đồng loạt, tua nhanh - Triệu chứng: Tua nhanh, Mất kết nối, Nuốt thao tác·rollback / Yếu tố: Ngưng trệ - Ai gặp: Cả server / Khi nào: Thỉnh thoảng bất chợt - Phụ trách chính: Phát triển server (Đội phát triển game) / Phối hợp: Hạ tầng server (Đội hạ tầng) - Việc cần làm (Đội phát triển game): Tính thời gian đã trôi qua, timeout và hồi chiêu bằng monotonic clock (không nhảy, không lùi), chỉ dùng wall clock để hiển thị và ghi log. - Việc cần làm (Đội hạ tầng): Chỉnh đồng hồ từ từ (makestep của chrony chỉ dùng ngay sau khi khởi động), giám sát trạng thái đồng bộ thời gian (độ lệch đồng hồ). - Con số tham khảo: ntpd chỉnh thẳng một lần khi độ lệch vượt 0,128 giây; nếu nhỏ hơn thì chỉnh dần, với tốc độ mà để xóa độ lệch 1 giây phải mất hơn 30 phút. Với chrony (hiện được dùng nhiều), cấu hình khuyến nghị (makestep) chỉ cho chỉnh thẳng vài lần ngay sau khi khởi động, sau đó chỉnh dần. Đồng hồ cũng nhảy khi máy ảo tạm dừng rồi chạy lại. - Trên đồ thị: Thỉnh thoảng vọt lên bất chợt (Số lần timer kích hoạt và số lần mất kết nối, log chỉnh đồng hồ) - Chỗ cần xem: Tìm trong log của dịch vụ đồng bộ thời gian các lần chỉnh đồng hồ với biên độ lớn trong một lần, đối chiếu với thời điểm xảy ra bất thường. Với chrony, mỗi lần chỉnh lớn hơn giá trị logchange (mặc định 1 giây) đều được ghi vào syslog - Đúng nếu: có bản ghi chỉnh đồng hồ vào lúc buff và hồi chiêu bất thường, mất kết nối đồng loạt hoặc tua nhanh, và biên độ chỉnh gần bằng mức bất thường - Loại trừ nếu: không có bản ghi chỉnh đồng hồ. Nếu là máy ảo, xem thêm trường hợp tạm dừng rồi chạy lại (“Bảo trì host cloud và live migration”) - Cách kiểm tra: Kiểm tra bằng công cụ hạ tầng (không cần code game) - Nguồn: - [ntpd - Network Time Protocol (NTP) daemon](https://www.ntp.org/documentation/4.2.8-series/ntpd/) · Network Time Foundation · Độ lệch vượt ngưỡng step 128 ms thì chỉnh một lần, nhỏ hơn thì chỉnh dần; tốc độ 0,5 ms mỗi giây nên chỉnh 1 giây mất 2.000 giây (khoảng 33 phút) - [chrony – Frequently Asked Questions](https://chrony-project.org/faq.html) · chrony · Khuyến nghị chỉ cho phép step vài lần ngay sau khi khởi động, như makestep 1 3; máy ảo tạm dừng rồi chạy lại có thể thức dậy với giờ bị lệch - [clock_gettime(2) — Linux manual page](https://man7.org/linux/man-pages/man2/clock_gettime.2.html) · Linux man-pages · CLOCK_MONOTONIC không bị ảnh hưởng bởi các lần nhảy đột ngột của đồng hồ hệ thống và không chạy lùi - [chrony.conf(5)](https://chrony-project.org/doc/4.6/chrony.conf.html) · chrony · logchange: chỉnh đồng hồ lớn hơn giá trị này (mặc định 1 giây) thì ghi vào syslog #### so-cron · Tác vụ theo lịch · Cron jobs (log rotation, backup, scans) Các tác vụ nén log, sao lưu và quét bảo mật chạy vào cùng một giờ mỗi ngày chiếm CPU và ổ đĩa. - Vì sao → Dẫn đến → Trên màn hình: Tác vụ của OS chạy vào giờ đã định → Tác vụ dùng chung CPU và ổ đĩa với server game → Giật khựng và quay chậm vào giờ cố định, ví dụ 4 giờ sáng mỗi ngày - Triệu chứng: Giật khựng, Quay chậm / Yếu tố: Ngưng trệ - Ai gặp: Cả server / Khi nào: Theo chu kỳ đều - Phụ trách chính: Hạ tầng server (Đội hạ tầng) - Việc cần làm (Đội hạ tầng): Rải giờ chạy tác vụ, hạ mức ưu tiên (nice, ionice), tách khỏi server game (chạy trên server riêng). - Trên đồ thị: Vọt lên theo chu kỳ (Mức sử dụng CPU, hàng đợi ổ đĩa, thời gian tick của server) - Chỗ cần xem: Giờ chạy của các tác vụ theo lịch, lấy từ crontab và systemctl list-timers; tiến trình nào dùng CPU và ổ đĩa vào lúc tick vọt lên, xem bằng pidstat -u -d - Đúng nếu: tick vọt lên vào cùng một thời điểm mỗi ngày (hoặc mỗi giờ), và lúc đó tiến trình của tác vụ theo lịch chiếm CPU và ổ đĩa - Loại trừ nếu: thời điểm vọt lên không trùng với một giờ cố định mỗi ngày. Vọt lên cách vài giây hoặc vài phút một lần → “GC dừng toàn bộ server”, “Timer kích hoạt dồn cùng lúc” - Cách kiểm tra: Kiểm tra bằng công cụ hạ tầng (không cần code game) - Nguồn: - [ionice(1) — Linux manual page](https://man7.org/linux/man-pages/man1/ionice.1.html) · util-linux · Tác vụ thuộc lớp idle chỉ được dùng I/O khi không có chương trình nào khác dùng ổ đĩa - [systemd.timer(5) — Linux manual page](https://man7.org/linux/man-pages/man5/systemd.timer.5.html) · systemd · RandomizedDelaySec lùi giờ chạy tác vụ theo lịch một khoảng ngẫu nhiên để giảm dồn tải - [systemctl(1) — Linux manual page](https://man7.org/linux/man-pages/man1/systemctl.1.html) · systemd · list-timers: liệt kê các timer unit theo thứ tự giờ chạy kế tiếp - [pidstat(1) — Linux manual page](https://man7.org/linux/man-pages/man1/pidstat.1.html) · sysstat · -u hiển thị CPU, -d hiển thị I/O ổ đĩa theo từng tiến trình #### so-os-update · Hiệu năng thay đổi sau khi cập nhật OS, kernel, driver hoặc firmware · Performance regression after OS / kernel / driver / firmware update Code game không đổi, nhưng server chậm đi kể từ khi cập nhật OS, kernel, driver hoặc firmware. Bản cập nhật có thể làm thay đổi giá trị mặc định, bộ lập lịch, các biện pháp giảm thiểu lỗ hổng CPU (mitigations) và cách driver hoạt động. - Vì sao → Dẫn đến → Trên màn hình: Bản vá bảo mật định kỳ hoặc image server mới làm thay đổi kernel, driver hoặc firmware → Giá trị mặc định hoặc bộ lập lịch thay đổi, hoặc biện pháp giảm thiểu lỗ hổng mới được bật, khiến cùng một việc tốn nhiều thời gian CPU hơn và thứ tự thread được cấp CPU thay đổi → Server đang chạy tốt bỗng luôn chậm hơn một chút kể từ ngày cập nhật, gây trễ thao tác; lúc đông người thì giật khựng và quay chậm - Triệu chứng: Trễ thao tác, Giật khựng, Quay chậm / Yếu tố: Độ trễ, Ngưng trệ, Jitter - Ai gặp: Cả server / Khi nào: Luôn luôn, Khi đông người - Phụ trách chính: Hạ tầng server (Đội hạ tầng) - Việc cần làm (Đội hạ tầng): Áp dụng bản cập nhật cho một số server trước, so sánh thời gian tick, độ trễ và mức sử dụng CPU với phiên bản cũ rồi mới mở rộng; triển khai khác ngày với bản cập nhật game; ghi lại phiên bản kernel, driver, firmware và các giá trị sysctl chính trước và sau khi cập nhật; khi có vấn đề, boot bằng kernel cũ để xác nhận; chỉ quyết định tắt giảm thiểu (mitigations=off) sau khi cân nhắc rủi ro bảo mật. - Con số tham khảo: Đổi phiên bản kernel là đổi cả hành vi mặc định. Ví dụ, Linux bắt đầu chuyển bộ lập lịch từ CFS sang EEVDF từ bản 6.6, và giá trị mặc định của giới hạn hàng đợi kết nối (somaxconn) đổi từ 128 lên 4.096 từ bản 5.4. Biện pháp giảm thiểu lỗ hổng CPU thêm việc cho CPU, như xóa bộ đệm nội bộ của CPU khi quay từ kernel về chương trình (mỗi lần kết thúc system call), khi context switch và khi chuyển sang máy ảo, nên server mạng nào gọi system call cho từng gói tin thì càng bị ảnh hưởng nhiều. Một số lỗ hổng muốn chặn hoàn toàn phải tắt SMT (tính năng cho một core chạy như hai thread), và tắt SMT có thể làm hiệu năng giảm mạnh tùy khối lượng công việc. Tham số kernel mitigations=off tắt hết các biện pháp này để lấy lại hiệu năng, nhưng khiến hệ thống bị lộ trước các lỗ hổng. - Trên đồ thị: Tăng như bậc thang từ một thời điểm (Thời gian tick của server, mức sử dụng CPU, độ trễ ở cùng mức tải) - Chỗ cần xem: Lịch sử cập nhật trong trình quản lý gói và thời điểm reboot, phiên bản kernel xem bằng uname -r, thông tin driver NIC xem bằng ethtool -i, đối chiếu với thời điểm độ trễ tăng. So sánh server đã cập nhật và chưa cập nhật ở cùng mức tải bằng mpstat, pidstat, và so sánh cả trạng thái giảm thiểu trong /sys/devices/system/cpu/vulnerabilities/ - Đúng nếu: độ trễ và mức sử dụng CPU tăng một bậc từ lúc reboot sau cập nhật rồi giữ nguyên, và ở cùng mức tải chỉ server đã cập nhật là cao. Boot bằng kernel hoặc driver cũ thì trở lại như trước - Loại trừ nếu: server đã cập nhật và chưa cập nhật chậm như nhau ở cùng mức tải. Nếu cùng ngày có triển khai bản cập nhật game và số lượng hoặc kích thước gói tin mỗi người chơi thay đổi → “Bản cập nhật làm thay đổi mô hình lưu lượng” - Cách kiểm tra: Kiểm tra bằng công cụ hạ tầng (không cần code game) - Tìm hiểu thêm: Trạng thái giảm thiểu được xem trong các file dưới /sys/devices/system/cpu/vulnerabilities/. Giá trị mặc định (mitigations=auto) giảm thiểu mà vẫn bật SMT, còn nếu đặt auto,nosmt thì SMT bị tắt trên CPU có lỗ hổng, nên sau khi nâng kernel, số core logic có thể giảm một nửa. Cập nhật OS cùng ngày với bản cập nhật game sẽ khó xác định nguyên nhân, nên hãy triển khai riêng. - Nguồn: - [The kernel’s command-line parameters](https://docs.kernel.org/admin-guide/kernel-parameters.html) · Linux kernel · mitigations=: off tắt mọi biện pháp giảm thiểu lỗ hổng CPU để tăng hiệu năng nhưng khiến hệ thống bị lộ trước lỗ hổng; mặc định auto giảm thiểu mà vẫn bật SMT; auto,nosmt tắt SMT khi cần - [MDS - Microarchitectural Data Sampling](https://docs.kernel.org/admin-guide/hw-vuln/mds.html) · Linux kernel · Biện pháp giảm thiểu xóa bộ đệm CPU khi quay từ kernel về user space và khi vào máy ảo; xem trạng thái lỗ hổng và giảm thiểu qua các file dưới /sys/devices/system/cpu/vulnerabilities/; nhiều CPU phải tắt SMT mới chặn hoàn toàn được, và tắt SMT có thể ảnh hưởng lớn đến hiệu năng tùy khối lượng công việc - [Spectre Side Channels](https://docs.kernel.org/admin-guide/hw-vuln/spectre.html) · Linux kernel · Để giảm thiểu, bộ đệm dự đoán rẽ nhánh bị xóa khi context switch và khi chuyển sang máy ảo; biện pháp giảm thiểu mạnh hơn thêm overhead cho mọi chương trình - [EEVDF Scheduler](https://docs.kernel.org/scheduler/sched-eevdf.html) · Linux kernel · Linux bắt đầu chuyển từ bộ lập lịch CFS sang EEVDF từ bản 6.6 - [listen(2) — Linux manual page](https://man7.org/linux/man-pages/man2/listen.2.html) · Linux man-pages · Giá trị mặc định của somaxconn đổi từ 128 lên 4.096 từ Linux 5.4 - [ethtool(8) — Linux manual page](https://man7.org/linux/man-pages/man8/ethtool.8.html) · ethtool · Xem thông tin driver của thiết bị mạng bằng ethtool -i #### so-conntrack · Bảng conntrack của server bị đầy · conntrack table full Khi bảng theo dõi kết nối (conntrack), nơi tường lửa Linux ghi lại mọi kết nối, chạm giới hạn, gói tin mới sẽ bị bỏ. - Vì sao → Dẫn đến → Trên màn hình: Kết nối dồn dập và kết nối ngắn lặp lại liên tục làm số mục kết nối tăng → Bảng đầy, kết nối mới và một phần gói tin bị bỏ → Không vào được, dịch chuyển tức thời do mất gói không rõ lý do - Triệu chứng: Không vào được·kẹt loading, Dịch chuyển tức thời / Yếu tố: Mất gói - Ai gặp: Cả server / Khi nào: Ngay sau đăng nhập hoặc bảo trì, Khi đông người - Phụ trách chính: Hạ tầng server (Đội hạ tầng) / Phối hợp: Phát triển server (Đội phát triển game), Phát triển client (Đội phát triển game) - Việc cần làm (Đội phát triển game): Server: giảm kết nối ngắn (tái sử dụng kết nối cho lời gọi giữa các server). Client: khi kết nối thất bại hoặc bị ngắt, tăng dần khoảng cách thử lại và rải ngẫu nhiên. - Việc cần làm (Đội hạ tầng): Tăng kích thước bảng (nf_conntrack_max), bỏ cổng game khỏi theo dõi (NOTRACK trong bảng raw), cảnh báo theo mức sử dụng. - Con số tham khảo: Giới hạn mặc định khoảng 60.000–260.000 mục tùy bộ nhớ server. Khi tràn, log kernel in ra “nf_conntrack: table full, dropping packet”. - Trên đồ thị: Chạm giới hạn rồi đi ngang (Số mục conntrack (nf_conntrack_count)) - Chỗ cần xem: net.netfilter.nf_conntrack_count (số mục hiện tại) trong sysctl, đặt cùng đồ thị với nf_conntrack_max; tìm “nf_conntrack: table full, dropping packet” trong dmesg - Đúng nếu: nf_conntrack_count đi ngang ở mức max, và từ lúc đó log kernel in ra table full - Loại trừ nếu: số mục còn cách xa max. Giới hạn theo dõi kết nối của chính instance AWS thì xem conntrack_allowance_exceeded ở “Vượt giới hạn PPS trên cloud” - Cách kiểm tra: Kiểm tra bằng công cụ hạ tầng (không cần code game) - Nguồn: - [Netfilter Conntrack Sysfs variables](https://docs.kernel.org/networking/nf_conntrack-sysctl.html) · Linux kernel · Giá trị mặc định của nf_conntrack_max bằng số hash bucket (nf_conntrack_buckets), và số bucket được xác định theo dung lượng bộ nhớ - [net/netfilter/nf_conntrack_core.c (Linux v6.12)](https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/net/netfilter/nf_conntrack_core.c?h=v6.12) · Linux kernel · Kích thước mặc định là 65.536 nếu bộ nhớ trên 1 GB, 262.144 nếu trên 4 GB (64-bit); khi đầy, kernel ghi “nf_conntrack: table full, dropping packet” rồi bỏ gói - [iptables-extensions(8) — Linux manual page](https://man7.org/linux/man-pages/man8/iptables-extensions.8.html) · netfilter · Dùng CT --notrack trong bảng raw để bỏ khỏi theo dõi kết nối #### so-ports · Cạn cổng tạm (ephemeral port) ở kết nối giữa các server · Ephemeral port exhaustion (TIME_WAIT) Khi server game mở rồi đóng kết nối ngắn tới DB hoặc server khác quá thường xuyên, các kết nối đã đóng vẫn chiếm giữ cổng một thời gian, nên không mở được kết nối mới. - Vì sao → Dẫn đến → Trên màn hình: Mỗi yêu cầu lại mở rồi đóng một kết nối mới → Bên đóng trước chiếm giữ cổng khoảng 60 giây (Linux) ở trạng thái TIME_WAIT, nên hết cổng để dùng → Yêu cầu nội bộ thất bại, gây lỗi lưu dữ liệu và lỗi tính năng - Triệu chứng: Nuốt thao tác·rollback, Không vào được·kẹt loading / Yếu tố: Mất gói - Ai gặp: Cả server, Chỉ một tính năng / Khi nào: Khi đông người - Phụ trách chính: Phát triển server (Đội phát triển game) / Phối hợp: Hạ tầng server (Đội hạ tầng) - Việc cần làm (Đội phát triển game): Tái sử dụng kết nối (connection pool), không mở rồi đóng kết nối mới cho mỗi yêu cầu. - Việc cần làm (Đội hạ tầng): Mở rộng dải cổng (ip_local_port_range), cân nhắc tái sử dụng TIME_WAIT cho kết nối đi ra (tcp_tw_reuse trên Linux), giám sát số TIME_WAIT. - Con số tham khảo: Dải cổng mặc định của Linux (32768–60999) có khoảng 28.000 cổng. Mở quá 470 kết nối mới trong 1 giây tới cùng một địa chỉ đích là cạn. Windows mặc định có khoảng 16.000 cổng (49152–65535) và TIME_WAIT cũng dài hơn nên còn cạn nhanh hơn. - Trên đồ thị: Chạm giới hạn rồi đi ngang (Số socket TIME_WAIT, số lần kết nối nội bộ thất bại) - Chỗ cần xem: Đếm socket TIME_WAIT theo từng địa chỉ đích bằng ss -tan state time-wait; tìm các lần connect thất bại (EADDRNOTAVAIL) trong log server game - Đúng nếu: số TIME_WAIT tới cùng một đích (DB chẳng hạn) đi ngang gần kích thước dải cổng tạm (mặc định khoảng 28.000), và connect thất bại với EADDRNOTAVAIL - Loại trừ nếu: TIME_WAIT ít mà chỉ kết nối đi ra bên ngoài thất bại → “Giới hạn kết nối và cổng của NAT gateway trên cloud” - Cách kiểm tra: Kiểm tra bằng công cụ hạ tầng (không cần code game) - Tìm hiểu thêm: TIME_WAIT 60 giây của Linux là giá trị cố định trong kernel. Giảm tcp_fin_timeout (tên nghe giống) cũng không làm TIME_WAIT ngắn lại. - Nguồn: - [IP Sysctl](https://docs.kernel.org/networking/ip-sysctl.html) · Linux kernel · ip_local_port_range mặc định 32768–60999; tcp_tw_reuse; tcp_fin_timeout là thời gian giữ trạng thái FIN_WAIT_2 - [include/net/tcp.h (Linux v6.12)](https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/include/net/tcp.h?h=v6.12) · Linux kernel · TCP_TIMEWAIT_LEN(60*HZ): TIME_WAIT khoảng 60 giây là hằng số của kernel - [TCP/IP port exhaustion troubleshooting](https://learn.microsoft.com/en-us/troubleshoot/windows-client/networking/tcp-ip-port-exhaustion-troubleshooting) · Microsoft · Dải cổng động mặc định của Windows là 49152–65535; kết nối đã đóng giữ cổng ở trạng thái TIME_WAIT, mặc định 4 phút - [ss(8) — Linux manual page](https://man7.org/linux/man-pages/man8/ss.8.html) · iproute2 · Bộ lọc trạng thái state time-wait chỉ hiển thị socket TIME_WAIT - [connect(2) — Linux manual page](https://man7.org/linux/man-pages/man2/connect.2.html) · Linux man-pages · EADDRNOTAVAIL: mọi cổng trong dải cổng tạm đều đang được dùng nên không mở được kết nối ### L8 Socket và giao thức (14 nguyên nhân) #### sk-hol · TCP HOL blocking · Head-of-line blocking Để giữ đúng thứ tự, TCP không giao cho game các gói đến sau cho tới khi nhận lại được gói bị mất đó. - Vì sao → Dẫn đến → Trên màn hình: Một gói tin bị mất → Các gói phía sau đã đến nhưng phải nằm chờ trong bộ đệm nhận → Game đứng hình rồi dữ liệu ùa ra cùng một lúc, gây tua nhanh - Triệu chứng: Đứng hình, Tua nhanh / Yếu tố: Mất gói, Ngưng trệ - Ai gặp: Chỉ mình tôi / Khi nào: Thỉnh thoảng bất chợt - Phụ trách chính: Phát triển server (Đội phát triển game) / Phối hợp: Phát triển client (Đội phát triển game) - Việc cần làm (Đội phát triển game): Server: gửi vị trí thời gian thực bằng UDP, chỉ truyền tin cậy những gì thật sự cần, chia thành nhiều stream. Client: đổi cách xử lý mạng theo cùng cách với server (UDP, tách kênh). - Con số tham khảo: Mất một gói là kết nối đứng ít nhất một thời gian khứ hồi cộng thêm một chút; nếu gói truyền lại cũng mất thì đứng từ vài trăm ms đến vài giây. - Trên đồ thị: Đứt quãng rồi dồn về (Lượng dữ liệu nhận theo từng kết nối, số lần truyền lại) - Chỗ cần xem: Gói truyền lại và khoảng trống trước sau nó trên kết nối của người chơi đó, xem bằng bản bắt gói (packet capture) phía server (tcpdump, Wireshark); với cả server, xem mức tăng TcpRetransSegs trong nstat -az - Đúng nếu: mỗi đoạn đứng bắt đầu bằng việc truyền lại một gói, và ngay sau khi gói truyền lại đến, dữ liệu bị dồn được xử lý cùng một lúc (lượng nhận đang ở 0 rồi vọt lên) - Loại trừ nếu: game giao tiếp bằng UDP (nguyên nhân này không áp dụng). Không có truyền lại mà vẫn đứng → xem phía tick của server (“Vượt tick budget”) - Cách kiểm tra: Kiểm tra bằng công cụ hạ tầng (không cần code game) - Nguồn: - [RFC 9293: Transmission Control Protocol (TCP)](https://www.rfc-editor.org/rfc/rfc9293) · IETF · TCP là dịch vụ luồng byte tin cậy và giữ đúng thứ tự - [RFC 5681: TCP Congestion Control](https://www.rfc-editor.org/rfc/rfc5681) · IETF · Phát hiện mất gói qua 3 ACK trùng lặp thì truyền lại nhanh, nếu không thì chờ timer truyền lại - [RFC 6298: Computing TCP's Retransmission Timer](https://www.rfc-editor.org/rfc/rfc6298) · IETF · Backoff nhân đôi thời gian chờ mỗi lần timer truyền lại hết hạn - [net/ipv4/proc.c (Linux v6.12)](https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/net/ipv4/proc.c?h=v6.12) · Linux kernel · Tên bộ đếm mà nstat hiển thị: RetransSegs (số segment đã truyền lại) trong nhóm Tcp #### sk-rto · TCP RTO và exponential backoff · RTO and exponential backoff Mỗi lần truyền lại tiếp tục thất bại, thời gian chờ lại tăng gấp đôi, nên đường truyền chỉ đứt chốc lát cũng thành một lần đứng dài. - Vì sao → Dẫn đến → Trên màn hình: Đường truyền bị đứt trong chốc lát, các lần truyền lại cũng liên tiếp thất bại → Thời gian chờ đến lần thử sau tăng gấp đôi mỗi lần, như 0,3 → 0,6 → 1,2 → 2,4 giây (với ping 100 ms) → Đường truyền chỉ đứt 1 giây nhưng game đứng hình hơn 2 giây. Đứt lâu hơn thì cuối cùng mất kết nối - Triệu chứng: Đứng hình, Mất kết nối / Yếu tố: Mất gói, Ngưng trệ - Ai gặp: Chỉ mình tôi / Khi nào: Thỉnh thoảng bất chợt, Khi di chuyển hoặc chuyển bản đồ - Phụ trách chính: Phát triển server (Đội phát triển game) / Phối hợp: Phát triển client (Đội phát triển game) - Việc cần làm (Đội phát triển game): Server: phản hồi heartbeat, nếu không nhận được trong một khoảng thời gian thì chủ động đóng kết nối (dùng TCP_USER_TIMEOUT để bỏ cuộc sớm hơn), nối lại phiên bằng session token, dùng UDP tin cậy. Client: gửi heartbeat với khoảng cách ngắn, khi mất phản hồi thì kết nối lại ngay, không chờ TCP truyền lại. - Con số tham khảo: RTO (thời gian chờ truyền lại) của Linux tối thiểu là “ping + 200 ms”, và bắt đầu từ 1 giây khi đang thiết lập kết nối. Với cấu hình mặc định (tcp_retries2=15), dù truyền lại liên tục thất bại, phải sau khoảng 15 phút kết nối mới bị bỏ. - Trên đồ thị: Đứt quãng rồi dồn về (RTO và backoff theo từng kết nối, số lần RTO hết hạn) - Chỗ cần xem: rto (thời gian chờ truyền lại, ms) và backoff (số lần hết hạn liên tiếp) của kết nối đang đứng, xem bằng ss -ti; với cả server, xem mức tăng TcpExtTCPTimeouts (số lần timer truyền lại hết hạn) trong nstat -az - Đúng nếu: kết nối đang đứng có backoff từ 1 trở lên và rto đã tăng lên hàng giây, đồng thời TCPTimeouts tăng vào lúc đó - Loại trừ nếu: truyền lại kết thúc bằng truyền lại nhanh và không có RTO hết hạn (khi đó lần đứng ngắn) → “TCP HOL blocking” - Cách kiểm tra: Kiểm tra bằng công cụ hạ tầng (không cần code game) - Nguồn: - [RFC 6298: Computing TCP's Retransmission Timer](https://www.rfc-editor.org/rfc/rfc6298) · IETF · RTO ban đầu 1 giây, nhân đôi mỗi lần timer hết hạn (exponential backoff) - [net/ipv4/tcp_input.c (Linux v6.12)](https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/net/ipv4/tcp_input.c?h=v6.12) · Linux kernel · RTO của Linux = RTT làm mượt + độ biến thiên RTT, và độ biến thiên có mức sàn là tcp_rto_min (200 ms), nên RTO luôn từ RTT + 200 ms trở lên - [IP Sysctl](https://docs.kernel.org/networking/ip-sysctl.html) · Linux kernel · tcp_rto_min_us mặc định 200 ms, RTO đầu tiên của yêu cầu kết nối là 1 giây, với tcp_retries2=15 thì phải ít nhất 924,6 giây (khoảng 15 phút) mới bỏ cuộc - [ss(8) — Linux manual page](https://man7.org/linux/man-pages/man8/ss.8.html) · iproute2 · rto (timer truyền lại, ms) và backoff (số lần exponential backoff) trong -i - [net/ipv4/proc.c (Linux v6.12)](https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/net/ipv4/proc.c?h=v6.12) · Linux kernel · Tên bộ đếm mà nstat hiển thị: TCPTimeouts trong nhóm TcpExt - [net/ipv4/tcp_timer.c (Linux v6.12)](https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/net/ipv4/tcp_timer.c?h=v6.12) · Linux kernel · Mỗi lần timer truyền lại hết hạn, TCPTimeouts tăng, backoff tăng thêm một và RTO nhân đôi (đến mức tối đa) #### sk-nagle · Thuật toán Nagle + delayed ACK · Nagle + delayed ACK (TCP_NODELAY off) Thuật toán Nagle (gom gói nhỏ lại để gửi) và delayed ACK (gửi ACK muộn) tác động chồng lên nhau, nên mỗi lần tách một message ra ghi nhiều lần là bị trễ 40–200 ms. - Vì sao → Dẫn đến → Trên màn hình: Tách message nhỏ ra ghi nhiều lần mà không bật TCP_NODELAY → Bên gửi chờ ACK, còn bên nhận thì gửi ACK muộn → Ping đường truyền thấp nhưng mọi thao tác lúc nào cũng ì ạch như nhau, gây trễ thao tác - Triệu chứng: Trễ thao tác / Yếu tố: Độ trễ - Ai gặp: Cả server, Chỉ mình tôi / Khi nào: Luôn luôn, Khi làm một thao tác nhất định - Phụ trách chính: Phát triển server (Đội phát triển game) / Phối hợp: Phát triển client (Đội phát triển game) - Việc cần làm (Đội phát triển game): Server: bật TCP_NODELAY, gom message trong một tick rồi ghi một lần; đừng trông vào cách tắt delayed ACK ở bên nhận (TCP_QUICKACK của Linux chỉ giữ được trong chốc lát, Windows thì phải sửa registry trên từng PC) vì game không kiểm soát chắc chắn được. Client: bật TCP_NODELAY, gom message trong một khung hình rồi ghi một lần. - Con số tham khảo: Delayed ACK trên Linux thường là 40 ms (tùy tình huống, tối đa 200 ms). Windows bản cũ là 200 ms, bản hiện nay là 40 ms (template mặc định của Windows Server 2019 là 40 ms). Delayed ACK do OS bên nhận quyết định, nên nếu server bật Nagle mà tách message ra gửi nhiều lần, mỗi message có thể bị trễ 40–200 ms tùy PC bên nhận. - Trên đồ thị: Luôn cao ngay từ đầu (Thời gian phản hồi thao tác (RTT trong game)) - Chỗ cần xem: Khoảng cách giữa yêu cầu và phản hồi trong bản bắt gói (packet capture) phía server (tcpdump, Wireshark); code server và client có bật TCP_NODELAY hay không - Đúng nếu: ping đường truyền thấp nhưng giữa các gói nhỏ liên tục có khoảng trống quanh 40 ms (Windows bản cũ là 200 ms), và mỗi khoảng trống kết thúc ngay sau khi ACK của bên kia đến. Bật TCP_NODELAY thì khoảng trống biến mất - Loại trừ nếu: khoảng cách phản hồi gần bằng ping đường truyền. Server game tạo phản hồi chậm → phía xử lý của server (“Hàng đợi message bị dồn ứ”) - Cách kiểm tra: Kiểm tra bằng công cụ hạ tầng (không cần code game) - Nguồn: - [RFC 9293: Transmission Control Protocol (TCP)](https://www.rfc-editor.org/rfc/rfc9293) · IETF · Nagle giữ lại dữ liệu nhỏ khi còn dữ liệu chưa nhận được ACK; phải tắt được theo từng kết nối; delayed ACK dưới 0,5 giây; vấn đề khi hai cơ chế tác động chồng lên nhau - [include/net/tcp.h (Linux v6.12)](https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/include/net/tcp.h?h=v6.12) · Linux kernel · Delayed ACK trên Linux tối thiểu TCP_DELACK_MIN (HZ/25 = 40 ms), tối đa TCP_DELACK_MAX (HZ/5 = 200 ms) - [TCP improvements in the Windows network stack (IETF 98 TCPM)](https://datatracker.ietf.org/meeting/98/materials/slides-98-tcpm-tcp-improvements-in-windows-01) · Microsoft · Timeout delayed ACK mặc định của Windows được đổi thành 40 ms (công bố năm 2017) - [TCP Templates for Windows Server 2019 – How to tune your Windows Server Transports (Advanced users only 😉)](https://techcommunity.microsoft.com/blog/networkingblog/tcp-templates-for-windows-server-2019-8211-how-to-tune-your-windows-server-trans/339795) · Microsoft · Template Server 2019: DelayedAckTimeout 40 ms, MaxSynRetransmissions 2, InitialRto 3000 ms - [Design issues - Sending small data segments over TCP with Winsock](https://learn.microsoft.com/en-us/previous-versions/troubleshoot/windows/win32/data-segment-tcp-winsock) · Microsoft · TCP trên Windows bản cũ đặt timer delayed ACK 200 ms mỗi khi nhận dữ liệu, còn Nagle bật sẵn theo mặc định, nên gói nhỏ phải chờ ACK; khắc phục bằng TCP_NODELAY #### sk-block-send · Gửi bị chặn (blocking send) do client chậm · Blocking send on a full socket Khi bộ đệm gửi của một người chơi có đường truyền chậm đã đầy mà server gửi theo kiểu blocking (lời gọi gửi không trả về cho tới khi bộ đệm có chỗ trống), thread của server phải chờ đúng một người đó. - Vì sao → Dẫn đến → Trên màn hình: Bộ đệm gửi của một client chậm bị đầy → Vì gửi kiểu blocking nên thread server phải chờ đến khi bộ đệm có chỗ trống → Mọi người chơi do thread đó phụ trách đều bị đứng hình hoặc quay chậm - Triệu chứng: Đứng hình, Quay chậm / Yếu tố: Ngưng trệ - Ai gặp: Một địa điểm hoặc kênh, Cả server / Khi nào: Thỉnh thoảng bất chợt, Khi đông người - Phụ trách chính: Phát triển server (Đội phát triển game) - Việc cần làm (Đội phát triển game): Gửi non-blocking, đặt giới hạn hàng đợi gửi cho từng client, bỏ các bản cập nhật cũ. - Trên đồ thị: Thỉnh thoảng vọt lên bất chợt (Thời gian tick của server, Send-Q theo từng kết nối) - Chỗ cần xem: Các kết nối có Send-Q (số byte chưa nhận được ACK hoặc chưa gửi đi) đã đầy bằng bộ đệm gửi, tìm bằng ss -tn; trong thread dump (stack) của server game lấy lúc tick vọt lên, xem có thread nào đứng ở lời gọi send không - Đúng nếu: khi có kết nối chậm với Send-Q đầy, thread phụ trách kết nối đó đứng ở send, và chỉ những người chơi cùng thread đó bị đứng theo - Loại trừ nếu: thread bị đứng đang chờ ở ngoài send (lock, lời gọi DB) → “Tranh chấp lock”, “Gọi đồng bộ (blocking) trên game thread” - Cách kiểm tra: Kiểm tra bằng công cụ hạ tầng (không cần code game) - Nguồn: - [send(2) — Linux manual page](https://man7.org/linux/man-pages/man2/send.2.html) · Linux man-pages · Khi bộ đệm gửi hết chỗ, send() bị block; ở chế độ non-blocking thì trả về ngay với EAGAIN - [send function (winsock2.h)](https://learn.microsoft.com/en-us/windows/win32/api/winsock2/nf-winsock2-send) · Microsoft · Winsock cũng vậy, khi bộ đệm hết chỗ thì send bị block, trừ khi đang ở chế độ non-blocking - [net/ipv4/tcp_diag.c (Linux v6.12)](https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/net/ipv4/tcp_diag.c?h=v6.12) · Linux kernel · Giá trị Recv-Q và Send-Q trong ss: với socket đang listen là số kết nối chờ accept và giới hạn backlog; với socket đã kết nối là số byte ứng dụng chưa đọc và số byte đã gửi nhưng chưa nhận được ACK #### sk-slow-client · Chính sách xử lý client chậm (slow consumer) · Slow-consumer policy Với client mà dữ liệu cần gửi cứ dồn lên mãi, server bỏ bớt các bản cập nhật cũ hoặc ngắt kết nối. - Vì sao → Dẫn đến → Trên màn hình: Đường truyền của client không theo kịp lượng dữ liệu server gửi → Server bỏ các bản cập nhật cũ, hoặc ngắt kết nối khi vượt giới hạn → Chỉ người chơi đó bị dịch chuyển tức thời hoặc mất kết nối - Triệu chứng: Dịch chuyển tức thời, Mất kết nối / Yếu tố: Mất gói - Ai gặp: Chỉ mình tôi / Khi nào: Khi đông người - Phụ trách chính: Phát triển server (Đội phát triển game) - Việc cần làm (Đội phát triển game): Giảm lượng gửi (tần suất cập nhật theo khoảng cách), hạ chất lượng nhưng vẫn gửi đều, giảm lượng dữ liệu nằm chờ trong kernel (TCP_NOTSENT_LOWAT trên Linux). - Con số tham khảo: Với bộ đệm gửi 256 KB, trên đường truyền 30 KB/s sẽ dồn lại lượng dữ liệu của hơn 8 giây. Linux còn có thể tự động nới bộ đệm này lên tới vài MB. - Trên đồ thị: Chỉ một phần cao (Send-Q theo từng kết nối, số bản cập nhật bị bỏ theo từng client) - Chỗ cần xem: Độ dài hàng đợi gửi, số bản cập nhật bị bỏ và lý do ngắt kết nối theo từng client mà server game ghi lại; trên server, xem cùng lúc Send-Q và cwnd của kết nối đó bằng ss -tni - Đúng nếu: chỉ kết nối của những người bị nhảy vị trí hoặc mất kết nối là có Send-Q luôn đầy, và log game ghi lại việc bỏ bản cập nhật hoặc ngắt kết nối do vượt hàng đợi gửi của những người đó - Loại trừ nếu: Send-Q trống mà người chơi vẫn bị dịch chuyển tức thời (vấn đề không nằm ở phía gửi của server). Xem mất gói trên đường truyền của người đó (“Mất gói ở chặng không dây”) hoặc phần nội suy trên màn hình - Cách kiểm tra: Cần log và chỉ số của server, client game - Nguồn: - [IP Sysctl](https://docs.kernel.org/networking/ip-sysctl.html) · Linux kernel · tcp_wmem: mức tối đa của bộ đệm gửi tự điều chỉnh mặc định là 64 KB–4 MB (tùy bộ nhớ); tcp_notsent_lowat và TCP_NOTSENT_LOWAT giới hạn lượng dữ liệu chưa gửi - [net/ipv4/tcp_diag.c (Linux v6.12)](https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/net/ipv4/tcp_diag.c?h=v6.12) · Linux kernel · Giá trị Recv-Q và Send-Q trong ss: với socket đang listen là số kết nối chờ accept và giới hạn backlog; với socket đã kết nối là số byte ứng dụng chưa đọc và số byte đã gửi nhưng chưa nhận được ACK #### sk-keepalive · Keepalive mặc định 2 giờ · TCP keepalive defaults Khi bên kia biến mất mà không gửi tín hiệu đóng kết nối, rất lâu sau TCP mới phát hiện. Keepalive (tính năng của TCP kiểm tra xem kết nối nhàn rỗi còn sống không) mặc định tắt, và dù có bật thì kết nối cũng phải nhàn rỗi 2 giờ mới bắt đầu kiểm tra. - Vì sao → Dẫn đến → Trên màn hình: Client biến mất mà không có tín hiệu đóng kết nối, do mất điện hoặc rớt đường truyền → Server coi kết nối vẫn còn sống (keepalive mặc định 7.200 giây; nếu đang gửi dở dữ liệu thì mất khoảng 15 phút mới bỏ truyền lại) → Nhân vật ma còn nằm lại, vào lại game thì bị lỗi “Tài khoản đang đăng nhập” - Triệu chứng: Không vào được·kẹt loading, Không hiển thị·đối tượng ma / Yếu tố: Mất gói - Ai gặp: Chỉ mình tôi / Khi nào: Sau khi để yên một lúc, Ngay sau đăng nhập hoặc bảo trì - Phụ trách chính: Phát triển server (Đội phát triển game) / Phối hợp: Phát triển client (Đội phát triển game), Hạ tầng server (Đội hạ tầng) - Việc cần làm (Đội phát triển game): Server: phản hồi heartbeat, nếu không nhận được trong một khoảng thời gian thì chủ động đóng kết nối (chỉnh TCP_KEEPIDLE, TCP_USER_TIMEOUT); khi người chơi kết nối lại, dùng session token để thay phiên cũ và nối tiếp. Client: gửi heartbeat ở tầng game cách vài giây đến vài chục giây một lần (bằng hoặc ngắn hơn một nửa idle timeout ngắn nhất), tự động kết nối lại khi bị ngắt. - Việc cần làm (Đội hạ tầng): Hạ giá trị mặc định của kernel (tcp_keepalive_time, v.v.) cho các socket mà code không tự đặt (chỉ áp dụng cho socket đã bật SO_KEEPALIVE). - Con số tham khảo: Mặc định trên Linux, kết nối nhàn rỗi 7.200 giây thì bắt đầu kiểm tra, gửi 9 probe cách nhau 75 giây, và nếu đến cuối vẫn không có phản hồi thì ngắt kết nối. Cộng lại khoảng 2 giờ 11 phút. Windows mặc định cũng phải nhàn rỗi 2 giờ mới bắt đầu kiểm tra. - Trên đồ thị: Chỉ một phần cao (Thời gian từ lần nhận cuối theo từng kết nối) - Chỗ cần xem: lastrcv (số ms từ lần nhận cuối) và timer keepalive (timer:(keepalive,…)) theo từng kết nối qua ss -tnoi, đối chiếu với các lần server game từ chối vì “Tài khoản đang đăng nhập” - Đúng nếu: vẫn còn kết nối ESTABLISHED có lastrcv từ vài phút đến vài giờ, và các lần kết nối lại của tài khoản đó bị từ chối với “Tài khoản đang đăng nhập” - Loại trừ nếu: không có kết nối nào im lặng lâu mà vẫn gặp “Tài khoản đang đăng nhập” → xem code dọn phiên của server game - Cách kiểm tra: Kiểm tra bằng công cụ hạ tầng (không cần code game) - Nguồn: - [tcp(7) — Linux manual page](https://man7.org/linux/man-pages/man7/tcp.7.html) · Linux man-pages · Sau 7.200 giây nhàn rỗi, gửi 9 probe cách nhau 75 giây (thêm khoảng 11 phút); chỉ áp dụng cho socket đã bật SO_KEEPALIVE; TCP_KEEPIDLE, TCP_USER_TIMEOUT - [RFC 9293: Transmission Control Protocol (TCP)](https://www.rfc-editor.org/rfc/rfc9293) · IETF · Keepalive phải tắt theo mặc định, và khoảng nhàn rỗi mặc định phải từ 2 giờ trở lên - [SO_KEEPALIVE socket option](https://learn.microsoft.com/en-us/windows/win32/winsock/so-keepalive) · Microsoft · Timeout keepalive TCP mặc định của Windows là 2 giờ - [ss(8) — Linux manual page](https://man7.org/linux/man-pages/man8/ss.8.html) · iproute2 · lastrcv (số ms từ lần nhận cuối) trong -i, timer:(keepalive,…) trong -o #### sk-fragment · Phân mảnh IP của gói UDP · IP fragmentation of large UDP Gói UDP lớn hơn MTU (kích thước tối đa gửi được trong một lần) bị phân mảnh ở tầng IP, và chỉ cần mất một fragment là cả gói bị bỏ. - Vì sao → Dẫn đến → Trên màn hình: Snapshot ở nơi đông người vượt quá 1.500 byte → Gói bị chia thành nhiều fragment để gửi, mất một fragment là bỏ cả gói → Gói lớn có tỷ lệ mất cao gấp mấy lần. Chỉ bị dịch chuyển tức thời ở nơi đông người - Triệu chứng: Dịch chuyển tức thời / Yếu tố: Mất gói - Ai gặp: Một địa điểm hoặc kênh, Một khu vực hoặc nhà mạng / Khi nào: Khi đông người - Phụ trách chính: Phát triển server (Đội phát triển game) - Việc cần làm (Đội phát triển game): Tự chia gói xuống từ 1.200 byte trở xuống, chỉ gửi phần thay đổi. - Con số tham khảo: Trên đường truyền mất gói 2%, gói bị chia thành 4 fragment sẽ mất khoảng 8%. Có tường lửa và nhà mạng bỏ hẳn gói bị phân mảnh, nên người chơi dùng mạng đó không nhận được gói lớn nào. - Trên đồ thị: Tăng theo số người và tải (Số lần phân mảnh IP (IpFragCreates), kích thước snapshot) - Chỗ cần xem: Trên server, xem mức tăng IpFragCreates (số fragment tạo ra khi gửi) trong nstat -az; ở bên nhận, xem IpReasmFails (số lần ghép lại thất bại). Kiểm tra phân bố kích thước gói UDP qua log server game hoặc bản bắt gói (packet capture) - Đúng nếu: IpFragCreates tăng ở nơi đông người, có gói UDP lớn hơn 1.500 byte, và cùng lúc đó số báo cáo dịch chuyển tức thời tăng - Loại trừ nếu: IpFragCreates không tăng (phía gửi của server không có phân mảnh) - Cách kiểm tra: Kiểm tra bằng công cụ hạ tầng (không cần code game) - Nguồn: - [RFC 8085: UDP Usage Guidelines](https://www.rfc-editor.org/rfc/rfc8085) · IETF · Mất một fragment thì không ghép lại được và mất cả gói; ứng dụng UDP nên tránh phân mảnh IP - [RFC 8900: IP Fragmentation Considered Fragile](https://www.rfc-editor.org/rfc/rfc8900) · IETF · Các trường hợp tường lửa và một số mạng bỏ fragment IP - [net/ipv4/proc.c (Linux v6.12)](https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/net/ipv4/proc.c?h=v6.12) · Linux kernel · Tên bộ đếm mà nstat hiển thị: FragCreates (số fragment đã tạo) và ReasmFails (số lần ghép lại thất bại) trong nhóm Ip #### sk-reliable-udp · Cấu hình truyền lại của UDP tin cậy · Reliable-UDP tuning (KCP, ENet…) Quy tắc truyền lại tự xây trên UDP mà quá thận trọng thì khôi phục chậm, còn quá mạnh tay thì càng làm nghẽn đường truyền. - Vì sao → Dẫn đến → Trên màn hình: Khoảng cách truyền lại, số lần thử và kích thước cửa sổ được cấu hình không hợp với đường truyền → Khôi phục chậm, hoặc gửi trùng lặp làm tắc nghẽn nặng thêm → Nuốt skill, tua nhanh, lag nặng hơn khi mạng tắc nghẽn - Triệu chứng: Nuốt thao tác·rollback, Tua nhanh / Yếu tố: Mất gói, Độ trễ - Ai gặp: Chỉ mình tôi / Khi nào: Thỉnh thoảng bất chợt - Phụ trách chính: Phát triển server (Đội phát triển game) / Phối hợp: Phát triển client (Đội phát triển game) - Việc cần làm (Đội phát triển game): Server: truyền lại dựa trên thời gian khứ hồi đo được, tách kênh theo mức độ quan trọng. Client: áp dụng cùng cấu hình truyền lại và kênh như server. - Trên đồ thị: Thỉnh thoảng vọt lên bất chợt (Tỷ lệ truyền lại của UDP tin cậy, RTT trong game) - Chỗ cần xem: Thống kê theo từng kết nối của thư viện đang dùng (số lần truyền lại, thời gian khứ hồi ước tính, thời gian chờ truyền lại), ghi lại ở server và client rồi so với tỷ lệ mất gói thực tế trên đường truyền của cùng người chơi (đo bằng mtr) - Đúng nếu: tỷ lệ truyền lại cao gấp mấy lần tỷ lệ mất gói thực tế (cấu hình quá mạnh tay), hoặc thời gian chờ truyền lại gấp mấy lần thời gian khứ hồi đo được (cấu hình quá thận trọng) - Loại trừ nếu: tỷ lệ truyền lại gần bằng tỷ lệ mất gói trên đường truyền và thời gian chờ khớp với thời gian khứ hồi (cấu hình vẫn ổn). Xem chính việc mất gói trên đường truyền - Cách kiểm tra: Cần log và chỉ số của server, client game - Nguồn: - [RFC 8085: UDP Usage Guidelines](https://www.rfc-editor.org/rfc/rfc8085) · IETF · Truyền lại có thể làm tắc nghẽn nặng thêm nên phải chịu kiểm soát tắc nghẽn; thời gian khứ hồi được ước tính bằng trung bình của nhiều lần đo (EWMA), giá trị ban đầu 1 giây; hạ tốc độ gửi khi timer hết hạn #### sk-slowstart · Slow start sau thời gian nhàn rỗi · Slow start after idle Khi kết nối nhàn rỗi một lúc, TCP thu nhỏ lại cửa sổ tắc nghẽn (lượng dữ liệu gửi được trong một lần), nên khi đột ngột gửi dữ liệu lớn, TCP phải chia ra gửi nhiều lượt. - Vì sao → Dẫn đến → Trên màn hình: Gửi dữ liệu lớn (ví dụ khi vào thị trấn) qua một kết nối đang nhàn rỗi → Cửa sổ tắc nghẽn đã bị thu nhỏ nên dữ liệu bị chia ra gửi qua nhiều lượt khứ hồi → Ngay sau khi vào, nhân vật và NPC xung quanh hiện ra trễ vài lượt khứ hồi (server càng xa càng dễ thấy) - Triệu chứng: Trễ thao tác, Không hiển thị·đối tượng ma / Yếu tố: Độ trễ - Ai gặp: Chỉ mình tôi / Khi nào: Khi di chuyển hoặc chuyển bản đồ, Sau khi để yên một lúc - Phụ trách chính: Hạ tầng server (Đội hạ tầng) / Phối hợp: Phát triển server (Đội phát triển game) - Việc cần làm (Đội phát triển game): Giảm dữ liệu gửi lúc vào (gửi những gì thật sự cần trước). - Việc cần làm (Đội hạ tầng): Tắt tcp_slow_start_after_idle (Linux, cấu hình cho cả server). - Con số tham khảo: Nhàn rỗi lâu hơn RTO thì cửa sổ tắc nghẽn bắt đầu thu nhỏ; nếu nhàn rỗi lâu, nó giảm xuống khoảng 14 KB (10 gói). Khi đó 100 KB không gửi được trong một lần mà phải chia ra 3 lượt khứ hồi. - Trên đồ thị: Chỉ một phần cao (Thời gian truyền ngay sau khi vào (người chơi có RTT dài)) - Chỗ cần xem: Giá trị sysctl net.ipv4.tcp_slow_start_after_idle; lúc người chơi vào một khu vực sau khi nhàn rỗi, cwnd (cửa sổ tắc nghẽn) của kết nối đó trong ss -ti có bị thu nhỏ không - Đúng nếu: cấu hình là 1 (mặc định), và lúc vào sau khi nhàn rỗi, cwnd giảm xuống quanh 10 nên việc truyền bị chia ra nhiều lượt khứ hồi. Người chơi có RTT càng dài thì mọi thứ hiện ra càng trễ, và đổi thành 0 thì hết - Loại trừ nếu: cwnd vẫn giữ ở mức lớn mà mọi thứ vẫn hiện ra trễ → phần xử lý vào khu vực phía server (“Spawn dồn dập khi vào khu vực đông người”) - Cách kiểm tra: Kiểm tra bằng công cụ hạ tầng (không cần code game) - Nguồn: - [IP Sysctl](https://docs.kernel.org/networking/ip-sysctl.html) · Linux kernel · tcp_slow_start_after_idle mặc định bật; nhàn rỗi trong một khoảng RTO thì thu nhỏ cửa sổ tắc nghẽn (theo cách của RFC 2861) - [RFC 5681: TCP Congestion Control](https://www.rfc-editor.org/rfc/rfc5681) · IETF · Nếu không gửi dữ liệu lâu hơn RTO, cửa sổ tắc nghẽn bị giảm xuống tối đa bằng cửa sổ khởi động lại min(IW, cwnd) và slow start lại từ đầu - [RFC 6928: Increasing TCP's Initial Window](https://www.rfc-editor.org/rfc/rfc6928) · IETF · Cửa sổ ban đầu 10 segment, tối đa 14.600 byte - [ss(8) — Linux manual page](https://man7.org/linux/man-pages/man8/ss.8.html) · iproute2 · cwnd (cửa sổ tắc nghẽn) và ssthresh (ngưỡng slow start) trong -i #### sk-congestion · Lượng gửi giảm mạnh do kiểm soát tắc nghẽn · Congestion control backoff TCP coi mất gói là tín hiệu tắc nghẽn và giảm tốc độ gửi 30–50%. Mất gói do Wi-Fi cũng bị xử lý y như vậy. - Vì sao → Dẫn đến → Trên màn hình: Có chút mất gói trên Wi-Fi hoặc đường truyền đúng lúc cần gửi nhiều dữ liệu → TCP giảm mạnh tốc độ gửi rồi hồi phục chậm (CUBIC, thuật toán mặc định của Linux và Windows, giảm 30%) → Ở nơi đông người, bản cập nhật bị dồn lại, gây tua nhanh và trễ thao tác - Triệu chứng: Tua nhanh, Trễ thao tác / Yếu tố: Độ trễ, Ngưng trệ - Ai gặp: Chỉ mình tôi / Khi nào: Khi đông người - Phụ trách chính: Hạ tầng server (Đội hạ tầng) / Phối hợp: Phát triển server (Đội phát triển game) - Việc cần làm (Đội phát triển game): Giảm lượng gửi (vùng quan tâm, chỉ gửi phần thay đổi), chia nhỏ ra gửi để không dồn một lượt. - Việc cần làm (Đội hạ tầng): Chuyển sang thuật toán kiểm soát tắc nghẽn như BBR (tcp_congestion_control). - Trên đồ thị: Tăng dần rồi rơi thẳng (Cửa sổ tắc nghẽn (cwnd) và tốc độ gửi theo từng kết nối) - Chỗ cần xem: Chụp ss -ti nhiều lần cho kết nối của người chơi bị dồn bản cập nhật để xem cwnd, ssthresh thay đổi thế nào và tên thuật toán kiểm soát tắc nghẽn (cubic, bbr), đồng thời xem Send-Q có dồn lên không - Đúng nếu: sau mỗi lần mất gói, cwnd giảm mạnh rồi tăng lại chậm, lặp đi lặp lại; trong lúc cwnd thấp, Send-Q dồn lên, trùng với thời điểm người chơi báo tua nhanh và trễ thao tác - Loại trừ nếu: cwnd dư dả mà vẫn bị dồn → xem cửa sổ của bên nhận (“Zero window (khoảng dừng dễ nhầm là truyền lại)”) hoặc phía gửi của server - Cách kiểm tra: Kiểm tra bằng công cụ hạ tầng (không cần code game) - Nguồn: - [RFC 9438: CUBIC for Fast and Long-Distance Networks](https://www.rfc-editor.org/rfc/rfc9438) · IETF · Khi mất gói, CUBIC nhân cửa sổ với 0,7 (giảm 30%), Reno nhân với 0,5; CUBIC là mặc định trên Linux, Windows và các nền tảng của Apple - [TCP BBR congestion control comes to GCP – your Internet just got faster](https://cloud.google.com/blog/products/networking/tcp-bbr-congestion-control-comes-to-gcp-your-internet-just-got-faster) · Google Cloud · Kiểm soát tắc nghẽn dựa trên mất gói giảm mạnh tốc độ gửi ngay cả với những lần mất gói không do tắc nghẽn; BBR quyết định dựa trên tốc độ truyền tới đích và RTT - [IP Sysctl](https://docs.kernel.org/networking/ip-sysctl.html) · Linux kernel · tcp_congestion_control chọn thuật toán kiểm soát tắc nghẽn cho các kết nối mới - [ss(8) — Linux manual page](https://man7.org/linux/man-pages/man8/ss.8.html) · iproute2 · cwnd, ssthresh và tên thuật toán kiểm soát tắc nghẽn trong -i - [net/ipv4/tcp_diag.c (Linux v6.12)](https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/net/ipv4/tcp_diag.c?h=v6.12) · Linux kernel · Giá trị Recv-Q và Send-Q trong ss: với socket đang listen là số kết nối chờ accept và giới hạn backlog; với socket đã kết nối là số byte ứng dụng chưa đọc và số byte đã gửi nhưng chưa nhận được ACK #### sk-linger · Mất dữ liệu cuối do đóng cưỡng bức bằng RST · SO_LINGER, abrupt RST Khi server cắt kết nối đột ngột, thông báo cuối cùng hoặc tín hiệu đã lưu xong mà server vừa gửi sẽ bị mất. - Vì sao → Dẫn đến → Trên màn hình: Server đóng kết nối kiểu cưỡng bức (RST). Xảy ra khi đặt SO_LINGER là 0 giây, hoặc khi đóng socket trước khi đọc hết dữ liệu đã nhận → Lý do kick và dữ liệu cuối cùng còn đang trên đường gửi bị vứt bỏ → Thông báo “Kết nối bị đóng do lỗi không xác định” mà không rõ lý do - Triệu chứng: Mất kết nối / Yếu tố: Mất gói - Ai gặp: Chỉ mình tôi / Khi nào: Thỉnh thoảng bất chợt - Phụ trách chính: Phát triển server (Đội phát triển game) - Việc cần làm (Đội phát triển game): Gửi lý do xong thì chỉ đóng chiều gửi trước (shutdown), đọc hết dữ liệu nhận được cho đến khi bên kia đóng rồi mới đóng socket, tránh SO_LINGER 0 giây. - Trên đồ thị: Thỉnh thoảng vọt lên bất chợt (Số kết nối kết thúc bằng RST) - Chỗ cần xem: Mức tăng TcpExtTCPAbortOnData (đóng bằng RST khi vẫn còn dữ liệu cần gửi, SO_LINGER 0 giây) và TcpExtTCPAbortOnClose (đóng khi vẫn còn dữ liệu chưa đọc) trong nstat -az; trong bản bắt gói (packet capture) phía server lúc mất kết nối, xem có RST đi ra ở chỗ lẽ ra phải là FIN không - Đúng nếu: vào thời điểm người chơi báo “Kết nối bị đóng do lỗi không xác định”, server gửi RST, và AbortOnData, AbortOnClose tăng - Loại trừ nếu: server đã đóng bình thường bằng FIN mà vẫn không thấy lý do → xem phần xử lý đóng kết nối của client - Cách kiểm tra: Kiểm tra bằng công cụ hạ tầng (không cần code game) - Nguồn: - [closesocket function (winsock.h)](https://learn.microsoft.com/en-us/windows/win32/api/winsock/nf-winsock-closesocket) · Microsoft · Bật SO_LINGER và đặt thời gian là 0 thì lệnh đóng trở thành đóng cưỡng bức, reset kết nối ngay lập tức, dữ liệu chưa gửi bị mất - [RFC 9293: Transmission Control Protocol (TCP)](https://www.rfc-editor.org/rfc/rfc9293) · IETF · Đóng khi vẫn còn dữ liệu đã nhận chưa đọc thì gửi RST để báo mất dữ liệu - [Graceful Shutdown, Linger Options, and Socket Closure](https://learn.microsoft.com/en-us/windows/win32/winsock/graceful-shutdown-linger-options-and-socket-closure-2) · Microsoft · Trình tự: dùng shutdown đóng chiều gửi trước, nhận được thông báo đóng của bên kia rồi mới đóng socket - [SNMP counter](https://docs.kernel.org/networking/snmp_counter.html) · Linux kernel · TcpExtTCPAbortOnData: đóng bằng RST khi vẫn còn dữ liệu cần gửi (do SO_LINGER 0 giây, v.v.); TcpExtTCPAbortOnClose: đóng khi vẫn còn dữ liệu chưa đọc nên gửi RST #### sk-blocking-io · Kiến trúc I/O blocking · Blocking I/O model Với kiến trúc mà thread không làm được việc gì khác trong lúc chờ một socket, người chơi càng đông thì cả hệ thống càng chậm. - Vì sao → Dẫn đến → Trên màn hình: Mỗi kết nối tự chờ đọc và ghi của riêng nó → Độ trễ của một kết nối lan sang các kết nối khác trên cùng thread → Số người chơi đồng thời càng tăng, mọi người càng bị quay chậm và trễ thao tác - Triệu chứng: Quay chậm, Trễ thao tác / Yếu tố: Ngưng trệ - Ai gặp: Cả server / Khi nào: Khi đông người, Giờ cao điểm buổi tối - Phụ trách chính: Phát triển server (Đội phát triển game) - Việc cần làm (Đội phát triển game): Chuyển sang I/O bất đồng bộ dựa trên epoll, IOCP hoặc io_uring. - Trên đồ thị: Tăng theo số người và tải (Thời gian phản hồi, số thread) - Chỗ cần xem: Số thread của server game và context switch tự nguyện theo từng thread (cswch/s, số lần dừng để chờ tài nguyên) qua pidstat -w -t, so với thời gian phản hồi khi số người chơi đồng thời tăng - Đúng nếu: số người chơi đồng thời càng tăng, thời gian phản hồi càng tăng vọt, và phần lớn thread (tăng theo số kết nối) chỉ có nhiều context switch tự nguyện mà gần như không dùng CPU (đang chờ socket) - Loại trừ nếu: thread dùng CPU liên tục mà không phải chờ → quá tải tính toán (“Vượt tick budget”) - Cách kiểm tra: Kiểm tra bằng công cụ hạ tầng (không cần code game) - Nguồn: - [epoll(7) — Linux manual page](https://man7.org/linux/man-pages/man7/epoll.7.html) · Linux man-pages · Cơ chế thông báo sự kiện I/O mở rộng được để theo dõi nhiều fd cùng lúc - [I/O Completion Ports](https://learn.microsoft.com/en-us/windows/win32/fileio/i-o-completion-ports) · Microsoft · Cách làm của Windows: xử lý nhiều I/O bất đồng bộ bằng thread pool tạo sẵn - [pidstat(1) — Linux manual page](https://man7.org/linux/man-pages/man1/pidstat.1.html) · sysstat · cswch/s trong -w: context switch tự nguyện (tác vụ tự dừng để chờ tài nguyên); -t hiển thị theo từng thread #### sk-reuseport · SO_REUSEPORT phân phối bị lệch · SO_REUSEPORT imbalance, stuck worker Khi nhiều tiến trình cùng nhận trên một cổng, kernel dùng hash địa chỉ để gán mỗi kết nối cho một tiến trình và không đổi lại. Nếu một tiến trình bị dừng, chỉ những người được gán vào đó phải chờ. - Vì sao → Dẫn đến → Trên màn hình: Gateway hoặc server đăng nhập chạy nhiều tiến trình trên cùng cổng bằng SO_REUSEPORT → Dù một tiến trình bị dừng vì GC hoặc quá tải, các kết nối mới và gói UDP đã gán cho nó cũng không chuyển sang tiến trình khác → Chỉ một số người không vào được hoặc bị đứng hình. Khi khởi động lại làm thay đổi số tiến trình, một số phiên UDP bị ngắt - Triệu chứng: Không vào được·kẹt loading, Đứng hình, Mất kết nối / Yếu tố: Ngưng trệ, Mất gói - Ai gặp: Chỉ mình tôi, Cả server / Khi nào: Ngay sau đăng nhập hoặc bảo trì, Thỉnh thoảng bất chợt - Phụ trách chính: Phát triển server (Đội phát triển game) / Phối hợp: Hạ tầng server (Đội hạ tầng) - Việc cần làm (Đội phát triển game): Tuyệt đối không để thread nhận bị dừng, xây dựng quy trình chuyển giao phiên khi khởi động lại. - Việc cần làm (Đội hạ tầng): Giám sát hàng đợi kết nối theo từng tiến trình (Recv-Q trong ss), bảo đảm các lần triển khai có đổi số tiến trình đều theo quy trình chuyển giao phiên. - Trên đồ thị: Chỉ một phần cao (Hàng đợi kết nối (Recv-Q) theo từng socket đang listen) - Chỗ cần xem: Recv-Q (số kết nối chờ accept) và tiến trình sở hữu của từng socket đang listen trên cùng cổng qua ss -ltnp, kèm so sánh thông lượng theo từng tiến trình - Đúng nếu: trong các socket cùng cổng, chỉ một socket có Recv-Q liên tục dồn lên, và tiến trình của nó đang bị dừng hoặc có thông lượng gần 0 - Loại trừ nếu: Recv-Q dồn lên đều ở mọi socket → quá tải toàn bộ (“Tràn hàng đợi kết nối (backlog)”) - Cách kiểm tra: Kiểm tra bằng công cụ hạ tầng (không cần code game) - Nguồn: - [socket(7) — Linux manual page](https://man7.org/linux/man-pages/man7/socket.7.html) · Linux man-pages · SO_REUSEPORT cho nhiều socket bind vào cùng một địa chỉ và chia nhau nhận kết nối TCP và gói UDP - [net/core/sock_reuseport.c (Linux v6.12)](https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/net/core/sock_reuseport.c?h=v6.12) · Linux kernel · Nếu không có chương trình BPF, giá trị hash của gói được chia theo số socket trong nhóm để chọn socket phụ trách - [Why does one NGINX worker take all the load?](https://blog.cloudflare.com/the-sad-state-of-linux-socket-balancing/) · Cloudflare · SO_REUSEPORT chia hàng đợi cho từng worker bằng một hàm hash đơn giản, nên nếu một worker bị nghẽn, mọi kết nối dồn trong hàng đợi của nó đều đứng - [net/ipv4/tcp_diag.c (Linux v6.12)](https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/net/ipv4/tcp_diag.c?h=v6.12) · Linux kernel · Giá trị Recv-Q và Send-Q trong ss: với socket đang listen là số kết nối chờ accept và giới hạn backlog; với socket đã kết nối là số byte ứng dụng chưa đọc và số byte đã gửi nhưng chưa nhận được ACK #### sk-udp-connreset · Lỗi WSAECONNRESET trên socket UDP của Windows · WSAECONNRESET on a Windows UDP socket Khi server Windows gửi UDP tới một client đã rời đi, thông báo “không tới được cổng” (ICMP) sẽ gửi về. Thông báo này khiến lời gọi nhận tiếp theo kết thúc bằng lỗi; nếu code server coi lỗi đó là bản thân socket bị hỏng, mọi người dùng socket đó đều bị ảnh hưởng. - Vì sao → Dẫn đến → Trên màn hình: Server tiếp tục gửi UDP tới địa chỉ của client vừa thoát, và thông báo “không tới được cổng” (ICMP) gửi về → Windows cho lời gọi nhận tiếp theo kết thúc bằng lỗi WSAECONNRESET (10054), và code server ngừng nhận hoặc đóng socket → Mọi người chơi đang dùng socket đó cùng lúc bị đứng hình hoặc mất kết nối - Triệu chứng: Mất kết nối, Đứng hình / Yếu tố: Mất gói, Ngưng trệ - Ai gặp: Cả server / Khi nào: Thỉnh thoảng bất chợt, Ngay sau đăng nhập hoặc bảo trì - Phụ trách chính: Phát triển server (Đội phát triển game) - Việc cần làm (Đội phát triển game): Dùng WSAIoctl tắt SIO_UDP_CONNRESET để không nhận thông báo này; khi có lỗi nhận, chỉ ghi log rồi tiếp tục nhận. - Trên đồ thị: Rớt kết nối hàng loạt (Số kết nối, log lỗi nhận) - Chỗ cần xem: Mã lỗi nhận UDP (WSAECONNRESET, 10054) và bản ghi vòng lặp nhận bị dừng hoặc socket bị đóng trong log server game; trong bản bắt gói (packet capture) phía server, xem ngay trước đó có ICMP Port Unreachable đến không - Đúng nếu: ngay trước khi kết nối rớt hàng loạt có ghi nhận lỗi nhận WSAECONNRESET, và trước đó có ICMP Port Unreachable gửi về từ địa chỉ của client vừa thoát - Loại trừ nếu: server chạy Linux, hoặc code đã tắt SIO_UDP_CONNRESET (không áp dụng) - Cách kiểm tra: Cần log và chỉ số của server, client game - Nguồn: - [Winsock IOCTLs](https://learn.microsoft.com/en-us/windows/win32/winsock/winsock-ioctls) · Microsoft · SIO_UDP_CONNRESET bật hoặc tắt việc báo các thông báo “không tới được cổng” (PORT_UNREACHABLE) của UDP - [recvfrom function (winsock.h)](https://learn.microsoft.com/en-us/windows/win32/api/winsock/nf-winsock-recvfrom) · Microsoft · WSAECONNRESET trên socket UDP nghĩa là một lần gửi trước đó đã nhận về ICMP Port Unreachable - [Windows Sockets Error Codes](https://learn.microsoft.com/en-us/windows/win32/winsock/windows-sockets-error-codes-2) · Microsoft · Mã lỗi WSAECONNRESET là 10054 ### L9 Tiến trình game phía server (18 nguyên nhân) #### sp-tick-overrun · Vượt tick budget · Tick overrun Khi khối lượng việc trong một tick vượt budget, chu kỳ tick của server bị kéo dài, và cả khu vực đó chạy chậm lại hoặc giật khựng. - Vì sao → Dẫn đến → Trên màn hình: Việc cần xử lý trong một tick (ví dụ 50 ms) vượt budget → Trạng thái game lẽ ra được tính 20 lần trong 1 giây chỉ được tính 8 lần → Cả khu vực bị quay chậm (tùy thiết kế server có thể là giật khựng), skill phản hồi chậm - Triệu chứng: Quay chậm, Trễ thao tác, Giật khựng / Yếu tố: Ngưng trệ - Ai gặp: Một địa điểm hoặc kênh, Cả server / Khi nào: Khi đông người, Giờ cao điểm buổi tối - Phụ trách chính: Phát triển server (Đội phát triển game) / Phối hợp: Hạ tầng server (Đội hạ tầng) - Việc cần làm (Đội phát triển game): Giảm các phép tính tốn kém, chia tick ra nhiều thread, phân tán người chơi (kênh), ghi thời gian xử lý tick thành chỉ số. - Việc cần làm (Đội hạ tầng): Thêm thời gian tick và mức sử dụng CPU theo từng core vào giám sát và cảnh báo, cân nhắc CPU hoặc instance có hiệu năng đơn nhân (xung nhịp) cao. - Con số tham khảo: Budget của server 20 tick là 50 ms, 30 tick là 33 ms, 60 tick là 16,7 ms. Để phòng lúc người chơi đột ngột dồn về, an toàn hơn là chừa khoảng dư: bình thường chỉ dùng khoảng một nửa budget. - Trên đồ thị: Tăng theo số người và tải (Thời gian tick của server, số người theo từng zone và kênh, CPU của game thread) - Chỗ cần xem: Thời gian xử lý tick (p99) và số lần vượt tick do server ghi lại, đặt cùng đồ thị với số người theo từng zone và kênh. Nếu không có chỉ số tick, xem mức sử dụng CPU của một game thread bằng pidstat -t 1 - Đúng nếu: vào lúc người chơi dồn về, thời gian tick vượt budget (20 tick thì 50 ms), và trong lúc đó mức sử dụng CPU của game thread dính sát 100% - Loại trừ nếu: tick bị vượt nhưng CPU của game thread thấp → nguyên nhân thuộc loại chờ (GC pause, lock, gọi đồng bộ). Độ trễ run queue trong bcc runqlat dài nghĩa là thread không được cấp CPU → thiếu CPU hoặc quá nhiều thread - Cách kiểm tra: Cần log và chỉ số của server, client game - Tìm hiểu thêm: Tick trễ trông ra sao là tùy thiết kế server. Server tiến trạng thái game thêm một khoảng thời gian cố định (ví dụ 50 ms) mỗi tick thì bản thân thời gian trong game chậm lại, thành quay chậm. Server di chuyển mọi thứ một lần theo đúng thời gian thực đã trôi qua thì giữ được tốc độ game, nhưng gói tin thưa và mỗi lần di chuyển một đoạn lớn, nên trông như giật khựng hoặc dịch chuyển tức thời. Dù kiểu nào, phản hồi thao tác cũng chậm đi. Nếu một game thread phụ trách cả server thì cả server chậm; nếu chia thread theo khu vực thì khu vực đó chậm. Có game như EVE Online cố ý làm chậm thời gian trong game tới 10 lần trong các trận đánh lớn (Time Dilation) để phần tính toán theo kịp. - Sự cố thực tế: eve-hedgp-2014 - Nguồn: - [VALORANT's 128-Tick Servers](https://www.riotgames.com/en/news/valorants-128-tick-servers) · Riot Games · Server 128 tick phải xong một frame trong 7,8125 ms; đo thời gian frame của server theo từng hệ thống con và chia budget để quản lý - [HED-GP Technical Retrospective: What a HED-ache](https://www.eveonline.com/news/view/what-a-hed-ache) · CCP Games · EVE Online dùng Time Dilation làm chậm thời gian game khi quá tải, mức sàn là 10% (chậm 10 lần); bình thường CPU của node dưới 80% - [Handling variation in time](https://docs.unity3d.com/Manual/time-handling-variations.html) · Unity · Khi mô phỏng bước cố định bị tụt lại, các bước đuổi theo được chạy dồn một lượt, phần thời gian vượt giới hạn bị bỏ, nên thời gian game trôi chậm hơn thực tế - [pidstat(1) — Linux manual page](https://man7.org/linux/man-pages/man1/pidstat.1.html) · sysstat · -t hiển thị kèm thống kê theo từng thread thuộc tiến trình (mức sử dụng CPU, v.v.) - [Demonstrations of runqlat, the Linux eBPF/bcc version](https://raw.githubusercontent.com/iovisor/bcc/master/tools/runqlat_example.txt) · IO Visor · Hiển thị độ trễ run queue của bộ lập lịch (thời gian tác vụ chờ đến khi được cấp CPU) dưới dạng histogram #### sp-aoi · Tính toán tầm nhìn (AOI) bùng nổ (N²) · Area-of-interest explosion Nếu so sánh mọi người với nhau để xem ai thấy được ai, số người tăng 10 lần thì khối lượng tính toán tăng 100 lần. - Vì sao → Dẫn đến → Trên màn hình: So khoảng cách giữa mọi nhân vật với nhau, hoặc dù đã chia lưới (grid) vẫn có hàng trăm người dồn quanh một ô → 100 người thì khoảng 10.000 lần so sánh, 1.000 người thì khoảng 1 triệu lần → Ở nơi đông người như world boss hay công thành chiến, tick tăng vọt, gây quay chậm và giật khựng - Triệu chứng: Quay chậm, Giật khựng / Yếu tố: Ngưng trệ - Ai gặp: Một địa điểm hoặc kênh, Cả server / Khi nào: Khi đông người - Phụ trách chính: Phát triển server (Đội phát triển game) - Việc cần làm (Đội phát triển game): Chia thành lưới hoặc vùng để chỉ so với đối tượng ở gần, cập nhật thưa hơn cho đối tượng ở xa, giới hạn số người mà một người nhìn thấy. - Con số tham khảo: Nếu cả việc so khoảng cách lẫn cập nhật danh sách thấy/không thấy mất 0,1 µs (một phần mười triệu giây) cho mỗi cặp hai người, thì 1.000 người (khoảng 1 triệu cặp) tốn 100 ms mỗi tick. Gấp đôi budget của 20 tick (50 ms). - Trên đồ thị: Tăng theo số người và tải (Thời gian tick của server, số người tụ tập ở một chỗ) - Chỗ cần xem: Số người và thời gian tick theo từng zone và kênh trên cùng đồ thị, cùng với thời gian tính toán tầm nhìn trong tick được đo riêng. Nếu không đo riêng, xem tỷ trọng CPU theo từng hàm của tiến trình game bằng perf top -p - Đúng nếu: số người tụ tập ở một chỗ tăng gấp đôi thì thời gian tick tăng gần 4 lần, và các hàm tính tầm nhìn, khoảng cách chiếm phần lớn thời gian CPU - Loại trừ nếu: thời gian tick tăng tỷ lệ thuận với số người, hoặc các hàm gửi và serialize chiếm tỷ trọng lớn → broadcast bùng nổ hoặc chi phí serialize và nén - Cách kiểm tra: Cần log và chỉ số của server, client game - Nguồn: - [Comparing Interest Management Algorithms for Massively Multiplayer Games](https://www.sable.mcgill.ca/~clump/papers/boulanger-06-comparing.pdf) · ACM · Bài báo NetGames 2006 (bản tác giả công bố). Cách đo khoảng cách của mọi cặp không kham nổi khi số người tăng; chia lưới ô vuông thì chỉ cần kiểm tra 9 ô xung quanh - [Replication Graph in Unreal Engine](https://dev.epicgames.com/documentation/en-us/unreal-engine/replication-graph-in-unreal-engine) · Epic Games · Cách mặc định xét mọi kết nối cho từng actor trở thành điểm nghẽn CPU của server khi có nhiều người chơi và actor; MMORPG và các game tương tự chia thế giới thành lưới và dùng lại danh sách theo từng ô - [perf-top(1) — Linux manual page](https://man7.org/linux/man-pages/man1/perf-top.1.html) · perf · Hiển thị theo thời gian thực tỷ trọng CPU theo từng hàm (symbol) của tiến trình (-p) hoặc thread (-t) đang chạy #### sp-broadcast · Broadcast bùng nổ · Broadcast fan-out (N×N) Gửi chuyển động của một người cho tất cả những ai nhìn thấy người đó thì số bản cập nhật phải gửi tăng theo bình phương số người tụ tập. - Vì sao → Dẫn đến → Trên màn hình: Thay đổi của một người được gửi cho mọi người nhìn thấy người đó → 1.000 người cùng nhìn thấy nhau thì mỗi tick có 1 triệu bản cập nhật → Hàng đợi gửi và băng thông bão hòa, gây trễ và mất gói (trễ thao tác, tua nhanh, dịch chuyển tức thời) - Triệu chứng: Trễ thao tác, Dịch chuyển tức thời, Tua nhanh / Yếu tố: Độ trễ, Mất gói - Ai gặp: Một địa điểm hoặc kênh, Cả server / Khi nào: Khi đông người - Phụ trách chính: Phát triển server (Đội phát triển game) / Phối hợp: Hạ tầng server (Đội hạ tầng) - Việc cần làm (Đội phát triển game): Giảm tần suất cập nhật theo khoảng cách và mức độ quan trọng (kẻ địch ở gần thì mỗi tick, người ở xa thì vài lần trong 1 giây), đặt giới hạn lượng gửi cho mỗi người và lấp bằng thứ quan trọng trước, gộp nhiều bản cập nhật vào một gói, giới hạn số người hiển thị. - Việc cần làm (Đội hạ tầng): So sánh băng thông gửi và số gói mỗi giây của từng server với giới hạn mạng của NIC và instance để cảnh báo, kiểm tra khoảng dư trước các sự kiện lớn. - Con số tham khảo: 1.000 người × 1.000 người × 20 tick = 20 triệu bản cập nhật mỗi giây. Nếu mỗi bản 40 byte thì cả server khoảng 6,4 Gbps, mỗi người nhận khoảng 6,4 Mbps. Giới hạn số người hiển thị ở 150 thì cả server còn khoảng 1 Gbps, mỗi người khoảng 1 Mbps. - Trên đồ thị: Tăng theo số người và tải (Số gói và số byte server gửi, số người tụ tập ở một chỗ) - Chỗ cần xem: txpck/s và txkB/s (số gói và số KB mà NIC của server gửi mỗi giây) trong sar -n DEV 1, xem cùng đồ thị số người. Với instance trên cloud, xem bộ đếm vượt giới hạn trong ethtool -S (bw_out_allowance_exceeded và pps_allowance_exceeded trên AWS ENA) - Đúng nếu: khi số người tụ tập tăng, số gói và byte gửi tăng dốc hơn số người (gần theo bình phương), và từ lúc chạm giới hạn, bộ đếm vượt giới hạn hoặc số gói gửi bị bỏ tăng lên - Loại trừ nếu: lượng gửi không đổi mà chỉ thời gian tick tăng → tính toán tầm nhìn hoặc logic game - Cách kiểm tra: Kiểm tra bằng công cụ hạ tầng (không cần code game) - Sự cố thực tế: eve-hedgp-2014 - Nguồn: - [HED-GP Technical Retrospective: What a HED-ache](https://www.eveonline.com/news/view/what-a-hed-ache) · CCP Games · Việc gửi O(n²), khi hành động của n người phải được n người nhìn thấy, là yếu tố giới hạn không thể tránh trong các trận hạm đội quy mô lớn - [Actor Priority in Unreal Engine](https://dev.epicgames.com/documentation/en-us/unreal-engine/actor-priority-in-unreal-engine) · Epic Games · Khi băng thông của kết nối bão hòa, mỗi actor được xếp mức ưu tiên (khoảng cách, tầm nhìn, thời gian từ lần gửi cuối) và băng thông được chia cho thứ quan trọng trước - [Detailed Actor Replication Flow in Unreal Engine](https://dev.epicgames.com/documentation/en-us/unreal-engine/detailed-actor-replication-flow-in-unreal-engine) · Epic Games · NetUpdateFrequency đặt tần suất cập nhật cho từng actor; actor được gửi theo thứ tự ưu tiên, khi kết nối bão hòa thì phần còn lại dời sang tick sau - [sar(1) — Linux manual page](https://man7.org/linux/man-pages/man1/sar.1.html) · sysstat · rxpck/s và txpck/s (số gói nhận và gửi mỗi giây), rxkB/s và txkB/s (số KB nhận và gửi mỗi giây) trong sar -n DEV - [Monitor network performance for ENA settings on your EC2 instance](https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/monitoring-network-performance-ena.html) · AWS · bw_out_allowance_exceeded (vượt giới hạn băng thông gửi) và pps_allowance_exceeded (vượt giới hạn PPS): số gói bị xếp vào hàng đợi hoặc bị bỏ #### sp-hotzone · Quá tải khu vực chạy trên một thread (hotspot) · Single-threaded hot zone Với kiến trúc mỗi khu vực do một thread phụ trách, khi người chơi dồn vào một chỗ, chỉ một core đó lên 100%. - Vì sao → Dẫn đến → Trên màn hình: Mỗi khu vực (kênh) do một thread phụ trách → Người chơi dồn vào một chỗ thì chỉ core đó bão hòa, các core còn lại vẫn dư → Chỉ khu vực đó bị lag, các khu vực khác vẫn bình thường - Triệu chứng: Quay chậm, Trễ thao tác / Yếu tố: Ngưng trệ - Ai gặp: Một địa điểm hoặc kênh / Khi nào: Khi đông người - Phụ trách chính: Phát triển server (Đội phát triển game) / Phối hợp: Hạ tầng server (Đội hạ tầng) - Việc cần làm (Đội phát triển game): Phân tán sang nhiều kênh, song song hóa bên trong khu vực, giới hạn số người. - Việc cần làm (Đội hạ tầng): Thêm mức sử dụng CPU theo từng core vào giám sát và cảnh báo (một core bão hòa sẽ bị che lấp trong mức trung bình của cả server). - Con số tham khảo: Trên server 16 core, dù một core đang 100%, mức sử dụng CPU của cả server chỉ hiện khoảng 6%. Phải xem mức sử dụng theo từng core mới tìm ra. - Trên đồ thị: Tăng theo số người và tải (Mức sử dụng CPU theo từng core, CPU theo từng thread) - Chỗ cần xem: Mức sử dụng theo từng core qua mpstat -P ALL 1 và CPU theo từng thread của tiến trình game qua pidstat -t 1, so với số người trong zone mà thread bận nhất phụ trách - Đúng nếu: CPU của cả server thấp nhưng chỉ một thread (một core) dính sát 100%, và lúc đó zone mà thread đó phụ trách đang đông người - Loại trừ nếu: nhiều core cao đều nhau → cả server quá tải. Chỉ %soft (xử lý nhận) của một core cao → ngắt NIC dồn vào một core - Cách kiểm tra: Kiểm tra bằng công cụ hạ tầng (không cần code game) - Tìm hiểu thêm: Các khu vực khác chỉ bình thường khi mỗi khu vực chạy tick riêng. Nếu các thread khu vực mỗi tick phải chờ nhau rồi cùng sang tick tiếp theo, thì khu vực bận nhất sẽ làm chậm tick của cả server. - Sự cố thực tế: eve-hedgp-2014 - Nguồn: - [Time Dilation – How’s That Going?](https://www.eveonline.com/news/view/time-dilation-hows-that-going) · CCP Games · Time Dilation của EVE Online áp dụng theo node, nên cả những hệ sao ở xa nằm cùng node cũng chậm theo; trận đánh lớn được xử lý trên node tăng cường chỉ chứa 4 hệ sao - [mpstat(1) — Linux manual page](https://man7.org/linux/man-pages/man1/mpstat.1.html) · sysstat · Hiển thị riêng mức sử dụng của từng bộ xử lý và mức trung bình chung (-P ALL); %soft là tỷ lệ thời gian dùng để xử lý ngắt mềm (software interrupt) - [pidstat(1) — Linux manual page](https://man7.org/linux/man-pages/man1/pidstat.1.html) · sysstat · -t hiển thị kèm thống kê theo từng thread thuộc tiến trình (mức sử dụng CPU, v.v.) #### sp-lock · Tranh chấp lock · Lock contention Khi nhiều thread cùng chờ một lock để ghi cùng một dữ liệu, dù có thêm thread thì mỗi lúc cũng chỉ một thread chạy. - Vì sao → Dẫn đến → Trên màn hình: Nhiều thread cùng lúc dùng dữ liệu chung, như sàn đấu giá hoặc kho bang hội → Các thread còn lại phải chờ đến khi thread đang giữ lock làm xong → Chỉ một số tính năng bị chậm, nặng thì cả tick bị trễ - Triệu chứng: Trễ thao tác, Đứng hình / Yếu tố: Ngưng trệ - Ai gặp: Chỉ một tính năng, Cả server / Khi nào: Khi đông người - Phụ trách chính: Phát triển server (Đội phát triển game) - Việc cần làm (Đội phát triển game): Chia lock nhỏ ra, giảm việc làm bên trong lock, chuyển sang kiến trúc dựa trên message (mỗi dữ liệu có một thread phụ trách, các thread khác chỉ gửi yêu cầu bằng message). - Con số tham khảo: Nếu phần việc nằm trong lock chiếm 20% công việc, dù tăng thêm bao nhiêu thread, thông lượng tối đa cũng chỉ gấp 5 lần so với khi chạy 1 thread; nếu là 40% thì dừng ở mức 2,5 lần. - Trên đồ thị: Tăng theo số người và tải (Thời gian xử lý yêu cầu, CPU và context switch theo từng thread) - Chỗ cần xem: Context switch tự nguyện theo từng thread (cswch/s, số lần dừng để chờ tài nguyên) qua pidstat -w -t 1; thread rời CPU rồi chờ ở đâu (thời gian chờ theo từng call stack) qua bcc offcputime -p. Với .NET, xem số lần tranh chấp lock trong dotnet-counters (dotnet.monitor.lock_contentions từ .NET 9, Monitor Lock Contention Count ở bản 8 trở xuống) - Đúng nếu: tải tăng mà mức sử dụng CPU vẫn thấp nhưng thời gian xử lý tăng, phần lớn thời gian chờ dồn vào các call stack đang cố lấy lock, và số lần tranh chấp lock tăng theo - Loại trừ nếu: CPU đầy → vấn đề khối lượng tính toán (vượt tick budget, quá tải khu vực chạy trên một thread). Chỗ chờ là lời gọi DB hoặc file → gọi đồng bộ trên game thread - Cách kiểm tra: Kiểm tra bằng công cụ hạ tầng (không cần code game) - Tìm hiểu thêm: Vấn đề này xảy ra ở kiến trúc có nhiều thread cùng sửa dữ liệu game. Kiến trúc mà mỗi khu vực hoặc tính năng do một thread phụ trách và chỉ trao đổi bằng message thì gần như không có lock, nhưng phải cẩn thận với việc dồn việc vào một thread (quá tải khu vực chạy trên một thread). Nếu game thread phải chờ lock mà một tác vụ lưu chậm đang giữ, cả tick đó sẽ dừng lại. - Sự cố thực tế: roblox-2021 - Nguồn: - [Amdahl's Law in the Multicore Era](https://research.cs.wisc.edu/multifacet/papers/ieeecomputer08_amdahl_multicore.pdf) · IEEE · Bài báo IEEE Computer 2008 (bản tác giả công bố). Nếu phần không song song hóa được là 1−f thì dù tăng bao nhiêu core, tốc độ cũng không tăng quá 1/(1−f) lần (định luật Amdahl) - [Request scheduling](https://learn.microsoft.com/en-us/dotnet/orleans/grains/request-scheduling) · Microsoft · Grain (actor) của Orleans dùng mô hình thực thi một thread, xử lý trọn từng yêu cầu một, nên trạng thái không bị sửa đồng thời; nếu các grain chờ phản hồi của nhau thì có thể deadlock - [pidstat(1) — Linux manual page](https://man7.org/linux/man-pages/man1/pidstat.1.html) · sysstat · cswch/s trong -w là số context switch tự nguyện do dừng để chờ tài nguyên; -t hiển thị theo từng thread - [Demonstrations of offcputime, the Linux eBPF/bcc version](https://raw.githubusercontent.com/iovisor/bcc/master/tools/offcputime_example.txt) · IO Visor · Cộng dồn thời gian thread bị dừng và rời CPU (off-CPU) theo từng call stack, -p để chỉ định tiến trình - [Well-known EventCounters in .NET](https://learn.microsoft.com/en-us/dotnet/core/diagnostics/available-counters) · Microsoft · Monitor Lock Contention Count (monitor-lock-contention-count): số lần xảy ra tranh chấp khi cố lấy monitor lock - [.NET runtime metrics](https://learn.microsoft.com/en-us/dotnet/core/diagnostics/built-in-metrics-runtime) · .NET · dotnet.monitor.lock_contentions từ .NET 9: số lần xảy ra tranh chấp khi cố lấy monitor lock kể từ khi tiến trình khởi động #### sp-deadlock · Deadlock · Deadlock Khi hai thread chờ lock mà bên kia đang giữ, cả hai sẽ đứng mãi mãi. - Vì sao → Dẫn đến → Trên màn hình: Thread A giữ lock 1 và chờ lock 2, B giữ lock 2 và chờ lock 1 → Cả hai đứng mãi mãi, các thread liên quan cũng lần lượt đứng theo → Cả server ngừng hoạt động, watchdog khởi động lại nên mọi người đều mất kết nối - Triệu chứng: Đứng hình, Mất kết nối / Yếu tố: Ngưng trệ - Ai gặp: Cả server, Chỉ một tính năng / Khi nào: Thỉnh thoảng bất chợt, Khi đông người - Phụ trách chính: Phát triển server (Đội phát triển game) - Việc cần làm (Đội phát triển game): Quy tắc thứ tự lấy lock, dùng lock có timeout, dùng watchdog và lưu thread dump ngay lúc bị treo. - Trên đồ thị: Rớt kết nối hàng loạt (Số kết nối, lượng gửi của server) - Chỗ cần xem: Chụp call stack của mọi thread trong lúc bị treo. JVM dùng jstack (tự tìm và đánh dấu deadlock), .NET dùng dotnet-stack, server native dùng thread apply all bt của gdb, hoặc dùng gcore tạo file core rồi khởi động lại và phân tích sau - Đúng nếu: có từ hai thread trở lên đứng với stack đang chờ lock mà bên kia giữ, và suốt lúc đó mức sử dụng CPU của tiến trình gần 0 - Loại trừ nếu: trong lúc treo có một thread chạy 100% CPU → vòng lặp vô hạn. Các thread đang chờ phản hồi từ DB hoặc bên ngoài → gọi đồng bộ hoặc cạn thread pool - Cách kiểm tra: Kiểm tra bằng công cụ hạ tầng (không cần code game) - Nguồn: - [Runtime locking correctness validator](https://docs.kernel.org/locking/lockdep-design.html) · Linux kernel · Lấy hai lock theo thứ tự ngược nhau sẽ gây chờ vòng tròn và deadlock (lock inversion deadlock); kernel Linux kiểm tra thứ tự lock và cảnh báo trước - [Liveness, Readiness, and Startup Probes](https://kubernetes.io/docs/concepts/workloads/pods/probes/) · Kubernetes · Liveness probe phát hiện trạng thái deadlock (vẫn đang chạy nhưng không tiến triển được) và khởi động lại container - [Diagnostic Tools (Java SE 21 Troubleshooting Guide)](https://docs.oracle.com/en/java/javase/21/troubleshoot/diagnostic-tools.html) · Oracle · jstack in stack của mọi thread trong JVM đang chạy, đồng thời tìm và đánh dấu deadlock (Found one Java-level deadlock) - [dotnet-stack diagnostic tool - .NET CLI](https://learn.microsoft.com/en-us/dotnet/core/diagnostics/dotnet-stack) · Microsoft · Chụp và in managed stack của mọi thread trong tiến trình .NET - [Threads (Debugging with GDB)](https://sourceware.org/gdb/current/onlinedocs/gdb.html/Threads.html) · GNU Project · thread apply all chạy cùng một lệnh (bt: in call stack) trên mọi thread - [gcore(1) — Linux manual page](https://man7.org/linux/man-pages/man1/gcore.1.html) · gdb · Tạo file core của chương trình đang chạy, và sau khi tạo xong chương trình vẫn tiếp tục chạy #### sp-sync-call · Gọi đồng bộ (blocking) trên game thread · Synchronous DB / file I/O on the game loop Nếu giữa tick mà phải chờ phản hồi DB hoặc ghi file, mọi diễn biến game trên server dừng lại đúng khoảng thời gian đó. - Vì sao → Dẫn đến → Trên màn hình: Trong tick phải chờ truy vấn và lưu DB, ghi log, gọi API bên ngoài → DB mất 100 ms thì tick cũng dừng 100 ms → Mỗi lần DB hoặc ổ đĩa chậm đi, cả bản đồ lại khựng - Triệu chứng: Đứng hình, Giật khựng / Yếu tố: Ngưng trệ - Ai gặp: Một địa điểm hoặc kênh, Cả server / Khi nào: Khi làm một thao tác nhất định, Thỉnh thoảng bất chợt - Phụ trách chính: Phát triển server (Đội phát triển game) - Việc cần làm (Đội phát triển game): Chuyển mọi việc chậm (truy vấn và lưu DB, ghi log, gọi API bên ngoài) sang chạy bất đồng bộ và áp kết quả ở tick sau (chỉ đặt timeout thì trong lúc chờ, tick vẫn dừng). - Con số tham khảo: Dù một lượt khứ hồi tới DB trong cùng trung tâm dữ liệu chỉ 0,5 ms, gọi 100 lần trong một tick là 50 ms. Một mình nó dùng hết budget của 20 tick. - Trên đồ thị: Thỉnh thoảng vọt lên bất chợt (Thời gian tick của server, độ trễ query DB) - Chỗ cần xem: Đồ thị thời gian tick trên cùng trục thời gian với độ trễ query DB (slow query log, v.v.) và độ trễ ổ đĩa. Nếu không có chỉ số tick, dùng bcc offcputime -p xem game thread đang chờ ở đâu - Đúng nếu: thời điểm tick vọt lên trùng với lúc độ trễ DB hoặc file vọt lên, và thời gian chờ của game thread dồn vào các call stack nhận phản hồi DB hoặc ghi file - Loại trừ nếu: độ trễ DB và ổ đĩa vẫn bình thường mà tick vọt lên → GC pause hoặc tranh chấp lock - Cách kiểm tra: Cần log và chỉ số của server, client game - Nguồn: - [Designs, Lessons and Advice from Building Large Distributed Systems](https://www.cs.cornell.edu/projects/ladis2009/talks/dean-keynote-ladis2009.pdf) · Google · Bài phát biểu chính tại LADIS 2009 (Jeff Dean). Khứ hồi trong cùng trung tâm dữ liệu khoảng 0,5 ms (500.000 ns) - [ASP.NET Core Best Practices](https://learn.microsoft.com/en-us/aspnet/core/fundamentals/best-practices) · Microsoft · Gọi bất đồng bộ cho truy cập dữ liệu, I/O và tác vụ chạy lâu; lời gọi blocking đồng bộ dẫn đến cạn thread pool và phản hồi chậm - [Demonstrations of offcputime, the Linux eBPF/bcc version](https://raw.githubusercontent.com/iovisor/bcc/master/tools/offcputime_example.txt) · IO Visor · Cộng dồn thời gian thread bị dừng và rời CPU (off-CPU) theo từng call stack, -p để chỉ định tiến trình #### sp-queue · Hàng đợi message bị dồn ứ · Mailbox / job queue backlog Khi yêu cầu đến nhanh hơn tốc độ xử lý và dồn vào hàng đợi, các yêu cầu phía sau vài giây sau mới được xử lý hoặc bị bỏ. - Vì sao → Dẫn đến → Trên màn hình: Yêu cầu đến nhanh hơn tốc độ xử lý → Hàng đợi dài ra, vượt giới hạn thì bị bỏ → Skill và giao dịch phản hồi chậm hoặc bị nuốt - Triệu chứng: Trễ thao tác, Nuốt thao tác·rollback / Yếu tố: Độ trễ, Mất gói - Ai gặp: Một địa điểm hoặc kênh, Chỉ một tính năng / Khi nào: Khi đông người - Phụ trách chính: Phát triển server (Đội phát triển game) - Việc cần làm (Đội phát triển game): Giám sát độ dài hàng đợi, chính sách ưu tiên bỏ yêu cầu cũ, song song hóa việc xử lý. - Trên đồ thị: Chạm giới hạn rồi đi ngang (Độ dài hàng đợi và tuổi của message cũ nhất, số message xử lý mỗi giây) - Chỗ cần xem: Độ dài từng hàng đợi, tuổi của message cũ nhất, số message vào, đã xử lý và bị bỏ mỗi giây do server ghi lại. Nếu code không có chỉ số, xem Recv-Q của socket game bằng ss (hoặc netstat): lượng dữ liệu kernel đã nhận nhưng tiến trình chưa đọc - Đúng nếu: trong lúc số vào vượt số xử lý, số xử lý đứng ở một mức không tăng thêm được, còn độ dài hàng đợi, tuổi message và số bị bỏ liên tục tăng - Loại trừ nếu: hàng đợi ngắn, tuổi message nhỏ mà phản hồi vẫn chậm → độ trễ đường truyền hoặc độ trễ của chính tick - Cách kiểm tra: Cần log và chỉ số của server, client game - Sự cố thực tế: eve-hedgp-2014 - Nguồn: - [Avoiding insurmountable queue backlogs](https://d1.awsstatic.com/builderslibrary/pdfs/avoiding-insurmountable-queue-backlogs.pdf) · AWS · Amazon Builders' Library. Giám sát tình trạng dồn ứ qua tuổi của message đang chờ; hệ thống thời gian thực xử lý dữ liệu mới trước (gần với LIFO), có khi bỏ message cũ - [Site Reliability Engineering, Chapter 22: Addressing Cascading Failures](https://sre.google/sre-book/addressing-cascading-failures/) · Google · Khi yêu cầu đến nhanh hơn tốc độ xử lý, hàng đợi đầy và độ trễ tăng; dùng LIFO hoặc CoDel thay cho FIFO để bớt các yêu cầu cũ đã vô dụng - [ss(8) — Linux manual page](https://man7.org/linux/man-pages/man8/ss.8.html) · iproute2 · Công cụ hiển thị thống kê socket (thông tin tương tự netstat); -p hiển thị tiến trình đang dùng socket - [netstat(8) — Linux manual page](https://man7.org/linux/man-pages/man8/netstat.8.html) · net-tools · Recv-Q: số byte trên socket đã kết nối mà chương trình người dùng chưa lấy đi #### sp-timer-burst · Timer kích hoạt dồn cùng lúc · Synchronized timers Khi mọi lượt respawn quái, mọi buff hết hạn và phần thưởng lúc tròn giờ dồn vào cùng một tick, riêng tick đó nặng lên gấp hàng chục lần. - Vì sao → Dẫn đến → Trên màn hình: Timer respawn, hết hạn, phần thưởng và tự động lưu được đặt trùng một thời điểm → Riêng tick đó phải làm lượng việc gấp hàng chục lần bình thường → Cứ đến giờ đã định là khựng một lần - Triệu chứng: Đứng hình, Giật khựng / Yếu tố: Ngưng trệ - Ai gặp: Một địa điểm hoặc kênh, Cả server / Khi nào: Theo chu kỳ đều - Phụ trách chính: Phát triển server (Đội phát triển game) - Việc cần làm (Đội phát triển game): Thêm một độ lệch ngẫu nhiên nhỏ vào thời điểm của timer, chia việc xử lý ra nhiều tick. - Trên đồ thị: Vọt lên theo chu kỳ (Thời gian tick của server) - Chỗ cần xem: Gom các thời điểm tick vọt lên để xem khoảng cách (tròn giờ, mỗi 5 phút, v.v.), đối chiếu với danh sách timer respawn, hết hạn buff, phần thưởng, tự động lưu chạy vào cùng thời điểm - Đúng nếu: tick lần nào cũng vọt lên vào cùng thời điểm hoặc cùng khoảng cách, và vào lúc đó có tác vụ timer của game kích hoạt cùng một lượt - Loại trừ nếu: có chu kỳ nhưng trùng với thời điểm dừng trong log GC hoặc giờ chạy cron, sao lưu của server → GC dừng toàn bộ server hoặc tác vụ theo lịch - Cách kiểm tra: Cần log và chỉ số của server, client game - Nguồn: - [Timeouts, retries, and backoff with jitter](https://d1.awsstatic.com/builderslibrary/pdfs/timeouts-retries-and-backoff-with-jitter.pdf) · AWS · Amazon Builders' Library. Thêm jitter vào mọi timer, tác vụ định kỳ và tác vụ trì hoãn để rải phần tải dồn vào cùng một thời điểm; ví dụ về yêu cầu chu kỳ 1 phút từ nhiều server dồn vào vài giây đầu của mỗi phút #### sp-pathfinding · Tìm đường (pathfinding) dồn dập · Pathfinding storms Hàng trăm con quái cùng lúc đuổi theo người chơi và tính đường đi thì tốn rất nhiều CPU. - Vì sao → Dẫn đến → Trên màn hình: Gom quái hoặc spawn quy mô lớn khiến nhiều quái cùng lúc đuổi theo người chơi → Mỗi con quái tự chạy tìm đường → Chỉ bãi săn đó bị quay chậm - Triệu chứng: Quay chậm / Yếu tố: Ngưng trệ - Ai gặp: Một địa điểm hoặc kênh / Khi nào: Khi đông người - Phụ trách chính: Phát triển server (Đội phát triển game) - Việc cần làm (Đội phát triển game): Cache đường đi, giới hạn số lần tính, chia ra nhiều tick. - Trên đồ thị: Tăng theo số người và tải (Thời gian tick của server, số quái theo từng zone) - Chỗ cần xem: Số quái đang đuổi theo người chơi và thời gian tick theo từng zone. Nếu không đếm riêng, xem tỷ trọng CPU theo từng hàm của tiến trình game bằng perf top -p - Đúng nếu: thời gian tick tăng khi gom quái hoặc spawn quy mô lớn, và hàm tìm đường (tìm kiếm đường đi) chiếm tỷ trọng lớn trong thời gian CPU - Loại trừ nếu: quái ít, chỉ có người chơi đông mà tick vẫn tăng → tính toán tầm nhìn hoặc broadcast - Cách kiểm tra: Cần log và chỉ số của server, client game - Nguồn: - [AI.NavMesh.pathfindingIterationsPerFrame](https://docs.unity3d.com/ScriptReference/AI.NavMesh-pathfindingIterationsPerFrame.html) · Unity · Mỗi frame chỉ xử lý tìm đường cho một số node nhất định, chia việc ra nhiều frame, nên game vẫn mượt dù đường đi dài hoặc có nhiều yêu cầu cùng lúc - [perf-top(1) — Linux manual page](https://man7.org/linux/man-pages/man1/perf-top.1.html) · perf · Hiển thị theo thời gian thực tỷ trọng CPU theo từng hàm (symbol) của tiến trình (-p) đang chạy #### sp-serialize · Chi phí serialize và nén · Serialization / compression cost Việc chuyển dữ liệu cần gửi thành byte và nén cũng tốn CPU, và khi đông người, chi phí này tăng vọt. - Vì sao → Dẫn đến → Trên màn hình: Mỗi bản cập nhật đều phải chuyển struct thành byte rồi nén → Chi phí tăng theo bình phương số người → Dữ liệu gửi đi trễ, gây trễ thao tác - Triệu chứng: Trễ thao tác / Yếu tố: Ngưng trệ, Độ trễ - Ai gặp: Một địa điểm hoặc kênh / Khi nào: Khi đông người - Phụ trách chính: Phát triển server (Đội phát triển game) - Việc cần làm (Đội phát triển game): Dùng lại gói đã tạo một lần cho nhiều người, dùng định dạng gọn nhẹ. - Trên đồ thị: Tăng theo số người và tải (Mức sử dụng CPU của server, CPU của thread tạo gói tin) - Chỗ cần xem: Tỷ trọng của các hàm serialize, nén, mã hóa (kể cả hàm thư viện như zlib, LZ4, OpenSSL) trong thời gian CPU của tiến trình game qua perf top -p, so giữa lúc vắng và lúc đông người - Đúng nếu: người càng đông, tỷ trọng các hàm serialize, nén, mã hóa càng lớn, và thread tạo gói tin bão hòa trước tiên - Loại trừ nếu: các hàm này chiếm tỷ trọng nhỏ → tính toán tầm nhìn hoặc logic game - Cách kiểm tra: Kiểm tra bằng công cụ hạ tầng (không cần code game) - Tìm hiểu thêm: Nếu kết nối có mã hóa gói tin (TLS, DTLS, v.v.), việc mã hóa và giải mã cũng tốn CPU. Mã hóa được làm riêng cho từng kết nối, nên dù dùng lại một gói cho nhiều người, chi phí mã hóa vẫn tăng theo số người nhận. Mã hóa đối xứng như AES-GCM nhanh đến mức một core xử lý được vài GB mỗi giây nên bình thường chiếm tỷ trọng nhỏ, nhưng tốc độ thay đổi nhiều theo kích thước của đơn vị mã hóa mỗi lần (record), nên với nhiều gói nhỏ như trong game, chi phí trên mỗi byte tăng lên. Ở bước handshake làm một lần cho mỗi kết nối, server ký bằng khóa của chứng chỉ và tính trao đổi khóa (ECDHE). Mỗi giây, một core ký được khoảng 1.100 lần (RSA 2048) đến 18.000 lần (ECDSA P-256) và trao đổi khóa khoảng 9.000 lần, nên khi đăng nhập dồn dập, đây là một gánh nặng. - Nguồn: - [Introduction to Iris in Unreal Engine](https://dev.epicgames.com/documentation/en-us/unreal-engine/introduction-to-iris-in-unreal-engine) · Epic Games · Giữ trạng thái cần replicate dưới dạng một bản sao đã lượng tử hóa để giảm việc tốn kém, và các kết nối dùng chung phần việc đó - [VALORANT's 128-Tick Servers](https://www.riotgames.com/en/news/valorants-128-tick-servers) · Riot Games · Cách mỗi frame so sánh biến replicate cho từng client rồi gom các giá trị đã đổi là việc chậm vì phải đọc bộ nhớ rải rác nhiều chỗ, nên tốn nhiều CPU của server - [How "expensive" is crypto anyway?](https://blog.cloudflare.com/how-expensive-is-crypto-anyway/) · Cloudflare · Đo trên BoringSSL: AES-128-GCM khoảng 3,7 GB mỗi giây (thay đổi nhiều theo kích thước record); mỗi core mỗi giây ký RSA 2048 được 1.120 lần, ký ECDSA P-256 được 18.477 lần, P-256 ECDHE được 9.394 lần; trên server edge của Cloudflare, thư viện TLS dùng khoảng 1,8% CPU - [perf-top(1) — Linux manual page](https://man7.org/linux/man-pages/man1/perf-top.1.html) · perf · Hiển thị theo thời gian thực tỷ trọng CPU theo từng hàm (symbol) của tiến trình (-p) đang chạy #### sp-crash · Server crash · Server process crash Khi tiến trình server chết vì một lỗi không được xử lý, mọi người trên server đó mất kết nối cùng lúc. - Vì sao → Dẫn đến → Trên màn hình: Lỗi nghiêm trọng như tham chiếu tới thứ không tồn tại (null reference), dữ liệu sai, hết bộ nhớ → Tiến trình server (hoặc zone) kết thúc → Mọi người cùng mất kết nối, tiến độ từ lần lưu cuối có thể bị rollback - Triệu chứng: Mất kết nối, Nuốt thao tác·rollback / Yếu tố: Ngưng trệ - Ai gặp: Một địa điểm hoặc kênh, Cả server / Khi nào: Thỉnh thoảng bất chợt, Khi làm một thao tác nhất định - Phụ trách chính: Phát triển server (Đội phát triển game) / Phối hợp: Hạ tầng server (Đội hạ tầng) - Việc cần làm (Đội phát triển game): Phân tích crash dump để sửa nguyên nhân, lưu thường xuyên. - Việc cần làm (Đội hạ tầng): Tự động khởi động lại tiến trình, dựng môi trường thu thập và lưu trữ crash dump, cảnh báo ngay khi server sập. - Trên đồ thị: Rớt kết nối hàng loạt (Số kết nối, số lần tiến trình khởi động lại) - Chỗ cần xem: Bản ghi core dump trong coredumpctl list (thời điểm, PID, tín hiệu kết thúc) và bản ghi kết thúc bất thường, khởi động lại của trình quản lý service (systemd). Với server Windows, xem file dump do WER lưu lại - Đúng nếu: vào lúc số kết nối tụt gần về 0 trong chớp mắt, tiến trình server game kết thúc bất thường và có core dump - Loại trừ nếu: tiến trình vẫn sống mà kết nối bị ngắt → thiết bị mạng hoặc idle timeout. Có bản ghi watchdog khởi động lại sau khi treo lâu → vòng lặp vô hạn hoặc deadlock - Cách kiểm tra: Kiểm tra bằng công cụ hạ tầng (không cần code game) - Nguồn: - [Collecting User-Mode Dumps](https://learn.microsoft.com/en-us/windows/win32/wer/collecting-user-mode-dumps) · Microsoft · Cấu hình Windows Error Reporting (WER) để thu full dump hoặc mini dump về máy cục bộ khi chương trình user mode bị crash - [systemd.service(5) — Linux manual page](https://man7.org/linux/man-pages/man5/systemd.service.5.html) · systemd · Restart=on-failure tự khởi động lại service khi kết thúc bất thường, bị tín hiệu kết thúc (kể cả có core dump) hoặc quá thời gian watchdog; khuyến nghị cho service chạy lâu dài - [coredumpctl(1) — Linux manual page](https://man7.org/linux/man-pages/man1/coredumpctl.1.html) · systemd · list truy vấn các core dump do systemd-coredump lưu, hiển thị thời điểm crash, PID và tín hiệu gây crash #### sp-threadpool · Cạn thread pool · Thread pool starvation Khi mọi worker thread xử lý việc đều bị kẹt ở các việc chậm, yêu cầu mới chỉ biết chờ vô thời hạn. - Vì sao → Dẫn đến → Trên màn hình: Các worker thread bị kẹt chờ phản hồi từ API bên ngoài hoặc DB → Yêu cầu mới không còn thread nào để nhận → Kẹt loading ở một số tính năng như đăng nhập hoặc cửa hàng - Triệu chứng: Không vào được·kẹt loading, Trễ thao tác, Đứng hình / Yếu tố: Ngưng trệ - Ai gặp: Chỉ một tính năng, Cả server / Khi nào: Khi đông người, Ngay sau đăng nhập hoặc bảo trì - Phụ trách chính: Phát triển server (Đội phát triển game) - Việc cần làm (Đội phát triển game): Đặt timeout cho lời gọi chậm, tách thread pool theo tính năng, chuyển sang bất đồng bộ. - Trên đồ thị: Chạm giới hạn rồi đi ngang (Số thread và độ dài hàng đợi của thread pool, thời gian xử lý yêu cầu) - Chỗ cần xem: Với .NET, số thread và độ dài hàng đợi của thread pool trong dotnet-counters monitor (dotnet.thread_pool.thread.count và dotnet.thread_pool.queue.length từ .NET 9, ThreadPool Thread Count và ThreadPool Queue Length ở bản 8 trở xuống), và dùng dotnet-stack xem worker thread đang chờ ở đâu. Với JVM và server native, xem tương tự bằng thread dump - Đúng nếu: mức sử dụng CPU thấp hơn 100% nhiều nhưng số thread cứ tăng dần hoặc dính ở mức trần, hàng đợi dồn lên, và phần lớn worker đang chờ phản hồi của cùng một lời gọi ra ngoài (DB, HTTP) - Loại trừ nếu: hàng đợi trống mà vẫn chậm → chính dịch vụ được gọi đang chậm, xem sự cố dây chuyền hoặc phụ thuộc vào dịch vụ bên ngoài - Cách kiểm tra: Kiểm tra bằng công cụ hạ tầng (không cần code game) - Tìm hiểu thêm: Nếu việc nhận gói tin và logic game dùng chung một worker thread pool, thì ngay lúc vài việc chậm chiếm hết worker, việc xử lý gói tin của cả server dừng lại. - Sự cố thực tế: riot-euw-2021 - Nguồn: - [Debug ThreadPool Starvation](https://learn.microsoft.com/en-us/dotnet/core/diagnostics/debug-threadpool-starvation) · Microsoft · Pool hết thread, việc mới phải chờ nên phản hồi chậm; nguyên nhân là code blocking chiếm giữ thread. Trong dotnet-counters, CPU thấp hơn 100% nhiều mà dotnet.thread_pool.thread.count cứ tăng dần là dấu hiệu cạn pool (dotnet.thread_pool.queue.length thường cũng lớn); dùng dotnet-stack xem thread chờ ở đâu - [Avoiding insurmountable queue backlogs](https://d1.awsstatic.com/builderslibrary/pdfs/avoiding-insurmountable-queue-backlogs.pdf) · AWS · Số yêu cầu xử lý đồng thời = tốc độ đến × độ trễ (định luật Little). Với 100 yêu cầu mỗi giây, độ trễ tăng từ 100 ms lên 10 giây thì 10 thread thành 1.000 thread và pool cạn - [Bulkhead Pattern](https://learn.microsoft.com/en-us/azure/architecture/patterns/bulkhead) · Microsoft Azure · Tách riêng connection pool và thread pool cho từng dịch vụ được gọi thì sự cố của một dịch vụ chỉ chặn pool của nó - [.NET runtime metrics](https://learn.microsoft.com/en-us/dotnet/core/diagnostics/built-in-metrics-runtime) · .NET · dotnet.thread_pool.thread.count (số thread của thread pool) và dotnet.thread_pool.queue.length (số việc đang chờ) có từ .NET 9 - [Well-known EventCounters in .NET](https://learn.microsoft.com/en-us/dotnet/core/diagnostics/available-counters) · Microsoft · ThreadPool Thread Count (threadpool-thread-count) và ThreadPool Queue Length (threadpool-queue-length) ở .NET 8 trở xuống #### sp-infinite-loop · Vòng lặp vô hạn và logic chạy mất kiểm soát · Infinite loop / runaway logic Khi một bug khiến tick không bao giờ kết thúc, server bị treo và watchdog buộc khởi động lại. - Vì sao → Dẫn đến → Trên màn hình: Vòng lặp không kết thúc vì điều kiện sai, hoặc đệ quy mất kiểm soát → Tick không kết thúc nên server bị treo → Đứng hình rồi mọi người đều mất kết nối - Triệu chứng: Đứng hình, Mất kết nối / Yếu tố: Ngưng trệ - Ai gặp: Một địa điểm hoặc kênh, Cả server / Khi nào: Khi làm một thao tác nhất định, Thỉnh thoảng bất chợt - Phụ trách chính: Phát triển server (Đội phát triển game) - Việc cần làm (Đội phát triển game): Giới hạn số vòng lặp, watchdog, test tái hiện input gây lỗi. - Trên đồ thị: Rớt kết nối hàng loạt (Số kết nối, CPU theo từng thread) - Chỗ cần xem: CPU theo từng thread qua pidstat -t 1 trong lúc treo, và thread đang chạy 100% quay ở hàm nào, xem bằng perf top -t (ID thread) hoặc gdb. Nếu đã khởi động lại rồi, xem bản ghi watchdog quá thời gian (WatchdogSec của systemd, liveness probe của Kubernetes thất bại) - Đúng nếu: trong lúc server bị treo, một game thread dính ở 100% CPU và stack cứ quay mãi trong cùng một hàm hoặc vòng lặp - Loại trừ nếu: trong lúc treo CPU gần 0 → deadlock hoặc đang chờ phản hồi bên ngoài - Cách kiểm tra: Kiểm tra bằng công cụ hạ tầng (không cần code game) - Nguồn: - [systemd.service(5) — Linux manual page](https://man7.org/linux/man-pages/man5/systemd.service.5.html) · systemd · WatchdogSec=: nếu service không gửi tín hiệu còn sống (WATCHDOG=1) trong thời gian quy định thì bị coi là lỗi và bị dừng, rồi tự khởi động lại tùy cấu hình Restart= - [Liveness, Readiness, and Startup Probes](https://kubernetes.io/docs/concepts/workloads/pods/probes/) · Kubernetes · Liveness probe phát hiện trạng thái vẫn chạy nhưng không tiến triển được và khởi động lại; mặc định kiểm tra 10 giây một lần, thất bại 3 lần liên tiếp thì khởi động lại - [pidstat(1) — Linux manual page](https://man7.org/linux/man-pages/man1/pidstat.1.html) · sysstat · -t hiển thị kèm thống kê theo từng thread thuộc tiến trình (mức sử dụng CPU, v.v.) - [perf-top(1) — Linux manual page](https://man7.org/linux/man-pages/man1/perf-top.1.html) · perf · Hiển thị theo thời gian thực tỷ trọng CPU theo từng hàm (symbol) của thread (-t) hoặc tiến trình (-p) đang chạy #### sp-hot-entity · Giao tranh dồn vào một mục tiêu (world boss) · Hot entity / combat event fan-out Khi hàng trăm người cùng đánh một boss, phần tính toán cho một con boss dồn vào một chỗ, và thông tin đòn đánh được gửi cho mọi người đang nhìn thấy. - Vì sao → Dẫn đến → Trên màn hình: Hàng trăm người liên tục dùng skill, buff, debuff lên một con boss → Việc tính máu, danh sách aggro và debuff của boss dồn vào một chỗ, và mỗi đòn đánh đều gửi gói số sát thương và hiệu ứng cho mọi người đang nhìn thấy → Skill trúng trễ và số sát thương hiện dồn một lượt, chỉ quanh boss bị quay chậm - Triệu chứng: Trễ thao tác, Tua nhanh, Quay chậm / Yếu tố: Ngưng trệ, Độ trễ - Ai gặp: Một địa điểm hoặc kênh / Khi nào: Khi đông người - Phụ trách chính: Phát triển server (Đội phát triển game) - Việc cần làm (Đội phát triển game): Gộp hoặc bỏ bớt số sát thương và hiệu ứng của người khác, giới hạn số debuff trên một mục tiêu, chia việc xử lý đòn đánh ra nhiều tick. - Con số tham khảo: 800 người mỗi người đánh 2 lần mỗi giây là 1.600 đòn mỗi giây. Báo hết cho 800 người đang xem là 1,28 triệu message mỗi giây. - Trên đồ thị: Tăng theo số người và tải (Thời gian tick của server, số message gửi đi) - Chỗ cần xem: Thời gian tick và số gói gửi đi trong lúc đánh boss, xem cùng số người quanh boss; nếu được thì thêm số sự kiện mỗi giây (đòn đánh, buff, debuff) theo từng mục tiêu - Đúng nếu: khi số người quanh boss tăng, thời gian tick và lượng gửi tăng dốc, và riêng con boss có số sự kiện mỗi giây gấp hàng chục lần các mục tiêu khác - Loại trừ nếu: cứ tụ tập ở một chỗ là chậm y như vậy, dù có boss hay không → tính toán tầm nhìn hoặc broadcast bùng nổ - Cách kiểm tra: Cần log và chỉ số của server, client game - Nguồn: - [HED-GP Technical Retrospective: What a HED-ache](https://www.eveonline.com/news/view/what-a-hed-ache) · CCP Games · Dù chỉ một đòn tấn công cũng phải báo cho mọi client đang nhìn thấy, tạo ra gánh nặng O(n²) khi n người báo cho n người; đòn tấn công bằng drone có nhiều message nên gánh nặng này tăng còn nhanh hơn #### sp-spawn-burst · Spawn dồn dập khi vào khu vực đông người · Spawn burst when entering a crowd Khi bạn teleport vào một thị trấn chật kín người, server phải gửi cùng lúc ngoại hình, trang bị và trạng thái của hàng trăm người vừa lọt vào tầm nhìn. - Vì sao → Dẫn đến → Trên màn hình: Đột ngột xuất hiện ở nơi đông người do teleport, đăng nhập hoặc chuyển kênh → Toàn bộ thông tin của hàng trăm người được tạo và gửi trong một lần, PC của bạn cũng phải tải tất cả cùng lúc → Vừa đến nơi thì đứng hình một chút, nhân vật hiện ra trễ từng người một và thao tác bị trễ - Triệu chứng: Đứng hình, Trễ thao tác, Tua nhanh / Yếu tố: Ngưng trệ, Độ trễ - Ai gặp: Chỉ mình tôi, Một địa điểm hoặc kênh / Khi nào: Khi di chuyển hoặc chuyển bản đồ, Ngay sau đăng nhập hoặc bảo trì - Phụ trách chính: Phát triển server (Đội phát triển game) / Phối hợp: Phát triển client (Đội phát triển game) - Việc cần làm (Đội phát triển game): Server: gửi theo thứ tự từ gần đến xa, chia ra nhiều tick, cache thông tin ngoại hình. Client: tải trước trong lúc hiện màn hình loading, tạo các nhân vật đã nhận qua nhiều khung hình. - Con số tham khảo: Nếu thông tin ngoại hình, trang bị và buff của một người là 300 byte thì 500 người khoảng 150 KB. Lượng dữ liệu gấp hàng chục lần một tick bình thường (vài KB) dồn vào cùng một lúc. - Trên đồ thị: Tăng vọt ngay sau đăng nhập hoặc bảo trì (Số byte gửi theo từng kết nối, frame time của client) - Chỗ cần xem: Số byte và số gói gửi qua kết nối đó trong vài giây ngay sau khi đến nơi đông người (log server) và frame time của client (net graph, log client) - Đúng nếu: ngay sau khi đến nơi, lượng gửi của kết nối đó vọt lên gấp hàng chục lần một tick bình thường rồi lắng xuống, và cùng lúc đó frame time của client cũng vọt lên - Loại trừ nếu: sang chỗ vắng người mà vẫn đứng y như vậy → chuyển zone (chuyển giao giữa các server) hoặc loading phía client - Cách kiểm tra: Cần log và chỉ số của server, client game - Nguồn: - [Detailed Actor Replication Flow in Unreal Engine](https://dev.epicgames.com/documentation/en-us/unreal-engine/detailed-actor-replication-flow-in-unreal-engine) · Epic Games · Khi mở actor channel lần đầu, gửi kèm các thông tin ban đầu như vị trí, hướng xoay; khi kết nối bão hòa, các actor còn lại dời sang tick sau - [Actor Priority in Unreal Engine](https://dev.epicgames.com/documentation/en-us/unreal-engine/actor-priority-in-unreal-engine) · Epic Games · Xếp mức ưu tiên theo khoảng cách và hướng nhìn, gửi các actor ở gần và trong tầm nhìn trước #### sp-entity-buildup · Đối tượng tích tụ (vật phẩm và vật triệu hồi không được dọn) · Entity / timer buildup over uptime Vật phẩm rơi trên đất, vật triệu hồi, timer đã xong lẽ ra phải biến mất. Nếu chúng không được dọn mà cứ tích tụ, server chạy càng lâu thì mỗi tick càng nhiều việc. - Vì sao → Dẫn đến → Trên màn hình: Vật phẩm rơi trên đất, vật triệu hồi, timer đã hết hạn, dữ liệu tổ đội trống không được xóa kịp thời → Danh sách phải duyệt mỗi tick dài thêm từng ngày → Ngay sau bảo trì vẫn bình thường, vài ngày sau chỉ server hoặc khu vực đó ngày càng ì - Triệu chứng: Quay chậm, Giật khựng, Trễ thao tác / Yếu tố: Ngưng trệ - Ai gặp: Cả server, Một địa điểm hoặc kênh / Khi nào: Càng chạy lâu càng nặng - Phụ trách chính: Phát triển server (Đội phát triển game) - Việc cần làm (Đội phát triển game): Ghi số đối tượng theo từng khu vực thành chỉ số để theo dõi xu hướng, đặt tuổi thọ và giới hạn số lượng cho từng loại đối tượng, dọn dẹp định kỳ. - Con số tham khảo: Với server duyệt qua mọi đối tượng một lần mỗi tick, số đối tượng tăng gấp đôi thì phần đó của thời gian tick cũng tăng gấp đôi. - Trên đồ thị: Tăng dần rồi rơi thẳng (Số đối tượng theo từng zone, thời gian tick của server) - Chỗ cần xem: Số đối tượng (vật phẩm rơi trên đất, vật triệu hồi, timer) và thời gian tick theo từng zone và server, trong khoảng thời gian dài hơn chu kỳ bảo trì (vài tuần) - Đúng nếu: số đối tượng và thời gian tick bắt đầu thấp sau bảo trì, tăng mỗi ngày rồi rơi thẳng xuống mỗi lần bảo trì hoặc khởi động lại, lặp đi lặp lại, trong khi bộ nhớ vẫn dư - Loại trừ nếu: tick không đổi mà chỉ bộ nhớ tăng liên tục → rò rỉ bộ nhớ - Cách kiểm tra: Cần log và chỉ số của server, client game - Tìm hiểu thêm: Giống rò rỉ bộ nhớ ở chỗ càng chạy lâu càng tệ, nhưng khác ở chỗ bộ nhớ vẫn dư mà chỉ thời gian tick tăng. Nếu đồ thị số đối tượng có dạng răng cưa theo từng chu kỳ bảo trì thì là trường hợp này. - Nguồn: - [Actor Ticking in Unreal Engine](https://dev.epicgames.com/documentation/en-us/unreal-engine/actor-ticking-in-unreal-engine) · Epic Games · Actor và component nếu không được đặt khoảng cách riêng sẽ tick mỗi frame một lần, và có thể tắt tick khi không cần - [AActor::SetLifeSpan](https://dev.epicgames.com/documentation/en-us/unreal-engine/API/Runtime/Engine/AActor/SetLifeSpan) · Epic Games · Đặt tuổi thọ (lifespan) cho actor thì actor tự bị hủy khi hết hạn #### sp-patch-traffic · Bản cập nhật làm thay đổi mô hình lưu lượng · Patch changes traffic pattern Khi nội dung, hiệu ứng hoặc trường đồng bộ mới làm tăng kích thước và tần suất gói tin, server đang chạy tốt bắt đầu chạm giới hạn MTU, băng thông và số gói kể từ sau bản cập nhật. - Vì sao → Dẫn đến → Trên màn hình: Bản cập nhật thêm hiệu ứng skill mới, trường đồng bộ, thông tin vật phẩm, khiến gói tin to hơn hoặc dày hơn → Gói lớn vượt MTU nên bị phân mảnh, còn lượng tăng thêm chạm giới hạn băng thông, giới hạn PPS của cloud và bộ đệm gửi → Từ ngay sau bản cập nhật, ở nơi đông người bị dịch chuyển tức thời, nuốt skill, trễ thao tác. Hạ tầng không thay đổi gì mà mất gói lại tăng - Triệu chứng: Dịch chuyển tức thời, Nuốt thao tác·rollback, Trễ thao tác / Yếu tố: Mất gói, Độ trễ - Ai gặp: Cả server, Một địa điểm hoặc kênh, Một khu vực hoặc nhà mạng / Khi nào: Khi đông người, Giờ cao điểm buổi tối, Luôn luôn - Phụ trách chính: Phát triển server (Đội phát triển game) / Phối hợp: Hạ tầng server (Đội hạ tầng), Hạ tầng mạng (Đội hạ tầng) - Việc cần làm (Đội phát triển game): Tự chia gói xuống từ 1.200 byte trở xuống; với trường đồng bộ mới, chỉ gửi phần thay đổi và giảm tần suất theo khoảng cách và mức độ quan trọng; trước khi triển khai, so sánh số gói và byte mỗi giây của mỗi người chơi cùng kích thước gói lớn nhất với build trước trên server test; gắn phiên bản build vào chỉ số lưu lượng. - Việc cần làm (Đội hạ tầng): Thiết bị server·OS: đánh dấu thời điểm triển khai trên đồ thị và so sánh số gói, byte mỗi giây của mỗi người chơi và kích thước gói trung bình trước và sau khi triển khai, cảnh báo theo bộ đếm vượt giới hạn của instance, chuyển sang instance lớn hơn nếu cần. Mạng: kiểm tra giới hạn xử lý của tường lửa, bộ cân bằng tải, thiết bị chống DDoS và việc chúng có chặn fragment hay không. - Con số tham khảo: Gói UDP từ 1.200 byte trở xuống là an toàn; path MTU trên Internet thường là 1.500 byte, đi qua tunnel thì nhỏ hơn (tunnel GRE là 1.476 byte). Gói vượt path MTU sẽ bị phân mảnh hoặc bị bỏ, và gói bị phân mảnh chỉ cần mất một fragment là mất cả gói. Số gói mỗi giây của mỗi người chơi tăng 20% thì cả server cũng tăng 20%, và instance vốn đang chạy gần giới hạn sẽ tràn ngay. - Trên đồ thị: Tăng như bậc thang từ một thời điểm (Số gói và byte mỗi giây của mỗi người chơi, kích thước gói trung bình) - Chỗ cần xem: Số gói và byte mỗi giây trên NIC của server (rxpck/s, txpck/s, rxkB/s, txkB/s của sar -n DEV; NetworkPacketsOut, NetworkOut trên EC2) chia cho số người chơi đồng thời, so sánh trước và sau thời điểm triển khai. Kích thước gói trung bình = byte ÷ gói; phân bố kích thước thì chạy thống kê Packet Lengths của Wireshark trên bản bắt gói (packet capture) - Đúng nếu: ngay sau khi triển khai, số gói và byte của mỗi người chơi hoặc kích thước gói trung bình tăng một bậc rồi giữ nguyên, và từ cùng thời điểm đó số fragment server tạo ra (fragcrt/s của sar -n IP) hoặc bộ đếm vượt giới hạn của instance (pps_allowance_exceeded, bw_out_allowance_exceeded của AWS ENA) tăng lên - Loại trừ nếu: mô hình lưu lượng trước và sau triển khai như nhau mà chỉ độ trễ và mất gói tăng → xem các thay đổi hạ tầng cùng thời điểm (cấu hình, tuyến đường, thiết bị, cập nhật OS hoặc kernel) - Cách kiểm tra: Kiểm tra bằng công cụ hạ tầng (không cần code game) - Tìm hiểu thêm: Khi có báo cáo “trước bản cập nhật vẫn ổn”, đây là nguyên nhân phía game cần kiểm tra trước tiên, cùng với các thay đổi hạ tầng. Dù patch note không có thay đổi gì về mạng, chỉ một hiệu ứng hoặc trường đồng bộ mới cũng bị nhân lên hàng trăm lần ở nơi đông người. Chỗ mà lưu lượng tăng thêm thực sự chạm giới hạn được trình bày ở các mục “Phân mảnh IP của gói UDP”, “Vượt giới hạn PPS trên cloud”, “Băng thông NIC bị bão hòa”, “Bộ đệm socket của kernel quá nhỏ”, “Thiết bị trung gian vượt giới hạn xử lý (tường lửa, IPS, chống DDoS)”. Mục này nói về trường hợp chính bản cập nhật game đã đẩy lưu lượng chạm các giới hạn đó, nên trước khi nâng giới hạn, hãy giảm phần lưu lượng mà bản cập nhật làm tăng thêm. Nếu cùng lúc đó OS hoặc kernel cũng được cập nhật, hãy phân biệt với “Hiệu năng thay đổi sau khi cập nhật OS, kernel, driver hoặc firmware” dựa vào việc lưu lượng của mỗi người chơi có thay đổi hay không. - Nguồn: - [RFC 8085: UDP Usage Guidelines](https://www.rfc-editor.org/rfc/rfc8085) · IETF · Ứng dụng UDP không nên (SHOULD NOT) gửi datagram lớn hơn path MTU; mất một fragment là mất cả gói bị phân mảnh, và một số NAT, tường lửa bỏ hết mọi fragment - [RFC 8899: Packetization Layer Path MTU Discovery for Datagram Transports](https://www.rfc-editor.org/rfc/rfc8899) · IETF · Khuyến nghị 1.200 byte làm kích thước an toàn cơ bản (BASE_PLPMTU) cho truyền datagram như UDP - [Maximum transmission unit and maximum segment size](https://developers.cloudflare.com/magic-transit/reference/mtu-mss/) · Cloudflare · Path MTU trên Internet là 1.500, qua tunnel GRE là 1.476 - [Monitor network performance for ENA settings on your EC2 instance](https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/monitoring-network-performance-ena.html) · AWS · pps_allowance_exceeded và bw_out_allowance_exceeded: số gói bị xếp vào hàng đợi hoặc bị bỏ vì vượt giới hạn PPS hoặc băng thông gửi của instance - [sar(1) — Linux manual page](https://man7.org/linux/man-pages/man1/sar.1.html) · sysstat · rxpck/s và txpck/s (số gói mỗi giây), rxkB/s và txkB/s (số KB mỗi giây) trong sar -n DEV; fragcrt/s (số fragment IP tạo ra mỗi giây, ipFragCreates) trong sar -n IP - [CloudWatch metrics that are available for your instances](https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/viewing_metrics_with_cloudwatch.html) · AWS · NetworkPacketsOut (số gói mà instance gửi qua mọi network interface) và NetworkOut (số byte đã gửi) - [8.7. Packet Lengths](https://www.wireshark.org/docs/wsug_html_chunked/ChStatPacketLengths.html) · Wireshark · Chia các gói đã bắt được theo từng khoảng độ dài, hiển thị số lượng, trung bình, nhỏ nhất và lớn nhất ### L10 Bộ nhớ (9 nguyên nhân) #### mem-gc · GC dừng toàn bộ server · Stop-the-world GC pause Trong lúc server Java hoặc C# dừng mọi thread để thu gom rác (stop-the-world), toàn bộ server đứng lại. - Vì sao → Dẫn đến → Trên màn hình: Heap đầy nên GC bắt đầu chạy → Dừng mọi thread game để thu gom (càng nhiều dữ liệu còn sống thì càng lâu) → Mọi người chơi trên server cùng lúc đứng hình rồi tua nhanh - Triệu chứng: Đứng hình, Tua nhanh / Yếu tố: Ngưng trệ - Ai gặp: Cả server / Khi nào: Theo chu kỳ đều, Càng chạy lâu càng nặng - Phụ trách chính: Phát triển server (Đội phát triển game) / Phối hợp: Hạ tầng server (Đội hạ tầng) - Việc cần làm (Đội phát triển game): Chỉ định rõ trong tùy chọn khởi chạy một GC có thời gian dừng ngắn (ZGC, Shenandoah, hoặc G1 với mục tiêu thời gian dừng thấp hơn), giảm cấp phát bộ nhớ, điều chỉnh kích thước heap. - Việc cần làm (Đội hạ tầng): Dùng instance đủ bộ nhớ để cấp heap rộng rãi, cấp cho container từ 2 CPU và khoảng 1,8 GB bộ nhớ trở lên (với JDK 26 trở xuống, nếu ít hơn mức này thì Serial GC được chọn làm mặc định), giám sát thời gian GC pause. - Con số tham khảo: Minor GC (chỉ thu gom đối tượng mới ở vùng Young) mất vài ms đến vài chục ms. Full GC, thu gom toàn bộ heap chứa vài GB dữ liệu còn sống, có khi mất hơn 1 giây. ZGC dừng dưới 1 ms gần như bất kể kích thước heap, còn Shenandoah cũng dừng ngắn vì thời gian dừng không tăng theo kích thước heap. - Trên đồ thị: Vọt lên theo chu kỳ (Thời gian xử lý tick của server, thời gian GC pause) - Chỗ cần xem: Bật log GC, đặt thời điểm và độ dài của mỗi lần dừng chồng lên đồ thị thời gian xử lý tick của server. Java: dòng Pause trong log do tùy chọn khởi chạy -Xlog:gc* ghi ra (JDK 8 trở xuống dùng -XX:+PrintGCDetails); .NET: chỉ số GC pause trong dotnet-counters (.NET 9 trở đi là dotnet.gc.pause.time, 8 trở xuống là % Time in GC since last GC); Go: dòng mà GODEBUG=gctrace=1 ghi ra sau mỗi lần GC - Đúng nếu: thời điểm tick vọt lên trùng với thời điểm GC pause, độ dài lần dừng xấp xỉ độ dài đợt vọt. Mọi zone và kênh trên server cùng lúc vọt lên - Loại trừ nếu: log GC không có lần dừng dài nào mà tick vẫn vọt lên → nguyên nhân khác như lock, gọi đồng bộ (blocking), ghi ổ đĩa. Chỉ một zone vọt lên → GC của script engine (mem-script-gc) hoặc tải của zone đó - Cách kiểm tra: Kiểm tra bằng công cụ hạ tầng (không cần code game) - Tìm hiểu thêm: Với G1 của Java, mục tiêu cho mỗi lần dừng mặc định là 200 ms, tương đương 4 tick trên server 20 tick. Nếu container được cấp dưới 2 CPU hoặc dưới khoảng 1,8 GB bộ nhớ, Java từ JDK 26 trở xuống sẽ chọn Serial GC (thu gom bằng một thread duy nhất) làm GC mặc định, nên thời gian dừng dài hơn nhiều. Server C# (.NET) thường bật server GC và background GC, nhưng việc thu gom thế hệ 0 và 1 (Gen0·1, nơi chứa đối tượng mới) và Full GC có kèm compaction (dồn gọn bộ nhớ) vẫn dừng mọi thread. Go thường chỉ dừng dưới 1 ms, nhưng khi cấp phát nhiều, bên xin bộ nhớ phải gánh một phần việc GC nên tick chậm đi. Dù là cơ chế nào, nếu cấp phát nhanh hơn thu gom thì cuối cùng thread game vẫn bị dừng. G1 chuyển sang Full GC, còn ZGC dừng thread đang xin bộ nhớ cho đến khi thu gom xong. - Sự cố thực tế: riot-euw-2021 - Nguồn: - [Garbage-First (G1) Garbage Collector](https://docs.oracle.com/en/java/javase/25/gctuning/garbage-first-g1-garbage-collector1.html) · Oracle · Mục tiêu dừng mặc định của G1 là 200 ms (MaxGCPauseMillis); nếu hết bộ nhớ trong lúc thu gom thì chuyển sang Full GC, tức dừng tất cả để compact toàn bộ heap - [JEP 523: Make G1 the Default Garbage Collector in All Environments](https://openjdk.org/jeps/523) · OpenJDK · Trước đây, nếu chỉ có 1 CPU hoặc bộ nhớ dưới 1.792 MB thì Serial GC được chọn làm mặc định; từ JDK 27, G1 là mặc định ở mọi môi trường - [JEP 439: Generational ZGC](https://openjdk.org/jeps/439) · OpenJDK · ZGC dừng không quá 1 ms và không phụ thuộc kích thước heap, G1 dừng từ vài ms đến vài giây. Nếu cấp phát nhanh hơn thu hồi thì có nguy cơ allocation stall (dừng do cấp phát) - [JEP 189: Shenandoah: A Low-Pause-Time Garbage Collector (Experimental)](https://openjdk.org/jeps/189) · OpenJDK · Thời gian dừng của Shenandoah tương đương nhau dù heap là 200 MB hay 200 GB - [Background garbage collection](https://learn.microsoft.com/en-us/dotnet/standard/garbage-collection/background-gc) · .NET · Background GC chỉ áp dụng cho thu gom thế hệ 2, còn thu gom thế hệ 0 và 1 (foreground GC) dừng toàn bộ managed thread - [A Guide to the Go Garbage Collector](https://go.dev/doc/gc-guide) · Go · GC của Go phần lớn chạy đồng thời (concurrent) với chương trình, chỉ có những lần dừng toàn bộ rất ngắn; khi cấp phát nhiều, goroutine phải gánh bớt việc GC (assist) nên phát sinh độ trễ - [JEP 271: Unified GC Logging](https://openjdk.org/jeps/271) · OpenJDK · Từ JDK 9, log GC được viết lại trên cơ chế log hợp nhất (-Xlog); -Xlog:gc ghi một dòng cho mỗi lần GC, giống -XX:+PrintGC trước đây - [The java Command](https://docs.oracle.com/en/java/javase/25/docs/specs/man/java.html) · Oracle · Bảng chuyển các tùy chọn log GC cũ sang -Xlog: -XX:+PrintGCDetails tương ứng với -Xlog:gc* - [dotnet-counters diagnostic tool](https://learn.microsoft.com/en-us/dotnet/core/diagnostics/dotnet-counters) · .NET · .NET 9 trở đi hiển thị bằng các meter System.Runtime (dotnet.gc.pause.time, v.v.), .NET 8 trở xuống hiển thị bằng EventCounter cũ (% Time in GC since last GC, v.v.) - [runtime package](https://pkg.go.dev/runtime) · Go · GODEBUG=gctrace=1: mỗi lần GC một dòng, gồm wall clock time của từng giai đoạn, kích thước heap lúc GC bắt đầu và kết thúc, và heap mục tiêu #### mem-script-gc · GC pause của script engine · Scripting VM GC (Lua, etc.) Ngay cả server viết bằng C++, nếu nhiệm vụ, AI và skill chạy bằng script như Lua thì trong lúc GC của script engine chạy, zone đó sẽ bị dừng. - Vì sao → Dẫn đến → Trên màn hình: Ở mỗi zone, script engine chạy nhiệm vụ, AI và sự kiện, tạo ra một lượng lớn đối tượng tạm → GC của script engine thu gom dồn một lượng lớn trong một lần, tick của zone đó bị dừng → Chỉ khựng theo chu kỳ ở một zone nhất định hoặc trong một sự kiện nhất định - Triệu chứng: Giật khựng, Đứng hình / Yếu tố: Ngưng trệ - Ai gặp: Một địa điểm hoặc kênh / Khi nào: Khi đông người, Theo chu kỳ đều - Phụ trách chính: Phát triển server (Đội phát triển game) - Việc cần làm (Đội phát triển game): Cấu hình GC tăng dần hoặc phân thế hệ, cho GC chạy từng chút một ở mỗi tick, giảm đối tượng tạm trong script. - Con số tham khảo: Khi heap của script phình lên vài trăm MB, một lần thu gom dồn có thể mất vài chục đến vài trăm ms. Đó là khi tắt thu gom tăng dần (incremental), hoặc khi chạy thu gom toàn phần ở chế độ phân thế hệ (generational). - Trên đồ thị: Vọt lên theo chu kỳ (Thời gian xử lý tick theo zone, bộ nhớ của script engine) - Chỗ cần xem: Mỗi tick ghi lại thời gian xử lý tick của từng zone và lượng bộ nhớ mà script engine của zone đó đang dùng (Lua: collectgarbage("count")), rồi đặt chồng lên cùng một đồ thị - Đúng nếu: thời điểm bộ nhớ script tụt mạnh (thu gom dồn một lần) trùng với lúc tick của zone đó vọt lên, các zone khác vẫn bình thường - Loại trừ nếu: tick vọt lên mà bộ nhớ script không đổi → tải hoặc lock của zone đó. Mọi zone trên server cùng lúc vọt lên → GC của server (mem-gc) hoặc swap (mem-swap) - Cách kiểm tra: Cần log và chỉ số của server, client game - Nguồn: - [Lua 5.4 Reference Manual](https://www.lua.org/manual/5.4/manual.html) · Lua.org · Chế độ tăng dần chia việc thu gom thành các bước nhỏ xen giữa lúc chương trình chạy (đặt bước quá lớn thì thành dừng toàn bộ); lần thu gom major ở chế độ phân thế hệ là dừng toàn bộ để duyệt mọi đối tượng; collectgarbage("count") trả về tổng bộ nhớ Lua đang dùng (KB) #### mem-alloc · Cấp phát bộ nhớ dồn dập · Allocation storms Khi sự kiện tạo ra hàng loạt đối tượng tạm, GC phải chạy thường xuyên hơn nhiều so với bình thường. - Vì sao → Dẫn đến → Trên màn hình: Vật phẩm rơi ra, log chiến đấu và phần thưởng sự kiện làm số đối tượng tạm tăng vọt → GC chạy dày gấp mấy lần, các đối tượng chưa kịp bị bỏ đi bị chuyển sang vùng Old, khiến Full GC cũng đến sớm hơn → Chỉ khựng theo chu kỳ trong lúc có sự kiện - Triệu chứng: Giật khựng, Đứng hình / Yếu tố: Ngưng trệ - Ai gặp: Cả server, Một địa điểm hoặc kênh / Khi nào: Khi đông người - Phụ trách chính: Phát triển server (Đội phát triển game) - Việc cần làm (Đội phát triển game): Object pool, dùng lại bộ đệm, profiling việc cấp phát bộ nhớ. - Trên đồ thị: Tăng theo số người và tải (Số lần GC, tốc độ cấp phát) - Chỗ cần xem: Đếm số lần GC mỗi phút qua log GC (Java -Xlog:gc, Go GODEBUG=gctrace=1); với .NET, xem lượng cấp phát và số lần GC trong dotnet-counters (.NET 9 trở đi: dotnet.gc.heap.total_allocated, dotnet.gc.collections; 8 trở xuống: Allocation Rate, Gen 0 GC Count). Đặt chồng với số người chơi đồng thời và thời điểm sự kiện - Đúng nếu: khi sự kiện bắt đầu, tốc độ cấp phát và số lần GC tăng dốc hơn mức tăng số người, các lần dừng ngắn dày lên. Sự kiện kết thúc thì trở lại như cũ - Loại trừ nếu: số lần GC giữ nguyên nhưng mỗi lần dừng dài ra → lượng dữ liệu còn sống đã tăng (mem-gc-thrash, mem-leak) - Cách kiểm tra: Kiểm tra bằng công cụ hạ tầng (không cần code game) - Nguồn: - [Garbage Collector Implementation](https://docs.oracle.com/en/java/javase/25/gctuning/garbage-collector-implementation.html) · Oracle · Vùng Young đầy thì chạy minor GC, một phần đối tượng sống sót được chuyển sang vùng Old; vùng Old đầy thì thu gom toàn bộ heap (lâu hơn minor GC nhiều); -Xlog:gc ghi một dòng cho mỗi lần GC - [A Guide to the Go Garbage Collector](https://go.dev/doc/gc-guide) · Go · Tốc độ cấp phát càng cao thì chu kỳ GC càng dày, GODEBUG=gctrace=1 in ra thông tin theo dõi GC - [dotnet-counters diagnostic tool](https://learn.microsoft.com/en-us/dotnet/core/diagnostics/dotnet-counters) · .NET · .NET 9 trở đi hiển thị là dotnet.gc.heap.total_allocated và dotnet.gc.collections, .NET 8 trở xuống là Allocation Rate và Gen 0 GC Count #### mem-leak · Rò rỉ bộ nhớ · Memory leak Bộ nhớ không được giải phóng tích tụ dần, vài ngày sau dẫn tới GC chạy dồn dập, swap hoặc tiến trình bị buộc kết thúc. - Vì sao → Dẫn đến → Trên màn hình: Dữ liệu của nhân vật đã thoát game và các event handler không được giải phóng → Bộ nhớ trống giảm dần qua nhiều ngày → Ngay sau bảo trì thì bình thường, càng ngày càng lag, cuối cùng server sập - Triệu chứng: Quay chậm, Đứng hình, Mất kết nối / Yếu tố: Ngưng trệ - Ai gặp: Cả server / Khi nào: Càng chạy lâu càng nặng, Giờ cao điểm buổi tối - Phụ trách chính: Phát triển server (Đội phát triển game) / Phối hợp: Hạ tầng server (Đội hạ tầng) - Việc cần làm (Đội phát triển game): Phân tích heap dump, test tải trong thời gian dài. - Việc cần làm (Đội hạ tầng): Giám sát và cảnh báo xu hướng mức dùng bộ nhớ của từng tiến trình. - Trên đồ thị: Tăng dần (Bộ nhớ tiến trình (RSS), heap ngay sau GC) - Chỗ cần xem: Xem bộ nhớ của tiến trình server game (RSS trong pidstat -r) theo đơn vị nhiều ngày; server có GC thì xem heap còn lại ngay sau GC. Java: giá trị sau GC trong cặp mức dùng trước và sau GC ở dòng -Xlog:gc; .NET: kích thước heap sau GC trong dotnet-counters (.NET 9 trở đi là dotnet.gc.last_collection.heap.size, 8 trở xuống là GC Heap Size) - Đúng nếu: heap còn lại ngay sau GC (đường đáy) tăng lên mỗi ngày kể từ lần khởi động lại, kể cả lúc rạng sáng ít người cũng không giảm xuống - Loại trừ nếu: đường đáy của heap đi ngang mà chỉ RSS tăng → phân mảnh (mem-fragment) hoặc bộ nhớ native. Lên xuống theo số người → mức dùng bình thường - Cách kiểm tra: Kiểm tra bằng công cụ hạ tầng (không cần code game) - Tìm hiểu thêm: Nếu server được khởi động lại hằng tuần trong đợt bảo trì định kỳ, rò rỉ bị che đi nên rất lâu mới bị phát hiện. Nhiều khi nó chỉ đột ngột lộ ra khi một đợt bảo trì bị lùi lại, hoặc khi sự kiện làm số người chơi tăng lên. - Nguồn: - [Troubleshoot Memory Leaks](https://docs.oracle.com/en/java/javase/25/troubleshoot/troubleshooting-memory-leaks.html) · Oracle · Chương trình chạy chậm dần thì nghi rò rỉ, cuối cùng hết bộ nhớ và kết thúc bất thường. Dữ liệu cốt lõi để phân tích rò rỉ là heap dump - [Debug a memory leak in .NET](https://learn.microsoft.com/en-us/dotnet/core/diagnostics/debug-memory-leak) · .NET · Dù có GC, nếu cứ tham chiếu mãi đối tượng không còn cần thì vẫn rò rỉ, gây giảm hiệu năng và OutOfMemoryException. Xem xu hướng bộ nhớ và phân tích dump - [Garbage Collector Implementation](https://docs.oracle.com/en/java/javase/25/gctuning/garbage-collector-implementation.html) · Oracle · Dòng -Xlog:gc có dạng “mức dùng trước GC->mức dùng sau GC (kích thước heap)” - [dotnet-counters diagnostic tool](https://learn.microsoft.com/en-us/dotnet/core/diagnostics/dotnet-counters) · .NET · .NET 9 trở đi hiển thị là dotnet.gc.last_collection.heap.size, .NET 8 trở xuống là GC Heap Size - [pidstat(1) — Linux manual page](https://man7.org/linux/man-pages/man1/pidstat.1.html) · sysstat · -r: RSS của từng tiến trình (bộ nhớ thực sự nằm trên RAM) và page fault #### mem-gc-thrash · GC thrashing (heap còn ít chỗ trống) · GC thrashing (heap nearly full) Khi dữ liệu còn sống gần chạm giới hạn heap, GC chạy xong cũng gần như không thu hồi được gì, nên GC cứ lặp lại không ngừng. - Vì sao → Dẫn đến → Trên màn hình: Số người tăng do sự kiện hoặc rò rỉ bộ nhớ làm dữ liệu còn sống lấp gần tới giới hạn heap → GC chỉ thu hồi được một ít nên lập tức lại chạy Full GC, phần lớn CPU bị GC chiếm → Cả server lặp đi lặp lại quay chậm và đứng hình trong vài phút rồi sập vì hết bộ nhớ - Triệu chứng: Quay chậm, Đứng hình, Mất kết nối / Yếu tố: Ngưng trệ - Ai gặp: Cả server / Khi nào: Giờ cao điểm buổi tối, Khi đông người, Càng chạy lâu càng nặng - Phụ trách chính: Phát triển server (Đội phát triển game) / Phối hợp: Hạ tầng server (Đội hạ tầng) - Việc cần làm (Đội phát triển game): Đặt heap rộng hơn hẳn lượng dữ liệu còn sống lúc cao điểm (thường từ 2 lần trở lên), giảm dữ liệu bị tham chiếu lâu và rò rỉ. - Việc cần làm (Đội hạ tầng): Cảnh báo theo tỷ lệ thời gian GC, khởi động lại sớm thay vì cố cầm cự, dùng instance nhiều bộ nhớ để có thể tăng heap. - Con số tham khảo: Nếu GC chiếm hơn 10% thời gian chạy thì thường được coi là dấu hiệu nguy hiểm. Một số GC của Java báo lỗi hết bộ nhớ khi đã dành 98% thời gian cho GC mà vẫn gần như không thu hồi được gì. - Trên đồ thị: Chạm giới hạn rồi đi ngang (Heap ngay sau GC, tỷ lệ thời gian GC) - Chỗ cần xem: Xem heap còn lại ngay sau GC gần mức heap tối đa đến đâu, và tỷ lệ thời gian dành cho GC. Java: “sau GC (kích thước heap)” trên dòng -Xlog:gc và tần suất dòng Pause Full; .NET: dotnet-counters (.NET 8 trở xuống là % Time in GC since last GC, .NET 9 trở đi là mức tăng của dotnet.gc.pause.time); Go: khoảng cách giữa các dòng GODEBUG=gctrace=1 - Đúng nếu: ngay sau GC heap vẫn gần mức tối đa, Full GC chạy liên tiếp, tỷ lệ thời gian GC tăng mạnh so với bình thường (thường vượt 10%). Trong lúc đó tick của cả server cùng chậm đi - Loại trừ nếu: heap sau GC vẫn còn dư mà chỉ lần dừng kéo dài → do loại GC hoặc cấu hình GC (mem-gc) - Cách kiểm tra: Kiểm tra bằng công cụ hạ tầng (không cần code game) - Nguồn: - [The Parallel Collector](https://docs.oracle.com/en/java/javase/25/gctuning/parallel-collector1.html) · Oracle · Parallel GC báo OutOfMemoryError khi dành hơn 98% tổng thời gian cho GC mà thu hồi được dưới 2% heap - [Garbage-First Garbage Collector Tuning](https://docs.oracle.com/en/java/javase/25/gctuning/garbage-first-garbage-collector-tuning.html) · Oracle · Mặc định (GCTimeRatio=12), G1 chọn kích thước heap sao cho thời gian GC khoảng 8% tổng thời gian trở xuống; Full GC do heap bị chiếm quá nhiều được tìm trong log qua dòng Pause Full (G1 Compaction Pause) - [A Guide to the Go Garbage Collector](https://go.dev/doc/gc-guide) · Go · Với mặc định GOGC=100, heap mục tiêu khoảng gấp 2 lần heap còn sống; khi bám sát giới hạn bộ nhớ, GC chạy không nghỉ (thrashing); GODEBUG=gctrace=1 in ra thông tin theo dõi GC - [Garbage Collector Implementation](https://docs.oracle.com/en/java/javase/25/gctuning/garbage-collector-implementation.html) · Oracle · Dòng -Xlog:gc gồm loại GC (Pause Young, Pause Full), “mức dùng trước GC->mức dùng sau GC (kích thước heap)” và thời gian dừng - [dotnet-counters diagnostic tool](https://learn.microsoft.com/en-us/dotnet/core/diagnostics/dotnet-counters) · .NET · .NET 9 trở đi hiển thị là dotnet.gc.pause.time, .NET 8 trở xuống là % Time in GC since last GC #### mem-swap · Swap · Swapping Khi thiếu bộ nhớ, OS đẩy một phần xuống ổ đĩa. Sau đó, mỗi lần cần dùng phần bộ nhớ ấy, server phải chờ ổ đĩa chậm hơn RAM trên 1.000 lần. - Vì sao → Dẫn đến → Trên màn hình: Bộ nhớ đang dùng vượt quá RAM thực → OS đẩy một phần xuống ổ đĩa, khi cần thì đọc lên lại → Tick tăng vọt lên vài trăm ms, mọi người chơi trên server bị quay chậm và đứng hình - Triệu chứng: Quay chậm, Đứng hình / Yếu tố: Ngưng trệ - Ai gặp: Cả server / Khi nào: Càng chạy lâu càng nặng, Giờ cao điểm buổi tối - Phụ trách chính: Hạ tầng server (Đội hạ tầng) / Phối hợp: Phát triển server (Đội phát triển game) - Việc cần làm (Đội phát triển game): Đặt giới hạn trên cho mức dùng bộ nhớ của tiến trình (kích thước heap, v.v.) và kiểm tra rò rỉ. - Việc cần làm (Đội hạ tầng): Cấu hình để server game không dùng swap, xử lý bằng cảnh báo bộ nhớ; khi tắt swap, tiến trình bị buộc kết thúc (OOM) ngay lúc thiếu bộ nhớ, nên cần chuẩn bị RAM dư dả so với mức dùng lúc cao điểm. - Con số tham khảo: Đọc RAM khoảng 100 ns, đọc lại từ SSD khoảng 100 µs (1.000 lần), ổ đĩa cloud nằm bên kia mạng khoảng 1 ms (10.000 lần), HDD 10 ms (100.000 lần). - Trên đồ thị: Tăng dần (Mức dùng swap, swap in/out) - Chỗ cần xem: Đặt chồng lên thời gian xử lý tick: cột si, so của vmstat 1 (lượng đọc vào từ swap và đẩy ra swap mỗi giây), some và full trong /proc/pressure/memory (tỷ lệ thời gian bị dừng vì chờ bộ nhớ), majflt/s trong pidstat -r của tiến trình server game (page fault phải đọc từ ổ đĩa) - Đúng nếu: lúc lag, si lớn hơn 0, đồng thời majflt/s của server game và giá trị full của memory cùng tăng - Loại trừ nếu: si, so bằng 0 và áp lực bộ nhớ (PSI) cũng gần 0 → swap không phải nguyên nhân. Không có swap mà majflt/s và PSI tăng → bộ nhớ đã cạn, OS đang phải đọc lại trang code, cần ưu tiên bảo đảm đủ bộ nhớ trước - Cách kiểm tra: Kiểm tra bằng công cụ hạ tầng (không cần code game) - Tìm hiểu thêm: Server có GC phải đọc khắp heap khi thu gom, nên chỉ cần một phần heap bị swap là một lần GC có thể kéo dài tới vài giây, thậm chí vài chục giây. Khi tắt swap, tiến trình chuyển thẳng sang bị buộc kết thúc (OOM) mà không qua giai đoạn chậm dần vì swap, vì vậy phải bảo đảm bộ nhớ dư trước. Kể cả không có swap, khi bộ nhớ gần cạn, OS còn gỡ cả trang code của file thực thi khỏi bộ nhớ rồi phải đọc lại, nên cả server có thể chậm đi nghiêm trọng một thời gian trước khi tiến trình bị buộc kết thúc. - Nguồn: - [Designs, Lessons and Advice from Building Large Distributed Systems (LADIS 2009 keynote)](https://www.cs.cornell.edu/projects/ladis2009/talks/dean-keynote-ladis2009.pdf) · Google · “Numbers Everyone Should Know”: truy cập bộ nhớ chính 100 ns, seek ổ đĩa 10 ms (số liệu năm 2009) - [Documentation for /proc/sys/vm/](https://docs.kernel.org/admin-guide/sysctl/vm.html) · Linux kernel · swappiness: chi phí tương đối giữa swap và thu hồi trang file; swap là I/O ngẫu nhiên nên tốn kém - [Concepts overview](https://docs.kernel.org/admin-guide/mm/concepts.html) · Linux kernel · Kernel thu hồi page cache (có bản gốc trên ổ đĩa) và các trang có thể swap; nếu vẫn thiếu thì OOM killer buộc một tiến trình kết thúc - [Solidigm™ D7-P5520 and D7-P5620 Product Brief](https://www.solidigm.com/products/data-center/product-briefs/d7-p5520-p5620-product-brief.html) · Solidigm · Độ trễ 99,99% (four-nines latency) của SSD NVMe dùng cho server là 130 µs: căn cứ cho con số một lần đọc SSD mất khoảng 100 µs - [Amazon EBS General Purpose SSD volumes](https://docs.aws.amazon.com/ebs/latest/userguide/general-purpose.html) · AWS · Độ trễ của ổ đĩa mặc định trên cloud (gp3) ở mức một chữ số ms - [vmstat(8) — Linux manual page](https://man7.org/linux/man-pages/man8/vmstat.8.html) · procps-ng · si: lượng bộ nhớ đọc vào từ swap mỗi giây, so: lượng bộ nhớ đẩy ra swap mỗi giây - [PSI - Pressure Stall Information](https://docs.kernel.org/accounting/psi.html) · Linux kernel · some (tỷ lệ thời gian một số tác vụ bị dừng) và full (tỷ lệ thời gian mọi tác vụ cùng bị dừng) trong /proc/pressure/memory - [pidstat(1) — Linux manual page](https://man7.org/linux/man-pages/man1/pidstat.1.html) · sysstat · -r: majflt/s (số page fault phải đọc trang từ ổ đĩa) #### mem-cache-miss · Cache miss · CPU cache misses Khi dữ liệu nằm rải rác khắp bộ nhớ, lần nào CPU cũng phải đi tới tận RAM vốn chậm hơn và chờ. - Vì sao → Dẫn đến → Trên màn hình: Đối tượng nằm rải rác, nối với nhau bằng con trỏ, và bị truy cập không theo thứ tự → Dữ liệu không có trong cache CPU nên lần nào cũng phải đọc từ RAM (chậm hơn khoảng 100 lần) → Cùng một việc mà chi phí mỗi tick tăng gấp mấy lần, nặng thì quay chậm - Triệu chứng: Quay chậm / Yếu tố: Ngưng trệ - Ai gặp: Cả server / Khi nào: Luôn luôn, Khi đông người - Phụ trách chính: Phát triển server (Đội phát triển game) - Việc cần làm (Đội phát triển game): Xếp liền nhau những dữ liệu hay được dùng cùng nhau (thiết kế hướng dữ liệu, data-oriented design). - Trên đồ thị: Luôn cao ngay từ đầu (Thời gian xử lý tick, mức sử dụng CPU) - Chỗ cần xem: Chạy perf stat -d -p PID trên tiến trình server game để đo số lệnh mỗi chu kỳ (insn per cycle) và cache miss L1, LLC, rồi xem cùng thời gian xử lý tick và mức sử dụng CPU - Đúng nếu: CPU bận liên tục mà insn per cycle thấp, LLC miss nhiều. Nếu ở bản build đã đổi cách bố trí dữ liệu, cùng số người mà thời gian xử lý tick giảm mạnh thì có thể khẳng định - Loại trừ nếu: mức sử dụng CPU thấp mà tick vẫn chậm → nguyên nhân chờ bên ngoài CPU như lock, chờ I/O - Cách kiểm tra: Kiểm tra bằng công cụ hạ tầng (không cần code game) - Nguồn: - [Designs, Lessons and Advice from Building Large Distributed Systems (LADIS 2009 keynote)](https://www.cs.cornell.edu/projects/ladis2009/talks/dean-keynote-ladis2009.pdf) · Google · Cache L1 mất 0,5 ns, cache L2 mất 7 ns, bộ nhớ chính mất 100 ns (số liệu năm 2009): đi tới RAM thì chậm hơn cache một đến hai bậc độ lớn - [perf-stat(1) — Linux manual page](https://man7.org/linux/man-pages/man1/perf-stat.1.html) · perf · -p đếm sự kiện phần cứng của tiến trình đang chạy và hiển thị insn per cycle, -d bổ sung các sự kiện cache dữ liệu L1 và LLC #### mem-fragment · Phân mảnh bộ nhớ · Heap fragmentation Cấp phát và giải phóng lặp đi lặp lại làm vùng trống bị chia vụn, khiến tiến trình chiếm nhiều bộ nhớ hơn hẳn lượng thực sự dùng. - Vì sao → Dẫn đến → Trên màn hình: Nhiều thread cấp phát và giải phóng các vùng nhớ đủ mọi kích thước trong thời gian dài → Vùng trống vụn rải rác nên không trả lại được cho OS, mức dùng cứ tăng như bị rò rỉ → Càng chạy lâu càng chậm vì swap hoặc thiếu bộ nhớ, rồi bị buộc kết thúc - Triệu chứng: Quay chậm, Mất kết nối / Yếu tố: Ngưng trệ - Ai gặp: Cả server / Khi nào: Càng chạy lâu càng nặng - Phụ trách chính: Phát triển server (Đội phát triển game) - Việc cần làm (Đội phát triển game): Memory pool theo kích thước, bộ cấp phát (allocator) chống phân mảnh tốt (jemalloc, mimalloc, v.v.). - Trên đồ thị: Tăng dần (Bộ nhớ tiến trình (RSS)) - Chỗ cần xem: Chạy hai server cùng bản build; ở một server, giảm số arena của glibc bằng biến môi trường MALLOC_ARENA_MAX hoặc đổi sang bộ cấp phát khác như jemalloc, rồi so sánh RSS trong pidstat -r của hai server trong vài ngày - Đúng nếu: số người và số đối tượng tương đương nhau mà chỉ server đã đổi là RSS ngừng tăng hoặc mức tăng giảm hẳn - Loại trừ nếu: đổi bộ cấp phát mà RSS vẫn tăng y như cũ → bộ nhớ không được giải phóng (mem-leak) - Cách kiểm tra: Kiểm tra bằng công cụ hạ tầng (không cần code game) - Tìm hiểu thêm: Dấu hiệu giống hệt rò rỉ nên phân tích heap cũng không thấy chỗ rò. Bộ cấp phát mặc định của Linux (glibc) bị phân mảnh đặc biệt nặng ở server có nhiều thread, nên chỉ cần đổi bộ cấp phát là mức dùng bộ nhớ có thể giảm đáng kể. - Nguồn: - [mallopt(3) — Linux manual page](https://man7.org/linux/man-pages/man3/mallopt.3.html) · Linux man-pages · Để giảm tranh chấp giữa các thread, glibc malloc tạo arena tới bội số của số CPU, và càng nhiều arena thì mức dùng bộ nhớ càng tăng (giới hạn bằng M_ARENA_MAX, cũng đặt được qua biến môi trường MALLOC_ARENA_MAX) - [jemalloc memory allocator](https://jemalloc.net/) · jemalloc · Bản cài đặt malloc đa dụng, chú trọng tránh phân mảnh và mở rộng tốt khi chạy đồng thời - [pidstat(1) — Linux manual page](https://man7.org/linux/man-pages/man1/pidstat.1.html) · sysstat · -r: RSS của từng tiến trình (bộ nhớ thực sự nằm trên RAM) #### mem-numa · Truy cập bộ nhớ NUMA từ xa · Remote NUMA access Trên server có hai CPU, nếu tiến trình dùng bộ nhớ gắn với CPU bên kia thì truy cập bộ nhớ chậm đi. - Vì sao → Dẫn đến → Trên màn hình: Thread và bộ nhớ nằm trên hai socket CPU khác nhau → Truy cập bộ nhớ chậm đi (tùy thiết bị, 1,5–2 lần) → Cùng cấu hình mà mỗi tiến trình cho hiệu năng khác nhau - Triệu chứng: Quay chậm / Yếu tố: Ngưng trệ - Ai gặp: Cả server / Khi nào: Luôn luôn - Phụ trách chính: Hạ tầng server (Đội hạ tầng) - Việc cần làm (Đội hạ tầng): Ghim tiến trình và bộ nhớ vào một socket (numactl); nếu máy có hai socket thì chia tiến trình server game ra chạy riêng trên từng socket. - Trên đồ thị: Chỉ một phần cao (Thời gian xử lý tick theo tiến trình, bộ nhớ theo node) - Chỗ cần xem: Dùng numastat -p PID xem bộ nhớ của tiến trình server game nằm ở NUMA node nào, numa_miss và other_node trong numastat có tăng không, rồi so với node của CPU mà tiến trình đó đang chạy - Đúng nếu: chỉ tiến trình chậm có phần lớn bộ nhớ nằm ở node khác với CPU nó đang chạy, và khi dùng numactl ghim CPU và bộ nhớ vào cùng một node rồi chạy lại thì chênh lệch biến mất - Loại trừ nếu: bố trí node giống tiến trình nhanh mà vẫn chậm → nguyên nhân khác như noisy neighbor, CPU throttling, tải của chính tiến trình đó - Cách kiểm tra: Kiểm tra bằng công cụ hạ tầng (không cần code game) - Nguồn: - [What is NUMA?](https://docs.kernel.org/mm/numa.html) · Linux kernel · Bộ nhớ trong cùng cell nhanh hơn và băng thông lớn hơn, bộ nhớ ở cell khác (từ xa) truy cập chậm hơn - [numactl(8) — Linux manual page](https://man7.org/linux/man-pages/man8/numactl.8.html) · numactl · --cpunodebind và --membind ghim CPU và bộ nhớ của tiến trình vào một NUMA node cụ thể - [numastat(8) — Linux manual page](https://man7.org/linux/man-pages/man8/numastat.8.html) · numactl · Counter numa_miss (cấp phát ở node khác node mong muốn) và other_node (tiến trình chạy ở node khác cấp phát trên node này), -p để xem bộ nhớ của tiến trình theo từng node ### L11 Ổ đĩa (9 nguyên nhân) #### dk-sync-log · Ghi log đồng bộ · Synchronous logging Nếu thread game chờ ổ đĩa ghi xong từng dòng log, thì lúc ổ đĩa bận, game cũng bị dừng theo. - Vì sao → Dẫn đến → Trên màn hình: Thread game ghi log chiến đấu và log giao dịch thẳng vào file → Khi yêu cầu lưu chắc chắn (fsync), hoặc khi bộ đệm ghi của OS (page cache) chạm giới hạn, một lần ghi lúc ổ đĩa bận mất tới vài chục ms → Khựng trong những trận đánh sinh nhiều log - Triệu chứng: Giật khựng, Đứng hình / Yếu tố: Ngưng trệ - Ai gặp: Một địa điểm hoặc kênh, Cả server / Khi nào: Khi đông người - Phụ trách chính: Phát triển server (Đội phát triển game) / Phối hợp: Hạ tầng server (Đội hạ tầng) - Việc cần làm (Đội phát triển game): Ghi log bất đồng bộ (bộ đệm trong bộ nhớ + thread riêng), giảm lượng log, không gọi fsync trên thread game. - Việc cần làm (Đội hạ tầng): Chạy log rotation và nén log với mức ưu tiên I/O thấp, để log trên ổ đĩa khác với dữ liệu, giám sát độ trễ ghi ổ đĩa. - Trên đồ thị: Thỉnh thoảng vọt lên bất chợt (Thời gian xử lý tick của server, độ trễ ghi ổ đĩa) - Chỗ cần xem: Đặt w_await, aqu-sz của iostat -x 1 chồng lên thời gian xử lý tick, dùng perf trace -p PID --duration 10 tìm các lời gọi write, fsync mất hơn 10 ms trong server game và thread đã gọi chúng - Đúng nếu: lúc tick vọt lên, lời gọi write, fsync của thread game mất vài chục ms, đồng thời độ trễ ghi ổ đĩa cũng vọt lên. Thường trùng với lúc log rotation hoặc nén log - Loại trừ nếu: thread game không có system call nào chạy lâu mà tick vẫn vọt lên → nguyên nhân khác như GC, lock, vượt tick budget. Chỉ thread chuyên ghi log có lời gọi chạy lâu → không ảnh hưởng đến game - Cách kiểm tra: Kiểm tra bằng công cụ hạ tầng (không cần code game) - Tìm hiểu thêm: Thông thường OS nhận thao tác ghi vào bộ nhớ (page cache) trước rồi mới ghi xuống ổ đĩa sau, nên ghi một dòng log thường xong ngay. Tình trạng dừng xảy ra khi fsync yêu cầu lưu chắc chắn, khi lượng ghi tồn đọng vượt giới hạn khiến OS chặn lời gọi ghi, hoặc khi file log được rotate hay nén. Vì vậy bình thường không sao, chỉ vọt lên đúng lúc ổ đĩa bận. - Nguồn: - [fsync(2) — Linux manual page](https://man7.org/linux/man-pages/man2/fsync.2.html) · Linux man-pages · fsync đẩy dữ liệu đã thay đổi xuống tận ổ đĩa (kể cả cache của ổ đĩa) và chặn cho tới khi thiết bị báo hoàn tất - [Documentation for /proc/sys/vm/](https://docs.kernel.org/admin-guide/sysctl/vm.html) · Linux kernel · Khi lượng ghi tồn đọng (dirty) chạm dirty_ratio, chính tiến trình đang ghi phải tự đảm nhận việc ghi xuống ổ đĩa - [ionice(1) — Linux manual page](https://man7.org/linux/man-pages/man1/ionice.1.html) · util-linux · Tác vụ chạy với mức ưu tiên I/O idle chỉ được dùng ổ đĩa khi không chương trình nào khác đang dùng - [iostat(1) — Linux manual page](https://man7.org/linux/man-pages/man1/iostat.1.html) · sysstat · -x: w_await (thời gian xử lý trung bình của yêu cầu ghi, tính cả thời gian chờ trong hàng đợi), aqu-sz (độ dài hàng đợi trung bình, tên cũ là avgqu-sz) - [perf-trace(1) — Linux manual page](https://man7.org/linux/man-pages/man1/perf-trace.1.html) · perf · -p theo dõi system call của tiến trình đang chạy, --duration chỉ hiện các lời gọi mất lâu hơn số ms chỉ định #### dk-fsync · fsync dồn dập · fsync storms Mỗi yêu cầu ghi dữ liệu xuống ổ đĩa “chắc chắn” mất từ 0,1 ms đến vài chục ms tùy ổ đĩa, và khi các yêu cầu dồn lại thì hàng đợi dài ra. - Vì sao → Dẫn đến → Trên màn hình: Lưu định kỳ và đợt đăng xuất ồ ạt làm yêu cầu ghi chắc chắn dồn lại → Hàng đợi ổ đĩa dài ra → Cứ đến giờ lưu là lag, đăng xuất và chuyển kênh bị chậm - Triệu chứng: Giật khựng, Trễ thao tác / Yếu tố: Ngưng trệ, Độ trễ - Ai gặp: Cả server / Khi nào: Theo chu kỳ đều, Khi đông người - Phụ trách chính: Phát triển server (Đội phát triển game) / Phối hợp: Hạ tầng server (Đội hạ tầng), Hạ tầng DB (Đội hạ tầng) - Việc cần làm (Đội phát triển game): Gom các lần lưu để ghi một lượt (nhiều lần lưu chung một lần fsync), rải thời điểm lưu. - Việc cần làm (Đội hạ tầng): Thiết bị server·OS: SSD cho server có tính năng bảo vệ khi mất điện, giám sát độ dài hàng đợi ổ đĩa và độ trễ fsync. Thiết bị DB: nếu dữ liệu được lưu vào DB thì ổ đĩa chứa log của DB cũng dùng loại SSD đó, giám sát độ trễ commit. - Con số tham khảo: Thời gian mỗi lần tùy thiết bị, nhưng đại khái khoảng 0,1 ms với SSD cho server (có tính năng bảo vệ khi mất điện), từ 1 đến vài ms với SSD thông thường, 1–2 ms với ổ đĩa cloud, từ 10 ms trở lên với HDD. Nếu một thread chờ từng lần một thì HDD không làm nổi 100 lần trong 1 giây. - Trên đồ thị: Vọt lên theo chu kỳ (Độ dài hàng đợi ổ đĩa, độ trễ flush và ghi) - Chỗ cần xem: Đặt f/s, f_await của iostat -x 1 (số lần flush ổ đĩa đã xử lý và thời gian mất), w/s, aqu-sz, w_await chồng lên thời điểm lưu định kỳ và đăng xuất. Bản sysstat cũ hiển thị aqu-sz là avgqu-sz. Với ổ đĩa cloud, xem VolumeQueueLength, VolumeAvgWriteLatency của EBS - Đúng nếu: cứ đến giờ lưu hoặc đợt đăng xuất ồ ạt thì số lần flush và độ dài hàng đợi cùng vọt lên, w_await và f_await tăng gấp mấy lần bình thường. Lúc đó việc lưu và chuyển kênh bị chậm - Loại trừ nếu: hàng đợi vọt lên vào lúc không liên quan tới lưu hay đăng xuất → sao lưu, nén (dk-backup) hoặc giới hạn IOPS (dk-iops). Số lần flush không đổi mà vẫn chậm đi → cạn burst credit (dk-burst) - Cách kiểm tra: Kiểm tra bằng công cụ hạ tầng (không cần code game) - Nguồn: - [fsync(2) — Linux manual page](https://man7.org/linux/man-pages/man2/fsync.2.html) · Linux man-pages · fsync xả cả cache của ổ đĩa và chặn cho tới khi thiết bị báo hoàn tất - [Reliability (PostgreSQL Documentation)](https://www.postgresql.org/docs/current/wal-reliability.html) · PostgreSQL · Ổ SATA thông thường và nhiều SSD có cache ghi bị mất khi mất điện, nên muốn lưu chắc chắn thì cần cache có pin hoặc có bảo vệ nguồn - [Amazon EBS General Purpose SSD volumes](https://docs.aws.amazon.com/ebs/latest/userguide/general-purpose.html) · AWS · Độ trễ của ổ đĩa mặc định trên cloud (gp3) ở mức một chữ số ms, io2 Block Express trung bình dưới 500 µs với I/O 16 KiB - [Designs, Lessons and Advice from Building Large Distributed Systems (LADIS 2009 keynote)](https://www.cs.cornell.edu/projects/ladis2009/talks/dean-keynote-ladis2009.pdf) · Google · Một lần seek HDD mất 10 ms - [iostat(1) — Linux manual page](https://man7.org/linux/man-pages/man1/iostat.1.html) · sysstat · -x: f/s, f_await (số yêu cầu flush ổ đĩa đã xử lý và thời gian trung bình), w/s, w_await, aqu-sz (tên cũ là avgqu-sz) - [Amazon CloudWatch metrics for Amazon EBS](https://docs.aws.amazon.com/ebs/latest/userguide/using_cloudwatch_ebs.html) · AWS · VolumeQueueLength (số yêu cầu đang chờ hoàn tất), VolumeAvgWriteLatency (độ trễ ghi trung bình 1 phút, instance Nitro) #### dk-burst · Cạn burst credit của ổ đĩa cloud · Burst credit depletion Một số ổ đĩa cloud và cấu hình server nhỏ có burst credit cho phép tạm chạy nhanh hơn mức cơ bản. Khi giờ bận kéo dài làm credit cạn, tốc độ đột ngột tụt xuống. - Vì sao → Dẫn đến → Trên màn hình: Dùng vượt hiệu năng cơ bản trong thời gian dài → Burst credit cạn, tụt thẳng về hiệu năng cơ bản → Tối nào cũng vậy, sau vài giờ thì bắt đầu lag - Triệu chứng: Giật khựng, Quay chậm, Trễ thao tác / Yếu tố: Ngưng trệ, Độ trễ - Ai gặp: Cả server / Khi nào: Giờ cao điểm buổi tối, Càng chạy lâu càng nặng - Phụ trách chính: Hạ tầng server (Đội hạ tầng) / Phối hợp: Hạ tầng DB (Đội hạ tầng) - Việc cần làm (Đội hạ tầng): Thiết bị server·OS: ổ đĩa bảo đảm hiệu năng (gp3, loại chỉ định IOPS), cảnh báo lượng credit còn lại, kiểm tra cả giới hạn burst băng thông ổ đĩa của instance và CPU credit. Thiết bị DB: chuyển cả ổ đĩa DB, kể cả DB managed, sang loại bảo đảm hiệu năng và cảnh báo lượng credit còn lại. - Con số tham khảo: Ổ gp2 100 GB của AWS bình thường đạt 300 IOPS, burst lên 3.000 IOPS, và nếu credit đầy thì trụ được khoảng 30 phút. gp3 không có credit, luôn ở mức 3.000. Các ổ Premium SSD cỡ nhỏ của Azure cũng burst bằng credit tối đa 30 phút. - Trên đồ thị: Chạm giới hạn rồi đi ngang (IOPS, lượng burst credit còn lại) - Chỗ cần xem: Xem BurstBalance của EBS trên CloudWatch (gp2, st1, sc1), EBSIOBalance% và EBSByteBalance% của instance (một số instance có burst), CPUCreditBalance của instance loại burstable. Với Azure, xem chỉ số tỷ lệ dùng burst credit như Data Disk Used Burst IO Credits Percentage - Đúng nếu: từ lúc lượng còn lại tụt gần về 0, IOPS (VolumeReadOps, VolumeWriteOps) đi ngang ở mức hiệu năng cơ bản, VolumeQueueLength và lag cùng tăng. Bắt đầu sau khi giờ cao điểm kéo dài vài giờ - Loại trừ nếu: mọi lượng còn lại đều dư dả mà IOPS vẫn đi ngang → giới hạn cố định của volume hoặc instance (dk-iops) - Cách kiểm tra: Kiểm tra bằng công cụ hạ tầng (không cần code game) - Tìm hiểu thêm: Dù ổ đĩa vẫn ổn, server ảo cỡ nhỏ còn có giới hạn burst ngay trên băng thông ổ đĩa của instance (ví dụ tối thiểu 30 phút mỗi ngày), nên đồ thị cũng có dạng y như vậy. Server giá rẻ dùng CPU credit cũng chậm xuống mức hiệu năng cơ bản khi hết credit. - Nguồn: - [Amazon EBS General Purpose SSD volumes](https://docs.aws.amazon.com/ebs/latest/userguide/general-purpose.html) · AWS · Hiệu năng cơ bản của gp2 là 3 IOPS mỗi GiB (tối thiểu 100), burst tới 3.000 IOPS bằng I/O credit, 5,4 triệu credit đủ cho ít nhất 30 phút. gp3 không có burst, luôn đạt 3.000 IOPS - [Managed disk bursting](https://learn.microsoft.com/en-us/azure/virtual-machines/disk-bursting) · Microsoft Azure · Premium SSD từ P20 trở xuống burst dựa trên credit, credit đầy thì chạy ở tốc độ burst tối đa trong 30 phút - [Amazon EBS-optimized instance types](https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/ebs-optimized.html) · AWS · Một số instance chỉ giữ hiệu năng EBS tối đa trong 30 phút, mỗi 24 giờ một lần, sau đó trở về hiệu năng cơ bản - [Standard mode for burstable performance instances](https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/burstable-performance-instances-standard-mode.html) · AWS · Instance loại burstable ở chế độ standard, khi hết CPU credit, hạ mức sử dụng CPU về mức cơ bản (giảm từ từ, không tụt đột ngột) - [Amazon CloudWatch metrics for Amazon EBS](https://docs.aws.amazon.com/ebs/latest/userguide/using_cloudwatch_ebs.html) · AWS · BurstBalance: lượng I/O credit còn lại của gp2, lượng credit thông lượng còn lại của st1, sc1 (%), VolumeReadOps, VolumeWriteOps, VolumeQueueLength - [CloudWatch metrics that are available for your instances](https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/viewing_metrics_with_cloudwatch.html) · AWS · EBSIOBalance%, EBSByteBalance%: lượng EBS credit còn lại của một số instance burst 30 phút mỗi 24 giờ một lần, CPUCreditBalance: lượng CPU credit còn lại của instance loại burstable - [Disk metrics](https://learn.microsoft.com/en-us/azure/virtual-machines/disks-metrics) · Microsoft Azure · Tỷ lệ dùng burst credit của ổ đĩa và VM như Data Disk Used Burst IO Credits Percentage (mỗi 5 phút) #### dk-iops · Chạm giới hạn IOPS, hàng đợi bão hòa · IOPS limit / queue saturation Khi vượt số yêu cầu ổ đĩa xử lý được trong 1 giây, hàng đợi dài ra và độ trễ tăng vọt. - Vì sao → Dẫn đến → Trên màn hình: Yêu cầu đọc, ghi tiến sát khả năng xử lý của ổ đĩa → Hàng đợi dài ra (thường tăng vọt khi mức sử dụng từ 90% trở lên) → Lưu và loading bị chậm, nếu là lời gọi đồng bộ thì đứng hình - Triệu chứng: Trễ thao tác, Đứng hình / Yếu tố: Độ trễ, Ngưng trệ - Ai gặp: Cả server / Khi nào: Khi đông người, Giờ cao điểm buổi tối - Phụ trách chính: Hạ tầng server (Đội hạ tầng) / Phối hợp: Phát triển server (Đội phát triển game), Hạ tầng DB (Đội hạ tầng) - Việc cần làm (Đội phát triển game): Gộp yêu cầu, cache dữ liệu hay đọc, chuyển sang bất đồng bộ để thread game không phải chờ ổ đĩa. - Việc cần làm (Đội hạ tầng): Thiết bị server·OS: ổ đĩa nhanh hơn, kiểm tra giới hạn băng thông ổ đĩa và IOPS theo loại instance, cảnh báo mức sử dụng và hàng đợi ổ đĩa, sao chép file lớn vào giờ vắng. Thiết bị DB: cảnh báo cả mức sử dụng IOPS và thông lượng của ổ đĩa DB, kiểm tra giới hạn ổ đĩa theo cấu hình instance DB. - Con số tham khảo: HDD khoảng 150, SATA SSD vài chục nghìn, NVMe vài trăm nghìn IOPS. Ổ đĩa mặc định trên cloud (AWS gp3) đạt 3.000 IOPS và 125 MiB mỗi giây. Giới hạn lượng truyền mỗi giây tách riêng khỏi IOPS, và khi việc sao chép file lớn lấp đầy nó thì cả những lần ghi nhỏ cũng bị dồn lại. - Trên đồ thị: Chạm giới hạn rồi đi ngang (IOPS, độ dài hàng đợi ổ đĩa) - Chỗ cần xem: Xem r/s, w/s, rkB/s, wkB/s, aqu-sz, r_await, w_await của iostat -x 1. Trên cloud, xem VolumeReadOps, VolumeWriteOps, VolumeQueueLength của EBS và các chỉ số vượt giới hạn VolumeIOPSExceededCheck, VolumeThroughputExceededCheck, phía instance là InstanceEBSIOPSExceededCheck, InstanceEBSThroughputExceededCheck - Đúng nếu: số yêu cầu mỗi giây hoặc lượng truyền đi ngang ở đúng giá trị giới hạn, aqu-sz và await cùng tăng vọt. Trên cloud, chỉ số kiểm tra vượt giới hạn bằng 1 - Loại trừ nếu: %util 100% mà await vẫn thấp → có thể vẫn còn dư. Với SSD và RAID xử lý song song nhiều yêu cầu, %util không thể hiện giới hạn. Chưa chạm giới hạn mà chỉ await cao → độ trễ của bản thân ổ đĩa (dk-hdd) hoặc fsync (dk-fsync) - Cách kiểm tra: Kiểm tra bằng công cụ hạ tầng (không cần code game) - Tìm hiểu thêm: Trên cloud, ngoài giới hạn của ổ đĩa, mỗi cấu hình server (loại instance) còn có giới hạn băng thông ổ đĩa và IOPS riêng. Dù gắn ổ đĩa đắt tiền, nếu server nhỏ thì vẫn bị chặn ở giới hạn của instance. - Nguồn: - [Exos X18 Data Sheet](https://www.seagate.com/www-content/datasheets/pdfs/exos-x18-mango-DS2045-1N-2007US-en_US.pdf) · Seagate · HDD 7.200 rpm cho server đọc ngẫu nhiên 4K đạt 170 IOPS (QD16) - [D3-S4520 SSD](https://www.solidigm.com/products/data-center/d3/s4520.html) · Solidigm · SATA SSD cho server đọc, ghi ngẫu nhiên 4 KB tối đa 92K/48K IOPS - [Solidigm™ D7-P5520 and D7-P5620 Product Brief](https://www.solidigm.com/products/data-center/product-briefs/d7-p5520-p5620-product-brief.html) · Solidigm · NVMe SSD cho server đọc, ghi ngẫu nhiên 1.000K/200K IOPS - [Amazon EBS General Purpose SSD volumes](https://docs.aws.amazon.com/ebs/latest/userguide/general-purpose.html) · AWS · Hiệu năng cơ bản của gp3 là 3.000 IOPS và 125 MiB/s, hai giới hạn riêng biệt có thể tăng độc lập - [Amazon EBS-optimized instance types](https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/ebs-optimized.html) · AWS · Mỗi loại instance có giới hạn cơ bản và tối đa riêng cho băng thông, thông lượng và IOPS của EBS - [iostat(1) — Linux manual page](https://man7.org/linux/man-pages/man1/iostat.1.html) · sysstat · -x: r/s, w/s, rkB/s, wkB/s, aqu-sz (tên cũ là avgqu-sz), r_await, w_await, %util. Với RAID và SSD đời mới xử lý song song nhiều yêu cầu, %util không thể hiện giới hạn hiệu năng - [Amazon CloudWatch metrics for Amazon EBS](https://docs.aws.amazon.com/ebs/latest/userguide/using_cloudwatch_ebs.html) · AWS · VolumeIOPSExceededCheck, VolumeThroughputExceededCheck: bằng 1 nếu đã cố vượt giới hạn IOPS hoặc thông lượng của volume (instance Nitro), VolumeQueueLength - [CloudWatch metrics that are available for your instances](https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/viewing_metrics_with_cloudwatch.html) · AWS · InstanceEBSIOPSExceededCheck, InstanceEBSThroughputExceededCheck: bằng 1 nếu đã cố vượt giới hạn IOPS hoặc thông lượng EBS của instance #### dk-full · Ổ đĩa đầy · Disk full Khi log và dump tích tụ làm ổ đĩa đầy, thao tác ghi thất bại, và nếu không có phương án dự phòng thì server sập. - Vì sao → Dẫn đến → Trên màn hình: Log, dump và file tạm tích tụ tới 100% → Ghi thất bại. Không có xử lý lỗi thì crash, có thì lưu thất bại → Mất kết nối, tiến độ chơi bị rollback - Triệu chứng: Mất kết nối, Nuốt thao tác·rollback / Yếu tố: Ngưng trệ - Ai gặp: Cả server / Khi nào: Càng chạy lâu càng nặng - Phụ trách chính: Hạ tầng server (Đội hạ tầng) / Phối hợp: Hạ tầng DB (Đội hạ tầng), Phát triển server (Đội phát triển game) - Việc cần làm (Đội phát triển game): Xử lý lỗi ghi để thử lưu lại và phát cảnh báo thay vì crash, giảm log và dump không cần thiết. - Việc cần làm (Đội hạ tầng): Thiết bị server·OS: log rotation, cảnh báo dung lượng, tách ổ đĩa log và ổ đĩa dữ liệu. Thiết bị DB: theo dõi để transaction log của DB (WAL, binlog) không bị tích tụ vì replication dừng hoặc thiếu bản sao lưu log. - Trên đồ thị: Tăng dần (Mức sử dụng dung lượng ổ đĩa) - Chỗ cần xem: Xem mức sử dụng trong df -h và mức sử dụng inode trong df -i, tìm lỗi ENOSPC trong log của server và DB. Với DB, xem slot có active bằng false trong pg_replication_slots của PostgreSQL, số lượng và kích thước file trong SHOW BINARY LOGS của MySQL, log_reuse_wait_desc trong sys.databases của SQL Server, FreeStorageSpace của RDS - Đúng nếu: mức sử dụng tăng đều qua nhiều ngày, thời điểm chạm 100% trùng với lúc crash hoặc lưu thất bại, và log có ENOSPC - Loại trừ nếu: vẫn còn nhiều dung lượng mà ghi thất bại → nguyên nhân khác như quyền truy cập, giới hạn kích thước file - Cách kiểm tra: Kiểm tra bằng công cụ hạ tầng (không cần code game) - Tìm hiểu thêm: Khi bản sao (replica) dừng hoặc bỏ sót sao lưu log, transaction log của DB (WAL, binlog, v.v.) không được xóa và cứ thế tích tụ. Nếu ổ đĩa này đầy, mọi thao tác ghi của DB dừng lại, việc lưu và giao dịch đồng loạt thất bại. - Nguồn: - [write(2) — Linux manual page](https://man7.org/linux/man-pages/man2/write.2.html) · Linux man-pages · Nếu thiết bị hết chỗ, thao tác ghi thất bại với lỗi ENOSPC - [Monitoring Disk Usage (PostgreSQL Documentation)](https://www.postgresql.org/docs/current/diskusage.html) · PostgreSQL · Ổ đĩa chứa WAL đầy thì server DB có thể panic và dừng - [Log-Shipping Standby Servers (PostgreSQL Documentation)](https://www.postgresql.org/docs/current/warm-standby.html) · PostgreSQL · Replication slot không xóa WAL cho tới khi bản sao nhận được, nên có thể lấp đầy dung lượng pg_wal (giới hạn bằng max_slot_wal_keep_size) - [Troubleshoot a full transaction log (SQL Server Error 9002)](https://learn.microsoft.com/en-us/sql/relational-databases/logs/troubleshoot-a-full-transaction-log-sql-server-error-9002) · Microsoft SQL Server · Log đầy thì DB chỉ đọc được, không sửa được; nguyên nhân thường gặp chặn việc dọn log là thiếu bản sao lưu log, replication lag, transaction chạy lâu; xem cái gì đang chặn qua log_reuse_wait_desc trong sys.databases - [df(1) — Linux manual page](https://man7.org/linux/man-pages/man1/df.1.html) · coreutils · Mức sử dụng theo từng file system, -i hiện mức sử dụng inode thay vì block - [pg_replication_slots (PostgreSQL Documentation)](https://www.postgresql.org/docs/current/view-pg-replication-slots.html) · PostgreSQL · active: slot có đang streaming không, wal_status: lượng WAL mà slot giữ lại đã vượt max_wal_size chưa - [SHOW BINARY LOGS Statement](https://dev.mysql.com/doc/refman/8.4/en/show-binary-logs.html) · MySQL · Danh sách file binary log của server và kích thước file (File_size) - [Amazon CloudWatch metrics for Amazon RDS](https://docs.aws.amazon.com/AmazonRDS/latest/UserGuide/rds-metrics.html) · AWS · FreeStorageSpace: dung lượng lưu trữ còn trống của instance DB #### dk-backup · Tác vụ sao lưu, nén, quét · Backup / compression / scans Khi sao lưu lúc rạng sáng, nén log hoặc quét bảo mật chiếm trọn ổ đĩa, việc đọc ghi của server game bị dồn lại. - Vì sao → Dẫn đến → Trên màn hình: Tác vụ sao lưu, nén đã lên lịch bắt đầu chạy → Chiếm phần lớn băng thông và IOPS của ổ đĩa → Ngày nào cũng lag vào cùng một giờ - Triệu chứng: Giật khựng, Trễ thao tác / Yếu tố: Ngưng trệ, Độ trễ - Ai gặp: Cả server / Khi nào: Theo chu kỳ đều - Phụ trách chính: Hạ tầng server (Đội hạ tầng) / Phối hợp: Hạ tầng DB (Đội hạ tầng) - Việc cần làm (Đội hạ tầng): Thiết bị server·OS: hạ mức ưu tiên I/O của tác vụ sao lưu, nén, quét, rải thời điểm chạy. Thiết bị DB: sao lưu trên bản sao (replica). - Trên đồ thị: Vọt lên theo chu kỳ (Mức sử dụng ổ đĩa, thời gian chờ ổ đĩa) - Chỗ cần xem: Dùng sar -d xem bản ghi vài ngày qua (file theo ngày trong /var/log/sa, sadc phải thu cả mục ổ đĩa bằng -S DISK), đặt %util, await, aqu-sz của các ngày chồng lên nhau; vào giờ đó, dùng pidstat -d 1 tìm tiến trình có kB_rd/s, kB_wr/s lớn nhất rồi đối chiếu với lịch cron và systemd timer - Đúng nếu: ngày nào cũng vào cùng một giờ await và %util vọt lên, và lúc đó tiến trình sao lưu, nén hoặc quét chiếm phần lớn lượng đọc ghi ổ đĩa - Loại trừ nếu: giờ vọt lên mỗi ngày một khác → ít khả năng là tác vụ định kỳ. Phần lớn I/O lúc đó do chính server game → phía lưu dữ liệu hoặc ghi log (dk-fsync, dk-sync-log) - Cách kiểm tra: Kiểm tra bằng công cụ hạ tầng (không cần code game) - Nguồn: - [ionice(1) — Linux manual page](https://man7.org/linux/man-pages/man1/ionice.1.html) · util-linux · Tác vụ chạy ở lớp idle chỉ được dùng ổ đĩa khi không chương trình nào khác đang dùng - [Using Replication for Backups](https://dev.mysql.com/doc/refman/8.4/en/replication-solutions-backups.html) · MySQL · Dừng bản sao để sao lưu cũng không ảnh hưởng tới vận hành của DB chính - [sar(1) — Linux manual page](https://man7.org/linux/man-pages/man1/sar.1.html) · sysstat · -d: await, aqu-sz, %util theo từng thiết bị trong file bản ghi theo ngày (mặc định /var/log/sa); mục ổ đĩa phải được thu bằng tùy chọn -S DISK của sadc - [pidstat(1) — Linux manual page](https://man7.org/linux/man-pages/man1/pidstat.1.html) · sysstat · -d: kB_rd/s, kB_wr/s theo từng tiến trình (lượng đọc, ghi ổ đĩa mỗi giây) #### dk-lazy-load · Lazy loading phía server · Lazy loading on the server Nếu server đợi đến lần đầu có yêu cầu mới đọc dữ liệu phó bản hay bản đồ từ ổ đĩa, thì trong tick đó mọi người đều bị dừng. - Vì sao → Dẫn đến → Trên màn hình: Có người vào một phó bản hoặc khu vực lần đầu → Server đọc dữ liệu từ ổ đĩa ngay trên thread game → Mọi người trên server đó đứng hình trong chốc lát - Triệu chứng: Đứng hình / Yếu tố: Ngưng trệ - Ai gặp: Một địa điểm hoặc kênh, Cả server / Khi nào: Khi di chuyển hoặc chuyển bản đồ - Phụ trách chính: Phát triển server (Đội phát triển game) / Phối hợp: Hạ tầng server (Đội hạ tầng) - Việc cần làm (Đội phát triển game): Tải sẵn khi khởi động server, loading bất đồng bộ. - Việc cần làm (Đội hạ tầng): Với server vừa tạo từ snapshot, làm nóng ổ đĩa (đọc toàn bộ block một lượt) trước khi đưa vào phục vụ, hoặc dùng tính năng khôi phục snapshot nhanh. - Trên đồ thị: Thỉnh thoảng vọt lên bất chợt (Thời gian xử lý tick của server, lượng đọc ổ đĩa) - Chỗ cần xem: Đối chiếu thời điểm dừng với bản ghi lần vào phó bản, khu vực đầu tiên trong log server game, và xem lượng đọc ổ đĩa của server game lúc đó (kB_rd/s trong pidstat -d) cùng các lời gọi read, open chạy lâu qua perf trace --duration. Nếu là server cloud mới bật, so sánh VolumeAvgReadLatency của EBS với server đã chạy lâu - Đúng nếu: chỉ dừng đúng lúc vào lần đầu, vào lại chỗ đó lần thứ hai thì không dừng. Trong lúc dừng, thread game đang chờ đọc file - Loại trừ nếu: ở khu vực đã tải xong mà vẫn dừng y như vậy → nguyên nhân khác như vượt tick budget, GC - Cách kiểm tra: Cần log và chỉ số của server, client game - Tìm hiểu thêm: Trên cloud, server vừa được tạo từ snapshot (bản sao ổ đĩa) phải tải từng block đọc lần đầu từ storage từ xa, nên chậm hơn bình thường nhiều. Nếu chỉ trên những server mới được autoscaling bật lên mà lần vào đầu tiên lâu bất thường, hãy nghi ngờ nguyên nhân này. - Nguồn: - [Initialize Amazon EBS volumes](https://docs.aws.amazon.com/ebs/latest/userguide/ebs-initialize.html) · AWS · Volume tạo từ snapshot có độ trễ tăng và hiệu năng giảm trong lúc tải block từ S3; khởi tạo trước bằng cách dùng dd hoặc fio đọc toàn bộ block - [Amazon EBS fast snapshot restore](https://docs.aws.amazon.com/ebs/latest/userguide/ebs-fast-snapshot-restore.html) · AWS · Fast snapshot restore cấp volume đã khởi tạo sẵn ngay từ lúc tạo, loại bỏ độ trễ ở lần truy cập đầu - [pidstat(1) — Linux manual page](https://man7.org/linux/man-pages/man1/pidstat.1.html) · sysstat · -d: kB_rd/s theo từng tiến trình (lượng đọc ổ đĩa mỗi giây) - [perf-trace(1) — Linux manual page](https://man7.org/linux/man-pages/man1/perf-trace.1.html) · perf · --duration chỉ hiện các system call mất lâu hơn số ms chỉ định - [Amazon CloudWatch metrics for Amazon EBS](https://docs.aws.amazon.com/ebs/latest/userguide/using_cloudwatch_ebs.html) · AWS · VolumeAvgReadLatency: độ trễ đọc trung bình 1 phút (instance Nitro) #### dk-coredump · Ghi core dump · Core dump writing Khi server sập, việc ghi vài GB bộ nhớ xuống ổ đĩa có thể làm việc khởi động lại chậm mất vài phút. - Vì sao → Dẫn đến → Trên màn hình: Server crash, ghi toàn bộ bộ nhớ ra file → Không thể khởi động lại trong lúc ghi vài GB → Server sập làm mất kết nối, sau đó rất lâu vẫn không vào được - Triệu chứng: Không vào được·kẹt loading / Yếu tố: Ngưng trệ - Ai gặp: Cả server / Khi nào: Thỉnh thoảng bất chợt - Phụ trách chính: Hạ tầng server (Đội hạ tầng) / Phối hợp: Phát triển server (Đội phát triển game) - Việc cần làm (Đội phát triển game): Cân nhắc dùng dump nhỏ chỉ chứa phần bộ nhớ cần thiết (minidump), sửa nguyên nhân crash. - Việc cần làm (Đội hạ tầng): Giới hạn kích thước dump (cấu hình core dump của OS), ổ đĩa nhanh, tách việc khởi động lại khỏi việc dump (nén và tải dump lên được xử lý riêng sau khi đã khởi động lại). - Trên đồ thị: Rớt kết nối hàng loạt (Số kết nối, thời điểm khởi động lại server) - Chỗ cần xem: Đặt cạnh nhau thời điểm crash, kích thước file core (coredumpctl list, info, hoặc file ở chỗ core_pattern trỏ tới), thời điểm ghi file xong, thời điểm dịch vụ chạy lại, và xem wkB/s của iostat -x trong khoảng đó - Đúng nếu: sau crash, trong lúc ghi file core vài GB, lượng ghi ổ đĩa bám sát giới hạn, và chỉ khi ghi xong thì việc khởi động lại mới bắt đầu - Loại trừ nếu: core dump đã tắt hoặc ghi xong nhanh vì nhỏ mà khởi động lại vẫn chậm → quá trình khởi động server như loading bản đồ hoặc cache lạnh của DB (db-cold-cache) - Cách kiểm tra: Kiểm tra bằng công cụ hạ tầng (không cần code game) - Nguồn: - [core(5) — Linux manual page](https://man7.org/linux/man-pages/man5/core.5.html) · Linux man-pages · Giới hạn kích thước file core bằng RLIMIT_CORE, chọn vùng bộ nhớ cần ghi bằng coredump_filter, pipe core dump sang một chương trình để xử lý riêng - [Minidump Files](https://learn.microsoft.com/en-us/windows/win32/debug/minidump-files) · Microsoft · Minidump chỉ chứa phần hữu ích của thông tin crash dump nên tạo nhanh và nhỏ - [coredumpctl(1) — Linux manual page](https://man7.org/linux/man-pages/man1/coredumpctl.1.html) · systemd · list: danh sách core dump còn trong journal (TIME là thời điểm crash do kernel báo), info: chi tiết từng dump và kích thước đã ghi xuống ổ đĩa - [iostat(1) — Linux manual page](https://man7.org/linux/man-pages/man1/iostat.1.html) · sysstat · -x: wkB/s (lượng ghi ổ đĩa mỗi giây) #### dk-hdd · Độ trễ seek của HDD · HDD seek latency HDD phải di chuyển đầu đọc trên đĩa từ (seek), nên mỗi lần đọc ghi dữ liệu nằm rải rác mất gần 10 ms. - Vì sao → Dẫn đến → Trên màn hình: Server cũ hoặc storage giá rẻ dùng HDD → Mỗi lần đọc ghi rải rác mất khoảng 10 ms → Lưu và loading chậm nói chung - Triệu chứng: Trễ thao tác / Yếu tố: Độ trễ - Ai gặp: Cả server / Khi nào: Luôn luôn - Phụ trách chính: Hạ tầng server (Đội hạ tầng) / Phối hợp: Hạ tầng DB (Đội hạ tầng), Phát triển server (Đội phát triển game) - Việc cần làm (Đội phát triển game): Thiết kế ưu tiên ghi tuần tự. - Việc cần làm (Đội hạ tầng): Thiết bị server·OS: thay bằng SSD (bắt đầu từ ổ lưu trữ có nhiều đọc ghi rải rác). Thiết bị DB: thay trước bằng SSD cho ổ đĩa DB có nhiều đọc ghi rải rác nhất. - Trên đồ thị: Luôn cao ngay từ đầu (Độ trễ đọc, ghi ổ đĩa (r_await, w_await)) - Chỗ cần xem: Dùng lsblk -d -o NAME,ROTA kiểm tra có phải ổ đĩa quay (HDD) không, xem r/s, w/s và r_await, w_await của iostat -x 1. Với server ảo, kiểm tra loại ổ đĩa trong thông số của cloud hoặc storage - Đúng nếu: là ổ đĩa quay, và dù chỉ vài chục đến hơn trăm yêu cầu mỗi giây, r_await và w_await lúc nào cũng từ vài ms đến vài chục ms - Loại trừ nếu: là SSD mà độ trễ vẫn cao → hàng đợi bão hòa (dk-iops) hoặc cạn burst credit (dk-burst) - Cách kiểm tra: Kiểm tra bằng công cụ hạ tầng (không cần code game) - Nguồn: - [Designs, Lessons and Advice from Building Large Distributed Systems (LADIS 2009 keynote)](https://www.cs.cornell.edu/projects/ladis2009/talks/dean-keynote-ladis2009.pdf) · Google · Một lần seek ổ đĩa 10 ms, đọc tuần tự 1 MB từ ổ đĩa 20 ms - [Exos X18 Data Sheet](https://www.seagate.com/www-content/datasheets/pdfs/exos-x18-mango-DS2045-1N-2007US-en_US.pdf) · Seagate · HDD 7.200 rpm có độ trễ quay trung bình 4,16 ms, đọc ngẫu nhiên 4K đạt 170 IOPS - [lsblk(8) — Linux manual page](https://man7.org/linux/man-pages/man8/lsblk.8.html) · util-linux · -o chọn cột xuất ra, cột topology thiết bị có ROTA (có phải loại quay không) - [ABI stable symbols](https://docs.kernel.org/admin-guide/abi-stable.html) · Linux kernel · /sys/block/(ổ đĩa)/queue/rotational: cho biết thiết bị thuộc loại quay hay không quay - [iostat(1) — Linux manual page](https://man7.org/linux/man-pages/man1/iostat.1.html) · sysstat · -x: r/s, w/s, r_await, w_await (thời gian xử lý trung bình mỗi yêu cầu, tính cả thời gian chờ trong hàng đợi) ### L12 Cơ sở dữ liệu (16 nguyên nhân) #### db-no-index · Query không có index · Missing index / full table scan Không có index thì muốn tìm các dòng thỏa điều kiện, DB phải đọc toàn bộ bảng (full scan). - Vì sao → Dẫn đến → Trên màn hình: Tính năng mới được triển khai, kèm truy vấn theo điều kiện không có index → Quét toàn bộ hàng triệu dòng, một query mất từ vài trăm ms đến vài giây → Hộp thư và lịch sử giao dịch tải chậm, kết nối DB bị giữ khiến các yêu cầu khác cũng phải chờ - Triệu chứng: Trễ thao tác, Không vào được·kẹt loading / Yếu tố: Độ trễ, Ngưng trệ - Ai gặp: Chỉ một tính năng, Cả server / Khi nào: Khi làm một thao tác nhất định - Phụ trách chính: Phát triển server (Đội phát triển game) / Phối hợp: Hạ tầng DB (Đội hạ tầng) - Việc cần làm (Đội phát triển game): Xem xét kế hoạch thực thi của query mới trước khi triển khai, thêm index, kiểm tra cả query sửa dữ liệu (UPDATE, DELETE) có dùng index không. - Việc cần làm (Đội hạ tầng): Theo dõi slow query log, tìm query full scan và chia sẻ cho đội phát triển game, khi đang vận hành thì thêm index theo cách online để thời gian khóa ngắn. - Con số tham khảo: Có index thì mất vài ms; không có thì chậm dần theo kích thước dữ liệu, với bảng lớn có thể chậm gấp vài trăm đến vài chục nghìn lần. - Trên đồ thị: Tăng như bậc thang từ một thời điểm (Độ trễ query DB, số dòng đã đọc) - Chỗ cần xem: MySQL: xem Rows_examined, Rows_sent trong slow query log (bật log_queries_not_using_indexes thì cả query không dùng index cũng được ghi), SUM_NO_INDEX_USED, SUM_ROWS_EXAMINED trong performance_schema events_statements_summary_by_digest, rồi chạy EXPLAIN. PostgreSQL: xem seq_scan, seq_tup_read trong pg_stat_user_tables, rồi chạy EXPLAIN - Đúng nếu: query mới xuất hiện sau khi triển khai đọc số dòng (Rows_examined) nhiều gấp hàng nghìn lần số dòng trả về (Rows_sent), EXPLAIN cho thấy quét toàn bộ bảng (MySQL type ALL, PostgreSQL Seq Scan). seq_tup_read của bảng lớn tăng dốc kể từ lúc triển khai - Loại trừ nếu: có dùng index mà vẫn chậm → chờ khóa (db-hot-row, db-ddl-lock) hoặc kế hoạch thực thi thay đổi (db-plan-flip). Quét toàn bộ bảng nhỏ có thể là bình thường - Cách kiểm tra: Kiểm tra bằng công cụ hạ tầng (không cần code game) - Tìm hiểu thêm: Không chỉ việc đọc bị chậm. Tùy DB, query sửa dữ liệu (UPDATE, DELETE) chạy không có index có thể khóa cả những dòng đã quét qua, chặn luôn việc lưu dữ liệu của những người chơi không liên quan. - Nguồn: - [How MySQL Uses Indexes](https://dev.mysql.com/doc/refman/8.4/en/mysql-indexes.html) · MySQL · Không có index thì phải đọc toàn bộ bảng từ dòng đầu tiên, bảng càng lớn chi phí càng cao - [Locks Set by Different SQL Statements in InnoDB](https://dev.mysql.com/doc/refman/8.4/en/innodb-locks-set.html) · MySQL · Không có index phù hợp nên phải quét toàn bộ bảng thì mọi dòng đều bị khóa, chặn cả việc thêm dòng của người dùng khác - [The Slow Query Log](https://dev.mysql.com/doc/refman/8.4/en/slow-query-log.html) · MySQL · Ghi các query vượt long_query_time (mặc định 10 giây), có thể ghi riêng cả các query không dùng index - [CREATE INDEX (PostgreSQL Documentation)](https://www.postgresql.org/docs/current/sql-createindex.html) · PostgreSQL · Tạo bằng CONCURRENTLY thì tạo được index mà không chặn thao tác ghi, cách tạo thông thường chặn ghi cho đến khi xong - [Statement Summary Tables](https://dev.mysql.com/doc/refman/8.4/en/performance-schema-statement-summary-tables.html) · MySQL · events_statements_summary_by_digest: SUM_NO_INDEX_USED (số lần chạy không dùng index) và SUM_ROWS_EXAMINED theo từng dạng query - [EXPLAIN Output Format](https://dev.mysql.com/doc/refman/8.4/en/explain-output.html) · MySQL · type là ALL nghĩa là quét toàn bộ bảng, thường tránh bằng cách thêm index - [The Cumulative Statistics System (PostgreSQL Documentation)](https://www.postgresql.org/docs/current/monitoring-stats.html) · PostgreSQL · seq_scan (số lần quét tuần tự) và seq_tup_read (số dòng đọc bằng quét tuần tự) trong pg_stat_user_tables - [Using EXPLAIN (PostgreSQL Documentation)](https://www.postgresql.org/docs/current/using-explain.html) · PostgreSQL · Seq Scan: kế hoạch thực thi đọc lần lượt mọi dòng của bảng #### db-hot-row · Tranh chấp khóa trên hot row · Hot row lock contention Khi ai cũng muốn sửa cùng một dòng (kho bang hội, vật phẩm hot ở nhà đấu giá, bộ đếm toàn server), mỗi lúc chỉ một người lấy được khóa. - Vì sao → Dẫn đến → Trên màn hình: Sự kiện hoặc vật phẩm hot làm các lần sửa dồn vào cùng một dòng → Các yêu cầu phải chờ đến khi lấy được khóa → Giao dịch thất bại, “Vui lòng thử lại sau”, timeout - Triệu chứng: Nuốt thao tác·rollback, Trễ thao tác / Yếu tố: Ngưng trệ, Độ trễ - Ai gặp: Chỉ một tính năng / Khi nào: Khi đông người - Phụ trách chính: Phát triển server (Đội phát triển game) / Phối hợp: Hạ tầng DB (Đội hạ tầng) - Việc cần làm (Đội phát triển game): Chia nhỏ dòng (sharded counter), giữ transaction ngắn, gom trong bộ nhớ rồi ghi một lượt. - Việc cần làm (Đội hạ tầng): Giám sát thời gian và số lần chờ khóa dòng để tìm ra dòng bị tranh chấp nhiều, rồi chia sẻ. - Con số tham khảo: Nếu mỗi yêu cầu giữ khóa 10 ms thì dòng đó chỉ sửa được tối đa 100 lần trong 1 giây. Nếu trong transaction còn xen một lượt khứ hồi tới server khác thì con số này càng giảm. - Trên đồ thị: Tăng theo số người và tải (Số lần và thời gian chờ khóa dòng) - Chỗ cần xem: MySQL: xem mức tăng của Innodb_row_lock_waits, Innodb_row_lock_time và giá trị Innodb_row_lock_current_waits, dùng sys.innodb_lock_waits để tìm ai đang chờ ai. PostgreSQL: xem các session có wait_event_type là Lock trong pg_stat_activity, các yêu cầu có granted là false trong pg_locks; bật log_lock_waits (mặc định tắt) thì các lần chờ khóa lâu được ghi vào log - Đúng nếu: số lần chờ khóa tăng dốc theo sự kiện hoặc số người, và phần lớn yêu cầu đang chờ cùng trỏ tới một dòng (cùng key) của cùng một bảng - Loại trừ nếu: chờ khóa rải đều trên nhiều bảng, nhiều dòng → ổ đĩa hoặc CPU bão hòa. Một session giữ khóa lâu không nhả → transaction mở quá lâu (db-long-tx) - Cách kiểm tra: Kiểm tra bằng công cụ hạ tầng (không cần code game) - Nguồn: - [InnoDB Locking](https://dev.mysql.com/doc/refman/8.4/en/innodb-locking.html) · MySQL · Khi một transaction khóa một dòng (index record), transaction khác không sửa được dòng đó và phải chờ - [How to Minimize and Handle Deadlocks](https://dev.mysql.com/doc/refman/8.4/en/innodb-deadlocks-handling.html) · MySQL · Khuyến nghị giữ transaction nhỏ và ngắn, commit ngay sau các thay đổi liên quan để giảm xung đột - [Server Status Variables](https://dev.mysql.com/doc/refman/8.4/en/server-status-variables.html) · MySQL · Innodb_row_lock_waits, Innodb_row_lock_time cho biết số lần và thời gian chờ khóa dòng, Innodb_row_lock_current_waits cho biết số yêu cầu đang chờ - [The innodb_lock_waits and x$innodb_lock_waits Views](https://dev.mysql.com/doc/refman/8.4/en/sys-innodb-lock-waits.html) · MySQL · Query đang chờ (waiting_query), session đang chặn (blocking_pid), thời gian chờ (wait_age) - [The Cumulative Statistics System (PostgreSQL Documentation)](https://www.postgresql.org/docs/current/monitoring-stats.html) · PostgreSQL · wait_event_type trong pg_stat_activity: là Lock thì đang chờ một khóa nặng (heavyweight lock) - [pg_locks (PostgreSQL Documentation)](https://www.postgresql.org/docs/current/view-pg-locks.html) · PostgreSQL · granted là false nghĩa là tiến trình đó đang chờ để lấy khóa - [Error Reporting and Logging (PostgreSQL Documentation)](https://www.postgresql.org/docs/current/runtime-config-logging.html) · PostgreSQL · log_lock_waits: ghi log khi chờ khóa lâu hơn deadlock_timeout, mặc định tắt #### db-deadlock · Deadlock trong DB · Database deadlock Khi hai transaction (nhóm thao tác DB được xử lý như một khối) chờ dòng mà bên kia đã khóa, DB buộc phải hủy một bên. - Vì sao → Dẫn đến → Trên màn hình: Giao dịch A khóa theo thứ tự vật phẩm→tiền tệ, B khóa theo thứ tự tiền tệ→vật phẩm → DB phát hiện deadlock và rollback một bên → Giao dịch và chế tạo thỉnh thoảng thất bại, vật phẩm bị trả lại - Triệu chứng: Nuốt thao tác·rollback, Trễ thao tác / Yếu tố: Mất gói, Ngưng trệ - Ai gặp: Chỉ một tính năng / Khi nào: Khi đông người, Khi làm một thao tác nhất định - Phụ trách chính: Phát triển server (Đội phát triển game) / Phối hợp: Hạ tầng DB (Đội hạ tầng) - Việc cần làm (Đội phát triển game): Thống nhất thứ tự khóa, giữ transaction ngắn, tự động thử lại khi thất bại. - Việc cần làm (Đội hạ tầng): Bật phát hiện deadlock, thu thập bản ghi deadlock và chia sẻ; với MySQL server đã tắt phát hiện, giảm giới hạn thời gian chờ khóa (mặc định 50 giây). - Con số tham khảo: Thời gian đến khi phát hiện: MySQL (InnoDB) gần như ngay lập tức, PostgreSQL mặc định 1 giây, SQL Server tối đa khoảng 5 giây. Trong lúc đó cả hai yêu cầu đều bị dừng. Nếu server tắt tính năng phát hiện của MySQL vì yêu cầu đồng thời quá nhiều, thì phải chờ tới giới hạn thời gian chờ khóa (mặc định 50 giây). - Trên đồ thị: Thỉnh thoảng vọt lên bất chợt (Số deadlock, số giao dịch thất bại) - Chỗ cần xem: MySQL: xem LATEST DETECTED DEADLOCK trong SHOW ENGINE INNODB STATUS (chỉ 1 lần gần nhất), mọi deadlock được ghi vào error log khi bật innodb_print_all_deadlocks, lock_deadlocks trong INFORMATION_SCHEMA.INNODB_METRICS. PostgreSQL: xem deadlocks trong pg_stat_database. SQL Server: xem xml_deadlock_report của session system_health (bật sẵn mặc định). Mã lỗi phía server game: MySQL 1213, PostgreSQL 40P01, SQL Server 1205 - Đúng nếu: vào lúc giao dịch hoặc chế tạo thất bại, số deadlock tăng, và hai transaction được ghi lại đang khóa cùng các bảng theo thứ tự ngược nhau - Loại trừ nếu: số deadlock không đổi mà vẫn thất bại → vượt giới hạn chờ khóa (lỗi MySQL 1205) hoặc hot row (db-hot-row) - Cách kiểm tra: Kiểm tra bằng công cụ hạ tầng (không cần code game) - Nguồn: - [InnoDB Startup Options and System Variables](https://dev.mysql.com/doc/refman/8.4/en/innodb-parameters.html) · MySQL · Khi bật phát hiện (mặc định), InnoDB phát hiện deadlock ngay và rollback; innodb_lock_wait_timeout mặc định 50 giây - [Deadlock Detection](https://dev.mysql.com/doc/refman/8.4/en/innodb-deadlock-detection.html) · MySQL · Khi mức đồng thời rất cao, bản thân việc phát hiện có thể chậm đi, nên đôi khi người ta tắt nó và dựa vào giới hạn chờ khóa - [Lock Management (PostgreSQL Documentation)](https://www.postgresql.org/docs/current/runtime-config-locks.html) · PostgreSQL · deadlock_timeout mặc định 1 giây: chỉ sau khi chờ khóa chừng ấy mới kiểm tra deadlock - [Deadlocks guide](https://learn.microsoft.com/en-us/sql/relational-databases/sql-server-deadlocks-guide) · Microsoft SQL Server · Chu kỳ kiểm tra deadlock mặc định 5 giây, giảm xuống tới 100 ms nếu deadlock xảy ra thường xuyên; session system_health bật sẵn mặc định thu thập xml_deadlock_report; bên bị chọn hủy nhận lỗi 1205 - [How to Minimize and Handle Deadlocks](https://dev.mysql.com/doc/refman/8.4/en/innodb-deadlocks-handling.html) · MySQL · Luôn sửa nhiều dòng, nhiều bảng theo cùng một thứ tự, thất bại thì thử lại, dùng innodb_print_all_deadlocks để ghi mọi deadlock - [InnoDB Standard Monitor and Lock Monitor Output](https://dev.mysql.com/doc/refman/8.4/en/innodb-standard-monitor.html) · MySQL · LATEST DETECTED DEADLOCK: hai transaction của deadlock gần nhất, khóa đã giữ và khóa đang chờ, bên bị rollback - [InnoDB INFORMATION_SCHEMA Metrics Table](https://dev.mysql.com/doc/refman/8.4/en/innodb-information-schema-metrics-table.html) · MySQL · Counter lock_deadlocks trong INNODB_METRICS (bật mặc định) - [Server Error Message Reference](https://dev.mysql.com/doc/mysql-errors/8.4/en/server-error-reference.html) · MySQL · 1213 ER_LOCK_DEADLOCK (deadlock), 1205 ER_LOCK_WAIT_TIMEOUT (vượt giới hạn chờ khóa) - [The Cumulative Statistics System (PostgreSQL Documentation)](https://www.postgresql.org/docs/current/monitoring-stats.html) · PostgreSQL · deadlocks trong pg_stat_database: số deadlock đã phát hiện trong DB này - [PostgreSQL Error Codes (PostgreSQL Documentation)](https://www.postgresql.org/docs/current/errcodes-appendix.html) · PostgreSQL · 40P01 deadlock_detected #### db-pool · Cạn connection pool · Connection pool exhaustion Số kết nối tạo sẵn tới DB là cố định, nên khi query chậm chiếm giữ kết nối, các yêu cầu còn lại phải chờ. - Vì sao → Dẫn đến → Trên màn hình: Query chậm hoặc yêu cầu dồn dập làm mọi kết nối đều đang bận → Yêu cầu mới phải chờ đến khi có kết nối rảnh → Đăng nhập kẹt loading, lưu chậm, timeout - Triệu chứng: Không vào được·kẹt loading, Trễ thao tác / Yếu tố: Ngưng trệ, Độ trễ - Ai gặp: Cả server, Chỉ một tính năng / Khi nào: Ngay sau đăng nhập hoặc bảo trì, Khi đông người - Phụ trách chính: Phát triển server (Đội phát triển game) / Phối hợp: Hạ tầng DB (Đội hạ tầng) - Việc cần làm (Đội phát triển game): Loại bỏ query chậm, điều chỉnh kích thước pool và timeout chờ (đừng tăng pool một cách mù quáng), tách pool theo tính năng. - Việc cần làm (Đội hạ tầng): Kiểm tra số kết nối tối đa của DB và dư địa CPU, IOPS; trước khi mở rộng hoặc autoscaling, kiểm tra số server × kích thước pool có nằm trong số kết nối tối đa không; thêm chỉ số chờ kết nối và chờ lock vào giám sát. - Con số tham khảo: Số kết nối cần có được ước lượng bằng “số yêu cầu mỗi giây × thời gian một yêu cầu chiếm giữ kết nối”. 2.000 yêu cầu trong 1 giây, mỗi yêu cầu 5 ms thì trung bình luôn có 10 kết nối bận. Để phòng lúc dồn dập, thường đặt gấp hai, ba lần số đó. Nếu query chậm đi thành 150 ms thì cùng lượng yêu cầu ấy cần tới 300 kết nối. - Trên đồ thị: Chạm giới hạn rồi đi ngang (Số kết nối DB đang dùng, thời gian chờ kết nối) - Chỗ cần xem: Đếm trạng thái kết nối theo từng server game từ phía DB. MySQL: xem Host, Command (kết nối rảnh là Sleep), Time trong SHOW PROCESSLIST cùng Threads_connected, Threads_running và số kết nối bị từ chối Connection_errors_max_connections. PostgreSQL: nhóm pg_stat_activity theo client_addr, state rồi đếm. Nếu thư viện connection pool của server game xuất ra số lượng và thời gian chờ thì xem cùng - Đúng nếu: toàn bộ kết nối của một server game (bằng kích thước pool) đều đang chạy query, số kết nối rảnh bằng 0, và trong lúc đó đăng nhập, lưu phải chờ. Hoặc tổng số kết nối của DB chạm max_connections và kết nối mới bị từ chối - Loại trừ nếu: còn nhiều kết nối rảnh mà vẫn chậm → độ trễ của bản thân query (db-no-index, db-hot-row) hoặc tài nguyên DB bão hòa - Cách kiểm tra: Kiểm tra bằng công cụ hạ tầng (không cần code game) - Tìm hiểu thêm: Tăng pool một cách mù quáng chỉ làm CPU của DB và tranh chấp khóa tăng lên, khiến mọi thứ cùng chậm. Ngoài ra, nếu số server × kích thước pool vượt quá số kết nối tối đa của DB, server mới mở rộng hoặc vừa khởi động lại sẽ không tạo nổi kết nối. Chuyện này hay gặp ngay sau autoscaling hoặc bảo trì. - Nguồn: - [Number Of Database Connections](https://wiki.postgresql.org/wiki/Number_Of_Database_Connections) · PostgreSQL · Khi tài nguyên DB đã dùng hết, tăng kết nối thì thông lượng ngược lại còn giảm; giữ số kết nối hoạt động vừa với tài nguyên, phần còn lại cho vào hàng đợi thì cả độ trễ lẫn thông lượng đều tốt hơn - [Too many connections](https://dev.mysql.com/doc/refman/8.4/en/too-many-connections.html) · MySQL · Khi dùng hết max_connections, kết nối mới bị từ chối với lỗi Too many connections - [Connections and Authentication (PostgreSQL Documentation)](https://www.postgresql.org/docs/current/runtime-config-connection.html) · PostgreSQL · max_connections: giới hạn số kết nối đồng thời, mặc định thường là 100 - [SHOW PROCESSLIST Statement](https://dev.mysql.com/doc/refman/8.4/en/show-processlist.html) · MySQL · Host (địa chỉ client), Command (session rảnh là Sleep), Time, State - [Server Status Variables](https://dev.mysql.com/doc/refman/8.4/en/server-status-variables.html) · MySQL · Threads_connected, Threads_running, Connection_errors_max_connections (số kết nối bị từ chối vì chạm max_connections) - [The Cumulative Statistics System (PostgreSQL Documentation)](https://www.postgresql.org/docs/current/monitoring-stats.html) · PostgreSQL · pg_stat_activity: client_addr và state (active, idle, idle in transaction, v.v.) của từng kết nối #### db-replica-lag · Replication lag · Replication lag Khi ghi vào DB chính và đọc từ bản sao, nếu bản sao bị tụt lại phía sau thì nội dung vừa ghi sẽ chưa hiện ra. - Vì sao → Dẫn đến → Trên màn hình: Thao tác ghi dồn vào DB chính khiến bản sao tụt lại vài giây → Đọc nội dung vừa lưu từ bản sao thì chưa có → Vật phẩm vừa mua không thấy đâu, giá trên sàn giao dịch là giá cũ, lỗi phát thưởng trùng - Triệu chứng: Nuốt thao tác·rollback / Yếu tố: Độ trễ - Ai gặp: Chỉ một tính năng / Khi nào: Khi đông người, Giờ cao điểm buổi tối - Phụ trách chính: Hạ tầng DB (Đội hạ tầng) / Phối hợp: Phát triển server (Đội phát triển game) - Việc cần làm (Đội phát triển game): Dữ liệu vừa ghi thì đọc từ DB chính; kiểm tra đã phát thưởng chưa và phát thưởng trong cùng một transaction trên DB chính (chặn trùng bằng unique key hoặc UPDATE có điều kiện). - Việc cần làm (Đội hạ tầng): Cảnh báo replication lag, đặt cấu hình bản sao ngang hoặc cao hơn DB chính và áp dụng replication song song, chia nhỏ các lệnh xóa hàng loạt, quản lý các query tổng hợp chạy lâu trên bản sao. - Trên đồ thị: Tăng theo số người và tải (Replication lag (giây)) - Chỗ cần xem: MySQL: trên bản sao, xem Seconds_Behind_Source trong SHOW REPLICA STATUS (phiên bản trước 8.0.22 dùng SHOW SLAVE STATUS). PostgreSQL: xem write_lag, flush_lag, replay_lag trong pg_stat_replication trên server chính; RDS: xem ReplicaLag - Đúng nếu: vào lúc có báo cáo “không thấy”, độ trễ từ vài giây trở lên, và xem lại sau khi hết trễ thì bình thường. Độ trễ tăng vào lúc ghi dồn dập, xóa hàng loạt hoặc có query tổng hợp dài chạy trên bản sao - Loại trừ nếu: độ trễ gần 0 mà vẫn không thấy → cache hoặc cơ chế đồng bộ của server game - Cách kiểm tra: Kiểm tra bằng công cụ hạ tầng (không cần code game) - Tìm hiểu thêm: Dù ghi không nhiều, bản sao vẫn có thể tụt lại. Một lệnh xóa hàng loạt mất 10 phút trên DB chính cũng khiến bản sao tụt lại chừng ấy thời gian trong lúc chạy lại lệnh đó, và query tổng hợp chạy lâu trên bản sao cũng làm việc bắt kịp chậm đi. - Nguồn: - [SHOW REPLICA STATUS Statement](https://dev.mysql.com/doc/refman/8.4/en/show-replica-status.html) · MySQL · Seconds_Behind_Source: chênh lệch so với thời điểm sự kiện mà bản sao đang áp dụng được ghi trên DB chính (replication lag) - [Replica Server Options and Variables](https://dev.mysql.com/doc/refman/8.4/en/replication-options-replica.html) · MySQL · replica_parallel_workers cho nhiều thread áp dụng transaction song song (mặc định 4, bằng 0 thì một thread áp dụng lần lượt) - [Log-Shipping Standby Servers (PostgreSQL Documentation)](https://www.postgresql.org/docs/current/warm-standby.html) · PostgreSQL · Streaming replication mặc định là bất đồng bộ nên có độ trễ giữa lúc commit và lúc bản sao áp dụng (nếu bản sao theo kịp thì thường dưới 1 giây) - [MySQL 8.0 Reference Manual: SHOW REPLICA STATUS Statement](https://dev.mysql.com/doc/refman/8.0/en/show-replica-status.html) · MySQL · Từ 8.0.22 dùng SHOW REPLICA STATUS thay cho SHOW SLAVE STATUS, các phiên bản trước đó dùng SHOW SLAVE STATUS - [The Cumulative Statistics System (PostgreSQL Documentation)](https://www.postgresql.org/docs/current/monitoring-stats.html) · PostgreSQL · write_lag, flush_lag, replay_lag trong pg_stat_replication: thời gian từ khi server chính ghi WAL đến khi bản sao báo đã ghi, đã đẩy xuống ổ đĩa, đã áp dụng - [Amazon CloudWatch metrics for Amazon RDS](https://docs.aws.amazon.com/AmazonRDS/latest/UserGuide/rds-metrics.html) · AWS · ReplicaLag: thời gian (giây) read replica bị tụt lại so với bản gốc #### db-checkpoint · Checkpoint, flush log · Checkpoint / log flush stalls Đúng lúc DB định kỳ ghi dồn các thay đổi trong bộ nhớ xuống ổ đĩa, query chậm đi. - Vì sao → Dẫn đến → Trên màn hình: Thay đổi tích tụ và định kỳ được ghi xuống ổ đĩa → Lúc đó ổ đĩa bận, query bị trễ → Lưu và loading chậm đi theo chu kỳ - Triệu chứng: Trễ thao tác, Giật khựng / Yếu tố: Độ trễ - Ai gặp: Cả server, Chỉ một tính năng / Khi nào: Theo chu kỳ đều - Phụ trách chính: Hạ tầng DB (Đội hạ tầng) - Việc cần làm (Đội hạ tầng): Chia nhỏ checkpoint để ghi đều, cấp transaction log (redo log, WAL) rộng rãi, dùng ổ đĩa nhanh. - Trên đồ thị: Vọt lên theo chu kỳ (Độ trễ query DB, lượng ghi ổ đĩa) - Chỗ cần xem: PostgreSQL: xem thời điểm checkpoint và số buffer đã ghi trong log của log_checkpoints (bật mặc định ở các phiên bản gần đây), số lần checkpoint (từ 17 là num_timed, num_requested trong pg_stat_checkpointer; 16 trở xuống là checkpoints_timed, checkpoints_req trong pg_stat_bgwriter), cảnh báo checkpoint_warning. MySQL: xem chênh lệch giữa Log sequence number và Last checkpoint at trong phần LOG của SHOW ENGINE INNODB STATUS. Đặt chồng cả lượng ghi và độ trễ ghi ổ đĩa của server - Đúng nếu: thời điểm độ trễ query vọt lên trùng với thời điểm checkpoint, và lúc đó lượng ghi cùng độ trễ ghi ổ đĩa vọt lên. Ở PostgreSQL, nếu checkpoint theo yêu cầu (num_requested) nhiều hơn hẳn checkpoint theo thời gian (num_timed) thì coi là WAL hay chạm max_wal_size nên checkpoint bị đẩy lên sớm - Loại trừ nếu: vọt lên theo chu kỳ không khớp thời điểm checkpoint → sao lưu hoặc batch (dk-backup, db-batch) - Cách kiểm tra: Kiểm tra bằng công cụ hạ tầng (không cần code game) - Tìm hiểu thêm: Nếu transaction log chứa bản ghi thay đổi (redo log của MySQL, WAL của PostgreSQL) quá nhỏ, mỗi lần log đầy DB phải vội vàng chạy checkpoint dồn một lượt, khiến thông lượng ghi thỉnh thoảng giảm mạnh. - Nguồn: - [WAL Configuration (PostgreSQL Documentation)](https://www.postgresql.org/docs/current/wal-configuration.html) · PostgreSQL · Checkpoint chạy mặc định mỗi 5 phút hoặc mỗi 1 GB WAL (max_wal_size), ghi toàn bộ dirty page nên tốn kém. checkpoint_completion_target rải việc ghi để tránh I/O dồn dập. Nếu khoảng cách giữa các checkpoint ngắn hơn checkpoint_warning thì ghi cảnh báo vào log, khuyên tăng max_wal_size - [Configuring Buffer Pool Flushing](https://dev.mysql.com/doc/refman/8.4/en/innodb-buffer-pool-flushing.html) · MySQL · redo log đầy thì checkpoint gấp (sharp) làm thông lượng giảm trong chốc lát; adaptive flushing chia đều việc ghi - [Error Reporting and Logging (PostgreSQL Documentation)](https://www.postgresql.org/docs/current/runtime-config-logging.html) · PostgreSQL · log_checkpoints: mỗi lần checkpoint ghi vào log số buffer đã ghi và thời gian mất, mặc định bật - [The Cumulative Statistics System (PostgreSQL Documentation)](https://www.postgresql.org/docs/current/monitoring-stats.html) · PostgreSQL · num_timed (checkpoint chạy khi đến giờ) và num_requested (checkpoint được yêu cầu) trong pg_stat_checkpointer - [PostgreSQL 17 Release Notes](https://www.postgresql.org/docs/release/17.0/) · PostgreSQL · Thêm mới pg_stat_checkpointer, chuyển các cột liên quan tới checkpoint từ pg_stat_bgwriter sang - [The Cumulative Statistics System (PostgreSQL 16 Documentation)](https://www.postgresql.org/docs/16/monitoring-stats.html) · PostgreSQL · Đến bản 16 là checkpoints_timed, checkpoints_req trong pg_stat_bgwriter - [InnoDB Standard Monitor and Lock Monitor Output](https://dev.mysql.com/doc/refman/8.4/en/innodb-standard-monitor.html) · MySQL · Phần LOG: số thứ tự log (log sequence number) hiện tại và vị trí checkpoint gần nhất #### db-cold-cache · Cache lạnh (ngay sau khởi động lại) · Cold buffer pool after restart Khi DB khởi động lại, cache trong bộ nhớ trống trơn, nên một thời gian mọi truy vấn đều phải đọc từ ổ đĩa. - Vì sao → Dẫn đến → Trên màn hình: DB khởi động lại trong đợt bảo trì → Dữ liệu hay dùng không có trong bộ nhớ nên phải đọc từ ổ đĩa → Ngay sau bảo trì, đăng nhập và loading chậm một thời gian - Triệu chứng: Không vào được·kẹt loading, Trễ thao tác / Yếu tố: Độ trễ - Ai gặp: Cả server / Khi nào: Ngay sau đăng nhập hoặc bảo trì - Phụ trách chính: Hạ tầng DB (Đội hạ tầng) / Phối hợp: Phát triển server (Đội phát triển game) - Việc cần làm (Đội phát triển game): Mở cửa từ từ (dùng hàng chờ đăng nhập để tăng dần số người vào game). - Việc cần làm (Đội hạ tầng): Làm nóng cache sau khi khởi động lại (warm-up, kiểm tra cấu hình lưu và khôi phục buffer pool), với DB khôi phục từ snapshot thì đọc trước cả ổ đĩa. - Trên đồ thị: Tăng vọt ngay sau đăng nhập hoặc bảo trì (Lượng đọc ổ đĩa, tỷ lệ hit của buffer cache) - Chỗ cần xem: MySQL: xem tỷ lệ giữa Innodb_buffer_pool_reads (số lần phải đọc từ ổ đĩa vì không có trong buffer pool) và Innodb_buffer_pool_read_requests, tiến độ làm nóng Innodb_buffer_pool_load_status. PostgreSQL: xem blks_read, blks_hit trong pg_stat_database. Xem cả số lần đọc ổ đĩa của server DB - Đúng nếu: ngay sau khi khởi động lại, lượng đọc ổ đĩa vọt lên và tỷ lệ hit thấp rồi dần hồi phục, và trong khoảng đó đăng nhập, loading chậm - Loại trừ nếu: tỷ lệ hit như bình thường mà ngay sau bảo trì vẫn chậm → đăng nhập ồ ạt, N+1 (db-login-storm) hoặc connection pool (db-pool) - Cách kiểm tra: Kiểm tra bằng công cụ hạ tầng (không cần code game) - Tìm hiểu thêm: MySQL lưu danh sách page trong buffer pool khi tắt, rồi đọc lại ở chế độ chạy nền khi bật, nhưng phải mất thời gian mới nạp đầy. Nếu DB trên cloud được khôi phục từ snapshot (bản sao ổ đĩa), bản thân ổ đĩa cũng chậm ở mỗi block đọc lần đầu nên tình trạng kéo dài hơn. - Sự cố thực tế: roblox-2021 - Nguồn: - [Saving and Restoring the Buffer Pool State](https://dev.mysql.com/doc/refman/8.4/en/innodb-preload-buffer-pool.html) · MySQL · Để rút ngắn thời gian làm nóng sau khi khởi động lại, khi tắt thì lưu danh sách các page dùng gần đây (mặc định 25%), khi bật thì đọc lại; cả hai đều bật mặc định - [pg_prewarm — preload relation data into buffer caches](https://www.postgresql.org/docs/current/pgprewarm.html) · PostgreSQL · Định kỳ ghi lại nội dung shared buffer, rồi nạp lại sau khi khởi động lại (autoprewarm) - [Initialize Amazon EBS volumes](https://docs.aws.amazon.com/ebs/latest/userguide/ebs-initialize.html) · AWS · Volume tạo từ snapshot có độ trễ tăng và hiệu năng giảm cho đến khi tải về hết mọi block - [Server Status Variables](https://dev.mysql.com/doc/refman/8.4/en/server-status-variables.html) · MySQL · Innodb_buffer_pool_reads (số lần đọc logic không có trong buffer pool nên phải đọc thẳng từ ổ đĩa), Innodb_buffer_pool_read_requests, Innodb_buffer_pool_load_status (tiến độ làm nóng) - [The Cumulative Statistics System (PostgreSQL Documentation)](https://www.postgresql.org/docs/current/monitoring-stats.html) · PostgreSQL · blks_read (số block đọc từ ổ đĩa) và blks_hit (số block tìm thấy trong buffer cache) trong pg_stat_database #### db-login-storm · Đăng nhập ồ ạt và query N+1 · Login storm, N+1 queries Nếu tải một nhân vật mà phải truy vấn riêng lẻ vài chục lần, thì vài chục nghìn người đăng nhập cùng lúc sẽ thành hàng triệu query. - Vì sao → Dẫn đến → Trên màn hình: Khi tải nhân vật, truy vấn vật phẩm, skill, nhiệm vụ riêng từng thứ một → Ngay sau bảo trì, đăng nhập đồng thời làm số query tăng vọt → Đăng nhập kẹt loading, việc lưu của người đang chơi cũng bị dồn lại - Triệu chứng: Không vào được·kẹt loading, Trễ thao tác / Yếu tố: Ngưng trệ, Độ trễ - Ai gặp: Cả server / Khi nào: Ngay sau đăng nhập hoặc bảo trì - Phụ trách chính: Phát triển server (Đội phát triển game) / Phối hợp: Hạ tầng DB (Đội hạ tầng) - Việc cần làm (Đội phát triển game): Gộp thành một lần truy vấn, hàng chờ đăng nhập, cache, kiểm tra số truy vấn do lazy loading của ORM sinh ra. - Việc cần làm (Đội hạ tầng): Lập bảng xếp hạng các query bị gọi nhiều nhất và chia sẻ, giám sát số query và số kết nối trong khung giờ đăng nhập ngay sau bảo trì. - Trên đồ thị: Tăng vọt ngay sau đăng nhập hoặc bảo trì (Số query mỗi giây của DB, số lượt đăng nhập) - Chỗ cần xem: Đặt chồng số lượt đăng nhập ngay sau bảo trì với số query mỗi giây của DB (MySQL: mức tăng của Questions) và tính số query cho mỗi lượt đăng nhập. Lấy các query bị gọi nhiều nhất từ COUNT_STAR trong events_statements_summary_by_digest của MySQL, calls trong pg_stat_statements của PostgreSQL - Đúng nếu: mỗi lượt đăng nhập tốn vài chục query, và các query đứng đầu là những query ngắn cùng dạng, truy vấn theo một ID nhân vật. Nếu số query mỗi lượt đăng nhập tăng sau bản cập nhật thì bản cập nhật đó là điểm xuất phát - Loại trừ nếu: số query mỗi lượt đăng nhập ít mà từng query đều chậm → cache lạnh (db-cold-cache) hoặc index (db-no-index) - Cách kiểm tra: Kiểm tra bằng công cụ hạ tầng (không cần code game) - Tìm hiểu thêm: Lazy loading của ORM (thư viện tự sinh truy vấn DB thay cho lập trình viên) tạo ra kiểu truy vấn này mà chính lập trình viên cũng không hay biết. Trên server dev chỉ có vài nhân vật nên không lộ ra, đến khi đăng nhập đồng thời trên môi trường live mới bộc lộ lần đầu. - Nguồn: - [Efficient Querying](https://learn.microsoft.com/en-us/ef/core/performance/efficient-querying) · .NET · Lazy loading của ORM gửi thêm một query cho mỗi phần tử, sinh ra vấn đề N+1 làm hiệu năng giảm mạnh; khuyến nghị nạp gộp một lần (eager loading) - [pg_stat_statements — track statistics of SQL planning and execution](https://www.postgresql.org/docs/current/pgstatstatements.html) · PostgreSQL · Thu thập số lần chạy (calls) và tổng thời gian chạy theo từng câu lệnh để xếp hạng các query bị gọi nhiều - [Performance Schema Statement Digests and Sampling](https://dev.mysql.com/doc/refman/8.4/en/performance-schema-statement-digests.html) · MySQL · events_statements_summary_by_digest gộp các query cùng dạng để thống kê số lần và thời gian - [Statement Summary Tables](https://dev.mysql.com/doc/refman/8.4/en/performance-schema-statement-summary-tables.html) · MySQL · COUNT_STAR (số lần chạy) và SUM_TIMER_WAIT (tổng thời gian) trong bảng tổng hợp - [Server Status Variables](https://dev.mysql.com/doc/refman/8.4/en/server-status-variables.html) · MySQL · Questions: số câu lệnh client đã gửi #### db-batch · Tác vụ batch khối lượng lớn · Batch jobs during service Nếu chạy tổng hợp bảng xếp hạng, gửi thư hàng loạt hay dọn dữ liệu cũ trong lúc đang vận hành, các tác vụ này sẽ chiếm khóa và ổ đĩa. - Vì sao → Dẫn đến → Trên màn hình: Chạy tác vụ khối lượng lớn trong giờ vận hành → Khóa phạm vi rộng, chiếm ổ đĩa và CPU → Giao dịch và lưu thất bại vào một khung giờ nhất định, loading chậm - Triệu chứng: Trễ thao tác, Nuốt thao tác·rollback / Yếu tố: Độ trễ, Ngưng trệ - Ai gặp: Chỉ một tính năng, Cả server / Khi nào: Theo chu kỳ đều - Phụ trách chính: Phát triển server (Đội phát triển game) / Phối hợp: Hạ tầng DB (Đội hạ tầng) - Việc cần làm (Đội phát triển game): Chia nhỏ để chạy từng ít một, tổng hợp trên bản sao. - Việc cần làm (Đội hạ tầng): Cung cấp bản sao dành cho tổng hợp, lên lịch batch vào khung giờ vắng, theo dõi lock escalation và chờ gap lock. - Trên đồ thị: Vọt lên theo chu kỳ (Độ trễ query DB, chờ khóa) - Chỗ cần xem: Tìm query dài đang chạy vào lúc lag. MySQL: xem slow query log; PostgreSQL: xem query_start, query trong pg_stat_activity; rồi đối chiếu với chỉ số chờ khóa cùng thời điểm và lịch batch (cron, event scheduler của DB). SQL Server: ghi lại lock escalation bằng extended event lock_escalation - Đúng nếu: lần nào cũng vào cùng một giờ có UPDATE, DELETE hoặc query tổng hợp khối lượng lớn chạy, và trong lúc đó chờ khóa cùng mức sử dụng ổ đĩa đều tăng - Loại trừ nếu: lúc đó không có query dài nào → checkpoint (db-checkpoint) hoặc sao lưu server (dk-backup) - Cách kiểm tra: Kiểm tra bằng công cụ hạ tầng (không cần code game) - Tìm hiểu thêm: SQL Server đổi sang khóa cả bảng khi một câu lệnh giữ quá khoảng 5.000 khóa dòng (lock escalation). Ngay lúc đó, mọi yêu cầu dùng cùng bảng đều bị dừng. MySQL với cấu hình mặc định, khi sửa theo điều kiện phạm vi, cũng khóa cả khoảng trống giữa các dòng (gap lock), chặn việc thêm dòng mới. - Nguồn: - [Transaction Locking and Row Versioning Guide](https://learn.microsoft.com/en-us/sql/relational-databases/sql-server-transaction-locking-and-row-versioning-guide) · Microsoft SQL Server · Khi một câu lệnh giữ từ 5.000 khóa trở lên trên một bảng (hoặc index) thì xảy ra lock escalation, được ghi lại bằng extended event lock_escalation - [InnoDB Locking](https://dev.mysql.com/doc/refman/8.4/en/innodb-locking.html) · MySQL · Ở mức cô lập mặc định REPEATABLE READ của InnoDB, tìm kiếm và quét dùng next-key lock, nên gap lock chặn việc thêm dòng mới vào khoảng trống đó - [The Slow Query Log](https://dev.mysql.com/doc/refman/8.4/en/slow-query-log.html) · MySQL · Ghi các query vượt long_query_time kèm thời gian chạy (Query_time), thời gian khóa (Lock_time), số dòng đã đọc - [The Cumulative Statistics System (PostgreSQL Documentation)](https://www.postgresql.org/docs/current/monitoring-stats.html) · PostgreSQL · pg_stat_activity: query đang chạy (query) và thời điểm bắt đầu (query_start) của từng session #### db-failover · Failover DB · Database failover Trong lúc DB chính chết và chuyển sang DB dự phòng, không ghi được dữ liệu, và phần dữ liệu cuối cùng chưa kịp replicate có thể bị mất. - Vì sao → Dẫn đến → Trên màn hình: DB chính gặp sự cố, DB dự phòng được nâng lên làm DB chính → Trong lúc chuyển, không ghi được từ vài giây đến vài phút; nếu replication bất đồng bộ thì dữ liệu chưa replicate có thể mất → Mọi thao tác lưu thất bại trong chốc lát, vật phẩm và điểm kinh nghiệm bị rollback - Triệu chứng: Nuốt thao tác·rollback, Đứng hình, Mất kết nối, Không vào được·kẹt loading / Yếu tố: Ngưng trệ, Mất gói - Ai gặp: Cả server / Khi nào: Thỉnh thoảng bất chợt - Phụ trách chính: Hạ tầng DB (Đội hạ tầng) / Phối hợp: Phát triển server (Đội phát triển game) - Việc cần làm (Đội phát triển game): Thao tác lưu có thể thử lại, cấu hình để nhanh chóng bỏ kết nối đã đứt và kết nối lại tới địa chỉ mới (connection pool, cache DNS), kiểm tra việc kết nối lại khi diễn tập failover. - Việc cần làm (Đội hạ tầng): Replication đồng bộ hoặc bán đồng bộ (đánh đổi bằng độ trễ ghi), diễn tập failover, giám sát thời gian failover và replication lag. - Con số tham khảo: Failover tự động của DB managed thường mất từ vài chục giây đến 2 phút. Nếu replication bất đồng bộ, các lần lưu gần nhất có thể mất, tương ứng với replication lag (dưới 1 giây đến vài giây). - Trên đồ thị: Rớt kết nối hàng loạt (Số kết nối DB, số lỗi ghi) - Chỗ cần xem: Đặt lên cùng một đồ thị bản ghi failover phía DB (RDS: sự kiện RDS-EVENT-0013 bắt đầu failover, RDS-EVENT-0049 failover hoàn tất; DB tự vận hành: log promote) cùng số kết nối DB và số lỗi kết nối của server game. Nếu là replication bất đồng bộ, xem cả replication lag ngay trước sự cố (RDS ReplicaLag, replay_lag trong pg_stat_replication của PostgreSQL) - Đúng nếu: các lần lưu thất bại dồn vào một khoảng, trùng với khoảng giữa lúc failover bắt đầu và hoàn tất. Khoảng thời gian bị quay lại xấp xỉ replication lag ngay trước sự cố. Server game nào vẫn lỗi sau khi failover xong là do vẫn dùng kết nối tạo tới địa chỉ cũ - Loại trừ nếu: mất kết nối vào lúc không có bản ghi failover → mạng hoặc DB quá tải - Cách kiểm tra: Kiểm tra bằng công cụ hạ tầng (không cần code game) - Sự cố thực tế: riot-euw-2021 - Nguồn: - [Failing over a Multi-AZ DB instance for Amazon RDS](https://docs.aws.amazon.com/AmazonRDS/latest/UserGuide/Concepts.MultiAZ.Failover.html) · AWS · Failover Multi-AZ thường mất 60–120 giây, sau failover phải tạo lại kết nối, khuyến nghị TTL cache DNS của JVM không quá 60 giây - [High availability for Amazon Aurora](https://docs.aws.amazon.com/AmazonRDS/latest/AuroraUserGuide/Concepts.AuroraHighAvailability.html) · AWS · Trong lúc sự cố, đọc và ghi đều thất bại, thường phục hồi trong vòng 60 giây (thường gặp là trong 30 giây) - [Semisynchronous Replication](https://dev.mysql.com/doc/refman/8.4/en/replication-semisync.html) · MySQL · Với replication bất đồng bộ, khi DB chính chết, transaction đã commit có thể chưa có trên bản sao; bán đồng bộ chờ một bản sao xác nhận đã nhận để giảm rủi ro này, đổi lại độ trễ tăng - [Log-Shipping Standby Servers (PostgreSQL Documentation)](https://www.postgresql.org/docs/current/warm-standby.html) · PostgreSQL · Log shipping là bất đồng bộ nên khi server chính chết thì mất các transaction chưa kịp gửi, độ trễ streaming replication thường dưới 1 giây - [Amazon RDS event categories and event messages](https://docs.aws.amazon.com/AmazonRDS/latest/UserGuide/USER_Events.Messages.html) · AWS · RDS-EVENT-0013: bắt đầu failover Multi-AZ, RDS-EVENT-0049: failover Multi-AZ hoàn tất - [Amazon CloudWatch metrics for Amazon RDS](https://docs.aws.amazon.com/AmazonRDS/latest/UserGuide/rds-metrics.html) · AWS · ReplicaLag: thời gian (giây) read replica bị tụt lại so với bản gốc - [The Cumulative Statistics System (PostgreSQL Documentation)](https://www.postgresql.org/docs/current/monitoring-stats.html) · PostgreSQL · replay_lag trong pg_stat_replication: thời gian từ khi server chính ghi WAL đến khi bản sao báo đã áp dụng #### db-save-interval · Mất tiến độ do chu kỳ lưu dài · Periodic save window Nếu để giảm tải mà vài phút mới lưu một lần, thì khi server sập giữa hai lần lưu, tiến độ chơi sẽ mất. - Vì sao → Dẫn đến → Trên màn hình: Trạng thái nhân vật được lưu vài phút một lần → Giữa hai lần lưu, server crash hoặc gặp sự cố → Vào lại game thì thấy trạng thái của vài phút trước (rollback) - Triệu chứng: Nuốt thao tác·rollback / Yếu tố: Mất gói - Ai gặp: Cả server, Một địa điểm hoặc kênh / Khi nào: Thỉnh thoảng bất chợt - Phụ trách chính: Phát triển server (Đội phát triển game) / Phối hợp: Hạ tầng DB (Đội hạ tầng) - Việc cần làm (Đội phát triển game): Lưu ngay các sự kiện quan trọng (giao dịch, nhận vật phẩm hiếm), ghi log thay đổi. - Việc cần làm (Đội hạ tầng): Kiểm tra dư địa IOPS và CPU của DB để gánh lượng ghi tăng thêm khi rút ngắn chu kỳ lưu. - Trên đồ thị: Rớt kết nối hàng loạt (Số kết nối, số báo cáo rollback) - Chỗ cần xem: Đặt cạnh nhau thời điểm crash hoặc sự cố và thời điểm lưu cuối cùng của các nhân vật báo bị rollback (log lưu của server game hoặc cột thời gian sửa trong DB) - Đúng nếu: thời điểm bị quay về trùng với lần lưu cuối cùng ngay trước crash, và khoảng thời gian bị mất ngắn hơn chu kỳ lưu - Loại trừ nếu: log server game ghi là đã lưu xong mà vẫn bị quay về → mất dữ liệu do failover DB (db-failover) hoặc đọc giá trị cũ từ bản sao (db-replica-lag) - Cách kiểm tra: Cần log và chỉ số của server, client game - Nguồn: - [Asynchronous Commit (PostgreSQL Documentation)](https://www.postgresql.org/docs/current/wal-async-commit.html) · PostgreSQL · Gom bản ghi lại để ghi xuống sau thì thông lượng tăng, đổi lại khi có sự cố, các transaction gần nhất có thể mất (cùng một sự đánh đổi) - [Redis persistence](https://redis.io/docs/latest/operate/oss_and_stack/management/persistence/) · Redis · Nếu vài phút mới tạo snapshot RDB một lần thì phải chấp nhận mất dữ liệu của vài phút cuối khi kết thúc bất thường #### db-cache-stampede · Cache stampede · Cache stampede / thundering herd Khi cache của dữ liệu được dùng nhiều hết hạn cùng lúc, hàng nghìn yêu cầu đồng loạt dồn về DB. - Vì sao → Dẫn đến → Trên màn hình: Dữ liệu hot lưu trong Redis hoặc tương tự hết hạn cùng lúc → Các yêu cầu muốn tạo lại cùng dữ liệu đó đồng loạt dồn về DB → DB quá tải khiến nhiều tính năng lần lượt chậm đi hoặc đứng hình - Triệu chứng: Trễ thao tác, Đứng hình, Không vào được·kẹt loading / Yếu tố: Ngưng trệ, Độ trễ - Ai gặp: Cả server / Khi nào: Theo chu kỳ đều, Khi đông người - Phụ trách chính: Phát triển server (Đội phát triển game) / Phối hợp: Hạ tầng DB (Đội hạ tầng) - Việc cần làm (Đội phát triển game): Rải ngẫu nhiên thời điểm hết hạn, chỉ một yêu cầu làm mới còn các yêu cầu khác dùng giá trị cũ. - Việc cần làm (Đội hạ tầng): Cấu hình bản sao và failover tự động để cache không bị trống toàn bộ khi Redis khởi động lại hoặc gặp sự cố, kiểm tra DB có đủ dư địa để chịu được khi cache trống. - Trên đồ thị: Vọt lên theo chu kỳ (Tỷ lệ hit của cache, số query mỗi giây của DB) - Chỗ cần xem: Đặt chồng keyspace_hits, keyspace_misses (tỷ lệ hit), expired_keys, việc khởi động lại (uptime_in_seconds) trong Redis INFO với số query mỗi giây của DB, và đếm xem lúc đó có bao nhiêu query giống nhau chạy đồng thời trên DB (MySQL SHOW PROCESSLIST, PostgreSQL pg_stat_activity) - Đúng nếu: vào lúc cache miss vọt lên đột ngột, số query DB cùng vọt lên, và phần lớn query chạy đồng thời là cùng một query đọc cùng dữ liệu. Trùng với chu kỳ hết hạn của key hot hoặc lúc Redis khởi động lại - Loại trừ nếu: cache miss như bình thường mà chỉ query DB tăng → đăng nhập ồ ạt (db-login-storm) hoặc batch (db-batch) - Cách kiểm tra: Kiểm tra bằng công cụ hạ tầng (không cần code game) - Tìm hiểu thêm: Khi Redis khởi động lại hoặc gặp sự cố làm cache trống toàn bộ, chuyện tương tự cũng xảy ra. Hệ thống càng dựa vào cache mà đặt DB nhỏ thì càng nguy hiểm. - Nguồn: - [Scaling Memcache at Facebook (NSDI '13)](https://www.usenix.org/conference/nsdi13/technical-sessions/presentation/nishtala) · USENIX · Khi key hay dùng bị vô hiệu hóa, nhiều lượt đọc dồn về DB (thundering herd); ngăn bằng lease (chỉ một client được làm mới) và trả về giá trị cũ; cụm cache trống thì làm nóng riêng - [Optimal Probabilistic Cache Stampede Prevention](https://www.vldb.org/pvldb/vol8/p886-vattani.pdf) · VLDB Endowment · Khi mục hot hết hạn, nhiều yêu cầu cùng tạo lại nó (cache stampede); ngăn bằng cách làm mới sớm theo xác suất trước khi hết hạn - [High availability with Redis Sentinel](https://redis.io/docs/latest/operate/oss_and_stack/management/sentinel/) · Redis · Failover tự động: nâng bản sao lên khi server chính chết - [INFO](https://redis.io/docs/latest/commands/info/) · Redis · keyspace_hits, keyspace_misses (số lần tra key thành công, thất bại), expired_keys (số key đã hết hạn), uptime_in_seconds (thời gian kể từ khi khởi động) - [SHOW PROCESSLIST Statement](https://dev.mysql.com/doc/refman/8.4/en/show-processlist.html) · MySQL · Câu lệnh đang chạy (Info) và thời gian ở trạng thái hiện tại (Time, giây) của từng session - [The Cumulative Statistics System (PostgreSQL Documentation)](https://www.postgresql.org/docs/current/monitoring-stats.html) · PostgreSQL · pg_stat_activity: query đang chạy (query) của từng session #### db-long-tx · Transaction mở quá lâu · Long-running transaction / MVCC purge lag Nếu một transaction mở quá lâu, nó giữ khóa mãi, và DB không dọn (purge) được dữ liệu phiên bản cũ nên toàn bộ hệ thống chậm dần. - Vì sao → Dẫn đến → Trên màn hình: Mở transaction rồi chờ phản hồi của server khác, hoặc chạy query tổng hợp dài trên DB chính trong giờ vận hành → Khóa đã giữ không được nhả, dữ liệu phiên bản cũ cần dọn cứ tích tụ → Tính năng dùng dòng đó bị timeout, việc lưu và truy vấn nói chung chậm dần trong vài giờ - Triệu chứng: Trễ thao tác, Nuốt thao tác·rollback / Yếu tố: Độ trễ, Ngưng trệ - Ai gặp: Chỉ một tính năng, Cả server / Khi nào: Càng chạy lâu càng nặng, Thỉnh thoảng bất chợt - Phụ trách chính: Phát triển server (Đội phát triển game) / Phối hợp: Hạ tầng DB (Đội hạ tầng) - Việc cần làm (Đội phát triển game): Không chờ gọi mạng hay input của người dùng bên trong transaction, chạy query tổng hợp trên bản sao. - Việc cần làm (Đội hạ tầng): Cảnh báo và buộc dừng transaction mở quá lâu, cung cấp bản sao cho tổng hợp, theo dõi mức tăng của undo log và dòng chết. - Trên đồ thị: Tăng dần (Độ dài undo log (History list length), số dòng chết) - Chỗ cần xem: MySQL: dùng trx_started trong INFORMATION_SCHEMA.INNODB_TRX để tìm transaction cũ nhất, xem History list length (lượng undo log chưa dọn) trong phần TRANSACTIONS của SHOW ENGINE INNODB STATUS. PostgreSQL: xem xact_start và các session có state là idle in transaction trong pg_stat_activity, n_dead_tup trong pg_stat_user_tables - Đúng nếu: có transaction đã mở từ vài phút đến vài giờ, trong thời gian đó History list length hoặc n_dead_tup tăng liên tục, rồi giảm xuống khi transaction đó kết thúc và việc dọn dẹp (purge, VACUUM) chạy - Loại trừ nếu: không có transaction cũ mà vẫn chậm nói chung → checkpoint (db-checkpoint) hoặc ổ đĩa - Cách kiểm tra: Kiểm tra bằng công cụ hạ tầng (không cần code game) - Tìm hiểu thêm: DB giữ lại phiên bản cũ để bên đọc vẫn thấy được dữ liệu như trước khi sửa (MVCC). Chỉ khi transaction cũ nhất kết thúc thì mới xóa được các bản ghi này, nên nếu một transaction mở suốt vài giờ, MySQL sẽ tích tụ undo log, còn PostgreSQL tích tụ các dòng chết (dead tuple) mà VACUUM không dọn được. Với SQL Server, transaction log không thu nhỏ được, có khi làm đầy cả ổ đĩa. - Nguồn: - [InnoDB Multi-Versioning](https://dev.mysql.com/doc/refman/8.4/en/innodb-multi-versioning.html) · MySQL · Nếu vẫn còn transaction có thể thấy phiên bản cũ thì update undo log không bỏ được, rollback segment phình to; khuyến nghị commit thường xuyên cả với transaction chỉ đọc - [Routine Vacuuming (PostgreSQL Documentation)](https://www.postgresql.org/docs/current/routine-vacuuming.html) · PostgreSQL · Phiên bản dòng cũ không xóa được chừng nào transaction khác còn thấy nó; transaction mở quá lâu phải được kết thúc hoặc ngắt session - [Client Connection Defaults (PostgreSQL Documentation)](https://www.postgresql.org/docs/current/runtime-config-client.html) · PostgreSQL · idle_in_transaction_session_timeout: ngắt các session mở transaction rồi để đó, để chúng không giữ khóa quá lâu - [Troubleshoot a full transaction log (SQL Server Error 9002)](https://learn.microsoft.com/en-us/sql/relational-databases/logs/troubleshoot-a-full-transaction-log-sql-server-error-9002) · Microsoft SQL Server · Transaction đang hoạt động chạy lâu sẽ chặn việc dọn transaction log - [The INFORMATION_SCHEMA INNODB_TRX Table](https://dev.mysql.com/doc/refman/8.4/en/information-schema-innodb-trx-table.html) · MySQL · TRX_STARTED: thời điểm bắt đầu transaction - [Purge Configuration](https://dev.mysql.com/doc/refman/8.4/en/innodb-purge-configuration.html) · MySQL · purge dọn danh sách undo log của các transaction đã commit (history list), lượng tồn đọng hiển thị ở History list length trong phần TRANSACTIONS của SHOW ENGINE INNODB STATUS - [The Cumulative Statistics System (PostgreSQL Documentation)](https://www.postgresql.org/docs/current/monitoring-stats.html) · PostgreSQL · xact_start (thời điểm bắt đầu transaction) và state (idle in transaction) trong pg_stat_activity, n_dead_tup (ước lượng số dòng chết) trong pg_stat_user_tables #### db-redis-block · Lệnh Redis chậm · Redis blocking commands (single-threaded) Redis xử lý lần lượt từng lệnh một, nên một lệnh chậm sẽ chặn mọi yêu cầu phía sau. - Vì sao → Dẫn đến → Trên màn hình: Trong giờ vận hành, dùng KEYS để quét toàn bộ, đọc hoặc xóa trọn một bảng xếp hạng hay danh sách có hàng triệu phần tử → Mọi yêu cầu khác phải chờ cho đến khi lệnh đó xong (từ vài chục ms đến vài giây) → Các tính năng dùng session, bảng xếp hạng, cache cùng lúc bị khựng, đăng nhập chậm - Triệu chứng: Đứng hình, Trễ thao tác, Không vào được·kẹt loading / Yếu tố: Ngưng trệ, Độ trễ - Ai gặp: Cả server, Chỉ một tính năng / Khi nào: Thỉnh thoảng bất chợt, Theo chu kỳ đều - Phụ trách chính: Phát triển server (Đội phát triển game) / Phối hợp: Hạ tầng DB (Đội hạ tầng) - Việc cần làm (Đội phát triển game): Dùng SCAN thay cho KEYS, chia nhỏ key lớn, xóa bằng UNLINK (xóa ở chế độ chạy nền), rải các thời điểm hết hạn đang dồn vào cùng một giây. - Việc cần làm (Đội hạ tầng): Theo dõi log lệnh chậm (SLOWLOG), chặn các lệnh nguy hiểm như KEYS trên server vận hành, kiểm tra key lớn định kỳ, tắt THP và chừa dư bộ nhớ cho fork, lưu RDB và AOF trên bản sao. - Con số tham khảo: Lệnh thông thường mất dưới 1 ms. Xử lý hàng triệu phần tử một lượt có thể mất từ vài trăm ms tới vài giây. - Trên đồ thị: Thỉnh thoảng vọt lên bất chợt (Độ trễ phản hồi của Redis, số lệnh chậm) - Chỗ cần xem: Dùng SLOWLOG GET xem các lệnh vượt slowlog-log-slower-than; bật latency monitor (mặc định tắt) bằng CONFIG SET latency-monitor-threshold, rồi xem độ trễ theo từng sự kiện như fork, expire-cycle bằng LATENCY LATEST, LATENCY DOCTOR. Kiểm tra cả thời gian fork và key lớn bằng latest_fork_usec trong INFO và redis-cli --bigkeys - Đúng nếu: vào lúc dừng, SLOWLOG có KEYS hoặc lệnh xử lý trọn một key lớn, hoặc LATENCY ghi nhận sự kiện fork, expire-cycle cùng thời điểm, kéo dài từ vài chục ms trở lên - Loại trừ nếu: SLOWLOG và LATENCY đều trống mà chỉ phía server game thấy chậm → mạng hoặc việc chờ bên trong server game (SLOWLOG chỉ đo thời gian thực thi lệnh, không tính thời gian trao đổi với client) - Cách kiểm tra: Kiểm tra bằng công cụ hạ tầng (không cần code game) - Tìm hiểu thêm: Redis cũng dừng đúng lúc nhân bản tiến trình (fork) để tạo file lưu (snapshot RDB) hoặc ghi lại AOF. Trên server ngày nay, fork mất khoảng 10 ms cho mỗi 1 GB bộ nhớ, nên 30 GB là khoảng 300 ms. Nếu bật huge page (THP), sau fork mỗi lần ghi phải sao chép nguyên cả huge page (copy-on-write), làm thời gian dừng và mức dùng bộ nhớ tăng mạnh, nên thường tắt THP và chừa dư nhiều bộ nhớ. Khi có rất nhiều key hết hạn trong cùng một giây, Redis cũng dừng một chút để xóa chúng. - Nguồn: - [Diagnosing latency issues](https://redis.io/docs/latest/operate/oss_and_stack/management/optimization/latency/) · Redis · Một thread xử lý lần lượt các yêu cầu nên lệnh chậm chặn mọi thứ phía sau; dùng SCAN thay cho KEYS; fork đo thực tế trên server vật lý và VM đời mới mất khoảng 9–13 ms mỗi 1 GB; THP làm độ trễ và bộ nhớ tăng vọt do sao chép sau fork; nhiều key hết hạn trong cùng một giây thì Redis dừng - [KEYS](https://redis.io/docs/latest/commands/keys/) · Redis · Phải cực kỳ thận trọng khi dùng trong môi trường vận hành, có thể phá hỏng hiệu năng trên DB lớn (1 triệu key mất 40 ms trên laptop phổ thông) - [UNLINK](https://redis.io/docs/latest/commands/unlink/) · Redis · Xóa bất đồng bộ: gỡ key ra ngay, còn việc thu hồi bộ nhớ do thread khác làm - [SLOWLOG](https://redis.io/docs/latest/commands/slowlog/) · Redis · Log lệnh chậm ghi các lệnh vượt slowlog-log-slower-than; thời gian thực thi không tính I/O trao đổi với client - [Redis latency monitoring](https://redis.io/docs/latest/operate/oss_and_stack/management/optimization/latency-monitor/) · Redis · latency-monitor-threshold mặc định 0 (tắt), LATENCY LATEST, LATENCY DOCTOR, ghi độ trễ theo từng sự kiện như fork, expire-cycle - [INFO](https://redis.io/docs/latest/commands/info/) · Redis · latest_fork_usec: thời gian của lần fork gần nhất (micro giây) - [Redis CLI](https://redis.io/docs/latest/develop/tools/cli/) · Redis · --bigkeys: quét keyspace để tìm key lớn #### db-plan-flip · Query chậm do kế hoạch thực thi thay đổi · Query plan regression (stats, parameter sniffing) Code không đổi, nhưng nếu DB đổi cách xử lý cùng một query (kế hoạch thực thi), query hôm qua mất 2 ms thì hôm nay mất vài trăm ms. - Vì sao → Dẫn đến → Trên màn hình: Thống kê tự động cập nhật, DB khởi động lại hoặc phân bố dữ liệu thay đổi khiến DB lập lại kế hoạch thực thi → Kế hoạch không dùng index được chọn, cùng một query chậm đi vài chục đến vài trăm lần, kết nối DB bị giữ → Không hề có triển khai mà một tính năng đột nhiên tải chậm, các yêu cầu khác cũng phải chờ - Triệu chứng: Trễ thao tác, Không vào được·kẹt loading / Yếu tố: Độ trễ, Ngưng trệ - Ai gặp: Chỉ một tính năng, Cả server / Khi nào: Thỉnh thoảng bất chợt - Phụ trách chính: Hạ tầng DB (Đội hạ tầng) / Phối hợp: Phát triển server (Đội phát triển game) - Việc cần làm (Đội phát triển game): Với query có số kết quả chênh lệch lớn tùy giá trị, tách ra dùng riêng hoặc cân nhắc hint cho kế hoạch, thiết kế query chắc chắn dùng index. - Việc cần làm (Đội hạ tầng): Theo dõi query chậm và lịch sử kế hoạch thực thi, cố định kế hoạch tốt (Query Store của SQL Server, v.v.), quản lý thời điểm cập nhật thống kê. - Trên đồ thị: Tăng như bậc thang từ một thời điểm (Thời gian chạy trung bình theo từng query) - Chỗ cần xem: Định kỳ thu thập thời gian trung bình của các query cùng dạng để xem xu hướng. MySQL: AVG_TIMER_WAIT trong events_statements_summary_by_digest; PostgreSQL: mean_exec_time trong pg_stat_statements (12 trở xuống là mean_time). So sánh kế hoạch thực thi trước và sau khi chậm bằng EXPLAIN hoặc auto_explain của PostgreSQL; SQL Server dùng màn hình Regressed Queries (query bị suy giảm hiệu năng) trong Query Store - Đúng nếu: vào lúc không có triển khai, thời gian trung bình của một query tăng mấy chục lần theo dạng bậc thang, thời điểm đó trùng với lúc cập nhật thống kê hoặc DB khởi động lại, và kế hoạch thực thi đã thay đổi - Loại trừ nếu: kế hoạch thực thi vẫn vậy mà chậm đi → dữ liệu tăng, chờ khóa (db-hot-row) hoặc ổ đĩa - Cách kiểm tra: Kiểm tra bằng công cụ hạ tầng (không cần code game) - Tìm hiểu thêm: SQL Server dùng lại kế hoạch đã lập theo giá trị được truyền vào lần đầu (parameter sniffing). Nếu kế hoạch lập cho một nhân vật mới chỉ có vài vật phẩm lại được dùng cho nhân vật lâu năm có hàng chục nghìn vật phẩm thì chậm đi rất nhiều, và trường hợp ngược lại cũng thường gặp. Khởi động lại xóa kế hoạch nên có khi hệ thống trở lại bình thường rồi lại tệ đi. - Nguồn: - [Query Processing Architecture Guide](https://learn.microsoft.com/en-us/sql/relational-databases/query-processing-architecture-guide) · Microsoft SQL Server · Parameter sniffing: lập kế hoạch thực thi theo giá trị tham số được truyền vào lúc biên dịch hoặc biên dịch lại - [Parameter Sensitive Plan Optimization](https://learn.microsoft.com/en-us/sql/relational-databases/performance/parameter-sensitive-plan-optimization) · Microsoft SQL Server · Khi phân bố dữ liệu không đều, một kế hoạch đã cache không phù hợp với mọi giá trị tham số - [Monitor performance by using the Query Store](https://learn.microsoft.com/en-us/sql/relational-databases/performance/monitoring-performance-by-using-the-query-store) · Microsoft SQL Server · Kế hoạch thay đổi do thống kê, schema, index thay đổi, còn plan cache chỉ giữ kế hoạch mới nhất; cố định kế hoạch tốt bằng plan forcing của Query Store; so sánh query bị chậm đi và kế hoạch của nó bằng màn hình Regressed Queries - [Statement Summary Tables](https://dev.mysql.com/doc/refman/8.4/en/performance-schema-statement-summary-tables.html) · MySQL · events_statements_summary_by_digest: COUNT_STAR và AVG_TIMER_WAIT (thời gian trung bình) theo từng dạng query - [pg_stat_statements — track statistics of SQL planning and execution](https://www.postgresql.org/docs/current/pgstatstatements.html) · PostgreSQL · calls, total_exec_time, mean_exec_time (thời gian chạy trung bình) theo từng câu lệnh - [pg_stat_statements (PostgreSQL 12 Documentation)](https://www.postgresql.org/docs/12/pgstatstatements.html) · PostgreSQL · Đến bản 12, tên cột là total_time, mean_time - [auto_explain — log execution plans of slow queries](https://www.postgresql.org/docs/current/auto-explain.html) · PostgreSQL · Ghi vào log kế hoạch thực thi của các query chạy lâu hơn auto_explain.log_min_duration #### db-ddl-lock · Khóa do thay đổi schema (DDL) khi đang vận hành · Schema change lock (DDL / metadata lock) Nếu thêm cột hay index vào bảng trong lúc đang phục vụ, chỉ vì một khóa cần trong chốc lát mà mọi yêu cầu dùng bảng đó có thể phải chờ. - Vì sao → Dẫn đến → Trên màn hình: Hotfix thêm cột hoặc index vào bảng đang vận hành → Thay đổi schema chờ transaction dài đã mở trước đó, còn mọi yêu cầu đến sau lại chờ thay đổi schema đó → Các tính năng dùng bảng đó (túi đồ, hộp thư, v.v.) ngừng phản hồi hoàn toàn rồi timeout - Triệu chứng: Trễ thao tác, Nuốt thao tác·rollback, Không vào được·kẹt loading / Yếu tố: Ngưng trệ - Ai gặp: Chỉ một tính năng, Cả server / Khi nào: Thỉnh thoảng bất chợt, Ngay sau đăng nhập hoặc bảo trì - Phụ trách chính: Hạ tầng DB (Đội hạ tầng) / Phối hợp: Phát triển server (Đội phát triển game) - Việc cần làm (Đội phát triển game): Hotfix có thay đổi schema thì thống nhất lịch với đội hạ tầng DB, triển khai trước code vẫn chạy được khi chưa có cột mới. - Việc cần làm (Đội hạ tầng): Đặt giới hạn chờ khóa ngắn và thử lại nếu thất bại, chạy khi không có transaction dài, dùng công cụ thay đổi schema online, với bảng lớn thì làm trong giờ bảo trì. - Trên đồ thị: Tăng như bậc thang từ một thời điểm (Số session chờ khóa, độ trễ query trên bảng đó) - Chỗ cần xem: MySQL: đếm các session có State là Waiting for table metadata lock trong SHOW PROCESSLIST, dùng sys.schema_table_lock_waits tìm session đang chặn (blocking_pid). PostgreSQL: xem các yêu cầu có granted là false và AccessExclusiveLock trong pg_locks, dùng pg_blocking_pids() tìm session đang chặn - Đúng nếu: từ lúc bắt đầu thay đổi schema, mọi query dùng bảng đó dồn lại vì chờ khóa, và ở đầu hàng là một transaction chưa kết thúc hoặc câu lệnh thay đổi schema - Loại trừ nếu: chờ chỉ dồn vào một số dòng nhất định, các dòng khác của cùng bảng vẫn xử lý tốt → hot row (db-hot-row) - Cách kiểm tra: Kiểm tra bằng công cụ hạ tầng (không cần code game) - Tìm hiểu thêm: Khi thay đổi schema, MySQL giữ metadata lock, còn PostgreSQL giữ khóa bảng mạnh nhất trong chốc lát. Bản thân thay đổi chỉ mất một thoáng, nhưng nếu phía trước còn một transaction chưa kết thúc, mọi yêu cầu phía sau đều phải chờ. - Nguồn: - [Online DDL Performance and Concurrency](https://dev.mysql.com/doc/refman/8.4/en/innodb-online-ddl-performance.html) · MySQL · Cả online DDL cũng cần metadata lock độc quyền trong chốc lát khi kết thúc; nếu có transaction dài thì phải chờ, và yêu cầu khóa đang chờ đó chặn mọi transaction phía sau - [Server System Variables](https://dev.mysql.com/doc/refman/8.4/en/server-system-variables.html) · MySQL · lock_wait_timeout: giới hạn chờ metadata lock, giá trị mặc định 31.536.000 giây (1 năm) - [ALTER TABLE (PostgreSQL Documentation)](https://www.postgresql.org/docs/current/sql-altertable.html) · PostgreSQL · ALTER TABLE nếu không ghi rõ sẽ giữ khóa ACCESS EXCLUSIVE, loại mạnh nhất - [Client Connection Defaults (PostgreSQL Documentation)](https://www.postgresql.org/docs/current/runtime-config-client.html) · PostgreSQL · lock_timeout: dừng câu lệnh nếu chờ khóa quá thời gian này - [General Thread States](https://dev.mysql.com/doc/refman/8.4/en/general-thread-states.html) · MySQL · Waiting for table metadata lock: trạng thái của thread đang chờ metadata lock - [The schema_table_lock_waits and x$schema_table_lock_waits Views](https://dev.mysql.com/doc/refman/8.4/en/sys-schema-table-lock-waits.html) · MySQL · Session đang chờ metadata lock (waiting_query) và session đang chặn (blocking_pid) - [pg_locks (PostgreSQL Documentation)](https://www.postgresql.org/docs/current/view-pg-locks.html) · PostgreSQL · granted là false nghĩa là đang chờ khóa, mode cho biết loại khóa như AccessExclusiveLock - [System Information Functions and Operators (PostgreSQL Documentation)](https://www.postgresql.org/docs/current/functions-info.html) · PostgreSQL · pg_blocking_pids(): danh sách session đang chặn không cho session chỉ định lấy được khóa ### L13 Kiến trúc và vận hành server (13 nguyên nhân) #### in-gateway · Đi qua gateway hoặc proxy · Gateway / proxy hop Khi đặt một server trung gian giữa client và server game, mỗi lần đi qua nó lại cộng thêm thời gian xử lý, và server đó trở thành điểm lỗi duy nhất (single point of failure). - Vì sao → Dẫn đến → Trên màn hình: Cấu trúc client ↔ gateway ↔ server game → Server trung gian cộng thêm thời gian xử lý và chờ, khi quá tải thì ảnh hưởng đến tất cả mọi người → Ping của mọi người đều tăng, khi gateway gặp sự cố thì toàn bộ người chơi đi qua gateway đó bị mất kết nối - Triệu chứng: Trễ thao tác, Mất kết nối / Yếu tố: Độ trễ, Ngưng trệ - Ai gặp: Cả server / Khi nào: Khi đông người, Luôn luôn - Phụ trách chính: Phát triển server (Đội phát triển game) / Phối hợp: Hạ tầng server (Đội hạ tầng), Phát triển client (Đội phát triển game) - Việc cần làm (Đội phát triển game): Server: thiết kế để có thể tăng lên nhiều gateway; khi một gateway chết, kết nối lại qua gateway khác thì nhân vật vẫn tiếp tục như cũ (kết nối lại phiên). Client: tự động kết nối lại khi mất kết nối với gateway. - Việc cần làm (Đội hạ tầng): Mở rộng gateway theo chiều ngang (thêm máy), giám sát CPU, số kết nối và độ trễ xử lý của từng gateway. - Con số tham khảo: Vì nằm trong cùng một trung tâm dữ liệu nên bình thường mỗi lần đi qua mất chưa tới 1 ms. Khi gateway quá tải, con số này tăng lên vài chục đến vài trăm ms. - Trên đồ thị: Tăng theo số người và tải (Độ trễ xử lý của gateway, CPU và số kết nối của gateway) - Chỗ cần xem: CPU, số kết nối của gateway và Recv-Q của socket trên gateway (ss, netstat), chênh lệch độ trễ trước và sau khi qua gateway. Với lời gọi HTTP, gRPC đi qua service mesh: so sánh chỉ số chuẩn của Istio istio_request_duration_milliseconds, tách theo bên gửi (reporter=source) và bên nhận (reporter=destination) - Đúng nếu: thời gian xử lý của server game không đổi nhưng riêng độ trễ ở đoạn gateway tăng, và cùng lúc đó CPU của gateway bão hòa hoặc Recv-Q dồn lên - Loại trừ nếu: đường không qua gateway (kết nối trực tiếp, gateway khác) cũng chậm y như vậy → nguyên nhân nằm ở đường truyền hoặc phía server game - Cách kiểm tra: Kiểm tra bằng công cụ hạ tầng (không cần code game) - Tìm hiểu thêm: Khi dùng service mesh (Istio, v.v.), sidecar proxy (Envoy) gắn bên cạnh mỗi server cũng thêm một chặng nữa. Yêu cầu giữa các service lần lượt đi qua sidecar bên gửi rồi sidecar bên nhận, và proxy càng được gắn thêm tính năng như thu thập log, chỉ số thì thời gian xử lý và thời gian chờ càng tăng. - Sự cố thực tế: riot-edge-2020 - Nguồn: - [The Unique Architecture behind Amazon Games’ Seamless MMO New World](https://aws.amazon.com/blogs/gametech/the-unique-architecture-behind-amazon-games-seamless-mmo-new-world/) · AWS · Ở New World, client kết nối vào một trong 4 server cổng vào (REP) có địa chỉ công cộng, rồi giao tiếp với các server mô phỏng (hub) phía sau - [Designs, Lessons and Advice from Building Large Distributed Systems](https://www.cs.cornell.edu/projects/ladis2009/talks/dean-keynote-ladis2009.pdf) · Google · Bài phát biểu chính tại LADIS 2009 (Jeff Dean). Khứ hồi trong cùng một trung tâm dữ liệu khoảng 0,5 ms - [Site Reliability Engineering, Chapter 22: Addressing Cascading Failures](https://sre.google/sre-book/addressing-cascading-failures/) · Google · Khi quá tải làm hàng đợi dài ra, thời gian chờ tăng lên gấp nhiều lần thời gian xử lý (xử lý mất 100 ms, hàng đợi dài gấp 10 lần số thread thì mất 1,1 giây) - [Performance and Scalability](https://istio.io/latest/docs/ops/deployment/performance-and-scalability/) · Istio · Ở chế độ sidecar, yêu cầu lần lượt đi qua sidecar proxy bên gửi rồi bên nhận; càng thêm tính năng thì đường xử lý bên trong proxy càng dài, và việc thu thập telemetry làm tăng thời gian chờ của yêu cầu tiếp theo - [What is Envoy](https://www.envoyproxy.io/docs/envoy/latest/intro/what_is_envoy) · Envoy · Envoy là một tiến trình chạy riêng bên cạnh mỗi server ứng dụng, ứng dụng gửi và nhận dữ liệu thông qua Envoy trên localhost - [Istio Standard Metrics](https://istio.io/latest/docs/reference/config/metrics/) · Istio · istio_request_duration_milliseconds (phân bố thời gian xử lý yêu cầu HTTP, gRPC), dùng nhãn reporter để phân biệt proxy bên gửi (source) và bên nhận (destination) - [netstat(8) — Linux manual page](https://man7.org/linux/man-pages/man8/netstat.8.html) · net-tools · Recv-Q: số byte trên socket đã kết nối mà chương trình người dùng chưa lấy đi #### in-zone-transfer · Chuyển zone (chuyển giao giữa các server) · Zone / server handoff Khi nhân vật vào bản đồ hoặc phó bản khác, thông tin nhân vật phải được chuyển sang server khác, và quá trình này có thể bị chậm hoặc thất bại. - Vì sao → Dẫn đến → Trên màn hình: Vào phó bản hoặc sang lục địa khác làm đổi server phụ trách → Lưu → chuyển → tải; nếu server đích đang đông hoặc không còn instance phó bản trống thì phải chờ → Loading lâu, không vào được, mất kết nối giữa lúc chuyển - Triệu chứng: Không vào được·kẹt loading, Đứng hình, Mất kết nối, Kéo ngược / Yếu tố: Độ trễ, Ngưng trệ - Ai gặp: Chỉ mình tôi, Một địa điểm hoặc kênh / Khi nào: Khi di chuyển hoặc chuyển bản đồ - Phụ trách chính: Phát triển server (Đội phát triển game) / Phối hợp: Hạ tầng server (Đội hạ tầng) - Việc cần làm (Đội phát triển game): Giảm dữ liệu cần chuyển giao, giữ chỗ trước ở server đích, khi thất bại thì đưa nhân vật về chỗ cũ. - Việc cần làm (Đội hạ tầng): Giám sát lượng instance còn trống của server phó bản và zone, chuẩn bị đủ số máy trước giờ cao điểm. - Trên đồ thị: Tăng theo số người và tải (Thời gian chuyển zone, số lần thất bại) - Chỗ cần xem: Thời gian của từng bước chuyển giao do server ghi lại (lưu, chuyển, tải) và lý do thất bại, số người và số instance trống của server đích - Đúng nếu: vào lúc có báo cáo loading lâu hoặc không vào được, thời gian chuyển giao tăng hoặc lỗi dồn lại, và server đích đang đông hoặc đã hết instance trống - Loại trừ nếu: chuyển giao xong nhanh nhưng dừng lại sau khi tới nơi → xem spawn dồn dập khi vào khu vực đông người hoặc phía loading của client - Cách kiểm tra: Cần log và chỉ số của server, client game - Tìm hiểu thêm: Thế giới liền mạch (seamless) không có màn hình loading cũng đổi server phụ trách khi nhân vật vượt qua ranh giới giữa các server. Gần ranh giới có thể bị khựng một chút hoặc bị kéo ngược về phía sau. - Nguồn: - [The Unique Architecture behind Amazon Games’ Seamless MMO New World](https://aws.amazon.com/blogs/gametech/the-unique-architecture-behind-amazon-games-seamless-mmo-new-world/) · AWS · Thế giới liền mạch (không có màn hình loading) được chia thành lưới cho nhiều server (hub) phụ trách, khi người chơi di chuyển thì trạng thái được chuyển từ hub này sang hub khác; chế độ theo phiên (session) lấy server còn trống từ một pool dùng chung #### in-cascade · Sự cố dây chuyền · Cascading failure Khi một service chậm đi, các server gọi đến nó bị giữ lại để chờ phản hồi, và cả những tính năng không liên quan cũng đứng lại. - Vì sao → Dẫn đến → Trên màn hình: Một service như DB hay xác thực bị chậm → Thread và kết nối của các server gọi đến bị giữ lại để chờ phản hồi, việc thử lại các yêu cầu thất bại càng làm tăng tải → Cả những tính năng tưởng như không liên quan cũng chậm hoặc đứng hết - Triệu chứng: Đứng hình, Trễ thao tác, Không vào được·kẹt loading / Yếu tố: Ngưng trệ - Ai gặp: Cả server / Khi nào: Khi đông người, Thỉnh thoảng bất chợt - Phụ trách chính: Phát triển server (Đội phát triển game) / Phối hợp: Hạ tầng mạng (Đội hạ tầng) - Việc cần làm (Đội phát triển game): Đặt timeout cho mọi lời gọi, circuit breaker, cô lập theo từng tính năng (bulkhead), thử lại với khoảng cách tăng dần và giới hạn số lần, tách phần trả lời health check khỏi các tác vụ nặng. - Việc cần làm (Đội hạ tầng): Nới số lần thất bại và khoảng cách health check của bộ cân bằng tải để server chỉ chậm trong chốc lát không bị loại ngay, giới hạn số server bị loại cùng lúc. - Trên đồ thị: Chạm giới hạn rồi đi ngang (Thời gian phản hồi và tỷ lệ lỗi theo service, số thread và kết nối đang dùng) - Chỗ cần xem: Đặt thời gian phản hồi, tỷ lệ lỗi, số lần thử lại của từng service lên cùng một màn hình với trục thời gian khớp nhau, rồi tìm chỗ chậm đi đầu tiên. Nếu nằm sau bộ cân bằng tải: thời gian phản hồi của target (AWS ALB là TargetResponseTime), số lỗi 5xx của target (HTTPCode_Target_5XX_Count), số target bị loại vì không khỏe (UnHealthyHostCount) - Đúng nếu: độ trễ của một service tăng trước, sau đó số thread và kết nối đang dùng ở phía gọi service đó chạm giới hạn, lỗi lan sang service khác, số lần thử lại và số target bị loại cũng tăng theo - Loại trừ nếu: nhiều service cùng chậm đi trong cùng một khoảnh khắc → xem trước sự cố ở tài nguyên dùng chung (DB, mạng, host) - Cách kiểm tra: Kiểm tra bằng công cụ hạ tầng (không cần code game) - Tìm hiểu thêm: Health check (kiểm tra xem server còn sống không) cũng làm sự cố dây chuyền lan rộng hơn. Khi server đang bận trả lời kiểm tra chậm, bộ cân bằng tải loại bỏ luôn server vẫn còn chạy tốt, lưu lượng của nó dồn sang các server còn lại, và server tiếp theo cũng chậm theo. - Sự cố thực tế: riot-edge-2020, riot-euw-2021, roblox-2021, aws-2021, aws-2025 - Nguồn: - [Site Reliability Engineering, Chapter 22: Addressing Cascading Failures](https://sre.google/sre-book/addressing-cascading-failures/) · Google · Server quá tải trượt health check và bị loại thì tải dồn sang các server còn lại, việc thử lại làm tải tăng thêm; khuyến nghị giới hạn số lần thử lại, exponential backoff ngẫu nhiên và deadline - [Circuit Breaker Pattern](https://learn.microsoft.com/en-us/azure/architecture/patterns/circuit-breaker) · Microsoft Azure · Yêu cầu bị kẹt cho tới timeout chiếm giữ thread và kết nối DB, làm cả những tính năng không liên quan thất bại theo; khi lỗi tích lại trong một khoảng thời gian định trước thì từ chối lời gọi ngay - [Timeouts, retries, and backoff with jitter](https://d1.awsstatic.com/builderslibrary/pdfs/timeouts-retries-and-backoff-with-jitter.pdf) · AWS · Amazon Builders' Library. Chuỗi gọi 5 tầng mà mỗi tầng thử lại 3 lần thì tải lên DB tăng 243 lần; chỉ thử lại ở một chỗ và giới hạn bằng token bucket - [CloudWatch metrics for your Application Load Balancer](https://docs.aws.amazon.com/elasticloadbalancing/latest/application/load-balancer-cloudwatch-metrics.html) · AWS · TargetResponseTime (thời gian từ khi yêu cầu rời bộ cân bằng tải đến khi target bắt đầu phản hồi), HTTPCode_Target_5XX_Count (số lỗi 5xx do target trả về), UnHealthyHostCount (số target không khỏe) #### in-subservice · Sự cố server phụ trợ · Auxiliary service outage Khi một server chạy tách riêng khỏi server game như chat, tổ đội, nhà đấu giá gặp sự cố, chỉ riêng tính năng đó ngừng hoạt động. - Vì sao → Dẫn đến → Trên màn hình: Server dành riêng cho một tính năng bị chậm hoặc chết → Chỉ yêu cầu của tính năng đó không có phản hồi → Không chat được, mời tổ đội không phản hồi, sàn giao dịch loading mãi (chiến đấu vẫn bình thường) - Triệu chứng: Nuốt thao tác·rollback, Không vào được·kẹt loading / Yếu tố: Ngưng trệ, Mất gói - Ai gặp: Chỉ một tính năng / Khi nào: Thỉnh thoảng bất chợt, Khi đông người - Phụ trách chính: Phát triển server (Đội phát triển game) / Phối hợp: Hạ tầng server (Đội hạ tầng) - Việc cần làm (Đội phát triển game): Thiết kế để game vẫn tiếp tục khi tính năng lỗi, hiển thị trạng thái từng tính năng, không dồn nhiều tính năng vào một server trung tâm. - Việc cần làm (Đội hạ tầng): Health check và cảnh báo cho từng server phụ trợ, dự phòng (redundancy) và tự khởi động lại. - Trên đồ thị: Rớt kết nối hàng loạt (Tỷ lệ yêu cầu thành công theo tính năng, số kết nối và health check của server phụ trợ) - Chỗ cần xem: Health check, trạng thái tiến trình và số kết nối của từng server phụ trợ như chat, tổ đội, nhà đấu giá; tỷ lệ thành công và thời gian phản hồi theo tính năng. Nếu nằm sau bộ cân bằng tải: UnHealthyHostCount của target group - Đúng nếu: chỉ server phụ trách tính năng bị báo lỗi có health check thất bại hoặc số kết nối tụt mạnh, còn tick và chiến đấu trên server game vẫn bình thường - Loại trừ nếu: nhiều tính năng cùng đứng một lúc → xem server trung tâm chuyển tiếp chung các tính năng đó hoặc sự cố dây chuyền - Cách kiểm tra: Kiểm tra bằng công cụ hạ tầng (không cần code game) - Tìm hiểu thêm: Nếu cấu trúc có một server trung tâm (server world hoặc server manager) chuyển tiếp mọi thứ như tổ đội, guild, tin nhắn riêng, di chuyển giữa các server, thì chỉ cần server đó chậm là nhiều tính năng cùng đứng lại một lúc. - Nguồn: - [Bulkhead Pattern](https://learn.microsoft.com/en-us/azure/architecture/patterns/bulkhead) · Microsoft Azure · Cô lập các thành phần vào pool riêng thì khi một thành phần hỏng, phần còn lại vẫn chạy và sự cố không lan ra - [REL05-BP01 Implement graceful degradation to transform applicable hard dependencies into soft dependencies](https://docs.aws.amazon.com/wellarchitected/latest/reliability-pillar/rel_mitigate_interaction_failure_graceful_degradation.html) · AWS · AWS Well-Architected. Thiết kế để khi thành phần phụ thuộc gặp sự cố, tính năng cốt lõi vẫn chạy tiếp bằng dữ liệu hơi cũ hoặc dữ liệu thay thế - [CloudWatch metrics for your Network Load Balancer](https://docs.aws.amazon.com/elasticloadbalancing/latest/network/load-balancer-cloudwatch-metrics.html) · AWS · UnHealthyHostCount: số target bị health check xác định là không khỏe #### in-deploy · Triển khai và khởi động lại · Deploy / rolling restart Khi khởi động lại server để cập nhật mà không chuyển kết nối đi nơi khác, những người đang ở server đó bị mất kết nối, đồng thời việc lưu dữ liệu ngay trước khi tắt và việc kết nối lại dồn vào cùng một lúc. - Vì sao → Dẫn đến → Trên màn hình: Triển khai hotfix, khởi động lại lần lượt từng server → Tắt server mà không chuyển kết nối sang server khác, dữ liệu cần lưu của mọi người chơi trên server đó dồn vào DB → Mất kết nối không có thông báo trước, lượng kết nối lại tăng đột biến - Triệu chứng: Mất kết nối, Không vào được·kẹt loading, Trễ thao tác / Yếu tố: Ngưng trệ - Ai gặp: Cả server, Một địa điểm hoặc kênh / Khi nào: Thỉnh thoảng bất chợt, Ngay sau đăng nhập hoặc bảo trì - Phụ trách chính: Phát triển server (Đội phát triển game) / Phối hợp: Hạ tầng server (Đội hạ tầng) - Việc cần làm (Đội phát triển game): Tính năng drain (chỉ chặn kết nối mới và chờ đến khi người đang chơi thoát ra), chuyển nhân vật sang server khác, chia nhỏ việc lưu trước khi tắt, sau khi khởi động lại thì tải cache và JIT warm-up xong mới báo sẵn sàng, hot reload thì đọc trước ở thread riêng rồi thay một lượt giữa hai tick. - Việc cần làm (Đội hạ tầng): Công cụ triển khai chờ drain từng máy xong rồi mới khởi động lại, server vừa khởi động lại chỉ nhận lưu lượng sau khi xác nhận đã sẵn sàng (làm nóng xong), thông báo trước giờ triển khai. - Con số tham khảo: Một server có 5.000 người thì trong vài giây trước khi tắt, 5.000 lượt lưu dồn vào DB. - Trên đồ thị: Rớt kết nối hàng loạt (Số kết nối theo server, số lượt ghi DB) - Chỗ cần xem: Chồng nhật ký tác vụ của công cụ triển khai (thời điểm khởi động lại từng server) lên đồ thị số kết nối, số lần mất kết nối, ghi DB, yêu cầu đăng nhập dưới dạng vạch dọc (annotation) - Đúng nếu: số kết nối của từng server lần lượt tụt mạnh đúng thời điểm khởi động lại, ngay trước đó lượt ghi DB vọt lên, ngay sau đó yêu cầu đăng nhập vọt lên - Loại trừ nếu: thời điểm mất kết nối không trùng với nhật ký triển khai hay khởi động lại → xem server crash hoặc thiết bị mạng - Cách kiểm tra: Kiểm tra bằng công cụ hạ tầng (không cần code game) - Tìm hiểu thêm: Vài phút ngay sau khi bật lại server cũng chậm. Cache còn trống nên truy vấn DB dồn dập, còn server Java, C# chưa xong quá trình tối ưu code trong lúc chạy (JIT warm-up) nên cùng một việc lại tốn nhiều thời gian hơn. Cách đọc lại script và bảng dữ liệu mà không tắt server (hot reload) cũng làm tick dừng trong lúc đọc, gây đứng hình chốc lát. - Nguồn: - [Site Reliability Engineering, Chapter 20: Load Balancing in the Datacenter](https://sre.google/sre-book/load-balancing-datacenter/) · Google · Server nhận SIGTERM chuyển sang trạng thái lame duck, đẩy yêu cầu mới sang server khác và chỉ xử lý nốt các yêu cầu đang dở; vài phút ngay sau khi khởi động lại, server chưa được JIT tối ưu nên tốn tài nguyên hơn, vì vậy cho server làm nóng xong rồi mới nhận lưu lượng - [Liveness, Readiness, and Startup Probes](https://kubernetes.io/docs/concepts/workloads/pods/probes/) · Kubernetes · Dùng readiness probe để không gửi lưu lượng cho tới khi thiết lập kết nối, tải file và làm nóng cache xong - [Edit target group attributes for your Network Load Balancer](https://docs.aws.amazon.com/elasticloadbalancing/latest/network/edit-target-group-attributes.html) · AWS · Khi hủy đăng ký target thì không gửi kết nối mới tới đó, còn kết nối cũ được drain (mặc định 300 giây) #### in-autoscale · Autoscaling chậm · Autoscaling lag Khi đông người, số server được tự động tăng lên nhưng phải mất vài phút mới sẵn sàng, và trong thời gian đó các server hiện có bị quá tải. - Vì sao → Dẫn đến → Trên màn hình: Sự kiện bắt đầu làm lượng kết nối tăng vọt → Mất vài phút để server mới bật lên và sẵn sàng → Vài phút ngay sau khi sự kiện bắt đầu bị quay chậm, không vào được - Triệu chứng: Quay chậm, Không vào được·kẹt loading / Yếu tố: Ngưng trệ - Ai gặp: Cả server / Khi nào: Khi đông người, Ngay sau đăng nhập hoặc bảo trì - Phụ trách chính: Hạ tầng server (Đội hạ tầng) / Phối hợp: Phát triển server (Đội phát triển game) - Việc cần làm (Đội phát triển game): Phân tán theo kênh (người đang ở kênh đã đông thì không thể chuyển sang server mới), rút ngắn thời gian khởi động và tải dữ liệu của server mới. - Việc cần làm (Đội hạ tầng): Mở rộng trước khi sự kiện bắt đầu, chuẩn bị server dự phòng đã làm nóng, khi thu nhỏ thì chờ người còn lại thoát hết rồi mới tắt. - Con số tham khảo: Mất 1 đến vài phút để phát hiện tải (vì chỉ số được lấy trung bình trong vài phút), rồi thêm vài phút nữa để bật server mới, đọc dữ liệu game và làm đầy cache. - Trên đồ thị: Tăng vọt ngay sau đăng nhập hoặc bảo trì (Số instance, mức sử dụng CPU, hàng chờ đăng nhập) - Chỗ cần xem: Chồng nhật ký hoạt động autoscaling (thời điểm quyết định mở rộng, thời điểm instance mới vào phục vụ) lên đồ thị mức sử dụng CPU và số kết nối. Với AWS: chỉ số của Auto Scaling group (phải bật mới thấy) GroupDesiredCapacity (số máy mục tiêu), GroupPendingInstances (đang chuẩn bị), GroupInServiceInstances (đang phục vụ) - Đúng nếu: sau khi kết nối tăng vọt, trong vài phút chỉ có số máy mục tiêu và số instance đang chuẩn bị tăng, CPU của các server hiện có bám sát giới hạn, rồi tình trạng được giải tỏa từ lúc số instance đang phục vụ tăng lên - Loại trừ nếu: instance mới đã vào phục vụ mà vẫn chậm → nguyên nhân nằm ngoài số lượng server (tài nguyên dùng chung như DB, sự cố dây chuyền) - Cách kiểm tra: Kiểm tra bằng công cụ hạ tầng (không cần code game) - Tìm hiểu thêm: Autoscaling chủ yếu dùng cho những chỗ chỉ cần đưa người mới vào server mới, như đăng nhập, gateway, phó bản. Thu nhỏ cũng gây rắc rối. Nếu lúc rạng sáng vắng người mà giảm server và tắt ngay, không chờ những người còn lại thoát ra, thì những người đó bị mất kết nối. - Sự cố thực tế: aws-2021, aws-2025 - Nguồn: - [Target tracking scaling policies for Amazon EC2 Auto Scaling](https://docs.aws.amazon.com/autoscaling/ec2/userguide/as-scaling-target-tracking.html) · AWS · Chỉ số cơ bản của EC2 có khoảng cách 5 phút (bật giám sát chi tiết thì 1 phút), nên muốn phản ứng nhanh thì khuyến nghị dùng chỉ số có khoảng cách 1 phút trở xuống - [Amazon CloudWatch metrics for Amazon EC2 Auto Scaling](https://docs.aws.amazon.com/autoscaling/ec2/userguide/ec2-auto-scaling-metrics.html) · AWS · Chỉ số của group phải bật mới được công bố, với chu kỳ 1 phút; GroupDesiredCapacity (số máy muốn duy trì), GroupPendingInstances (số instance chưa vào phục vụ), GroupInServiceInstances (số instance đang phục vụ) - [Scheduled scaling for Amazon EC2 Auto Scaling](https://docs.aws.amazon.com/autoscaling/ec2/userguide/ec2-auto-scaling-scheduled-scaling.html) · AWS · Tăng và giảm dung lượng trước vào thời điểm định sẵn, theo những thay đổi tải có thể dự đoán được - [Decrease latency for applications with long boot times using warm pools](https://docs.aws.amazon.com/autoscaling/ec2/userguide/ec2-auto-scaling-warm-pools.html) · AWS · Ứng dụng khởi động lâu thì giảm độ trễ mở rộng bằng pool instance đã khởi tạo sẵn (warm pool) #### in-monitoring · Quá tải log và giám sát · Logging / monitoring overhead Khi có sự cố, log tăng đột biến, và server gửi log theo kiểu đồng bộ lại càng chậm hơn vì chính log. - Vì sao → Dẫn đến → Trên màn hình: Lỗi xảy ra làm lượng log và chỉ số gửi đi tăng đột biến → Bộ thu thập log bị dồn việc, server gửi đồng bộ phải chờ → Khi có sự cố, giật khựng và đứng hình càng nặng hơn vì log - Triệu chứng: Giật khựng, Đứng hình / Yếu tố: Ngưng trệ - Ai gặp: Cả server / Khi nào: Khi đông người, Thỉnh thoảng bất chợt - Phụ trách chính: Phát triển server (Đội phát triển game) / Phối hợp: Hạ tầng server (Đội hạ tầng) - Việc cần làm (Đội phát triển game): Gửi bất đồng bộ, lấy mẫu, bộ đệm đầy thì bỏ bớt, gộp các log cùng một lỗi rồi gửi một lần. - Việc cần làm (Đội hạ tầng): Chuẩn bị dung lượng bộ thu thập log theo mức tăng đột biến khi có sự cố, cảnh báo khi bộ thu thập bị dồn việc. - Trên đồ thị: Thỉnh thoảng vọt lên bất chợt (Lượng log gửi đi, hàng đợi của bộ thu thập log) - Chỗ cần xem: Số dòng log và số byte mỗi giây của server, hàng đợi và số bản ghi bị bỏ của agent thu thập log, xem cùng với thời gian tick. Nếu có thread bị dừng: dùng bcc offcputime -p để xem thread có đang chờ ghi hoặc gửi log không - Đúng nếu: lúc tick vọt lên, lượng log tăng vọt gấp vài chục lần bình thường, và thời gian chờ của thread game dồn vào call stack ghi hoặc gửi log - Loại trừ nếu: lượng log vẫn như bình thường hoặc thread game không chờ ở phía log → log tăng đột biến chỉ là hệ quả của sự cố, cần tìm riêng nguyên nhân gây ra lỗi đầu tiên - Cách kiểm tra: Kiểm tra bằng công cụ hạ tầng (không cần code game) - Nguồn: - [Logging in C#](https://learn.microsoft.com/en-us/dotnet/core/extensions/logging) · Microsoft · Phương thức ghi log của .NET là đồng bộ, nên nếu nơi lưu chậm thì khuyến nghị ghi trước vào nơi lưu nhanh rồi chuyển sau, thay vì ghi thẳng vào đó - [Asynchronous loggers](https://logging.apache.org/log4j/2.x/manual/async.html) · Apache Software Foundation · Ghi log bất đồng bộ hấp thụ các đợt tăng ngắn bằng hàng đợi, nhưng nếu đầu ra chậm kéo dài thì hàng đợi đầy, rồi tốc độ tụt xuống bằng đầu ra chậm nhất hoặc log bị bỏ tùy theo chính sách (Discard) - [Demonstrations of offcputime, the Linux eBPF/bcc version](https://raw.githubusercontent.com/iovisor/bcc/master/tools/offcputime_example.txt) · IO Visor · Cộng dồn thời gian thread bị dừng và rời CPU (off-CPU) theo từng call stack, -p để chỉ định tiến trình #### in-clock-skew · Lệch đồng hồ giữa các server · Clock skew between servers Nếu đồng hồ của các server lệch nhau một chút, việc phán định hồi chiêu, buff, thời điểm bắt đầu sự kiện sẽ không khớp giữa các server. - Vì sao → Dẫn đến → Trên màn hình: Đồng hồ của server bị ngừng đồng bộ thời gian lệch với server khác từ vài trăm ms đến vài giây → Truyền thời điểm tuyệt đối như lúc buff hết hạn giữa các server thì phán định bị lệch → Di chuyển sang nơi khác thì buff biến mất hoặc hồi chiêu chạy lại từ đầu - Triệu chứng: Nuốt thao tác·rollback / Yếu tố: Độ trễ - Ai gặp: Chỉ mình tôi / Khi nào: Khi di chuyển hoặc chuyển bản đồ - Phụ trách chính: Hạ tầng server (Đội hạ tầng) / Phối hợp: Phát triển server (Đội phát triển game) - Việc cần làm (Đội phát triển game): Giữa các server, truyền thời gian còn lại thay cho thời điểm tuyệt đối. - Việc cần làm (Đội hạ tầng): Giám sát đồng bộ thời gian (NTP, chrony), cảnh báo độ lệch đồng hồ giữa các server. - Con số tham khảo: Khi đồng bộ thời gian (NTP, chrony) hoạt động bình thường, các server trong cùng một trung tâm dữ liệu thường lệch nhau trong vòng vài ms. Khi đồng bộ bị ngừng hoặc server ảo bị dừng lâu rồi chạy lại, độ lệch tăng lên vài trăm ms đến vài giây. - Trên đồ thị: Tăng dần (Độ lệch (offset) đồng hồ theo server) - Chỗ cần xem: Thu thập và so sánh System time (chênh lệch giữa đồng hồ hệ thống và đồng hồ NTP), Last offset và Ref time (thời điểm lần cuối áp dụng giá trị đo từ nguồn thời gian) trong chronyc tracking của từng server - Đúng nếu: offset của server có vấn đề lệch hơn các server khác từ vài trăm ms trở lên hoặc Ref time đã dừng từ lâu, và việc phán định bị lệch chỉ xảy ra khi di chuyển qua lại server đó - Loại trừ nếu: offset của mọi server đều trong vòng vài ms → xem cách tính thời gian phía game hoặc sai số đồng bộ đồng hồ của client - Cách kiểm tra: Kiểm tra bằng công cụ hạ tầng (không cần code game) - Tìm hiểu thêm: Trường hợp đồng hồ của một server nhảy tới hoặc lùi trong một lần được trình bày ở “Đồng hồ hệ thống nhảy (NTP step)” thuộc tầng OS server. - Nguồn: - [RFC 5905: Network Time Protocol Version 4: Protocol and Algorithms Specification](https://www.rfc-editor.org/rfc/rfc5905) · IETF · NTP client trong mạng LAN tốc độ cao thường khớp trong vòng vài trăm µs - [chrony – Frequently Asked Questions](https://chrony-project.org/faq.html) · chrony · Độ trôi (drift) của đồng hồ máy tính thông thường dưới 100 ppm nhưng máy ảo có thể lớn hơn; máy ảo bị tạm dừng rồi chạy lại có thể bị lệch giờ và cần hiệu chỉnh kiểu step - [clock_gettime(2) — Linux manual page](https://man7.org/linux/man-pages/man2/clock_gettime.2.html) · Linux man-pages · CLOCK_REALTIME có thể nhảy không liên tục do đổi giờ thủ công hoặc hiệu chỉnh NTP, còn CLOCK_MONOTONIC không bị ảnh hưởng bởi các bước nhảy đó - [chronyc(1)](https://chrony-project.org/doc/4.6/chronyc.html) · chrony · System time (chênh lệch giữa đồng hồ NTP và đồng hồ hệ thống), Last offset (offset ước tính ở lần hiệu chỉnh gần nhất), Ref time (thời điểm áp dụng giá trị đo cuối cùng từ nguồn thời gian) trong chronyc tracking #### in-bots · Quá nhiều macro và bot · Bots and macros Bot gửi yêu cầu thường xuyên hơn người thật rất nhiều, dần chiếm mất năng lực xử lý của server. - Vì sao → Dẫn đến → Trên màn hình: Lượng lớn bot kết nối, lặp đi lặp lại săn quái, di chuyển, giao dịch không nghỉ → Khối lượng xử lý trên server và tải DB tăng → Một bãi săn cụ thể hoặc cả server chậm đi (quay chậm, trễ thao tác) - Triệu chứng: Quay chậm, Trễ thao tác / Yếu tố: Ngưng trệ - Ai gặp: Cả server, Một địa điểm hoặc kênh / Khi nào: Luôn luôn, Giờ cao điểm buổi tối - Phụ trách chính: Phát triển server (Đội phát triển game) / Phối hợp: Hạ tầng mạng (Đội hạ tầng) - Việc cần làm (Đội phát triển game): Phát hiện bot, giới hạn tần suất yêu cầu theo tài khoản và nhân vật. - Việc cần làm (Đội hạ tầng): Giới hạn tần suất kết nối và yêu cầu theo IP (nới rộng cho quán net và mạng di động vì nhiều người dùng chung một IP), chặn dải IP của bot bằng tường lửa hoặc WAF. - Trên đồ thị: Chỉ một phần cao (Số yêu cầu mỗi giây theo tài khoản và IP) - Chỗ cần xem: Dùng log server game để xem phân bố và danh sách top số yêu cầu mỗi giây theo tài khoản, nhân vật. Không có chỉ số trong code thì xem số yêu cầu theo IP trên tường lửa hoặc WAF - Đúng nếu: một số ít tài khoản hoặc IP gửi yêu cầu liên tục với tần suất mà người thật không thể đạt tới, và khi giới hạn nhóm này thì tải server giảm rõ rệt - Loại trừ nếu: yêu cầu trải đều trên các tài khoản → số người chơi tăng bình thường (vượt tick budget, autoscaling chậm) - Cách kiểm tra: Cần log và chỉ số của server, client game - Nguồn: - [Using rate-based rule statements in AWS WAF](https://docs.aws.amazon.com/waf/latest/developerguide/waf-rule-statement-type-rate-based.html) · AWS · Đếm số yêu cầu theo tiêu chí như IP, nếu quá nhiều trong một khung thời gian định sẵn thì giới hạn tốc độ - [RFC 6269: Issues with IP Address Sharing](https://www.rfc-editor.org/rfc/rfc6269) · IETF · Khi nhiều thuê bao dùng chung một IP, chặn hoặc giới hạn theo IP sẽ chặn luôn cả những người dùng khác #### in-external · Phụ thuộc service bên ngoài · External dependencies (auth, billing, platform) Khi service bên ngoài như đăng nhập qua nền tảng, thanh toán, xác thực danh tính chậm hoặc ngừng, người chơi bị kẹt ở đúng bước đó. - Vì sao → Dẫn đến → Trên màn hình: Service xác thực hoặc thanh toán bên ngoài gặp sự cố hoặc chậm → Chờ phản hồi ở bước đó → Không đăng nhập được, thanh toán thất bại. Người đang chơi thì vẫn bình thường - Triệu chứng: Không vào được·kẹt loading, Nuốt thao tác·rollback / Yếu tố: Ngưng trệ - Ai gặp: Cả server, Chỉ một tính năng / Khi nào: Ngay sau đăng nhập hoặc bảo trì, Khi làm một thao tác nhất định - Phụ trách chính: Bên ngoài (Bên ngoài) / Phối hợp: Phát triển server (Đội phát triển game) - Việc cần làm (Đội phát triển game): Đặt timeout cho lời gọi ra bên ngoài kèm thông báo dễ hiểu, cache kết quả xác thực, quy trình thử lại và bồi hoàn khi thanh toán lỗi. - Việc cần làm (Bên ngoài): Yêu cầu nhà cung cấp dịch vụ xác thực, thanh toán, nền tảng xác nhận sự cố và khôi phục, thông báo cho người chơi rằng đây là sự cố của service bên ngoài. - Trên đồ thị: Tăng như bậc thang từ một thời điểm (Thời gian phản hồi và tỷ lệ lỗi của lời gọi ra bên ngoài, số lần đăng nhập thành công) - Chỗ cần xem: Thời gian phản hồi, tỷ lệ lỗi, số lần timeout theo từng lời gọi ra bên ngoài như đăng nhập nền tảng, thanh toán, xác thực danh tính, và trang trạng thái của nhà cung cấp - Đúng nếu: từ lúc đăng nhập hoặc thanh toán thất bại dồn lại, chỉ lỗi và timeout của một lời gọi bên ngoài cụ thể tăng lên một bậc rồi giữ nguyên, và trang trạng thái của nhà cung cấp có sự cố vào cùng thời điểm - Loại trừ nếu: lời gọi bên ngoài bình thường mà vẫn không đăng nhập được → xem chính server đăng nhập (cạn thread pool, DB) hoặc hàng chờ kết nối (backlog) của hệ điều hành - Cách kiểm tra: Cần log và chỉ số của server, client game - Sự cố thực tế: fastly-2021, aws-2021, aws-2025 - Nguồn: - [Timeouts, retries, and backoff with jitter](https://d1.awsstatic.com/builderslibrary/pdfs/timeouts-retries-and-backoff-with-jitter.pdf) · AWS · Amazon Builders' Library. Trong lúc chờ phản hồi, tài nguyên như thread, kết nối bị chiếm giữ nên cần đặt timeout; API có tác dụng phụ chỉ nên thử lại khi đã bảo đảm tính idempotent - [Circuit Breaker Pattern](https://learn.microsoft.com/en-us/azure/architecture/patterns/circuit-breaker) · Microsoft Azure · Lời gọi có khả năng thất bại cao thì từ chối ngay, không chờ đến timeout, để giữ được thời gian phản hồi - [REL05-BP01 Implement graceful degradation to transform applicable hard dependencies into soft dependencies](https://docs.aws.amazon.com/wellarchitected/latest/reliability-pillar/rel_mitigate_interaction_failure_graceful_degradation.html) · AWS · AWS Well-Architected. Dù service phụ thuộc gặp sự cố vẫn duy trì tính năng cốt lõi, kể cả bằng dữ liệu hơi cũ (cơ sở cho việc cache kết quả xác thực) #### in-region-match · Lỗi matchmaking hoặc phân bổ region · Wrong region assignment (matchmaking / GeoDNS) Nếu bị xếp vào server ở region xa thay vì region gần, dù đường truyền vẫn ổn, riêng người chơi đó luôn có ping cao. - Vì sao → Dẫn đến → Trên màn hình: Dữ liệu GeoIP sai, VPN, xếp cả tổ đội theo ping trung bình của các thành viên, quy tắc mở rộng sang region xa khi thiếu người, phân bổ theo vị trí của DNS resolver → Kết nối vào server ở region bên kia đại dương dù có region gần hơn → Trong game đặt server ở nhiều region, chỉ riêng bạn (hoặc chỉ tổ đội của bạn) luôn có ping cao, bị trễ thao tác, kéo ngược, nuốt skill - Triệu chứng: Trễ thao tác, Kéo ngược, Nuốt thao tác·rollback / Yếu tố: Độ trễ - Ai gặp: Chỉ mình tôi, Một khu vực hoặc nhà mạng / Khi nào: Luôn luôn, Ngay sau đăng nhập hoặc bảo trì - Phụ trách chính: Phát triển server (Đội phát triển game) / Phối hợp: Phát triển client (Đội phát triển game), Hạ tầng mạng (Đội hạ tầng), Bên ngoài (Bên ngoài) - Việc cần làm (Đội phát triển game): Server: phân bổ theo ping tới từng region do client đo thay cho GeoIP, đặt giới hạn ping tối đa cho quy tắc mở rộng sang region xa, với tổ đội thì xem cả ping cao nhất trong tổ đội bên cạnh ping trung bình, ghi log region đã phân bổ và ping lúc đó. Client: đo ping tới từng region bằng UDP rồi gửi kèm yêu cầu ghép trận, hiển thị region đang kết nối và ping trên màn hình, cho phép người chơi tự chọn region. - Việc cần làm (Đội hạ tầng): Nếu chọn region bằng DNS: kiểm tra DNS có thẩm quyền (authoritative DNS) có hỗ trợ EDNS Client Subnet không (nếu resolver người chơi dùng không gửi thông tin này thì sẽ phân bổ theo vị trí resolver), cập nhật cơ sở dữ liệu GeoIP định kỳ, gắn quốc gia và ASN theo GeoIP vào log kết nối của server từng region để tìm các quốc gia, nhà mạng đang bị đưa sang region xa. - Việc cần làm (Bên ngoài): Hướng dẫn người chơi tắt VPN, phần mềm tăng tốc game rồi kết nối lại, hướng dẫn người chơi đang dùng DNS công ty hoặc DNS nước ngoài đổi sang DNS của nhà mạng, yêu cầu nhà cung cấp GeoIP sửa vị trí sai. - Con số tham khảo: Người chơi ở Seoul bị xếp vào region miền Tây nước Mỹ thay vì Tokyo thì ping tăng từ khoảng 30 ms lên khoảng 130 ms. GeoIP đúng khoảng 99,8% ở cấp quốc gia, nhưng ở cấp thành phố thì ngay cả tại Mỹ, tỷ lệ nằm trong phạm vi 50 km cũng chỉ khoảng 66%, và khi người chơi dùng VPN thì GeoIP trả về vị trí của server VPN thay cho vị trí người chơi. - Trên đồ thị: Chỉ một phần cao (RTT (ping) theo người chơi, phân bố region được phân bổ) - Chỗ cần xem: Gắn quốc gia và ASN theo GeoIP vào IP client trong nhật ký kết nối của server từng region (access log của bộ cân bằng tải, VPC flow log), rồi đếm xem mỗi quốc gia, nhà mạng kết nối vào region nào. Nếu chỉ có một người chơi: so sánh region người đó thực sự kết nối với ping đo tới region gần (người chơi tự đo, hoặc chạy mtr từ server ở region đó tới IP người chơi) - Đúng nếu: người chơi hoặc quốc gia có RTT cao đang kết nối vào region xa thay vì region gần, trong khi ping đo tới region gần thì thấp - Loại trừ nếu: đã được phân bổ đúng vào region gần mà ping vẫn cao → xem định tuyến đi đường vòng hoặc đường truyền, Wi-Fi của người chơi đó - Cách kiểm tra: Kiểm tra bằng công cụ hạ tầng (không cần code game) - Tìm hiểu thêm: Cách chọn region bằng DNS (DNS theo vị trí địa lý hoặc theo độ trễ) đoán vị trí dựa trên địa chỉ của DNS resolver mà người chơi dùng, thay vì địa chỉ của chính người chơi. Nếu resolver không hỗ trợ EDNS Client Subnet (cơ chế chuyển tiếp một phần địa chỉ người chơi), người chơi dùng DNS công ty hoặc DNS ở xa sẽ được phân bổ theo nơi đặt resolver. Hệ thống ghép trận cũng có thể đánh giá tổ đội bằng ping trung bình của các thành viên, hoặc nới tiêu chí ping khi chờ lâu rồi xếp vào region xa. Ở AWS GameLift Servers, tiêu chí mặc định cho ping của tổ đội cũng là trung bình, và tài liệu lấy ví dụ cấu hình nới giới hạn ping từ 50 ms lên 100 ms rồi 200 ms. Người chơi bật VPN có thể vừa bị cộng thêm độ trễ do đi qua server trung chuyển (“Đi qua VPN hoặc phần mềm tăng tốc game”) vừa bị xếp vào region xa, nên cần phân biệt bằng cách tắt VPN, kết nối lại và xem region được phân bổ có thay đổi không. Trường hợp không có region nào ở gần nên phải kết nối tới region xa được trình bày ở “Độ trễ lan truyền (khoảng cách vật lý)”. - Nguồn: - [RFC 7871: Client Subnet in DNS Queries](https://www.rfc-editor.org/rfc/rfc7871) · IETF · DNS trả lời khác nhau theo vị trí sẽ đoán vị trí bằng địa chỉ của resolver gửi truy vấn, nên nếu người dùng dùng resolver trung tâm ở xa thì câu trả lời không phù hợp. EDNS Client Subnet (tính năng tùy chọn) chuyển tiếp một phần địa chỉ người dùng - [How Amazon Route 53 uses EDNS0 to estimate the location of a user](https://docs.aws.amazon.com/Route53/latest/DeveloperGuide/routing-policy-edns0.html) · AWS · Nếu resolver không hỗ trợ edns-client-subnet, vị trí người dùng được đoán bằng địa chỉ resolver và câu trả lời dựa theo vị trí resolver (chung cho định tuyến theo vị trí địa lý và theo độ trễ) - [Geolocation accuracy](https://support.maxmind.com/knowledge-base/articles/maxmind-geolocation-accuracy) · MaxMind · Cấp quốc gia khoảng 99,8%, cấp thành phố ở Mỹ (trong phạm vi 50 km) khoảng 66%; dùng VPN thì ra vị trí server VPN thay cho người dùng cuối; IP mạng di động được dùng trên vùng rộng nên không xác định được vị trí chi tiết; cơ sở dữ liệu phải được cập nhật liên tục; có thể yêu cầu sửa - [FlexMatch rule types](https://docs.aws.amazon.com/gameliftservers/latest/flexmatchguide/match-rules-reference-ruletype.html) · AWS · Quy tắc độ trễ (maxLatency) xem độ trễ của người chơi theo từng vị trí, tổ đội mặc định dùng trung bình của các thành viên (partyAggregation avg), hàng chờ có thể xếp cả vào region không thỏa quy tắc độ trễ - [Create a player latency policy](https://docs.aws.amazon.com/gameliftservers/latest/developerguide/queues-design-latency.html) · AWS · Xếp vào vị trí có độ trễ trung bình của mọi người chơi thấp nhất nhưng người chơi có độ trễ cực cao vẫn được xếp vào; ví dụ chính sách nới giới hạn ping từ 50 ms lên 100 ms rồi 200 ms - [Amazon GameLift Servers UDP ping beacons](https://docs.aws.amazon.com/gameliftservers/latest/developerguide/reference-udp-ping-beacons.html) · AWS · Client game đo độ trễ tới endpoint UDP đặt ở từng vị trí hosting rồi dùng để xếp chỗ và ghép trận, gần với lưu lượng game thực hơn ping ICMP - [Azure network round-trip latency statistics](https://learn.microsoft.com/en-us/azure/networking/azure-network-latency) · Microsoft Azure · Trung vị thời gian khứ hồi đo thực tế tính từ Seoul (Korea Central): Tokyo (Japan East) 29 ms, miền Tây nước Mỹ 124–136 ms - [Flow log records](https://docs.aws.amazon.com/vpc/latest/userguide/flow-log-records.html) · AWS · srcaddr trong bản ghi VPC flow log: với lưu lượng đi vào là địa chỉ IP bên gửi #### in-cert · Chứng chỉ TLS hết hạn hoặc cấu hình sai · TLS certificate expiry / misconfiguration Khi chứng chỉ của server đăng nhập, API hoặc server patch hết hạn hoặc thiếu chứng chỉ trung gian, kết nối TLS của các client kết nối mới sẽ thất bại kể từ thời điểm đó. - Vì sao → Dẫn đến → Trên màn hình: Chứng chỉ đã quá hạn hiệu lực, server gửi thiếu chứng chỉ trung gian, hoặc ngày giờ trên thiết bị của người chơi bị sai → Client xác minh chứng chỉ thất bại và cắt kết nối TLS → Không vào được, kẹt loading ở bước đăng nhập hoặc patch, chỉ các tính năng HTTPS như cửa hàng bị lỗi. Người đã vào game từ trước thường vẫn bình thường - Triệu chứng: Không vào được·kẹt loading, Nuốt thao tác·rollback / Yếu tố: Ngưng trệ - Ai gặp: Cả server, Chỉ một tính năng, Chỉ mình tôi / Khi nào: Ngay sau đăng nhập hoặc bảo trì, Khi làm một thao tác nhất định - Phụ trách chính: Hạ tầng mạng (Đội hạ tầng) / Phối hợp: Hạ tầng server (Đội hạ tầng), Phát triển client (Đội phát triển game) - Việc cần làm (Đội phát triển game): Ghi lỗi chứng chỉ bằng mã lỗi riêng, tách khỏi các lỗi kết nối khác, kèm hướng dẫn; nếu do sai ngày thì hướng dẫn người chơi bật tự động đặt ngày giờ trên thiết bị; nếu dùng certificate pinning thì nhúng kèm khóa dự phòng và thống nhất lịch thay chứng chỉ với đội hạ tầng. - Việc cần làm (Đội hạ tầng): Mạng: nếu kết thúc TLS ở bộ cân bằng tải hoặc CDN thì cảnh báo trạng thái tự động gia hạn và số ngày còn lại của chứng chỉ được quản lý (ACM là DaysToExpiry), giữ nguyên bản ghi DNS dùng để xác minh. Thiết bị server·OS: nếu kết thúc TLS trên server thì tự động hóa việc gia hạn và đọc lại cấu hình sau khi gia hạn, cấu hình chuỗi chứng chỉ có cả chứng chỉ trung gian, định kỳ kiểm tra từ bên ngoài thời hạn còn lại của từng địa chỉ đăng nhập, API, patch rồi cảnh báo. - Con số tham khảo: Chứng chỉ Let’s Encrypt có hạn 90 ngày nên được khuyến nghị gia hạn mỗi 60 ngày, còn AWS Certificate Manager kiểm tra chứng chỉ đã xác minh bằng DNS từ 45 ngày trước khi hết hạn rồi tự động gia hạn. Nếu việc tự động gia hạn thất bại âm thầm, đúng thời điểm hết hạn mọi kết nối mới sẽ bị chặn cùng một lúc. - Trên đồ thị: Rớt kết nối hàng loạt (Số lần đăng nhập thành công, số lỗi TLS handshake) - Chỗ cần xem: Dùng openssl s_client -connect HOST:443 -showcerts để xem danh sách chứng chỉ server thực sự gửi, rồi kiểm tra ngày hết hạn (notAfter) của từng chứng chỉ bằng openssl x509 -noout -enddate. Nếu kết thúc TLS ở bộ cân bằng tải: số lỗi thương lượng TLS (AWS ALB, NLB là ClientTLSNegotiationErrorCount) và số lần đăng nhập thành công - Đúng nếu: ngày hết hạn đã qua hoặc danh sách server gửi thiếu chứng chỉ trung gian, và thời điểm lỗi bắt đầu tăng trùng với lúc hết hạn hoặc lúc thay chứng chỉ - Loại trừ nếu: danh sách chứng chỉ và ngày hết hạn bình thường nhưng chỉ một số người chơi thất bại → xem ngày giờ trên thiết bị của những người đó hoặc danh sách chứng chỉ gốc của OS cũ - Cách kiểm tra: Kiểm tra bằng công cụ hạ tầng (không cần code game) - Tìm hiểu thêm: Cấu hình thiếu chứng chỉ trung gian có thể trông vẫn bình thường khi mở bằng trình duyệt trên PC. Trình duyệt nhớ các chứng chỉ trung gian đã nhận từ trang khác để bù vào chỗ thiếu, nhưng những client không có bộ nhớ như vậy, chẳng hạn ứng dụng Android, sẽ thất bại. Thời hạn hiệu lực cũng đang ngắn lại. Let’s Encrypt dự kiến rút thời hạn mặc định xuống 64 ngày vào năm 2027 và 45 ngày vào năm 2028, nên cấu hình cố định gia hạn mỗi 60 ngày chỉ còn dư 4 ngày với chứng chỉ 64 ngày, và sẽ để chứng chỉ 45 ngày hết hạn trước khi kịp gia hạn. AWS Certificate Manager cũng không tự động gia hạn chứng chỉ được nhập vào (import), và nếu xóa bản ghi DNS dùng để xác minh thì việc gia hạn sẽ thất bại. Biểu hiện không đăng nhập được trông giống “DNS lỗi hoặc chậm”, nhưng lỗi chứng chỉ xảy ra ở bước TLS handshake sau khi đã tìm được địa chỉ server, và thời điểm bắt đầu trùng với lúc chứng chỉ hết hạn hoặc lúc thay chứng chỉ. - Nguồn: - [RFC 5280: Internet X.509 Public Key Infrastructure Certificate and Certificate Revocation List (CRL) Profile](https://www.rfc-editor.org/rfc/rfc5280) · IETF · Thời hạn hiệu lực của chứng chỉ tính từ notBefore đến notAfter; khi xác minh đường dẫn, từng chứng chỉ trong chuỗi đều được kiểm tra xem thời điểm hiện tại có nằm trong thời hạn không (đồng hồ của bên xác minh sai thì thất bại) - [FAQ](https://letsencrypt.org/docs/faq/) · Let's Encrypt · Thời hạn chứng chỉ mặc định 90 ngày, khuyến nghị gia hạn mỗi 60 ngày - [Decreasing Certificate Lifetimes to 45 Days](https://letsencrypt.org/2025/12/02/from-90-to-45) · Let's Encrypt · Rút thời hạn mặc định xuống 64 ngày vào tháng 2 năm 2027 và 45 ngày vào tháng 2 năm 2028; gia hạn theo khoảng cố định 60 ngày không còn đủ nên khuyến nghị gia hạn ở khoảng 2/3 thời hạn - [Renewal for domains validated by DNS](https://docs.aws.amazon.com/acm/latest/userguide/dns-renewal-validation.html) · AWS · Từ 45 ngày trước khi hết hạn, kiểm tra chứng chỉ có đang được dùng trong service AWS không và bản ghi CNAME dùng để xác minh còn không, rồi tự động gia hạn; nếu không xác minh được thì gửi thông báo ở các mốc 30 ngày, 15 ngày, 7 ngày, 3 ngày và 1 ngày trước khi hết hạn - [Managed certificate renewal in AWS Certificate Manager](https://docs.aws.amazon.com/acm/latest/userguide/managed-renewal.html) · AWS · Chứng chỉ được nhập vào (import) và chứng chỉ đã hết hạn không thuộc diện tự động gia hạn - [Supported CloudWatch metrics](https://docs.aws.amazon.com/acm/latest/userguide/cloudwatch-metrics.html) · AWS · DaysToExpiry: số ngày còn lại đến khi chứng chỉ hết hạn, được công bố hai lần mỗi ngày cho đến khi hết hạn - [Security with network protocols](https://developer.android.com/privacy-and-security/security-ssl) · Android (Google) · Nếu server gửi thiếu chứng chỉ trung gian, ứng dụng Android thất bại với SSLHandshakeException, nhưng trình duyệt trên PC có thể bù bằng chứng chỉ trung gian đã nhận từ trước nên không báo lỗi; dùng openssl s_client để kiểm tra chuỗi server gửi - [Network security configuration](https://developer.android.com/privacy-and-security/security-config) · Android (Google) · Nếu dùng certificate pinning thì phải nhúng kèm khóa dự phòng để phòng khi thay khóa hoặc đổi CA; nếu không, kết nối sẽ bị cắt cho đến khi cập nhật ứng dụng - [openssl-s_client](https://docs.openssl.org/3.0/man1/openssl-s_client/) · OpenSSL · -showcerts: hiển thị danh sách chứng chỉ đúng theo thứ tự server gửi (đây chưa phải chuỗi đã được xác minh) - [openssl-x509](https://docs.openssl.org/3.0/man1/openssl-x509/) · OpenSSL · -enddate: in ra ngày hết hạn của chứng chỉ (notAfter), -checkend: kiểm tra chứng chỉ có hết hạn trong số giây chỉ định không - [CloudWatch metrics for your Application Load Balancer](https://docs.aws.amazon.com/elasticloadbalancing/latest/application/load-balancer-cloudwatch-metrics.html) · AWS · ClientTLSNegotiationErrorCount: số kết nối không thiết lập được phiên TLS, chẳng hạn khi client xác minh chứng chỉ server thất bại rồi cắt kết nối - [CloudWatch metrics for your Network Load Balancer](https://docs.aws.amazon.com/elasticloadbalancing/latest/network/load-balancer-cloudwatch-metrics.html) · AWS · ClientTLSNegotiationErrorCount: số TLS handshake thương lượng thất bại giữa client và TLS listener #### in-login-queue · Giới hạn hàng chờ đăng nhập, thiếu thời gian giữ chỗ khi kết nối lại · Login queue cap / no reconnect grace Ngay sau khi ra mắt hoặc bảo trì, lượng kết nối dồn đến khiến hàng chờ đăng nhập chạm giới hạn và từ chối lượt chờ mới, còn người chơi đang chờ chỉ cần mất kết nối trong giây lát là mất chỗ và phải quay về cuối hàng. - Vì sao → Dẫn đến → Trên màn hình: Số người muốn vào nhiều hơn lượng server đăng nhập nhận được cùng lúc nên phải có hàng chờ, và khi hàng chờ quá dài thì server từ chối lượt chờ mới để tự bảo vệ → Hàng chờ càng dài thì thời gian chờ càng lâu, và trong lúc đó chỉ cần Wi-Fi hoặc mạng di động rớt trong giây lát là mất chỗ trong hàng → Không vào được, kẹt loading, game thoát kèm thông báo lỗi khi đang chờ, lại phải chờ từ cuối hàng - Triệu chứng: Không vào được·kẹt loading, Mất kết nối / Yếu tố: Ngưng trệ - Ai gặp: Cả server, Chỉ mình tôi / Khi nào: Ngay sau đăng nhập hoặc bảo trì, Giờ cao điểm buổi tối - Phụ trách chính: Phát triển server (Đội phát triển game) / Phối hợp: Phát triển client (Đội phát triển game), Hạ tầng server (Đội hạ tầng) - Việc cần làm (Đội phát triển game): Server: đặt giới hạn hàng chờ theo lượng mà server đăng nhập thực sự xử lý được, giữ chỗ cho người chơi bị ngắt khi đang chờ trong một khoảng thời gian (thời gian giữ chỗ khi kết nối lại), hiển thị số thứ tự và thời gian chờ dự kiến, ghi độ dài hàng chờ, số lượt từ chối, số lần mất kết nối khi đang chờ thành chỉ số. Client: nếu mất kết nối khi đang chờ thì không tắt game mà tự động kết nối lại vào đúng chỗ cũ, giãn khoảng cách thử lại bằng exponential backoff và jitter. - Việc cần làm (Đội hạ tầng): Thiết bị server·OS: chạy kiểm thử tải trước khi ra mắt để đo giới hạn xử lý của server đăng nhập và lobby, chuẩn bị sẵn thiết bị dự phòng để gắn thêm khi ra mắt, xem chỉ số hàng chờ trên cùng đồ thị với số lượt thử kết nối. - Con số tham khảo: Khi ra mắt bản mở rộng FINAL FANTASY XIV năm 2021, mỗi trung tâm dữ liệu logic từ chối lượt chờ mới khi số người chờ vượt 17.000 (Error 2002). Nếu mất kết nối khi đang chờ, server lobby đợi từ vài chục giây đến 1 phút, và nếu kết nối lại trong khoảng đó thì người chơi được tiếp tục từ giữa hàng chờ. - Trên đồ thị: Chạm giới hạn rồi đi ngang (Độ dài hàng chờ đăng nhập, số lượt bị từ chối do chạm giới hạn, số lần mất kết nối khi đang chờ) - Chỗ cần xem: Đặt độ dài hàng chờ, thời gian chờ trung bình, số lượt từ chối do chạm giới hạn, số lần mất kết nối khi đang chờ do server đăng nhập và lobby ghi lại lên cùng đồ thị với số lượt thử kết nối - Đúng nếu: ngay sau khi ra mắt hoặc bảo trì, trong lúc độ dài hàng chờ chạm giới hạn và đi ngang thì số lượt từ chối tăng, và mất kết nối khi đang chờ dồn vào người chơi dùng Wi-Fi hoặc mạng di động - Loại trừ nếu: hàng chờ ngắn mà đăng nhập vẫn chậm → xem DB (db-login-storm) hoặc hàng đợi kết nối của hệ điều hành (so-backlog) - Cách kiểm tra: Cần log và chỉ số của server, client game - Tìm hiểu thêm: Trường hợp đăng nhập ồ ạt làm DB chậm được trình bày ở “Đăng nhập ồ ạt và query N+1”, còn trường hợp hàng đợi kết nối của hệ điều hành bị tràn nằm ở “Tràn hàng đợi kết nối (backlog)”. Mục này nói về vấn đề thiết kế của hàng chờ đăng nhập mà game chủ động đặt ra. Giới hạn hàng chờ là cơ chế an toàn bảo vệ server đăng nhập nên không thể bỏ đi, vì phải từ chối sớm các yêu cầu vượt mức thì mới tiếp tục xử lý được những yêu cầu trong khả năng. Thay vào đó, điều cốt lõi là giảm thiệt hại mà việc bị từ chối và bị ngắt gây ra cho người chơi, bởi hàng chờ càng dài thì lỗi càng dồn vào những người chơi có đường truyền không ổn định như Wi-Fi, mạng di động. - Sự cố thực tế: ffxiv-2021 - Nguồn: - [Response to Congestion (as of Dec. 11)](https://na.finalfantasyxiv.com/lodestone/news/detail/6a94b30182b6d963994fdc0b789264ac9f24986f) · Square Enix · Mỗi trung tâm dữ liệu logic từ chối lượt chờ mới khi số người chờ vượt 17.000 để server đăng nhập không bị sập (Error 2002); nếu mất kết nối khi đang chờ, server lobby đợi từ vài chục giây đến 1 phút, kết nối lại trong khoảng đó thì tiếp tục từ giữa hàng chờ, quá thời gian thì về cuối hàng - [Using load shedding to avoid overload](https://aws.amazon.com/builders-library/using-load-shedding-to-avoid-overload/) · Amazon Builders' Library · Chặn bớt tải (load shedding): từ chối sớm các yêu cầu vượt mức để tiếp tục xử lý được những yêu cầu trong khả năng ### Thiết kế đồng bộ (16 nguyên nhân) #### sy-request-response · Chỉ hiển thị sau khi server phản hồi (mô hình yêu cầu-phản hồi) · Request-response (no client-side feedback) Khi bấm nút, không có animation hay âm thanh nào cho đến khi server trả lời. Ping trở thành chính tốc độ phản hồi. - Vì sao → Dẫn đến → Trên màn hình: Skill, di chuyển, nhặt đồ chỉ được phát sau khi server xác nhận → Từ lúc bấm, không có phản hồi nào trong khoảng thời gian bằng thời gian khứ hồi + thời gian chờ tick → Ping 150 ms thì hành động nào cũng chậm mất 0,2 giây - Triệu chứng: Trễ thao tác / Yếu tố: Độ trễ - Ai gặp: Chỉ mình tôi / Khi nào: Luôn luôn, Khi làm một thao tác nhất định - Phụ trách chính: Phát triển client (Đội phát triển game) / Phối hợp: Phát triển server (Đội phát triển game) - Việc cần làm (Đội phát triển game): Client: animation, âm thanh, hiệu ứng bắt đầu ngay khi bấm (hiển thị trước), chỉ kết quả (sát thương, phần thưởng) mới hiển thị sau khi server xác nhận, di chuyển và đòn đánh thường thì dự đoán rồi phản ánh ngay, khi nhận hiệu chỉnh vị trí từ server thì áp dụng lại các input chưa được xác nhận tính từ vị trí đó. Server: tự tính di chuyển từ input nhận được, chỉ gửi giá trị hiệu chỉnh khi chênh lệch với vị trí client dự đoán vượt ngưỡng. - Con số tham khảo: Thời gian phản hồi ≈ ping + nửa khoảng cách tick + một khung hình. 20 tick, ping 150 ms thì khoảng 190 ms. - Trên đồ thị: Luôn cao ngay từ đầu (Thời gian từ input đến khi bắt đầu hiển thị, RTT (ping)) - Chỗ cần xem: Ghi vào log client của bản build dev thời điểm bấm nút, thời điểm animation hoặc âm thanh đầu tiên bắt đầu, thời điểm phản hồi server tới, rồi đặt cạnh RTT trong game. Dùng mô phỏng mạng của engine (Unreal NetEmulation.PktLag) hoặc Linux tc netem trên server thử nghiệm để thêm độ trễ, đổi ping rồi đo lại - Đúng nếu: hiển thị luôn bắt đầu đúng lúc phản hồi server tới, thời gian từ input đến hiển thị bằng khoảng RTT + thời gian chờ tick, và tăng đúng bằng độ trễ đã thêm vào - Loại trừ nếu: hiển thị bắt đầu ngay khi bấm, chỉ kết quả như số sát thương đến muộn → thiết kế bình thường. Ở nơi ping thấp mà vẫn trễ hơn một khoảng cách tick → vấn đề chờ tick hai lần hoặc khung hình phía client - Cách kiểm tra: Cần log và chỉ số của server, client game - Tìm hiểu thêm: Với các game không cần phản hồi nhanh như game theo lượt, game thẻ bài, game idle, cách này đơn giản và an toàn nhất. Vấn đề phát sinh khi game có điều khiển thời gian thực mà làm cả di chuyển lẫn đòn đánh thường theo cách này. - Nguồn: - [Latency Compensating Methods in Client/Server In-game Protocol Design and Optimization (Yahn W. Bernier, GDC 2001)](https://web.cs.wpi.edu/~claypool/courses/4513-B03/papers/games/bernier.pdf) · Valve · Client chỉ chờ kết quả từ server thì khi độ trễ là 500 ms, mọi hành động đều phải 500 ms sau mới thấy. Giải quyết bằng dự đoán phía client và hiệu chỉnh theo server - [Using Gameplay Abilities in Unreal Engine](https://dev.epicgames.com/documentation/en-us/unreal-engine/using-gameplay-abilities-in-unreal-engine) · Epic Games · Local Predicted thực thi ngay khi bấm và server quyết định cuối cùng; Server Initiated không có dự đoán nên người tung skill thấy độ trễ - [Understanding Networked Movement in the Character Movement Component for Unreal Engine](https://dev.epicgames.com/documentation/en-us/unreal-engine/understanding-networked-movement-in-the-character-movement-component-for-unreal-engine) · Epic Games · Client dự đoán và lưu lại các bước di chuyển, chỉ hiệu chỉnh khi sai lệch với server vượt ngưỡng cho phép (MAXPOSITIONERRORSQUARED), sau khi hiệu chỉnh thì áp dụng lại các bước di chuyển đã lưu - [Using Network Emulation in Unreal Engine](https://dev.epicgames.com/documentation/en-us/unreal-engine/using-network-emulation-in-unreal-engine) · Epic Games · Thử nghiệm bằng cách đặt độ trễ tối thiểu, tối đa và tỷ lệ mất gói cho server và client; trên console thì cấu hình như NetEmulation.PktLag - [tc-netem(8) — Linux manual page](https://man7.org/linux/man-pages/man8/tc-netem.8.html) · iproute2 · Công cụ thử nghiệm giả lập mạng thực bằng cách thêm độ trễ, jitter (delay TIME JITTER) và mất gói (loss random PERCENT) vào gói tin đi ra #### sy-chatty · Giao thức nhiều lượt khứ hồi tuần tự (chatty) · Chatty protocol / sequential round trips Nếu một thao tác cần nhiều lượt khứ hồi tới server theo thứ tự, ping bị nhân lên đúng bấy nhiêu lần. - Vì sao → Dẫn đến → Trên màn hình: Mở cửa hàng → yêu cầu danh sách → kiểm tra giá → mua → cập nhật túi đồ, mỗi bước là một yêu cầu riêng → Phải nhận được trả lời của yêu cầu trước mới gửi yêu cầu tiếp theo → Ping 150 ms thì mua một lần mất gần 1 giây. Loading lâu bất thường - Triệu chứng: Trễ thao tác, Không vào được·kẹt loading / Yếu tố: Độ trễ - Ai gặp: Chỉ một tính năng, Chỉ mình tôi / Khi nào: Khi làm một thao tác nhất định, Ngay sau đăng nhập hoặc bảo trì - Phụ trách chính: Phát triển server (Đội phát triển game) / Phối hợp: Phát triển client (Đội phát triển game) - Việc cần làm (Đội phát triển game): Server: đổi giao thức để gộp nhiều bước vào một lần yêu cầu và phản hồi (ví dụ: gửi kèm túi đồ đã cập nhật trong phản hồi mua hàng). Client: tải trước dữ liệu cần dùng, UI không phải chờ kết quả. - Con số tham khảo: Thời gian mất ≈ số lượt khứ hồi × (ping + xử lý trên server + chờ tick). 5 lượt với ping 150 ms thì mất khoảng 0,85–1 giây. - Trên đồ thị: Luôn cao ngay từ đầu (Thời gian hoàn tất theo tính năng, số lượt khứ hồi của một thao tác) - Chỗ cần xem: Trong bản bắt gói tin phía server (Wireshark), dùng tài khoản thử nghiệm thực hiện một lần thao tác như mua hàng hoặc đăng nhập, rồi đếm số lần yêu cầu và phản hồi luân phiên qua lại cùng khoảng cách giữa chúng. Nếu có log yêu cầu phía server: nhóm theo session ID để xem số yêu cầu và thời điểm đến, thời điểm phản hồi của từng yêu cầu - Đúng nếu: một thao tác có nhiều yêu cầu lần lượt qua lại, yêu cầu sau chờ phản hồi của yêu cầu trước, thời gian hoàn tất xấp xỉ số lượt khứ hồi × RTT, và người chơi ở khu vực ping càng cao thì cùng một tính năng càng chậm theo tỷ lệ - Loại trừ nếu: chỉ một hai lượt khứ hồi nhưng một phản hồi mất lâu → nguyên nhân ở xử lý trên server hoặc DB. Mọi người chơi đều chậm như nhau bất kể ping → xem tải server - Cách kiểm tra: Kiểm tra bằng công cụ hạ tầng (không cần code game) - Nguồn: - [Chatty I/O antipattern](https://learn.microsoft.com/en-us/azure/architecture/antipatterns/chatty-io/) · Microsoft Azure · Nhiều yêu cầu I/O nhỏ thì độ trễ cộng dồn làm giảm mạnh khả năng phản hồi. Khuyến nghị gộp thành ít yêu cầu lớn hơn #### sy-no-queue · Không có buffer input cho skill · No input/spell queue Nếu phải nhận xác nhận từ server rằng skill trước đã xong mới bấm được skill tiếp theo, mỗi nhịp combo sẽ bị chen thêm một khoảng thời gian khứ hồi. - Vì sao → Dẫn đến → Trên màn hình: Chỉ nhận input skill tiếp theo “sau khi skill trước được xác nhận” → Giữa các skill luôn có một khoảng trống bằng ping → Giữa các đòn combo có khoảng hở, ping càng cao thì DPS càng giảm - Triệu chứng: Trễ thao tác, Nuốt thao tác·rollback / Yếu tố: Độ trễ - Ai gặp: Chỉ mình tôi, Chỉ một tính năng / Khi nào: Khi làm một thao tác nhất định - Phụ trách chính: Phát triển client (Đội phát triển game) / Phối hợp: Phát triển server (Đội phát triển game) - Việc cần làm (Đội phát triển game): Client: thời gian nhận buffer input, tức là nhận cả input trong một khoảng nhất định trước khi hồi chiêu kết thúc (ví dụ 0,3–0,4 giây) và gửi ngay lên server. Server: giữ lại input đến sớm một chút và thực thi đúng lúc hồi chiêu kết thúc, thay vì từ chối. - Con số tham khảo: Với combo có hồi chiêu 1 giây, ping 150 ms làm giữa mỗi skill trống từ 0,15 giây trở lên, số skill dùng được trong cùng khoảng thời gian giảm hơn 13%. - Trên đồ thị: Luôn cao ngay từ đầu (Khoảng trống giữa các skill, RTT (ping)) - Chỗ cần xem: Ghi vào log server theo từng nhân vật thời điểm hồi chiêu kết thúc, thời điểm yêu cầu skill tiếp theo tới, thời điểm thực thi, rồi so sánh khoảng trống ở giữa với RTT của người chơi - Đúng nếu: từ lúc hồi chiêu kết thúc đến khi skill tiếp theo được thực thi luôn trống khoảng bằng RTT, người chơi ping càng cao thì khoảng trống càng dài và số skill dùng được trong cùng khoảng thời gian càng ít - Loại trừ nếu: khoảng trống không đổi bất kể ping → thiết kế hồi chiêu chung (global cooldown) hoặc độ dài animation. Khoảng trống chỉ thỉnh thoảng vọt lên → xem jitter, mất gói hoặc vượt tick budget - Cách kiểm tra: Cần log và chỉ số của server, client game - Tìm hiểu thêm: Ví dụ, World of Warcraft có thời gian nhận buffer input và cho người chơi chỉnh trong phần cài đặt. Nếu thời gian này dài hơn thời gian khứ hồi thì giữa các đòn combo gần như không bị ping chen vào. - Nguồn: - [Latency and Player Actions in Online Games (Communications of the ACM, 2006)](https://web.cs.wpi.edu/~claypool/papers/precision-deadline/final.pdf) · ACM · Trong EverQuest 2, độ trễ càng lớn thì sát thương nhân vật gây ra càng giảm, trận đánh càng kéo dài (0→500 ms: trận đánh khoảng 2 phút dài thêm 5 giây) #### sy-short-window · Khung phán định ngắn bị ping ăn mất · Timing window too short for latency + reaction Nếu thời gian phải phản ứng ngắn như né tránh, parry, đỡ đòn, ping sẽ ăn mất khoảng thời gian đó và tạo ra những đòn không thể né. - Vì sao → Dẫn đến → Trên màn hình: Khung phán định ngắn, như cảnh báo đòn đánh của boss 0,5 giây, phán định parry 0,2 giây → Thấy cảnh báo muộn (độ trễ chiều xuống + nội suy), input của bạn cũng tới muộn (độ trễ chiều lên + chờ tick) → Rõ ràng đã né mà vẫn trúng, parry bị nuốt - Triệu chứng: Nuốt thao tác·rollback, Trễ thao tác / Yếu tố: Độ trễ - Ai gặp: Chỉ mình tôi, Chỉ một tính năng / Khi nào: Khi làm một thao tác nhất định - Phụ trách chính: Phát triển server (Đội phát triển game) / Phối hợp: Phát triển client (Đội phát triển game), Hạ tầng server (Đội hạ tầng) - Việc cần làm (Đội phát triển game): Server: đặt lịch cảnh báo đòn đánh theo giờ server và gửi trước, nới khung phán định thêm bằng ping (bù trễ). Client: phát cảnh báo nhận được đúng theo giờ server đã hẹn. - Việc cần làm (Đội hạ tầng): Đặt server gần khu vực có nhiều người chơi (server khu vực) để giảm chính ping. - Con số tham khảo: Ping 150 ms, nội suy 100 ms thì mất khoảng 0,18 giây để cảnh báo hiện trên màn hình của bạn, và khoảng 0,1 giây để input của bạn tới server. Cộng thêm thời gian phản ứng của con người 0,25 giây thì gần như không thể né cảnh báo 0,5 giây. - Trên đồ thị: Chỉ một phần cao (Tỷ lệ né, parry thất bại (theo khoảng ping)) - Chỗ cần xem: Ghi vào log server thời điểm bắt đầu và kết thúc khung phán định, thời điểm input của người chơi tới server, RTT của người chơi đó, rồi chia tỷ lệ thất bại theo khoảng ping (ví dụ mỗi 50 ms) - Đúng nếu: khoảng ping càng cao thì tỷ lệ thất bại càng cao rõ rệt, và input thất bại tới ngay sau khi khung phán định kết thúc (trong phạm vi RTT cộng thời gian nội suy) - Loại trừ nếu: tỷ lệ thất bại tương tự nhau bất kể khoảng ping → vấn đề độ khó của pattern. Input tới trong khung phán định mà vẫn bị tính là thất bại → xem code phán định hoặc bước kiểm tra phía server - Cách kiểm tra: Cần log và chỉ số của server, client game - Nguồn: - [Factors influencing the latency of simple reaction time (Frontiers in Human Neuroscience, 2015)](https://doi.org/10.3389/fnhum.2015.00131) · Frontiers · Thời gian phản ứng đơn giản trung bình khoảng 231 ms (213 ms khi đã hiệu chỉnh độ trễ thiết bị), các nghiên cứu quy mô lớn gần đây cho kết quả 233–400 ms - [Latency and Player Actions in Online Games (Communications of the ACM, 2006)](https://web.cs.wpi.edu/~claypool/papers/precision-deadline/final.pdf) · ACM · Hành động càng cần chính xác và có hạn chót càng ngắn thì càng nhạy với độ trễ (giới hạn ở khoảng 100 ms với góc nhìn thứ 1, khoảng 500 ms với góc nhìn thứ 3, khoảng 1.000 ms với góc nhìn toàn cảnh) - [NetworkTime and ticks (Netcode for GameObjects 2.5)](https://docs.unity3d.com/Packages/com.unity.netcode.gameobjects@2.5/manual/advanced-topics/networktime-ticks.html) · Unity · Cách đặt lịch sự kiện theo giờ server (ServerTime) để mọi client phát cùng một khoảnh khắc #### sy-no-lagcomp · Phán định không có bù trễ · Server-now hit validation Nếu server chỉ phán định trúng đích bằng “vị trí hiện tại trên server”, phán định sẽ lệch với những gì bạn thấy trên màn hình. - Vì sao → Dẫn đến → Trên màn hình: Đối thủ trên màn hình của bạn đang ở vị trí trong quá khứ khoảng 0,2 giây (khi ping 150 ms, nội suy 100 ms) → Server phán định theo vị trí hiện tại nên đối thủ đã không còn ở chỗ bạn ngắm → Rõ ràng đã bắn trúng mà vẫn trượt. Phải bắn đón đầu mục tiêu đang di chuyển - Triệu chứng: Nuốt thao tác·rollback / Yếu tố: Độ trễ - Ai gặp: Chỉ mình tôi / Khi nào: Khi làm một thao tác nhất định - Phụ trách chính: Phát triển server (Đội phát triển game) / Phối hợp: Phát triển client (Đội phát triển game) - Việc cần làm (Đội phát triển game): Server: quay ngược về thời điểm người tấn công đang nhìn thấy để phán định (bù trễ), hoặc chuyển sang cơ chế chọn mục tiêu (tab-target). Client: khi tấn công thì gửi kèm thời điểm mình đang nhìn thấy (giờ server đang được nội suy). - Trên đồ thị: Chỉ một phần cao (Tỷ lệ trúng mục tiêu di chuyển (theo khoảng ping)) - Chỗ cần xem: Ghi cùng lúc vào log phán định của server: thời điểm tấn công, vị trí mục tiêu trên màn hình người tấn công (giá trị client gửi), vị trí mục tiêu trên server dùng để phán định, RTT của người tấn công. Trong bản build dev, vẽ chồng vị trí server dùng để phán định lên màn hình client là thấy ngay - Đúng nếu: ở các lần phán định trượt, chênh lệch giữa hai vị trí xấp xỉ tốc độ mục tiêu × (RTT người tấn công + thời gian nội suy), và ping càng cao thì riêng tỷ lệ trúng mục tiêu di chuyển càng giảm - Loại trừ nếu: mục tiêu đứng yên cũng trượt → vấn đề hitbox hoặc kiểm tra va chạm. Đã có quay ngược mà vẫn lệch → xem client có báo sai thời gian nội suy cho server không - Cách kiểm tra: Cần log và chỉ số của server, client game - Nguồn: - [Latency Compensating Methods in Client/Server In-game Protocol Design and Optimization (Yahn W. Bernier, GDC 2001)](https://web.cs.wpi.edu/~claypool/courses/4513-B03/papers/games/bernier.pdf) · Valve · Không có bù trễ thì phải bắn đón đầu đúng bằng độ trễ. Bù trễ là việc server quay ngược đúng bằng độ trễ và thời gian nội suy để phán định - [Peeking into VALORANT's Netcode](https://www.riotgames.com/en/news/peeking-valorants-netcode) · Riot Games · Server quay ngược về trạng thái game mà người chơi thấy vào lúc bắn để phán định trúng đích, client gửi kèm thời điểm mô phỏng mà mình đang thấy - [Source SDK 2013: player_lagcompensation.cpp](https://raw.githubusercontent.com/ValveSoftware/source-sdk-2013/master/src/game/server/player_lagcompensation.cpp) · Valve · Thời gian quay ngược của Source engine = độ trễ mạng + thời gian nội suy #### sy-lagcomp-overreach · Bù trễ quá mức · Excessive lag compensation Nếu quay ngược quá xa theo góc nhìn của người tấn công, người bị bắn dù đã nấp vẫn bị trúng. - Vì sao → Dẫn đến → Trên màn hình: Server quay ngược rất xa để phán định cho người tấn công có ping cao → Trên màn hình của người bị bắn, họ đã vào chỗ nấp → “Bị bắn trúng sau tường”, người ping cao được lợi - Triệu chứng: Nuốt thao tác·rollback / Yếu tố: Độ trễ - Ai gặp: Chỉ mình tôi, Một khu vực hoặc nhà mạng / Khi nào: Khi làm một thao tác nhất định - Phụ trách chính: Phát triển server (Đội phát triển game) - Việc cần làm (Đội phát triển game): Đặt giới hạn quay ngược (ví dụ 200–250 ms), người tấn công có ping cao hơn thì chỉ quay ngược tới giới hạn, phần còn lại để họ tự bắn đón đầu. - Trên đồ thị: Chỉ một phần cao (Thời gian quay ngược của mỗi lần trúng (theo ping người tấn công)) - Chỗ cần xem: Ghi vào log phán định của server cho mỗi lần trúng: thời gian quay ngược, RTT của người tấn công, thời điểm trên server mà người bị bắn vào chỗ nấp. Trong bản build dev, vẽ hitbox đã quay ngược lên màn hình (Source engine là sv_showlagcompensation) - Đúng nếu: các lần trúng trong báo cáo “bị bắn sau tường” dồn vào người tấn công có thời gian quay ngược dài, và thời gian quay ngược tăng theo ping người tấn công mà không có giới hạn - Loại trừ nếu: lần trúng có thời gian quay ngược ngắn cũng bị bắn sau tường → vấn đề hitbox hoặc kiểm tra va chạm. Người bị bắn có ping cao → do di chuyển của người đó tới server muộn - Cách kiểm tra: Cần log và chỉ số của server, client game - Tìm hiểu thêm: Phán định quay ngược theo nguyên tắc “ưu tiên người bắn”. Cũng có đề xuất ngoại lệ “ưu tiên người bị bắn”: không quay ngược nếu trên màn hình của người bị bắn, họ đã vào chỗ an toàn. - Nguồn: - [Peeking into VALORANT's Netcode](https://www.riotgames.com/en/news/peeking-valorants-netcode) · Riot Games · Nếu quay ngược không có giới hạn, người có độ trễ 500 ms vẫn bắn trúng được đối phương đã nấp 0,5 giây, nên cần đặt giới hạn - [A Survey and Taxonomy of Latency Compensation Techniques for Network Computer Games (ACM Computing Surveys, 2022)](https://web.cs.wpi.edu/~claypool/papers/lag-taxonomy/paper.pdf) · ACM · Hiện tượng “bị bắn sau góc tường (shot around the corner)”, giới hạn quay ngược trong các game FPS thương mại, đề xuất kỹ thuật không quay ngược khi người bị bắn đã an toàn - [Source SDK 2013: player_lagcompensation.cpp](https://raw.githubusercontent.com/ValveSoftware/source-sdk-2013/master/src/game/server/player_lagcompensation.cpp) · Valve · Giới hạn quay ngược sv_maxunlag của Source engine mặc định 1 giây (tối đa 1 giây), sv_showlagcompensation hiển thị hitbox đã quay ngược lên màn hình #### sy-client-auth · Client có thẩm quyền · Client-authoritative results Nếu mỗi client tự quyết định kết quả của mình, màn hình của bạn mượt mà nhưng kết quả lệch với màn hình người khác và dễ bị hack. - Vì sao → Dẫn đến → Trên màn hình: Client quyết định vị trí và việc trúng đích, server chỉ chuyển tiếp → Hai người cùng khẳng định mình bắn trúng trước, server không kiểm chứng được → Đối thủ dịch chuyển tức thời, đi xuyên tường, “tôi bắn trúng rồi mà không ăn” - Triệu chứng: Dịch chuyển tức thời, Nuốt thao tác·rollback / Yếu tố: Độ trễ - Ai gặp: Cả server / Khi nào: Luôn luôn - Phụ trách chính: Phát triển server (Đội phát triển game) / Phối hợp: Phát triển client (Đội phát triển game) - Việc cần làm (Đội phát triển game): Server: tự kiểm tra các kết quả quan trọng (trúng đích, v.v.), kiểm tra tốc độ và quãng đường di chuyển. Client: khi nhận kết quả bị server từ chối hoặc hiệu chỉnh thì quay về giá trị đó. - Trên đồ thị: Luôn cao ngay từ đầu (Số báo cáo có tốc độ di chuyển bất khả thi hoặc trúng đích mâu thuẫn) - Chỗ cần xem: Ghi lại nguyên vẹn trên server các vị trí và lần trúng mà client báo cáo, tính tốc độ di chuyển từ các báo cáo vị trí liên tiếp, rồi đếm các báo cáo vượt tốc độ tối đa và các báo cáo hai người cùng nói mình trúng trước - Đúng nếu: server chuyển tiếp báo cáo cho client khác mà không kiểm tra, và các báo cáo tốc độ bất khả thi hoặc trúng đích mâu thuẫn xuất hiện đều đặn bất kể bản cập nhật hay khu vực - Loại trừ nếu: server đang tự tính hoặc kiểm tra kết quả → không phải nguyên nhân này. Khi đó hiện tượng dịch chuyển tức thời thì xem mất gói hoặc bộ đệm nội suy - Cách kiểm tra: Cần log và chỉ số của server, client game - Nguồn: - [Latency Compensating Methods in Client/Server In-game Protocol Design and Optimization (Yahn W. Bernier, GDC 2001)](https://web.cs.wpi.edu/~claypool/courses/4513-B03/papers/games/bernier.pdf) · Valve · Cách để client báo cáo kết quả chỉ khả thi khi có thể tin client. Vì lo ngại hack nên dùng server có thẩm quyền - [Peeking into VALORANT's Netcode](https://www.riotgames.com/en/news/peeking-valorants-netcode) · Riot Games · Mô hình server có thẩm quyền: server tuyệt đối không tin trạng thái game mà client thấy - [Distributed authority topologies (Netcode for GameObjects 2.5)](https://docs.unity3d.com/Packages/com.unity.netcode.gameobjects@2.5/manual/terms-concepts/distributed-authority.html) · Unity · Chia quyền cho client thì gian lận dễ hơn, và không còn một mô phỏng duy nhất điều khiển mọi đối tượng #### sy-lockstep · Lockstep phải chờ người chơi chậm nhất · Lockstep waits for the slowest peer Trong cấu trúc mọi người cùng tính chung một lượt, chỉ cần input của một người đến muộn là tất cả phải chờ. - Vì sao → Dẫn đến → Trên màn hình: Mỗi lượt phải gom đủ input của mọi người chơi mới tính được → Input của một người tới muộn do jitter hoặc mất gói → Mọi người cùng khựng một lúc, nặng thì hiện cửa sổ “Đang chờ người chơi” - Triệu chứng: Đứng hình, Giật khựng, Trễ thao tác / Yếu tố: Jitter, Mất gói, Ngưng trệ - Ai gặp: Một địa điểm hoặc kênh / Khi nào: Thỉnh thoảng bất chợt - Phụ trách chính: Phát triển server (Đội phát triển game) / Phối hợp: Phát triển client (Đội phát triển game) - Việc cần làm (Đội phát triển game): Server: tự động điều chỉnh độ trễ input theo ping, tạm tách riêng người bị chậm để những người còn lại tiếp tục mà không phải chờ. Client: áp dụng độ trễ input đã định; nếu là P2P không có server trung chuyển thì client host đảm nhận cả việc điều chỉnh độ trễ input và xử lý người bị chậm. - Con số tham khảo: Nếu đặt độ trễ input ngắn hơn “thời gian input tới đối phương + jitter”, hiện tượng đứng hình sẽ xảy ra thường xuyên hơn. Thời gian tới là khoảng một nửa ping nếu hai bên gửi trực tiếp cho nhau, và khoảng một nửa tổng ping của hai người nếu đi qua server trung chuyển. - Trên đồ thị: Thỉnh thoảng vọt lên bất chợt (Thời gian chờ lượt, độ trễ input tới theo người chơi) - Chỗ cần xem: Ghi cho mỗi lượt thời điểm input của từng người chơi tới và thời gian lượt bị dừng để chờ, rồi xem ở các lượt bị dừng đang chờ input của ai. Nếu có server trung chuyển thì cũng xem được qua khoảng cách giữa các gói input của từng người chơi trong bản bắt gói phía server - Đúng nếu: ở mỗi lượt bị dừng, input của cùng một người tới muộn hơn độ trễ input, và vào lúc đó jitter hoặc mất gói của người đó vọt lên - Loại trừ nếu: input đều tới đúng lúc mà vẫn dừng → vấn đề thời gian tính toán của PC chậm nhất hoặc xử lý trên server. Không dừng mà chỉ kết quả trên hai màn hình khác nhau → kết quả tính toán bị lệch (desync), xem tính toán đường đi không khớp khi đồng bộ lệnh - Cách kiểm tra: Cần log và chỉ số của server, client game - Nguồn: - [Deterministic Lockstep](https://gafferongames.com/post/deterministic_lockstep/) · Gaffer On Games · Phải có đủ input của khung hình n mới tính được, nên input tới muộn thì phải chờ. Bộ đệm trễ phát lại dùng để hấp thụ jitter mà nhỏ thì sẽ bị khựng - [1500 Archers on a 28.8: Network Programming in Age of Empires and Beyond](https://www.gamedeveloper.com/programming/1500-archers-on-a-28-8-network-programming-in-age-of-empires-and-beyond) · Game Developer · Đặt lịch thực thi lệnh sau hai lượt, và điều chỉnh độ dài lượt theo máy tính chậm nhất và ping (Speed Control) - [A Survey and Taxonomy of Latency Compensation Techniques for Network Computer Games (ACM Computing Surveys, 2022)](https://web.cs.wpi.edu/~claypool/papers/lag-taxonomy/paper.pdf) · ACM · Đặt độ trễ input (incoming delay) bằng độ trễ “A→server + server→B” để mọi người áp dụng cùng một khoảnh khắc #### sy-rollback · Rollback netcode dự đoán sai · Rollback misprediction Game dự đoán input của đối thủ để hiển thị trước, nếu sai thì quay ngược lại và tính lại. Ping càng lớn thì khoảng quay ngược càng lớn. - Vì sao → Dẫn đến → Trên màn hình: Đối thủ đổi input (khác với dự đoán) → Input thực tới muộn một nửa ping, nên phải quay ngược đúng khoảng đó để tính lại → Động tác của đối thủ bỏ qua vài khung hình hoặc đột ngột thay đổi - Triệu chứng: Dịch chuyển tức thời / Yếu tố: Độ trễ, Jitter - Ai gặp: Chỉ mình tôi / Khi nào: Khi làm một thao tác nhất định, Thỉnh thoảng bất chợt - Phụ trách chính: Phát triển client (Đội phát triển game) - Việc cần làm (Đội phát triển game): Kết hợp độ trễ input 1–3 khung hình để giảm khoảng quay ngược, đặt giới hạn quay ngược. - Con số tham khảo: Ping 100 ms (mỗi chiều 50 ms) thì ở 60 fps phải quay ngược khoảng 3 khung hình. Đặt độ trễ input 2 khung hình thì giảm còn 1 khung hình. - Trên đồ thị: Thỉnh thoảng vọt lên bất chợt (Số khung hình bị quay ngược, RTT (ping)) - Chỗ cần xem: Client ghi lại cho mỗi lần quay ngược: số khung hình quay ngược, RTT lúc đó, cấu hình độ trễ input, thời gian quay ngược và tính lại - Đúng nếu: vào lúc có báo cáo động tác đối thủ bị nhảy, số khung hình quay ngược lớn, khoảng quay ngược trung bình xấp xỉ (độ trễ một chiều − độ trễ input) ÷ thời gian một khung hình, và càng lớn khi ping càng cao - Loại trừ nếu: khoảng quay ngược nhỏ mà vẫn bị giật khựng → vấn đề hiệu năng, việc tính lại vượt quá thời gian một khung hình. Sau khi quay ngược mà kết quả trên hai màn hình vẫn tiếp tục khác nhau → kết quả tính toán bị lệch (desync) - Cách kiểm tra: Cần log và chỉ số của server, client game - Nguồn: - [GGPO Rollback Networking SDK](https://www.ggpo.net/) · GGPO · Dự đoán input của đối thủ để chạy trước, nếu input thực khác thì tính lại từ thời điểm bị lệch đến hiện tại - [8 Frames in 16ms: Rollback Networking in 'Mortal Kombat' and 'Injustice 2'](https://www.gdcvault.com/play/1025471/8-Frames-in-16ms-Rollback) · GDC · Dùng rollback để loại bỏ độ trễ input cục bộ của lockstep, quay ngược và tính lại tối đa 8 khung hình trong vòng 16 ms #### sy-no-timestamp · Phát ngay khi nhận, không có timestamp · Events played on arrival (no timestamps) Nếu sự kiện từ server không kèm thời điểm xảy ra mà được phát ngay khi nhận, thời điểm hiển thị sẽ lệch lung tung đúng theo jitter của mạng. - Vì sao → Dẫn đến → Trên màn hình: Thực thi sự kiện “bắt đầu tấn công”, “phát hiệu ứng” ngay khi nhận → Mỗi gói tin có thời gian tới khác nhau nên khoảng cách lúc dài lúc ngắn → Chuỗi động tác tấn công lúc nhanh lúc chậm, thời điểm ra pattern của boss mỗi lần một khác - Triệu chứng: Giật khựng, Tua nhanh / Yếu tố: Jitter - Ai gặp: Chỉ mình tôi / Khi nào: Luôn luôn - Phụ trách chính: Phát triển client (Đội phát triển game) / Phối hợp: Phát triển server (Đội phát triển game) - Việc cần làm (Đội phát triển game): Client: phát theo thời điểm gắn trong sự kiện (đặt lịch sự kiện, bộ đệm nội suy). Server: gắn thời điểm xảy ra (giờ server) vào sự kiện khi gửi. - Trên đồ thị: Thỉnh thoảng vọt lên bất chợt (Khoảng cách phát sự kiện, khoảng cách gói tin tới) - Chỗ cần xem: Khớp thời điểm xảy ra sự kiện trong log server với thời điểm tới và thời điểm phát trong log client theo số thứ tự sự kiện, rồi so sánh khoảng cách. Trong bản build dev, thêm jitter (giá trị jitter của tc netem, độ trễ tối thiểu và tối đa trong mô phỏng mạng của Unreal) để tái hiện - Đúng nếu: khoảng cách xảy ra trên server đều đặn nhưng khoảng cách phát lệch lung tung theo đúng khoảng cách gói tin tới - Loại trừ nếu: khoảng cách tới đều mà phát vẫn lệch lung tung → vấn đề khung hình phía client (frame time vọt lên). Khoảng cách xảy ra trên server đã dao động → vượt tick budget - Cách kiểm tra: Cần log và chỉ số của server, client game - Nguồn: - [Latency Compensating Methods in Client/Server In-game Protocol Design and Optimization (Yahn W. Bernier, GDC 2001)](https://web.cs.wpi.edu/~claypool/courses/4513-B03/papers/games/bernier.pdf) · Valve · Gắn giờ server vào mỗi bản cập nhật, rồi vẽ đối tượng ở vị trí của thời điểm mục tiêu, bằng thời điểm hiện tại trừ thời gian nội suy (100 ms) - [Snapshot Interpolation](https://gafferongames.com/post/snapshot_interpolation/) · Gaffer On Games · Vẽ ngay snapshot nhận được thì bị ngắt quãng do jitter, gom một chút vào bộ đệm nội suy rồi mới vẽ thì mượt - [NetworkTime and ticks (Netcode for GameObjects 2.5)](https://docs.unity3d.com/Packages/com.unity.netcode.gameobjects@2.5/manual/advanced-topics/networktime-ticks.html) · Unity · Ví dụ đưa thời điểm gửi vào RPC để bên nhận phát hiệu ứng theo giờ server - [tc-netem(8) — Linux manual page](https://man7.org/linux/man-pages/man8/tc-netem.8.html) · iproute2 · Công cụ thử nghiệm giả lập mạng thực bằng cách thêm độ trễ, jitter (delay TIME JITTER) và mất gói (loss random PERCENT) vào gói tin đi ra - [Using Network Emulation in Unreal Engine](https://dev.epicgames.com/documentation/en-us/unreal-engine/using-network-emulation-in-unreal-engine) · Epic Games · Thử nghiệm bằng cách đặt độ trễ tối thiểu, tối đa và tỷ lệ mất gói cho server và client; trên console thì cấu hình như NetEmulation.PktLag #### sy-double-tick · Chờ tick hai lần · Double tick quantization Nếu gom yêu cầu tới tick sau mới xử lý, rồi kết quả lại đợi đến tick sau nữa mới gửi, khoảng cách tick bị cộng vào hai lần. - Vì sao → Dẫn đến → Trên màn hình: Yêu cầu nhận được sẽ xử lý ở tick tiếp theo → Kết quả xử lý cũng được gom lại gửi ở tick gửi tiếp theo → Ping đường truyền thấp mà phản hồi luôn chậm đều khoảng 1,5 lần khoảng cách tick. Server 10 tick thì trung bình 0,15 giây, tệ nhất 0,2 giây - Triệu chứng: Trễ thao tác / Yếu tố: Độ trễ - Ai gặp: Cả server / Khi nào: Luôn luôn - Phụ trách chính: Phát triển server (Đội phát triển game) - Việc cần làm (Đội phát triển game): Gửi phản hồi ngay trong tick xử lý, tăng tick rate, gửi ngay các phản hồi quan trọng. - Con số tham khảo: Server 10 tick có mỗi tick 100 ms, nên riêng việc chờ tick đã cộng thêm trung bình 150 ms, tệ nhất 200 ms. Nếu chỉ chờ một lần thì trung bình 50 ms. - Trên đồ thị: Luôn cao ngay từ đầu (Thời gian từ khi yêu cầu tới đến khi gửi phản hồi) - Chỗ cần xem: Trong bản bắt gói phía server, khi tài khoản thử nghiệm lặp lại nhiều lần cùng một hành động (ví dụ dùng vật phẩm), đo khoảng cách giữa thời điểm gói yêu cầu tới và thời điểm gói phản hồi đi ra. Nếu có log server: xem thời điểm yêu cầu tới, số thứ tự tick xử lý, thời điểm gửi phản hồi - Đúng nếu: thời gian yêu cầu nằm trong server trung bình khoảng 1,5 lần, tối đa khoảng 2 lần khoảng cách tick, và không đổi bất kể RTT - Loại trừ nếu: thời gian yêu cầu nằm trong server trung bình quanh mức một nửa khoảng cách tick → chỉ chờ tick một lần. Dài hơn khoảng cách tick và lúc dài lúc ngắn → xem vượt tick budget - Cách kiểm tra: Kiểm tra bằng công cụ hạ tầng (không cần code game) - Nguồn: - [Peeking into VALORANT's Netcode](https://www.riotgames.com/en/news/peeking-valorants-netcode) · Riot Games · Input tới phải chờ tối đa một tick cho tới ranh giới tick, rồi áp dụng và gửi đi lại tốn thêm một khung hình. Tick rate càng cao thì độ trễ này càng giảm - [VALORANT's 128-Tick Servers](https://www.riotgames.com/en/news/valorants-128-tick-servers) · Riot Games · Một phần độ trễ đến từ mạng, một phần đến từ tick rate của server - [NetworkTime and ticks (Netcode for GameObjects 2.5)](https://docs.unity3d.com/Packages/com.unity.netcode.gameobjects@2.5/manual/advanced-topics/networktime-ticks.html) · Unity · Thay đổi của NetworkVariable không được gửi ngay mà được gom lại gửi theo từng network tick #### sy-strict-check · Server kiểm tra quá chặt · Over-strict server validation Nếu server kiểm tra tốc độ di chuyển, hồi chiêu, tầm đánh quá chặt, cả những input hợp lệ bị dồn lại do jitter cũng bị từ chối. - Vì sao → Dẫn đến → Trên màn hình: Tiêu chí chặt như “quãng đường di chuyển được trong một tick”, “dung sai hồi chiêu 0 ms” → Jitter làm hai lệnh dồn tới trong cùng một tick thì bị coi là vi phạm quy tắc → Kéo ngược, hồi chiêu đã xong mà skill vẫn bị từ chối - Triệu chứng: Kéo ngược, Nuốt thao tác·rollback / Yếu tố: Jitter - Ai gặp: Chỉ mình tôi / Khi nào: Thỉnh thoảng bất chợt, Khi di chuyển hoặc chuyển bản đồ - Phụ trách chính: Phát triển server (Đội phát triển game) - Việc cần làm (Đội phát triển game): Kiểm tra theo kiểu hạn mức tích lũy (token bucket), chừa khoảng dư bằng ping và jitter. - Trên đồ thị: Thỉnh thoảng vọt lên bất chợt (Số lần server từ chối khi kiểm tra, số lần hiệu chỉnh vị trí) - Chỗ cần xem: Ghi vào log server cho mỗi lần từ chối hoặc hiệu chỉnh vị trí: lý do, số lệnh của người chơi đó tới trong tick đó, khoảng cách tới so với lệnh ngay trước - Đúng nếu: các lần từ chối, hiệu chỉnh dồn vào những lúc có từ 2 lệnh trở lên tới cùng một tick, trong khi lượng di chuyển và số lần sử dụng cộng theo đơn vị vài giây vẫn nằm trong quy tắc - Loại trừ nếu: cộng theo đơn vị vài giây mà vẫn vượt quy tắc → có thể là chạy quá tốc độ thật hoặc cheat. Từ chối dồn vào một nhà mạng cụ thể và vào buổi tối → xem báo nhầm khi kiểm tra, dồn vào người chơi dùng một nhà mạng - Cách kiểm tra: Cần log và chỉ số của server, client game - Nguồn: - [Source SDK 2013: player.cpp](https://raw.githubusercontent.com/ValveSoftware/source-sdk-2013/master/src/game/server/player.cpp) · Valve · Cho phép các lệnh dồn tới bằng ngân sách xử lý lệnh tích lũy qua mỗi tick (tối đa sv_maxusrcmdprocessticks 24 tick). Chú thích của nhà phát triển cho biết chặn chặt hơn thì người chơi bình thường cũng bị giật khựng - [RFC 2697: A Single Rate Three Color Marker](https://www.rfc-editor.org/rfc/rfc2697) · IETF · Token bucket: xét theo tốc độ trung bình (CIR) và kích thước burst cho phép trong một lần (CBS) #### sy-host · Cấu trúc host (chủ phòng) · Listen server / host advantage Nếu PC của một người chơi đóng vai server, đường truyền và hiệu năng PC của người đó quyết định cảm nhận của tất cả mọi người. - Vì sao → Dẫn đến → Trên màn hình: PC của chủ phòng đóng vai server (P2P, listen server) → Đường truyền hoặc PC của chủ phòng chậm thì lan sang mọi người, riêng chủ phòng có ping 0 → Chỉ chủ phòng được lợi, chủ phòng thoát thì mọi người đứng hình, mất kết nối - Triệu chứng: Giật khựng, Đứng hình, Mất kết nối / Yếu tố: Độ trễ, Ngưng trệ - Ai gặp: Một địa điểm hoặc kênh / Khi nào: Thỉnh thoảng bất chợt - Phụ trách chính: Phát triển server (Đội phát triển game) / Phối hợp: Phát triển client (Đội phát triển game), Hạ tầng server (Đội hạ tầng) - Việc cần làm (Đội phát triển game): Server: chuyển sang server chuyên dụng (dedicated server) đảm nhận phán định; trước khi làm được điều đó thì khi ghép trận chọn người có đường truyền và PC tốt làm chủ phòng. Client: hỗ trợ chuyển host (migration), khi ghép trận thì đo và gửi ping tới những người tham gia khác, tốc độ upload, hiệu năng PC. - Việc cần làm (Đội hạ tầng): Chuẩn bị thiết bị server, instance cho server chuyên dụng, đặt gần khu vực có nhiều người chơi. - Trên đồ thị: Chỉ một phần cao (Số báo cáo lag và mất kết nối theo chủ phòng) - Chỗ cần xem: Ghi vào log trận đấu tốc độ upload của chủ phòng (host), RTT của từng người tham gia tới chủ phòng, frame time trên PC chủ phòng, thời điểm chủ phòng thoát, rồi nhóm các báo cáo lag và mất kết nối theo chủ phòng. Người chơi cũng có thể tự kiểm tra bằng cách chơi lại với cùng nhóm người nhưng đổi chủ phòng - Đúng nếu: lag và mất kết nối dồn vào phòng của một chủ phòng cụ thể, khi tốc độ upload của chủ phòng đó thấp hoặc frame time dài thì mọi người tham gia cùng tệ đi, và nếu không có chuyển host thì mọi người mất kết nối đúng lúc chủ phòng thoát - Loại trừ nếu: chỉ những người tham gia ở cùng một khu vực bị tệ, bất kể chủ phòng là ai → vấn đề đường truyền hoặc tuyến đường. Nếu dùng cấu trúc server chuyên dụng thì không phải nguyên nhân này - Cách kiểm tra: Cần log và chỉ số của server, client game - Nguồn: - [Networking Overview for Unreal Engine](https://dev.epicgames.com/documentation/en-us/unreal-engine/networking-overview-for-unreal-engine) · Epic Games · Host của listen server có lợi thế hơn các client khác, và phải gánh cả server lẫn việc render nên tải lớn - [Distributed authority topologies (Netcode for GameObjects 2.5)](https://docs.unity3d.com/Packages/com.unity.netcode.gameobjects@2.5/manual/terms-concepts/distributed-authority.html) · Unity · Khi chủ sở hữu phiên thoát, tự động bầu chủ sở hữu mới trong số các client còn lại - [A Survey and Taxonomy of Latency Compensation Techniques for Network Computer Games (ACM Computing Surveys, 2022)](https://web.cs.wpi.edu/~claypool/papers/lag-taxonomy/paper.pdf) · ACM · Game P2P cũng có thể xem là cấu trúc client-server trong đó host kiêm vai trò server #### sy-optimistic-reject · Server từ chối sau khi đã hiển thị trước · Client-side feedback rejected by server Nếu server không công nhận đòn đánh hay skill đã được hiển thị trước trên màn hình của bạn, kết quả bạn thấy rõ ràng lại bị coi như chưa từng xảy ra. - Vì sao → Dẫn đến → Trên màn hình: Phát trước hiệu ứng đòn đánh, động tác skill khi server chưa xác nhận (hiển thị trước) → Server tính lại tầm đánh, vị trí mục tiêu, hồi chiêu, tài nguyên rồi từ chối → Hiệu ứng máu đã bắn ra mà không có sát thương, chỉ có động tác skill mà không có tác dụng, chỉ có hồi chiêu chạy - Triệu chứng: Nuốt thao tác·rollback, Kéo ngược / Yếu tố: Độ trễ - Ai gặp: Chỉ mình tôi, Chỉ một tính năng / Khi nào: Khi làm một thao tác nhất định - Phụ trách chính: Phát triển client (Đội phát triển game) / Phối hợp: Phát triển server (Đội phát triển game) - Việc cần làm (Đội phát triển game): Client: chỉ những phần cần xác nhận như số sát thương, cái chết, phần thưởng mới hiển thị theo kết quả server, kiểm tra trước các lý do từ chối thường gặp, khi bị từ chối thì hoàn lại hồi chiêu và tài nguyên rồi hiển thị lý do. Server: chừa khoảng dư bằng ping khi kiểm tra tầm đánh và vị trí mục tiêu, gửi kèm lý do trong phản hồi từ chối, thu thập tỷ lệ từ chối theo skill thành chỉ số. - Con số tham khảo: Phản hồi từ chối tới muộn sau khi bấm một khoảng bằng ping + thời gian chờ tick. Ping 150 ms thì trong khoảng 0,2 giây bạn cứ tưởng “đã đánh trúng”. - Trên đồ thị: Chỉ một phần cao (Tỷ lệ server từ chối theo skill (theo khoảng ping)) - Chỗ cần xem: Trên server, thu thập tỷ lệ từ chối và lý do từ chối (tầm đánh, vị trí mục tiêu, hồi chiêu, tài nguyên) theo từng skill, chia theo khoảng RTT của người chơi. Ở client, ghi lại số lần hành động đã hiển thị trước bị từ chối - Đúng nếu: từ chối dồn vào một skill cụ thể và vào lý do tầm đánh, vị trí mục tiêu, và ping càng cao thì tỷ lệ từ chối càng tăng - Loại trừ nếu: lý do từ chối là hồi chiêu, tài nguyên và không liên quan đến ping → xem giá trị dữ liệu (hồi chiêu, chi phí) ở client và server có khác nhau không. Không bị từ chối mà hiển thị chỉ bắt đầu sau khi server phản hồi → chỉ hiển thị sau khi server phản hồi (mô hình yêu cầu-phản hồi) - Cách kiểm tra: Cần log và chỉ số của server, client game - Tìm hiểu thêm: Hiển thị trước là cách tốt nhất để che ping. Tuy nhiên, thông tin client và server dùng để phán đoán (vị trí đối thủ, tài nguyên còn lại) càng khác nhau thì càng hay bị từ chối. Thu thập tỷ lệ từ chối theo skill thành chỉ số sẽ giúp dễ tìm ra chỗ phán định bị lệch. - Nguồn: - [Using Gameplay Abilities in Unreal Engine](https://dev.epicgames.com/documentation/en-us/unreal-engine/using-gameplay-abilities-in-unreal-engine) · Epic Games · Ability Local Predicted thực thi ngay ở client nhưng server quyết định cuối cùng và có thể lật ngược kết quả - [Latency Compensating Methods in Client/Server In-game Protocol Design and Optimization (Yahn W. Bernier, GDC 2001)](https://web.cs.wpi.edu/~claypool/courses/4513-B03/papers/games/bernier.pdf) · Valve · Dự đoán việc bắn vũ khí ở client để phát hiệu ứng trước, rồi sửa sai lệch dự đoán theo kết quả server #### sy-path-mismatch · Tính toán đường đi không khớp khi đồng bộ lệnh · Command sync with divergent pathing Nếu hai bên chỉ trao đổi “đi tới đây” còn đường đi thì mỗi bên tự tính, chỉ cần tính lệch một chút là nhân vật hay quái sẽ đi theo đường khác rồi bị kéo về chỗ cũ. - Vì sao → Dẫn đến → Trên màn hình: Khi click để di chuyển hoặc khi quái đuổi theo, chỉ gửi điểm đến còn đường đi thì client tự tính riêng → Do khác biệt dữ liệu địa hình, va chạm với nhân vật khác, khác thứ tự tính toán mà đi theo đường khác với server → Quái đi xuyên tường rồi bị dời đi chỗ khác trong nháy mắt, nhân vật di chuyển theo click đổi hướng như đang trượt - Triệu chứng: Dịch chuyển tức thời, Kéo ngược / Yếu tố: Độ trễ - Ai gặp: Một địa điểm hoặc kênh, Chỉ mình tôi / Khi nào: Khi di chuyển hoặc chuyển bản đồ - Phụ trách chính: Phát triển server (Đội phát triển game) / Phối hợp: Phát triển client (Đội phát triển game) - Việc cần làm (Đội phát triển game): Server: gửi kèm cả các điểm trung gian (waypoint) của đường đi, đồng bộ vị trí định kỳ. Client: cho sai lệch hội tụ dần một cách mượt mà, dùng cùng dữ liệu địa hình với server. - Trên đồ thị: Thỉnh thoảng vọt lên bất chợt (Số lần và khoảng cách hiệu chỉnh vị trí theo đối tượng) - Chỗ cần xem: Ghi lại chênh lệch giữa vị trí server gửi và vị trí client tính cho từng đối tượng, rồi chấm tọa độ xảy ra hiệu chỉnh lên bản đồ. Tóm tắt kết quả đường đi hoặc vị trí của hai bên bằng checksum rồi so sánh định kỳ thì tìm được thời điểm bắt đầu lệch - Đúng nếu: hiệu chỉnh dồn vào một địa hình cụ thể (bậc cửa, lối đi hẹp, dốc) hoặc nơi đông người, và lặp lại ở cùng vị trí ngay cả với người chơi có chỉ số mạng bình thường - Loại trừ nếu: chỉ hiệu chỉnh vào lúc mất gói hoặc jitter vọt lên, bất kể vị trí → vấn đề đường truyền. Một con quái cùng bị nhảy trên màn hình của nhiều người → xem quyền điều khiển quái có nằm ở client chậm không - Cách kiểm tra: Cần log và chỉ số của server, client game - Tìm hiểu thêm: Cách này là một trong những lý do khiến game click để di chuyển và game tab-target ít nhạy với ping. Đổi lại, không có gì bảo đảm kết quả hai bên giống nhau, nên nhất định phải có cơ chế thỉnh thoảng đồng bộ lại vị trí. Phép tính dấu phẩy động có thể cho kết quả hơi khác nhau tùy loại CPU, trình biên dịch và cấu hình tối ưu hóa của nó (kể cả khác biệt giữa bản build debug và release). Trong các cấu trúc chỉ trao đổi input và giả định kết quả tính toán hai bên giống hệt nhau như lockstep, rollback, những khác biệt nhỏ này có thể tích tụ lại và gây lệch (desync), khiến trạng thái game trên hai màn hình tách ra. - Nguồn: - [Deterministic Lockstep](https://gafferongames.com/post/deterministic_lockstep/) · Gaffer On Games · Dù tất định trên cùng một máy, kết quả dấu phẩy động vẫn có thể khác nhau khi trình biên dịch, OS, CPU khác nhau - [State Synchronization](https://gafferongames.com/post/state_synchronization/) · Gaffer On Games · Gửi trạng thái kèm với input thì có thể đồng bộ hai bên mà không cần tính tất định hoàn hảo - [Peeking into VALORANT's Netcode](https://www.riotgames.com/en/news/peeking-valorants-netcode) · Riot Games · Khi mất gói tin hoặc khi hai nhân vật cùng muốn đi vào một chỗ, mô phỏng trên server và client bị lệch nên cần hiệu chỉnh - [1500 Archers on a 28.8: Network Programming in Age of Empires and Beyond](https://www.gamedeveloper.com/programming/1500-archers-on-a-28-8-network-programming-in-age-of-empires-and-beyond) · Game Developer · Khác biệt rất nhỏ lớn dần theo thời gian khiến đường đi của đơn vị worker lệch dần. So sánh thế giới, đối tượng, tìm đường bằng checksum để phát hiện lệch (out-of-sync) - [Floating Point Determinism](https://gafferongames.com/post/floating_point_determinism/) · Gaffer On Games · Cùng một đoạn code dấu phẩy động cũng có thể cho kết quả khác nhau tùy trình biên dịch, kiến trúc CPU, bản build debug hay release. Có trường hợp CPU AMD và Intel trả về giá trị hơi khác nhau ở hàm siêu việt - [/fp (Specify floating-point behavior)](https://learn.microsoft.com/en-us/cpp/build/reference/fp-specify-floating-point-behavior) · Microsoft · /fp:fast có thể đổi thứ tự hoặc gộp các phép tính dấu phẩy động nên kết quả khác với các cấu hình /fp khác, và phép tính được gộp bằng FMA cũng có thể khác với kết quả nhân rồi cộng riêng #### sy-low-send-rate · Tần suất gửi snapshot thấp · Low snapshot / update rate Nếu server chỉ gửi bản cập nhật vị trí (snapshot) vài lần trong 1 giây, bộ đệm nội suy phải đặt dài tương ứng, nên bạn thấy các nhân vật khác ở quá khứ xa hơn. - Vì sao → Dẫn đến → Trên màn hình: Để tiết kiệm lưu lượng, chỉ gửi bản cập nhật vị trí 5–10 lần trong 1 giây → Muốn vẽ mượt thì bộ đệm phải bằng 2 lần khoảng cách gói (200–400 ms), đặt ngắn thì chỉ lỡ một gói là đối tượng khựng lại → Thấy đối thủ đổi hướng muộn và lệch với phán định. Bộ đệm ngắn thì giật khựng, khi mất gói thì dịch chuyển tức thời - Triệu chứng: Giật khựng, Dịch chuyển tức thời, Nuốt thao tác·rollback / Yếu tố: Độ trễ, Mất gói - Ai gặp: Cả server / Khi nào: Luôn luôn - Phụ trách chính: Phát triển server (Đội phát triển game) / Phối hợp: Phát triển client (Đội phát triển game) - Việc cần làm (Đội phát triển game): Server: đối tượng ở gần hoặc đang chiến đấu thì gửi thường xuyên, đối tượng ở xa thì gửi thưa, chỉ gửi phần thay đổi (nén delta) để giảm kích thước mỗi lần gửi và tăng tần suất. Client: tự động điều chỉnh độ dài bộ đệm nội suy theo khoảng cách gói. - Con số tham khảo: 10 lần trong 1 giây thì khoảng cách gói 100 ms, bộ đệm 200 ms. Cộng thêm độ trễ một chiều 75 ms của ping 150 ms thì bạn thấy đối thủ ở quá khứ khoảng 0,3 giây. - Trên đồ thị: Luôn cao ngay từ đầu (Khoảng cách gói tin tới theo client, độ dài bộ đệm nội suy) - Chỗ cần xem: Trong bản bắt gói phía server, lọc riêng luồng gửi tới một người chơi rồi dùng I/O Graphs của Wireshark để xem số gói mỗi giây và khoảng cách giữa các gói. Nếu có log phía game: xem cùng khoảng cách cập nhật theo đối tượng và phần dư của bộ đệm nội suy phía client (thời gian còn lại đến khi snapshot tiếp theo tới) - Đúng nếu: bản cập nhật vị trí luôn thưa, 5–10 lần mỗi giây (khoảng cách 100–200 ms), và bộ đệm nội suy được đặt trên 200 ms hoặc phần dư của bộ đệm thường xuyên về 0 - Loại trừ nếu: bản cập nhật được gửi dày mà chỉ khoảng cách tới dao động → xem jitter, mất gói. Khi đông người chỉ nhận thưa các đối tượng ở xa → ngân sách gửi và mức ưu tiên theo kết nối - Cách kiểm tra: Kiểm tra bằng công cụ hạ tầng (không cần code game) - Nguồn: - [Latency Compensating Methods in Client/Server In-game Protocol Design and Optimization (Yahn W. Bernier, GDC 2001)](https://web.cs.wpi.edu/~claypool/courses/4513-B03/papers/games/bernier.pdf) · Valve · Cập nhật 10 lần mỗi giây thì nội suy 200 ms chịu được một lần mất gói. Mặc định của Half-Life là 20 lần mỗi giây, nội suy 100 ms - [Snapshot Interpolation](https://gafferongames.com/post/snapshot_interpolation/) · Gaffer On Games · 10 gói mỗi giây thì cần độ trễ 350 ms để chịu được mất 2 gói liên tiếp, 30 gói mỗi giây thì giảm còn 150 ms - [State Synchronization](https://gafferongames.com/post/state_synchronization/) · Gaffer On Games · Dùng mức ưu tiên tích lũy để gửi các đối tượng quan trọng thường xuyên hơn, và gửi luân phiên các đối tượng còn lại trong giới hạn băng thông - [8.8. The “I/O Graphs” Window](https://www.wireshark.org/docs/wsug_html_chunked/ChStatIOGraphs.html) · Wireshark · Vẽ số gói và số byte khớp với bộ lọc hiển thị (display filter) thành đồ thị theo từng khoảng thời gian ### Sự cố chỉ một số người gặp (24 nguyên nhân) #### pt-slow-burst · Người chơi chậm di chuyển dồn cục trên màn hình người khác · Laggy player seen by others (bursty inputs) Input của người có đường truyền kém tới server lúc thưa lúc dồn. Nếu server áp dụng đúng số lượng nhận được ở mỗi tick, trong mắt người khác nhân vật đó sẽ khựng lại rồi đi một lúc nhiều bước. - Vì sao → Dẫn đến → Trên màn hình: Lệnh di chuyển của người chơi chậm có tick tới 0 lệnh, có tick tới 2–3 lệnh → Server áp dụng cùng lúc ở tick nhận được, nên vị trí nhân vật đó thay đổi như bậc thang → Trên màn hình người khác, chỉ nhân vật đó khựng lại rồi di chuyển dồn cục một lượt. Những thứ khác vẫn bình thường - Triệu chứng: Tua nhanh, Dịch chuyển tức thời / Yếu tố: Jitter, Mất gói - Ai gặp: Chỉ một nhân vật trông bất thường, Một khu vực hoặc nhà mạng / Khi nào: Luôn luôn, Khi di chuyển hoặc chuyển bản đồ - Phụ trách chính: Phát triển server (Đội phát triển game) / Phối hợp: Bên ngoài (Bên ngoài) - Việc cần làm (Đội phát triển game): Dùng bộ đệm input theo người chơi để chia đều lệnh khi áp dụng, dựa vào số thứ tự input để áp dụng theo đúng khoảng cách ban đầu; chỉ tăng bộ đệm nội suy trên màn hình người khác là chưa đủ (vì chính bản ghi vị trí trên server đã có dạng bậc thang). - Việc cần làm (Bên ngoài): Hướng dẫn người chơi bị chậm dùng kết nối có dây, kiểm tra Wi-Fi và router. - Con số tham khảo: Jitter 80 ms thì trên server 20 tick (50 ms), số lệnh mỗi tick dao động từ 0 đến 3. - Trên đồ thị: Chỉ một phần cao (Số lệnh được áp dụng mỗi tick theo người chơi, jitter theo người chơi) - Chỗ cần xem: Trong bản bắt gói phía server, lọc riêng các gói do người chơi bị báo cáo gửi, đếm số gói tới trong mỗi khoảng tick (ví dụ 50 ms) rồi so sánh với người chơi khác. Nếu có log server: xem theo từng người chơi số lệnh di chuyển được áp dụng mỗi tick và số thứ tự input - Đúng nếu: chỉ gói của người chơi bị báo cáo tới dồn cục, dao động giữa 0 và 2–3 gói mỗi tick, jitter và mất gói của người đó cao, còn gói của người chơi khác tới đều. Người đó chuyển sang mạng có dây thì hiện tượng giảm - Loại trừ nếu: nhiều nhân vật cùng di chuyển dồn cục một lúc → tick server bị chậm hoặc đường truyền của người đang xem. Gói tới và được áp dụng đều mà nhân vật đó vẫn trông như bị nhảy → vấn đề nội suy hoặc hiển thị ở phía người xem - Cách kiểm tra: Kiểm tra bằng công cụ hạ tầng (không cần code game) - Tìm hiểu thêm: Trong cấu trúc server có thẩm quyền, đây là hành vi bình thường. Lag của một người chơi chậm chỉ hiện ra với người khác dưới dạng “người đó di chuyển kỳ lạ”, và không ảnh hưởng đến thao tác của người khác hay chuyển động của quái. Tuy nhiên, những việc tương tác trực tiếp với người đó (giao dịch, cơ chế boss khi đi tổ đội, phán định PvP) sẽ cùng bị chậm. - Nguồn: - [Peeking into VALORANT's Netcode](https://www.riotgames.com/en/news/peeking-valorants-netcode) · Riot Games · Server đưa input đã tới vào hàng đợi di chuyển của từng người chơi theo thứ tự tick, nếu trống thì lấp bằng dự đoán. Hiệu chỉnh chỉ người chơi đó thấy, những người còn lại vẫn thấy mượt - [State Synchronization](https://gafferongames.com/post/state_synchronization/) · Gaffer On Games · Gói gửi 60 lần mỗi giây vẫn tới dồn cục, như khung hình này 2 gói, khung hình sau 0 gói - [Source SDK 2013: player.cpp](https://raw.githubusercontent.com/ValveSoftware/source-sdk-2013/master/src/game/server/player.cpp) · Valve · Chia các lệnh dồn tới ra xử lý qua nhiều tick server (meter out) #### pt-event-server · Tua nhanh do server xử lý ngay khi gói tới · Event-driven processing of bursty inputs Ở server xử lý và thông báo ngay khi gói tin tới, các hành động dồn lại của người chơi chậm sẽ được thực thi liên tiếp ngay lập tức. - Vì sao → Dẫn đến → Trên màn hình: Yêu cầu skill, di chuyển của người chơi chậm tới dồn lại → Server thực thi theo thứ tự ngay khi nhận và thông báo ngay cho mọi người → Trong mắt người khác, người đó dùng nhiều skill trong một khoảnh khắc hoặc di chuyển như tua nhanh - Triệu chứng: Tua nhanh / Yếu tố: Jitter - Ai gặp: Chỉ một nhân vật trông bất thường / Khi nào: Khi làm một thao tác nhất định, Luôn luôn - Phụ trách chính: Phát triển server (Đội phát triển game) / Phối hợp: Phát triển client (Đội phát triển game) - Việc cần làm (Đội phát triển game): Server: thực thi theo khoảng cách của thời điểm input gắn trong hành động (chỉ chấp nhận thời điểm trong phạm vi cho phép), hoặc giãn các hành động dồn tới theo khoảng cách tối thiểu (hồi chiêu chung) rồi thực thi lần lượt thay vì từ chối, không kiểm tra hồi chiêu chỉ dựa trên thời điểm tới (input hợp lệ sẽ bị nuốt). Client: gắn thời điểm input vào hành động khi gửi. - Trên đồ thị: Chỉ một phần cao (Khoảng cách thực thi hành động theo người chơi) - Chỗ cần xem: Ghi vào log server thời điểm tới, thời điểm thực thi, thời điểm input do client gắn (nếu có) của hành động theo từng người chơi, rồi so sánh khoảng cách thực thi với khoảng cách input. Xem cùng khoảng cách tới của gói tin người đó trong bản bắt gói phía server - Đúng nếu: khoảng cách input bình thường nhưng khoảng cách tới và thực thi trên server dồn lại chỉ còn vài ms, và lúc dồn lại trùng với thời điểm người khác báo cáo tua nhanh - Loại trừ nếu: khoảng cách thời điểm input đã dồn lại ngay từ đầu → xem client hoặc macro. Khoảng cách thực thi trên server đều mà chỉ trên màn hình người khác trông dồn lại → đường truyền của người xem - Cách kiểm tra: Cần log và chỉ số của server, client game - Nguồn: - [Understanding Networked Movement in the Character Movement Component for Unreal Engine](https://dev.epicgames.com/documentation/en-us/unreal-engine/understanding-networked-movement-in-the-character-movement-component-for-unreal-engine) · Epic Games · Server tính di chuyển mỗi khi nhận ServerMove, xác định khoảng thời gian bằng chênh lệch timestamp so với lần di chuyển trước. Nếu lệch quá nhiều so với giờ server thì bỏ lần di chuyển đó - [Deterministic Lockstep](https://gafferongames.com/post/deterministic_lockstep/) · Gaffer On Games · Áp dụng input ngay khi tới thì dù gửi ở 60 Hz, khoảng cách vẫn không đều và kết quả lúc thế này lúc thế khác #### pt-input-buffer · Kích thước bộ đệm input theo người chơi · Per-player server input buffer (jitter buffer) Nếu server gom input của mỗi người một chút rồi lấy ra một input mỗi tick, người khác thấy mượt, nhưng thời điểm hành động của chính người đó được xác nhận trên server sẽ muộn đi tương ứng. - Vì sao → Dẫn đến → Trên màn hình: Server gom input của người chơi chậm vào bộ đệm và áp dụng một input mỗi tick → Bộ đệm nhỏ thì hay bị trống, nhân vật đó đứng yên tại chỗ hoặc server đoán theo input cuối để di chuyển; bộ đệm lớn thì input của chính người đó được xác nhận muộn → Nhỏ thì trong mắt người khác bị khựng, lớn thì kết quả skill của chính người đó ra muộn (trễ thao tác) - Triệu chứng: Giật khựng, Trễ thao tác / Yếu tố: Jitter - Ai gặp: Chỉ một nhân vật trông bất thường, Chỉ mình tôi / Khi nào: Luôn luôn - Phụ trách chính: Phát triển server (Đội phát triển game) / Phối hợp: Phát triển client (Đội phát triển game) - Việc cần làm (Đội phát triển game): Server: tự động điều chỉnh kích thước bộ đệm theo tình trạng đường truyền của từng người, khi bị dồn thì lấy ra hai input một lần để bắt kịp, yêu cầu client của người hay bị trống bộ đệm gửi input sớm hơn. Client: gửi input sớm hơn một chút theo chỉ thị của server (điều chỉnh thời gian client). - Con số tham khảo: Tùy game nhưng thường là lượng input của 1–3 tick. VALORANT trên server 128 tick giữ bộ đệm phía server ngắn hơn, trung bình nửa khung hình (khoảng 4 ms). Kiểu thích ứng, chỉ tăng bộ đệm cho người có jitter lớn, là phổ biến. - Trên đồ thị: Chỉ một phần cao (Độ dài bộ đệm input, số lần bị trống theo người chơi) - Chỗ cần xem: Ghi trên server theo từng người chơi, mỗi tick: số input còn lại trong bộ đệm, số lần bộ đệm trống phải đoán theo input cuối để lấp, thời gian từ khi input tới đến khi được áp dụng - Đúng nếu: người có bộ đệm nhỏ bị trống nhiều lần và vào lúc đó dừng ngắn trên màn hình người khác, còn người có bộ đệm lớn thì thời gian từ input đến áp dụng tăng lên đúng bằng độ dài bộ đệm - Loại trừ nếu: bộ đệm gần như không bị trống mà trên màn hình người khác vẫn thấy giật khựng → vấn đề nội suy ở phía người xem. Bộ đệm ngắn mà trễ thao tác vẫn lớn → do chính RTT hoặc chờ tick hai lần - Cách kiểm tra: Cần log và chỉ số của server, client game - Nguồn: - [Peeking into VALORANT's Netcode](https://www.riotgames.com/en/news/peeking-valorants-netcode) · Riot Games · Server điều chỉnh mốc thời gian của client để giữ hàng đợi input ở mức độ trễ tối thiểu mà vẫn đủ hấp thụ việc gói tới không đều. Mục tiêu đệm trên server là trung bình nửa khung hình - [NetworkTimeSystem class (Netcode for GameObjects 2.5)](https://docs.unity3d.com/Packages/com.unity.netcode.gameobjects@2.5/api/Unity.Netcode.NetworkTimeSystem.html) · Unity · LocalBufferSec: thời gian server đệm message của client. Đẩy thời gian client lên sớm hơn để message tới server sớm hơn - [NetworkTime and ticks (Netcode for GameObjects 2.5)](https://docs.unity3d.com/Packages/com.unity.netcode.gameobjects@2.5/manual/advanced-topics/networktime-ticks.html) · Unity · Người chơi có đường truyền kém có thể tăng giá trị bộ đệm #### pt-isp-validation · Báo nhầm khi kiểm tra, dồn vào người chơi dùng một nhà mạng · Anti-cheat / movement validation false positives on bad ISPs Người chơi dùng đường truyền có jitter lớn có input tới dồn cục, nên hay bị vướng các bước kiểm tra tốc độ và hồi chiêu của server. - Vì sao → Dẫn đến → Trên màn hình: Jitter của đường truyền thuộc một nhà mạng hoặc khu vực tăng vào buổi tối → Server coi input hợp lệ tới dồn lại là chạy quá tốc độ hoặc vi phạm hồi chiêu → Chỉ người chơi dùng nhà mạng đó bị kéo ngược, bị từ chối skill, nặng thì bị server đá ra và mất kết nối - Triệu chứng: Kéo ngược, Nuốt thao tác·rollback, Mất kết nối / Yếu tố: Jitter - Ai gặp: Một khu vực hoặc nhà mạng, Chỉ mình tôi / Khi nào: Giờ cao điểm buổi tối, Khi di chuyển hoặc chuyển bản đồ - Phụ trách chính: Phát triển server (Đội phát triển game) / Phối hợp: Hạ tầng mạng (Đội hạ tầng) - Việc cần làm (Đội phát triển game): Kiểm tra bằng hạn mức tích lũy trải trên vài giây, nới tiêu chí theo tình trạng đường truyền (ping, jitter), có bước cảnh báo trước khi buộc thoát, dùng bộ đệm input theo người chơi để chia đều input dồn tới qua từng tick, giảm chính số lần báo nhầm. - Việc cần làm (Đội hạ tầng): Xem phân bố tỷ lệ mất gói và jitter theo nhà mạng và khung giờ rồi chia sẻ cho đội phát triển game, kiểm tra tuyến đường ở đoạn thuộc nhà mạng đó (mtr hai chiều), nếu cần thì đổi tuyến hoặc escalate lên nhà mạng. - Trên đồ thị: Chỉ cao vào một khung giờ (Số lần kiểm tra từ chối và buộc thoát theo nhà mạng (ASN), jitter theo nhà mạng) - Chỗ cần xem: Gắn nhà mạng (ASN) của IP kết nối và thời điểm vào log từ chối, hiệu chỉnh, buộc thoát khi kiểm tra trên server, rồi đếm theo nhà mạng và khung giờ. Đội hạ tầng chạy mtr hai chiều về phía nhà mạng đó cùng thời điểm để xem jitter và mất gói - Đúng nếu: từ chối và buộc thoát dồn vào một nhà mạng cụ thể và tăng vào buổi tối, cùng lúc đó jitter của nhà mạng này cũng cao, và lượng di chuyển cộng theo đơn vị vài giây vẫn nằm trong quy tắc - Loại trừ nếu: chỉ một số tài khoản nhất định lặp lại, bất kể nhà mạng → có thể là cheat thật. Tăng đồng loạt ở mọi nhà mạng → nguyên nhân phía server, tick server bị trễ khiến lệnh bị áp dụng dồn (vượt tick budget) - Cách kiểm tra: Cần log và chỉ số của server, client game - Nguồn: - [Source SDK 2013: player.cpp](https://raw.githubusercontent.com/ValveSoftware/source-sdk-2013/master/src/game/server/player.cpp) · Valve · Chặn chạy quá tốc độ bằng ngân sách lệnh tích lũy qua mỗi tick; chú thích của nhà phát triển cho biết giới hạn chặt hơn sẽ làm cả người chơi bình thường cũng bị giật khựng - [RFC 2697: A Single Rate Three Color Marker](https://www.rfc-editor.org/rfc/rfc2697) · IETF · Token bucket: xét theo tốc độ trung bình và kích thước burst cho phép - [Understanding Networked Movement in the Character Movement Component for Unreal Engine](https://dev.epicgames.com/documentation/en-us/unreal-engine/understanding-networked-movement-in-the-character-movement-component-for-unreal-engine) · Epic Games · Nếu chênh lệch timestamp giữa client và server lớn thì bỏ lần di chuyển đó hoặc xử lý bằng thủ tục giải quyết chênh lệch thời gian, tính theo giờ server để chống speed hack #### pt-raid-member · Một đồng đội chậm và cơ chế boss · One laggy member in a synchronized mechanic Trong cơ chế raid đòi hỏi mọi người cùng phản ứng vào một thời điểm định sẵn, phản ứng muộn của một người chậm sẽ thành thất bại của cả tổ đội. - Vì sao → Dẫn đến → Trên màn hình: Cơ chế tập thể như “tất cả cùng tản ra một lúc”, “một người bấm nút” → Người chậm thấy cảnh báo muộn, input cũng tới muộn → Cả tổ đội bị diệt vì một người đó, các đồng đội khác cảm thấy “tại người bị lag” - Triệu chứng: Nuốt thao tác·rollback, Trễ thao tác / Yếu tố: Độ trễ - Ai gặp: Một địa điểm hoặc kênh, Chỉ một nhân vật trông bất thường / Khi nào: Khi đông người, Khi làm một thao tác nhất định - Phụ trách chính: Phát triển server (Đội phát triển game) / Phối hợp: Phát triển client (Đội phát triển game) - Việc cần làm (Đội phát triển game): Server: nới khung phán định của cơ chế thêm bằng ping, gửi trước cảnh báo theo giờ server, thiết kế để thất bại của một người không dẫn tới diệt cả đội. Client: phát cảnh báo nhận được theo giờ server. - Trên đồ thị: Chỉ một phần cao (RTT của người chơi gây ra thất bại cơ chế) - Chỗ cần xem: Ghi vào log cơ chế trên server: người chơi gây ra thất bại, thời điểm input của người đó tới, khung phán định, RTT và mất gói của người đó - Đúng nếu: phần lớn input gây ra việc diệt đội là của cùng một người, RTT của người đó cao hơn hẳn mức trung bình của tổ đội, và input tới ngay sau khi khung phán định kết thúc - Loại trừ nếu: thất bại chia đều giữa các đồng đội → vấn đề do chính khung phán định quá ngắn (khung phán định ngắn bị ping ăn mất). Input của người chậm tới trong khung phán định mà vẫn thất bại → xem code phán định trên server - Cách kiểm tra: Cần log và chỉ số của server, client game - Nguồn: - [NetworkTime and ticks (Netcode for GameObjects 2.5)](https://docs.unity3d.com/Packages/com.unity.netcode.gameobjects@2.5/manual/advanced-topics/networktime-ticks.html) · Unity · Cách đặt lịch sự kiện theo giờ server để mọi người phát cùng một khoảnh khắc - [Factors influencing the latency of simple reaction time (Frontiers in Human Neuroscience, 2015)](https://doi.org/10.3389/fnhum.2015.00131) · Frontiers · Thời gian phản ứng đơn giản trung bình khoảng 231 ms #### pt-mob-control · Quyền điều khiển quái nằm ở client chậm · Monster movement delegated to a player client Có những game giao việc tính di chuyển của quái cho client của một người chơi ở gần để giảm tải server. Nếu đường truyền của người đó kém, con quái đó di chuyển kỳ lạ trên màn hình của mọi người. - Vì sao → Dẫn đến → Trên màn hình: Server giao việc tính di chuyển của quái cho client của người chơi gần nhất (hoặc đến trước) → Báo cáo kết quả của người được giao tới server muộn hoặc dồn lại → Chỉ con quái đó khựng lại rồi dịch chuyển tức thời trên màn hình của mọi người xung quanh. Trên màn hình của chính người được giao thì vẫn bình thường - Triệu chứng: Dịch chuyển tức thời, Giật khựng, Tua nhanh / Yếu tố: Jitter, Mất gói - Ai gặp: Chỉ một nhân vật trông bất thường, Một địa điểm hoặc kênh / Khi nào: Luôn luôn, Thỉnh thoảng bất chợt - Phụ trách chính: Phát triển server (Đội phát triển game) - Việc cần làm (Đội phát triển game): Chuyển quyền điều khiển cho người có đường truyền tốt (theo ping, mất gói), server thu hồi ngay khi báo cáo bị gián đoạn, quái quan trọng như boss thì server tự tính. - Trên đồ thị: Chỉ một phần cao (Khoảng cách báo cáo vị trí theo quái (theo client giữ quyền điều khiển)) - Chỗ cần xem: Ghi trên server cho mỗi con quái: client giữ quyền điều khiển, khoảng cách báo cáo, RTT, mất gói của client đó. Trong bản bắt gói phía server cũng xem được khoảng cách tới của các gói client đó gửi - Đúng nếu: mọi con quái di chuyển bất thường đều do cùng một người giữ quyền điều khiển, khoảng cách báo cáo của người đó lúc dài lúc ngắn hoặc bị ngắt, và chuyển quyền cho người khác thì trở lại bình thường ngay - Loại trừ nếu: quái do server tự tính cũng bị nhảy như vậy → tick server bị chậm hoặc đường truyền của người xem. Đã chuyển quyền mà vẫn tiếp tục nhảy → tính toán đường đi không khớp khi đồng bộ lệnh - Cách kiểm tra: Cần log và chỉ số của server, client game - Tìm hiểu thêm: Người được giao cảm thấy bình thường nên báo cáo chỉ gửi đến dưới dạng “quái bị lạ”. Nếu mọi người trừ một người đều thấy cùng một con quái bất thường, hãy kiểm tra trước xem ai đang giữ quyền điều khiển con quái đó. - Nguồn: - [Authority (Netcode for GameObjects 2.5)](https://docs.unity3d.com/Packages/com.unity.netcode.gameobjects@2.5/manual/terms-concepts/authority.html) · Unity · Trong mô hình phân quyền, mỗi instance game (client) giữ quyền trên một phần đối tượng mạng và tính toán các đối tượng đó - [Distributed authority topologies (Netcode for GameObjects 2.5)](https://docs.unity3d.com/Packages/com.unity.netcode.gameobjects@2.5/manual/terms-concepts/distributed-authority.html) · Unity · Quyền chia cho client thì không có mô phỏng duy nhất và dễ bị gian lận #### pt-heavy-char · Dữ liệu của một nhân vật quá lớn · One character with oversized data (inventory, mail, buffs) Nhân vật tích hàng nghìn vật phẩm, thư, hoặc có danh sách bạn bè, danh sách chặn, buff nhiều bất thường thì lượng dữ liệu phải xử lý khi vào game, khi lưu và khi thông báo cho xung quanh lớn gấp nhiều lần người khác. Chỉ nhân vật đó chậm, bất kể đường truyền. - Vì sao → Dẫn đến → Trên màn hình: Nhân vật chơi lâu năm hoặc nhận nhiều phần thưởng sự kiện có tới hàng nghìn món trong túi đồ, hộp thư → Mỗi lần vào game, chuyển bản đồ, lưu đều phải đọc và ghi DB tương ứng, thông tin trang bị và buff gửi cho xung quanh cũng lớn → Chỉ nhân vật đó loading vào game lâu, khựng khi mở túi đồ hoặc hộp thư. Nếu server chờ lưu trên thread game thì cả những người xung quanh cũng bị đứng hình chốc lát - Triệu chứng: Không vào được·kẹt loading, Trễ thao tác, Đứng hình / Yếu tố: Ngưng trệ - Ai gặp: Chỉ mình tôi, Chỉ một tính năng / Khi nào: Ngay sau đăng nhập hoặc bảo trì, Khi làm một thao tác nhất định, Khi di chuyển hoặc chuyển bản đồ - Phụ trách chính: Phát triển server (Đội phát triển game) / Phối hợp: Hạ tầng DB (Đội hạ tầng) - Việc cần làm (Đội phát triển game): Giới hạn sức chứa túi đồ và hộp thư, tự động dọn thư cũ, chia nhỏ để chỉ tải phần cần thiết, chỉ lưu phần thay đổi và lưu ngoài thread game. - Việc cần làm (Đội hạ tầng): Tìm trong slow query log các truy vấn chậm lặp lại với cùng một nhân vật rồi chuyển cho đội phát triển game, cung cấp danh sách top nhân vật có nhiều dòng vật phẩm, thư. - Con số tham khảo: Nếu mỗi vật phẩm là một dòng trong DB, nhân vật có 5.000 vật phẩm sẽ đọc 5.000 dòng mỗi lần vào game, gấp vài chục lần một nhân vật bình thường. - Trên đồ thị: Chỉ một phần cao (Thời gian vào game và lưu theo nhân vật, số dòng DB truy vấn theo nhân vật) - Chỗ cần xem: Trong log truy vấn chậm của DB (MySQL slow query log, PostgreSQL log_min_duration_statement), tìm các truy vấn đọc, lưu chậm lặp lại với cùng một ID nhân vật, và lấy danh sách top số dòng theo nhân vật trong bảng vật phẩm, thư - Đúng nếu: truy vấn chậm dồn vào vài ID nhân vật, số dòng vật phẩm, thư của các nhân vật đó gấp vài chục lần trung bình, và vào từ PC hoặc đường truyền khác vẫn chậm như vậy - Loại trừ nếu: nhân vật khác cùng tài khoản hoặc người chơi khác cũng cùng chậm → xem thiết bị DB hoặc khóa. Nhân vật đó bình thường trên PC khác → môi trường của người chơi - Cách kiểm tra: Kiểm tra bằng công cụ hạ tầng (không cần code game) - Tìm hiểu thêm: Nếu vào bằng cùng một nhân vật từ PC hoặc đường truyền khác vẫn chậm y như vậy, còn nhân vật khác cùng tài khoản thì bình thường, hãy nghi ngờ dữ liệu nhân vật. Đó là lý do báo cáo lag nhất định phải có tên nhân vật. - Nguồn: - [Extraneous Fetching antipattern](https://learn.microsoft.com/en-us/azure/architecture/antipatterns/extraneous-fetching/) · Microsoft Azure · Lấy nhiều dữ liệu hơn mức cần thiết thì tải I/O tăng và phản hồi chậm đi - [PostgreSQL Documentation: Error Reporting and Logging](https://www.postgresql.org/docs/current/runtime-config-logging.html) · PostgreSQL · log_min_duration_statement: ghi lại các câu SQL mất từ một khoảng thời gian nhất định trở lên để theo dõi truy vấn chậm - [The Slow Query Log](https://dev.mysql.com/doc/refman/8.4/en/slow-query-log.html) · MySQL · Slow query log ghi lại các truy vấn vượt quá long_query_time #### pt-phase · Khác kênh, instance hoặc phasing · Different channel / instance / phase Nếu hai nhân vật ở khác kênh hoặc khác instance, hoặc ở “phasing” khác nhau (NPC hiển thị khác nhau tùy tiến độ nhiệm vụ), hai bên sẽ thấy hai thế giới khác nhau. - Vì sao → Dẫn đến → Trên màn hình: Nhân vật thứ hai được xếp vào kênh khác hoặc đang ở bước nhiệm vụ khác → Server không gửi NPC đó cho nhân vật này (bình thường) → Chỉ một bên không có NPC. Trông như lỗi nhưng đúng theo thiết kế - Triệu chứng: Không hiển thị·đối tượng ma / Yếu tố: Mất gói - Ai gặp: Chỉ một client trên cùng PC, Chỉ mình tôi / Khi nào: Luôn luôn, Ngay sau đăng nhập hoặc bảo trì - Phụ trách chính: Phát triển server (Đội phát triển game) / Phối hợp: Phát triển client (Đội phát triển game) - Việc cần làm (Đội phát triển game): Server: gửi thông tin kênh và phasing cho client, thêm “kiểm tra kênh và bước nhiệm vụ của hai nhân vật” vào checklist QA. Client: hiển thị kênh và phasing trên màn hình. - Trên đồ thị: Chỉ một phần cao (Số đối tượng xung quanh theo client, kênh và phasing) - Chỗ cần xem: So sánh số kênh và bước tiến độ của nhiệm vụ liên quan giữa hai nhân vật trên màn hình game, rồi chỉnh cho cùng kênh, cùng bước để xem lại. Nếu có log gửi đối tượng của server: kiểm tra lý do NPC đó không được gửi cho nhân vật này (kênh, phasing) - Đúng nếu: hai nhân vật khác kênh hoặc khác bước nhiệm vụ, và chỉnh cho giống nhau thì thấy NPC - Loại trừ nếu: cùng kênh, cùng bước mà vẫn chỉ một bên không có → xem bỏ thông báo xuất hiện tới trong lúc loading, mất thông tin xuất hiện dồn tới ngay sau khi vào, thứ tự đăng ký tầm nhìn bị rối - Cách kiểm tra: Kiểm tra ở môi trường phía người chơi - Tìm hiểu thêm: Cũng cần kiểm tra tiến độ nhiệm vụ được lưu theo tài khoản hay theo nhân vật. Nếu là hai nhân vật cùng tài khoản, tiến độ của bên này có thể làm thay đổi phasing của bên kia. - Nguồn: - [Actor Relevancy in Unreal Engine](https://dev.epicgames.com/documentation/en-us/unreal-engine/actor-relevancy-in-unreal-engine) · Epic Games · Server chỉ replicate các actor có liên quan (relevant) cho từng kết nối, actor không liên quan thì không gửi - [Object visibility (Netcode for GameObjects 2.5)](https://docs.unity3d.com/Packages/com.unity.netcode.gameobjects@2.5/manual/basics/object-visibility.html) · Unity · Dùng CheckObjectVisibility để xác định đối tượng hiển thị cho từng client, đối tượng bị ẩn thì không gửi cho client đó #### pt-loading-drop · Bỏ thông báo xuất hiện tới trong lúc loading · Spawn messages dropped before the client is ready Ngay khi nhân vật vào zone, server gửi thông báo xuất hiện của các NPC xung quanh, nhưng client vẫn đang tải bản đồ nên bỏ thông báo đó đi. - Vì sao → Dẫn đến → Trên màn hình: Server gửi thông báo xuất hiện của các đối tượng xung quanh ngay sau khi xử lý vào zone → Client đang loading nên chưa có message handler, thông báo bị bỏ → Server coi như đã gửi nên không gửi lại. NPC không hiển thị cho đến khi ra khỏi tầm nhìn rồi quay lại - Triệu chứng: Không hiển thị·đối tượng ma / Yếu tố: Mất gói - Ai gặp: Chỉ một client trên cùng PC, Chỉ mình tôi / Khi nào: Ngay sau đăng nhập hoặc bảo trì, Khi di chuyển hoặc chuyển bản đồ - Phụ trách chính: Phát triển client (Đội phát triển game) / Phối hợp: Phát triển server (Đội phát triển game) - Việc cần làm (Đội phát triển game): Client: loading xong thì gửi “sẵn sàng”, hoặc giữ lại các gói nhận được trong lúc loading rồi xử lý sau. Server: chỉ gửi thông tin xung quanh sau khi nhận “sẵn sàng”. - Con số tham khảo: Nếu hai client cùng loading trên một PC, hoặc bên đang loading là cửa sổ chạy nền, CPU và ổ đĩa bị chia sẻ, việc xử lý cũng bị hạn chế, nên thời gian loading bên đó có thể dài gấp nhiều lần. Server xử lý vào zone nhanh hơn cũng làm lộ ra cùng lỗi này. - Trên đồ thị: Chỉ một phần cao (Thời gian loading theo client, số message bị bỏ trong lúc loading) - Chỗ cần xem: So sánh số lượng, loại message client nhận rồi bỏ trong lúc loading và thời điểm loading xong với thời điểm server gửi thông báo xuất hiện. Dễ tái hiện khi cho hai client cùng loading trên một PC, hoặc để bên đang loading ở cửa sổ chạy nền - Đúng nếu: server đã gửi thông báo xuất hiện của NPC không hiển thị, thời điểm tới là trước khi loading xong, và vào lúc đó số message bị bỏ tăng lên. Chỉ xảy ra ở client loading lâu hơn - Loại trừ nếu: thông báo xuất hiện tới sau khi loading xong mà vẫn không thấy → xem mất snapshot gốc hoặc nhầm lẫn do dùng lại ID đối tượng. Server hoàn toàn không gửi thông báo về NPC đó → xem thứ tự đăng ký tầm nhìn bị rối hoặc khác kênh, instance, phasing - Cách kiểm tra: Cần log và chỉ số của server, client game - Nguồn: - [NetworkConfig class (Netcode for GameObjects 2.5)](https://docs.unity3d.com/Packages/com.unity.netcode.gameobjects@2.5/api/Unity.Netcode.NetworkConfig.html) · Unity · SpawnTimeout: message của đối tượng chưa được tạo sẽ được giữ lại, nếu không được tạo trong thời hạn thì bỏ - [Actor Relevancy in Unreal Engine](https://dev.epicgames.com/documentation/en-us/unreal-engine/actor-relevancy-in-unreal-engine) · Epic Games · Actor không còn liên quan bị xóa ở client, khi liên quan trở lại thì được replicate mới #### pt-aoi-race · Thứ tự đăng ký tầm nhìn bị rối · Interest-management race on enter/leave Nếu thời điểm nhân vật được đăng ký vào lưới tầm nhìn trùng với thời điểm NPC chuyển ô lưới, thông báo xuất hiện của NPC đó có thể bị sót. - Vì sao → Dẫn đến → Trên màn hình: Việc xử lý vào zone, chuyển kênh, teleport trùng cùng lúc với việc NPC di chuyển → NPC đó bị sót khi tính “các đối tượng mới lọt vào tầm nhìn” → Chỉ vài NPC cụ thể không hiển thị, hoặc NPC đã rời đi vẫn còn đó - Triệu chứng: Không hiển thị·đối tượng ma / Yếu tố: Mất gói - Ai gặp: Chỉ một client trên cùng PC, Chỉ mình tôi / Khi nào: Khi di chuyển hoặc chuyển bản đồ, Thỉnh thoảng bất chợt - Phụ trách chính: Phát triển server (Đội phát triển game) - Việc cần làm (Đội phát triển game): Xử lý cập nhật tầm nhìn trên một thread và theo một thứ tự, định kỳ đồng bộ lại toàn bộ “danh sách đang thấy”. - Trên đồ thị: Thỉnh thoảng vọt lên bất chợt (Số chênh lệch giữa danh sách đang thấy trên server và danh sách đối tượng ở client) - Chỗ cần xem: Ghi trên server việc đăng ký vào lưới tầm nhìn, việc đối tượng chuyển ô, việc gửi thông báo xuất hiện và rời đi kèm số thứ tự tick, rồi định kỳ so sánh “danh sách đang thấy” của server với danh sách client đang có - Đúng nếu: NPC bị sót đã chuyển ô trong cùng tick với lúc xử lý vào zone hoặc teleport của nhân vật đó, và không có bản ghi gửi thông báo xuất hiện cho NPC đó - Loại trừ nếu: thông báo xuất hiện đã gửi nhưng client không nhận được hoặc đã bỏ → phía truyền tải (mất thông tin xuất hiện dồn tới ngay sau khi vào, bỏ thông báo xuất hiện tới trong lúc loading). Luôn chỉ sót cùng một NPC → xem phasing hoặc khác tùy chọn hiển thị - Cách kiểm tra: Cần log và chỉ số của server, client game - Nguồn: - [Replication Graph in Unreal Engine](https://dev.epicgames.com/documentation/en-us/unreal-engine/replication-graph-in-unreal-engine) · Epic Games · MMORPG và các game tương tự chia thế giới thành lưới, mỗi ô có danh sách actor, và gửi dữ liệu dựa trên ô mà client đang ở - [Actor Relevancy in Unreal Engine](https://dev.epicgames.com/documentation/en-us/unreal-engine/actor-relevancy-in-unreal-engine) · Epic Games · Tính liên quan được xét theo từng kết nối, actor không còn liên quan sẽ bị xóa ở client #### pt-baseline · Mất snapshot gốc (baseline) · Lost baseline for delta compression Với cách server chỉ gửi “những gì khác so với lần trước”, nếu mất thông tin đầy đủ (bản gốc) gửi một lần lúc đầu thì các phần thay đổi sau đó không thể áp dụng được. - Vì sao → Dẫn đến → Trên màn hình: Gói thông tin đầy đủ (bản gốc) của đối tượng bị mất hoặc bị bỏ trước khi xử lý → Client không có đối tượng để áp dụng các phần thay đổi về sau nên bỏ qua → Đối tượng đó không hiển thị, hoặc một lúc lâu sau mới đột ngột xuất hiện - Triệu chứng: Không hiển thị·đối tượng ma, Dịch chuyển tức thời / Yếu tố: Mất gói - Ai gặp: Chỉ một client trên cùng PC, Chỉ mình tôi / Khi nào: Thỉnh thoảng bất chợt - Phụ trách chính: Phát triển server (Đội phát triển game) / Phối hợp: Phát triển client (Đội phát triển game) - Việc cần làm (Đội phát triển game): Server: bản gốc phải được truyền lại cho đến khi nhận được xác nhận (ACK), chỉ tạo phần thay đổi dựa trên bản gốc mà client đã xác nhận đã nhận. Client: chỉ gửi xác nhận (ACK) sau khi đã thực sự áp dụng bản gốc, khi nhận phần thay đổi của đối tượng lạ thì yêu cầu server gửi lại. - Trên đồ thị: Thỉnh thoảng vọt lên bất chợt (Số lần nhận phần thay đổi của đối tượng lạ) - Chỗ cần xem: Đối chiếu số lần và ID đối tượng mà client bỏ phần thay đổi vì không có bản gốc với thời điểm server gửi bản gốc của đối tượng đó và thời điểm nhận ACK. Trong môi trường dev, thêm mất gói (loss của tc netem, tỷ lệ mất gói trong mô phỏng mạng của Unreal) để tái hiện - Đúng nếu: với đối tượng không hiển thị, server đã gửi bản gốc nhưng chưa nhận ACK mà vẫn liên tục chỉ gửi phần thay đổi, và client bỏ các phần thay đổi đó - Loại trừ nếu: bản gốc đã được ACK và áp dụng ở client mà vẫn không thấy → xem mất thông báo rời đi hoặc nhầm lẫn do dùng lại ID đối tượng - Cách kiểm tra: Cần log và chỉ số của server, client game - Nguồn: - [Snapshot Compression](https://gafferongames.com/post/snapshot_compression/) · Gaffer On Games · Phần thay đổi chỉ được tạo dựa trên bản gốc (baseline) mà bên kia đã xác nhận (ack) đã nhận, còn trạng thái ban đầu được gửi riêng - [Quake III Arena source: code/server/sv_snapshot.c](https://raw.githubusercontent.com/id-Software/Quake-III-Arena/master/code/server/sv_snapshot.c) · id Software · Nén delta dựa trên snapshot client đã xác nhận, nếu bản gốc quá cũ thì gửi snapshot đầy đủ - [tc-netem(8) — Linux manual page](https://man7.org/linux/man-pages/man8/tc-netem.8.html) · iproute2 · Công cụ thử nghiệm giả lập mạng thực bằng cách thêm độ trễ, jitter (delay TIME JITTER) và mất gói (loss random PERCENT) vào gói tin đi ra - [Using Network Emulation in Unreal Engine](https://dev.epicgames.com/documentation/en-us/unreal-engine/using-network-emulation-in-unreal-engine) · Epic Games · Thử nghiệm bằng cách đặt độ trễ tối thiểu, tối đa và tỷ lệ mất gói cho server và client; trên console thì cấu hình như NetEmulation.PktLag #### pt-ghost · Mất thông báo rời đi (đối tượng ma) · Missed despawn (ghost entity) Ngược lại, nếu bỏ lỡ thông báo “đã biến mất”, NPC hoặc người chơi đã chết hay đã rời đi vẫn còn trên màn hình của riêng bạn. - Vì sao → Dẫn đến → Trên màn hình: Thông báo chết, rời đi, ra khỏi tầm nhìn bị mất hoặc sai thứ tự → Client cho rằng đối tượng đó vẫn còn → Quái bị đánh mà không phản ứng, người chơi đã thoát vẫn đứng đó - Triệu chứng: Không hiển thị·đối tượng ma / Yếu tố: Mất gói - Ai gặp: Chỉ một client trên cùng PC, Chỉ mình tôi / Khi nào: Thỉnh thoảng bất chợt, Khi đông người - Phụ trách chính: Phát triển server (Đội phát triển game) / Phối hợp: Phát triển client (Đội phát triển game) - Việc cần làm (Đội phát triển game): Server: định kỳ gửi “danh sách đang thấy hiện tại”. Client: xóa các đối tượng không có trong danh sách, ẩn đối tượng lẽ ra phải di chuyển mà lâu không được cập nhật. - Trên đồ thị: Thỉnh thoảng vọt lên bất chợt (Số đối tượng chỉ còn ở client) - Chỗ cần xem: So sánh “danh sách đang thấy hiện tại” server gửi với danh sách đối tượng client đang có để đếm các đối tượng chỉ có ở client, rồi đối chiếu log gửi và nhận thông báo rời đi theo ID đối tượng - Đúng nếu: server đã gửi thông báo rời đi của đối tượng ma nhưng client không có bản ghi nhận, hoặc thông báo rời đi tới trước thông báo xuất hiện nên thứ tự bị đảo - Loại trừ nếu: đối tượng đó vẫn còn trong danh sách đang thấy của server → server bỏ sót việc dọn đối tượng. Ngay sau khi một đối tượng mới xuất hiện với cùng ID → nhầm lẫn do dùng lại ID đối tượng - Cách kiểm tra: Cần log và chỉ số của server, client game - Nguồn: - [Actor Relevancy in Unreal Engine](https://dev.epicgames.com/documentation/en-us/unreal-engine/actor-relevancy-in-unreal-engine) · Epic Games · Actor động không còn liên quan sẽ bị xóa ở client - [Object visibility (Netcode for GameObjects 2.5)](https://docs.unity3d.com/Packages/com.unity.netcode.gameobjects@2.5/manual/basics/object-visibility.html) · Unity · Khi đối tượng bị ẩn, client đó despawn và xóa đối tượng #### pt-spawn-burst · Mất thông tin xuất hiện dồn tới ngay sau khi vào · Initial spawn burst lost (unreliable channel, receive buffer, fragmentation) Ngay khi nhân vật bước vào zone, server gửi cùng lúc thông tin xuất hiện của vài chục đến vài trăm đối tượng xung quanh. Nếu gửi chúng qua kênh không tin cậy (unreliable), hoặc bộ đệm nhận bị tràn trong lúc client đang loading nên không đọc socket, một phần sẽ biến mất và không được gửi lại. - Vì sao → Dẫn đến → Trên màn hình: Ngay sau khi vào, thông tin xuất hiện dồn tới trong khoảnh khắc ngắn → Client đang loading đọc socket muộn nên bộ đệm nhận của OS bị tràn, hoặc gói UDP lớn bị phân mảnh nên chỉ mất một fragment là mất cả gói. Nếu là kênh không tin cậy thì cũng không được gửi lại → Chỉ ở client loading chậm hơn mới thiếu vài NPC. Ra khỏi tầm nhìn rồi quay lại thì thấy - Triệu chứng: Không hiển thị·đối tượng ma / Yếu tố: Mất gói - Ai gặp: Chỉ một client trên cùng PC, Chỉ mình tôi / Khi nào: Ngay sau đăng nhập hoặc bảo trì, Khi di chuyển hoặc chuyển bản đồ - Phụ trách chính: Phát triển server (Đội phát triển game) / Phối hợp: Phát triển client (Đội phát triển game) - Việc cần làm (Đội phát triển game): Server: thông báo xuất hiện và rời đi nhất định phải gửi qua kênh tin cậy có bảo đảm truyền lại, chia nhỏ thông tin ban đầu khi gửi. Client: nhận dữ liệu trên thread tách riêng khỏi loading, tăng kích thước bộ đệm nhận. - Con số tham khảo: Giá trị mặc định của bộ đệm nhận UDP trên PC khác nhau tùy OS nhưng thường khoảng vài chục đến vài trăm KB. Nếu thông tin khi vào một thị trấn đông người lớn hơn mức này, chỉ cần vì loading mà không đọc socket trong giây lát là bị tràn. - Trên đồ thị: Tăng vọt ngay sau đăng nhập hoặc bảo trì (Lượng dữ liệu nhận ngay sau khi vào, số thông báo xuất hiện bị sót) - Chỗ cần xem: So sánh số thông báo xuất hiện server gửi ngay sau khi vào với số client nhận được, và xem đã gửi qua kênh nào (tin cậy hay không tin cậy). Trong bản bắt gói phía server, xem lượng dữ liệu gửi tới người chơi đó ngay sau khi vào và các gói bị phân mảnh (filter Wireshark ip.flags.mf == 1 || ip.frag_offset > 0) - Đúng nếu: số nhận ít hơn số gửi, phần thiếu tập trung ở đoạn dồn ngay sau khi vào, và đã gửi qua kênh không tin cậy hoặc gói lớn bị phân mảnh. Xảy ra thường hơn ở client loading chậm hơn - Loại trừ nếu: số gửi và số nhận bằng nhau mà vẫn không thấy → đã bị bỏ sau khi nhận (bỏ thông báo xuất hiện tới trong lúc loading) hoặc vấn đề tính toán tầm nhìn. Thiếu liên tục, không liên quan tới lúc vừa vào → mất gói trên đường truyền - Cách kiểm tra: Cần log và chỉ số của server, client game - Nguồn: - [RFC 8085: UDP Usage Guidelines](https://www.rfc-editor.org/rfc/rfc8085) · IETF · Gói bị phân mảnh chỉ cần mất một fragment là mất cả gói - [UDP vs. TCP](https://gafferongames.com/post/udp_vs_tcp/) · Gaffer On Games · UDP không bảo đảm việc truyền tới và thứ tự, gói bị mất phải tự phát hiện rồi gửi lại - [Socket.ReceiveBufferSize Property](https://learn.microsoft.com/en-us/dotnet/api/system.net.sockets.socket.receivebuffersize) · Microsoft · Kích thước mặc định của bộ đệm nhận socket khác nhau tùy OS - [Display Filter Reference: Internet Protocol Version 4](https://www.wireshark.org/docs/dfref/i/ip.html) · Wireshark · Dùng ip.flags.mf (More fragments), ip.frag_offset (Fragment Offset) để lọc các gói IP bị phân mảnh #### pt-id-reuse · Nhầm lẫn do dùng lại ID đối tượng · Entity ID reused without a generation counter Khi NPC đã chết xuất hiện lại mà server dùng lại cùng ID đối tượng, client đã bỏ lỡ thông báo rời đi trong khoảng đó sẽ nhầm NPC mới là NPC cũ. - Vì sao → Dẫn đến → Trên màn hình: NPC chết rồi xuất hiện lại với cùng ID đối tượng → Client đã bỏ lỡ thông báo rời đi coi đây là “đối tượng đã biết” nên bỏ qua thông báo xuất hiện, hoặc giữ nguyên trạng thái đã chết → NPC không có trên màn hình của một bên, hoặc hiện ở trạng thái đã gục, cũng có khi hiện thành hình dạng của NPC khác - Triệu chứng: Không hiển thị·đối tượng ma / Yếu tố: Mất gói - Ai gặp: Chỉ một client trên cùng PC, Chỉ mình tôi / Khi nào: Thỉnh thoảng bất chợt, Khi đông người - Phụ trách chính: Phát triển server (Đội phát triển game) / Phối hợp: Phát triển client (Đội phát triển game) - Việc cần làm (Đội phát triển game): Server: gắn số thế hệ (generation) vào ID đối tượng để phân biệt lần dùng lại. Client: khi nhận thông báo xuất hiện với ID đã biết thì xóa đối tượng cũ và tạo mới. - Trên đồ thị: Thỉnh thoảng vọt lên bất chợt (Số thông báo xuất hiện với ID đã biết) - Chỗ cần xem: Ghi trên server thời điểm tạo và xóa theo từng ID đối tượng (kèm số thế hệ nếu có), đếm số lần client nhận thông báo xuất hiện với ID đã biết và số lần xóa rồi tạo lại bị xử lý thành “không thay đổi” khi cập nhật tầm nhìn - Đúng nếu: ID của NPC không hiển thị hoặc hiện ở trạng thái đã gục trùng với NPC vừa chết ngay trước đó, và trong khoảng đó client ấy không nhận được thông báo rời đi, hoặc server không gửi cả thông báo rời đi lẫn thông báo xuất hiện - Loại trừ nếu: ID có số thế hệ và số này cũng được dùng khi so sánh → không phải nguyên nhân này. ID không bị dùng lại mà vẫn không thấy → xem phía mất thông báo xuất hiện - Cách kiểm tra: Cần log và chỉ số của server, client game - Tìm hiểu thêm: Lỗi này cũng xảy ra ở phía server. Nếu chỉ so sánh danh sách đối tượng trong tầm nhìn bằng ID, NPC đã chết rồi được spawn lại với cùng ID giữa hai lần cập nhật tầm nhìn sẽ bị coi là “không thay đổi”, và server không gửi cả thông báo rời đi lẫn thông báo xuất hiện. Nếu thời điểm cập nhật tầm nhìn lệch nhau giữa từng người, chỉ client rơi đúng vào khoảnh khắc đó mới gặp lỗi. - Nguồn: - [Entity struct (Entities 1.3)](https://docs.unity3d.com/Packages/com.unity.entities@1.3/api/Unity.Entities.Entity.html) · Unity · Entity gồm Index và số thế hệ (Version), để phân biệt Index được dùng lại còn hợp lệ hay không - [NetworkConfig class (Netcode for GameObjects 2.5)](https://docs.unity3d.com/Packages/com.unity.netcode.gameobjects@2.5/api/Unity.Netcode.NetworkConfig.html) · Unity · RecycleNetworkIds, NetworkIdRecycleDelay: để trống network ID một khoảng thời gian rồi mới dùng lại #### pt-port-collision · Xung đột cổng UDP cố định · Two clients bound to the same local UDP port Nếu client được viết để dùng một cổng cục bộ cố định, client thứ hai trên cùng PC sẽ không dùng được cổng đó hoặc phải chia gói tin nhận được với client thứ nhất. - Vì sao → Dẫn đến → Trên màn hình: Hai client cùng mở một cổng UDP cục bộ (dùng tùy chọn reuse để ép dùng chung) → OS chỉ chuyển gói tin đến cho một socket, hoặc không bảo đảm bên nào nhận. Router và server cũng thấy hai client là cùng một địa chỉ → Một bên không nhận được gói tin thế giới nên NPC và người chơi khác không hiển thị, hoặc bị mất kết nối - Triệu chứng: Không hiển thị·đối tượng ma, Mất kết nối, Không vào được·kẹt loading / Yếu tố: Mất gói - Ai gặp: Chỉ một client trên cùng PC / Khi nào: Ngay sau đăng nhập hoặc bảo trì, Luôn luôn - Phụ trách chính: Phát triển client (Đội phát triển game) / Phối hợp: Phát triển server (Đội phát triển game) - Việc cần làm (Đội phát triển game): Client: để OS tự chọn cổng cục bộ (bind cổng 0). Server: phân biệt kết nối bằng session token cấp cho từng kết nối. - Trên đồ thị: Chỉ một phần cao (Số gói tin nhận theo client) - Chỗ cần xem: Trên PC của người chơi, bật cả hai client rồi chạy netstat -ano -p udp trong Command Prompt để xem cổng UDP cục bộ mà từng tiến trình game (PID) mở. Phía server: kiểm tra hai phiên có vào từ cùng IP công cộng, cùng cổng không - Đúng nếu: hai tiến trình game bị gắn vào cùng một cổng cục bộ, hoặc trên server hai phiên hiện cùng IP và cổng. Chỉ bật một bên thì bình thường - Loại trừ nếu: hai client dùng hai cổng cục bộ khác nhau mà một bên vẫn bất thường → xem lỗi phân biệt phiên theo IP hoặc thiết bị, hoặc giới hạn chạy nhiều client - Cách kiểm tra: Kiểm tra ở môi trường phía người chơi - Nguồn: - [Using SO_REUSEADDR and SO_EXCLUSIVEADDRUSE](https://learn.microsoft.com/en-us/windows/win32/winsock/using-so-reuseaddr-and-so-exclusiveaddruse) · Microsoft · Gọi bind lần thứ hai vào cùng một cổng bằng SO_REUSEADDR sẽ chiếm luôn cổng, và không thể biết socket nào sẽ nhận gói tin - [bind function (winsock.h)](https://learn.microsoft.com/en-us/windows/win32/api/winsock/nf-winsock-bind) · Microsoft · Gọi bind với cổng 0 thì được cấp một cổng riêng trong dải cổng động (49152–65535) - [netstat](https://learn.microsoft.com/en-us/windows-server/administration/windows-commands/netstat) · Microsoft · -a hiển thị cổng TCP, UDP; -n hiển thị địa chỉ dạng số; -o hiển thị ID tiến trình (PID); -p udp chỉ hiển thị UDP #### pt-session-key · Lỗi phân biệt phiên theo IP hoặc thiết bị · Session keyed by IP or machine ID Nếu server hoặc server trung gian phân biệt kết nối bằng IP hay ID thiết bị, hai client trên cùng một PC (cùng IP công cộng) sẽ bị coi là một người. - Vì sao → Dẫn đến → Trên màn hình: Bảng phiên được lập theo IP hoặc IP + ID thiết bị → Thông tin của client thứ hai ghi đè lên hoặc bị trộn vào phiên thứ nhất → Một bên không thấy NPC, bên kia bị mất kết nối hoặc nhận nhầm thông tin của người khác - Triệu chứng: Không hiển thị·đối tượng ma, Mất kết nối / Yếu tố: Mất gói - Ai gặp: Chỉ một client trên cùng PC, Cùng một nhà, Một khu vực hoặc nhà mạng / Khi nào: Ngay sau đăng nhập hoặc bảo trì - Phụ trách chính: Phát triển server (Đội phát triển game) / Phối hợp: Phát triển client (Đội phát triển game) - Việc cần làm (Đội phát triển game): Server: cả server lẫn server trung gian đều phân biệt bằng session token riêng cho từng kết nối; nhiều người trong cùng một nhà (sau NAT của router) và người chơi dùng mạng di động nơi nhà mạng chia một IP cho nhiều thuê bao (CGNAT) cũng gặp cùng vấn đề, nên nhất định phải sửa. Client: dùng session token nhận riêng cho mỗi client đang chạy. - Trên đồ thị: Chỉ một phần cao (Số phiên đồng thời từ cùng một IP công cộng, số lần phiên bị ghi đè) - Chỗ cần xem: Ghi vào log server và server trung gian khóa dùng để tìm phiên, session token, IP và cổng client, rồi xem phiên cũ có bị thay đổi đúng lúc kết nối thứ hai từ cùng IP đi vào không. Bật lần lượt hai client trên cùng một PC là tái hiện được - Đúng nếu: đúng lúc client thứ hai kết nối, địa chỉ hoặc thông tin nhân vật của phiên thứ nhất bị thay đổi, và những người chơi khác sau cùng router hoặc mạng di động (CGNAT) cũng gặp mất kết nối tương tự - Loại trừ nếu: hai phiên từ cùng IP được duy trì riêng với token khác nhau → không phải nguyên nhân này. Hai tiến trình dùng cùng một cổng cục bộ → xung đột cổng UDP cố định - Cách kiểm tra: Cần log và chỉ số của server, client game - Nguồn: - [RFC 6269: Issues with IP Address Sharing](https://www.rfc-editor.org/rfc/rfc6269) · IETF · Khi nhiều thuê bao dùng chung một địa chỉ IPv4 qua NAT hoặc CGN, chỉ dựa vào IP thì không phân biệt được người dùng #### pt-multiclient · Giới hạn chạy nhiều client · Multi-client restriction policy Nếu module bảo mật hoặc chính sách server giới hạn nhiều client trên một PC, client thứ hai sẽ bị chặn chạy hoặc chặn kết nối, hoặc client bật trước bị mất kết nối. Một số game chỉ chặn tính năng của client chạy thêm. - Vì sao → Dẫn đến → Trên màn hình: Module bảo mật phát hiện chạy trùng, hoặc server giới hạn kết nối thêm từ cùng một thiết bị → Từ chối lần chạy hoặc kết nối thứ hai, hoặc ngắt một bên. Hiếm khi chỉ chặn một phần tính năng của client chạy thêm → Không vào được hoặc một bên mất kết nối. Ở game chỉ chặn tính năng thì chỉ một bên không thấy NPC, cửa hàng - Triệu chứng: Không vào được·kẹt loading, Không hiển thị·đối tượng ma, Mất kết nối / Yếu tố: Mất gói - Ai gặp: Chỉ một client trên cùng PC / Khi nào: Ngay sau đăng nhập hoặc bảo trì - Phụ trách chính: Phát triển client (Đội phát triển game) / Phối hợp: Phát triển server (Đội phát triển game) - Việc cần làm (Đội phát triển game): Client: nếu giới hạn thì hiện thông báo hướng dẫn rõ ràng, cấu hình ngoại lệ cho QA trong module bảo mật. Server: cũng cấu hình ngoại lệ cho QA với giới hạn kết nối từ cùng thiết bị. - Trên đồ thị: Chỉ một phần cao (Số lần từ chối và ngắt kết nối theo lý do (kết nối trùng)) - Chỗ cần xem: Xem thông báo hiện ra khi bật client thứ hai và thông báo mất kết nối ở client bật trước. Kiểm tra log từ chối kết nối, buộc thoát của server có ghi mã lý do như kết nối trùng, cùng thiết bị không - Đúng nếu: đúng lúc chạy hoặc kết nối lần thứ hai thì hiện thông báo từ chối, hoặc client bật trước bị ngắt với lý do kết nối trùng, và chỉ bật một client thì không có vấn đề - Loại trừ nếu: cả hai bên đều kết nối được, không có lý do từ chối hay ngắt, mà chỉ một bên không thấy NPC → xem xung đột cổng UDP cố định, lỗi phân biệt phiên theo IP hoặc thiết bị, nguyên nhân phía loading và hiển thị - Cách kiểm tra: Kiểm tra ở môi trường phía người chơi - Nguồn: - [CreateMutexW function (synchapi.h)](https://learn.microsoft.com/en-us/windows/win32/api/synchapi/nf-synchapi-createmutexw) · Microsoft · Nếu named mutex đã tồn tại thì trả về ERROR_ALREADY_EXISTS, được dùng để phát hiện chạy trùng và giới hạn chỉ chạy một bản #### pt-background · Giới hạn xử lý ở cửa sổ chạy nền · Background window throttling Với client ở cửa sổ chạy nền, game, engine và OS đều giảm khung hình và việc xử lý. Gói tin nhận được không được xử lý kịp nên bị dồn lại hoặc tràn. - Vì sao → Dẫn đến → Trên màn hình: Giới hạn khung hình khi chạy nền trong tùy chọn game hoặc driver đồ họa (ví dụ driver NVIDIA cho chọn từ 20 đến 200 khung hình mỗi giây), tiết kiệm điện, cấu hình tạm dừng khi chạy nền của engine. OS cũng ưu tiên cấp CPU và GPU cho cửa sổ đang ở phía trước (foreground) → Số gói xử lý mỗi khung hình giảm nên hàng đợi dồn lại, bộ đệm nhận tràn thì gói bị bỏ → Đưa cửa sổ lên phía trước thì mọi thứ hiện ra dồn một lượt, hoặc một số NPC mãi không hiển thị - Triệu chứng: Không hiển thị·đối tượng ma, Tua nhanh, Mất kết nối / Yếu tố: Ngưng trệ, Mất gói - Ai gặp: Chỉ một client trên cùng PC / Khi nào: Sau khi để yên một lúc, Luôn luôn - Phụ trách chính: Phát triển client (Đội phát triển game) / Phối hợp: Bên ngoài (Bên ngoài) - Việc cần làm (Đội phát triển game): Nhận dữ liệu mạng liên tục trên thread tách riêng khỏi vòng lặp game, bảo đảm thông lượng xử lý tối thiểu cả khi chạy nền, bật cấu hình chạy nền của engine (Unity là runInBackground). - Việc cần làm (Bên ngoài): Hướng dẫn người chơi tắt giới hạn khung hình khi chạy nền của driver đồ họa và chế độ tiết kiệm điện của PC. - Con số tham khảo: Với Unity, nếu tắt runInBackground, vòng lặp game dừng ngay khi cửa sổ mất focus. Nếu chỉ nhận dữ liệu trong vòng lặp đó thì trong thời gian này không gói tin nào được xử lý. - Trên đồ thị: Đứt quãng rồi dồn về (Khoảng cách khung hình của client, số gói xử lý mỗi khung hình) - Chỗ cần xem: Trên cùng một PC, đặt một cửa sổ ở trước, cửa sổ kia ở sau rồi đổi vai để so sánh. Dùng PresentMon đo khoảng cách khung hình của hai tiến trình; nếu có log phía game thì xem trạng thái focus của cửa sổ và số gói xử lý mỗi khung hình - Đúng nếu: chỉ khi ở cửa sổ chạy nền thì khoảng cách khung hình tăng mạnh (nếu do giới hạn của driver thì đi ngang ở khoảng cách tương ứng với số khung hình mỗi giây đã đặt) hoặc việc xử lý dừng lại, và khi đổi cửa sổ thì vấn đề cũng chuyển sang client kia - Loại trừ nếu: cửa sổ ở phía trước cũng bị y như vậy → không phải do giới hạn chạy nền. Luôn chỉ cùng một client bất thường, bất kể vị trí cửa sổ → xem khác tùy chọn hiển thị hoặc khác phiên bản - Cách kiểm tra: Kiểm tra ở môi trường phía người chơi - Nguồn: - [Application.runInBackground](https://docs.unity3d.com/ScriptReference/Application-runInBackground.html) · Unity · Giá trị mặc định của runInBackground là false, khi đó ứng dụng tạm dừng khi chạy nền - [Manage 3D Settings (reference) — NVIDIA Control Panel Help](https://www.nvidia.com/content/Control-Panel-Help/vLatest/en-us/mergedProjects/nv3d/Manage_3D_Settings_(reference).htm) · NVIDIA · Background Application Max Frame Rate: giới hạn khung hình tối đa của game chạy nền trong khoảng 20–200 khung hình mỗi giây - [Priority Boosts](https://learn.microsoft.com/en-us/windows/win32/procthread/priority-boosts) · Microsoft · Windows nâng mức ưu tiên tiến trình của cửa sổ foreground lên bằng hoặc cao hơn tiến trình chạy nền - [PresentMon README](https://raw.githubusercontent.com/GameTechDev/PresentMon/main/README.md) · Intel · Công cụ thu thập frame time của CPU, GPU, màn hình theo từng ứng dụng đồ họa trên Windows #### pt-asset-lock · Xung đột khi cùng truy cập file cache hoặc asset · Shared cache / asset file lock conflicts Nếu hai client cùng ghi vào một thư mục cache hoặc khóa file, một bên sẽ không tải được model, texture của NPC. - Vì sao → Dẫn đến → Trên màn hình: Hai client cùng lúc ghi file cache, file patch trong cùng một thư mục cài đặt → Khóa file thất bại hoặc đọc phải file đang ghi dở nên tải thất bại → NPC có bảng tên nhưng không có model nhân vật, hoặc trong suốt - Triệu chứng: Không hiển thị·đối tượng ma / Yếu tố: Ngưng trệ - Ai gặp: Chỉ một client trên cùng PC / Khi nào: Ngay sau đăng nhập hoặc bảo trì, Khi di chuyển hoặc chuyển bản đồ - Phụ trách chính: Phát triển client (Đội phát triển game) - Việc cần làm (Đội phát triển game): Thư mục cache riêng cho từng client, thử lại khi khóa file thất bại, khi tải thất bại thì ít nhất cũng hiển thị model mặc định. - Trên đồ thị: Chỉ một phần cao (Số lần tải asset thất bại theo client) - Chỗ cần xem: Trên PC người chơi, dùng Process Monitor lọc riêng đường dẫn thư mục cài đặt và cache của game để xem kết quả mở, ghi file của hai tiến trình game. Nếu có log client: tìm lỗi tải asset và mã lỗi mở file (ERROR_SHARING_VIOLATION) - Đúng nếu: việc mở file của model không hiển thị kết thúc bằng vi phạm chia sẻ hoặc khóa thất bại, và cùng lúc đó client kia đang ghi file này. Chỉ bật một client hoặc tách thư mục cài đặt, cache thì hết - Loại trừ nếu: chỉ bật một client mà cùng model đó vẫn không hiển thị → xem file hỏng hoặc phiên bản, dữ liệu client không khớp. File mở bình thường mà không được vẽ → thiếu bộ nhớ hoặc VRAM - Cách kiểm tra: Kiểm tra ở môi trường phía người chơi - Nguồn: - [Creating and Opening Files](https://learn.microsoft.com/en-us/windows/win32/fileio/creating-and-opening-files) · Microsoft · File được mở không có chế độ chia sẻ thì tiến trình khác không mở được và gặp ERROR_SHARING_VIOLATION - [Process Monitor](https://learn.microsoft.com/en-us/sysinternals/downloads/procmon) · Microsoft · Ghi lại theo thời gian thực hoạt động của file system, registry, tiến trình, lọc được theo mọi trường như đường dẫn #### pt-vram · Streaming thất bại do thiếu bộ nhớ hoặc VRAM · Memory / VRAM exhaustion Khi hai client dùng chung bộ nhớ đồ họa, không còn chỗ để nạp model, texture mới cần dùng nên một phần không được vẽ. - Vì sao → Dẫn đến → Trên màn hình: Hai client dùng chung VRAM và RAM. OS cũng có thể giảm hạn mức bộ nhớ đồ họa của cửa sổ chạy nền trước → Engine không nạp được model, texture mới hoặc liên tục gỡ ra rồi nạp lại → NPC xuất hiện muộn, bị mờ hoặc không hiển thị, giật khựng - Triệu chứng: Không hiển thị·đối tượng ma, Giật khựng / Yếu tố: Ngưng trệ - Ai gặp: Chỉ một client trên cùng PC / Khi nào: Khi di chuyển hoặc chuyển bản đồ, Khi đông người - Phụ trách chính: Phát triển client (Đội phát triển game) / Phối hợp: Bên ngoài (Bên ngoài) - Việc cần làm (Đội phát triển game): Tự động điều chỉnh chất lượng theo ngân sách bộ nhớ, hiển thị model thay thế khi tải thất bại. - Việc cần làm (Bên ngoài): Hướng dẫn người chơi bật cùng lúc hai client hạ chất lượng đồ họa hoặc dùng chế độ cấu hình thấp, cung cấp cấu hình VRAM và RAM khuyến nghị. - Trên đồ thị: Chạm giới hạn rồi đi ngang (Lượng bộ nhớ GPU chuyên dụng theo tiến trình) - Chỗ cần xem: Trên PC người chơi, thêm cột bộ nhớ GPU chuyên dụng vào tab “Chi tiết” của Task Manager rồi so sánh tổng lượng dùng của hai client với dung lượng VRAM của card đồ họa. Phía game: ghi lại ngân sách (Budget) và lượng dùng hiện tại (CurrentUsage) mà QueryVideoMemoryInfo của DXGI trả về - Đúng nếu: tổng lượng dùng của hai client đi ngang quanh dung lượng VRAM, và lỗi tải model, texture dồn vào lúc lượng dùng hiện tại vượt ngân sách. Hạ chất lượng hoặc chỉ bật một client thì hết - Loại trừ nếu: VRAM vẫn còn dư mà vẫn không hiển thị → xem xung đột khi cùng truy cập file cache hoặc asset, hoặc khác tùy chọn hiển thị - Cách kiểm tra: Kiểm tra ở môi trường phía người chơi - Nguồn: - [Residency (Direct3D 12)](https://learn.microsoft.com/en-us/windows/win32/direct3d12/residency) · Microsoft · Ngân sách bộ nhớ video có thể giảm mạnh khi chuyển sang ứng dụng khác, vượt ngân sách thì bị khựng hoặc tạo tài nguyên thất bại. Không ở foreground thì phần đã đặt trước cũng không được bảo đảm - [GPUs in the task manager](https://devblogs.microsoft.com/directx/gpus-in-the-task-manager/) · Microsoft · Thêm cột vào tab Chi tiết của Task Manager thì xem được lượng bộ nhớ GPU chuyên dụng và dùng chung theo tiến trình. Bộ nhớ GPU chuyên dụng là VRAM của card đồ họa - [DXGI_QUERY_VIDEO_MEMORY_INFO structure (dxgi1_4.h)](https://learn.microsoft.com/en-us/windows/win32/api/dxgi1_4/ns-dxgi1_4-dxgi_query_video_memory_info) · Microsoft · Budget (ngân sách bộ nhớ video do OS định) và CurrentUsage (lượng ứng dụng đang dùng). Lượng dùng vượt ngân sách thì có thể bị giật #### pt-display-option · Khác tùy chọn hiển thị · Different display settings Nếu các tùy chọn như giới hạn số người hiển thị, ẩn bảng tên hoặc model NPC, chế độ cấu hình thấp khác nhau giữa hai client, những gì nhìn thấy sẽ khác nhau. - Vì sao → Dẫn đến → Trên màn hình: Chỉ một client bật “giới hạn số nhân vật xung quanh được hiển thị” hoặc chế độ cấu hình thấp → Không vẽ các NPC ở xa hoặc có mức ưu tiên thấp (bình thường) → Chỉ một bên không có NPC - Triệu chứng: Không hiển thị·đối tượng ma / Yếu tố: Ngưng trệ - Ai gặp: Chỉ một client trên cùng PC, Chỉ mình tôi / Khi nào: Khi đông người, Luôn luôn - Phụ trách chính: Phát triển client (Đội phát triển game) - Việc cần làm (Đội phát triển game): Hiển thị để biết đối tượng đã bị ẩn do tùy chọn, tách file cấu hình theo từng client để không bị lẫn. - Trên đồ thị: Chỉ một phần cao (Số đối tượng được vẽ trên màn hình theo client) - Chỗ cần xem: So sánh song song các cấu hình giới hạn số người hiển thị, ẩn bảng tên hoặc model, chế độ cấu hình thấp của hai client, rồi chỉnh một bên giống hệt bên kia để xem. Kiểm tra cả việc hai client có dùng chung một file cấu hình và ghi đè lẫn nhau không - Đúng nếu: chỉnh cấu hình giống nhau thì hai màn hình giống nhau, và NPC không hiển thị trước đó là đối tượng ở xa ngoài số lượng giới hạn hiển thị hoặc đối tượng có mức ưu tiên thấp - Loại trừ nếu: chỉnh cấu hình giống hệt nhau mà vẫn chỉ một bên không có → xem khác kênh, instance, phasing hoặc phía mất thông báo xuất hiện - Cách kiểm tra: Kiểm tra ở môi trường phía người chơi - Nguồn: - [Changing the Quantity of Characters Displayed On-screen (FINAL FANTASY XIV UI Guide)](https://na.finalfantasyxiv.com/uiguide/faq/faq-other/setting_ch_quantity.html) · Square Enix · Dùng cấu hình giới hạn hiển thị (Character and Object Quantity) để điều chỉnh số nhân vật và vật thể được vẽ trên màn hình #### pt-version · Phiên bản hoặc dữ liệu client không khớp · Client version / data table mismatch Nếu client thứ hai là bản cài đặt khác hoặc chưa cập nhật xong, nó không biết ID NPC mới mà server gửi nên âm thầm bỏ qua. - Vì sao → Dẫn đến → Trên màn hình: Bản cài đặt ở thư mục khác, hoặc client được chạy khi đang cập nhật → Nhận ID NPC hoặc ID model không biết thì bỏ qua → Chỉ NPC mới thêm không hiển thị ở một bên - Triệu chứng: Không hiển thị·đối tượng ma / Yếu tố: Mất gói - Ai gặp: Chỉ một client trên cùng PC / Khi nào: Ngay sau đăng nhập hoặc bảo trì - Phụ trách chính: Phát triển client (Đội phát triển game) / Phối hợp: Phát triển server (Đội phát triển game) - Việc cần làm (Đội phát triển game): Client: gửi phiên bản dữ liệu khi kết nối, nhận ID không biết thì ghi log và hiển thị thay thế. Server: kiểm tra phiên bản dữ liệu khi kết nối, khác thì từ chối kết nối và hướng dẫn cập nhật. - Trên đồ thị: Chỉ một phần cao (Số lần nhận ID không biết theo phiên bản client) - Chỗ cần xem: So sánh đường dẫn file thực thi của hai client và phiên bản client, phiên bản dữ liệu hiện trên màn hình hoặc trong log. Phía game: ghi lại phiên bản dữ liệu gửi khi kết nối và số lần nhận ID NPC, ID model không biết rồi bỏ qua - Đúng nếu: hai client khác phiên bản hoặc khác thư mục cài đặt, NPC không hiển thị là NPC mới được thêm ở bản cập nhật gần đây, và ở bản cài đặt đã cập nhật xong thì thấy - Loại trừ nếu: cùng phiên bản và cùng thư mục cài đặt mà vẫn chỉ một bên không có → xem khác kênh, instance, phasing hoặc nguyên nhân phía loading, truyền tải - Cách kiểm tra: Kiểm tra ở môi trường phía người chơi - Nguồn: - [NetworkConfig class (Netcode for GameObjects 2.5)](https://docs.unity3d.com/Packages/com.unity.netcode.gameobjects@2.5/api/Unity.Netcode.NetworkConfig.html) · Unity · ProtocolVersion khác nhau thì không giao tiếp với nhau, ForceSamePrefabs kiểm tra khác biệt danh sách prefab khi kết nối #### pt-priority · Ngân sách gửi và mức ưu tiên theo kết nối · Per-connection bandwidth budget and priority Nếu server đặt giới hạn lượng gửi cho từng kết nối và gửi những thứ ở gần trước, bên có giới hạn thấp sẽ nhận NPC ở xa muộn hoặc không nhận được. - Vì sao → Dẫn đến → Trên màn hình: Ở nơi đông người, server gửi theo thứ tự quan trọng trong giới hạn lượng gửi của từng kết nối → Với kết nối có băng thông ước tính thấp (ví dụ bên ở cửa sổ chạy nền nên gửi xác nhận đã nhận muộn), các đối tượng xếp sau cứ bị hoãn mãi → NPC ở xa chỉ hiển thị muộn hoặc không hiển thị ở một bên - Triệu chứng: Không hiển thị·đối tượng ma, Trễ thao tác / Yếu tố: Độ trễ - Ai gặp: Chỉ một client trên cùng PC, Một địa điểm hoặc kênh / Khi nào: Khi đông người - Phụ trách chính: Phát triển server (Đội phát triển game) / Phối hợp: Phát triển client (Đội phát triển game) - Việc cần làm (Đội phát triển game): Server: tăng dần mức ưu tiên của đối tượng bị hoãn theo thời gian (chống starvation), bảo đảm chu kỳ cập nhật tối thiểu. Client: gửi xác nhận đã nhận đúng lúc cả khi chạy nền để băng thông ước tính không bị hạ thấp. - Trên đồ thị: Tăng theo số người và tải (Số đối tượng bị hoãn theo kết nối, lượng gửi theo kết nối) - Chỗ cần xem: Ghi trên server cho mỗi kết nối: số byte gửi mỗi tick, giới hạn gửi (băng thông ước tính), số đối tượng không gửi được phải hoãn, thời gian trôi qua kể từ lần gửi cuối của từng đối tượng. Với Unreal, Networking Insights cho xem kích thước gói theo kết nối và các đối tượng replicate chứa trong đó - Đúng nếu: NPC không hiển thị là đối tượng bị hoãn lâu trên kết nối đó, giới hạn của kết nối này thấp hơn các kết nối khác, và càng đông người thì số đối tượng bị hoãn càng tăng - Loại trừ nếu: không có đối tượng bị hoãn và NPC đó cũng đã được gửi đúng lúc → xem các bước sau khi gửi (bộ đệm nhận, loading, tùy chọn hiển thị). Mọi kết nối đều bám sát giới hạn → vấn đề thiết kế lượng gửi và tầm nhìn của toàn server - Cách kiểm tra: Cần log và chỉ số của server, client game - Nguồn: - [Actor Priority in Unreal Engine](https://dev.epicgames.com/documentation/en-us/unreal-engine/actor-priority-in-unreal-engine) · Epic Games · Khi băng thông bão hòa, chọn actor để replicate theo mức ưu tiên (khoảng cách, hướng nhìn, thời gian trôi qua từ lần replicate cuối). Không phải actor nào cũng được replicate mỗi lần - [State Synchronization](https://gafferongames.com/post/state_synchronization/) · Gaffer On Games · Mức ưu tiên tích lũy: đối tượng không vừa gói lần này sẽ được đưa vào gói sau trước tiên, giới hạn băng thông được điều chỉnh theo thời gian thực - [Networking Insights in Unreal Engine](https://dev.epicgames.com/documentation/en-us/unreal-engine/networking-insights-in-unreal-engine) · Epic Games · Hiển thị kích thước gói gửi và nhận theo từng kết nối, cùng các đối tượng và thuộc tính replicate chứa trong đó #### pt-clock-hold · Đối tượng bị giữ lại do ước tính đồng hồ sai · Clock estimate error holds or discards entities Nếu giờ server mà client ước tính bị sai, client sẽ giữ lại thông tin đối tượng vừa tới vì cho là “vẫn còn ở tương lai”, hoặc bỏ đi vì cho là “quá cũ”. - Vì sao → Dẫn đến → Trên màn hình: Giờ server mà một client ước tính bị lệch nhiều (đo trong lúc loading, vừa thoát chế độ tiết kiệm điện) → Thời điểm mốc của nội suy và thời điểm trong thông tin đối tượng không khớp → Đối tượng xuất hiện muộn hoặc hiện ở trạng thái đứng yên - Triệu chứng: Không hiển thị·đối tượng ma, Giật khựng / Yếu tố: Độ trễ - Ai gặp: Chỉ một client trên cùng PC / Khi nào: Sau khi để yên một lúc, Ngay sau đăng nhập hoặc bảo trì - Phụ trách chính: Phát triển client (Đội phát triển game) - Việc cần làm (Đội phát triển game): Đồng bộ thời gian lại định kỳ, nếu lệch nhiều thì đặt lại ngay, không dùng giá trị đo trong lúc loading hoặc ngay sau khi thoát chế độ tiết kiệm điện. - Trên đồ thị: Chỉ một phần cao (Sai số ước tính giờ server theo client) - Chỗ cần xem: Ghi ở client giờ server ước tính, RTT, thời điểm đồng bộ thời gian lại, số lần giữ lại hoặc bỏ thông tin đối tượng. Tái hiện ngay sau loading hoặc ngay sau khi thoát chế độ tiết kiệm điện - Đúng nếu: chỉ client gặp sự cố có sai số ước tính vượt ngưỡng đặt lại (Unity là hardResetThresholdSec, mặc định 0,2 giây), có bản ghi giữ lại thông tin đối tượng vì cho là ở tương lai hoặc bỏ đi vì cho là quá khứ, và đồng bộ thời gian lại thì bình thường ngay - Loại trừ nếu: sai số ước tính nhỏ mà vẫn xuất hiện muộn → xem ngân sách gửi và mức ưu tiên theo kết nối hoặc nguyên nhân phía loading - Cách kiểm tra: Cần log và chỉ số của server, client game - Nguồn: - [NetworkTimeSystem class (Netcode for GameObjects 2.5)](https://docs.unity3d.com/Packages/com.unity.netcode.gameobjects@2.5/api/Unity.Netcode.NetworkTimeSystem.html) · Unity · Chênh lệch thời gian vượt hardResetThresholdSec (mặc định 0,2 giây) thì ép khớp ngay, bình thường thì dùng adjustmentRatio để chỉnh nhanh hoặc chậm từng chút - [NetworkTime and ticks (Netcode for GameObjects 2.5)](https://docs.unity3d.com/Packages/com.unity.netcode.gameobjects@2.5/manual/advanced-topics/networktime-ticks.html) · Unity · LocalTime đi trước server, ServerTime đi sau. Message tới muộn có thể có thời gian chờ âm ### Nguyên nhân gốc của truyền lại TCP (20 nguyên nhân) #### rt-wireless · Mất gói ở chặng không dây · Wi-Fi / cellular link loss Wi-Fi và mạng di động tự truyền lại vài lần trên chặng không dây, nếu vẫn không được thì bỏ gói tin. Gói đã bị bỏ phải đợi khá lâu sau đó TCP mới gửi lại. - Vì sao → Dẫn đến → Trên màn hình: Sóng yếu hoặc nhiễu mạnh khiến việc truyền trên chặng không dây thất bại liên tiếp → Vượt giới hạn số lần thử lại của thiết bị không dây (thường từ vài lần đến hơn chục lần) thì gói tin bị bỏ → Đứng hình trong lúc chờ TCP truyền lại, các gói phía sau nằm chờ trong bộ đệm nhận rồi tua nhanh - Triệu chứng: Đứng hình, Tua nhanh, Dịch chuyển tức thời / Yếu tố: Mất gói, Jitter - Ai gặp: Chỉ mình tôi, Cùng một nhà / Khi nào: Thỉnh thoảng bất chợt, Khi di chuyển hoặc chuyển bản đồ - Phụ trách chính: Bên ngoài (Bên ngoài) / Phối hợp: Hạ tầng server (Đội hạ tầng), Phát triển server (Đội phát triển game), Phát triển client (Đội phát triển game) - Việc cần làm (Đội phát triển game): Server: bật TCP_NODELAY (nếu Nagle còn bật thì RACK không có gói đi sau để dựa vào khi xác định mất gói); trong lúc kết nối bị chặn vì truyền lại, không dồn các bản cập nhật trạng thái chờ gửi, chỉ gửi bản mới nhất (giới hạn lượng dữ liệu dồn trong kernel bằng TCP_NOTSENT_LOWAT). Client: bật TCP_NODELAY (gói mất ở chiều gửi input của người chơi do OS phía client phục hồi); khi mất gói dồn lại hoặc ping tăng vọt thì hiển thị trạng thái mạng trên màn hình. - Việc cần làm (Đội hạ tầng): Dùng RACK-TLP để phục hồi mất gói nhanh hơn (server không thể ngăn mất gói không dây, chỉ có thể phục hồi nhanh hơn); kiểm tra các giá trị mặc định trên Linux mới là net.ipv4.tcp_recovery=1 (RACK) và net.ipv4.tcp_early_retrans=3 (TLP) chưa bị thay đổi. - Việc cần làm (Bên ngoài): Hướng dẫn người chơi dùng mạng có dây, dùng băng tần 5 GHz hoặc 6 GHz, đổi vị trí hoặc kênh của router. - Con số tham khảo: Mất gói không dây 1% nghĩa là cứ 100 gói tin game thì mất 1 gói. Nếu nhận 10 gói trong 1 giây thì khoảng 10 giây lại khựng một lần. Nếu không có RACK-TLP, mỗi lần như vậy game đứng hình trong một khoảng bằng RTO (ping + 200 ms trở lên). - Trên đồ thị: Chỉ một phần cao (Tỷ lệ truyền lại theo từng kết nối, RTT (ping) theo từng kết nối) - Chỗ cần xem: Từ PC của người chơi, ping vài trăm lần tới địa chỉ router (gateway) và tới server game rồi so sánh mức mất gói và biên độ dao động độ trễ; chuyển sang mạng có dây hoặc dữ liệu di động rồi đo lại. Trên server, xem retrans và rtt (trung bình/độ lệch) của kết nối người chơi đó bằng ss -ti - Đúng nếu: ping tới router đã thấy mất gói hoặc độ trễ thất thường, chuyển sang mạng có dây thì hết. Nhìn từ server, chỉ riêng kết nối của người chơi đó có retrans và độ lệch RTT lớn - Loại trừ nếu: chặng tới router vẫn sạch, mất gói bắt đầu từ phía sau router → phía nhà mạng hoặc tuyến đường (“Tràn hàng đợi ở điểm nghẽn”, “Đổi tuyến đường hoặc tuyến ECMP lỗi”). Nhiều người chơi cùng nhà mạng cùng lúc xấu đi thì xem chặng nhà mạng trước - Cách kiểm tra: Kiểm tra ở môi trường phía người chơi - Tìm hiểu thêm: Mỗi lần thiết bị không dây thử lại sẽ tạo ra jitter (vài ms mỗi lần), và chỉ những gói vượt giới hạn thử lại mới trở thành mất gói. Vì vậy sóng càng kém thì triệu chứng càng nặng theo thứ tự “jitter → thỉnh thoảng đứng hình → đứng hình thường xuyên”. Lúc thiết bị chuyển từ router (AP) này sang router khác (roaming), gói tin có thể mất liên tiếp trong vài chục ms đến vài giây. Mạng di động truyền lại nhiều ở chặng tới trạm phát sóng, nên vấn đề thường lộ ra thành độ trễ tăng vọt vài trăm ms nhiều hơn là thành mất gói. - Sự cố thực tế: ffxiv-2021 - Nguồn: - [net/wireless/core.c](https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/net/wireless/core.c?h=v6.12) · Linux kernel · Giới hạn thử lại mặc định trong stack không dây của Linux: 7 lần với frame ngắn, 4 lần với frame dài (dot11ShortRetryLimit, dot11LongRetryLimit) - [RFC 3481: TCP over Second (2.5G) and Third (3G) Generation Wireless Networks](https://www.rfc-editor.org/rfc/rfc3481) · IETF · Mạng di động ít mất gói ở tầng IP nhờ truyền lại ở tầng liên kết (link layer), nhưng việc phục hồi đó lộ ra thành jitter và độ trễ tăng vọt - [Wi-Fi roaming support in Apple devices](https://support.apple.com/guide/deployment/wi-fi-roaming-support-dep98f116c0f/web) · Apple · Khi chuyển sang AP mới, thiết bị không gửi được dữ liệu cho đến khi xác thực với AP mới xong; trong môi trường 802.1X việc này có thể mất vài giây - [RFC 8985: The RACK-TLP Loss Detection Algorithm for TCP](https://www.rfc-editor.org/rfc/rfc8985) · IETF · Định nghĩa RACK (xác định mất gói dựa trên thời gian) và TLP (truyền lại gói cuối) - [IP Sysctl](https://docs.kernel.org/networking/ip-sysctl.html) · Linux kernel · tcp_recovery mặc định 0x1 (RACK), tcp_early_retrans mặc định 3 (bật TLP); TCP_NOTSENT_LOWAT và tcp_notsent_lowat giới hạn lượng dữ liệu chưa gửi - [tcp(7) — Linux manual page](https://man7.org/linux/man-pages/man7/tcp.7.html) · Linux man-pages · TCP_NODELAY tắt thuật toán Nagle để dữ liệu nhỏ cũng được gửi ngay - [include/net/tcp.h](https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/include/net/tcp.h?h=v6.12) · Linux kernel · RTO tối thiểu TCP_RTO_MIN = 200 ms - [misc/ss.c](https://git.kernel.org/pub/scm/network/iproute2/iproute2.git/tree/misc/ss.c?h=v6.12.0) · iproute2 · ss -ti hiển thị retrans:số gói đang truyền lại/tổng số lần truyền lại và rtt:RTT/độ lệch RTT (rttvar) #### rt-queue-drop · Tràn hàng đợi ở điểm nghẽn (mất gói do tắc nghẽn) · Tail drop at a congested bottleneck Khi hàng đợi ở chỗ hẹp nhất trên đường đi (router, chặng kết nối giữa các nhà mạng, đường truyền của trung tâm dữ liệu) đầy, gói tin mới đến sẽ bị bỏ. - Vì sao → Dẫn đến → Trên màn hình: Video, tải file hoặc lưu lượng của người dùng khác làm điểm nghẽn đầy → Trong lúc hàng đợi đầy, các gói mới đến liên tiếp bị bỏ (tail drop). Gói không bị bỏ cũng phải chờ ở cuối hàng đợi đang đầy → Nhiều gói biến mất cùng lúc nên đứng hình lâu rồi tua nhanh, hay gặp vào buổi tối - Triệu chứng: Đứng hình, Tua nhanh, Kéo ngược / Yếu tố: Mất gói, Độ trễ - Ai gặp: Cùng một nhà, Một khu vực hoặc nhà mạng, Cả server / Khi nào: Giờ cao điểm buổi tối, Khi đông người - Phụ trách chính: Hạ tầng mạng (Đội hạ tầng) / Phối hợp: Bên ngoài (Bên ngoài), Phát triển client (Đội phát triển game) - Việc cần làm (Đội phát triển game): Khi mất gói dồn lại hoặc ping tăng vọt thì hiển thị trạng thái mạng trên màn hình (báo khả năng có thiết bị đang truyền dữ liệu dung lượng lớn trên cùng đường truyền). - Việc cần làm (Đội hạ tầng): Bảo đảm dư băng thông cho đường truyền trung tâm dữ liệu, kiểm tra counter gói bị bỏ ở hàng đợi (output drops) trên đường truyền và cổng switch của mình, nếu chặng nhà mạng bị nghẽn thì đi vòng qua đường truyền hoặc peering khác. - Việc cần làm (Bên ngoài): Hướng dẫn người chơi bật SQM (fq_codel, CAKE) và ECN trên router (để giảm tốc trước khi hàng đợi tràn), yêu cầu nhà mạng nâng cấp điểm nghẽn. - Con số tham khảo: Vào lúc hàng đợi tràn, trong vài chục ms một phần lớn gói tin đi vào bị mất cùng lúc. Vì gói mất liên tiếp và gói gửi lại cũng dễ bị mất, nhiều trường hợp phải chờ tới RTO. - Trên đồ thị: Chỉ cao vào một khung giờ (Tỷ lệ truyền lại, RTT (ping)) - Chỗ cần xem: Tỷ lệ truyền lại của server (phần tăng của TcpRetransSegs ÷ TcpOutSegs khi chạy nstat cách nhau 1 phút) và RTT từng kết nối, chia theo khu vực, nhà mạng, khung giờ; xem kèm số gói bị bỏ ở chiều ra (ifOutDiscards) trên đường truyền và cổng switch của mình. Chạy mtr tới khu vực có vấn đề vào giờ cao điểm và giờ vắng rồi so sánh - Đúng nếu: tỷ lệ truyền lại chỉ tăng vào giờ cao điểm buổi tối, và ngay trước khi mất gói thì RTT đã tăng lên trước (dấu hiệu hàng đợi đang đầy dần). Trên mtr, chỉ vào giờ cao điểm mới thấy mất gói và độ trễ cùng tăng từ một chặng nào đó cho đến cuối - Loại trừ nếu: RTT không tăng ngay trước khi mất gói → “Policer bỏ phần vượt mức”. Mất gói lúc nào cũng gần như nhau bất kể khung giờ → “Lỗi vật lý” hoặc “Đổi tuyến đường hoặc tuyến ECMP lỗi” - Cách kiểm tra: Kiểm tra bằng công cụ hạ tầng (không cần code game) - Sự cố thực tế: riot-direct-2015 - Nguồn: - [RFC 7567: IETF Recommendations Regarding Active Queue Management](https://www.rfc-editor.org/rfc/rfc7567) · IETF · Giải thích rằng tail drop giữ hàng đợi đầy trong thời gian dài, làm tăng độ trễ và gây mất gói theo loạt, kèm khuyến nghị dùng AQM - [RFC 8290: The Flow Queue CoDel Packet Scheduler and Active Queue Management Algorithm](https://www.rfc-editor.org/rfc/rfc8290) · IETF · fq_codel: dùng hàng đợi theo từng luồng và AQM để giữ hàng đợi ngắn, giảm bufferbloat - [RFC 3168: The Addition of Explicit Congestion Notification (ECN) to IP](https://www.rfc-editor.org/rfc/rfc3168) · IETF · ECN: báo tắc nghẽn bằng cách đánh dấu trong IP header thay vì bỏ gói tin - [Smart Queue Management](https://www.bufferbloat.net/projects/cerowrt/wiki/Smart_Queue_Management/) · Bufferbloat.net · SQM: cách kết hợp lập lịch theo từng luồng, quản lý độ dài hàng đợi (AQM) và shaping - [Cake](https://www.bufferbloat.net/projects/codel/wiki/Cake/) · Bufferbloat.net · CAKE: SQM dành cho router, kết hợp shaper với cơ chế quản lý hàng đợi họ fq_codel - [net/ipv4/proc.c](https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/net/ipv4/proc.c?h=v6.12) · Linux kernel · TcpRetransSegs và TcpOutSegs trong nstat (RetransSegs và OutSegs của mục Tcp) - [nstat(8) — Linux manual page](https://man7.org/linux/man-pages/man8/nstat.8.html) · iproute2 · Mặc định nstat hiển thị phần tăng kể từ lần chạy trước - [RFC 2863: The Interfaces Group MIB](https://www.rfc-editor.org/rfc/rfc2863) · IETF · ifOutDiscards: số gói tin bị bỏ, không được gửi đi dù không có lỗi, vì các lý do như cần giải phóng chỗ trong bộ đệm - [An Internet-Wide Analysis of Traffic Policing](https://research.google/pubs/an-internet-wide-analysis-of-traffic-policing/) · Google · Cách phân biệt: tràn hàng đợi thì thời gian chờ và RTT tăng trước khi mất gói, còn policing bỏ phần vượt mức mà RTT không tăng (SIGCOMM 2016) #### rt-burst · Burst gửi làm tràn bộ đệm nhỏ · Sender bursts overflow shallow buffers Mỗi tick, nếu server dồn bản cập nhật cho hàng nghìn người rồi gửi đi trong một khoảnh khắc, bộ đệm nhỏ của switch hoặc giới hạn tức thời của cloud sẽ tràn trong chưa đầy 1 ms và một phần gói tin bị bỏ. - Vì sao → Dẫn đến → Trên màn hình: Ngay đầu tick, server gửi dồn một lượt toàn bộ gói tin cho mọi người → Bộ đệm của cổng switch nơi lưu lượng từ nhiều server dồn về (vài trăm KB đến vài MB mỗi cổng) hoặc giới hạn của instance cloud bị tràn trong khoảnh khắc (mức sử dụng trung bình vẫn thấp) → Nhiều người cùng lúc bị dịch chuyển tức thời hoặc khựng, nhìn chỉ số trung bình thì không thấy nguyên nhân - Triệu chứng: Dịch chuyển tức thời, Đứng hình, Tua nhanh / Yếu tố: Mất gói - Ai gặp: Một địa điểm hoặc kênh, Cả server / Khi nào: Khi đông người - Phụ trách chính: Phát triển server (Đội phát triển game) / Phối hợp: Hạ tầng server (Đội hạ tầng), Hạ tầng mạng (Đội hạ tầng) - Việc cần làm (Đội phát triển game): Chia dữ liệu gửi của một tick rải ra trong suốt tick (pacing theo từng kết nối khó gỡ được cảnh hàng nghìn kết nối cùng dồn vào đầu tick), lệch thời điểm bắt đầu tick giữa các server, giới hạn tốc độ bằng SO_MAX_PACING_RATE cho kết nối gửi dữ liệu lớn. - Việc cần làm (Đội hạ tầng): Thiết bị server·OS: giới hạn tốc độ gửi của toàn server (shaper trong OS của server, tc trên Linux), dùng pacing (hàng đợi fq của Linux, BBR) để dàn đều khi một kết nối gửi dồn. Mạng: dùng switch có bộ đệm lớn, kiểm tra counter bỏ gói ở chiều ra của cổng switch với chu kỳ ngắn (nhìn mức sử dụng trung bình sẽ không thấy). - Con số tham khảo: Lượng dữ liệu một cổng 10 Gbps gửi được trong 1 ms là khoảng 1,25 MB. Khi tick của nhiều server trùng nhau và cùng dồn vào một cổng, bộ đệm đầy ngay tức thì. - Trên đồ thị: Tăng theo số người và tải (Số gói bị bỏ ở chiều ra của cổng switch, tỷ lệ truyền lại) - Chỗ cần xem: Số gói bị bỏ ở chiều ra (ifOutDiscards) của cổng switch nối với server và cổng tầng trên của nó, thu thập với chu kỳ vài giây; nếu là cloud thì xem bw_out_allowance_exceeded và pps_allowance_exceeded trong ethtool -S. Gom các lần truyền lại cùng thời điểm bằng bcc tcpretrans để đối chiếu - Đúng nếu: mức sử dụng trung bình theo phút thấp nhưng số gói bị bỏ ở chiều ra hoặc số lần vượt allowance tăng, và tăng theo số người chơi đồng thời hoặc số người tụ tập một chỗ. Truyền lại xảy ra cùng một lúc trên nhiều kết nối của server đó và không dồn vào dải IP người chơi của một nhà mạng hay khu vực cụ thể - Loại trừ nếu: cùng cổng đó lỗi CRC và lỗi chiều vào (input error) cũng tăng → “Lỗi vật lý”. Counter bỏ gói trên NIC hoặc softnet dropped của server nhận tăng → “Host server nhận bỏ gói tin” - Cách kiểm tra: Kiểm tra bằng công cụ hạ tầng (không cần code game) - Tìm hiểu thêm: Pacing hoạt động riêng cho từng kết nối. Khi hàng nghìn kết nối, mỗi kết nối gửi một hai gói, cùng dồn vào đầu tick thì pacing theo kết nối khó gỡ được, server game phải tự chia thời điểm gửi. Ngược lại, khi một kết nối gửi dữ liệu lớn, NIC cắt vài chục KB dữ liệu thành các gói rồi đẩy ra liên tiếp (TSO), và kiểu dồn này thì pacing dàn đều rất tốt. - Nguồn: - [High-Resolution Measurement of Data Center Microbursts](https://research.facebook.com/publications/high-resolution-measurement-of-data-center-microbursts/) · Meta · Hơn 70% burst ở switch rack trong trung tâm dữ liệu kết thúc trong vài chục µs, và mức sử dụng trung bình theo phút ít liên quan đến số gói bị drop (IMC 2017) - [tc-fq(8) — Linux manual page](https://man7.org/linux/man-pages/man8/tc-fq.8.html) · iproute2 · Hàng đợi fq pacing theo từng socket (kết nối), SO_MAX_PACING_RATE đặt tốc độ tối đa cho từng kết nối - [net/ipv4/tcp_bbr.c](https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/net/ipv4/tcp_bbr.c?h=v6.12) · Linux kernel · BBR đặt pacing_rate theo băng thông điểm nghẽn ước tính rồi gửi theo tốc độ đó - [IP Sysctl](https://docs.kernel.org/networking/ip-sysctl.html) · Linux kernel · TCP chọn kích thước frame TSO theo tốc độ của luồng (tối đa 64 KB, tcp_min_tso_segs) - [Monitor network performance for ENA settings on your EC2 instance](https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/monitoring-network-performance-ena.html) · AWS · bw_out_allowance_exceeded, pps_allowance_exceeded: số gói tin bị đưa vào hàng đợi hoặc bị bỏ vì vượt giới hạn băng thông hoặc số gói mỗi giây của instance - [RFC 2863: The Interfaces Group MIB](https://www.rfc-editor.org/rfc/rfc2863) · IETF · ifOutDiscards: số gói tin bị bỏ, không được gửi đi dù không có lỗi, vì các lý do như cần giải phóng chỗ trong bộ đệm - [Demonstrations of tcpretrans, the Linux eBPF/bcc version](https://raw.githubusercontent.com/iovisor/bcc/master/tools/tcpretrans_example.txt) · IO Visor · Hiển thị mỗi lần truyền lại trên một dòng, gồm địa chỉ, cổng của phía bên kia và trạng thái kết nối #### rt-policer · Policer bỏ phần vượt mức · Traffic policing Gói cước của nhà mạng, giới hạn của instance cloud và thiết bị chống DDoS đôi khi bỏ ngay các gói vượt tốc độ quy định mà không đưa vào hàng đợi. - Vì sao → Dẫn đến → Trên màn hình: Lượng dữ liệu gửi tức thời vượt tốc độ cho phép hoặc burst cho phép → Gói vượt mức bị bỏ ngay, không qua hàng đợi (policing) → Mỗi lúc burst lớn lại mất nhiều gói nên đứng hình rồi tua nhanh, trong khi tốc độ trung bình trông vẫn dưới giới hạn - Triệu chứng: Đứng hình, Tua nhanh, Dịch chuyển tức thời / Yếu tố: Mất gói - Ai gặp: Cả server, Một khu vực hoặc nhà mạng, Chỉ mình tôi / Khi nào: Khi đông người, Giờ cao điểm buổi tối - Phụ trách chính: Hạ tầng mạng (Đội hạ tầng) / Phối hợp: Hạ tầng server (Đội hạ tầng), Phát triển server (Đội phát triển game) - Việc cần làm (Đội phát triển game): Chia lượng dữ liệu dồn gửi mỗi tick ra rải trong tick để lượng gửi tức thời thấp hơn burst cho phép; nếu chạm giới hạn số gói mỗi giây thì gộp các message của một tick vào một gói. - Việc cần làm (Đội hạ tầng): Mạng: kiểm tra counter vượt mức của policer trên thiết bị, dùng shaper thay cho policer, tăng burst cho phép. Thiết bị server·OS: kiểm tra chỉ số vượt giới hạn của cloud (trên AWS là bw_out_allowance_exceeded và pps_allowance_exceeded trong ethtool -S), nâng cấp instance, pacing phía server (hàng đợi fq của Linux). - Con số tham khảo: Shaper (đưa vào hàng đợi để làm chậm) làm tăng độ trễ, còn policer (bỏ ngay) làm tăng mất gói. Kết nối game TCP có thể đứng vài trăm ms chỉ vì một lần mất gói, nên nếu chỉ vượt giới hạn trong chốc lát thì policer thường gây ảnh hưởng lớn hơn. - Trên đồ thị: Chạm giới hạn rồi đi ngang (Lượng gửi theo chu kỳ ngắn, counter vượt mức của policer hoặc allowance) - Chỗ cần xem: Counter vượt mức (exceed) và bỏ gói của thiết bị có đặt policer; nếu là cloud thì xem bw_out_allowance_exceeded và pps_allowance_exceeded trong ethtool -S. Với kết nối bị mất gói, xem RTT ngay trước lúc mất gói qua rtt của ss -ti hoặc bản bắt gói (packet capture) - Đúng nếu: counter vượt mức tăng, lượng gửi nhìn theo chu kỳ ngắn phẳng như bị cắt ngang ở một mức nào đó. RTT không tăng ngay trước khi mất gói, và chỉ lúc burst lớn mới mất nhiều gói cùng lúc - Loại trừ nếu: ngay trước khi mất gói, RTT đã tăng lên trước → tràn hàng đợi (“Tràn hàng đợi ở điểm nghẽn”, “Burst gửi làm tràn bộ đệm nhỏ”). Counter vượt mức không đổi → nguyên nhân khác - Cách kiểm tra: Kiểm tra bằng công cụ hạ tầng (không cần code game) - Nguồn: - [RFC 2475: An Architecture for Differentiated Services](https://www.rfc-editor.org/rfc/rfc2475) · IETF · Định nghĩa: shaping làm chậm gói tin cho khớp với traffic profile, còn policing bỏ các gói vượt quá profile - [An Internet-Wide Analysis of Traffic Policing](https://research.google/pubs/an-internet-wide-analysis-of-traffic-policing/) · Google · Kết nối bị policing có tỷ lệ mất gói trung bình cao gấp 6 lần, và pacing hoặc shaping đạt được cùng mục đích đó. Cách phân biệt: policing bỏ phần vượt mức mà RTT không tăng, còn tràn hàng đợi thì RTT tăng trước khi mất gói (SIGCOMM 2016) - [Monitor network performance for ENA settings on your EC2 instance](https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/monitoring-network-performance-ena.html) · AWS · bw_out_allowance_exceeded, pps_allowance_exceeded trong ethtool -S: số gói tin bị đưa vào hàng đợi hoặc bị bỏ vì vượt giới hạn của instance - [tc-fq(8) — Linux manual page](https://man7.org/linux/man-pages/man8/tc-fq.8.html) · iproute2 · Pacing theo từng kết nối của hàng đợi fq trên Linux - [ss(8) — Linux manual page](https://man7.org/linux/man-pages/man8/ss.8.html) · iproute2 · rtt (thời gian khứ hồi trung bình) trong ss -i #### rt-physical · Lỗi vật lý (cáp, module quang, đầu nối hỏng) · Bit errors: bad cable, optics, dirty fiber Cáp bị hỏng, đầu nối quang bám bụi, module quang hết tuổi thọ sẽ gây lỗi bit, và thiết bị âm thầm bỏ các gói bị hỏng. - Vì sao → Dẫn đến → Trên màn hình: Cáp, module quang hoặc đầu nối hỏng làm bit bị đảo → Thiết bị bỏ gói có checksum (CRC) không khớp → Chỉ những người đi qua tuyến đó bị khựng ngắn rồi tua nhanh một cách đều đặn, không liên quan khung giờ - Triệu chứng: Đứng hình, Tua nhanh, Dịch chuyển tức thời / Yếu tố: Mất gói - Ai gặp: Một địa điểm hoặc kênh, Cùng một nhà / Khi nào: Luôn luôn - Phụ trách chính: Hạ tầng mạng (Đội hạ tầng) / Phối hợp: Hạ tầng server (Đội hạ tầng), Bên ngoài (Bên ngoài) - Việc cần làm (Đội hạ tầng): Lỗi CRC dồn ở phía nhận của chiều bị hỏng nên phải kiểm tra cả hai đầu. Mạng: kiểm tra counter lỗi CRC và lỗi chiều vào trên cổng thiết bị, kiểm tra cường độ tín hiệu quang (thông tin module quang trên switch), vệ sinh đầu nối quang, thay cáp hoặc module quang. Thiết bị server·OS: kiểm tra rx_crc_errors trong ethtool -S trên server (tên hơi khác tùy driver), kiểm tra cường độ tín hiệu quang (ethtool -m), thay cáp hoặc NIC phía server. - Việc cần làm (Bên ngoài): Nếu lỗi nằm ở chặng trong nhà người chơi thì hướng dẫn thay cáp LAN hoặc router; nếu ở chặng đường truyền của nhà mạng thì yêu cầu nhà mạng kiểm tra đường truyền. - Con số tham khảo: Dù chỉ mất 0,1% thì cũng là cứ 1.000 gói tin game lại mất một gói. Nếu vài chục người đi qua tuyến đó thì cứ vài giây lại có người bị khựng. Gói càng lớn càng dễ dính lỗi bit. - Trên đồ thị: Chỉ một phần cao (Số lỗi CRC theo cổng, tỷ lệ truyền lại theo server hoặc theo cổng) - Chỗ cần xem: Counter CRC ở cả hai đầu liên kết. Server: rx_crc_errors trong ethtool -S hoặc crc trong ip -s -s link; switch: lỗi FCS (dot3StatsFCSErrors) và lỗi chiều vào (ifInErrors) của cổng. Với liên kết quang, xem cường độ quang nhận được bằng ethtool -m và thông tin module quang trên switch - Đúng nếu: lỗi CRC của một cổng tăng đều bất kể khung giờ, và chỉ các server hoặc kết nối đi qua cổng đó có tỷ lệ truyền lại cao. Cường độ quang nhận được thấp hơn các liên kết cùng loại khác - Loại trừ nếu: CRC không đổi mà chỉ số gói bị bỏ ở chiều ra tăng → tràn hàng đợi (“Burst gửi làm tràn bộ đệm nhỏ”, “Tràn hàng đợi ở điểm nghẽn”). Late collision ở một đầu và CRC ở đầu kia cùng tăng → “Không khớp chế độ duplex” - Cách kiểm tra: Kiểm tra bằng công cụ hạ tầng (không cần code game) - Nguồn: - [Interface statistics](https://docs.kernel.org/networking/statistics.html) · Linux kernel · rx_crc_errors là số gói mà interface phía nhận đếm là lỗi CRC, xem bằng ip -s -s link và ethtool -S - [ethtool(8) — Linux manual page](https://man7.org/linux/man-pages/man8/ethtool.8.html) · ethtool · -S xem thống kê theo NIC và driver, -m xem EEPROM và thông tin chẩn đoán quang của module quang (SFP+, QSFP) - [RFC 3635: Definitions of Managed Objects for the Ethernet-like Interface Types](https://www.rfc-editor.org/rfc/rfc3635) · IETF · Lỗi FCS của cổng switch (dot3StatsFCSErrors); lỗi này được cộng vào lỗi chiều vào (ifInErrors) #### rt-duplex · Không khớp chế độ duplex · Duplex mismatch Nếu một đầu để tự động đàm phán (auto-negotiation) còn đầu kia cố định tốc độ và duplex, một bên sẽ chạy half-duplex và mỗi lần có tải lại mất gói do va chạm (collision). - Vì sao → Dẫn đến → Trên màn hình: Chỉ một đầu thiết bị cố định cấu hình tốc độ và duplex → Một bên chạy full-duplex, bên kia chạy half-duplex nên xảy ra va chạm và va chạm muộn (late collision) → Bình thường không sao, nhưng khi lưu lượng tăng thì mọi người đi qua thiết bị đó đều đứng hình rồi tua nhanh - Triệu chứng: Đứng hình, Tua nhanh / Yếu tố: Mất gói - Ai gặp: Cả server, Một địa điểm hoặc kênh / Khi nào: Khi đông người, Giờ cao điểm buổi tối - Phụ trách chính: Hạ tầng mạng (Đội hạ tầng) / Phối hợp: Hạ tầng server (Đội hạ tầng) - Việc cần làm (Đội hạ tầng): Hai đầu cùng tự động đàm phán hoặc cùng cố định một giá trị. Mạng: kiểm tra tốc độ và duplex trong trạng thái cổng switch; trên counter cổng, kiểm tra phía half-duplex có tăng late collision, phía full-duplex có tăng lỗi CRC và frame quá ngắn (runt) hay không. Thiết bị server·OS: kiểm tra tốc độ và duplex bằng ethtool. - Con số tham khảo: Cáp đồng 1 Gbps bắt buộc phải tự động đàm phán, còn từ 10 Gbps trở lên thì hoàn toàn không có half-duplex. Vì vậy ngày nay lỗi này chủ yếu gặp ở thiết bị cũ từ 100 Mbps trở xuống, cổng quản trị và một số chặng kết nối đường truyền. - Trên đồ thị: Tăng theo số người và tải (Số late collision và lỗi CRC trên cổng, tỷ lệ truyền lại) - Chỗ cần xem: Tốc độ và duplex thực tế ở cả hai đầu liên kết. Server: ethtool chạy chỉ với tên interface; switch: trạng thái cổng hoặc dot3StatsDuplexStatus trong SNMP. Xem kèm late collision (tx_window_errors trên server, dot3StatsLateCollisions trên switch) và lỗi CRC - Đúng nếu: một đầu báo half-duplex, đầu kia báo full-duplex. Mỗi lần lưu lượng tăng, phía half-duplex tăng late collision, phía full-duplex tăng lỗi CRC - Loại trừ nếu: tốc độ và duplex hai đầu giống nhau, chỉ CRC tăng → “Lỗi vật lý”. Liên kết từ 10 Gbps trở lên không có half-duplex nên loại nguyên nhân này - Cách kiểm tra: Kiểm tra bằng công cụ hạ tầng (không cần code game) - Nguồn: - [Linux Base Driver for Intel(R) Ethernet Network Connection](https://docs.kernel.org/networking/device_drivers/ethernet/intel/e1000.html) · Linux kernel · Chuẩn 1000BASE-T yêu cầu tự động đàm phán - [IEEE 802.3ae 10 Gigabit Ethernet: HSSG Objectives](https://www.ieee802.org/3/ae/objectives.pdf) · IEEE · Ethernet 10 Gigabit chỉ hỗ trợ full-duplex - [IEEE P802.3ba Objectives](https://www.ieee802.org/3/ba/PAR/P802.3ba_Objectives_0709.pdf) · IEEE · Ethernet 40 và 100 Gigabit cũng chỉ hỗ trợ full-duplex - [Interface statistics](https://docs.kernel.org/networking/statistics.html) · Linux kernel · tx_window_errors là số lần gửi thất bại do va chạm muộn (late collision), rx_crc_errors là số gói nhận bị lỗi CRC - [ethtool(8) — Linux manual page](https://man7.org/linux/man-pages/man8/ethtool.8.html) · ethtool · Các tham số speed, duplex, autoneg của ethtool -s dùng để đặt tốc độ, duplex và tự động đàm phán; chỉ truyền tên interface thì hiển thị cấu hình hiện tại - [RFC 3635: Definitions of Managed Objects for the Ethernet-like Interface Types](https://www.rfc-editor.org/rfc/rfc3635) · IETF · dot3StatsDuplexStatus (hiển thị duplex hiện tại là halfDuplex hoặc fullDuplex), dot3StatsLateCollisions (số late collision) #### rt-host-drop · Host server nhận bỏ gói tin · Receiver host drops (ring, softirq, CPU) Gói tin đã đến được server nhưng bị bỏ vì ring buffer của NIC (bộ đệm tạm giữ gói tin vừa đến) bị tràn, hoặc core xử lý nhận gói của kernel bị bão hòa. - Vì sao → Dẫn đến → Trên màn hình: Số người vào tăng vọt, ngắt dồn vào một core, CPU steal trên máy ảo, switch ảo quá tải → Bị bỏ ở ring buffer (rx_missed_errors và các counter tương tự, tên khác nhau tùy driver) hoặc ở hàng đợi nhận của kernel (softnet dropped) → Khi đông người, cả server cùng lúc bị trễ thao tác và khựng - Triệu chứng: Trễ thao tác, Đứng hình, Tua nhanh, Dịch chuyển tức thời / Yếu tố: Mất gói, Ngưng trệ - Ai gặp: Cả server / Khi nào: Khi đông người - Phụ trách chính: Hạ tầng server (Đội hạ tầng) - Việc cần làm (Đội hạ tầng): Thêm counter bỏ gói như rx_missed_errors trong ethtool -S và dropped trong /proc/net/softnet_stat vào giám sát, tăng kích thước ring buffer (ethtool -G), phân tán RSS và ngắt ra nhiều core, tách core xử lý nhận gói khỏi thread game, bảo đảm CPU còn dư, nếu là máy ảo thì kiểm tra CPU steal và tải của switch ảo. - Con số tham khảo: Gói mà server đã nhận rồi bỏ thì client sẽ gửi lại. Vì vậy chỉ số truyền lại của server ít khi phản ánh vấn đề này; nó hiện ra trước ở các counter bỏ gói như rx_missed_errors trong ethtool -S (tên khác nhau tùy driver) và cột dropped trong /proc/net/softnet_stat. - Trên đồ thị: Chạm giới hạn rồi đi ngang (Mức sử dụng softirq theo core, counter bỏ gói của NIC) - Chỗ cần xem: Counter bỏ gói trong ethtool -S (rx_missed_errors và các counter tương tự; với mlx5 là rx_out_of_buffer và rx_discards_phy), missed trong ip -s -s link, cột thứ 2 (dropped) và cột thứ 3 (time_squeeze) của /proc/net/softnet_stat; xem %soft (thời gian xử lý ngắt mềm) theo core bằng mpstat -P ALL. Nếu là máy ảo thì xem cả %steal - Đúng nếu: vào lúc đông người, counter bỏ gói hoặc softnet dropped tăng, %soft của core phụ trách nhận gói lên gần 100% rồi không tăng thêm được. Mọi kết nối của server đó cùng lúc bị trễ thao tác - Loại trừ nếu: counter bỏ gói của server không đổi và truyền lại dồn vào kết nối của một khu vực hoặc nhà mạng nhất định → mất gói trên tuyến đường. Nếu gói server gửi đi bị mất trên đường thì TcpRetransSegs trong nstat của server tăng, còn các counter này không đổi - Cách kiểm tra: Kiểm tra bằng công cụ hạ tầng (không cần code game) - Nguồn: - [Interface statistics](https://docs.kernel.org/networking/statistics.html) · Linux kernel · rx_missed_errors là số gói host không nhận được vì hết bộ đệm (trong /proc/net/dev được cộng vào drop), xem bằng ip -s -s link - [drivers/net/ethernet/intel/igb/igb_ethtool.c](https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/drivers/net/ethernet/intel/igb/igb_ethtool.c?h=v6.12) · Linux kernel · Tên counter trong ethtool -S do driver đặt (ví dụ rx_missed_errors và rx_no_buffer_count của igb) - [Ethtool counters](https://docs.kernel.org/networking/device_drivers/ethernet/mellanox/mlx5/counters.html) · Linux kernel · rx_out_of_buffer (hàng đợi nhận hết bộ đệm) và rx_discards_phy (bỏ gói vì thiếu bộ đệm của cổng) của driver mlx5 - [net/core/net-procfs.c](https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/net/core/net-procfs.c?h=v6.12) · Linux kernel · /proc/net/softnet_stat có mỗi CPU một dòng, số hệ 16; cột thứ 2 là dropped, cột thứ 3 là time_squeeze - [Documentation for /proc/sys/net/](https://docs.kernel.org/admin-guide/sysctl/net.html) · Linux kernel · netdev_max_backlog: giới hạn của hàng đợi nhận dùng để giữ gói khi gói đến nhanh hơn tốc độ kernel xử lý - [Scaling in the Linux Networking Stack](https://docs.kernel.org/networking/scaling.html) · Linux kernel · RSS: NIC chia gói ra nhiều hàng đợi nhận để nhiều CPU cùng xử lý - [ethtool(8) — Linux manual page](https://man7.org/linux/man-pages/man8/ethtool.8.html) · ethtool · Đổi kích thước ring buffer bằng -G (--set-ring) - [proc_stat(5) — Linux manual page](https://man7.org/linux/man-pages/man5/proc_stat.5.html) · Linux man-pages · steal trong /proc/stat: thời gian OS khác dùng CPU trong môi trường ảo hóa - [mpstat(1) — Linux manual page](https://man7.org/linux/man-pages/man1/mpstat.1.html) · sysstat · mpstat -P ALL cho biết mức sử dụng theo core; %soft là thời gian xử lý ngắt mềm, %steal là thời gian phải chờ vì hypervisor đang phục vụ CPU ảo khác - [net/ipv4/proc.c](https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/net/ipv4/proc.c?h=v6.12) · Linux kernel · TcpRetransSegs trong nstat (RetransSegs của mục Tcp) #### rt-stateful-fw · Tường lửa và theo dõi kết nối bỏ gói · Stateful firewall / conntrack drops Tường lửa hoặc tính năng theo dõi kết nối của Linux (conntrack, ghi các kết nối đi qua vào một bảng) sẽ bỏ gói tin khi bảng đầy hoặc khi xác định trạng thái kết nối không khớp. - Vì sao → Dẫn đến → Trên màn hình: Bảng theo dõi kết nối đầy (table full), hoặc chiều đi và chiều về khác đường nên chỉ một chiều đi qua tường lửa (tuyến đường bất đối xứng) → Tường lửa coi đó là gói của “kết nối lạ” hoặc có “số thứ tự nằm ngoài phạm vi cửa sổ” rồi bỏ đi → Bảng đầy thì kết nối mới bị chặn; tuyến đường lệch thì chỉ người đi tuyến đó bị truyền lại liên tục rồi mất kết nối - Triệu chứng: Đứng hình, Mất kết nối, Không vào được·kẹt loading / Yếu tố: Mất gói - Ai gặp: Cả server, Một khu vực hoặc nhà mạng / Khi nào: Khi đông người, Ngay sau đăng nhập hoặc bảo trì, Thỉnh thoảng bất chợt - Phụ trách chính: Hạ tầng mạng (Đội hạ tầng) / Phối hợp: Hạ tầng server (Đội hạ tầng), Phát triển server (Đội phát triển game), Phát triển client (Đội phát triển game) - Việc cần làm (Đội phát triển game): Server: phòng khi bảng đầy, dùng hệ thống hàng chờ đăng nhập để điều tiết lượng kết nối dồn vào, tái sử dụng kết nối để không lặp lại các kết nối ngắn (kể cả gọi giữa các server), dọn trước các kết nối đã mất heartbeat. Client: khi kết nối thất bại hoặc bị ngắt, tăng dần khoảng cách thử lại và rải ngẫu nhiên (để không cùng dồn lại một lượt khi bảng đầy). - Việc cần làm (Đội hạ tầng): Mạng: tăng kích thước bảng theo dõi kết nối của tường lửa, cho cổng game ra khỏi theo dõi kết nối, chỉnh định tuyến để chiều đi và chiều về qua cùng một tường lửa, xem lại cấu hình kiểm tra cửa sổ TCP của tường lửa. Thiết bị server·OS: tăng kích thước bảng trên Linux (nf_conntrack_max), cho cổng game ra khỏi theo dõi kết nối (NOTRACK), xem lại cấu hình kiểm tra cửa sổ TCP (nf_conntrack_tcp_be_liberal), trên AWS thì kiểm tra thêm conntrack_allowance_exceeded. - Con số tham khảo: Giới hạn mặc định của conntrack trên Linux (nf_conntrack_max) từ vài chục nghìn đến vài trăm nghìn mục, tùy dung lượng bộ nhớ. Khi số hiện tại (nf_conntrack_count) chạm giới hạn, log sẽ ghi “nf_conntrack: table full, dropping packet”. - Trên đồ thị: Chạm giới hạn rồi đi ngang (Số mục conntrack (nf_conntrack_count), số kết nối mới thất bại) - Chỗ cần xem: Trên server Linux: nf_conntrack_count và nf_conntrack_max, “nf_conntrack: table full, dropping packet” trong dmesg, drop và invalid trong /proc/net/stat/nf_conntrack (mỗi core một dòng, số hệ 16). Với tường lửa: mức sử dụng bảng phiên và log drop; trên AWS: conntrack_allowance_exceeded trong ethtool -S - Đúng nếu: số mục đi ngang ở mức giới hạn, cùng lúc đó log table full và drop, hoặc conntrack_allowance_exceeded tăng. Nếu là tuyến đường bất đối xứng thì giới hạn vẫn còn dư nhưng invalid và log drop của tường lửa tăng ở các kết nối đi một tuyến nhất định - Loại trừ nếu: số mục còn xa giới hạn, invalid và log drop cũng không đổi → nguyên nhân khác. Bảng còn dư nhưng CPU hoặc số gói mỗi giây của tường lửa đã kịch trần → “Thiết bị trung gian vượt giới hạn xử lý” - Cách kiểm tra: Kiểm tra bằng công cụ hạ tầng (không cần code game) - Nguồn: - [Netfilter Conntrack Sysfs variables](https://docs.kernel.org/networking/nf_conntrack-sysctl.html) · Linux kernel · Giá trị mặc định của nf_conntrack_max bằng số hash bucket (bộ nhớ ÷ 16384, từ 1.024 đến 262.144), số hiện tại là nf_conntrack_count, nf_conntrack_tcp_be_liberal chỉ coi RST nằm ngoài cửa sổ là INVALID - [net/netfilter/nf_conntrack_core.c](https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/net/netfilter/nf_conntrack_core.c?h=v6.12) · Linux kernel · Khi bảng đầy thì ghi log “nf_conntrack: table full, dropping packet” rồi bỏ gói (thống kê drop tăng); gói không khớp trạng thái kết nối làm thống kê invalid tăng - [iptables-extensions(8) — Linux manual page](https://man7.org/linux/man-pages/man8/iptables-extensions.8.html) · netfilter · Loại khỏi theo dõi kết nối bằng CT --notrack trong bảng raw - [Amazon EC2 security group connection tracking](https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/security-group-connection-tracking.html) · AWS · Vượt giới hạn số kết nối được theo dõi của mỗi instance thì gói bị bỏ, xem qua conntrack_allowance_exceeded; khuyến nghị tránh tuyến đường bất đối xứng - [net/netfilter/nf_conntrack_standalone.c](https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/net/netfilter/nf_conntrack_standalone.c?h=v6.12) · Linux kernel · /proc/net/stat/nf_conntrack có mỗi core một dòng, số hệ 16, gồm các cột như entries, invalid, insert_failed, drop, early_drop #### rt-appliance-pps · Thiết bị trung gian vượt giới hạn xử lý (tường lửa, IPS, chống DDoS) · Inline appliance PPS / CPU overload Tường lửa, thiết bị chống xâm nhập (IPS) và thiết bị chống DDoS kiểm tra từng gói tin đi qua. Từ lúc vượt năng lực kiểm tra, thiết bị bỏ những gói không xử lý kịp. - Vì sao → Dẫn đến → Trên màn hình: Giờ cao điểm hoặc sự kiện làm hơn vài trăm nghìn gói game nhỏ dồn tới mỗi giây, hoặc bộ luật kiểm tra quá nặng → CPU hoặc giới hạn số gói mỗi giây của thiết bị đã đầy nên thiết bị bỏ gói. Nếu phát hiện nhầm (false positive) thì cả gói bình thường cũng bị chặn → Toàn bộ server phía sau thiết bị đó cùng lúc bị đứng hình hoặc dịch chuyển tức thời, chỉ nặng lên khi đông người - Triệu chứng: Đứng hình, Tua nhanh, Dịch chuyển tức thời, Mất kết nối / Yếu tố: Mất gói, Độ trễ - Ai gặp: Cả server, Một khu vực hoặc nhà mạng / Khi nào: Giờ cao điểm buổi tối, Khi đông người - Phụ trách chính: Hạ tầng mạng (Đội hạ tầng) / Phối hợp: Phát triển server (Đội phát triển game) - Việc cần làm (Đội phát triển game): Chia sẻ đặc điểm lưu lượng game (cổng, kích thước gói, số gói mỗi giây) với đội hạ tầng, gom các message nhỏ cần gửi trong một tick lại gửi một lần để giảm số gói. - Việc cần làm (Đội hạ tầng): Xem CPU, số gói mỗi giây và counter drop của thiết bị cùng với chỉ số game, tính dung lượng thiết bị theo gói nhỏ, cho cổng game ra khỏi các bước kiểm tra nặng, chỉnh luật chống DDoS cho khớp với đặc điểm lưu lượng game. - Con số tham khảo: Con số “10 Gbps” trong thông số thiết bị thường được ghi theo gói lớn 1.500 byte. Gói game chỉ khoảng 100 byte nên với cùng băng thông, số gói nhiều gấp hơn 10 lần; đường truyền trông vẫn rảnh nhưng giới hạn số gói mỗi giây đã đầy trước. - Trên đồ thị: Chạm giới hạn rồi đi ngang (Số gói mỗi giây và mức sử dụng CPU của thiết bị, số gói bị thiết bị drop) - Chỗ cần xem: CPU, số gói mỗi giây và counter drop của thiết bị; so sánh số gói trên cổng switch phía trước và phía sau thiết bị với cùng chu kỳ. Đặt chồng lên cùng một màn hình với số người chơi đồng thời và tỷ lệ truyền lại của server - Đúng nếu: vào giờ cao điểm hoặc sự kiện, số gói mỗi giây hoặc CPU của thiết bị chững lại ở một mức không tăng thêm được, số gói ra khỏi thiết bị ít hơn số gói đi vào, và cùng lúc đó tỷ lệ truyền lại của toàn bộ server phía sau cũng tăng - Loại trừ nếu: số gói trước và sau thiết bị bằng nhau, thiết bị cũng không drop → nguyên nhân khác. Counter bỏ gói trên NIC hoặc softnet dropped của server tăng → “Host server nhận bỏ gói tin” - Cách kiểm tra: Kiểm tra bằng công cụ hạ tầng (không cần code game) - Nguồn: - [RFC 2544: Benchmarking Methodology for Network Interconnect Devices](https://www.rfc-editor.org/rfc/rfc2544) · IETF · Hiệu năng thiết bị phải được kiểm thử với nhiều kích thước frame, gồm cả kích thước nhỏ nhất và lớn nhất (hiệu năng xử lý thay đổi theo kích thước gói) #### rt-mtu · MTU black hole (chỉ gói lớn liên tục bị mất) · PMTU black hole Khi kích thước tối đa mà một chặng giữa đường nhận được bị giảm đi nhưng thông báo “quá lớn” (ICMP) lại bị chặn, gói lớn gửi lại bao nhiêu lần cũng tiếp tục biến mất. - Vì sao → Dẫn đến → Trên màn hình: Kích thước tối đa giảm ở chặng VPN hoặc tunnel, còn thông báo vượt kích thước bị tường lửa chặn → Bên gửi không biết lý do nên cứ truyền lại cùng một gói lớn, RTO tăng gấp đôi sau mỗi lần → Bình thường không sao, nhưng vào lúc có dữ liệu lớn đi qua (mở túi đồ, đến chỗ đông người, loading khi vào khu vực mới) thì cả các gói nhỏ phía sau cũng bị kẹt theo và game đứng hình, cuối cùng mất kết nối hoặc kẹt loading - Triệu chứng: Đứng hình, Mất kết nối, Không vào được·kẹt loading / Yếu tố: Mất gói - Ai gặp: Một khu vực hoặc nhà mạng, Chỉ mình tôi / Khi nào: Khi làm một thao tác nhất định, Ngay sau đăng nhập hoặc bảo trì - Phụ trách chính: Hạ tầng mạng (Đội hạ tầng) / Phối hợp: Hạ tầng server (Đội hạ tầng), Phát triển server (Đội phát triển game) - Việc cần làm (Đội phát triển game): Muốn tự hạ từ phía server thì đặt kích thước segment tối đa của socket (TCP_MAXSEG); chỉ chia nhỏ message trong code game thì không chặn được (TCP gom dữ liệu cần gửi lại theo kích thước MSS). - Việc cần làm (Đội hạ tầng): Mạng: điều chỉnh MSS (clamping) ở thiết bị biên, cho phép ICMP báo vượt kích thước (type 3 code 4, fragmentation needed) trên tường lửa và network ACL của cloud. Thiết bị server·OS: cấu hình path MTU, kiểm tra tường lửa trên server và security group của cloud cũng không chặn ICMP báo vượt kích thước, dùng tcp_mtu_probing=1 của Linux làm lưới an toàn cuối cùng. - Con số tham khảo: Thường là 1.500 byte, đi qua tunnel thì còn khoảng 1.400. Cùng một gói bị truyền lại 5–6 lần thì thời gian đứng hình vượt quá 10 giây. - Trên đồ thị: Chỉ một phần cao (RTO và backoff theo từng kết nối, số lần mất kết nối theo khu vực hoặc nhà mạng) - Chỗ cần xem: Các lần truyền lại của kết nối có vấn đề, qua bản bắt gói phía server hoặc bcc tcpretrans -s (hiển thị số thứ tự); xem mss, pmtu, backoff của kết nối đó bằng ss -ti. Từ server gửi tới địa chỉ người chơi đó một ping nhỏ và một ping 1.500 byte có bật DF (ping -M do -s 1472) rồi so sánh - Đúng nếu: gói đầy đúng kích thước MSS liên tục bị truyền lại với cùng số thứ tự, khoảng cách tăng gấp đôi mỗi lần, còn gói nhỏ hơn vẫn đi lại bình thường. Không có ICMP báo vượt kích thước (filter Wireshark icmp.type == 3 and icmp.code == 4), ping nhỏ có phản hồi nhưng ping lớn có DF thì mất hẳn, không phản hồi - Loại trừ nếu: gói nhỏ cũng biến mất theo → mất gói không liên quan kích thước (“Tràn hàng đợi ở điểm nghẽn”, “Đổi tuyến đường hoặc tuyến ECMP lỗi”). ICMP báo vượt kích thước có đến và pmtu trong ss -ti giảm xuống → cơ chế dò path MTU đang hoạt động đúng - Cách kiểm tra: Kiểm tra bằng công cụ hạ tầng (không cần code game) - Tìm hiểu thêm: tcp_mtu_probing=1 chỉ xác định là black hole và hạ MSS xuống 1.024 byte sau khi timeout truyền lại kéo dài vài giây (tương ứng tcp_retries1=3). Trong khoảng đó game đứng hình, nên chỉ coi đây là lưới an toàn cuối cùng; việc cần làm trước là điều chỉnh MSS để chặn từ đầu. - Nguồn: - [RFC 1191: Path MTU discovery](https://www.rfc-editor.org/rfc/rfc1191) · IETF · Dò path MTU: gói quá lớn được báo bằng ICMP “fragmentation needed and DF set” (type 3 code 4) - [RFC 2923: TCP Problems with Path MTU Discovery](https://www.rfc-editor.org/rfc/rfc2923) · IETF · Vấn đề PMTU black hole: ICMP bị chặn nên chỉ gói lớn liên tục biến mất - [RFC 4821: Packetization Layer Path MTU Discovery](https://www.rfc-editor.org/rfc/rfc4821) · IETF · Cách tầng giao vận tự dò kích thước gói mà không cần ICMP (nền tảng của tcp_mtu_probing trên Linux) - [IP Sysctl](https://docs.kernel.org/networking/ip-sysctl.html) · Linux kernel · tcp_mtu_probing: 0 là tắt, 1 là chỉ khi phát hiện black hole, 2 là luôn luôn (MSS khởi đầu là tcp_base_mss). tcp_retries1 mặc định 3 - [net/ipv4/tcp_timer.c](https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/net/ipv4/tcp_timer.c?h=v6.12) · Linux kernel · Khi truyền lại do RTO kéo dài đủ tcp_retries1 lần thì coi là đã phát hiện black hole, bật dò MTU để hạ MSS - [include/net/tcp.h](https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/include/net/tcp.h?h=v6.12) · Linux kernel · TCP_BASE_MSS = 1.024 byte - [RFC 6298: Computing TCP's Retransmission Timer](https://www.rfc-editor.org/rfc/rfc6298) · IETF · Mỗi lần timer truyền lại hết hạn thì RTO tăng gấp đôi - [iptables-extensions(8) — Linux manual page](https://man7.org/linux/man-pages/man8/iptables-extensions.8.html) · netfilter · TCPMSS --clamp-mss-to-pmtu: đi vòng qua vấn đề gói lớn bị kẹt do chặng chặn ICMP bằng cách điều chỉnh MSS trong SYN - [tcp(7) — Linux manual page](https://man7.org/linux/man-pages/man7/tcp.7.html) · Linux man-pages · TCP_MAXSEG: kích thước segment tối đa của gói đi ra; đặt trước khi kết nối thì MSS báo cho phía bên kia cũng đổi theo - [Network maximum transmission unit (MTU) for your EC2 instance](https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/network_mtu.html) · AWS · Internet gateway và VPN có MTU 1.500; PMTUD cần ICMP type 3 code 4, nếu security group hoặc network ACL chặn thì không nhận được - [MTU considerations | Cloud VPN](https://cloud.google.com/network-connectivity/docs/vpn/concepts/mtu-considerations) · Google Cloud · MTU của Cloud VPN gateway là 1.460 byte, MTU payload của tunnel IPv4 là 1.406 byte (đi qua tunnel thì còn khoảng 1.400) - [Demonstrations of tcpretrans, the Linux eBPF/bcc version](https://raw.githubusercontent.com/iovisor/bcc/master/tools/tcpretrans_example.txt) · IO Visor · Hiển thị mỗi lần truyền lại trên một dòng, -s hiển thị kèm số thứ tự của gói đã truyền lại - [ss(8) — Linux manual page](https://man7.org/linux/man-pages/man8/ss.8.html) · iproute2 · mss, pmtu (path MTU), backoff (số lần RTO đã tăng gấp đôi) trong ss -i - [ping(8) — Linux manual page](https://man7.org/linux/man-pages/man8/ping.8.html) · iputils · -M do bật DF và không gửi gói lớn hơn path MTU mà kernel biết; -s là kích thước dữ liệu (chưa tính 8 byte ICMP header) - [Display Filter Reference: Internet Control Message Protocol](https://www.wireshark.org/docs/dfref/i/icmp.html) · Wireshark · Bộ lọc hiển thị icmp.type, icmp.code #### rt-mapping · Ánh xạ NAT hoặc bộ cân bằng tải hết hạn giữa chừng kết nối · NAT / load balancer mapping expired mid-connection Nếu thiết bị trung gian xóa ánh xạ (mapping: mục ghi lại kết nối này cần được chuyển tới đâu) của một kết nối nhàn rỗi, gói gửi tiếp theo sẽ không được chuyển đi. Bên gửi chỉ truyền lại liên tục rồi mất kết nối, hoặc thiết bị gửi trả gói từ chối kết nối (RST) khiến kết nối đứt ngay. - Vì sao → Dẫn đến → Trên màn hình: Kết nối không có gói nào đi lại trong một thời gian (người chơi rời máy, đứng ở sảnh chờ) → NAT của router, CGNAT của nhà mạng, tường lửa, bộ cân bằng tải hoặc security group của cloud xóa ánh xạ nhàn rỗi → Lúc hoạt động trở lại thì truyền lại liên tiếp rồi mất kết nối, hoặc mất kết nối ngay - Triệu chứng: Mất kết nối, Đứng hình / Yếu tố: Mất gói - Ai gặp: Chỉ mình tôi, Một khu vực hoặc nhà mạng / Khi nào: Sau khi để yên một lúc - Phụ trách chính: Phát triển client (Đội phát triển game) / Phối hợp: Phát triển server (Đội phát triển game), Hạ tầng mạng (Đội hạ tầng), Hạ tầng server (Đội hạ tầng) - Việc cần làm (Đội phát triển game): Client: gửi heartbeat với khoảng cách không quá một nửa idle timeout ngắn nhất (ánh xạ trên router của người chơi và CGNAT của nhà mạng chỉ được làm mới chắc chắn bằng gói đi từ trong ra, và chúng ta không đổi được timeout của các thiết bị đó, nên client phải gửi), tự động kết nối lại khi bị ngắt. Server: phản hồi heartbeat, không nhận được trong một khoảng thời gian thì chủ động dọn kết nối (rút ngắn khoảng keepalive TCP bằng các tùy chọn socket như TCP_KEEPIDLE, phát hiện sớm bằng TCP_USER_TIMEOUT), nối tiếp phiên bằng session token. - Việc cần làm (Đội hạ tầng): Mạng: thu thập idle timeout của tường lửa và bộ cân bằng tải trên tuyến đường rồi chia sẻ cho đội phát triển game, tăng timeout trên tường lửa và bộ cân bằng tải của mình nếu cần. Thiết bị server·OS: kiểm tra thời gian theo dõi kết nối của security group trên cloud rồi chia sẻ cho đội phát triển game. - Con số tham khảo: Thời gian giữ ánh xạ TCP khác nhau tùy thiết bị, từ vài phút đến vài giờ. Nếu security group của cloud được cấu hình theo dõi kết nối, loại instance AWS Nitro v6 mặc định xóa mục theo dõi sau 350 giây (các loại khác là 5 ngày, xem mục “Hết hạn theo dõi kết nối của security group trên cloud”). TCP keepalive của Linux có giá trị mặc định là “kiểm tra sau 2 giờ nhàn rỗi” nên muộn hơn hầu hết các thiết bị. - Trên đồ thị: Rớt kết nối hàng loạt (Số lần mất kết nối, thời gian nhàn rỗi trước khi bị ngắt) - Chỗ cần xem: Vài phút cuối của kết nối bị đứt, qua bản bắt gói phía server; với kết nối còn sống, xem thời gian nhàn rỗi qua lastsnd và lastrcv của ss -ti (số ms đã trôi qua từ lần gửi và nhận cuối). Xem kèm TcpExtTCPAbortOnTimeout trong nstat (số kết nối bị bỏ vì timer hết hạn) - Đúng nếu: mỗi kết nối bị đứt đều có thời gian nhàn rỗi ngay trước đó vượt một giá trị gần giống nhau (idle timeout của thiết bị trên tuyến đường, ví dụ 350 giây của security group trên instance AWS Nitro v6); từ gói đầu tiên sau khoảng nhàn rỗi chỉ có truyền lại mà không có ACK rồi bỏ cuộc, hoặc RST trả về ngay - Loại trừ nếu: đang chơi cũng bị mất kết nối, không liên quan thời gian nhàn rỗi → nguyên nhân khác (“Đổi tuyến đường hoặc tuyến ECMP lỗi”, “Tường lửa và theo dõi kết nối bỏ gói”). Kết nối có heartbeat qua lại với khoảng cách không quá một nửa idle timeout ngắn nhất → loại nguyên nhân này - Cách kiểm tra: Kiểm tra bằng công cụ hạ tầng (không cần code game) - Nguồn: - [RFC 5382: NAT Behavioral Requirements for TCP](https://www.rfc-editor.org/rfc/rfc5382) · IETF · Khuyến nghị idle timeout của kết nối TCP qua NAT phải từ 2 giờ 4 phút trở lên (với tiền đề là thiết bị có thể xóa phiên nhàn rỗi trước) - [RFC 4787: Network Address Translation (NAT) Behavioral Requirements for Unicast UDP](https://www.rfc-editor.org/rfc/rfc4787) · IETF · Ánh xạ NAT phải được làm mới bằng gói đi từ trong ra (REQ-6), còn làm mới bằng gói từ ngoài vào là tùy chọn (áp dụng cho UDP) - [Amazon EC2 security group connection tracking](https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/security-group-connection-tracking.html) · AWS · Idle timeout theo dõi TCP mặc định với loại instance Nitro v6 là 350 giây, với các loại khác là 432.000 giây (5 ngày). Khuyến nghị keepalive với khoảng cách ngắn hơn 5 phút - [IP Sysctl](https://docs.kernel.org/networking/ip-sysctl.html) · Linux kernel · tcp_keepalive_time mặc định 2 giờ - [tcp(7) — Linux manual page](https://man7.org/linux/man-pages/man7/tcp.7.html) · Linux man-pages · TCP_KEEPIDLE (thời gian nhàn rỗi trước khi bắt đầu keepalive), TCP_USER_TIMEOUT (thời gian chờ dữ liệu chưa được xác nhận trước khi đóng kết nối) - [RFC 5482: TCP User Timeout Option](https://www.rfc-editor.org/rfc/rfc5482) · IETF · TCP user timeout: dữ liệu đã gửi chưa được xác nhận trong bao lâu thì đóng kết nối - [ss(8) — Linux manual page](https://man7.org/linux/man-pages/man8/ss.8.html) · iproute2 · lastsnd, lastrcv trong ss -i: thời gian đã trôi qua từ lần gửi và nhận cuối (ms) - [SNMP counter](https://docs.kernel.org/networking/snmp_counter.html) · Linux kernel · TcpExtTCPAbortOnTimeout: số kết nối TCP bị bỏ mà không gửi RST vì timer hết hạn #### rt-path · Đổi tuyến đường hoặc tuyến ECMP lỗi · Route change / bad ECMP member Gói tin biến mất trong vài giây lúc tuyến đường Internet thay đổi, hoặc trên kết nối được gán vào tuyến lỗi trong số nhiều tuyến ECMP. - Vì sao → Dẫn đến → Trên màn hình: BGP tính lại tuyến đường, hoặc thiết bị hay đường truyền của một tuyến trong số nhiều tuyến (ECMP, LAG) bị lỗi → Mất gói tạm thời trong lúc chuyển tuyến, hoặc chỉ kết nối đi tuyến đó bị mất gói đều đặn → Đột nhiên đứng hình vài giây rồi tua nhanh, hoặc “kết nối lại thì đỡ” (được gán sang tuyến khác) - Triệu chứng: Đứng hình, Tua nhanh, Dịch chuyển tức thời / Yếu tố: Mất gói - Ai gặp: Một khu vực hoặc nhà mạng / Khi nào: Thỉnh thoảng bất chợt - Phụ trách chính: Hạ tầng mạng (Đội hạ tầng) / Phối hợp: Phát triển server (Đội phát triển game), Bên ngoài (Bên ngoài) - Việc cần làm (Đội phát triển game): Ghi lại thống kê truyền lại theo từng kết nối (TCP_INFO) để có thể lọc ra IP, cổng và thời điểm của người gặp lỗi; không ngắt ngay kết nối bị đứng vài giây. - Việc cần làm (Đội hạ tầng): Giám sát tỷ lệ truyền lại theo khu vực và nhà mạng, kiểm tra kết nối lại có làm đổi tuyến không, có đường truyền của nhiều nhà mạng, kiểm tra link lỗi trong các tuyến ECMP và LAG trên thiết bị của mình, đo tuyến đường cũng bằng cùng cổng TCP với game (mtr --tcp --port; tuyến đường được chọn theo địa chỉ và cổng nên ping thông thường có thể đi tuyến khác và cho kết quả bình thường). - Việc cần làm (Bên ngoài): Báo tuyến lỗi cho nhà mạng, đính kèm kết quả đo tuyến đường bằng cùng cổng TCP và bản so sánh trước và sau khi kết nối lại. - Trên đồ thị: Tăng như bậc thang từ một thời điểm (RTT (ping), tỷ lệ truyền lại theo khu vực hoặc nhà mạng) - Chỗ cần xem: Gom truyền lại theo từng kết nối bằng bcc tcpretrans -c để lọc ra địa chỉ và cổng của người chơi gặp lỗi; chạy mtr bằng cùng cổng TCP với game (mtr -T -P PORT) theo cả hai chiều, từ server tới người chơi và từ người chơi tới server, rồi so sánh. So sánh cả kết quả trước và sau khi kết nối lại - Đúng nếu: từ một thời điểm, RTT của một khu vực hoặc nhà mạng đổi theo kiểu bậc thang và mất gói dồn lại trong vài giây; hoặc ngay trong cùng nhà mạng, chỉ một số kết nối (tổ hợp địa chỉ và cổng) truyền lại đều đặn và kết nối lại thì đỡ. Có khi ping thông thường vẫn bình thường mà chỉ mtr TCP mới thấy mất gói - Loại trừ nếu: mọi kết nối của nhà mạng đó cùng xấu đi vào giờ cao điểm buổi tối → “Tràn hàng đợi ở điểm nghẽn”. Chỉ một người chơi bị và mất gói ngay từ ping tới router → “Mất gói ở chặng không dây” - Cách kiểm tra: Kiểm tra bằng công cụ hạ tầng (không cần code game) - Sự cố thực tế: cloudflare-2020 - Nguồn: - [RFC 2991: Multipath Issues in Unicast and Multicast Next-Hop Selection](https://www.rfc-editor.org/rfc/rfc2991) · IETF · Mô tả việc kết quả của các công cụ chẩn đoán như ping, traceroute khó tin cậy khi có nhiều tuyến song song, và cách hash luồng để cố định tuyến đường - [RFC 2992: Analysis of an Equal-Cost Multi-Path Algorithm](https://www.rfc-editor.org/rfc/rfc2992) · IETF · ECMP chọn tuyến tiếp theo bằng hash của các trường header dùng để phân biệt luồng (cùng luồng thì cùng tuyến) - [tcp(7) — Linux manual page](https://man7.org/linux/man-pages/man7/tcp.7.html) · Linux man-pages · TCP_INFO: truy vấn trạng thái của từng socket (struct tcp_info) - [mtr(8) manual page source](https://raw.githubusercontent.com/traviscross/mtr/master/man/mtr.8.in) · mtr · -T (--tcp) dùng TCP SYN thay cho ICMP, -P (--port) chỉ định cổng đích - [Demonstrations of tcpretrans, the Linux eBPF/bcc version](https://raw.githubusercontent.com/iovisor/bcc/master/tools/tcpretrans_example.txt) · IO Visor · Hiển thị mỗi lần truyền lại trên một dòng kèm địa chỉ, cổng của phía bên kia; -c đếm tổng số lần truyền lại theo từng luồng #### rt-spurious-delay · Truyền lại không cần thiết do độ trễ tăng vọt · Spurious RTO from delay spikes Gói tin vẫn đến nơi, chỉ là đến rất muộn trong chốc lát; nếu độ trễ đó dài hơn RTO thì bên gửi xác định là mất rồi truyền lại. - Vì sao → Dẫn đến → Trên màn hình: Bufferbloat, chế độ tiết kiệm điện của Wi-Fi, mạng di động chuyển trạng thái vô tuyến, máy ảo bị tạm dừng làm độ trễ tức thời lên vài trăm ms → RTO hết hạn trước nên truyền lại, gói gốc cũng đến ngay sau đó (bên nhận nhận trùng) → Đứng hình và tua nhanh là do chính độ trễ tăng vọt. Truyền lại không cần thiết hầu như không làm đứng hình lâu hơn, chỉ đẩy chỉ số truyền lại lên nên bị hiểu nhầm là mất gói - Triệu chứng: Đứng hình, Tua nhanh, Trễ thao tác / Yếu tố: Độ trễ, Jitter - Ai gặp: Chỉ mình tôi, Cả server / Khi nào: Thỉnh thoảng bất chợt, Sau khi để yên một lúc - Phụ trách chính: Bên ngoài (Bên ngoài) / Phối hợp: Hạ tầng server (Đội hạ tầng), Phát triển client (Đội phát triển game) - Việc cần làm (Đội phát triển game): Với client Android 10 trở lên, xin chế độ Wi-Fi độ trễ thấp khi đang chơi (Wi-Fi lock WIFI_MODE_FULL_LOW_LATENCY, chỉ có hiệu lực khi màn hình bật và game đang chạy ở foreground) để giảm độ trễ tăng vọt do tiết kiệm điện. - Việc cần làm (Đội hạ tầng): Tránh loại instance burstable, không hạ RTO tối thiểu quá thấp, giữ F-RTO và timestamp (tcp_frto, tcp_timestamps), xem chỉ số truyền lại cùng với TCPSpuriousRTOs và TCPDSACKRecv trong nstat để không hiểu nhầm là mất gói. - Việc cần làm (Bên ngoài): Để giảm chính độ trễ tăng vọt, hướng dẫn người chơi bật SQM trên router, tắt tiết kiệm điện Wi-Fi. - Con số tham khảo: Linux dùng F-RTO để phát hiện RTO không cần thiết và có thể khôi phục lại lượng gửi đã bị giảm. Kiểm tra bằng TCPSpuriousRTOs (số lần xác định là RTO không cần thiết) và TCPDSACKRecv (số lần bên nhận báo “đã nhận rồi”) trong nstat. - Trên đồ thị: Thỉnh thoảng vọt lên bất chợt (RTT (ping), số RTO không cần thiết) - Chỗ cần xem: Chạy nstat cách nhau 1 phút, xem cùng lúc phần tăng của TcpExtTCPTimeouts (RTO hết hạn), TcpExtTCPSpuriousRTOs, TcpExtTCPDSACKRecv, TcpExtTCPLostRetransmit. Nếu có bản bắt gói thì dùng filter Wireshark tcp.analysis.spurious_retransmission - Đúng nếu: khi RTO tăng thì TcpExtTCPSpuriousRTOs hoặc TcpExtTCPDSACKRecv cũng tăng theo, cùng lúc RTT vọt lên vài trăm ms. Bản bắt gói phía nhận có cả gói gốc lẫn gói truyền lại - Loại trừ nếu: TcpExtTCPSpuriousRTOs và DSACK không đổi nhưng TcpExtTCPLostRetransmit (mất cả gói đã gửi lại) tăng → mất gói thật. RTT không vọt lên mà chỉ DSACK luôn nhiều → “Truyền lại nhanh không cần thiết do gói đến sai thứ tự” - Cách kiểm tra: Kiểm tra bằng công cụ hạ tầng (không cần code game) - Nguồn: - [RFC 5682: Forward RTO-Recovery (F-RTO): An Algorithm for Detecting Spurious Retransmission Timeouts with TCP](https://www.rfc-editor.org/rfc/rfc5682) · IETF · F-RTO: dùng ACK đến sau RTO để phân biệt RTO đó có phải là không cần thiết hay không - [RFC 3481: TCP over Second (2.5G) and Third (3G) Generation Wireless Networks](https://www.rfc-editor.org/rfc/rfc3481) · IETF · Độ trễ tăng vọt trên mạng di động (handover, phục hồi liên kết…) gây ra timeout và truyền lại TCP không cần thiết, kèm theo việc thu nhỏ cửa sổ tắc nghẽn - [RFC 2883: An Extension to the Selective Acknowledgement (SACK) Option for TCP](https://www.rfc-editor.org/rfc/rfc2883) · IETF · DSACK: bên nhận báo đã nhận trùng để bên gửi biết đó là lần truyền lại không cần thiết - [SNMP counter](https://docs.kernel.org/networking/snmp_counter.html) · Linux kernel · TcpExtTCPSpuriousRTOs (RTO không cần thiết do F-RTO phát hiện), TcpExtTCPDSACKRecv (số DSACK đã nhận), TcpExtTCPLostRetransmit (số lần SACK báo gói đã gửi lại lại bị mất) - [IP Sysctl](https://docs.kernel.org/networking/ip-sysctl.html) · Linux kernel · tcp_frto mặc định bật (có lợi cho mạng không dây có RTT dao động), tcp_timestamps mặc định 1 - [RFC 6298: Computing TCP's Retransmission Timer](https://www.rfc-editor.org/rfc/rfc6298) · IETF · Cơ sở cho việc cần RTO tối thiểu lớn để tránh truyền lại không cần thiết (khuyến nghị tối thiểu 1 giây) - [WifiManager](https://developer.android.com/reference/android/net/wifi/WifiManager#WIFI_MODE_FULL_LOW_LATENCY) · Android (Google) · WIFI_MODE_FULL_LOW_LATENCY (API 29, Android 10): Wi-Fi lock độ trễ thấp, chỉ có hiệu lực khi đang kết nối với AP, màn hình bật và ứng dụng đang chạy ở foreground - [net/ipv4/proc.c](https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/net/ipv4/proc.c?h=v6.12) · Linux kernel · Tên counter trong nstat: TCPTimeouts, TCPSpuriousRTOs, TCPDSACKRecv, TCPLostRetransmit - [net/ipv4/tcp_timer.c](https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/net/ipv4/tcp_timer.c?h=v6.12) · Linux kernel · TCPTimeouts tăng mỗi khi timer truyền lại (RTO) hết hạn - [nstat(8) — Linux manual page](https://man7.org/linux/man-pages/man8/nstat.8.html) · iproute2 · Mặc định nstat hiển thị phần tăng kể từ lần chạy trước - [Display Filter Reference: Transmission Control Protocol](https://www.wireshark.org/docs/dfref/t/tcp.html) · Wireshark · Bộ lọc hiển thị tcp.analysis.spurious_retransmission #### rt-reorder · Truyền lại nhanh không cần thiết do gói đến sai thứ tự · Reordering triggers spurious fast retransmit Khi đi qua nhiều tuyến hoặc các link được gộp, thứ tự gói tin có thể bị đảo; bên nhận dùng ACK trùng (duplicate ACK) để báo “có gói bị thiếu”, và bên gửi gửi lại cả gói vẫn còn nguyên vẹn. - Vì sao → Dẫn đến → Trên màn hình: Thiết bị chia tuyến theo từng gói, LAG (gộp link) chia tải theo từng gói, hoặc khoảnh khắc đổi tuyến làm xáo trộn thứ tự → Gói phía sau đến trước nên dồn đủ 3 ACK trùng → truyền lại nhanh → Gói game đi lại thưa thớt nên gần như không bị ảnh hưởng. Bản cập nhật trạng thái lớn ở chỗ đông người và việc tải bản cập nhật (patch) bị chậm lại, thỉnh thoảng giật khựng - Triệu chứng: Giật khựng, Trễ thao tác / Yếu tố: Jitter - Ai gặp: Một khu vực hoặc nhà mạng, Cả server / Khi nào: Luôn luôn, Khi đông người - Phụ trách chính: Hạ tầng mạng (Đội hạ tầng) / Phối hợp: Hạ tầng server (Đội hạ tầng) - Việc cần làm (Đội hạ tầng): Mạng: chia tải theo kết nối thay vì theo gói (ECMP và LAG dùng hash địa chỉ và cổng). Thiết bị server·OS: dùng RACK (xác định mất gói dựa trên thời gian, chịu được gói sai thứ tự; khi phát hiện truyền lại không cần thiết qua DSACK sẽ tự nới rộng mức chấp nhận sai thứ tự), kiểm tra mức sai thứ tự mà Linux tự ước tính cho từng kết nối (giá trị reordering trong ss -ti, giá trị khởi đầu tcp_reordering=3). - Trên đồ thị: Luôn cao ngay từ đầu (Số lần phát hiện sai thứ tự, số DSACK nhận được) - Chỗ cần xem: TcpExtTCPSACKReorder và TcpExtTCPTSReorder (số lần phát hiện sai thứ tự) cùng TcpExtTCPDSACKRecv trong nstat; theo từng kết nối thì xem reordering (chỉ hiển thị khi khác 3) và reord_seen trong ss -ti. Với bản bắt gói, dùng filter Wireshark tcp.analysis.out_of_order - Đúng nếu: counter sai thứ tự và DSACK tăng đều bất kể khung giờ, giá trị reordering của các kết nối đi qua một tuyến hoặc thiết bị nhất định lớn hơn 3. Trong bản bắt gói phía nhận, gói sau đến trước và gói trước cũng đến ngay sau đó - Loại trừ nếu: counter sai thứ tự không đổi mà TcpExtTCPLostRetransmit tăng → mất gói thật. DSACK chỉ tăng vào lúc RTT vọt lên → “Truyền lại không cần thiết do độ trễ tăng vọt” - Cách kiểm tra: Kiểm tra bằng công cụ hạ tầng (không cần code game) - Nguồn: - [RFC 2991: Multipath Issues in Unicast and Multicast Next-Hop Selection](https://www.rfc-editor.org/rfc/rfc2991) · IETF · Chia tuyến theo từng gói làm đảo thứ tự, và nếu từ 3 gói phía sau trở lên đến trước thì TCP truyền lại nhanh một cách không cần thiết - [RFC 2992: Analysis of an Equal-Cost Multi-Path Algorithm](https://www.rfc-editor.org/rfc/rfc2992) · IETF · ECMP chọn tuyến bằng hash của các trường header dùng để phân biệt luồng (chia tải theo luồng) - [RFC 5681: TCP Congestion Control](https://www.rfc-editor.org/rfc/rfc5681) · IETF · Truyền lại nhanh ở ACK trùng thứ ba - [RFC 8985: The RACK-TLP Loss Detection Algorithm for TCP](https://www.rfc-editor.org/rfc/rfc8985) · IETF · RACK xác định mất gói dựa trên thời gian nên chịu được gói sai thứ tự, và khi nhận DSACK thì nới rộng thời gian chấp nhận sai thứ tự (reo_wnd) - [IP Sysctl](https://docs.kernel.org/networking/ip-sysctl.html) · Linux kernel · tcp_reordering khởi đầu là 3 (mỗi kết nối tự điều chỉnh, tối đa đến tcp_max_reordering), cấu hình RACK trong tcp_recovery - [misc/ss.c](https://git.kernel.org/pub/scm/network/iproute2/iproute2.git/tree/misc/ss.c?h=v6.12.0) · iproute2 · ss -ti hiển thị reordering:giá trị khi giá trị reordering của kết nối khác mặc định 3, và reord_seen:số lần nếu kết nối từng gặp gói sai thứ tự - [SNMP counter](https://docs.kernel.org/networking/snmp_counter.html) · Linux kernel · TcpExtTCPSACKReorder, TcpExtTCPTSReorder (phát hiện sai thứ tự), TcpExtTCPDSACKRecv (số DSACK đã nhận), TcpExtTCPLostRetransmit (gói đã gửi lại lại bị mất) - [include/uapi/linux/tcp.h](https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/include/uapi/linux/tcp.h?h=v6.12) · Linux kernel · tcpi_reord_seen trong tcp_info: số lần kết nối gặp gói sai thứ tự - [Display Filter Reference: Transmission Control Protocol](https://www.wireshark.org/docs/dfref/t/tcp.html) · Wireshark · Bộ lọc hiển thị tcp.analysis.out_of_order #### rt-ack-path · ACK đến muộn hoặc bị mất (chiều tải lên bị bão hòa) · ACK path congestion on asymmetric links Dữ liệu đã đến nơi, nhưng nếu ACK báo “đã nhận” bị trễ hoặc bị mất trong hàng đợi upload đang đầy, bên gửi sẽ xác định là mất gói rồi truyền lại. - Vì sao → Dẫn đến → Trên màn hình: Ở nhà có người upload video hoặc sao lưu lên cloud làm chiều tải lên bị đầy → ACK bị trễ vài trăm ms trong hàng đợi của router, hoặc bị bỏ vì hàng đợi tràn → Gói server game gửi xuống nhìn chung vẫn đến đúng lúc. Input của bạn nằm chung hàng đợi upload nên bị chậm, gây trễ thao tác và kéo ngược, thỉnh thoảng có truyền lại không cần thiết - Triệu chứng: Trễ thao tác, Kéo ngược / Yếu tố: Độ trễ, Mất gói - Ai gặp: Cùng một nhà / Khi nào: Thỉnh thoảng bất chợt, Giờ cao điểm buổi tối - Phụ trách chính: Bên ngoài (Bên ngoài) / Phối hợp: Phát triển client (Đội phát triển game) - Việc cần làm (Đội phát triển game): Khi ping tăng vọt thì hiển thị trạng thái mạng trên màn hình, kèm hướng dẫn “Kiểm tra chương trình đang upload”. - Việc cần làm (Bên ngoài): Hướng dẫn người chơi dùng SQM trên router để giữ hàng đợi upload ngắn, ưu tiên gói nhỏ (ACK), giới hạn tốc độ upload (upload video, sao lưu lên cloud). - Con số tham khảo: ACK phía sau xác nhận thay cho ACK phía trước, nên mất vài ACK thường không sao. Vấn đề nằm ở việc ACK bị trễ trong hàng đợi. - Trên đồ thị: Chỉ một phần cao (RTT (ping) theo từng kết nối) - Chỗ cần xem: Từ PC của người chơi, so sánh ping tới server game khi đang bật và khi tắt việc upload (upload video, sao lưu lên cloud). Trên server, xem rtt của kết nối người chơi đó bằng ss -ti - Đúng nếu: chỉ khi đang upload thì ping mới lên vài trăm ms và xuất hiện trễ thao tác, kéo ngược; dừng upload thì trở lại bình thường ngay. Nhìn từ server, lúc đó rtt của kết nối này cũng tăng theo - Loại trừ nếu: mất gói và độ trễ xảy ra bất kể có upload hay không → “Mất gói ở chặng không dây” hoặc nguyên nhân phía tuyến đường. Chỉ chiều từ server tới người chơi bị chậm và không liên quan upload → “Tràn hàng đợi ở điểm nghẽn” - Cách kiểm tra: Kiểm tra ở môi trường phía người chơi - Nguồn: - [RFC 3449: TCP Performance Implications of Network Path Asymmetry](https://www.rfc-editor.org/rfc/rfc3449) · IETF · Trên đường truyền bất đối xứng có chiều tải lên hẹp, ACK bị trễ hoặc bị mất làm giảm hiệu năng TCP; ACK là xác nhận tích lũy nên mất một phần thì ACK sau vẫn thay thế được; các biện pháp như lập lịch ưu tiên ACK - [Smart Queue Management](https://www.bufferbloat.net/projects/cerowrt/wiki/Smart_Queue_Management/) · Bufferbloat.net · Giữ hàng đợi ngắn trên router bằng quản lý hàng đợi và shaping - [tc-cake(8) — Linux manual page](https://man7.org/linux/man-pages/man8/tc-cake.8.html) · iproute2 · CAKE tách riêng từng luồng và giảm tối đa độ trễ của các luồng gửi thưa thớt (sparse flow) - [ss(8) — Linux manual page](https://man7.org/linux/man-pages/man8/ss.8.html) · iproute2 · rtt (thời gian khứ hồi trung bình) và rttvar (độ lệch) trong ss -i #### rt-rto-setting · Cấu hình RTO không hợp với môi trường · RTO min too low or too high Hạ RTO tối thiểu quá thấp thì chỉ trễ một chút cũng sinh ra truyền lại không cần thiết, còn giá trị mặc định (200 ms) lại quá dài với game nên mỗi lần mất gói là đứng lâu. - Vì sao → Dẫn đến → Trên màn hình: Hạ mạnh RTO tối thiểu theo kiểu dành cho trung tâm dữ liệu, hoặc để nguyên giá trị mặc định cho chặng Internet → Thấp thì chỉ một thoáng trễ cũng làm truyền lại ồ ạt, cao thì mỗi lần mất gói phải chờ lâu → Với giá trị mặc định, mỗi lần mất gói là đứng hình vài trăm ms rồi tua nhanh; hạ quá thấp thì đứng hình ít đi nhưng truyền lại không cần thiết tăng vọt, lãng phí đường truyền - Triệu chứng: Đứng hình, Tua nhanh, Trễ thao tác / Yếu tố: Độ trễ - Ai gặp: Cả server / Khi nào: Luôn luôn - Phụ trách chính: Hạ tầng server (Đội hạ tầng) / Phối hợp: Phát triển server (Đội phát triển game) - Việc cần làm (Đội phát triển game): Với Linux 6.15 trở lên, cân nhắc hạ trần RTO cho kết nối game bằng TCP_RTO_MAX_MS (thời gian đến lúc bỏ cuộc cũng ngắn lại nên đặt kèm thời gian kết luận mất kết nối bằng TCP_USER_TIMEOUT); chỉ với kết nối nội bộ giữa các server mới hạ RTO tối thiểu bằng tùy chọn socket TCP_RTO_MIN_US (6.15 trở lên); cân nhắc dùng tùy chọn socket TCP_THIN_LINEAR_TIMEOUTS để riêng kết nối game không bị tăng gấp đôi khi RTO liên tiếp. - Việc cần làm (Đội hạ tầng): Chỉ hạ rto_min theo tuyến cho kết nối nội bộ giữa các server; chặng Internet giữ giá trị mặc định nhưng bù lại bằng RACK-TLP và cấu hình thin stream (tcp_thin_linear_timeouts). - Con số tham khảo: RTO của Linux = thời gian khứ hồi + max(200 ms, độ lệch RTT×4). Mỗi lần thất bại tăng gấp đôi, tối đa 120 giây. Từ Linux 6.15 có thể dùng TCP_RTO_MAX_MS để hạ mức trần này xuống tới 1 giây. - Trên đồ thị: Luôn cao ngay từ đầu (RTO theo từng kết nối, số RTO không cần thiết) - Chỗ cần xem: Cấu hình RTO tối thiểu của server (rto_min trong ip route show, từ Linux 6.11 là sysctl net.ipv4.tcp_rto_min_us), rto và rtt trong ss -ti, phần tăng của TcpExtTCPSpuriousRTOs trong nstat - Đúng nếu: trên server đã hạ giá trị tối thiểu, rto của các kết nối Internet sát ngay rtt và TcpExtTCPSpuriousRTOs tăng nhiều. Nếu để nguyên mặc định thì rto của kết nối game lớn hơn rtt từ 200 ms trở lên, và mỗi lần mất gói là đứng hình đúng chừng ấy thời gian - Loại trừ nếu: rto đúng theo cách tính mặc định (khoảng rtt + 200 ms), RTO không cần thiết cũng ít mà thời gian đứng hình vẫn dài bất thường → do mất gói liên tiếp hoặc do cách phục hồi (“Thin stream phục hồi chậm”, “Thiết bị trung gian xóa tùy chọn TCP”) - Cách kiểm tra: Kiểm tra bằng công cụ hạ tầng (không cần code game) - Nguồn: - [RFC 6298: Computing TCP's Retransmission Timer](https://www.rfc-editor.org/rfc/rfc6298) · IETF · RTO = SRTT + max(G, 4·RTTVAR), khuyến nghị tối thiểu 1 giây, mỗi lần thất bại tăng gấp đôi, nếu đặt giá trị tối đa thì phải từ 60 giây trở lên - [include/net/tcp.h](https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/include/net/tcp.h?h=v6.12) · Linux kernel · TCP_RTO_MIN của Linux là 200 ms, TCP_RTO_MAX là 120 giây - [net/ipv4/tcp_input.c](https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/net/ipv4/tcp_input.c?h=v6.12) · Linux kernel · RTO của Linux là SRTT + rttvar, và rttvar không xuống dưới RTO tối thiểu (mặc định 200 ms) - [tcp: add the ability to control max RTO](https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=54a378f43425085d0684679d99735696b69165bc) · Linux kernel · Thêm tùy chọn socket TCP_RTO_MAX_MS (1–120 giây), từ Linux 6.15 - [tcp: add sysctl_tcp_rto_min_us](https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=f086edef71be7174a16c1ed67ac65a085cda28b1) · Linux kernel · Thêm tcp_rto_min_us, RTO tối thiểu mặc định cho toàn server, từ Linux 6.11 - [tcp: support TCP_RTO_MIN_US for set/getsockopt use](https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=f38805c5d26fe4af97837c10d58074a7496638bf) · Linux kernel · Thêm tùy chọn socket TCP_RTO_MIN_US để đặt RTO tối thiểu cho từng socket, từ Linux 6.15 - [IP Sysctl](https://docs.kernel.org/networking/ip-sysctl.html) · Linux kernel · tcp_rto_min_us mặc định 200000 (tùy chọn tuyến rto_min và tùy chọn socket TCP_RTO_MIN_US được ưu tiên hơn), tcp_rto_max_ms 1.000–120.000 (mặc định 120.000), tcp_thin_linear_timeouts - [ip-route(8) — Linux manual page](https://man7.org/linux/man-pages/man8/ip-route.8.html) · iproute2 · Tùy chọn rto_min theo tuyến: RTO tối thiểu khi giao tiếp với đích đó - [Thin-streams and TCP](https://docs.kernel.org/networking/tcp-thin.html) · Linux kernel · TCP_THIN_LINEAR_TIMEOUTS có thể tắt backoff theo cấp số nhân (exponential backoff) chỉ cho kết nối thin stream - [tcp(7) — Linux manual page](https://man7.org/linux/man-pages/man7/tcp.7.html) · Linux man-pages · TCP_USER_TIMEOUT: thời gian chờ dữ liệu chưa được xác nhận trước khi đóng kết nối - [ss(8) — Linux manual page](https://man7.org/linux/man-pages/man8/ss.8.html) · iproute2 · rto (ms) và rtt trong ss -i - [SNMP counter](https://docs.kernel.org/networking/snmp_counter.html) · Linux kernel · TcpExtTCPSpuriousRTOs: RTO không cần thiết do F-RTO phát hiện #### rt-thin · Thin stream phục hồi chậm · Thin streams fall back to RTO Khi gửi gói nhỏ thưa thớt như game, RTO đến trước khi gom đủ “3 gói theo sau”. Với cùng mức mất gói, game đứng lâu hơn nhiều so với khi truyền dữ liệu dung lượng lớn. - Vì sao → Dẫn đến → Trên màn hình: Khoảng cách giữa các gói khoảng 100 ms nên chỉ có vài gói chưa nhận được ACK (in-flight) → Để gom đủ 3 ACK trùng phải mất hơn 300 ms nên RTO (ping + 200 ms) kích hoạt trước; mất liên tiếp thì mỗi lần tăng gấp đôi → Mỗi lần mất gói đứng hình khoảng 0,3 giây; nếu mất cả gói đã gửi lại thì đứng gần 1 giây rồi tua nhanh - Triệu chứng: Đứng hình, Tua nhanh / Yếu tố: Mất gói, Ngưng trệ - Ai gặp: Chỉ mình tôi, Cả server / Khi nào: Thỉnh thoảng bất chợt - Phụ trách chính: Phát triển server (Đội phát triển game) / Phối hợp: Hạ tầng server (Đội hạ tầng), Phát triển client (Đội phát triển game) - Việc cần làm (Đội phát triển game): Server: bật TCP_NODELAY (nếu Nagle còn bật thì RACK không có gói đi sau để dựa vào khi xác định mất gói), chuyển gói thời gian thực sang cơ chế truyền lại tự viết trên UDP. Client: bật TCP_NODELAY, gói thời gian thực dùng cùng cách với server (UDP). - Việc cần làm (Đội hạ tầng): Dùng RACK-TLP (mặc định trên Linux mới), dùng tcp_thin_linear_timeouts để RTO liên tiếp không tăng gấp đôi. - Con số tham khảo: Với khoảng cách gói 100 ms và ping 60 ms, truyền lại nhanh mất khoảng 360 ms (đến khi 3 gói sau tới nơi và xác nhận quay về), còn RTO khoảng 260 ms. Nếu dùng RACK thì gửi lại ngay khi xác nhận của gói kế tiếp quay về, khoảng 160 ms. Khi khoảng cách gói vượt 200 ms thì RACK cũng không nhanh hơn RTO. - Trên đồ thị: Đứt quãng rồi dồn về (Lượng dữ liệu nhận theo từng kết nối, số lần RTO hết hạn) - Chỗ cần xem: So sánh phần tăng của TcpExtTCPTimeouts (RTO hết hạn), TcpExtTCPFastRetrans (truyền lại nhanh), TcpExtTCPLossProbes và TcpExtTCPLossProbeRecovery (TLP) trong nstat; xem rto và backoff của kết nối game bằng ss -ti. Kiểm tra cả giá trị net.ipv4.tcp_recovery, tcp_early_retrans, tcp_sack của server - Đúng nếu: trong số lần truyền lại, RTO hết hạn nhiều hơn truyền lại nhanh, và thường thấy kết nối game có backoff lớn hơn 0 (đang chịu RTO). Lượng nhận bằng 0 trong lúc đứng, phục hồi xong thì dồn về một lượt - Loại trừ nếu: truyền dữ liệu dung lượng lớn trên cùng server cũng đứng lâu y như vậy → vấn đề mất gói không liên quan đến dạng kết nối. Tập trung ở kết nối thiếu SACK và timestamp → “Thiết bị trung gian xóa tùy chọn TCP” - Cách kiểm tra: Kiểm tra bằng công cụ hạ tầng (không cần code game) - Tìm hiểu thêm: Linux trước đây có tùy chọn dành cho thin stream để truyền lại ngay khi có 1 ACK trùng (tcp_thin_dupack), nhưng đã bị bỏ vào năm 2017, và hiện nay RACK đảm nhận vai trò đó. Nếu Nagle bật (TCP_NODELAY tắt), trong lúc chờ xác nhận cho gói bị mất, TCP cũng không gửi gói mới, nên RACK không có gói đi sau để dựa vào và phải chờ đến RTO. - Nguồn: - [Thin-streams and TCP](https://docs.kernel.org/networking/tcp-thin.html) · Linux kernel · Thin stream gửi thưa thớt như game không tận dụng được truyền lại nhanh nên phải trông vào timeout dài; tiêu chí là dưới 4 gói chưa nhận được ACK (in-flight) - [RFC 5681: TCP Congestion Control](https://www.rfc-editor.org/rfc/rfc5681) · IETF · Truyền lại nhanh ở ACK trùng thứ ba - [RFC 8985: The RACK-TLP Loss Detection Algorithm for TCP](https://www.rfc-editor.org/rfc/rfc8985) · IETF · RACK xác định mất gói dựa trên việc gói gửi sau đã tới nơi; thời gian chờ TLP là 2·SRTT (nếu chỉ còn một gói chưa được xác nhận thì cộng thêm khoảng dự phòng cho delayed ACK) - [include/net/tcp.h](https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/include/net/tcp.h?h=v6.12) · Linux kernel · TCP_RTO_MIN 200 ms, tiêu chí thin stream (dưới 4 gói in-flight) và 6 lần thử lại tuyến tính - [tcp: remove thin_dupack feature](https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=4a7f6009441144783e5925551c72e3f2e1b0839b) · Linux kernel · Giải thích việc thin_dupack bị xóa vào tháng 1 năm 2017 (Linux 4.11) và RACK đảm nhận vai trò đó - [IP Sysctl](https://docs.kernel.org/networking/ip-sysctl.html) · Linux kernel · tcp_thin_linear_timeouts: với thin stream, không tăng gấp đôi RTO trong tối đa 6 lần (mặc định tắt) - [tcp(7) — Linux manual page](https://man7.org/linux/man-pages/man7/tcp.7.html) · Linux man-pages · TCP_NODELAY tắt thuật toán Nagle - [net/ipv4/proc.c](https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/net/ipv4/proc.c?h=v6.12) · Linux kernel · Tên counter trong nstat: TCPTimeouts, TCPFastRetrans, TCPLossProbes, TCPLossProbeRecovery - [net/ipv4/tcp_timer.c](https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/net/ipv4/tcp_timer.c?h=v6.12) · Linux kernel · TCPTimeouts tăng mỗi khi timer truyền lại (RTO) hết hạn - [SNMP counter](https://docs.kernel.org/networking/snmp_counter.html) · Linux kernel · TcpExtTCPFastRetrans (truyền lại khi không ở trạng thái Loss), TcpExtTCPLossProbes (đã gửi TLP), TcpExtTCPLossProbeRecovery (phục hồi mất gói nhờ TLP) - [ss(8) — Linux manual page](https://man7.org/linux/man-pages/man8/ss.8.html) · iproute2 · rto (ms), backoff (số lần RTO đã tăng gấp đôi) trong ss -i #### rt-sack-stripped · Thiết bị trung gian xóa tùy chọn TCP · Middlebox strips TCP options Nếu một số tường lửa hoặc thiết bị tăng tốc xóa hay sửa tùy chọn TCP, khi mất nhiều gói thì mỗi vòng khứ hồi chỉ phục hồi được một gói, hoặc cửa sổ (lượng dữ liệu gửi được trong một lần) bị thu nhỏ, khiến kết nối chậm đi. - Vì sao → Dẫn đến → Trên màn hình: “Chuẩn hóa TCP” (TCP normalization) của tường lửa hoặc thiết bị tăng tốc đời cũ xóa các tùy chọn SACK, timestamp, window scale → Mất nhiều gói thì mỗi vòng khứ hồi chỉ phục hồi một gói, cửa sổ bị giới hạn ở 64 KB → Mỗi lần mất gói, thời gian đứng hình dài hơn nhiều (không có SACK thì cũng không dùng được RACK-TLP), thông rồi thì tua nhanh. Truyền dữ liệu dung lượng lớn như tải bản cập nhật cũng chậm - Triệu chứng: Đứng hình, Tua nhanh / Yếu tố: Ngưng trệ, Độ trễ - Ai gặp: Một khu vực hoặc nhà mạng, Cả server / Khi nào: Luôn luôn - Phụ trách chính: Hạ tầng mạng (Đội hạ tầng) / Phối hợp: Hạ tầng server (Đội hạ tầng) - Việc cần làm (Đội hạ tầng): Mạng: tắt cấu hình chuẩn hóa TCP trên thiết bị đó, kiểm tra cả tính năng ngẫu nhiên hóa số thứ tự của tường lửa, bắt gói ở cả hai đầu để so sánh tùy chọn trong SYN. Thiết bị server·OS: kiểm tra xem các kết nối thiếu sack, wscale trong ss -ti có tập trung ở một tuyến nhất định không (PC Windows có thể không dùng ts tùy cấu hình, nên chỉ thiếu ts có thể vẫn là bình thường), kiểm tra net.ipv4.tcp_sack của server có bằng 1 không. - Trên đồ thị: Luôn cao ngay từ đầu (Số lần phục hồi bắt đầu mà không có SACK (TcpExtTCPRenoRecovery)) - Chỗ cần xem: Có hay không sack và wscale ở từng kết nối trong ss -ti; tỷ lệ giữa TcpExtTCPRenoRecovery (phục hồi bắt đầu không có SACK) và TcpExtTCPSackRecovery, cùng TcpExtTCPSACKDiscard (số khối SACK bị bỏ vì không khớp) trong nstat. Với tuyến bị nghi ngờ, bắt SYN ở cả hai đầu để so sánh tùy chọn (tcp.options.sack_perm… trong Wireshark) - Đúng nếu: chỉ những kết nối đi qua một tuyến hoặc thiết bị nhất định thiếu sack, wscale và tỷ trọng TcpExtTCPRenoRecovery cao. Tùy chọn cho phép SACK có trong SYN phía gửi nhưng không còn trong SYN phía nhận. Nếu nguyên nhân là ngẫu nhiên hóa số thứ tự thì tùy chọn vẫn còn nhưng TcpExtTCPSACKDiscard tăng - Loại trừ nếu: mọi kết nối đều thiếu sack → kiểm tra giá trị net.ipv4.tcp_sack của server trước. Tùy chọn còn nguyên và TcpExtTCPSACKDiscard không đổi → phục hồi chậm vì lý do khác (“Thin stream phục hồi chậm”) - Cách kiểm tra: Kiểm tra bằng công cụ hạ tầng (không cần code game) - Tìm hiểu thêm: Tùy chọn vẫn còn mà SACK vẫn có thể hỏng. Nếu tính năng ngẫu nhiên hóa số thứ tự (sequence randomization) của tường lửa chỉ đổi số thứ tự trong header mà giữ nguyên các số bên trong SACK, bên gửi sẽ bỏ các SACK không khớp. Trường hợp server đã tắt SACK bằng tcp_sack=0 hồi xảy ra lỗ hổng bảo mật SACK năm 2019 rồi quên bật lại cũng cho kết quả tương tự. - Nguồn: - [RFC 2018: TCP Selective Acknowledgment Options](https://www.rfc-editor.org/rfc/rfc2018) · IETF · Không có SACK, chỉ với ACK tích lũy thì mỗi vòng khứ hồi chỉ biết được một gói bị mất - [RFC 7323: TCP Extensions for High Performance](https://www.rfc-editor.org/rfc/rfc7323) · IETF · Không có tùy chọn window scale thì cửa sổ tối đa là 2^16 = 64 KiB - [RFC 8985: The RACK-TLP Loss Detection Algorithm for TCP](https://www.rfc-editor.org/rfc/rfc8985) · IETF · RACK-TLP bắt buộc phải dùng SACK - [net/ipv4/tcp_output.c](https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/net/ipv4/tcp_output.c?h=v6.12) · Linux kernel · TLP trên Linux chỉ được lên lịch trên kết nối có dùng SACK - [IP Sysctl](https://docs.kernel.org/networking/ip-sysctl.html) · Linux kernel · tcp_sack mặc định 1 (bật) - [misc/ss.c](https://git.kernel.org/pub/scm/network/iproute2/iproute2.git/tree/misc/ss.c?h=v6.12.0) · iproute2 · ss -ti hiển thị ts, sack, wscale:gửi,nhận tùy theo tùy chọn kết nối đang dùng - [tcp: limit payload size of sacked skbs](https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=3b4929f65b0d8249f19a50245cd88ed1a2f78cff) · Linux kernel · Commit sửa lỗ hổng xử lý SACK năm 2019 (CVE-2019-11477) - [Linux and FreeBSD Kernel: Multiple TCP-based remote denial of service vulnerabilities (NFLX-2019-001)](https://raw.githubusercontent.com/Netflix/security-bulletins/master/advisories/third-party/2019-001.md) · Netflix · Hồi đó tcp_sack=0 (tắt xử lý SACK) được hướng dẫn như biện pháp tạm thời - [SNMP counter](https://docs.kernel.org/networking/snmp_counter.html) · Linux kernel · TcpExtTCPRenoRecovery (bắt đầu phục hồi không có SACK), TcpExtTCPSackRecovery (bắt đầu phục hồi bằng SACK), TcpExtTCPSACKDiscard (số khối SACK không hợp lệ) - [Display Filter Reference: Transmission Control Protocol](https://www.wireshark.org/docs/dfref/t/tcp.html) · Wireshark · Bộ lọc hiển thị tcp.options.sack_perm (tùy chọn cho phép SACK trong SYN) #### rt-zero-window · Zero window (khoảng dừng dễ nhầm là truyền lại) · Zero window, often mistaken for retransmission Nếu chương trình bên nhận không đọc socket kịp khiến bộ đệm đầy, bên gửi sẽ ngừng gửi và chỉ gửi zero window probe. Đường truyền không có vấn đề gì. - Vì sao → Dẫn đến → Trên màn hình: Client bị đứng khung hình, thread server bị chặn nên không đọc được socket → Cửa sổ nhận về 0 nên bên gửi ngừng gửi và chỉ gửi probe (khoảng cách thưa dần) → Đứng hình rồi tua nhanh. Bản bắt gói có “ZeroWindow” và không có mất gói - Triệu chứng: Đứng hình, Tua nhanh / Yếu tố: Ngưng trệ - Ai gặp: Chỉ mình tôi, Cả server / Khi nào: Khi đông người, Thỉnh thoảng bất chợt - Phụ trách chính: Phát triển client (Đội phát triển game) / Phối hợp: Phát triển server (Đội phát triển game), Hạ tầng server (Đội hạ tầng) - Việc cần làm (Đội phát triển game): Trong bản bắt gói, xem bên gửi ZeroWindow (bên không đọc được socket) trước; nhận dữ liệu mạng trên một thread riêng và đọc liên tục; đặt bộ đệm nhận kích thước phù hợp. Client: xử lý nguyên nhân gây đứng khung hình như loading, GC. Server: xử lý nguyên nhân làm thread đọc socket bị chặn. - Việc cần làm (Đội hạ tầng): Thêm TcpExtTCPToZeroWindowAdv trong nstat của server (số lần server báo cửa sổ nhận bằng 0) vào giám sát (nếu tăng thì lỗi ở phía server, chuyển cho bên phát triển server), cung cấp bản bắt gói phía server. - Trên đồ thị: Đứt quãng rồi dồn về (Lượng dữ liệu nhận theo từng kết nối, số lần zero window) - Chỗ cần xem: Trong bản bắt gói, tìm bên báo cửa sổ bằng 0 bằng filter Wireshark tcp.analysis.zero_window. Trong nstat của server, xem riêng TcpExtTCPToZeroWindowAdv (server báo cửa sổ 0) và TcpExtTCPWinProbe (server gửi probe khi phía bên kia báo cửa sổ 0); xem Recv-Q của socket server (số byte chương trình chưa đọc, trong ss) - Đúng nếu: trong khoảng dừng không có truyền lại, chỉ có zero window và probe đi lại. TcpExtTCPToZeroWindowAdv của server hoặc Recv-Q của socket server tăng → server không đọc kịp; TcpExtTCPWinProbe tăng → client không đọc kịp - Loại trừ nếu: bản bắt gói không có zero window và cùng dữ liệu được gửi lại → nguyên nhân thuộc về mất gói hoặc truyền lại không cần thiết - Cách kiểm tra: Kiểm tra bằng công cụ hạ tầng (không cần code game) - Sự cố thực tế: roblox-2021 - Nguồn: - [RFC 9293: Transmission Control Protocol (TCP)](https://www.rfc-editor.org/rfc/rfc9293) · IETF · Khi cửa sổ nhận bằng 0, bên gửi gửi zero window probe, và khoảng cách giữa các probe tăng theo cấp số nhân - [7.5. TCP Analysis](https://www.wireshark.org/docs/wsug_html_chunked/ChAdvTCPAnalysis.html) · Wireshark · TCP ZeroWindow: gói tin bên nhận báo cửa sổ bằng 0 để bảo bên gửi ngừng gửi - [Display Filter Reference: Transmission Control Protocol](https://www.wireshark.org/docs/dfref/t/tcp.html) · Wireshark · Bộ lọc hiển thị tcp.analysis.zero_window, tcp.analysis.zero_window_probe - [SNMP counter](https://docs.kernel.org/networking/snmp_counter.html) · Linux kernel · TcpExtTCPToZeroWindowAdv: số lần báo cửa sổ nhận chuyển từ giá trị khác 0 về 0 - [net/ipv4/proc.c](https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/net/ipv4/proc.c?h=v6.12) · Linux kernel · Tên counter trong nstat: TCPToZeroWindowAdv, TCPWinProbe - [net/ipv4/tcp_output.c](https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/net/ipv4/tcp_output.c?h=v6.12) · Linux kernel · TCPWinProbe: tăng mỗi lần gửi probe (tcp_send_probe0) khi cửa sổ nhận của phía bên kia bằng 0 - [net/ipv4/tcp_diag.c](https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/net/ipv4/tcp_diag.c?h=v6.12) · Linux kernel · Với một kết nối, Recv-Q trong ss là số byte đã nhận nhưng chương trình chưa đọc #### rt-syn · Truyền lại yêu cầu kết nối (SYN) · SYN retransmission on connect Khi yêu cầu kết nối biến mất do hàng đợi kết nối (backlog) bị tràn hoặc bị tường lửa chặn, OS phía client sẽ gửi lại, bắt đầu sau 1 giây và giãn dần khoảng cách. - Vì sao → Dẫn đến → Trên màn hình: Ngay sau bảo trì, lượng kết nối dồn vào làm tràn hàng đợi kết nối của server, hoặc tường lửa hay hệ thống chống DDoS bỏ SYN → OS phía client truyền lại SYN theo khoảng cách định sẵn, bắt đầu sau 1 giây (Linux đời cũ là 1 giây → 2 giây → 4 giây) → Sau khi bấm nút kết nối, thời gian trễ tròn đúng từng giây như 1 giây, 3 giây; thất bại liên tục thì không vào được game hoặc kẹt loading - Triệu chứng: Không vào được·kẹt loading / Yếu tố: Mất gói - Ai gặp: Cả server, Một khu vực hoặc nhà mạng / Khi nào: Ngay sau đăng nhập hoặc bảo trì - Phụ trách chính: Phát triển server (Đội phát triển game) / Phối hợp: Hạ tầng server (Đội hạ tầng), Hạ tầng mạng (Đội hạ tầng), Phát triển client (Đội phát triển game) - Việc cần làm (Đội phát triển game): Server: tăng tham số backlog của listen (cùng với somaxconn), bảo đảm server game gọi accept kịp thời, dùng hệ thống hàng chờ đăng nhập. Client: giãn khoảng cách thử kết nối lại (rải ngẫu nhiên). - Việc cần làm (Đội hạ tầng): Thiết bị server·OS: kiểm tra tràn hàng đợi kết nối của server qua TcpExtListenOverflows, TcpExtListenDrops trong nstat và cảnh báo “Possible SYN flooding” trong log, tăng somaxconn (cùng với tham số của listen), dùng SYN cookie. Mạng: nới giới hạn SYN của tường lửa và hệ thống chống DDoS. - Con số tham khảo: Trên Linux (kể cả Android), SYN được truyền lại lần đầu sau 1 giây. Kernel đời cũ sau đó tăng gấp đôi khoảng chờ mỗi lần và gửi lại ở giây thứ 1, 3, 7, 15 …, còn từ 6.5 trở lên thì gửi lại 5 lần ở giây thứ 1, 2, 3, 4, 5 rồi mới tăng gấp đôi (7, 11, 19 giây …) (tcp_syn_linear_timeouts=4). Điện thoại Android thường vẫn dùng kernel lúc xuất xưởng dù đã cập nhật OS, nên cùng phiên bản Android mà mỗi máy có thể khác nhau. Dù theo cách nào, thất bại hết thì khoảng 2 phút sau sẽ bỏ cuộc. Windows bắt đầu giãn từ 1 giây hoặc 3 giây tùy phiên bản và cấu hình, số lần gửi lại là 2–4 nên bỏ cuộc sau 20–30 giây (giá trị trên PC đó xem ở Max SYN Retransmissions trong netsh int tcp show global). - Trên đồ thị: Tăng vọt ngay sau đăng nhập hoặc bảo trì (Số lần thử kết nối, số lần tràn hàng đợi kết nối) - Chỗ cần xem: TcpExtListenOverflows và TcpExtListenDrops trong nstat của server, cảnh báo “Possible SYN flooding on port” trong dmesg; dùng ss -lnt xem Recv-Q (số kết nối đang chờ accept) của socket đang lắng nghe có chạm Send-Q (giới hạn backlog) không. Dùng bản bắt gói phía server để xác nhận SYN có đến không và server có trả SYN-ACK không - Đúng nếu: khi lượng kết nối dồn vào ngay sau bảo trì, TcpExtListenOverflows tăng và Recv-Q dính sát Send-Q. Trong bản bắt gói, SYN của cùng một client đến lại cách nhau từng giây mà server không phản hồi - Loại trừ nếu: SYN không tới được server và counter của server cũng không đổi → tường lửa hoặc hệ thống chống DDoS phía trước đã bỏ gói, xem giới hạn SYN và log drop của thiết bị đó. Server đã gửi SYN-ACK mà kết nối vẫn chậm → mất gói ở chiều về - Cách kiểm tra: Kiểm tra bằng công cụ hạ tầng (không cần code game) - Nguồn: - [include/net/tcp.h](https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/include/net/tcp.h?h=v6.12) · Linux kernel · RTO đầu tiên TCP_TIMEOUT_INIT = 1 giây (giá trị khởi đầu của RFC 6298) - [RFC 6298: Computing TCP's Retransmission Timer](https://www.rfc-editor.org/rfc/rfc6298) · IETF · RTO khởi đầu 1 giây, tăng gấp đôi mỗi lần truyền lại - [IP Sysctl](https://docs.kernel.org/networking/ip-sysctl.html) · Linux kernel · tcp_syn_retries mặc định 6, tcp_syn_linear_timeouts mặc định 4 (SYN RTO 1, 1, 1, 1, 1, 2, 4 …), lần truyền lại cuối ở giây thứ 67 và bỏ cuộc ở giây thứ 131, somaxconn mặc định 4096, tcp_syncookies mặc định 1 - [tcp: make the first N SYN RTO backoffs linear](https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=ccce324dabfe2143519daf50ed8b1ef1d0c542f7) · Linux kernel · Commit đổi phần đầu của chuỗi truyền lại SYN sang khoảng cách cố định, từ Linux 6.5 (mặc định 4 theo cách của macOS và iOS) - [Android common kernels](https://source.android.com/docs/core/architecture/kernel/android-common) · Android (Google) · Các kernel chung 5.10–6.18 được hỗ trợ song song, và kernel dành cho nền tảng trước (ví dụ android14-6.1) có thể dùng khi ra mắt hoặc nâng cấp thiết bị Android mới - [TcpMaxConnectRetransmissions](https://learn.microsoft.com/en-us/previous-versions/windows/it-pro/windows-2000-server/cc938209(v=technet.10)) · Microsoft · Mặc định của Windows đời cũ: truyền lại SYN 2 lần, chờ lần đầu 3 giây rồi tăng gấp đôi, sau lần cuối chờ thêm gấp đôi rồi bỏ cuộc (3+6+12=21 giây) - [TCP/IP connectivity issues troubleshooting](https://learn.microsoft.com/en-us/troubleshoot/windows-client/networking/tcp-ip-connectivity-issues-troubleshooting) · Microsoft · Số lần truyền lại SYN khác nhau tùy OS, xem bằng Max SYN Retransmissions trong netsh int tcp show global - [listen(2) — Linux manual page](https://man7.org/linux/man-pages/man2/listen.2.html) · Linux man-pages · Tham số backlog của listen bị cắt theo somaxconn (từ Linux 5.4 mặc định 4096, trước đó 128) - [SNMP counter](https://docs.kernel.org/networking/snmp_counter.html) · Linux kernel · Khi hàng đợi accept đầy thì SYN bị bỏ và TcpExtListenOverflows, TcpExtListenDrops cùng tăng; TcpExtTCPSynRetrans - [net/ipv4/tcp_input.c](https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/net/ipv4/tcp_input.c?h=v6.12) · Linux kernel · Thông điệp log “Possible SYN flooding on port …” - [net/ipv4/tcp_diag.c](https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/net/ipv4/tcp_diag.c?h=v6.12) · Linux kernel · Với socket đang lắng nghe, Recv-Q trong ss là số kết nối đang chờ accept, Send-Q là giới hạn backlog ## Nguồn theo chương ### #l-client-game - [Slow rendering](https://developer.android.com/topic/performance/vitals/render) · Android (Google) · Để đạt 60 FPS, mỗi khung hình phải vẽ xong trong 16 ms; nếu trễ thì khung hình bị bỏ qua và trông như giật khựng - [Interpolation and extrapolation (Netcode for Entities 6.5)](https://docs.unity3d.com/Packages/com.unity.netcode@6.5/manual/interpolation.html) · Unity · Nội suy (vẽ nối giữa các snapshot đến cách quãng), ngoại suy (khi dữ liệu đến trễ thì tiếp tục theo cùng hướng và tốc độ) và giới hạn của ngoại suy - [Introduction to prediction (Netcode for Entities 6.5)](https://docs.unity3d.com/Packages/com.unity.netcode@6.5/manual/intro-to-prediction.html) · Unity · Dự đoán: client không chờ kết quả từ server mà di chuyển trước theo input của chính mình; nếu khác với server thì hiệu chỉnh - [Peeking into VALORANT's Netcode](https://technology.riotgames.com/news/peeking-valorants-netcode) · Riot Games · Dùng bộ đệm để dàn đều dữ liệu đến thất thường thì mượt, nhưng độ trễ tăng tương ứng; đoán sai thì nhân vật nhảy vị trí hoặc bị trượt đi - [Garbage collection modes](https://docs.unity3d.com/Manual/performance-incremental-garbage-collection.html) · Unity · GC spike trong thí nghiệm: tắt GC tăng dần (incremental GC) thì main thread dừng trong lúc quét toàn bộ heap, vượt giới hạn khung hình 16 ms - [Scripting.GarbageCollector.CollectIncremental](https://docs.unity3d.com/ScriptReference/Scripting.GarbageCollector.CollectIncremental.html) · Unity · Time slice 3 ms của GC tăng dần trong thí nghiệm: giá trị mặc định của incrementalTimeSliceNanoseconds là 3 ms - [Shader loading](https://docs.unity3d.com/Manual/shader-loading.html) · Unity · Loading khu vực mới trong thí nghiệm: lần đầu dùng một biến thể shader, game có thể đứng vì driver phải tạo bản dành cho GPU - [Frame Pacing library](https://developer.android.com/games/sdk/frame-pacing) · Android (Google) · Ranh giới V-Sync trong thí nghiệm: màn hình 60 Hz sẽ hiển thị lại khung hình trước nếu chưa có khung hình mới - [Set fixed timestep to optimize physics simulation frequency](https://docs.unity3d.com/Manual/physics-optimization-cpu-frequency.html) · Unity · Chạy bù bước cố định (fixed step) trong thí nghiệm: nếu một khung hình dài hơn khoảng cách bước, bước đó phải chạy nhiều lần trong một khung hình nên tải tăng - [Handling variation in time](https://docs.unity3d.com/Manual/time-handling-variations.html) · Unity · Giới hạn chạy bù trong thí nghiệm (thí nghiệm cho tối đa 5 lần mỗi khung hình): Unity giới hạn thời gian game của một khung hình tối đa 1/3 giây để chặn vòng luẩn quẩn chạy bù, và đồng hồ game bị chậm lại đúng bằng phần thời gian vượt ra ### #l-client-os - [Multitasking](https://learn.microsoft.com/en-us/windows/win32/procthread/multitasking) · Microsoft · Đa nhiệm ưu tiên (preemptive multitasking): mỗi thread được cấp một time slice (khoảng 20 ms, khác nhau tùy OS và CPU), dùng hết thì chuyển sang thread tiếp theo - [Scheduling Priorities](https://learn.microsoft.com/en-us/windows/win32/procthread/scheduling-priorities) · Microsoft · Trong các thread có thể chạy, các thread có mức ưu tiên cao nhất lần lượt nhận time slice (round robin) - [Priority Boosts](https://learn.microsoft.com/en-us/windows/win32/procthread/priority-boosts) · Microsoft · Nâng mức ưu tiên của tiến trình có cửa sổ đang ở phía trước (foreground) lên ít nhất bằng tiến trình chạy nền - [socket(7) — Linux manual page](https://man7.org/linux/man-pages/man7/socket.7.html) · Linux man-pages · Mỗi socket có bộ đệm nhận (SO_RCVBUF), kích thước mặc định và tối đa do cấu hình hệ thống quy định - [Wi-Fi low-latency mode](https://source.android.com/docs/core/connect/wifi-low-latency) · Android (Google) · Ở chế độ Wi-Fi độ trễ thấp trên Android 10 trở lên, framework chủ động tắt tiết kiệm điện Wi-Fi (doze) khi ứng dụng ở foreground và màn hình đang bật - [Cached apps freezer](https://source.android.com/docs/core/perf/cached-apps-freezer) · Android (Google) · Android 14 trở lên đóng băng ứng dụng ở trạng thái cache sau 10 giây, khiến ứng dụng không dùng được CPU - [Extending your app’s background execution time](https://developer.apple.com/documentation/uikit/extending-your-app-s-background-execution-time) · Apple · iOS tạm dừng ứng dụng vài giây sau khi ứng dụng chuyển sang chạy nền - [_WDF_TIMER_CONFIG (wdftimer.h)](https://learn.microsoft.com/en-us/windows-hardware/drivers/ddi/wdftimer/ns-wdftimer-_wdf_timer_config) · Microsoft · Timer 15,6 ms trong thí nghiệm: khoảng cách mặc định giữa các tick đồng hồ hệ thống của Windows là 15,6 ms - [timeBeginPeriod function (timeapi.h)](https://learn.microsoft.com/en-us/windows/win32/api/timeapi/nf-timeapi-timebeginperiod) · Microsoft · Timer 1 ms trong thí nghiệm: chương trình có thể yêu cầu độ phân giải timer cao hơn bằng timeBeginPeriod - [Customize the Windows performance power slider](https://learn.microsoft.com/en-us/windows-hardware/customize/desktop/customize-power-slider) · Microsoft · Chế độ tiết kiệm điện trong thí nghiệm: chế độ nguồn (power mode) của Windows đổi cấu hình nguồn và CPU theo hướng giảm hiệu năng để kéo dài thời lượng pin - [Thermal API](https://developer.android.com/games/optimize/adpf/thermal) · Android (Google) · Điện thoại nóng máy trong thí nghiệm: thiết bị chỉ giữ hiệu năng cao trong thời gian có hạn, sau đó bị bóp xung do nhiệt (thermal throttling) ### #l-memory - [Designs, Lessons and Advice from Building Large Distributed Systems (LADIS 2009 keynote)](https://www.cs.cornell.edu/projects/ladis2009/talks/dean-keynote-ladis2009.pdf) · Google · Cơ sở của bảng “Cảm nhận con số”: cache L1 0,5 ns, L2 7 ns, bộ nhớ chính 100 ns, khứ hồi trong cùng trung tâm dữ liệu 0,5 ms, seek ổ đĩa 10 ms (số liệu năm 2009; số liệu cache trong bảng là giá trị ước lượng hơi khác) - [Solidigm™ D7-P5520 and D7-P5620 Product Brief](https://www.solidigm.com/products/data-center/product-briefs/d7-p5520-p5620-product-brief.html) · Solidigm · Độ trễ 99,99% (four-nines latency) 130 µs của SSD NVMe cho server: căn cứ cho việc đọc SSD và đọc lại từ swap trong bảng vào khoảng 100 µs - [IP Sysctl](https://docs.kernel.org/networking/ip-sysctl.html) · Linux kernel · tcp_rto_min_us mặc định 200.000 µs: thời gian chờ tối thiểu trước khi truyền lại của TCP trên Linux là 200 ms (dòng truyền lại TCP trong bảng) - [What is NUMA?](https://docs.kernel.org/mm/numa.html) · Linux kernel · Bộ nhớ thuộc CPU khác (remote) truy cập chậm hơn và băng thông thấp hơn bộ nhớ local (dòng NUMA trong bảng) - [JEP 439: Generational ZGC](https://openjdk.org/jeps/439) · OpenJDK · G1 dừng từ vài ms đến vài giây, ZGC dừng không quá 1 ms (GC toàn heap vài trăm ms đến vài giây trong phần nội dung, GC heap lớn 1 giây trong bảng) - [Available Collectors](https://docs.oracle.com/en/java/javase/25/gctuning/available-collectors.html) · Oracle · ZGC nhường một chút thông lượng để giữ thời gian dừng tối đa dưới 1 ms, thời gian dừng không phụ thuộc kích thước heap - [Garbage Collector Implementation](https://docs.oracle.com/en/java/javase/25/gctuning/garbage-collector-implementation.html) · Oracle · Thu gom theo thế hệ: lần thu gom minor chỉ quét vùng Young thì ngắn, lần thu gom major quét toàn bộ heap thì lâu hơn nhiều (chế độ theo thế hệ trong thí nghiệm GC) - [The Z Garbage Collector](https://docs.oracle.com/en/java/javase/25/gctuning/z-garbage-collector1.html) · Oracle · ZGC làm các việc tốn kém song song với ứng dụng nên không dừng quá 1 ms, nhưng nếu thu hồi không kịp thì ứng dụng có thể phải dừng chờ GC (chế độ chạy đồng thời trong thí nghiệm GC) - [A Guide to the Go Garbage Collector](https://go.dev/doc/gc-guide) · Go · Giai đoạn đánh dấu của GC dùng 25% CPU nên trong lúc đó chương trình chậm đi, và nếu cấp phát nhiều thì goroutine bị trễ vì phải giúp GC (assist) (chế độ chạy đồng thời trong thí nghiệm GC) - [Go 1.8 Release Notes](https://go.dev/doc/go1.8) · Go · Thời gian dừng của Go GC thường dưới 100 µs - [Debug a memory leak in .NET](https://learn.microsoft.com/en-us/dotnet/core/diagnostics/debug-memory-leak) · .NET · Dù có GC, nếu vẫn tiếp tục tham chiếu tới đối tượng không còn cần thì vẫn rò rỉ bộ nhớ - [Concepts overview](https://docs.kernel.org/admin-guide/mm/concepts.html) · Linux kernel · Khi thiếu bộ nhớ, kernel thu hồi page cache và các trang có thể swap; nếu vẫn không đủ thì OOM killer buộc dừng một tiến trình (thí nghiệm rò rỉ) ### #l-disk - [Exos X18 Data Sheet](https://www.seagate.com/www-content/datasheets/pdfs/exos-x18-mango-DS2045-1N-2007US-en_US.pdf) · Seagate · HDD 7.200 rpm đọc ngẫu nhiên 4K đạt 170 IOPS, độ trễ quay trung bình 4,16 ms (con số HDD hơn 150 lần trong phần nội dung, HDD trong thí nghiệm ổ đĩa) - [D3-S4520 SSD](https://www.solidigm.com/products/data-center/d3/s4520.html) · Solidigm · SSD SATA đọc và ghi ngẫu nhiên 4 KB tối đa 92K/48K IOPS (con số SSD vài chục nghìn lần trong phần nội dung) - [Solidigm™ D7-P5520 and D7-P5620 Product Brief](https://www.solidigm.com/products/data-center/product-briefs/d7-p5520-p5620-product-brief.html) · Solidigm · SSD NVMe đọc và ghi ngẫu nhiên 1.000K/200K IOPS (con số vài trăm nghìn lần trong phần nội dung) - [Amazon EBS General Purpose SSD volumes](https://docs.aws.amazon.com/ebs/latest/userguide/general-purpose.html) · AWS · gp3 mặc định 3.000 IOPS; gp2 burst tới 3.000 IOPS bằng I/O credit, hết credit thì về hiệu năng cơ sở - [Amazon EBS-optimized instance types](https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/ebs-optimized.html) · AWS · Instance nhỏ chỉ đạt hiệu năng EBS tối đa trong 30 phút, mỗi 24 giờ một lần, rồi quay về hiệu năng cơ sở (ví dụ t4g.2xlarge cơ sở 4.000, tối đa 15.700 IOPS; giả định cloud loại burstable trong thí nghiệm ổ đĩa) - [Managed disk bursting](https://learn.microsoft.com/en-us/azure/virtual-machines/disk-bursting) · Microsoft Azure · Ổ đĩa và VM nhỏ burst bằng credit tối đa 30 phút - [Concepts overview](https://docs.kernel.org/admin-guide/mm/concepts.html) · Linux kernel · Dữ liệu ghi vào file được đưa vào page cache trước, đánh dấu dirty, rồi sau đó mới ghi xuống ổ đĩa - [Documentation for /proc/sys/vm/](https://docs.kernel.org/admin-guide/sysctl/vm.html) · Linux kernel · Khi lượng ghi tồn đọng chạm dirty_ratio, chính tiến trình đang ghi phải tự đảm nhận việc ghi xuống ổ đĩa (giới hạn ghi của OS trong thí nghiệm ổ đĩa) - [fsync(2) — Linux manual page](https://man7.org/linux/man-pages/man2/fsync.2.html) · Linux man-pages · fsync chặn (block) cho đến khi thiết bị báo đã ghi xong ### #l-db - [How MySQL Uses Indexes](https://dev.mysql.com/doc/refman/8.4/en/mysql-indexes.html) · MySQL · Không có index thì đọc toàn bộ bảng từ dòng đầu tiên (full scan) - [InnoDB Locking](https://dev.mysql.com/doc/refman/8.4/en/innodb-locking.html) · MySQL · Cho đến khi transaction đang giữ khóa dòng kết thúc, các yêu cầu sửa cùng dòng đó phải chờ (hot row) - [Number Of Database Connections](https://wiki.postgresql.org/wiki/Number_Of_Database_Connections) · PostgreSQL · Khi tài nguyên DB đã dùng hết, tăng thêm kết nối còn làm thông lượng giảm (căn cứ cho việc trong thí nghiệm DB, tăng connection pool mà thiếu core CPU thì mọi thứ đều chậm) - [WAL Configuration (PostgreSQL Documentation)](https://www.postgresql.org/docs/current/wal-configuration.html) · PostgreSQL · Checkpoint là thao tác tốn kém, mặc định cứ 5 phút hoặc mỗi 1 GB WAL lại ghi dồn các dirty page; rải việc ghi ra để tránh I/O tăng đột biến - [Semisynchronous Replication](https://dev.mysql.com/doc/refman/8.4/en/replication-semisync.html) · MySQL · Với replication bất đồng bộ, nếu DB chính chết thì transaction đã commit có thể không có trên DB dự phòng (bị rollback sau failover) - [Log-Shipping Standby Servers (PostgreSQL Documentation)](https://www.postgresql.org/docs/current/warm-standby.html) · PostgreSQL · Streaming replication mặc định là bất đồng bộ nên có độ trễ giữa lúc commit và lúc bản sao nhận được (nội dung vừa ghi chưa thấy trên bản sao) - [Asynchronous Commit (PostgreSQL Documentation)](https://www.postgresql.org/docs/current/wal-async-commit.html) · PostgreSQL · Đánh đổi của việc gom dữ liệu ghi rồi ghi xuống muộn: nhanh hơn nhưng khi có sự cố thì mất các thay đổi gần nhất (cùng cấu trúc với server game lưu dữ liệu vài phút một lần) ### #judge - [Service Level Objectives (Site Reliability Engineering, ch. 4)](https://sre.google/sre-book/service-level-objectives/) · Google · Xem hình dạng và phần đuôi của phân bố độ trễ bằng phân vị (thứ 50, 95, 99) thay vì trung bình - [The Tail at Scale](https://research.google/pubs/the-tail-at-scale/) · Google · Những lần trễ dài thỉnh thoảng xảy ra (tail latency) ngày càng quyết định cảm nhận về toàn bộ dịch vụ khi quy mô lớn dần - [RFC 3550: RTP, A Transport Protocol for Real-Time Applications](https://www.rfc-editor.org/rfc/rfc3550) · IETF · Định nghĩa và cách tính jitter của khoảng cách giữa các lần gói đến (interarrival jitter) - [RFC 1812: Requirements for IP Version 4 Routers](https://www.rfc-editor.org/rfc/rfc1812) · IETF · Router phải có khả năng giới hạn tần suất phát sinh thông báo lỗi ICMP như Time Exceeded, và cũng có thể giới hạn Echo Reply (cần chú ý khi đọc kết quả mtr và ping) - [IP Sysctl](https://docs.kernel.org/networking/ip-sysctl.html) · Linux kernel · icmp_ratelimit, icmp_ratemask: Linux mặc định giới hạn tốc độ phản hồi ICMP như Time Exceeded, Destination Unreachable - [ss(8) — Linux manual page](https://man7.org/linux/man-pages/man8/ss.8.html) · iproute2 · Trường rtt (thời gian khứ hồi trung bình)/rttvar trong ss -ti - [pidstat(1) — Linux manual page](https://man7.org/linux/man-pages/man1/pidstat.1.html) · sysstat · pidstat -t: mức sử dụng CPU theo từng thread - [bcc tools: runqlat examples](https://raw.githubusercontent.com/iovisor/bcc/master/tools/runqlat_example.txt) · IO Visor · Đo phân bố độ trễ run queue (thời gian thread chờ CPU) - [RIPE Atlas documentation](https://atlas.ripe.net/docs/) · RIPE NCC · Công cụ giám sát giả lập (synthetic monitoring) công khai, chạy ping và traceroute từ các điểm đo khắp thế giới - [GeoLite2 Free Geolocation Data](https://dev.maxmind.com/geoip/geolite2-free-geolocation-data) · MaxMind · Cơ sở dữ liệu công khai gắn quốc gia và ASN cho địa chỉ IP - [View CloudWatch metrics for your instances](https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/viewing_metrics_with_cloudwatch.html) · AWS · Giám sát cơ bản có chu kỳ 5 phút, giám sát chi tiết 1 phút (khoảng gộp dữ liệu che mất các lần vọt lên ngắn) ### #l-nic - [NAPI](https://docs.kernel.org/networking/napi.html) · Linux kernel · Khi thiết bị báo có gói mới bằng ngắt, kernel lấy gói ra xử lý qua NAPI; việc gộp ngắt (interrupt coalescing) thường do thiết bị làm - [Scaling in the Linux Networking Stack](https://docs.kernel.org/networking/scaling.html) · Linux kernel · Cấu trúc RSS chia nhiều hàng đợi nhận cho nhiều core, mỗi hàng đợi một ngắt, chọn hàng đợi bằng hash - [Interface statistics](https://docs.kernel.org/networking/statistics.html) · Linux kernel · Gói thiết bị bỏ vì hết bộ đệm (rx_missed_errors) và thống kê theo driver trong ethtool -S - [ethtool(8) — Linux manual page](https://man7.org/linux/man-pages/man8/ethtool.8.html) · ethtool · Cấu hình và kiểm tra ring buffer (-G), gộp ngắt (-C), hash nhận (-N), thống kê (-S) - [How to receive a million packets per second](https://blog.cloudflare.com/how-to-receive-a-million-packets/) · Cloudflare · Kết quả đo: một hàng đợi với một core bị nghẽn ở khoảng 350.000–430.000 gói mỗi giây, phải tăng hàng đợi và core mới nhận được 1 triệu pps (mô phỏng giả định thông lượng mỗi core rộng rãi hơn, ở mức 700.000 pps) - [Monitor network performance for ENA settings on your EC2 instance](https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/monitoring-network-performance-ena.html) · AWS · Khi vượt giới hạn băng thông, PPS hoặc theo dõi kết nối của instance cloud, gói bị xếp hàng bên ngoài instance rồi bị bỏ; counter vượt giới hạn ### #l-server-os - [listen(2) — Linux manual page](https://man7.org/linux/man-pages/man2/listen.2.html) · Linux man-pages · Hàng đợi kết nối (backlog) và trần somaxconn (từ 5.4 mặc định 4.096, trước đó 128) - [SNMP counter](https://docs.kernel.org/networking/snmp_counter.html) · Linux kernel · Khi hàng đợi accept đầy, Linux bỏ yêu cầu kết nối (SYN) và tăng TcpExtListenOverflows - [listen function (winsock2.h)](https://learn.microsoft.com/en-us/windows/win32/api/winsock2/nf-winsock2-listen) · Microsoft · Trên Windows, khi hàng đợi đầy thì client nhận WSAECONNREFUSED - [getrlimit(2) — Linux manual page](https://man7.org/linux/man-pages/man2/getrlimit.2.html) · Linux man-pages · RLIMIT_NOFILE: giới hạn số fd một tiến trình được mở, vượt thì báo EMFILE - [systemd-system.conf(5) — Linux manual page](https://man7.org/linux/man-pages/man5/systemd-system.conf.5.html) · systemd · Giới hạn fd mặc định của service là 1024:524288 (lỗi cấu hình fd 1.024 trong mô phỏng) - [The /proc Filesystem](https://docs.kernel.org/filesystems/proc.html) · Linux kernel · OOM killer chọn tiến trình sẽ bị kill theo điểm (badness) tính từ tỷ lệ bộ nhớ sử dụng, điều chỉnh được bằng oom_score_adj - [Control Group v2](https://docs.kernel.org/admin-guide/cgroup-v2.html) · Linux kernel · Khi chạm memory.max mà không thu hồi được thì OOM killer chạy trong cgroup đó; giới hạn CPU bằng cpu.max - [proc_stat(5) — Linux manual page](https://man7.org/linux/man-pages/man5/proc_stat.5.html) · Linux man-pages · steal: thời gian CPU bị hệ điều hành khác chiếm mất trong môi trường ảo hóa - [Exponential Backoff And Jitter](https://aws.amazon.com/blogs/architecture/exponential-backoff-and-jitter/) · AWS · Chỉ dùng exponential backoff thì các lần thử lại vẫn dồn vào nhau, phải trộn thêm yếu tố ngẫu nhiên (jitter) mới giảm tranh chấp (cách thử lại trong mô phỏng) ### #l-socket - [RFC 9293: Transmission Control Protocol (TCP)](https://www.rfc-editor.org/rfc/rfc9293) · IETF · TCP là dòng byte (byte stream) tin cậy, giữ đúng thứ tự; định nghĩa Nagle và delayed ACK - [RFC 768: User Datagram Protocol](https://www.rfc-editor.org/rfc/rfc768) · IETF · UDP không bảo đảm gói đến nơi và không chống trùng lặp - [RFC 8085: UDP Usage Guidelines](https://www.rfc-editor.org/rfc/rfc8085) · IETF · Ứng dụng UDP cần độ tin cậy và thứ tự thì phải tự cài đặt - [tcp(7) — Linux manual page](https://man7.org/linux/man-pages/man7/tcp.7.html) · Linux man-pages · Các tùy chọn socket TCP như TCP_NODELAY, TCP_USER_TIMEOUT, keepalive - [socket(7) — Linux manual page](https://man7.org/linux/man-pages/man7/socket.7.html) · Linux man-pages · Các tùy chọn socket SO_SNDBUF, SO_RCVBUF, SO_KEEPALIVE, SO_LINGER - [RFC 5681: TCP Congestion Control](https://www.rfc-editor.org/rfc/rfc5681) · IETF · 3 ACK trùng thì truyền lại nhanh, sau truyền lại do timer thì cửa sổ tắc nghẽn còn 1 segment (quy tắc sách giáo khoa trong mô phỏng) - [RFC 8985: The RACK-TLP Loss Detection Algorithm for TCP](https://www.rfc-editor.org/rfc/rfc8985) · IETF · RACK xác định mất gói theo thời điểm gửi thay vì đếm ACK trùng - [RFC 6298: Computing TCP's Retransmission Timer](https://www.rfc-editor.org/rfc/rfc6298) · IETF · Khuyến nghị RTO tối thiểu 1 giây, backoff gấp đôi sau mỗi lần hết hạn - [IP Sysctl](https://docs.kernel.org/networking/ip-sysctl.html) · Linux kernel · tcp_rto_min_us của Linux mặc định 200 ms, phát hiện mất gói bằng RACK (tcp_recovery) - [include/net/tcp.h (Linux v6.12)](https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/include/net/tcp.h?h=v6.12) · Linux kernel · Delayed ACK 40 ms của Linux trong mô phỏng: TCP_DELACK_MIN (HZ/25 = 40 ms), tối đa TCP_DELACK_MAX (HZ/5 = 200 ms) - [Design issues - Sending small data segments over TCP with Winsock](https://learn.microsoft.com/en-us/previous-versions/troubleshoot/windows/win32/data-segment-tcp-winsock) · Microsoft · Delayed ACK 200 ms của Windows trong mô phỏng: nhận dữ liệu thì đặt timer delayed ACK 200 ms, kết hợp với Nagle thì gói nhỏ phải chờ ACK - [RFC 896: Congestion Control in IP/TCP Internetworks](https://www.rfc-editor.org/rfc/rfc896) · IETF · Mục đích ban đầu của quy tắc Nagle: vấn đề của terminal từ xa, mỗi phím gõ 1 byte lại phát ra một gói 41 byte - [send(2) — Linux manual page](https://man7.org/linux/man-pages/man2/send.2.html) · Linux man-pages · Khi bộ đệm gửi đầy, send() kiểu blocking không trả về ### #journey - [ITU-T G.114: One-way transmission time](https://www.itu.int/rec/T-REC-G.114-200305-I/en) · ITU · Độ trễ lan truyền trên cáp quang 5 µs/km (1.000 km một chiều là 5 ms); một chiều không quá 150 ms thì đa số ứng dụng gần như không nhận ra, nhưng tác vụ tương tác cao có thể bị ảnh hưởng ngay cả dưới 100 ms - [Azure network round-trip latency statistics](https://learn.microsoft.com/en-us/azure/networking/azure-network-latency) · Microsoft Azure · Quy mô chặng Internet theo vị trí server trong thí nghiệm: tính từ Seoul, khu vực Busan 8 ms, Tokyo 30 ms, Singapore 68 ms, miền Tây Hoa Kỳ 124–136 ms, châu Âu 234–244 ms (trung vị khứ hồi) - [The Internet at the Speed of Light (HotNets 2014)](https://conferences.sigcomm.org/hotnets/2014/papers/hotnets-XIII-final111.pdf) · ACM · Hệ số tuyến đường 1,5 trong thí nghiệm: tuyến đường thực tế qua các router dài khoảng 1,5 lần (trung vị) so với đường thẳng cáp quang - [Report ITU-R M.2134: Requirements related to technical performance for IMT-Advanced radio interface(s)](https://www.itu.int/pub/R-REP-M.2134-2008) · ITU · Yêu cầu độ trễ một chiều trên chặng vô tuyến của LTE-Advanced là dưới 10 ms (không tải, gói nhỏ; giá trị LTE trong thí nghiệm là giả định cộng thêm tải và thời gian chờ lập lịch) - [Report ITU-R M.2410: Minimum requirements related to technical performance for IMT-2020 radio interface(s)](https://www.itu.int/pub/R-REP-M.2410-2017) · ITU · Yêu cầu độ trễ một chiều trên chặng vô tuyến của 5G (IMT-2020) là 4 ms (eMBB, không tải) - [An Experimental Study of Home Gateway Characteristics (IMC 2010)](https://conferences.sigcomm.org/imc/2010/papers/p260.pdf) · ACM · Hàng đợi router trong thí nghiệm: với router gia đình, khi upload và download chồng lên nhau thì độ trễ chờ có thể tăng tới vài trăm ms (tối đa khoảng 400 ms) - [Maximum transmission unit and maximum segment size](https://developers.cloudflare.com/magic-transit/reference/mtu-mss/) · Cloudflare · Đi qua hệ thống chống DDoS trong thí nghiệm: chỉ lưu lượng đi vào mới qua mạng chống DDoS, còn phản hồi của server đi thẳng ra Internet (DSR) ### #l-home - [RFC 7567: IETF Recommendations Regarding Active Queue Management](https://www.rfc-editor.org/rfc/rfc7567) · IETF · Hàng đợi ứ lại trên thiết bị là nguyên nhân chính gây độ trễ Internet, kèm khuyến nghị bật quản lý hàng đợi (AQM) theo mặc định - [RFC 8289: Controlled Delay Active Queue Management](https://www.rfc-editor.org/rfc/rfc8289) · IETF · Thời gian chờ mục tiêu 5 ms và khoảng quan sát 100 ms của CoDel (hàng đợi SQM khoảng 5 ms trong thí nghiệm) - [RFC 8290: The Flow Queue CoDel Packet Scheduler and Active Queue Management Algorithm](https://www.rfc-editor.org/rfc/rfc8290) · IETF · FQ-CoDel: chia hàng đợi riêng cho từng luồng theo địa chỉ và cổng, ưu tiên đẩy ra trước các luồng nhỏ không tạo hàng đợi - [What Can I Do About Bufferbloat?](https://www.bufferbloat.net/projects/bloat/wiki/What_can_I_do_about_Bufferbloat/) · Bufferbloat.net · Dùng router hỗ trợ SQM như cake, fq_codel, vừa đo độ trễ khi có tải vừa chỉnh tốc độ SQM - [An Experimental Study of Home Gateway Characteristics (IMC 2010)](https://conferences.sigcomm.org/imc/2010/papers/p260.pdf) · ACM · Đo 34 mẫu router gia đình: khi upload và download chồng lên nhau, độ trễ chờ tối đa khoảng 400 ms; thời gian giữ ánh xạ UDP trung vị 90 giây - [Ending the Anomaly: Achieving Low Latency and Airtime Fairness in WiFi (USENIX ATC 2017)](https://www.usenix.org/system/files/conference/atc17/atc17-hoiland-jorgensen.pdf) · USENIX · Hàng đợi vô tuyến của Wi-Fi cũng có độ trễ vài trăm ms khi có tải, và thiết bị chậm chiếm luôn thời gian truyền của các thiết bị khác - [RFC 4787: Network Address Translation (NAT) Behavioral Requirements for Unicast UDP](https://www.rfc-editor.org/rfc/rfc4787) · IETF · Yêu cầu về timer ánh xạ UDP của NAT (từ 2 phút trở lên, khuyến nghị mặc định từ 5 phút trở lên) và làm mới bằng gói đi từ trong ra - [RFC 8325: Mapping Diffserv to IEEE 802.11](https://www.rfc-editor.org/rfc/rfc8325) · IETF · Wi-Fi dùng cơ chế CSMA/CA: kênh bận thì hoãn, chờ backoff ngẫu nhiên rồi mới gửi - [Resolve Wi-Fi and Bluetooth issues caused by wireless interference](https://support.apple.com/en-us/102319) · Apple · Nhiễu lò vi sóng trong thí nghiệm: lò vi sóng, Bluetooth… gây nhiễu Wi-Fi 2,4 GHz, chuyển sang 5 GHz thì đỡ - [RFC 9438: CUBIC for Fast and Long-Distance Networks](https://www.rfc-editor.org/rfc/rfc9438) · IETF · Điều tiết tốc độ upload trong thí nghiệm: khi gặp mất gói, CUBIC thu cửa sổ gửi còn 0,7 lần (giảm khoảng 30%) rồi tăng lại ### #l-isp - [ITU-T G.114: One-way transmission time](https://www.itu.int/rec/T-REC-G.114-200305-I/en) · ITU · Độ trễ lan truyền trên cáp quang 5 µs/km: ánh sáng trong cáp quang đi khoảng 200.000 km mỗi giây, khứ hồi 1.000 km tối thiểu 10 ms - [The Internet at the Speed of Light (HotNets 2014)](https://conferences.sigcomm.org/hotnets/2014/papers/hotnets-XIII-final111.pdf) · ACM · Tuyến đường thực tế qua các router dài khoảng 1,5 lần (trung vị) so với đường thẳng cáp quang; ping tối thiểu gấp 3,2 lần so với tốc độ ánh sáng (khoảng 2 lần so với đường thẳng cáp quang) - [Azure network round-trip latency statistics](https://learn.microsoft.com/en-us/azure/networking/azure-network-latency) · Microsoft Azure · Trung vị khứ hồi đo thực tế tính từ Seoul: Tokyo 30 ms, miền Tây Hoa Kỳ 124–136 ms, châu Âu 234–244 ms (Hàn Quốc–châu Âu dài khoảng 2,8 lần đường thẳng cáp quang) - [AAE-1 & SMW5 cable cuts impact millions of users across multiple countries](https://blog.cloudflare.com/aae-1-smw5-cable-cuts/) · Cloudflare · Lưu lượng châu Âu–châu Á thường đi qua Ai Cập, và sửa cáp quang biển mất từ vài ngày đến vài tuần - [Quantifying the Causes of Path Inflation (SIGCOMM 2003)](https://conferences.sigcomm.org/sigcomm/2003/papers/p113-spring.pdf) · ACM · Chính sách peering giữa các nhà mạng và định tuyến liên miền làm tuyến đường dài ra đáng kể - [Inferring Persistent Interdomain Congestion (SIGCOMM 2018)](https://www.caida.org/catalog/papers/2018_inferring_persistent_interdomain_congestion/inferring_persistent_interdomain_congestion.pdf) · ACM · Một số điểm kết nối giữa các nhà mạng bị tắc nghẽn lặp lại: độ trễ và mất gói tăng vào giờ cao điểm mỗi ngày - [RFC 4271: A Border Gateway Protocol 4 (BGP-4)](https://www.rfc-editor.org/rfc/rfc4271) · IETF · BGP trao đổi thông tin tuyến đường Internet, hold time mặc định khuyến nghị là 90 giây - [BGP updates in 2024](https://blog.apnic.net/2025/01/07/bgp-updates-in-2024/) · APNIC · Thời gian để định tuyến ổn định trở lại sau khi tuyến đường thay đổi, tính trung bình theo ngày, là 25–35 giây với IPv4 và 40–50 giây với IPv6 - [Delayed Internet Routing Convergence (SIGCOMM 2000)](https://conferences.sigcomm.org/sigcomm/2000/conf/paper/sigcomm2000-5-2.pdf) · ACM · Sau sự cố tuyến đường, việc hội tụ có thể mất đến vài phút, trong thời gian đó mất gói và độ trễ tăng (số liệu đo năm 2000) ### #l-dc-net - [High-Resolution Measurement of Data Center Microbursts (IMC 2017)](https://conferences.sigcomm.org/imc/2017/papers/imc17-final60.pdf) · ACM · Độ trễ đầu cuối trong mạng trung tâm dữ liệu dưới 1 ms, và hơn 70% burst kết thúc trong vài chục µs nên nhìn mức sử dụng trung bình không thấy - [Data Center TCP (DCTCP) (SIGCOMM 2010)](https://conferences.sigcomm.org/sigcomm/2010/papers/sigcomm/p63.pdf) · ACM · Switch phổ thông cho nhiều cổng dùng chung một bộ đệm nhỏ; khi nhiều luồng cùng dồn vào một cổng trong khoảnh khắc ngắn, bộ đệm tràn và mất gói - [Netfilter Conntrack Sysfs variables](https://docs.kernel.org/networking/nf_conntrack-sysctl.html) · Linux kernel · Số mục tối đa của bảng theo dõi kết nối và thời gian giữ mặc định (UDP 30 giây, stream 120 giây, TCP đã thiết lập 5 ngày) - [Edit attributes for your Application Load Balancer](https://docs.aws.amazon.com/elasticloadbalancing/latest/application/edit-load-balancer-attributes.html) · AWS · Idle timeout của ALB mặc định 60 giây, hết thời gian thì bộ cân bằng tải đóng kết nối - [Network Load Balancers](https://docs.aws.amazon.com/elasticloadbalancing/latest/network/network-load-balancers.html) · AWS · Giá trị mặc định của bộ cân bằng tải trong thí nghiệm: NLB TCP 350 giây, UDP 120 giây (không đổi được); sau khoảng nhàn rỗi thì âm thầm ngừng theo dõi - [Configure load balancer TCP reset and idle timeout](https://learn.microsoft.com/en-us/azure/load-balancer/load-balancer-tcp-idle-timeout) · Microsoft Azure · Idle timeout của Azure Load Balancer mặc định 4 phút - [Amazon EC2 security group connection tracking](https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/security-group-connection-tracking.html) · AWS · Giá trị mặc định theo dõi kết nối của security group, kèm giải thích rằng idle timeout TCP của bộ cân bằng tải và tường lửa thường là 60–90 phút - [Flow-Based Sessions](https://www.juniper.net/documentation/us/en/software/junos/flow-packet-processing/topics/topic-map/security-flow-based-session-for-srx-series-devices.html) · Juniper Networks · Tường lửa doanh nghiệp trong thí nghiệm: session timeout mặc định của tường lửa SRX là TCP 1.800 giây (30 phút), UDP 60 giây - [An Experimental Study of Home Gateway Characteristics (IMC 2010)](https://conferences.sigcomm.org/imc/2010/papers/p260.pdf) · ACM · TCP 1 giờ của router gia đình trong thí nghiệm: trung vị ánh xạ TCP khoảng 60 phút. Ánh xạ UDP khác nhau tùy thiết bị, từ 30 đến 691 giây, trung vị 90 giây với lưu lượng một chiều và khoảng 180 giây với lưu lượng hai chiều - [A Multi-perspective Analysis of Carrier-Grade NAT Deployment (IMC 2016)](https://www.icir.org/vern/papers/cgn-imc16.pdf) · ACM · CGNAT UDP 30 giây và router gia đình UDP 1 phút trong thí nghiệm: trung vị ánh xạ UDP của CGN là 35 giây trên mạng cố định và 65 giây trên mạng di động, 74% NAT được đo có thời gian không quá 1 phút, NAT của router gia đình (CPE) phần lớn là 65 giây - [tcp(7) — Linux manual page](https://man7.org/linux/man-pages/man7/tcp.7.html) · Linux man-pages · TCP keepalive trong thí nghiệm: mặc định sau 2 giờ (7.200 giây) nhàn rỗi thì kiểm tra 9 lần, cách nhau 75 giây, không có phản hồi thì ngắt - [Cached apps freezer](https://source.android.com/docs/core/perf/cached-apps-freezer) · Android (Google) · Chạy nền trên di động trong thí nghiệm: Android 14 trở lên đóng băng tiến trình ứng dụng đã vào trạng thái cache sau 10 giây - [RFC 5880: Bidirectional Forwarding Detection (BFD)](https://www.rfc-editor.org/rfc/rfc5880) · IETF · BFD phát hiện hỏng tuyến đường nhanh hơn gói Hello tính theo giây của các giao thức định tuyến - [RFC 2923: TCP Problems with Path MTU Discovery](https://www.rfc-editor.org/rfc/rfc2923) · IETF · MTU black hole trên tuyến đường: ICMP bị chặn nên chỉ gói lớn biến mất ### #basics - [Teletraffic Engineering Handbook (ITU-D Study Group 2 Question 16/2)](https://www.itu.int/dms_pub/itu-d/opb/stg/D-STG-SG02.16.2.1-2002-PDF-E.pdf) · ITU · Thời gian chờ trung bình của M/M/1 W = A·s/(1−A): ở mức sử dụng 50%, 80%, 90% thì bằng 1, 4, 9 lần thời gian xử lý. Cùng mức sử dụng, càng nhiều worker (server) và gói đến càng đều thì thời gian chờ càng ngắn - [CloudWatch metrics that are available for your instances](https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/viewing_metrics_with_cloudwatch.html) · AWS · CPUUtilization của EC2 là giá trị của toàn instance, được gộp theo chu kỳ 5 phút mặc định hoặc 1 phút với giám sát chi tiết - [Designs, Lessons and Advice from Building Large Distributed Systems (Jeff Dean, LADIS 2009 keynote)](https://www.cs.cornell.edu/projects/ladis2009/talks/dean-keynote-ladis2009.pdf) · Google · Truy cập bộ nhớ chính 100 ns, khứ hồi trong cùng trung tâm dữ liệu 500.000 ns (0,5 ms) - [ITU-T G.114: One-way transmission time](https://www.itu.int/rec/T-REC-G.114-200305-I/en) · ITU · Độ trễ lan truyền trên cáp quang 5 µs/km (căn cứ để tính giới hạn tốc độ ánh sáng) - [Latency Compensating Methods in Client/Server In-game Protocol Design and Optimization (Yahn W. Bernier, GDC 2001)](https://web.cs.wpi.edu/~claypool/courses/4513-B03/papers/games/bernier.pdf) · Valve · Giá trị mặc định của Half-Life: 20 lần cập nhật mỗi giây, nội suy 100 ms. Nếu 10 lần mỗi giây thì nội suy 200 ms để chịu được một lần mất gói - [IP Sysctl](https://docs.kernel.org/networking/ip-sysctl.html) · Linux kernel · tcp_rto_min_us mặc định 200000 (200 ms) - [Factors influencing the latency of simple reaction time (Frontiers in Human Neuroscience, 2015)](https://doi.org/10.3389/fnhum.2015.00131) · Frontiers · Thời gian phản ứng đơn giản trung bình khoảng 231 ms (213 ms sau khi hiệu chỉnh độ trễ thiết bị) - [A Survey and Taxonomy of Latency Compensation Techniques for Network Computer Games (ACM Computing Surveys, 2022)](https://web.cs.wpi.edu/~claypool/papers/lag-taxonomy/paper.pdf) · ACM · Độ trễ dưới 100 ms cũng ảnh hưởng đến kết quả tác vụ trong game; với thao tác như kéo thả thì khoảng 10 ms đã nhận ra - [Peeking into VALORANT's Netcode](https://www.riotgames.com/en/news/peeking-valorants-netcode) · Riot Games · Người chơi thành thạo nhận ra cả chênh lệch khoảng 10 ms trong thử nghiệm mù - [Latency and Player Actions in Online Games (Communications of the ACM, 2006)](https://web.cs.wpi.edu/~claypool/papers/precision-deadline/final.pdf) · ACM · Mức độ trễ chấp nhận được theo thể loại: góc nhìn thứ nhất khoảng 100 ms, góc nhìn thứ ba (RPG, MMO) khoảng 500 ms, RTS khoảng 1.000 ms - [JEP 333: ZGC: A Scalable Low-Latency Garbage Collector](https://openjdk.org/jeps/333) · OpenJDK · G1 với heap 128 GB có thời gian dừng trung bình 157 ms, tối đa 544 ms; ZGC khoảng 1–2 ms bất kể kích thước heap và dữ liệu còn sống - [CommonNetworkParametersExtensions (Unity Transport 2.5)](https://docs.unity3d.com/Packages/com.unity.transport@2.5/api/Unity.Networking.Transport.CommonNetworkParametersExtensions.html) · Unity · disconnectTimeoutMS: không nhận được gì trong khoảng thời gian này thì ngắt kết nối (mặc định 30.000 ms) ### #lab - [Latency Compensating Methods in Client/Server In-game Protocol Design and Optimization (Yahn W. Bernier, GDC 2001)](https://web.cs.wpi.edu/~claypool/courses/4513-B03/papers/games/bernier.pdf) · Valve · Nội suy mượt nhưng vẽ trạng thái trong quá khứ; ngoại suy không biết lúc đổi hướng nên đoán sai thì nhảy vị trí. Sai số dự đoán được hiệu chỉnh theo kết quả của server - [UDP vs. TCP](https://gafferongames.com/post/udp_vs_tcp/) · Gaffer On Games · TCP mất một gói thì dù dữ liệu mới đã đến cũng không chuyển lên cho đến khi gói đó được truyền lại (thường từ 2×RTT trở lên) - [Deterministic Lockstep](https://gafferongames.com/post/deterministic_lockstep/) · Gaffer On Games · Gửi lặp lại các input chưa được xác nhận trong mọi gói thì không phải chờ truyền lại (trường hợp xấu nhất là lượng input của 2 giây) - [Snapshot Interpolation](https://gafferongames.com/post/snapshot_interpolation/) · Gaffer On Games · Nhận đến đâu vẽ ngay đến đó thì giật vì jitter; bộ đệm nội suy tăng độ trễ một chút để đổi lấy độ mượt - [Understanding Networked Movement in the Character Movement Component for Unreal Engine](https://dev.epicgames.com/documentation/en-us/unreal-engine/understanding-networked-movement-in-the-character-movement-component-for-unreal-engine) · Epic Games · Nếu dữ liệu di chuyển của client bị thiếu hoặc sai do sự cố kết nối, server hiệu chỉnh vị trí (kéo ngược) - [Source SDK 2013: player.cpp](https://raw.githubusercontent.com/ValveSoftware/source-sdk-2013/master/src/game/server/player.cpp) · Valve · Giới hạn các lệnh dồn tới bằng ngân sách lệnh tích lũy theo từng tick. Quá chặt thì cả người chơi bình thường cũng bị giật khựng - [Introducing Time Dilation (TiDi)](https://www.eveonline.com/news/view/introducing-time-dilation-tidi) · CCP Games · Thiết kế làm chậm đồng hồ game (Time Dilation) khi server quá tải để mọi thứ trôi chậm lại - [CommonNetworkParametersExtensions (Unity Transport 2.5)](https://docs.unity3d.com/Packages/com.unity.transport@2.5/api/Unity.Networking.Transport.CommonNetworkParametersExtensions.html) · Unity · Timeout không hoạt động: không nhận được gì trong một khoảng thời gian thì ngắt kết nối ### #symptoms - [Latency Compensating Methods in Client/Server In-game Protocol Design and Optimization (Yahn W. Bernier, GDC 2001)](https://web.cs.wpi.edu/~claypool/courses/4513-B03/papers/games/bernier.pdf) · Valve · Khi thiếu bản cập nhật, đối tượng dừng ở vị trí cuối (giật khựng) hoặc ngoại suy rồi nhảy (dịch chuyển tức thời). Phải giới hạn thời gian ngoại suy - [Understanding Networked Movement in the Character Movement Component for Unreal Engine](https://dev.epicgames.com/documentation/en-us/unreal-engine/understanding-networked-movement-in-the-character-movement-component-for-unreal-engine) · Epic Games · Nếu dữ liệu di chuyển của client bị thiếu hoặc khác kết quả server tính, server gửi hiệu chỉnh để đưa vị trí về lại (kéo ngược) - [UDP vs. TCP](https://gafferongames.com/post/udp_vs_tcp/) · Gaffer On Games · TCP giữ lại dữ liệu phía sau cho đến khi nhận được gói truyền lại của gói bị mất, rồi mới chuyển lên một lượt (tua nhanh) - [Deterministic Lockstep](https://gafferongames.com/post/deterministic_lockstep/) · Gaffer On Games · Khi input tồn đọng đến cùng một lúc, server tính dồn nhiều khung hình để đuổi kịp - [Introducing Time Dilation (TiDi)](https://www.eveonline.com/news/view/introducing-time-dilation-tidi) · CCP Games · Time Dilation (TiDi) làm chậm đồng hồ game khi server quá tải, và hiện tượng tác vụ bị dời lại vài giây khi quá tải - [Peeking into VALORANT's Netcode](https://www.riotgames.com/en/news/peeking-valorants-netcode) · Riot Games · Thời gian đệm thay đổi theo tick rate của server và khung hình render của client - [Using Gameplay Abilities in Unreal Engine](https://dev.epicgames.com/documentation/en-us/unreal-engine/using-gameplay-abilities-in-unreal-engine) · Epic Games · Server có thể đảo ngược skill (ability) mà client đã chạy trước theo dự đoán (nuốt thao tác·rollback) - [Actor Relevancy in Unreal Engine](https://dev.epicgames.com/documentation/en-us/unreal-engine/actor-relevancy-in-unreal-engine) · Epic Games · Actor mà server coi là không liên quan sẽ không được replicate hoặc bị xóa ở client (không hiển thị·đối tượng ma) - [CommonNetworkParametersExtensions (Unity Transport 2.5)](https://docs.unity3d.com/Packages/com.unity.transport@2.5/api/Unity.Networking.Transport.CommonNetworkParametersExtensions.html) · Unity · Hết timeout không hoạt động thì ngắt kết nối (mất kết nối) ### #sync - [Latency Compensating Methods in Client/Server In-game Protocol Design and Optimization (Yahn W. Bernier, GDC 2001)](https://web.cs.wpi.edu/~claypool/courses/4513-B03/papers/games/bernier.pdf) · Valve · Nguyên lý của server có thẩm quyền, dự đoán và hiệu chỉnh phía client, nội suy, bù trễ, và các đánh đổi như “đã nấp sau góc tường vẫn bị bắn trúng” - [What Every Programmer Needs To Know About Game Networking](https://gafferongames.com/post/what_every_programmer_needs_to_know_about_game_networking/) · Gaffer On Games · Quá trình phát triển của netcode từ lockstep P2P sang client/server rồi dự đoán phía client - [Using Gameplay Abilities in Unreal Engine](https://dev.epicgames.com/documentation/en-us/unreal-engine/using-gameplay-abilities-in-unreal-engine) · Epic Games · Đánh đổi giữa độ phản hồi và độ chính xác của Local Predicted và Server Initiated - [Peeking into VALORANT's Netcode](https://www.riotgames.com/en/news/peeking-valorants-netcode) · Riot Games · Server có thẩm quyền, dự đoán, bộ đệm, phán định bằng quay ngược (rewind) và giới hạn quay ngược - [NetworkTime and ticks (Netcode for GameObjects 2.5)](https://docs.unity3d.com/Packages/com.unity.netcode.gameobjects@2.5/manual/advanced-topics/networktime-ticks.html) · Unity · Cách hẹn lịch sự kiện theo thời gian của server rồi phát lại - [1500 Archers on a 28.8: Network Programming in Age of Empires and Beyond](https://www.gamedeveloper.com/programming/1500-archers-on-a-28-8-network-programming-in-age-of-empires-and-beyond) · Game Developer · Lockstep: hẹn lệnh sau hai lượt, độ dài lượt theo máy chậm nhất, độ trễ ổn định 500 ms vẫn chấp nhận được nhưng độ trễ thất thường thì khó chịu - [Deterministic Lockstep](https://gafferongames.com/post/deterministic_lockstep/) · Gaffer On Games · Lockstep chỉ chạy tiếp khi đã nhận đủ input, dùng bộ đệm trễ phát lại để hấp thụ jitter - [GGPO Rollback Networking SDK](https://www.ggpo.net/) · GGPO · Rollback: dự đoán input của đối thủ để chạy tiếp, đoán sai thì quay ngược và tính lại - [8 Frames in 16ms: Rollback Networking in 'Mortal Kombat' and 'Injustice 2'](https://www.gdcvault.com/play/1025471/8-Frames-in-16ms-Rollback) · GDC · Rollback loại bỏ độ trễ input cục bộ của lockstep, tính lại tối đa 8 khung hình - [Latency and Player Actions in Online Games (Communications of the ACM, 2006)](https://web.cs.wpi.edu/~claypool/papers/precision-deadline/final.pdf) · ACM · Cùng độ trễ nhưng ảnh hưởng khác nhau tùy độ chính xác và thời hạn của hành động, và góc nhìn (thứ nhất, thứ ba, toàn cảnh) - [Networking Overview for Unreal Engine](https://dev.epicgames.com/documentation/en-us/unreal-engine/networking-overview-for-unreal-engine) · Epic Games · Lợi thế và tải của host trong listen server - [Factors influencing the latency of simple reaction time (Frontiers in Human Neuroscience, 2015)](https://doi.org/10.3389/fnhum.2015.00131) · Frontiers · Thời gian phản ứng đơn giản của con người khoảng 0,23 giây ### #partial - [Peeking into VALORANT's Netcode](https://www.riotgames.com/en/news/peeking-valorants-netcode) · Riot Games · Một người bị lệch thì chỉ hiệu chỉnh cho người đó, chín người còn lại vẫn thấy mượt. Phán định bằng quay ngược có giới hạn - [State Synchronization](https://gafferongames.com/post/state_synchronization/) · Gaffer On Games · Gói tin đến thành từng cụm kiểu khung hình này 2 gói, khung hình sau 0 gói, và bộ đệm jitter dàn đều lại - [Source SDK 2013: player.cpp](https://raw.githubusercontent.com/ValveSoftware/source-sdk-2013/master/src/game/server/player.cpp) · Valve · Ngân sách lệnh chia các lệnh dồn tới ra xử lý theo tick, và tác dụng phụ của giới hạn quá chặt - [Source SDK 2013: player_lagcompensation.cpp](https://raw.githubusercontent.com/ValveSoftware/source-sdk-2013/master/src/game/server/player_lagcompensation.cpp) · Valve · Giới hạn quay ngược sv_maxunlag của Source engine mặc định 1 giây - [A Survey and Taxonomy of Latency Compensation Techniques for Network Computer Games (ACM Computing Surveys, 2022)](https://web.cs.wpi.edu/~claypool/papers/lag-taxonomy/paper.pdf) · ACM · “Đã nấp sau góc tường vẫn bị bắn trúng” do bù trễ, và độ trễ input để đồng bộ input của mọi người - [send(2) — Linux manual page](https://man7.org/linux/man-pages/man2/send.2.html) · Linux man-pages · Khi bộ đệm gửi đầy, send() ở chế độ blocking sẽ chờ, còn ở chế độ non-blocking thì trả về ngay với EAGAIN - [Networking Overview for Unreal Engine](https://dev.epicgames.com/documentation/en-us/unreal-engine/networking-overview-for-unreal-engine) · Epic Games · Host của listen server có lợi thế hơn người chơi khác - [Authority (Netcode for GameObjects 2.5)](https://docs.unity3d.com/Packages/com.unity.netcode.gameobjects@2.5/manual/terms-concepts/authority.html) · Unity · Quyền phân tán: mỗi client đảm nhận tính toán một phần đối tượng - [Actor Relevancy in Unreal Engine](https://dev.epicgames.com/documentation/en-us/unreal-engine/actor-relevancy-in-unreal-engine) · Epic Games · Server chỉ gửi cho mỗi kết nối các actor liên quan, hết liên quan thì xóa ở client - [NetworkConfig class (Netcode for GameObjects 2.5)](https://docs.unity3d.com/Packages/com.unity.netcode.gameobjects@2.5/api/Unity.Netcode.NetworkConfig.html) · Unity · Message của đối tượng chưa được tạo sẽ bị giữ lại, quá thời gian thì bỏ (SpawnTimeout) - [RFC 8085: UDP Usage Guidelines](https://www.rfc-editor.org/rfc/rfc8085) · IETF · Gói UDP bị phân mảnh chỉ cần mất một fragment là mất toàn bộ - [Using SO_REUSEADDR and SO_EXCLUSIVEADDRUSE](https://learn.microsoft.com/en-us/windows/win32/winsock/using-so-reuseaddr-and-so-exclusiveaddruse) · Microsoft · Hai socket dùng chung một cổng thì không biết bên nào sẽ nhận gói - [RFC 6269: Issues with IP Address Sharing](https://www.rfc-editor.org/rfc/rfc6269) · IETF · Nhiều thuê bao dùng chung một địa chỉ IP thì không thể phân biệt người dùng chỉ bằng IP - [Application.runInBackground](https://docs.unity3d.com/ScriptReference/Application-runInBackground.html) · Unity · Hành vi mặc định: ứng dụng bị tạm dừng khi chạy nền - [Actor Priority in Unreal Engine](https://dev.epicgames.com/documentation/en-us/unreal-engine/actor-priority-in-unreal-engine) · Epic Games · Khi băng thông bão hòa, chỉ replicate một số actor theo mức ưu tiên ### #retrans - [RFC 6298: Computing TCP's Retransmission Timer](https://www.rfc-editor.org/rfc/rfc6298) · IETF · RTO = SRTT + max(G, 4·RTTVAR), khởi đầu 1 giây, khuyến nghị tối thiểu 1 giây, mỗi lần hết hạn tăng gấp đôi, nếu đặt trần thì từ 60 giây trở lên, gói truyền lại không dùng làm mẫu RTT (Karn) - [RFC 5681: TCP Congestion Control](https://www.rfc-editor.org/rfc/rfc5681) · IETF · Truyền lại nhanh ở ACK trùng thứ ba; sau RTO thì bắt đầu lại từ cửa sổ tắc nghẽn 1 segment (loss window) - [RFC 6675: A Conservative Loss Recovery Algorithm Based on Selective Acknowledgment (SACK) for TCP](https://www.rfc-editor.org/rfc/rfc6675) · IETF · Phục hồi mất gói dựa trên thông tin SACK để xác định gói bị thiếu (tín hiệu ACK trùng và SACK cho truyền lại nhanh) - [RFC 8985: The RACK-TLP Loss Detection Algorithm for TCP](https://www.rfc-editor.org/rfc/rfc8985) · IETF · Biên độ chấp nhận sai thứ tự của RACK (min_RTT/4) và điều chỉnh theo DSACK, thời gian chờ TLP 2·SRTT (nếu chỉ còn một gói chưa được xác nhận thì cộng thêm khoảng dự phòng cho delayed ACK), đặt lại RTO sau khi gửi TLP, bắt buộc có SACK - [RFC 2018: TCP Selective Acknowledgment Options](https://www.rfc-editor.org/rfc/rfc2018) · IETF · SACK: bên nhận báo phần bị thiếu ở giữa - [RFC 2883: An Extension to the Selective Acknowledgement (SACK) Option for TCP](https://www.rfc-editor.org/rfc/rfc2883) · IETF · DSACK: báo đã nhận lại thứ đã nhận rồi, làm lộ ra truyền lại không cần thiết - [RFC 5682: Forward RTO-Recovery (F-RTO): An Algorithm for Detecting Spurious Retransmission Timeouts with TCP](https://www.rfc-editor.org/rfc/rfc5682) · IETF · F-RTO: phát hiện RTO không cần thiết - [RFC 3522: The Eifel Detection Algorithm for TCP](https://www.rfc-editor.org/rfc/rfc3522) · IETF · Dùng timestamp để xác định lại sau đó xem việc phục hồi có thật sự cần hay không (việc khôi phục cửa sổ tắc nghẽn trong mô phỏng) - [RFC 7323: TCP Extensions for High Performance](https://www.rfc-editor.org/rfc/rfc7323) · IETF · Tùy chọn timestamp và window scale; không có scale thì cửa sổ tối đa 64 KiB - [RFC 6937: Proportional Rate Reduction for TCP](https://www.rfc-editor.org/rfc/rfc6937) · IETF · PRR: trong lúc phục hồi, giảm lượng gửi theo lượng dữ liệu mới được chuyển tới (giới hạn gửi trong lúc phục hồi trong mô phỏng) - [RFC 9438: CUBIC for Fast and Long-Distance Networks](https://www.rfc-editor.org/rfc/rfc9438) · IETF · CUBIC thu cửa sổ tắc nghẽn còn 0,7 lần khi mất gói (mức giảm 30% trong mô phỏng) - [RFC 9293: Transmission Control Protocol (TCP)](https://www.rfc-editor.org/rfc/rfc9293) · IETF · Zero window probe: cửa sổ bằng 0 vẫn gửi probe, khoảng cách tăng theo cấp số nhân - [RFC 9000: QUIC: A UDP-Based Multiplexed and Secure Transport](https://www.rfc-editor.org/rfc/rfc9000) · IETF · QUIC: mất gói chỉ chặn các stream có dữ liệu trong gói đó, các stream khác vẫn chạy tiếp (tách riêng các stream) - [RFC 2475: An Architecture for Differentiated Services](https://www.rfc-editor.org/rfc/rfc2475) · IETF · Định nghĩa: shaping làm chậm gói tin, còn policing bỏ phần vượt mức - [RFC 3168: The Addition of Explicit Congestion Notification (ECN) to IP](https://www.rfc-editor.org/rfc/rfc3168) · IETF · ECN: báo tắc nghẽn mà không bỏ gói tin - [Smart Queue Management](https://www.bufferbloat.net/projects/cerowrt/wiki/Smart_Queue_Management/) · Bufferbloat.net · SQM trên router: giảm tràn hàng đợi và bufferbloat bằng lập lịch theo luồng, AQM và shaping - [RFC 1191: Path MTU discovery](https://www.rfc-editor.org/rfc/rfc1191) · IETF · Tìm path MTU bằng ICMP báo vượt kích thước (type 3 code 4) - [RFC 1812: Requirements for IP Version 4 Routers](https://www.rfc-editor.org/rfc/rfc1812) · IETF · Router có thể giới hạn tốc độ phát sinh thông báo lỗi ICMP (kết quả mtr chỉ một chặng giữa trông như mất gói) - [RFC 2991: Multipath Issues in Unicast and Multicast Next-Hop Selection](https://www.rfc-editor.org/rfc/rfc2991) · IETF · Khi có nhiều tuyến song song, kết quả chẩn đoán như ping, traceroute khó tin cậy; cách cố định tuyến đường bằng hash luồng - [IP Sysctl](https://docs.kernel.org/networking/ip-sysctl.html) · Linux kernel · tcp_recovery (mặc định 0x1 RACK; từ 6.17, RACK là cơ chế phát hiện mất gói duy nhất nên đặt 0 cũng không có tác dụng), tcp_early_retrans (mặc định 3, đặt 0 thì tắt TLP), tcp_sack, tcp_dsack, tcp_timestamps (mặc định bật), tcp_thin_linear_timeouts (dưới 4 gói in-flight, tối đa 6 lần tuyến tính), tcp_rto_max_ms, tcp_mtu_probing, tcp_base_mss, tcp_rto_min_us, tcp_retries2 (mặc định 15, khoảng 924,6 giây), tcp_syn_linear_timeouts, tcp_notsent_lowat - [SNMP counter](https://docs.kernel.org/networking/snmp_counter.html) · Linux kernel · TcpOutSegs không tính truyền lại; ý nghĩa của TCPLossProbes, TCPLossProbeRecovery, TCPLostRetransmit, TCPSpuriousRTOs, TCPDSACKRecv, TCPSynRetrans - [net/ipv4/proc.c](https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/net/ipv4/proc.c?h=v6.12) · Linux kernel · Tên counter trong nstat (RetransSegs, TCPTimeouts, TCPLossProbes, TCPSpuriousRTOs, TCPDSACKRecv…) - [Thin-streams and TCP](https://docs.kernel.org/networking/tcp-thin.html) · Linux kernel · Thin stream như game khó dùng được truyền lại nhanh nên phải trông vào timeout dài; TCP_THIN_LINEAR_TIMEOUTS - [include/net/tcp.h](https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/include/net/tcp.h?h=v6.12) · Linux kernel · TCP_RTO_MIN 200 ms, TCP_RTO_MAX 120 giây, TCP_TIMEOUT_INIT 1 giây, delayed ACK 40–200 ms (TCP_DELACK_MIN, MAX), TCP_BASE_MSS 1.024, tiêu chí thin stream (dưới 4 gói in-flight) - [net/ipv4/tcp_input.c](https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/net/ipv4/tcp_input.c?h=v6.12) · Linux kernel · RTO = SRTT + rttvar (không dưới RTO tối thiểu), RACK chỉ áp dụng cho kết nối có SACK, dùng PRR để thu nhỏ cửa sổ tắc nghẽn trong lúc phục hồi - [net/ipv4/tcp_output.c](https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/net/ipv4/tcp_output.c?h=v6.12) · Linux kernel · TLP chỉ dùng cho kết nối có SACK, chờ 2·RTT, nếu chỉ còn một gói chưa được xác nhận thì cộng thêm RTO tối thiểu - [net/ipv4/tcp_timer.c](https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/net/ipv4/tcp_timer.c?h=v6.12) · Linux kernel · RTO kéo dài đủ tcp_retries1 lần thì coi là phát hiện black hole và dò MTU; timeout tuyến tính cho thin stream và SYN - [net/ipv4/tcp_recovery.c](https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/net/ipv4/tcp_recovery.c?h=v6.12) · Linux kernel · Thời gian dự phòng của RACK = min(min_RTT/4 × số bậc, SRTT); lần truyền lại được xác nhận nhanh hơn RTT tối thiểu thì bị loại khỏi phép tính - [net/ipv4/tcp_ipv4.c](https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/net/ipv4/tcp_ipv4.c?h=v6.12) · Linux kernel · Khởi tạo giá trị mặc định: tcp_early_retrans 3, tcp_recovery RACK, tcp_syn_linear_timeouts 4, tcp_base_mss 1.024 (tcp_mtu_probing không được đặt riêng nên là 0) - [net/ipv4/tcp_bbr.c](https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/net/ipv4/tcp_bbr.c?h=v6.12) · Linux kernel · pacing_rate của BBR = pacing_gain × băng thông điểm nghẽn (dùng pacing để gửi đều) - [tcp: use RACK to detect losses](https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=4f41b1c58a32537542f14c1150099131613a5e8a) · Linux kernel · Đưa RACK và tcp_recovery vào (Linux 4.4), ban đầu hoạt động như cơ chế phụ trợ cho cách cũ - [tcp: disable RFC6675 loss detection](https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=b38a51fec1c1f693f03b1aa19d0622123634d4b7) · Linux kernel · Chuyển RACK thành cơ chế phát hiện mất gói mặc định (năm 2018, Linux 4.18) - [tcp: remove obsolete and unused RFC3517/RFC6675 loss recovery code](https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=1c120191dcec510cc17d587ece48a7ae875a90c5) · Linux kernel · Xóa code phục hồi mất gói theo RFC6675 (Linux 6.17), kèm giải thích rằng RACK-TLP đã là mặc định từ năm 2018 - [tcp: make the first N SYN RTO backoffs linear](https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=ccce324dabfe2143519daf50ed8b1ef1d0c542f7) · Linux kernel · Không tăng gấp đôi backoff trong 4 lần đầu của RTO SYN (sau RTO đầu tiên 1 giây là thêm 4 lần chờ 1 giây, rồi mới đến 2, 4 giây …, Linux 6.5) - [tcp: add sysctl_tcp_rto_min_us](https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=f086edef71be7174a16c1ed67ac65a085cda28b1) · Linux kernel · RTO tối thiểu cho toàn server tcp_rto_min_us (Linux 6.11) - [tcp: add the ability to control max RTO](https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=54a378f43425085d0684679d99735696b69165bc) · Linux kernel · Tùy chọn socket TCP_RTO_MAX_MS, 1–120 giây (Linux 6.15) - [tcp: support TCP_RTO_MIN_US for set/getsockopt use](https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=f38805c5d26fe4af97837c10d58074a7496638bf) · Linux kernel · Tùy chọn socket TCP_RTO_MIN_US để đặt RTO tối thiểu cho từng kết nối (Linux 6.15) - [Android common kernels](https://source.android.com/docs/core/architecture/kernel/android-common) · Android (Google) · Kernel chung Android đang được hỗ trợ là từ 5.10 trở lên (mới hơn 4.18, phiên bản RACK trở thành mặc định) - [tcp(7) — Linux manual page](https://man7.org/linux/man-pages/man7/tcp.7.html) · Linux man-pages · TCP_NODELAY (tắt Nagle), TCP_USER_TIMEOUT (không đổi thời điểm truyền lại, chỉ đặt thời điểm bỏ cuộc), TCP_KEEPIDLE, TCP_INFO - [ip-route(8) — Linux manual page](https://man7.org/linux/man-pages/man8/ip-route.8.html) · iproute2 · Tùy chọn rto_min theo tuyến - [tc-fq(8) — Linux manual page](https://man7.org/linux/man-pages/man8/tc-fq.8.html) · iproute2 · Pacing theo từng kết nối của hàng đợi fq và SO_MAX_PACING_RATE - [iptables-extensions(8) — Linux manual page](https://man7.org/linux/man-pages/man8/iptables-extensions.8.html) · netfilter · TCPMSS --clamp-mss-to-pmtu: đi vòng qua chặng chặn ICMP bằng cách điều chỉnh MSS trong SYN - [ss(8) — Linux manual page](https://man7.org/linux/man-pages/man8/ss.8.html) · iproute2 · rto (ms), backoff (số lần exponential backoff), rtt/rttvar, cwnd trong ss -i - [misc/ss.c](https://git.kernel.org/pub/scm/network/iproute2/iproute2.git/tree/misc/ss.c?h=v6.12.0) · iproute2 · Các trường retrans:hiện tại/tích lũy, lost, reordering, bytes_sent, bytes_retrans trong kết quả ss -ti - [nstat(8) — Linux manual page](https://man7.org/linux/man-pages/man8/nstat.8.html) · iproute2 · Mặc định nstat hiển thị phần tăng kể từ lần chạy trước - [Demonstrations of tcpretrans, the Linux eBPF/bcc version](https://raw.githubusercontent.com/iovisor/bcc/master/tools/tcpretrans_example.txt) · IO Visor · Mỗi lần truyền lại hiển thị địa chỉ, cổng, trạng thái trên một dòng; -c gộp theo luồng, -l bao gồm cả các lần thử TLP - [Interface statistics](https://docs.kernel.org/networking/statistics.html) · Linux kernel · Xem đến lỗi chi tiết bằng ip -s -s link; ý nghĩa của rx_missed_errors, rx_crc_errors; thống kê theo driver trong ethtool -S - [drivers/net/ethernet/intel/igb/igb_ethtool.c](https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/drivers/net/ethernet/intel/igb/igb_ethtool.c?h=v6.12) · Linux kernel · Ví dụ tên counter theo driver: rx_missed_errors, rx_no_buffer_count, rx_crc_errors - [Ethtool counters](https://docs.kernel.org/networking/device_drivers/ethernet/mellanox/mlx5/counters.html) · Linux kernel · rx_out_of_buffer và rx_discards_phy của mlx5 - [net/core/net-procfs.c](https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/net/core/net-procfs.c?h=v6.12) · Linux kernel · /proc/net/softnet_stat có mỗi CPU một dòng, số hệ 16, cột thứ 2 là dropped, cột thứ 3 là time_squeeze - [Monitor network performance for ENA settings on your EC2 instance](https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/monitoring-network-performance-ena.html) · AWS · Ý nghĩa của bw_in, bw_out, pps, conntrack, linklocal_allowance_exceeded; muốn xem trên CloudWatch thì phải cài CloudWatch agent - [High-Resolution Measurement of Data Center Microbursts](https://research.facebook.com/publications/high-resolution-measurement-of-data-center-microbursts/) · Meta · Phần lớn burst kết thúc trong vài chục µs nên nhìn mức sử dụng trung bình theo phút không thấy nguyên nhân drop (IMC 2017) - [mtr(8) manual page source](https://raw.githubusercontent.com/traviscross/mtr/master/man/mtr.8.in) · mtr · -T (--tcp) dùng TCP SYN, -P (--port) chỉ định cổng đích - [pathping](https://learn.microsoft.com/en-us/windows-server/administration/windows-commands/pathping) · Microsoft · Lệnh chẩn đoán tuyến đường trên Windows, gửi nhiều lần để tính mất gói và độ trễ theo từng chặng - [Display Filter Reference: Transmission Control Protocol](https://www.wireshark.org/docs/dfref/t/tcp.html) · Wireshark · Bộ lọc hiển thị tcp.analysis.retransmission, fast_retransmission, spurious_retransmission, duplicate_ack, lost_segment, zero_window - [7.5. TCP Analysis](https://www.wireshark.org/docs/wsug_html_chunked/ChAdvTCPAnalysis.html) · Wireshark · Điều kiện để xác định là truyền lại, truyền lại nhanh, truyền lại không cần thiết và ZeroWindow - [Network-Related Performance Counters](https://learn.microsoft.com/en-us/windows-server/networking/technologies/network-subsystem/net-sub-performance-counters) · Microsoft · TCPv4 Segments Retransmitted/sec·Segments Sent/sec, Network Interface Packets Received Discarded - [TCP/IP connectivity issues troubleshooting](https://learn.microsoft.com/en-us/troubleshoot/windows-client/networking/tcp-ip-connectivity-issues-troubleshooting) · Microsoft · Xem cấu hình TCP toàn cục (Max SYN Retransmissions) bằng netsh int tcp show global; bắt gói đồng thời ở hai đầu để xác định mất gói ở giữa - [Packet Monitor (Pktmon)](https://learn.microsoft.com/en-us/windows-server/networking/technologies/pktmon/pktmon) · Microsoft · Công cụ tích hợp sẵn cho biết gói bị bỏ ở đâu và vì sao, tại nhiều điểm trong network stack của Windows - [Pktmon command formatting](https://learn.microsoft.com/en-us/windows-server/networking/technologies/pktmon/pktmon-syntax) · Microsoft · Tích hợp sẵn trong Windows 10 và Windows Server 2019 (1809 trở lên) dưới tên pktmon.exe - [TCP improvements in the Windows network stack (IETF 98 TCPM)](https://datatracker.ietf.org/meeting/98/materials/slides-98-tcpm-tcp-improvements-in-windows-01) · Microsoft · Windows 10 Anniversary Update (1607) và Server 2016 bật TLP và RACK mặc định (cho kết nối có RTT trên 10 ms); khi chỉ còn một gói, TLP tính đến delayed ACK 200 ms - [Algorithmic improvements boost TCP performance on the Internet](https://techcommunity.microsoft.com/blog/networkingblog/algorithmic-improvements-boost-tcp-performance-on-the-internet/2347061) · Microsoft · TLP mặc định từ Windows Server 2016, RACK mới (phục hồi được cả lần truyền lại bị mất) có trong Server 2022, PRR mặc định từ Windows 10 1903 - [TcpMaxConnectRetransmissions](https://learn.microsoft.com/en-us/previous-versions/windows/it-pro/windows-2000-server/cc938209(v=technet.10)) · Microsoft · Truyền lại SYN trên Windows đời cũ: bắt đầu từ 3 giây, tăng gấp đôi, 2 lần ### #owners - [RFC 4787: Network Address Translation (NAT) Behavioral Requirements for Unicast UDP](https://www.rfc-editor.org/rfc/rfc4787) · IETF · Ánh xạ NAT chắc chắn được làm mới bằng gói đi từ trong ra (REQ-6), còn làm mới bằng gói từ ngoài vào là tùy chọn (áp dụng cho UDP). Vì vậy heartbeat do client gửi - [RFC 5382: NAT Behavioral Requirements for TCP](https://www.rfc-editor.org/rfc/rfc5382) · IETF · NAT có thể xóa phiên TCP nhàn rỗi, idle timeout khuyến nghị là từ 2 giờ 4 phút trở lên (cấu hình có thể khác nhau tùy thiết bị) - [Amazon EC2 security group connection tracking](https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/security-group-connection-tracking.html) · AWS · Idle timeout TCP trong theo dõi kết nối của security group (loại instance Nitro v6 là 350 giây, loại khác 5 ngày, chỉnh được từ 60 giây đến 5 ngày), khuyến nghị keepalive ngắn hơn 5 phút, TCP đi qua NLB là 350 giây - [Control subnet traffic with network access control lists](https://docs.aws.amazon.com/vpc/latest/userguide/vpc-network-acls.html) · AWS · Network ACL không lưu trạng thái (không theo dõi kết nối) nên lưu lượng phản hồi cũng phải được cho phép bằng luật riêng - [Monitor network performance for ENA settings on your EC2 instance](https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/monitoring-network-performance-ena.html) · AWS · Counter vượt giới hạn theo dõi kết nối và số gói mỗi giây của instance (conntrack_allowance_exceeded, pps_allowance_exceeded) - [listen(2) — Linux manual page](https://man7.org/linux/man-pages/man2/listen.2.html) · Linux man-pages · Tham số backlog của listen là kích thước hàng đợi thực tế, nếu lớn hơn somaxconn thì bị cắt về giá trị đó (từ 5.4 mặc định 4096) - [IP Sysctl](https://docs.kernel.org/networking/ip-sysctl.html) · Linux kernel · somaxconn (trần backlog của listen), tcp_max_syn_backlog, tcp_syncookies (mặc định 1, biện pháp dự phòng khi hàng đợi SYN tràn), tcp_keepalive_time (mặc định 2 giờ) - [SNMP counter](https://docs.kernel.org/networking/snmp_counter.html) · Linux kernel · Khi hàng đợi accept đầy thì SYN bị bỏ và TcpExtListenOverflows, TcpExtListenDrops tăng - [Netfilter Conntrack Sysfs variables](https://docs.kernel.org/networking/nf_conntrack-sysctl.html) · Linux kernel · Tính mức sử dụng conntrack từ nf_conntrack_count và nf_conntrack_max - [Control Group v2](https://docs.kernel.org/admin-guide/cgroup-v2.html) · Linux kernel · nr_throttled trong cpu.stat: số lần container bị throttling do giới hạn CPU - [proc_stat(5) — Linux manual page](https://man7.org/linux/man-pages/man5/proc_stat.5.html) · Linux man-pages · steal trong /proc/stat: thời gian OS khác dùng CPU trong môi trường ảo hóa - [RFC 2992: Analysis of an Equal-Cost Multi-Path Algorithm](https://www.rfc-editor.org/rfc/rfc2992) · IETF · ECMP chọn tuyến bằng hash của các trường header dùng để phân biệt luồng (cùng luồng thì cùng tuyến, khác luồng có thể khác tuyến) - [mtr(8) manual page source](https://raw.githubusercontent.com/traviscross/mtr/master/man/mtr.8.in) · mtr · Đo tuyến đường bằng cùng giao thức và cổng với game (-T, -u, -P) ### #l-server-proc - [VALORANT's 128-Tick Servers](https://www.riotgames.com/en/news/valorants-128-tick-servers) · Riot Games · Tick budget: với 128 tick, mỗi khung hình phải xong trong 7,8125 ms; đo frame time theo từng hệ thống con rồi chia budget để quản lý - [Introducing Time Dilation (TiDi)](https://www.eveonline.com/news/view/introducing-time-dilation-tidi) · CCP Games · Mô phỏng vật lý của EVE Online cập nhật một lần trong 1 giây; khi quá tải thì làm chậm đồng hồ game để giảm tương ứng phần tải gắn với thời gian - [HED-GP Technical Retrospective: What a HED-ache](https://www.eveonline.com/news/view/what-a-hed-ache) · CCP Games · Time Dilation có mức thấp nhất 10%; việc truyền O(n²), tức hành động của n người phải báo cho n người, là yếu tố giới hạn của các trận chiến quy mô lớn - [Handling variation in time](https://docs.unity3d.com/Manual/time-handling-variations.html) · Unity · Mô hình của thí nghiệm tick: khi vòng chạy theo bước cố định bị trễ, server chạy dồn các bước để đuổi kịp (tua nhanh), phần thời gian vượt giới hạn bị bỏ nên thời gian game chậm lại (quay chậm) - [Comparing Interest Management Algorithms for Massively Multiplayer Games](https://www.sable.mcgill.ca/~clump/papers/boulanger-06-comparing.pdf) · ACM · Bài báo NetGames 2006 (bản công khai của tác giả). So sánh khoảng cách giữa mọi cặp không kham nổi khi số người tăng, chia lưới thì chỉ cần kiểm tra các ô xung quanh - [Replication Graph in Unreal Engine](https://dev.epicgames.com/documentation/en-us/unreal-engine/replication-graph-in-unreal-engine) · Epic Games · Chia thế giới thành lưới và chọn đối tượng cần gửi theo danh sách của từng ô thì dù nhiều người và actor vẫn tiết kiệm được CPU server - [Amdahl's Law in the Multicore Era](https://research.cs.wisc.edu/multifacet/papers/ieeecomputer08_amdahl_multicore.pdf) · IEEE · Trần thông lượng trong thí nghiệm lock: nếu tỷ lệ phần việc chỉ làm được từng cái một là 1−f thì mức tăng tốc không vượt quá 1/(1−f) - [Runtime locking correctness validator](https://docs.kernel.org/locking/lockdep-design.html) · Linux kernel · Deadlock trong thí nghiệm lock: hai lock bị giữ theo thứ tự ngược nhau thì chờ vòng tròn và deadlock - [Liveness, Readiness, and Startup Probes](https://kubernetes.io/docs/concepts/workloads/pods/probes/) · Kubernetes · Watchdog trong thí nghiệm lock: bắt trạng thái deadlock bằng kiểm tra liveness rồi khởi động lại; mặc định kiểm tra mỗi 10 giây, thất bại 3 lần liên tiếp thì khởi động lại (khoảng 30 giây) - [ASP.NET Core Best Practices](https://learn.microsoft.com/en-us/aspnet/core/fundamentals/best-practices) · Microsoft · Gọi đồng bộ: truy cập dữ liệu và I/O nên gọi bất đồng bộ, gọi blocking dẫn đến cạn thread pool và phản hồi chậm ### #l-infra - [Site Reliability Engineering, Chapter 22: Addressing Cascading Failures](https://sre.google/sre-book/addressing-cascading-failures/) · Google · Sự cố dây chuyền: backend chậm giữ chặt thread và tài nguyên ở tầng trước, sự cố lan rộng qua thử lại, health check thất bại và khởi động lại với cache trống; cùng các biện pháp xử lý - [Circuit Breaker Pattern](https://learn.microsoft.com/en-us/azure/architecture/patterns/circuit-breaker) · Microsoft Azure · Các trạng thái đóng, mở, nửa mở của circuit breaker và ngưỡng số lần thất bại; timeout dài thì thread bị giữ cho đến khi bị ngắt mạch - [Bulkhead Pattern](https://learn.microsoft.com/en-us/azure/architecture/patterns/bulkhead) · Microsoft Azure · Cô lập tài nguyên theo tính năng và theo đích gọi để sự cố ở một chỗ không lan sang chỗ khác - [Timeouts, retries, and backoff with jitter](https://d1.awsstatic.com/builderslibrary/pdfs/timeouts-retries-and-backoff-with-jitter.pdf) · AWS · Amazon Builders' Library. Dùng timeout để giải phóng tài nguyên, giới hạn số lần thử lại và thêm jitter - [The Unique Architecture behind Amazon Games’ Seamless MMO New World](https://aws.amazon.com/blogs/gametech/the-unique-architecture-behind-amazon-games-seamless-mmo-new-world/) · AWS · Ví dụ cấu trúc server MMO: server cổng vào, server mô phỏng theo từng ô lưới (hub), pool server dùng chung cho các phiên, DB lưu trạng thái - [Working with DB instance read replicas](https://docs.aws.amazon.com/AmazonRDS/latest/UserGuide/USER_ReadRepl.html) · AWS · Replication lag trong thí nghiệm sơ đồ kiến trúc: read replica được cập nhật bất đồng bộ nên có thể đọc phải dữ liệu cũ - [Site Reliability Engineering, Chapter 20: Load Balancing in the Datacenter](https://sre.google/sre-book/load-balancing-datacenter/) · Google · Triển khai và khởi động lại: chuyển sang trạng thái lame duck để đưa yêu cầu mới sang nơi khác rồi mới dừng, làm nóng (warm-up) ngay sau khi khởi động lại - [Amazon EC2 Auto Scaling lifecycle hooks](https://docs.aws.amazon.com/autoscaling/ec2/userguide/lifecycle-hooks.html) · AWS · Khi mở rộng hoặc thu hẹp, giữ instance ở trạng thái chờ để hoàn tất việc chuẩn bị và dọn dẹp (mặc định tối đa 1 giờ) - [systemd.service(5) — Linux manual page](https://man7.org/linux/man-pages/man5/systemd.service.5.html) · systemd · Watchdog trong thí nghiệm sơ đồ kiến trúc: dừng service đã mất tín hiệu còn sống và tự động khởi động lại ## Dạng đồ thị - **Vọt lên theo chu kỳ** (`periodic`): Bình thường ở mức thấp, rồi vọt lên đều đặn theo cùng một khoảng như vài giây, vài phút hay đúng mỗi đầu giờ. - **Thỉnh thoảng vọt lên bất chợt** (`random`): Vọt lên thất thường, không theo khoảng nào, rồi nhanh chóng trở lại. - **Tăng như bậc thang từ một thời điểm** (`step`): Tăng lên một bậc kể từ một thời điểm cụ thể như bản cập nhật, thay đổi cấu hình hay thay đổi tuyến đường, rồi giữ nguyên ở mức đó. - **Tăng dần** (`ramp`): Tăng từng chút trong nhiều giờ hoặc nhiều ngày. Càng chạy lâu càng lớn. - **Tăng dần rồi rơi thẳng** (`sawtooth`): Tăng chậm rồi rơi thẳng xuống vào lúc khởi động lại hoặc dọn dẹp, cứ thế lặp lại. - **Chỉ cao vào một khung giờ** (`peak`): Dâng lên như một ngọn đồi chỉ vào cùng một khung giờ trong ngày, như giờ cao điểm buổi tối. - **Tăng theo số người và tải** (`load`): Khi số người chơi đồng thời hoặc số người tụ ở một chỗ tăng lên, chỉ số tăng theo còn dốc hơn. - **Chạm giới hạn rồi đi ngang** (`ceiling`): Thông lượng hoặc số kết nối chạm một giá trị rồi không tăng thêm được, từ đó thời gian chờ và lỗi bắt đầu tăng. - **Luôn cao ngay từ đầu** (`high`): Luôn nằm ở mức cao, không vọt lên từng đợt. Đây là trường hợp do cấu trúc như khoảng cách, tuyến đường hay thiết kế. - **Chỉ một phần cao** (`outlier`): Phần lớn bình thường, riêng một số người chơi, khu vực, nhà mạng hay thiết bị cao hẳn. - **Đứt quãng rồi dồn về** (`gap`): Lượng nhận được về 0 trong một lúc rồi ào về cùng lúc. - **Rớt kết nối hàng loạt** (`drop`): Số kết nối tụt thẳng xuống hoặc số lần mất kết nối vọt lên trong chớp mắt. - **Tăng vọt ngay sau đăng nhập hoặc bảo trì** (`surge`): Vọt lên mạnh ngay sau khi mở server hoặc sự kiện bắt đầu, rồi lắng xuống dần. ## Quy trình theo tình huống ### Lag sau bản cập nhật Khi báo cáo lag tăng lên kể từ một bản cập nhật hoặc một lần triển khai cụ thể. Dùng khi các báo cáo kiểu “từ bản cập nhật này game có gì đó không ổn” dồn về, hoặc khi đồ thị tăng như bậc thang từ một thời điểm rồi giữ nguyên ở mức đó. 1. **Xác định thời điểm bắt đầu và gom mọi thay đổi quanh thời điểm đó**: Tìm thời điểm báo cáo bắt đầu dồn về và thời điểm đồ thị tăng lên như bậc thang, rồi ghi lại đầy đủ mọi thay đổi được đưa ra trước và sau thời điểm đó. Xem cùng lúc bản cập nhật client, triển khai server, thay đổi cấu hình, thay đổi schema DB (DDL) và khởi động lại, công việc về mạng và tường lửa, thay thế hạ tầng (loại instance, kernel, driver). Nếu mỗi lần triển khai đều dùng tính năng chú thích (annotation) của công cụ giám sát để đánh một vạch dọc trên mọi đồ thị, bước này sẽ xong rất nhanh. Nếu bản cập nhật game và công việc hạ tầng được đưa ra trong cùng một đợt bảo trì, giữ cả hai trong danh sách nghi vấn. Gọi ai trước: cả đội phát triển game và đội hạ tầng, tức các bên đã đưa ra thay đổi. (nguyên nhân: in-deploy, so-os-update, db-ddl-lock, db-plan-flip, db-cold-cache) 2. **Chia theo phạm vi: build, thiết bị, server, khu vực**: Xem bất thường tập trung ở chiều nào. Nếu chỉ người chơi dùng bản build mới có vấn đề, nghi ngờ client trước; nếu chỉ một số OS, card đồ họa hoặc thiết bị có vấn đề, nghi ngờ hiệu năng client hoặc driver; nếu chỉ một số server, kênh hoặc zone, nghi ngờ server; nếu chỉ một số quốc gia hoặc nhà mạng, nghi ngờ tuyến đường mạng; nếu tất cả cùng có vấn đề một lúc, nghi ngờ tài nguyên dùng chung (DB, bộ cân bằng tải, gateway) hoặc bản triển khai server vừa đưa ra. Nếu telemetry của client có số hiệu build, hãy đặt ping, FPS, số lần frame spike và số lần mất kết nối của build cũ và build mới cạnh nhau. Nếu ping không đổi mà chỉ FPS kém đi thì vấn đề nghiêng về hiệu năng client hơn là mạng. Gọi ai trước: dồn vào build hoặc thiết bị thì gọi đội phát triển game (client); dồn vào server hoặc kênh thì gọi đội phát triển game (server) nếu chỉ số host bình thường, đội hạ tầng (thiết bị server·OS) nếu bất thường; dồn vào quốc gia hoặc nhà mạng thì gọi đội hạ tầng (mạng). (nguyên nhân: cg-hitch, cg-sync-load, co-vram, cg-crash) 3. **So sánh phiên bản mới và phiên bản cũ trong cùng khung giờ**: Nếu chỉ so sánh trước và sau khi triển khai, các biến động theo ngày trong tuần, khung giờ và sự kiện sẽ lẫn vào, khiến khó kết luận. Nếu được, hãy đưa phiên bản mới lên một số server trước (canary), rồi so sánh song song với các server phiên bản cũ trong cùng khung giờ (nhóm đối chứng) về thời gian tick p50 và p99, số lần vượt tick budget, CPU, bộ nhớ và tỷ lệ lỗi. Nếu đã triển khai toàn bộ, hãy so sánh với cùng ngày, cùng khung giờ của tuần trước. Nếu chỉ nhìn trung bình của toàn bộ server, vấn đề ở một số server hoặc zone sẽ bị che lấp, nên hãy tách ra xem theo từng server và từng zone. Gọi ai trước: đội phát triển game (server). (nguyên nhân: sp-tick-overrun, mem-alloc, mem-leak, sp-broadcast) 4. **So sánh đặc trưng lưu lượng trước và sau**: Dù không biết code server, vẫn có thể dùng các giá trị nhìn thấy từ phía mạng để kiểm tra xem bản cập nhật có làm thay đổi hình dạng lưu lượng hay không. So sánh trước và sau: số gói tin mỗi giây (pps) và số byte trên mỗi người chơi, kích thước gói trung bình và tối đa, số kết nối, kích thước burst gửi dồn một lượt ở mỗi tick. Nếu gói UDP bắt đầu vượt MTU của tuyến đường (thường là 1.500 byte), sẽ xảy ra phân mảnh IP. Chỉ cần mất một fragment là mất cả gói, và có những NAT hoặc tường lửa bỏ hẳn fragment. Người chơi đi qua đoạn có MTU nhỏ ở giữa đường (tunnel, VPN) sẽ chỉ bị mất các gói lớn. Nếu pps tăng, hãy xem đã chạm giới hạn PPS của instance cloud hay giới hạn xử lý của tường lửa, thiết bị chống DDoS chưa. Gọi ai trước: nếu đặc trưng lưu lượng thay đổi, gửi kèm bằng chứng cho đội phát triển game (server); nếu đặc trưng giữ nguyên mà chỉ mất gói và truyền lại tăng, gọi đội hạ tầng (mạng). (nguyên nhân: sp-patch-traffic, sk-fragment, rt-mtu, nic-cloud-pps, rt-appliance-pps, rt-burst) 5. **So sánh loại và số lần chạy query DB trước và sau**: Nếu độ trễ DB tăng, trước tiên xem số query (QPS) có tăng theo không. pg_stat_statements của PostgreSQL hay bảng tổng hợp theo digest của MySQL Performance Schema gom các query chỉ khác nhau về giá trị thành một loại và cộng dồn số lần chạy cùng tổng thời gian. Vì vậy, so sánh danh sách query hàng đầu trước và sau bản cập nhật sẽ làm lộ ra các query mới xuất hiện, các query có số lần chạy tăng gấp nhiều lần (N+1) và các query đọc toàn bộ bảng mà không dùng index (với MySQL là cột SUM_NO_INDEX_USED). Gọi ai trước: nếu QPS hoặc dạng query thay đổi thì gọi đội phát triển game (server); nếu query giữ nguyên mà chỉ độ trễ tăng thì gọi đội hạ tầng (DB: kế hoạch thực thi, IOPS, khóa). (nguyên nhân: db-no-index, db-login-storm, db-plan-flip, db-cache-stampede) 6. **Dùng chỉ số của host và tiến trình server để phân định tầng**: Không cần code, dùng các giá trị nhìn thấy từ OS để phân định vấn đề nằm bên trong server hay ở host. Nếu hàng đợi nhận (Recv-Q) của socket server dồn lên, tiến trình server đang không đọc kịp (tick bị dừng, GC, lock); nếu chỉ một thread chạy 100%, đó là điểm nghẽn ở một thread duy nhất; nếu thời gian dừng trong log GC tăng, cách dùng bộ nhớ đã thay đổi. Kiểm tra thêm xem có phải bản triển khai đã để log level cao khiến lượng ghi log tăng lên không. Ngược lại, nếu CPU steal, throttling hoặc số gói bị NIC bỏ (drop) tăng, hãy xem phần hạ tầng đã thay đổi cùng thời điểm (loại instance, kernel, giới hạn container). Gọi ai trước: tín hiệu bên trong tiến trình thì gọi đội phát triển game (server), tín hiệu ở host thì gọi đội hạ tầng (thiết bị server·OS). (nguyên nhân: mem-gc, sp-hotzone, dk-sync-log, so-cpu-quota, so-steal, so-os-update) 7. **Hoàn tác để xác nhận và ghi lại kết quả**: Hoàn tác thay đổi đáng nghi nhất chỉ trên một số server hoặc một nhóm người chơi (rollback, tắt feature flag), hoặc đưa cấu hình về giá trị cũ, rồi xem triệu chứng có biến mất theo không. Nếu chỉ phía đã hoàn tác được cải thiện thì nguyên nhân được xác nhận. Bản thân việc hoàn tác cũng có thể gây chậm trong chốc lát do khởi động lại và cache lạnh, nên nếu không gấp, hãy làm vào khung giờ vắng người. Ghi kết quả vào hồ sơ sự cố kèm ID nguyên nhân, và đưa các ngưỡng về kích thước gói, số query và thời gian tick vào danh mục kiểm tra trước khi triển khai bản cập nhật tiếp theo. Gọi ai trước: đội đã đưa ra thay đổi. (nguyên nhân: in-deploy, db-cold-cache) ### Mở thêm quốc gia hoặc khu vực ở nước ngoài Khi mở dịch vụ ở một quốc gia mới hoặc thêm region, trung tâm dữ liệu mới. Dùng cho cả việc kiểm tra trước khi ra mắt và việc phân định các báo cáo kiểu “trong nước thì ổn, chỉ người chơi ở quốc gia mới bị lag”. 1. **Đo chất lượng tuyến đường của từng nhà mạng địa phương trước khi ra mắt**: Với từng nhà mạng chính (ASN) của quốc gia mục tiêu, đo phân bố thời gian khứ hồi (RTT), jitter (độ dao động của khoảng cách giữa các lần gói tin đến) và tỷ lệ mất gói đến các vị trí ứng viên đặt server game. Một con số trung bình duy nhất sẽ che mất khác biệt giữa các nhà mạng, nên hãy xem trung vị và phân vị 95 của từng nhà mạng, tách riêng giờ cao điểm buổi tối và rạng sáng. Mạng đo lường công khai RIPE Atlas cho phép chọn quốc gia hoặc ASN rồi gửi ping, traceroute từ các probe trên toàn thế giới; cũng có thể dựng VM tạm thời ở region ứng viên để đo. Thiết bị trung gian đôi khi giới hạn tốc độ phản hồi ICMP, nên nếu được, hãy đo thêm bằng đúng giao thức và cổng mà game dùng. Nếu riêng một nhà mạng đi qua thành phố xa bất thường, đó là vấn đề peering hoặc tuyến đường. Nhà mạng ưu tiên chi phí hơn độ trễ khi chọn tuyến, nên cả điểm đến ở gần cũng có thể bị đi vòng xa. Gọi ai trước: đội hạ tầng (mạng); nếu tuyến đường nằm ở phía nhà mạng thì gọi bên ngoài (nhà mạng, IX). (nguyên nhân: isp-distance, isp-routing, isp-peak, isp-cable) 2. **So sánh số đo với giới hạn mà thiết kế game chịu được**: So sánh RTT và jitter đo được với khung phán định của game (thời gian phản ứng cho các thao tác như né, đỡ đòn), giới hạn bù trễ, độ dài bộ đệm nội suy và kích thước bộ đệm input. Ví dụ, nếu khung phán định đỡ đòn là 0,2 giây, thì với người dùng của những nhà mạng mà độ trễ khứ hồi cộng với bộ đệm nội suy vượt quá mức đó, dù phản ứng đúng lúc vẫn bị tính là trễ. Nếu nới rộng bù trễ để bù lại, lần này phía bị đánh trúng sẽ báo cáo nhiều hơn rằng “đã núp sau tường rồi mà vẫn trúng đòn”. Nếu có nhiều nhà mạng vượt giới hạn, đội hạ tầng xem xét phương án đặt region hoặc edge PoP gần hơn, còn đội phát triển game xem lại các giá trị phán định, nội suy và bù trễ. Bảng tham chiếu nằm trong chương về cơ chế đồng bộ của sách trắng này. Gọi ai trước: đội phát triển game (server và client: giới hạn thiết kế), đội hạ tầng (mạng: vị trí region và PoP). (nguyên nhân: sy-short-window, sy-no-lagcomp, sy-lagcomp-overreach, cg-no-buffer) 3. **Kiểm tra MTU và UDP có đi qua được không**: Kiểm tra xem gói tin lớn nhất của game có đi qua mạng địa phương nguyên vẹn không. Gửi ping có cờ cấm phân mảnh (DF) với nhiều kích thước khác nhau để đo MTU của tuyến đường, và xem có đoạn nào nhỏ hơn 1.500 byte như PPPoE, tunnel hay mạng di động không. Tiêu chuẩn cho truyền datagram như UDP (RFC 8899) khuyến nghị 1.200 byte làm kích thước cơ bản đi qua được hầu hết các tuyến trên IPv4, nên nếu gói lớn nhất của game vượt mức này, hãy thống nhất với đội phát triển game phương án thu nhỏ hoặc chia nhỏ gói. Kiểm tra thêm xem Wi-Fi công cộng, mạng công ty hoặc một số nhà mạng có chặn hay giới hạn tốc độ UDP hoặc cổng của game không, và xem đã có đường thay thế (TCP, cổng 443) để dùng khi bị chặn chưa. Gọi ai trước: đội hạ tầng (mạng) và đội phát triển game (server: kích thước gói). (nguyên nhân: dc-mtu, rt-mtu, sk-fragment, isp-udp-block, hn-captive, isp-shaping) 4. **Đo idle timeout của NAT và CGNAT, rồi chỉnh khoảng cách heartbeat cho phù hợp**: Đo xem router gia đình và mạng di động (CGNAT) ở địa phương xóa ánh xạ (mapping) của kết nối UDP nhàn rỗi sau bao lâu. Trong mỗi lần thử, cho thiết bị thử nghiệm gửi một gói tới server để tạo ánh xạ, sau đó thiết bị không gửi gì nữa, còn server gửi một gói về thiết bị sau một khoảng thời gian định sẵn (30 giây, 60 giây, 120 giây …). Khoảng thời gian mà từ đó thiết bị bắt đầu không nhận được gói chính là idle timeout của mạng đó. Tiêu chuẩn (RFC 4787) quy định ánh xạ UDP không được hết hạn trước 2 phút và khuyến nghị mặc định từ 5 phút trở lên, nhưng giá trị thực tế khác nhau rất nhiều giữa các thiết bị, có thiết bị xóa sớm hơn. Ánh xạ chỉ chắc chắn được làm mới bằng gói đi ra từ thiết bị, nên heartbeat phải do client gửi; hãy kiểm tra khoảng cách heartbeat có bằng hoặc nhỏ hơn một nửa giá trị ngắn nhất trong số: giá trị đo được, idle timeout của bộ cân bằng tải và của security group trên cloud. Gọi ai trước: đội phát triển game (client: khoảng cách heartbeat; server: giá trị timeout), đội hạ tầng (cấu hình bộ cân bằng tải, security group). (nguyên nhân: hn-nat, isp-cgnat, rt-mapping, dc-lb-idle, dc-cloud-conntrack) 5. **Kiểm tra dịch vụ bên ngoài và thiết bị bảo mật mà lưu lượng từ địa phương đi qua**: Kiểm tra xem đăng nhập qua nền tảng địa phương, thanh toán và xác minh danh tính có phản hồi đúng tốc độ không; DNS địa phương có phân giải đúng địa chỉ server đăng nhập và server bản cập nhật không; CDN có phân phối bản cập nhật từ PoP gần quốc gia đó không. Xem dải IP của quốc gia mới có bị dính vào quy tắc chặn theo quốc gia và giới hạn tốc độ của hệ thống chống DDoS, tường lửa không, đặc biệt là dải CGNAT (nơi nhiều thuê bao dùng chung một IP) có bị chặn cả loạt không. Gọi ai trước: đội hạ tầng (thiết bị bảo mật, DNS, CDN), bên ngoài (nền tảng, đơn vị thanh toán, nhà mạng). (nguyên nhân: in-external, isp-dns, dc-ddos, isp-cgnat) 6. **Sau khi ra mắt, tách số liệu theo quốc gia và ASN**: Gắn quốc gia và ASN vào IP client trong log kết nối và log của bộ cân bằng tải, rồi xem RTT, truyền lại, số lần mất kết nối và lý do mất kết nối (heartbeat timeout, RST, server kick) theo từng quốc gia và nhà mạng. Có thể dùng cơ sở dữ liệu miễn phí như MaxMind GeoLite ASN để chuyển IP thành ASN và tên tổ chức; khi lưu, hãy rút gọn IP về mức /24 hoặc ASN cho phù hợp với quy định về dữ liệu cá nhân của nước sở tại. Nếu chỉ dồn vào một ASN, xem trước tuyến đường của nhà mạng đó (đội hạ tầng, bên ngoài); nếu cả quốc gia mới đều có vấn đề, xem khoảng cách và giới hạn thiết kế (đội hạ tầng, đội phát triển game); nếu chỉ xấu đi vào buổi tối, xem tắc nghẽn peering. Nếu chỉ một số người chơi lúc nào cũng có ping cao, cùng đội phát triển game (server) kiểm tra xem họ có bị xếp vào region xa do GeoIP sai, VPN hoặc do xếp theo vị trí của đội trưởng tổ đội không. Nếu giám sát giả lập vẫn bình thường mà chỉ phía người chơi có vấn đề, vấn đề nằm ở môi trường người chơi hoặc phía client. (nguyên nhân: isp-peak, isp-routing, rt-queue-drop, pt-isp-validation, in-region-match) 7. **Kiểm tra ảnh hưởng của người chơi ở xa lên những người chơi khác**: Khi số người chơi kết nối từ xa tăng lên, ảnh hưởng không chỉ dừng lại ở màn hình của chính họ. Input của người chơi có kết nối chậm đến dồn một lượt, khiến trên màn hình người khác, riêng nhân vật đó bị tua nhanh; đồng thời nhân vật bị server bắt lỗi ở bước kiểm tra tốc độ và hồi chiêu, gây kéo ngược hoặc skill bị từ chối. Với các cơ chế (gimmick) đòi hỏi cả tổ đội phối hợp, phản ứng muộn của một người chậm trở thành thất bại của cả tổ đội; còn với cơ chế lockstep, mọi người phải chờ người chậm nhất. Sau khi ra mắt ở quốc gia mới, hãy xem các báo cáo kiểu “chỉ một nhân vật trông bất thường” từ người chơi hiện có có tăng lên không, rồi cùng đội phát triển game quyết định về bộ đệm input, ngưỡng dung sai khi kiểm tra và việc tách khu vực ghép trận. Gọi ai trước: đội phát triển game (server). (nguyên nhân: pt-slow-burst, pt-isp-validation, pt-raid-member, sy-lockstep) ## Sự cố thực tế ### eve-hedgp-2014 · CCP Games 2014: EVE Online: server quá tải trong trận hạm đội quy mô lớn tại HED-GP - Chuyện gì xảy ra: Trong trận hạm đội quy mô lớn tại hệ sao HED-GP, được phân tích trong bài tổng kết tháng 1 năm 2014, server bị quá tải nặng. Time Dilation (tính năng làm chậm thời gian trong game khi quá tải) đã chạm mức sàn 10% và cả chiến trường chuyển sang quay chậm, nhưng tải vẫn tiếp tục dồn lên. Mức tồn đọng của tác vụ xử lý việc dừng và kích hoạt lặp lại của các module (Dogma Lateness) lên tới tối đa 193 giây thời gian trong game, tức khoảng 32 phút thời gian thực. Trận 6VDT vào tháng 7 năm 2013, với quy mô gần như tương đương, đạt tối đa 42 giây (khoảng 7 phút thực). - Nguyên nhân: CCP nói trước rằng họ không thể chắc chắn: công cụ phân tích hiệu năng tự nó cũng làm tăng tải, nên họ không chạy công cụ này trong tình huống như vậy. Sau đó CCP nêu hai nguyên nhân nhiều khả năng nhất. Thứ nhất, trận đánh kéo dài nên phần tải chưa xử lý kịp cứ tiếp tục dồn lại. Thứ hai, drone được dùng nhiều hơn: số drone được triển khai trong trận (không tính trùng) là 21.123 ở 6VDT và 38.852 ở HED-GP, tức nhiều hơn 84%. Việc thông báo hành động của một người cho tất cả những ai nhìn thấy người đó làm lượng dữ liệu gửi đi tăng theo bình phương số người (O(n²)), và mỗi lần tấn công, drone tạo ra nhiều message hơn. Code chọn mục tiêu tấn công của drone cũng thường xuyên duyệt toàn bộ các mục tiêu có thể tấn công trong cùng chiến trường, nên chi phí tăng gần như n². - Bài học: Khi lượng xử lý ở một khu vực đông người vượt quá giới hạn, cả khu vực đó rơi vào quay chậm, và trận đánh càng kéo dài thì phần xử lý tồn đọng càng nhiều, khiến trễ thao tác càng lớn. Tín hiệu cần kiểm tra là thời gian tick và lượng tác vụ tồn đọng của server (node) phụ trách khu vực đó, cùng với số người và số đối tượng; đặc điểm nhận biết là các khu vực khác vẫn bình thường. Bên phụ trách chính là đội phát triển game (server), và chỗ cần sửa là phạm vi đối tượng nhận thông báo cho mỗi hành động và chi phí tìm mục tiêu của AI. Thiết kế làm chậm thời gian trong game không xóa được tình trạng quá tải, nhưng nó khiến mọi người cùng chậm đi với một tốc độ, nhờ đó tránh được việc chỉ một số hành động bị trễ mãi không dứt. - Nguyên nhân liên quan: sp-broadcast, sp-tick-overrun, sp-queue, sp-hotzone - Bài gốc: [CCP Games](https://www.eveonline.com/news/view/what-a-hed-ache) ### riot-direct-2015 · Riot Games 2015: Lưu lượng League of Legends đi đường vòng xa và Riot Direct - Chuyện gì xảy ra: Đây là bài viết kỹ thuật trong đó Riot Games giải thích vì sao Internet không phù hợp với game thời gian thực. Lưu lượng thực tế do một người chơi League of Legends báo cáo lẽ ra phải đi thẳng từ San Francisco đến Portland, nhưng lại đi qua Los Angeles, Denver và Seattle, nên mất 70 ms, trong khi đi thẳng chỉ cần 14 ms. Riot giải thích rằng khi router bị tràn và gói tin bị bỏ, các tướng khác trên màn hình sẽ nhảy lung tung, còn đạn và chiêu bay (projectile) trông như dịch chuyển tức thời. - Nguyên nhân: Riot chỉ ra nguyên nhân nằm ở tuyến đường và router. Các nhà cung cấp mạng trục (backbone) và nhà mạng ưu tiên đưa lưu lượng qua tuyến rẻ nhất hơn là tuyến có độ trễ thấp nhất, và khi tuyến đường do BGP chọn đi vòng xa thì số router phải đi qua cũng tăng lên. Router chịu tải xử lý theo số lượng gói tin, bất kể kích thước gói. Gói tin game chỉ khoảng 55 byte, nên với cùng một lượng dữ liệu, số gói gấp 27 lần so với gói 1.500 byte và làm đầy bộ đệm đầu vào của router nhanh hơn tương ứng. Theo Riot, nhiều router khi quá tải sẽ bỏ gói UDP trước tiên. Để giải quyết, Riot xây dựng mạng riêng Riot Direct: đặt router tại 10 điểm trung chuyển Internet lớn ở Mỹ và kết nối trực tiếp (peering) với càng nhiều nhà mạng càng tốt. Theo phần 2 của loạt bài, tỷ lệ người chơi có ping dưới 80 ms tăng từ 31% lên 50% trong hơn 9 tháng, và lên 80% chỉ sau một đêm khi server game được chuyển đến Chicago. - Bài học: Nếu ngay trong cùng một quốc gia mà chỉ người dùng của một nhà mạng có ping cao bất thường, hãy nghi ngờ tuyến đường. Tín hiệu cần kiểm tra là phân bố RTT theo từng nhà mạng (ASN) và các thành phố trung chuyển hiện ra trong traceroute. Bên phụ trách chính là đội hạ tầng (mạng), khắc phục bằng peering trực tiếp với nhà mạng, kết nối IX và chọn vị trí đặt server. Chính sách định tuyến phía nhà mạng thì cần trao đổi với bên ngoài (nhà mạng). Trường hợp này cũng cho thấy chỉ riêng việc chuyển server về gần trung tâm phân bố người chơi đã mang lại hiệu quả lớn. - Nguyên nhân liên quan: isp-routing, isp-distance, rt-queue-drop - Bài gốc: [Riot Games](https://www.riotgames.com/en/news/fixing-internet-real-time-applications-part-i) ### riot-edge-2020 · Riot Games 2020: League of Legends: host edge của server châu Âu và Brazil bị quá tải - Chuyện gì xảy ra: Cuối tháng 2 năm 2020, các server EUW, EUNE và BR của League of Legends gặp sự cố nhiều lần, khiến số trận mới bắt đầu giảm mạnh. Các dịch vụ backend như ghép trận và server game đều báo trạng thái bình thường, nhưng gần như không có lưu lượng đi vào. Riot đã lùi lịch một tuần để không mở chế độ giải đấu (Clash) trên cụm server có thể thiếu ổn định. Bài tổng kết không ghi mỗi sự cố kéo dài bao lâu. - Nguyên nhân: Ba yếu tố chồng lên nhau. Thứ nhất, yêu cầu gửi tới một dịch vụ được tạo sai, nên trong một số trường hợp nhất định cứ thất bại rồi lại bị thử lại liên tục, làm số yêu cầu tăng vọt. Thứ hai, một lỗi tương thích đã biết giữa hệ thống container và phiên bản OS khiến bộ nhớ bên trong OS bị rò rỉ; việc nâng cấp mới chỉ hoàn tất trên khoảng 60% toàn bộ môi trường container của Riot, còn các cụm châu Âu và Mỹ Latinh vẫn đang nâng cấp dở. Thứ ba, các container edge (nhận lưu lượng từ Internet, lọc rồi chuyển vào backend) được bố trí tách nhau trong cùng một shard (nhóm server), nhưng giữa các shard khác nhau thì không có ràng buộc như vậy, nên mỗi lần sự cố đều có container edge của ít nhất ba shard dồn trên một host. Lượng thử lại tăng vọt đổ dồn vào host đó, và rò rỉ bộ nhớ làm host đó ngừng hoạt động. - Bài học: Khi mọi dịch vụ phía sau đều báo “bình thường nhưng không có lưu lượng đi vào”, hãy xem lớp phía trước (edge, gateway, bộ cân bằng tải). Tín hiệu cần kiểm tra là số kết nối inbound dồn lệch vào một số host và tỷ lệ thất bại, thử lại của một loại yêu cầu cụ thể. Bên phụ trách chính là đội phát triển game (server: yêu cầu bị tạo sai và cách thử lại), còn quy tắc bố trí container, nâng cấp OS và cảnh báo phân bố lệch do đội hạ tầng (thiết bị server·OS) đảm nhận. Riot đã sửa code tạo yêu cầu, thay đổi để lượt thử lại không tăng vọt, và đặt cảnh báo phân bố lệch cho đến khi triển khai được việc phân tán giữa các shard. - Nguyên nhân liên quan: in-gateway, in-cascade - Bài gốc: [Riot Games](https://www.leagueoflegends.com/en-gb/news/riot-games/incident-report-recent-outages-in-europe-brazil/) ### riot-euw-2021 · Riot Games 2021: Sự cố 5 giờ ở League of Legends EUW: một DB phụ làm tê liệt toàn bộ server - Chuyện gì xảy ra: Ngày 22 tháng 1 năm 2021, server EUW của League of Legends không hoạt động bình thường trong thời gian nhỉnh hơn 5 giờ. Hai chỉ số là số người chơi đã đăng nhập và số người chơi đang trong trận cùng lúc bị đứt, và giữa hai lần khởi động lại, số lượt đăng nhập tăng nhưng gần như không có trận nào bắt đầu. - Nguyên nhân: Server chính của một DB phục vụ tính năng không quan trọng bị hỏng phần cứng, và DB đó không được cấu hình tự động chuyển sang server dự phòng (failover). Mỗi DB có connection pool riêng, nhưng mọi pool đều dùng chung một thread pool; các tác vụ gửi tới DB bị hỏng không kết thúc mà cứ chiếm giữ thread, khiến cả hệ thống không còn thread để dùng. Giữa lúc cảnh báo dồn dập, Riot nghi ngờ trước tiên cuộc tấn công mạng ác ý vừa gặp gần đây và một đợt thao tác phần cứng ở khu vực khác, nên phải khoảng 1 giờ sau mới để ý đến cảnh báo của DB bị hỏng. Do mọi hệ thống chạy trong cùng một JVM, khi GC dừng tiến trình mỗi lần vài giây giữa lúc tải kết nối lại dồn đến sau khi khởi động lại, việc thu thập chỉ số cũng bị gián đoạn một khoảng dài. Hàng chờ đăng nhập cũng không giữ đúng giới hạn đã cấu hình nên lượng người vào lúc nhiều lúc ít. - Bài học: Ngay cả một DB phụ bị coi là không quan trọng cũng có thể làm ngừng toàn bộ hệ thống thông qua tài nguyên dùng chung như thread pool. Tín hiệu cần kiểm tra là số yêu cầu đang chờ của từng DB, mức sử dụng thread pool, và tỷ lệ số trận bắt đầu quá thấp so với số lượt đăng nhập. Bên phụ trách là đội phát triển game (server: cô lập thread pool, timeout) và đội hạ tầng (DB: failover tự động). Khi cảnh báo dồn dập, người ta dễ nghi ngờ trước tiên những vấn đề vừa gặp gần đây (như tấn công), nên hãy loại trừ lần lượt theo trình tự chẩn đoán (Phạm vi → Thời điểm → Tầng). Sau khi khởi động lại, kiểm tra thêm xem hàng chờ đăng nhập có giới hạn lượng người vào đúng như cấu hình không. - Nguyên nhân liên quan: sp-threadpool, db-failover, in-cascade, mem-gc - Bài gốc: [Riot Games](https://www.riotgames.com/en/news/keeping-legacy-software-alive-case-study) ### roblox-2021 · Roblox 2021: Sự cố 73 giờ của Roblox: tranh chấp trong cụm service discovery (Consul) - Chuyện gì xảy ra: Chiều ngày 28 tháng 10 năm 2021 (giờ Thái Bình Dương), sự cố bắt đầu từ tải CPU cao trên một server Consul. Lúc 16:35, số người chơi đang kết nối giảm còn một nửa so với bình thường, rồi toàn bộ dịch vụ ngừng hoạt động. Phải đến 16:45 ngày 31 tháng 10, mọi người chơi mới vào lại được, tức mất 73 giờ kể từ khi sự cố bắt đầu. Roblox cho biết mỗi ngày có 50 triệu người sử dụng nền tảng này. - Nguyên nhân: Roblox dùng HashiCorp Consul cho service discovery (tính năng để các dịch vụ tìm địa chỉ của nhau), health check và kho KV, và một cụm Consul duy nhất gánh cùng lúc nhiều workload. Có hai nguyên nhân gốc. Thứ nhất, tính năng streaming mới của Consul (đã được mở rộng dần trong nhiều tháng) được bật thêm cho dịch vụ định tuyến lưu lượng vào ngày trước sự cố, đồng thời số node của dịch vụ này tăng 50%. Dưới tải đọc và ghi đều rất lớn, tính năng này gây tranh chấp trên một tài nguyên dùng chung (một Go channel). Trên các server hai socket (NUMA) có nhiều core hơn được thay vào giữa sự cố, tranh chấp còn nặng hơn. Thứ hai, việc quản lý danh sách trang trống (freelist) của BoltDB, nơi Consul lưu log Raft, trở nên chậm bất thường: mỗi lần thêm dữ liệu từ 16 kB trở xuống lại ghi 7,8 MB xuống ổ đĩa. Trung vị độ trễ ghi KV, bình thường dưới 300 ms, lên tới 2 giây, và trên server leader bị chậm còn quan sát thấy zero window (bộ đệm TCP đầy). Vì hệ thống telemetry phụ thuộc vào Consul, các chỉ số cần để tìm nguyên nhân cũng mất theo. - Bài học: Khi một hệ thống nền tảng mà nhiều dịch vụ cùng phụ thuộc (service discovery, kho cấu hình, xác thực) bị chậm, mọi tính năng ngừng cùng một lúc. Tín hiệu cần kiểm tra là độ trễ ghi, số lần đổi leader và CPU của hệ thống đó, cùng với các thay đổi cấu hình ngay trước sự cố. Bên phụ trách gồm cả đội phát triển game (server) và đội hạ tầng (thiết bị server·OS). Hệ thống giám sát phải được tách riêng, không phụ thuộc vào chính hệ thống nó giám sát, thì khi có sự cố mới vẫn xem được chỉ số. Khi phục hồi, cache đang trống nên nếu nhận người chơi vào cùng lúc thì hệ thống có thể sập lại; vì vậy Roblox dùng DNS để điều chỉnh tỷ lệ người chơi được vào, tăng dần mỗi lần khoảng 10%. - Nguyên nhân liên quan: in-cascade, sp-lock, rt-zero-window, db-cold-cache - Bài gốc: [Roblox](https://about.roblox.com/newsroom/2022/01/roblox-return-to-service-10-28-10-31-2021) ### ffxiv-2021 · Square Enix 2021: FINAL FANTASY XIV: tình trạng quá đông khi ra mắt bản mở rộng và lỗi hàng chờ đăng nhập - Chuyện gì xảy ra: Từ đợt early access của bản mở rộng Endwalker vào tháng 12 năm 2021, các world đều đông nghẹt. Hàng chờ đăng nhập kéo dài, và Error 2002 xuất hiện thường xuyên khi đăng nhập từ màn hình chọn nhân vật hoặc trong lúc đang chờ. Ngoài ra còn có tình trạng một số world và zone bị sập (Error 3001) và hàng chờ bị timeout (Error 4004). Đến thời điểm thông báo ngày 11 tháng 12, tức ngày thứ 8 của early access, tình trạng đông nghẹt vẫn tiếp diễn. - Nguyên nhân: Error 2002 xảy ra trong hai trường hợp. Trường hợp thứ nhất là khi số người chờ ở mỗi trung tâm dữ liệu logic vượt 17.000. Đây là giới hạn được đặt ra để hàng chờ không dài đến mức làm sập server đăng nhập, và khi đó client bị thoát hẳn. Ngày 7 tháng 12, Square Enix đưa thiết bị dự phòng dành cho phát triển vào làm server lobby để nâng giới hạn này; lỗi giảm đi nhưng hàng chờ lại càng dài hơn. Trường hợp thứ hai là khi đường truyền của người chơi đang chờ không ổn định. Thời gian chờ càng dài thì càng nhiều lần kết nối bị ngắt trong chốc lát do mất gói trên tuyến Internet hoặc Wi-Fi chập chờn. Server lobby chờ kết nối lại trong khoảng vài chục giây đến 1 phút; nếu kết nối lại trong khoảng đó, người chơi được tiếp tục từ vị trí cũ trong hàng chờ, còn nếu quá thời gian thì phải xếp lại từ cuối. Square Enix cho biết phần lớn báo cáo thuộc trường hợp này. Do thiếu chip bán dẫn, họ cũng không thể tăng số world ngay được. - Bài học: Hàng chờ càng dài thì những lần đứt kết nối ngắn của người chơi đang chờ càng dễ biến thành lỗi kết nối. Dù cùng một mức quá tải, lỗi lại dồn vào những người dùng Wi-Fi hoặc đường truyền không ổn định, nên đây trở thành vấn đề “chỉ một số người gặp”. Tín hiệu cần kiểm tra là độ dài hàng chờ, thời gian chờ, và tỷ lệ ngắt kết nối trong lúc chờ trong số các lý do ngắt kết nối. Bên phụ trách chính là đội phát triển game (server: giới hạn hàng chờ và thời gian giữ chỗ khi kết nối lại), còn việc tăng thêm server lobby và server world do đội hạ tầng cùng thực hiện. Đặt thời gian giữ chỗ khi kết nối lại đủ dài sẽ giúp những lần đứt kết nối ngắn trên đường truyền của người chơi ít dẫn tới mất lượt chờ hơn. - Nguyên nhân liên quan: in-login-queue, hn-wifi, rt-wireless - Bài gốc: [Square Enix](https://na.finalfantasyxiv.com/lodestone/news/detail/6a94b30182b6d963994fdc0b789264ac9f24986f) ### cloudflare-2020 · Cloudflare 2020: Cloudflare: lỗi cấu hình backbone làm mất lưu lượng ở một số thành phố - Chuyện gì xảy ra: Nhiều game giao việc phục vụ web, API và chống DDoS cho nhà cung cấp CDN, nên đây là kiểu sự cố hạ tầng mà game cũng bị ảnh hưởng theo. Từ 21:12 đến 21:39 (UTC) ngày 17 tháng 7 năm 2020, trong 27 phút, tổng lưu lượng trên mạng Cloudflare giảm khoảng 50%. Ảnh hưởng chỉ giới hạn ở các điểm hiện diện (PoP) tại một số thành phố ở Mỹ, châu Âu, Nga và Brazil có kết nối vào backbone; các PoP khác vẫn bình thường. - Nguyên nhân: Sự cố trên đoạn backbone Newark–Chicago làm đoạn Atlanta–Washington bị nghẽn, nên một kỹ sư đã sửa cấu hình router để giảm bớt lưu lượng backbone đi qua Atlanta. Lẽ ra phải tắt toàn bộ mục chính sách (term), nhưng kỹ sư chỉ tắt điều kiện bên trong nó (prefix-list), khiến router Atlanta quảng bá mọi tuyến BGP với mức ưu tiên cao hơn (local-preference 200) ra toàn bộ backbone. Mức ưu tiên mà mỗi PoP đặt cho tuyến đến server của chính nó là 100, nên toàn bộ lưu lượng của các PoP nối vào backbone dồn về Atlanta. Atlanta bị quá tải, còn các PoP bị ảnh hưởng gần như không còn lưu lượng để xử lý. Hệ thống phục hồi sau khi router Atlanta được tách khỏi backbone. Cloudflare cho biết sự cố không liên quan đến tấn công hay xâm nhập. - Bài học: Nếu chỉ người chơi ở một thành phố hoặc khu vực cùng lúc bị mất kết nối hoặc không vào được·kẹt loading, còn những nơi khác vẫn bình thường, hãy nghi ngờ trước tiên thay đổi cấu hình định tuyến vừa thực hiện. Trên đồ thị, CPU và lưu lượng chỉ vọt lên ở một PoP, còn các PoP bị ảnh hưởng thì ngược lại rơi xuống gần 0. Bên phụ trách chính là đội hạ tầng (mạng); nếu sự cố nằm ở phía nhà cung cấp thì bên phụ trách chính là bên ngoài. Cloudflare quyết định đặt giới hạn số tuyến có thể nhận (maximum-prefix) trên các phiên BGP của backbone, và điều chỉnh mức ưu tiên để một PoP không thể kéo lưu lượng của PoP khác về mình. - Nguyên nhân liên quan: isp-bgp, rt-path - Bài gốc: [Cloudflare](https://blog.cloudflare.com/cloudflare-outage-on-july-17-2020/) ### fastly-2021 · Fastly 2021: Fastly: lỗi CDN trên toàn cầu - Chuyện gì xảy ra: Nhiều game phân phối file bản cập nhật, launcher và trang web qua CDN, nên đây là kiểu sự cố hạ tầng mà game cũng bị ảnh hưởng theo. 85% mạng Fastly trả về lỗi từ 09:47 (UTC) ngày 8 tháng 6 năm 2021. Trong vòng 49 phút, 95% mạng đã hoạt động bình thường trở lại, và sự cố được khắc phục xong lúc 12:35. - Nguyên nhân: Bản phần mềm bắt đầu triển khai từ ngày 12 tháng 5 chứa một lỗi, chỉ kích hoạt khi một cấu hình khách hàng cụ thể gặp một điều kiện cụ thể. Ngày 8 tháng 6, một khách hàng đẩy lên một thay đổi cấu hình hoàn toàn hợp lệ, và điều kiện đó khớp đúng. Fastly phát hiện bất thường trong vòng 1 phút; sau khi tìm ra và tắt cấu hình khách hàng gây lỗi, hệ thống bắt đầu phục hồi. Việc triển khai bản sửa lỗi bắt đầu lúc 17:25 cùng ngày. - Bài học: Ngay cả code đã triển khai được vài tuần, khi gặp một điều kiện hiếm cũng có thể lập tức gây sự cố toàn cầu. Ở phía game, tín hiệu cần kiểm tra là tỷ lệ lỗi HTTP của các yêu cầu tải bản cập nhật, launcher và web cùng tăng ở mọi khu vực, và trang trạng thái của nhà cung cấp CDN. Đặc điểm nhận biết là các kết nối game đã vào sẵn vẫn bình thường nếu không đi qua CDN, chỉ có kết nối mới, tải bản cập nhật và đăng nhập web bị chặn. Bên phụ trách chính là bên ngoài (nhà cung cấp CDN); đội phát triển game và đội hạ tầng nên chuẩn bị sẵn đường vòng như dùng từ hai CDN trở lên hoặc tải thẳng từ server gốc (origin). - Nguyên nhân liên quan: in-external - Bài gốc: [Fastly](https://www.fastly.com/blog/summary-of-june-8-outage) ### meta-2021 · Meta 2021: Facebook: một lệnh trên backbone làm biến mất cả DNS - Chuyện gì xảy ra: Đây là sự cố hạ tầng có thể xảy ra y hệt với mạng riêng và DNS của công ty game. Ngày 4 tháng 10 năm 2021, các dịch vụ của Facebook (nay là Meta) không truy cập được trên toàn thế giới. Toàn bộ backbone nối các trung tâm dữ liệu bị đứt, và từ phía Internet không còn tìm thấy server DNS của Facebook. Bài tổng kết không ghi sự cố kéo dài bao lâu. - Nguyên nhân: Trong một đợt bảo trì định kỳ, một lệnh chạy để kiểm tra dung lượng backbone toàn cầu đã vô tình cắt mọi kết nối của backbone, còn công cụ audit lẽ ra phải chặn những lệnh như vậy thì không chặn được vì có lỗi. Server DNS ở các cơ sở quy mô nhỏ được thiết kế để tự coi mình là bất thường và rút quảng bá BGP khi không liên lạc được với trung tâm dữ liệu, nên dù server DNS vẫn chạy, từ Internet không thể truy cập tới chúng. Cả đường truy cập thông thường lẫn đường truy cập ngoài băng (out-of-band) đều bị đứt, công cụ nội bộ cũng mất DNS, nên phải cử kỹ sư đến tận trung tâm dữ liệu, và quy trình bảo mật khiến việc này mất thêm thời gian. Khi phục hồi, điện năng tiêu thụ ở mỗi trung tâm dữ liệu đã giảm hàng chục MW; Meta đánh giá rằng bật lại toàn bộ cùng lúc có thể gây rủi ro từ hệ thống điện cho đến cache, nên đã tăng tải theo từng bước. - Bài học: Khi mọi khu vực và mọi nhà mạng cùng lúc gặp tình trạng không vào được·kẹt loading, hãy xem DNS và tuyến BGP trước khi xem server game. Việc này kiểm tra được cả từ bên ngoài công ty, bằng truy vấn DNS qua resolver bên ngoài và thông tin tuyến BGP công khai. Bên phụ trách chính là đội hạ tầng (mạng). Hãy kiểm tra trước xem đường truy cập ngoài băng và công cụ nội bộ dùng khi có sự cố có phụ thuộc vào cùng DNS và mạng đó hay không, và khi phục hồi, tăng tải theo từng bước để lượt kết nối lại không dồn đến cùng lúc. - Nguyên nhân liên quan: isp-bgp, isp-dns - Bài gốc: [Meta](https://engineering.fb.com/2021/10/05/networking-traffic/outage-details/) ### aws-2021 · AWS 2021: AWS us-east-1: tắc nghẽn mạng nội bộ - Chuyện gì xảy ra: Nhiều game đặt server, hệ thống đăng nhập và dữ liệu trên public cloud, nên đây là kiểu sự cố hạ tầng mà game cũng bị ảnh hưởng theo. Lúc 7:30 sáng ngày 7 tháng 12 năm 2021 (giờ chuẩn Thái Bình Dương), mạng nội bộ của region Bắc Virginia (us-east-1) bị tắc nghẽn. Từ 7:33, lỗi và độ trễ của EC2 API tăng lên khiến việc khởi tạo instance mới gặp khó khăn (khởi tạo instance phục hồi lúc 2:40 chiều), kéo theo đăng nhập console thất bại, không thay đổi được cấu hình Route 53, chỉ số CloudWatch bị trễ và mất một phần. Thiết bị mạng phục hồi hoàn toàn lúc 2:22 chiều. Các instance EC2 đang chạy sẵn và các phản hồi DNS hiện có không bị ảnh hưởng. - Nguyên nhân: Một tác vụ tự động nhằm tăng dung lượng cho một dịch vụ nằm trên mạng chính đã gây ra hành vi không lường trước ở rất nhiều client trong mạng nội bộ, làm số lần thử kết nối tăng vọt. Thiết bị nối mạng nội bộ với mạng chính bị quá tải nên việc truyền thông bị trễ, và độ trễ này lại làm tăng thêm số lần thử kết nối và thử lại, khiến tắc nghẽn kéo dài. Các client có cơ chế backoff (giãn khoảng cách giữa các yêu cầu khi tắc nghẽn), nhưng một lỗi tiềm ẩn khiến cơ chế này không hoạt động đúng. Hệ thống giám sát nội bộ cũng phụ thuộc vào chính mạng đó, nên đội vận hành phải xử lý dựa vào log mà không có chỉ số thời gian thực. - Bài học: Nếu lượt thử lại không giãn khoảng cách, một đợt tắc nghẽn ngắn sẽ thành sự cố kéo dài hàng giờ. Nhìn từ phía game, dù server game đang chạy sẵn vẫn bình thường, việc thêm server mới (autoscaling), các chức năng đăng nhập, ghép trận và thanh toán dùng API của cloud, cùng hệ thống giám sát đều có thể bị chặn theo. Tín hiệu cần kiểm tra là trang trạng thái của nhà cung cấp cloud, tỷ lệ lỗi API của cloud và số lần khởi tạo instance thất bại. Bên phụ trách chính là bên ngoài (nhà cung cấp cloud). Đội phát triển game đặt exponential backoff với khoảng chờ ngẫu nhiên và giới hạn số lần cho mọi lượt thử lại; đội hạ tầng chuẩn bị dung lượng dự phòng đủ để trụ được khi không mở rộng được, cùng phương án chuyển sang region khác. - Nguyên nhân liên quan: in-cascade, in-autoscale, in-external - Bài gốc: [AWS](https://aws.amazon.com/message/12721/) ### cloudflare-dns-2025 · Cloudflare 2025: Cloudflare: sự cố DNS công cộng 1.1.1.1 - Chuyện gì xảy ra: Đây là sự cố của một DNS resolver công cộng mà người dùng tự cấu hình trên thiết bị hoặc router. Ở kiểu sự cố này, chỉ những người dùng cấu hình này bị chặn truy cập cùng lúc mọi game và dịch vụ. Từ 21:52 đến 22:54 (UTC) ngày 14 tháng 7 năm 2025, trong 62 phút, resolver 1.1.1.1 không phản hồi trên toàn thế giới. Cloudflare cho biết với nhiều người dùng, điều này đồng nghĩa với việc gần như không dùng được bất kỳ dịch vụ Internet nào. Các truy vấn qua UDP, TCP và DNS over TLS bị ảnh hưởng, còn DNS over HTTPS (kết nối bằng tên miền) tương đối ổn định. - Nguyên nhân: Ngày 6 tháng 6, khi chuẩn bị service topology (cấu hình quy định dải IP được quảng bá từ những PoP nào) cho một dịch vụ khác sẽ dùng trong tương lai, dải IP của resolver 1.1.1.1 đã vô tình bị gắn vào cấu hình đó. Ngày 14 tháng 7, khi cấu hình của dịch vụ kia được thay đổi, số PoP quảng bá dải IP của resolver giảm từ tất cả các nơi xuống còn một PoP đang offline, và tuyến BGP bị rút trên toàn thế giới. Thay đổi này lan ngay ra mọi trung tâm dữ liệu mà không qua triển khai canary. Lúc 22:20, cấu hình được hoàn tác và lưu lượng hồi lại khoảng 77%, nhưng trong lúc đó khoảng 23% server edge đã bị xóa mất cấu hình IP cần thiết, phải cấu hình lại, nên đến 22:54 mới bình thường. Cloudflare cho biết đây là lỗi cấu hình nội bộ, không liên quan đến tấn công hay BGP hijacking. - Bài học: Nếu server game và những người chơi khác vẫn bình thường mà chỉ một số người chơi gặp tình trạng không vào được·kẹt loading với server đăng nhập hoặc server bản cập nhật, hãy nghi ngờ DNS mà những người chơi đó đang dùng. Đặc điểm nhận biết là các phiên đã kết nối vẫn được giữ, chỉ kết nối mới thất bại. Chỉ cần hướng dẫn người chơi đổi cấu hình DNS hoặc tự tra địa chỉ server là phân định được ngay. Bên phụ trách chính là bên ngoài (đơn vị vận hành DNS, nhà mạng). Nếu đội phát triển game (client) hiển thị lỗi phân giải tên miền tách biệt với các lỗi khác, bộ phận chăm sóc khách hàng có thể kết luận ngay. - Nguyên nhân liên quan: isp-dns, isp-bgp - Bài gốc: [Cloudflare](https://blog.cloudflare.com/cloudflare-1-1-1-1-incident-on-july-14-2025/) ### aws-2025 · AWS 2025: AWS us-east-1: sự cố DNS của DynamoDB và quá trình phục hồi kéo dài - Chuyện gì xảy ra: Nhiều game đặt server, hệ thống đăng nhập và dữ liệu trên public cloud, nên đây là kiểu sự cố hạ tầng mà game cũng bị ảnh hưởng theo. Từ 11:48 tối ngày 19 tháng 10 năm 2025 đến 2:20 chiều ngày 20 (giờ mùa hè Thái Bình Dương), region Bắc Virginia chịu ảnh hưởng qua ba giai đoạn. Lỗi DynamoDB API tăng cho đến 2:40 sáng ngày 20; từ 2:25 sáng đến 10:36 sáng, việc khởi tạo instance EC2 mới thất bại (vấn đề kết nối của một số instance mới được giải quyết lúc 1:50 chiều); và từ 5:30 sáng đến 2:09 chiều, lỗi kết nối của một số Network Load Balancer (NLB) tăng lên. - Nguyên nhân: Hệ thống tự động quản lý DNS của DynamoDB có một race condition tiềm ẩn. Trong số các bộ thực thi (DNS Enactor) áp dụng kế hoạch DNS ở các availability zone khác nhau, một bộ bị chậm bất thường đã ghi đè kế hoạch mới bằng một kế hoạch cũ; ngay sau đó, tác vụ dọn dẹp của một bộ thực thi khác xóa kế hoạch cũ này, làm bản ghi DNS của endpoint theo region (dynamodb.us-east-1.amazonaws.com) trở thành rỗng. Hệ thống tự động không tự sửa được nên phải có người khôi phục thủ công. Hệ thống quản lý server vật lý của EC2 phụ thuộc vào DynamoDB, nên trong thời gian đó, lease mà mỗi server vật lý duy trì đã hết hạn. Sau khi DynamoDB hoạt động trở lại, do có quá nhiều server vật lý, việc thiết lập lại lease bị timeout trước khi hoàn tất và các tác vụ thử lại lại dồn lên, đưa hệ thống vào trạng thái “sụp đổ do tắc nghẽn (congestive collapse)”. Cấu hình mạng của các instance mới khởi tạo lan truyền chậm, khiến health check của NLB lúc thành công lúc thất bại, và cả các node bình thường cũng liên tục bị loại khỏi DNS rồi được đưa trở lại. - Bài học: Lỗi bản ghi DNS ở một nơi lan sang các dịch vụ khác phụ thuộc vào dịch vụ đó, và ngay cả khi nguyên nhân đã được gỡ, tác vụ tồn đọng cùng health check chập chờn vẫn khiến việc phục hồi mất thêm vài giờ. Nhìn từ phía game, các server đang chạy sẵn vẫn trụ được, nhưng không khởi tạo được server mới nên autoscaling ngừng lại, và khi health check chập chờn, bộ cân bằng tải có thể loại bỏ cả những server đang bình thường. Tín hiệu cần kiểm tra là trang trạng thái của cloud, tỷ lệ lỗi API của các dịch vụ được quản lý (managed service), số lần khởi tạo instance thất bại và số target khỏe mạnh của bộ cân bằng tải. Bên phụ trách chính là bên ngoài (nhà cung cấp cloud); đội hạ tầng giới hạn số server có thể bị loại cùng lúc do health check thất bại và chuẩn bị phương án chuyển sang region khác. - Nguyên nhân liên quan: in-external, in-cascade, in-autoscale, dc-lb-imbalance, isp-dns - Bài gốc: [AWS](https://aws.amazon.com/message/101925/) ## Thuật ngữ - **Ping** (Ping, RTT): Thời gian để tín hiệu bạn gửi đi tới server rồi quay về (khứ hồi). Ping hiển thị trong game đôi khi còn lẫn cả thời gian chờ xử lý trên server. - **Độ trễ** (Latency): Thời gian gói tin đi từ lúc xuất phát đến lúc tới nơi. Thường chỉ tính một chiều nên vào khoảng một nửa ping. - **Jitter** (Jitter): Độ dao động của khoảng cách giữa các lần gói tin đến. Dù ping trung bình như nhau, jitter lớn thì màn hình vẫn giật khựng. - **Gói tin** (Packet): Một khối dữ liệu gửi qua mạng trong một lần. Thường tối đa 1.500 byte; một bản cập nhật trạng thái của game chỉ vài chục đến vài trăm byte. - **Mất gói tin** (Packet loss): Gói tin đã gửi nhưng không tới được nơi nhận, bị mất giữa đường. Với game dùng TCP, chỉ 1% thôi là cứ vài giây đến hơn chục giây lại thấy khựng một lần; game dùng UDP có nội suy và gửi lặp input thì có khi che được tới vài %. - **Băng thông** (Bandwidth): Lượng dữ liệu tối đa đường truyền gửi được trong 1 giây (Mbps). Đây là khái niệm khác với việc dữ liệu đến nhanh hay chậm (độ trễ). - **Tick** (Tick): Đơn vị một lần server tính toán trạng thái game. Server 20 tick tính 20 lần trong 1 giây, tức cứ 50 ms một lần. - **Tick rate** (Tick rate): Số tick chạy trong 1 giây. Càng cao thì phản hồi càng nhanh nhưng chi phí server và lượng dữ liệu truyền đi càng tăng. Để tiết kiệm lượng dữ liệu, số lần gửi gói tin đôi khi được đặt thấp hơn tick rate. - **Tick budget** (Tick budget): Thời gian tối đa để hoàn thành một tick. Vượt quá thì tick sau bị trễ và khoảng cách giữa các tick giãn ra. - **FPS** (Frames per second): Số lần vẽ màn hình trong 1 giây. 60 FPS nghĩa là mỗi khung hình có 16,7 ms. - **Frame time** (Frame time): Thời gian để vẽ một khung hình. Với cảm nhận của người chơi, những khung hình thỉnh thoảng vọt lên quan trọng hơn FPS trung bình. - **Snapshot** (Snapshot): Bản tóm tắt “trạng thái game lúc này” mà server gửi mỗi tick, gồm vị trí, máu, trạng thái… Thường chỉ gửi phần khác với trạng thái bên nhận đã có (nén delta). - **Nội suy** (Interpolation): Kỹ thuật vẽ nối giữa hai snapshot đã nhận để chuyển động trông mượt. Đổi lại, thứ bạn thấy là quá khứ một chút. - **Bộ đệm nội suy** (Interpolation buffer): Khoảng thời gian cố ý vẽ trễ để nội suy. Đây là phần dự phòng để hấp thụ jitter và một hai lần mất gói. Thường bằng 2 lần khoảng cách giữa các gói (nhận 20 lần trong 1 giây thì là 100 ms); có game tự tăng lên khi jitter lớn. - **Ngoại suy** (Extrapolation, Dead reckoning): Kỹ thuật đoán vị trí sắp tới dựa trên vận tốc cuối cùng khi chưa có gói tin mới. Đoán sai sẽ trông như dịch chuyển tức thời, nên nhiều game chỉ đoán tới khoảng 0,25 giây rồi dừng (mặc định của Source Engine là 0,25 giây). - **Dự đoán phía client** (Client-side prediction): Kỹ thuật cho nhân vật của bạn di chuyển trước trên màn hình mà không chờ server xác nhận. - **Hiệu chỉnh theo server** (Reconciliation): Khi kết quả từ server về, game so với phần đã dự đoán rồi sửa lại vị trí nhân vật của bạn. Game lấy vị trí server đã xác nhận, áp dụng lại các input chưa được xác nhận rồi tính lại. Chênh lệch lớn thì trông như kéo ngược. - **Bù trễ** (Lag compensation): Kỹ thuật mà khi phán định đòn đánh, server quay ngược về thời điểm quá khứ người tấn công đang thấy để kiểm tra có trúng hay không. Để người bị đánh không chịu thiệt, biên độ quay ngược có giới hạn trên. Game bắn súng đối kháng thường khoảng 0,2–0,25 giây; cũng có trường hợp quay ngược tới 1 giây như mặc định của Source Engine. - **Server có thẩm quyền** (Authoritative server): Thiết kế trong đó chỉ server mới đưa ra phán định cuối cùng. Cách này chống gian lận nhưng mọi kết quả đều phải qua một vòng khứ hồi tới server, vì vậy game dùng dự đoán và hiển thị trước để che thời gian chờ. - **Lockstep** (Deterministic lockstep): Cách mọi người chỉ trao đổi input rồi cùng tính giống hệt nhau trong cùng một lượt. Input được cộng thêm một độ trễ cố định, và chỉ cần input của một người đến muộn là tất cả phải chờ. - **Bộ đệm input phía server** (Server-side input buffer): Bộ đệm nơi server gom một ít input của từng người rồi mỗi tick lấy ra dùng một cái. Người có jitter lớn vẫn trông mượt trong mắt người khác, nhưng thời điểm hành động của người đó được server chốt cũng trễ đi tương ứng. - **Listen server** (Listen server): Cách PC của một người chơi vừa chơi game vừa kiêm luôn vai trò server. Chủ phòng có ping bằng 0, nhưng đường truyền hay PC của chủ phòng chậm thì mọi người đều bị lag. - **Phasing** (Phasing): Tính năng cho cùng một địa điểm hiển thị NPC và địa hình khác nhau tùy tiến độ nhiệm vụ. Nếu hai nhân vật có tiến độ khác nhau thì việc chỉ một bên không thấy NPC là bình thường. - **Rollback netcode** (Rollback netcode (GGPO)): Cách dự đoán input của đối thủ để chạy trước, nếu input thật khác đi thì quay lại khung hình trong quá khứ và tính lại. Dùng nhiều trong game đối kháng. Thuật ngữ này khác với rollback của DB. - **Buffer input** (Input buffer, spell queue): Nhận trước input tiếp theo được bấm ngay trước khi hồi chiêu hoặc động tác kết thúc, rồi thực hiện đúng lúc kết thúc. Nhờ vậy giữa các đòn liên hoàn không bị chen thêm thời gian khứ hồi. - **Hiển thị trước** (Client-side feedback): Phát trước hoạt ảnh, âm thanh, hiệu ứng mà không chờ server xác nhận. Chỉ những kết quả cần chốt như sát thương hay phần thưởng mới chờ server trả lời. Nếu server từ chối thì phải hoàn tác những gì đã hiển thị. - **TCP** (Transmission Control Protocol): Giao thức chuyển dữ liệu đúng thứ tự, không thiếu gói nào. Khi mất gói, TCP giữ lại các gói phía sau, không chuyển cho game cho đến khi nhận lại được gói đó. - **UDP** (User Datagram Protocol): Giao thức gửi đi thế nào thì chuyển thế ấy, không bảo đảm gì. Không phải chờ, đổi lại game phải tự xử lý mất gói và thứ tự. - **UDP tin cậy** (Reliable UDP (KCP, ENet…)): Cách tự cài đặt trên nền UDP phần truyền lại và bảo đảm thứ tự, chỉ ở mức vừa đủ dùng. - **HOL blocking** (Head-of-line blocking): Hiện tượng một gói phía trước bị kẹt khiến mọi gói phía sau phải chờ. Đây là nguyên nhân gây tua nhanh với TCP. - **RTO** (Retransmission timeout): Timer truyền lại. Khoảng thời gian TCP chờ trước khi xác định gói tin đã mất và gửi lại. Trên Linux là ping + 200 ms trở lên, mỗi lần thất bại lại tăng gấp đôi. - **Thuật toán Nagle** (Nagle’s algorithm): Tính năng của TCP gom dữ liệu nhỏ lại cho đến khi nhận được xác nhận (ACK) cho dữ liệu đã gửi trước đó, rồi gửi một lần để tiết kiệm số gói. Với game thường nên tắt. - **TCP_NODELAY** (TCP_NODELAY): Tùy chọn socket để tắt thuật toán Nagle. Message nhỏ được gửi đi ngay. - **Delayed ACK** (Delayed ACK): Tính năng gửi xác nhận đã nhận muộn một chút, gộp chung với dữ liệu khác. Linux thường là 40 ms (tối đa 200 ms); Windows bản cũ là 200 ms, bản gần đây là 40 ms. - **Bộ đệm socket** (SO_SNDBUF / SO_RCVBUF): Kích thước vùng chờ gửi và nhận mà OS cấp cho từng socket. Quá nhỏ thì bị tràn, quá lớn thì dữ liệu cũ chất đống và phải chờ. - **keepalive** (SO_KEEPALIVE): Tính năng của TCP kiểm tra xem kết nối nhàn rỗi còn sống hay không. Mặc định tắt, và dù bật thì theo mặc định cũng phải sau 2 giờ mới kiểm tra. - **RST** (TCP reset): Tín hiệu TCP cắt kết nối ngay lập tức. Dữ liệu chưa kịp gửi sẽ bị bỏ. - **Heartbeat** (Heartbeat): Tín hiệu “vẫn còn sống” do chính game gửi định kỳ. Dùng để phát hiện kết nối đã đứt và giữ cho kết nối trên các thiết bị trung gian không bị xóa. - **Timeout** (Timeout): Ngưỡng thời gian mà nếu không có phản hồi thì coi là thất bại. Quá ngắn thì báo nhầm, quá dài thì phát hiện muộn. - **NAT** (Network Address Translation): Tính năng của router cho nhiều thiết bị trong nhà đi ra ngoài bằng chung một IP công cộng, đồng thời ghi lại từng kết nối vào bảng NAT. - **CGNAT** (Carrier-grade NAT): NAT quy mô lớn do nhà mạng vận hành, cho nhiều thuê bao dùng chung một IP. - **MTU** (Maximum Transmission Unit): Kích thước gói tin tối đa gửi được trong một lần. Thường là 1.500 byte, nhỏ hơn trên các chặng VPN hay PPPoE. - **Bufferbloat** (Bufferbloat): Hiện tượng thiết bị giữ hàng đợi quá lớn khiến độ trễ tăng lên hàng trăm ms. - **SQM** (Smart Queue Management (fq_codel, CAKE)): Tính năng của router giữ hàng đợi ngắn và đẩy dữ liệu ra công bằng theo từng luồng. Giải pháp cho bufferbloat. - **QoS** (Quality of Service): Tính năng đặt mức ưu tiên để lưu lượng quan trọng được gửi trước. - **Peering** (Peering): Điểm các nhà mạng kết nối mạng của nhau. Dễ bị nghẽn vào buổi tối. - **BGP** (Border Gateway Protocol): Giao thức các nhà mạng dùng để báo cho nhau nên gửi dữ liệu theo tuyến đường nào trên Internet. Khi thay đổi, tuyến đường và ping cũng đổi theo. - **DDoS** (Distributed Denial of Service): Kiểu tấn công gửi lượng lưu lượng khổng lồ từ nhiều nơi để làm tê liệt dịch vụ. - **Trung tâm lọc DDoS** (DDoS scrubbing center): Cơ sở của nhà cung cấp dịch vụ chống DDoS. Khi bị tấn công, lưu lượng hướng tới server được đưa qua đây trước để lọc bỏ tấn công, chỉ lưu lượng hợp lệ được chuyển tiếp. Nếu cơ sở này ở xa thì tuyến đường dài ra. - **Tường lửa** (Firewall): Thiết bị hoặc phần mềm chỉ cho các kết nối được phép đi qua. Tường lửa theo dõi các kết nối bằng bảng phiên. - **Bộ cân bằng tải** (Load balancer): Thiết bị chia các kết nối đi vào cho nhiều server. - **Bảng phiên** (Session table, conntrack): Bảng mà thiết bị hoặc OS dùng để theo dõi các kết nối hiện có. Kích thước có giới hạn. - **Microburst** (Microburst): Hiện tượng lưu lượng dồn đến trong một khoảnh khắc cực ngắn, dưới 1 ms, dù mức trung bình thấp. - **NIC** (Network Interface Card): Card mạng của server. - **Ring buffer** (Ring buffer): Bộ đệm giữ các gói tin NIC đã nhận cho đến khi CPU lấy đi. Bộ đệm này dùng xoay vòng một số slot cố định; khi mọi slot đã đầy thì gói tin mới bị bỏ. - **Ngắt** (Interrupt): Tín hiệu thiết bị gửi tới CPU để báo “có việc cần xử lý”. - **RSS** (Receive Side Scaling): Tính năng của NIC chia các gói tin nhận được vào nhiều hàng đợi nhận để nhiều core CPU cùng xử lý. - **PPS** (Packets per second): Số gói tin mỗi giây. Server game thường chạm giới hạn ở con số này trước cả băng thông. - **Kernel** (Kernel): Phần lõi của hệ điều hành, phụ trách mạng, bộ nhớ và việc phân chia CPU. - **backlog** (Listen backlog): Hàng đợi chứa các yêu cầu kết nối mới mà server chưa nhận vào. Khi hàng đợi đầy, Linux lặng lẽ bỏ yêu cầu mới, còn Windows gửi phản hồi từ chối. - **TIME_WAIT** (TIME_WAIT): Trạng thái mà bên đóng kết nối trước giữ lại cặp cổng đó một lúc (Linux là 60 giây) để phòng gói tin đến muộn. - **CPU steal** (Steal time): Thời gian máy ảo muốn dùng CPU nhưng phải chờ vì server vật lý đang cấp CPU cho máy ảo khác. Xem bằng giá trị st trong top. - **CPU throttling** (CFS throttling): Cơ chế buộc container dừng đến hết chu kỳ khi đã dùng hết quota CPU trong một chu kỳ cố định (CFS period, thường 100 ms). - **File descriptor** (File descriptor): Số được gắn cho mỗi tệp hoặc kết nối mà tiến trình mở (fd). Số lượng có giới hạn. - **Thread** (Thread): Đơn vị công việc chạy độc lập bên trong một chương trình. Nhiều thread có thể chạy cùng lúc. - **Context switch** (Context switch): Việc CPU đổi thread đang chạy sang thread khác. Việc này tốn chi phí. - **Lock** (Lock, Mutex): Cơ chế khóa chỉ cho một thread dùng dữ liệu chung tại một thời điểm. - **Deadlock** (Deadlock): Trạng thái các thread chờ lock của nhau nên cùng dừng mãi mãi. - **Thread pool** (Thread pool): Nhóm worker thread tạo sẵn. Khi tất cả đều bận, việc mới phải chờ. - **I/O bất đồng bộ** (epoll, IOCP, io_uring): Cách để chương trình làm việc khác trong lúc nhập xuất đang diễn ra, khi xong thì nhận thông báo. - **AOI** (Area of Interest): “Phạm vi nhìn thấy được” của từng người chơi. Chỉ gửi các thay đổi trong phạm vi này để giảm lượng dữ liệu. Để giảm chi phí xác định ai ở trong phạm vi, bản đồ thường được chia thành lưới (grid) và chỉ xét các ô ở gần. - **Broadcast** (Broadcast, fan-out): Gửi một thay đổi tới nhiều người có thể thấy nó. Nếu mọi người tụ tập đều thấy nhau thì lượng phải gửi tăng theo bình phương số người. - **GC** (Garbage collection): Tính năng tự động thu hồi bộ nhớ đã dùng xong. Trong lúc GC chạy, chương trình có thể bị dừng. - **Heap** (Heap): Vùng bộ nhớ mà chương trình xin cấp mỗi khi cần trong lúc chạy. - **Rò rỉ bộ nhớ** (Memory leak): Lỗi không trả lại bộ nhớ đã dùng xong khiến mức sử dụng tăng mãi. Dù có GC, lỗi này vẫn xảy ra nếu đối tượng đã dùng xong vẫn bị tham chiếu ở đâu đó. - **Swap** (Swap, paging): Khi thiếu RAM, một phần bộ nhớ được chuyển tạm xuống ổ đĩa. Khi cần dùng lại phần bộ nhớ đó thì chậm hơn RAM trên 1.000 lần. - **OOM killer** (Out-of-memory killer): Khi hết bộ nhớ, Linux chọn tiến trình dùng nhiều bộ nhớ nhất và buộc nó kết thúc. Với container, chỉ cần chạm giới hạn bộ nhớ là cơ chế này đã chạy. - **Cache miss** (Cache miss): Dữ liệu không có trong cache gần CPU nên phải đi tới bộ nhớ chậm hơn. - **IOPS** (I/O operations per second): Số lần đọc, ghi ổ đĩa xử lý được trong 1 giây. Ổ đĩa cloud có giới hạn tùy theo số tiền bạn trả. - **fsync** (fsync): Lệnh chờ đến khi dữ liệu chắc chắn đã được ghi xuống ổ đĩa. Thao tác ghi thông thường được giữ trong bộ nhớ của OS trước rồi mới ghi xuống ổ đĩa sau, nên nếu server mất điện trong khoảng đó thì dữ liệu có thể mất. fsync an toàn nhưng chậm. - **Burst credit** (Burst credits): Lượng tích lũy cho phép ổ đĩa hay server trên cloud tạm thời chạy vượt mức hiệu năng cơ bản. Hết tích lũy thì tụt về hiệu năng cơ bản. - **Index** (Index): Chỉ mục của DB. Không có index thì phải đọc toàn bộ bảng. - **Full scan** (Full table scan): Truy vấn kiểm tra mọi dòng trong bảng mà không dùng index. - **Kế hoạch thực thi** (Query plan): Cách DB quyết định sẽ giải một query theo thứ tự nào, dùng index nào. Code không đổi nhưng DB đổi kế hoạch thì cùng một query cũng có thể đột ngột chậm đi. - **Transaction** (Transaction): Nhóm thao tác DB gói thành một khối “hoặc thành công hết, hoặc không có gì”. Giao dịch trong game bắt buộc phải xử lý bằng transaction. Transaction khóa các dòng đã sửa cho đến khi kết thúc nên càng ngắn càng tốt. - **Connection pool** (Connection pool): Nhóm kết nối DB tạo sẵn. Khi tất cả đang được dùng, yêu cầu mới phải chờ. - **Hot row** (Hot row): Một dòng mà rất nhiều yêu cầu cùng muốn sửa một lúc. Nguyên nhân gây tranh chấp khóa. - **Replication lag** (Replication lag): Khoảng thời gian DB bản sao bị tụt lại phía sau DB chính. - **Rollback** (Rollback): Việc lưu bị hủy và dữ liệu quay về trạng thái trước đó. Người chơi sẽ thấy “vật phẩm biến mất”. - **Cache** (Cache (Redis etc.)): Bản sao của dữ liệu hay dùng, đặt ở nơi truy cập nhanh. Giúp giảm tải cho DB. - **Checkpoint** (Checkpoint): Việc DB định kỳ ghi dồn các thay đổi đang gom trong bộ nhớ xuống ổ đĩa. Vào lúc đó, việc lưu và truy vấn có thể chậm đi một chút. - **Failover** (Failover): Việc chuyển sang bản dự phòng khi server hoặc DB chính chết. Trong lúc chuyển, tạm thời không lưu được dữ liệu, và nếu replication đang trễ thì dữ liệu cuối cùng có thể bị mất. - **MVCC** (Multi-version concurrency control): Cách DB tạm giữ các phiên bản cũ để người đọc và người sửa không chặn nhau. Nếu có transaction mở quá lâu, phiên bản cũ chất đống và làm DB chậm đi. - **Cache stampede** (Cache stampede): Hiện tượng cache bị trống cùng lúc khiến yêu cầu dồn về nguồn gốc (DB). - **Gateway** (Gateway): Server trung gian nhận kết nối từ client rồi chuyển tiếp tới các server game phía sau. - **Circuit breaker** (Circuit breaker): Cơ chế tạm ngắt lời gọi tới dịch vụ đang liên tục lỗi và trả lỗi ngay để ngăn sự cố dây chuyền. Sau một lúc, cơ chế này thử gọi một hai lần; nếu dịch vụ đã hồi phục thì cho phép gọi lại. - **Sự cố dây chuyền** (Cascading failure): Sự cố ở một nơi lan sang các dịch vụ khác theo chuỗi gọi. - **Autoscaling** (Autoscaling): Tính năng tự động tăng, giảm số server theo tải. Việc tăng thêm server cần có thời gian. - **Watchdog** (Watchdog): Timer giám sát xem server có bị đứng không. Nếu vòng lặp game dừng quá thời gian quy định (vài giây đến vài chục giây), watchdog ghi lại trạng thái (dump) rồi buộc server kết thúc để khởi động lại. - **Mức sử dụng** (Utilization): Tỷ lệ thời gian worker (chủ thể xử lý yêu cầu như core CPU, thread, kết nối DB) bận. Vượt 80–90% thì thời gian chờ tăng vọt. - **p99** (99th percentile): Giá trị mà 99 trên 100 lần nhanh hơn nó và khoảng 1 lần chậm hơn nó. Phản ánh lag người chơi cảm nhận tốt hơn giá trị trung bình. - **V-Sync** (Vertical sync): Tính năng xuất khung hình theo đúng chu kỳ làm tươi của màn hình. Loại bỏ được hiện tượng xé hình nhưng gây trễ thao tác, và khi FPS tụt dưới tần số quét thì có thể nhảy qua lại giữa 60 và 30 gây giật khựng. - **Tần số quét biến thiên** (VRR, G-Sync, FreeSync): Tính năng cho màn hình đổi hình đúng lúc khung hình sẵn sàng. Giảm giật khựng và trễ thao tác sinh ra khi V-Sync nhảy qua lại giữa 60 và 30. - **Anti-cheat** (Anti-cheat): Module bảo mật chống hack game. Khi việc kiểm tra định kỳ hoặc heartbeat với server thất bại, anti-cheat có thể gây giật khựng hoặc mất kết nối. - **Overlay** (Overlay): Tính năng vẽ đè lên màn hình game của các chương trình nhắn tin, quay màn hình hay hiển thị FPS. Vì chen vào quá trình vẽ của game nên có thể gây giật khựng. - **Biên dịch shader** (Shader compilation): Việc chuyển chương trình hiệu ứng đồ họa sang dạng dành cho GPU. Nếu không làm trước thì màn hình khựng ở lần đầu nhìn thấy hiệu ứng; cập nhật driver đồ họa làm kết quả đã lưu mất hiệu lực nên phải làm lại. - **Main thread** (Main thread, Game thread): Thread trung tâm của game, lần lượt xử lý việc tính luật chơi và chuẩn bị màn hình. Chỉ cần một việc ở đây tốn nhiều thời gian là trong lúc đó màn hình đứng lại. - **Độ phân giải timer** (Timer resolution): Khoảng ngắn nhất mà hệ điều hành có thể đánh thức chương trình đang ngủ. Mặc định của Windows là 15,6 ms, nên nếu chương trình không tự đổi thì dù yêu cầu “1 ms nữa đánh thức tôi” vẫn bị đánh thức muộn. - **Bóp xung do nhiệt** (Thermal throttling): Tính năng bảo vệ: thiết bị nóng lên thì tự giảm tốc độ CPU, GPU. Trên điện thoại, chơi game vài phút đến vài chục phút là thường xảy ra. - **VRAM** (Video memory): Bộ nhớ riêng gắn trên card đồ họa. Texture và model được nạp vào đây để vẽ. Thiếu VRAM thì phải trao đổi với bộ nhớ của PC qua đường chậm nên bị giật khựng. - **Net graph** (Net graph): Bảng hiển thị dành cho phát triển và debug, vẽ đồ thị thời gian thực của ping, mất gói, FPS, tick ngay trên màn hình game. Nếu quay được cùng trong video báo lag thì tìm nguyên nhân dễ hơn nhiều. - **Tỷ lệ truyền lại** (Retransmission rate): Tỷ lệ gói TCP phải gửi lại trên tổng số đã gửi. Không có chuẩn chính thức, nhưng trung bình toàn server dưới 0,1% là khá khỏe, còn vượt 1% thì nhiều người chơi dễ thấy lag. Cũng nên xem giá trị đã tăng gấp mấy lần so với bình thường. - **SACK** (Selective ACK): Tính năng của TCP cho bên nhận báo chi tiết “đoạn này đã nhận, chỉ thiếu phần này”. Mất nhiều gói vẫn có thể phục hồi trong một lượt. - **RACK-TLP** (Recent ACK, Tail Loss Probe): Tính năng của TCP xác định mất gói dựa trên thời gian, và nếu một lúc không có ACK thì gửi lại gói cuối thêm một lần để phục hồi sớm hơn. Là mặc định trên Linux và Android mới nhất. Windows bật TLP và RACK mặc định từ Windows 10 (1607) và Server 2016; bản RACK mới, phục hồi được cả gói truyền lại bị mất, có từ Server 2022. Chỉ hoạt động trên kết nối đã bật SACK. - **Truyền lại không cần thiết** (Spurious retransmission): Gói tin thật ra vẫn tới, chỉ đến muộn hoặc sai thứ tự, nhưng bị nhầm là đã mất nên bị gửi lại. Việc này lãng phí đường truyền và làm giảm tốc độ gửi một cách vô ích. - **Zero window** (Zero window): Trạng thái bộ đệm bên nhận đã đầy và báo “tạm ngừng gửi”. Trông giống truyền lại nhưng đường truyền vẫn ổn; thực chất là chương trình bên nhận không đọc kịp. - **thin stream** (Thin stream): Kết nối gửi gói nhỏ thưa thớt như game. Tín hiệu để truyền lại nhanh khó gom đủ nên khi mất gói sẽ đứng lâu. - **Policer** (Policer): Kiểu giới hạn tốc độ bỏ ngay các gói vượt tốc độ quy định mà không cho vào hàng đợi. Kiểu cho vào hàng đợi rồi đẩy ra từ từ thì gọi là shaper. - **Pacing** (Pacing): Chia các gói cần gửi ra đều theo thời gian, không đổ ra một lượt. Giúp tránh làm tràn các bộ đệm nhỏ. - **ECN** (Explicit Congestion Notification): Tính năng khi nghẽn thì gắn dấu “đang nghẽn” vào gói tin, không bỏ gói, để bên gửi giảm tốc độ. Báo nghẽn mà không cần mất gói. Chỉ hiệu quả khi cả hai đầu và thiết bị ở chặng bị nghẽn đều hỗ trợ. - **MSS** (Maximum Segment Size): Kích thước dữ liệu tối đa TCP chứa trong một gói. Thường là 1.460 byte; giảm cho khớp với chặng tunnel thì có thể tránh được MTU black hole. - **Handover** (Handover): Việc điện thoại đang di chuyển đổi sang trạm phát sóng khác. - **Phân vị** (Percentile (p50, p95, p99)): Giá trị nằm ở vị trí bao nhiêu phần trăm khi xếp các giá trị từ nhỏ đến lớn. p50 là trung vị, p99 là giá trị quanh 1 lần chậm nhất trong 100 lần. Phân vị cho thấy những lần vọt lên mà giá trị trung bình che mất. - **Tail latency** (Tail latency): Độ trễ dài thỉnh thoảng mới xảy ra dù phần lớn đều nhanh. Gần như không lộ ra trong giá trị trung bình, nhưng đây lại là phần người chơi nhớ là lag. - **Giám sát giả lập** (Synthetic monitoring): Dùng thiết bị hoặc server đo đặt ở vị trí cố định, thay cho người chơi thật, để định kỳ gửi ping, traceroute… và đo chất lượng tuyến đường. RIPE Atlas là công cụ công khai tiêu biểu. - **Khoảng gộp dữ liệu** (Aggregation interval): Một điểm trên đồ thị gộp giá trị của bao nhiêu giây hay bao nhiêu phút. Khoảng càng dài thì các lần vọt lên ngắn càng bị trung bình làm mờ đi. - **Postmortem** (Postmortem): Bài viết tổng kết sau khi sự cố kết thúc: chuyện gì đã xảy ra, vì sao, và sẽ thay đổi gì. Mục đích là ngăn sự cố lặp lại, hơn là đổ lỗi. - **C-state** (CPU idle state): Trạng thái tiết kiệm điện mà CPU đi vào khi rảnh. Trạng thái càng sâu càng tiết kiệm điện nhưng càng mất thời gian để thức dậy. - **Live migration** (Live migration): Việc cloud chuyển một máy ảo đang chạy sang host khác, chẳng hạn để bảo trì host. Vào lúc chuyển, máy ảo có thể dừng trong chốc lát. - **SNAT** (Source NAT): NAT đổi địa chỉ nguồn của gói tin đi ra thành địa chỉ công cộng. Một địa chỉ công cộng chỉ dùng được một số cổng có hạn, dùng hết thì kết nối mới thất bại. - **NAT gateway** (NAT gateway): Thiết bị trên cloud cho các server trong mạng riêng dùng chung một địa chỉ công cộng khi ra Internet. Có giới hạn số kết nối đồng thời theo từng đích. - **Internet vệ tinh quỹ đạo thấp** (LEO satellite internet): Internet kết nối qua các vệ tinh ở độ cao vài trăm đến vài nghìn km. Độ trễ ngắn hơn nhiều so với vệ tinh địa tĩnh, nhưng có thể vọt lên khi đổi vệ tinh đang kết nối. - **GeoIP** (IP geolocation): Cơ sở dữ liệu ước đoán quốc gia, thành phố, nhà mạng từ địa chỉ IP. Có mục sai hoặc đã cũ nên đôi khi khiến người chơi bị xếp vào server ở khu vực xa. - **Chứng chỉ TLS** (TLS certificate): Tài liệu điện tử chứng minh server đúng là server đó. Chứng chỉ có thời hạn; hết hạn thì kết nối mã hóa thất bại và không thể truy cập. - **Tạo khung hình** (Frame generation): Công nghệ card đồ họa chèn khung hình dự đoán vào giữa các khung hình thật để tăng FPS. Màn hình mượt hơn nhưng độ trễ từ input đến màn hình có thể tăng.