March 20 ChatGPT outage: Here's what happened

Bài tóm tắt OpenAI 2023 3. Quy mô lớn Độ khó: Trung bình

OpenAI · 2023 · 3. Quy mô lớn · Độ khó: Trung bình — bài gốc

Bối cảnh

  • OpenAI phải tắt ChatGPT vì một bug khiến một số user thấy tiêu đề lịch sử chat của user khác đang hoạt động, và có thể cả tin nhắn đầu của hội thoại mới.
  • Điều tra sâu hơn: trong một cửa sổ 9 giờ, 1,2% thuê bao ChatGPT Plus đang hoạt động có thể bị lộ thông tin thanh toán cho người khác (họ tên, email, địa chỉ thanh toán, loại thẻ, 4 số cuối, ngày hết hạn; không lộ số thẻ đầy đủ), qua trang quản lý thuê bao hoặc email xác nhận gửi nhầm người. Theo OpenAI, số người thực sự bị lộ "cực kỳ thấp".

Nguyên nhân

  • Kiến trúc: cache thông tin user trong Redis Cluster để không phải hỏi DB mỗi request; server Python chạy asyncio dùng thư viện redis-py với một pool kết nối dùng chung.
  • Bug trong redis-py (bản asyncio cho Redis Cluster): mỗi kết nối hoạt động như hai hàng đợi, request vào và response ra. Nếu request bị huỷ sau khi đã đẩy vào nhưng trước khi lấy response ra, kết nối bị "bẩn": request không liên quan tiếp theo dùng lại kết nối đó sẽ nhận nhầm dữ liệu còn sót lại.
    • Phần lớn trường hợp chỉ là lỗi server, user thử lại.
    • Nhưng đôi khi dữ liệu nhầm đúng kiểu dữ liệu mong đợi, nên cache trả về trông hợp lệ mà lại thuộc về user khác.
  • Kích hoạt: lúc 1 giờ sáng ngày 20/3, một thay đổi ở server vô tình làm tăng vọt số request Redis bị huỷ, nên xác suất nhận nhầm tăng lên.

Khắc phục (theo OpenAI)

  • Gửi bản vá cho maintainer Redis, test kỹ.
  • Thêm kiểm tra thừa: dữ liệu trả về từ cache phải khớp với user đang hỏi.
  • Quét log để chắc mỗi tin nhắn chỉ tới đúng user; đối chiếu nhiều nguồn để xác định chính xác người bị ảnh hưởng và thông báo.
  • Thêm logging để phát hiện nếu tái diễn; tăng độ bền và quy mô cụm Redis để giảm lỗi kết nối khi tải cao.
  • Lịch sử chat được khôi phục, trừ vài giờ.

Bài học cho SWE

  • Cache dùng chung giữa user cần lớp bảo vệ cuối: kiểm tra chủ sở hữu của dữ liệu trả về. Lỗi cache ở đây không còn là "dữ liệu cũ" mà là rò rỉ dữ liệu (Cache và invalidation).
  • Huỷ request giữa chừng (timeout, cancel trong async) là đường dẫn ít được test của thư viện client; connection pool dùng chung biến nó thành lỗi chéo giữa user.
  • Kích hoạt là một thay đổi nhỏ làm tăng tần suất của một đường lỗi hiếm, đúng mẫu "thay đổi nhỏ + giới hạn ẩn" (Sự cố và postmortem).

Liên quan