Attack of the Shards: Our Migration to FoundationDB, Part 2

Bài tóm tắt Inngest 2026 3. Quy mô lớn Độ khó: Nâng cao

Inngest · 2026 · 3. Quy mô lớn · Độ khó: Nâng cao — bài gốc

Phần 2 của series. Phần 1 (lý do chọn): Inngest 2026; phần 3 (rollout, benchmark) chưa đăng. Bài viết khi đang rollout, chưa có kết quả.

Bối cảnh

  • Inngest (nền tảng durable execution) chuyển queue từ Valkey (bản fork của Redis, chạy trong RAM, thao tác queue đơn luồng) sang FoundationDB (KV có transaction, dữ liệu trên đĩa).
  • Lý do nêu trong bài: backlog lớn trên RAM là "tình trạng khẩn cấp về bộ nhớ"; đơn luồng giới hạn lượng queue có thể xem cùng lúc.

Thay đổi chính

  • Hai cấp thay vì ba: thiết kế đầu có ba cấp account → function → tenant queue, nên mỗi lần enqueue/dequeue phải cập nhật cả ba. Thiết kế mới có index các function pointer sắp theo thời điểm đến hạn, và tenant queue con chỉ ở function nào có ràng buộc concurrency theo key. Công bằng giữa account chuyển sang tầng scanner.

  • Logical shard: mỗi shard khoảng 20.000 account để range scan trên FDB ngắn. Mỗi bậc giá có một cluster FDB riêng. Account mới vào một shard chọn ngẫu nhiên (seed theo account ID); operator dời được về sau.

  • Định tuyến enqueue:

    • metadata service ánh xạ account → shard, cache trong Redis;
    • khi dời account: cập nhật ánh xạ, xoá cache. Nếu một request đã lấy ánh xạ cũ ngay trước đó: cùng database thì không sao; khác database thì migration phải gom phần sót lại.
  • Dời account giữa shard:

    • cùng database: chỉ đổi quyền sở hữu, không chép item. Trước khi dời phải chờ không còn lease đang xử lý hay item đang bay, rồi khoá queue nguồn;
    • khác database: chép từng item (thêm ở đích trước, xoá ở nguồn sau). Hai database không chung transaction được, nên dựa vào ID ổn định và xử lý trùng.
    • Item giữ nguyên thời điểm đến hạn. Hiện rebalancing làm bằng tay; tự động theo tải là bước tiếp.
  • Công bằng khi không còn đơn luồng: nhiều executor cùng lease việc, mỗi bước claim/xoá là một transaction FDB. Để account nhiều function không chiếm hết executor, họ dùng hai kiểu scan:

    • weighted scan theo cây account → môi trường → function → key, chọn theo trọng số;
    • sequential scan theo thời gian để việc cũ không bị bỏ đói.

    Kết quả là công bằng theo xác suất, không đảm bảo thời gian chờ bằng nhau.

Đánh đổi (theo Inngest)

Rollout phải theo dõi và tinh chỉnh nhiều. Đổi lại, backlog nằm trên đĩa (thêm storage server khi cần), và dời việc giữa các pool executor trong cùng database không phải chép dữ liệu.

Bài học cho SWE

  • Queue trong RAM chịu tải rất tốt cho tới khi backlog lớn; lúc đó cần kho trên đĩa có transaction (Chọn database theo workload).
  • Mô hình dữ liệu phản chiếu cây nghiệp vụ (ba cấp) làm mỗi thao tác đắt; làm phẳng và đẩy công bằng sang tầng lập lịch.
  • Shard theo nhóm tenant cộng bảng ánh xạ có cache là thiết kế quen thuộc. Phải tính khoảng hở khi cache cũ, giống bài toán dời document giữa shard của Tinder (Sharding và tách database).
  • Noisy neighbor trong hệ multi-tenant: công bằng cần thiết kế riêng, không tự có (Hot partition và thundering herd).

Liên quan