Chạy bot trên hơn 2.000 máy chủ Discord đã dạy chúng tôi điều gì
Bốn điều diễn ra khác ở quy mô lớn so với những gì bạn giả định khi viết mã.

Một bot Discord trên ba máy chủ và cùng con bot đó trên hơn hai nghìn máy chủ không phải là cùng một chương trình ở kích thước lớn hơn. Một vài giả định hoàn toàn vô hại khi còn nhỏ, đến một quy mô nhất định lại trở thành đúng những lỗi khó tìm nhất.
Đây là bốn giả định đã khiến chúng tôi gặp sự cố thật.
1. Lỗi không lan đều, chúng ẩn mình
AlphaCloud chạy trên nhiều tiến trình tách biệt, gọi là shard. Discord quyết định máy chủ nào nằm trên shard nào, và mỗi shard chỉ biết các máy chủ của riêng nó.
Điều đó tạo ra một loại lỗi mà từ bên ngoài trông như ngẫu nhiên: tin nhắn chào mừng hoạt động trên một số máy chủ và không hoạt động trên số khác. Không có lỗi trong nhật ký, không có cảnh báo, không có gì cả. Nguyên nhân là một lệnh gọi mạng duy nhất không có thời gian chờ tối đa. Nếu phía bên kia chấp nhận kết nối rồi im lặng, mã sẽ chờ trong suốt phần đời còn lại của tiến trình. Mọi thứ sau đó không bao giờ xảy ra, và vì không có lỗi nào được ném ra, chẳng có gì báo lỗi.
Bài học chẳng có gì hoành tráng và áp dụng ở mọi nơi: mọi lệnh gọi ra ngoài đều cần một giới hạn trên. Một lệnh gọi chờ mãi mãi còn tệ hơn một lệnh gọi thất bại, vì thất bại ít nhất còn nhìn thấy được.
2. Khởi động lại không phải là cách sửa, mà là một hạn chót
Trong lúc chưa biết nguyên nhân, khởi động lại shard bị ảnh hưởng lần nào cũng giúp được. Đó chính là vấn đề của việc khởi động lại: nó hiệu quả, và điều đó làm mất đi áp lực phải tìm ra nguyên nhân.
Giờ chúng tôi coi "khởi động lại có giúp không?" là một chẩn đoán, không phải phương thuốc. Nếu có, trạng thái hỏng nằm trong bộ nhớ, và có một chỗ trong mã tạo ra nó mà không bao giờ dọn dẹp nó.
Điều ngược lại cũng đúng: bất cứ thứ gì phải tồn tại qua một lần khởi động lại đều thuộc về cơ sở dữ liệu. Nếu một thao tác nhiều bước đang chạy, chẳng hạn thay đổi vai trò cho hàng nghìn thành viên, và tiến trình chết giữa chừng, thao tác đó không được phép đơn giản là biến mất. Nó phải được phát hiện và tiếp tục ở lần khởi động tiếp theo.
3. Giới hạn của Discord áp dụng theo bot, không theo tiến trình
Discord giới hạn số yêu cầu một bot được gửi mỗi giây. Giả định hiển nhiên: nhiều tiến trình hơn, thông lượng lớn hơn. Sai. Giới hạn đặt trên bot, không phải trên tiến trình. Ba shard mỗi cái có đủ ngân sách cộng lại thành gấp ba mức cho phép, và Discord sẽ bóp băng thông cả ba.
Vì vậy mỗi tiến trình của chúng tôi tự tính xem tổng cộng có bao nhiêu tiến trình và chỉ lấy phần của mình. Nghe thì hiển nhiên, nhưng thực ra không phải vậy, và lỗi này vô hình trong thời gian dài vì từng tiến trình riêng lẻ trông hoàn toàn đúng mực.
Một cái bẫy liên quan: các tác vụ định kỳ chạy theo khoảng thời gian cố định, chẳng hạn mỗi năm phút. Nếu mọi shard khởi động cùng lúc, chúng sẽ kích hoạt tác vụ vào cùng một thời điểm mãi mãi. Từ đó mỗi tiến trình nhận một độ lệch ngẫu nhiên khi khởi động.
4. Thứ mọi người thực sự cấu hình không phải thứ bạn mong đợi
Phần kỹ thuật là một nửa. Nửa còn lại: với hầu hết máy chủ, giá trị mặc định chính là cài đặt. Một mô-đun có giá trị mặc định sai sẽ bị cấu hình sai trên đại đa số máy chủ, dù trang cài đặt có tốt đến đâu.
Với chúng tôi, hai điều rút ra từ đó. Thứ nhất, mỗi giá trị mặc định đáng được suy nghĩ nhiều hơn chính cài đặt. Thứ hai, câu hỏi tốt hơn cho một mô-đun mới không phải là "ở đây có thể cấu hình gì" mà là "mô-đun này làm gì khi không ai cấu hình gì cả, và điều đó có đúng với một máy chủ bình thường không".
Đó cũng là lý do đằng sau một quyết định thoạt nghe có vẻ ngược lại: từ v1.8.0, gần như mọi tin nhắn bot gửi đều có thể thiết kế tự do. Nhưng các khối bắt buộc như lý do trong tin nhắn riêng về cảnh cáo hay menu danh mục trong bảng ticket chỉ có thể di chuyển chứ không thể xóa. Tự do thiết kế, có, nhưng không đến mức một máy chủ vô tình tạo ra thứ trông như đang hoạt động mà thực ra không.
Sợi chỉ chung
Ba trong bốn điều trên là cùng một ý tưởng khoác những bộ áo khác nhau: ở quy mô này, một lỗi âm thầm đắt hơn một lỗi ồn ào. Một sự cố sập được báo cáo và sửa. Một tính năng đơn giản là ngừng làm gì đó có thể ở nguyên trạng thái đó hàng tháng trời, và bạn chỉ biết qua một báo cáo lỗi từ ai đó chịu khó viết.
Vì vậy câu hỏi đầu tiên chúng tôi đặt ra cho mã mới không phải là "cái này có chạy không", mà là "nó trông thế nào khi không chạy, và liệu chúng tôi có nhận ra không".
Bài viết khác
Theo dõi 4.000 máy chủ Discord: Grafana, PostHog và những gì chúng cho chúng tôi thấy
Một vòng lặp âm thầm tạo ra một nửa lưu lượng của chúng tôi, ba triệu yêu cầu vô ích mỗi ngày và một cuộc tấn công kết thúc sau một phút. Hệ thống giám sát của chúng tôi thấy gì, và cố ý không thấy gì.
Hậu trườngKhi nhà cung cấp AI của chúng tôi ngừng mô hình
Một lần đổi mô hình, những câu trả lời rỗng không kèm lỗi, và một hóa đơn bỗng cao gấp nhiều lần. Điều gì đã hỏng, và vì sao nguyên nhân lại khó thấy đến vậy.
Hậu trườngLuật pháp bên trong mã nguồn của chúng tôi
Một nút hủy hợp đồng phải hoạt động mà không cần đăng nhập. Một biểu mẫu không bao giờ được từ chối bạn. Vì sao với chúng tôi, nghĩa vụ pháp lý là kiến trúc, không phải văn bản mẫu.