Python at Scale: Strict Modules

Bài tóm tắt Instagram 2019 3. Quy mô lớn Độ khó: Khó

Instagram · 2019 · 3. Quy mô lớn · Độ khó: Khó — bài gốc

Bài thứ ba trong series "Python at scale" (sheet chỉ trỏ bài này). Medium chặn crawler; đọc qua trình duyệt, ghi chú bằng lời của wiki.

Bối cảnh

  • Instagram Server là monolith Python vài triệu dòng, hàng trăm commit mỗi ngày, deploy production vài phút một lần (2019). Theo Instagram, các tính năng động của Python ổn với codebase nhỏ và đội có kỷ luật, nhưng thành vấn đề khi hàng trăm developer (nhiều người mới với Python) cùng làm.
  • Ba nỗi đau:
    • Khởi động chậm: import một module chạy rất nhiều code (biên dịch regex, decorator đăng ký route, thân class, metaclass). Server khởi động hơn 20 giây, có lúc tụt về khoảng một phút; gần hết thời gian là import. Vì import có thể có side effect tuỳ ý, không reload tăng dần một cách an toàn được, phải khởi động lại từ đầu dù 99% code không đổi. Việc này còn lãng phí compute ở production vì deploy liên tục.
    • Side effect khi import không an toàn: ví dụ lấy cấu hình qua mạng ở mức module: service mạng chết thì server không khởi động được; thứ tự import là thuộc tính nổi lên của toàn hệ, một import vô hại ở chỗ khác có thể làm module dùng service trước khi nó được khởi tạo, có lúc làm sập production.
    • Trạng thái toàn cục khả biến: gán thuộc tính request vào class (singleton toàn cục) làm bẩn mọi request sau trong tiến trình; monkeypatch trong test gây test flaky nhiều tới mức họ chuyển sang mỗi test một tiến trình.

Thay đổi chính

  • Theo họ, viết lại sang ngôn ngữ khác là không khả thi ở quy mô đó, và developer thích Python; nên phải làm Python chạy được ở quy mô này.
  • Strict modules (thử nghiệm, đang rollout): module đánh dấu __strict__ = True được một module loader riêng phân tích AST bằng abstract interpretation lúc biên dịch. Code ở mức module phải thuần (không side effect, không I/O), nhưng vẫn cho phép comprehension, nhiều loại decorator. Gọi hàm từ module không strict bị coi là không an toàn (trừ một số hàm thư viện chuẩn).
  • Module và class trong strict module bất biến sau khi tạo (thân module được biến thành một hàm, biến toàn cục thành closure); class tự có __slots__ từ các thuộc tính gán trong __init__, nên không gắn thêm thuộc tính tuỳ tiện, đồng thời tiết kiệm bộ nhớ và truy cập nhanh hơn.
  • Hướng tiếp theo họ nêu: reload tăng dần an toàn, thực thi lười từng câu lệnh mức module, lưu sẵn metadata module cho module không đổi.

Vì sao

Bài học cho SWE

  • Ở mức module chỉ nên khai báo; mọi I/O và đăng ký toàn cục để vào lúc chạy có kiểm soát.
  • Thời gian khởi động là chi phí trả cho mỗi lần deploy và mỗi lần developer thử thay đổi.
  • Khi không đổi được ngôn ngữ, thêm một tập con có kiểm chứng của ngôn ngữ (opt-in theo module).

Liên quan