Caching Without Marshal Part 2: The Path to MessagePack

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

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

Phần 2; phần 1 Shopify 2022 kể sự cố và lý do.

Thay đổi chính

  • MessagePack từ chối object lạ: chỉ mã hoá kiểu cơ bản và extension type đã đăng ký; object khác ném NoMethodError có tham chiếu tới object. Đó là điểm để log xem thứ gì đang bị đưa vào cache.
  • Migrate song song khoảng 6 tháng:
    1. Thử mã hoá bằng MessagePack; được thì thêm byte tiền tố phiên bản (theo dõi tập extension type đã định nghĩa) rồi ghi.
    2. Không được thì bắt lỗi, quay về Marshal, và gửi lớp của object hỏng vào log và StatsD.
    3. Khi đọc, phân biệt bằng tiền tố: 0408 là Marshal, còn lại là MessagePack. Log của bước 2 vừa cho biết cần extension type nào, vừa lộ những thứ lẽ ra không bao giờ nên vào cache.
  • Bản mã hoá riêng cho ActiveRecord, gồm cả association và tham chiếu vòng; khi đọc mà tên class hay association không còn, ném lỗi rõ ràng và coi như trượt cache. Hệ quả: trong lúc deploy đổi tên class, các key liên quan bị vô hiệu và phải nạp lại, chấp nhận được vì sau deploy cache chạy lại bình thường.
  • Gặp bug của msgpack-ruby (extension type không áp cho lớp con của Hash như HashWithIndifferentAccess), gửi PR sửa (bản 1.4.3).
  • 95% payload mã hoá được ngay; 5% cuối là đuôi dài. Thêm type cho TimeWithZone, Set, rồi một type bắt mọi object có as_pack/from_pack, và module cho Struct/T::Struct. Khi log xác nhận không còn payload Marshal, bỏ hẳn Marshal.
  • Kết quả: cache an toàn mặc định; tỉ lệ đầy của memcached giảm rõ (MessagePack gọn hơn). Mở mã nguồn thành thư viện Paquito.

Vì sao

  • Định dạng có tiền tố phiên bản và danh sách kiểu tường minh biến lỗi tương thích từ "sập" thành "trượt cache" (Cache và invalidation).
  • Ghi định dạng mới, đọc được cả hai, quay về định dạng cũ khi chưa hỗ trợ, đo phần còn lại tới 0 rồi mới bỏ: mẫu migrate không downtime cho dữ liệu đang được dùng (Migration không downtime).

Bài học cho SWE

  • Đặt tiền tố phiên bản vào mọi payload bền hoặc dùng chung để đọc được nhiều định dạng.
  • Dùng đường fallback làm cảm biến: log mọi lần phải quay về định dạng cũ để biết còn bao nhiêu và là gì.
  • Với cache, "trượt có kiểm soát" khi gặp dữ liệu không hiểu được tốt hơn exception.

Liên quan