The Events Rail: Inside Atlassian's Webhook Delivery Platform
Bài tóm tắt Atlassian 2026 3. Quy mô lớn Độ khó: Trung bình
Atlassian · 2026 · 3. Quy mô lớn · Độ khó: Trung bình — bài gốc
Bối cảnh
- webhooks-processor ("events rail") đánh giá, ghép và giao event từ Jira, Confluence, Bitbucket tới app Forge, repository hook, endpoint audit log. Forge là consumer lớn nhất.
- Quy mô (2026): hơn 10 tỷ event/tháng, trung bình khoảng 6.000 event/giây, ngày đỉnh trên 600 triệu; nhiều region production; tăng khoảng 18% mỗi tháng, cộng dồn.
- Ba điều làm bài toán khó (theo Atlassian):
- Subscription rất chọn lọc: app đăng ký loại event, tenant, kèm biểu thức lọc trên payload (ví dụ
project.key = FOOvàpriority = Critical). Phần lớn event không khớp ai. Nên chi phí chính là đánh giá subscription, không phải network I/O, và đường "không khớp" phải rẻ nhất. - Công bằng giữa tenant: một lần import CSV 10.000 issue sinh nhiều event trong một phút hơn cả tuần bình thường của tenant đó.
- Nguyên tắc thiết kế: mất event một cách im lặng tệ hơn nhiều so với giao trễ.
- Subscription rất chọn lọc: app đăng ký loại event, tenant, kèm biểu thức lọc trên payload (ví dụ
Thay đổi chính
- Producer không gọi thẳng: sản phẩm publish lên một event substrate nội bộ (kiểm schema, lưu bền), substrate fan-out vào Events Queue của rail.
- Hai giai đoạn, hai queue:
- Event Matching (CPU-bound): tra registry subscription, đánh giá tiêu chí của producer (event nhạy cảm chỉ giao cho app có quyền hẹp) và của consumer (bộ lọc), ghi bản khớp vào Webhooks Queue. Một pod matching đánh giá hàng chục nghìn lần/giây khi cache ấm.
- Event Delivery (network-bound): giữ hàng trăm HTTP exchange đồng thời mỗi pod; recipient phản hồi từ vài chục ms tới vài giây.
- Thế hệ trước chạy chung một process, nên một recipient chậm làm tắc cả vòng matching. Tách hai giai đoạn là để sửa đúng lỗi đó.
- Mỗi stage autoscale theo độ sâu queue của nó. Pod giữ một permit cho mỗi delivery đang bay, nên delivery chậm tự hãm tốc độ kéo việc.
- Một lõi, nhiều loại event: lõi chỉ có interface (matching, delivery, retry policy); mỗi "surface" (product event, lifecycle, repository hook, audit log, workflow) tự khai báo cách tra đích, định tuyến, lọc, xử lý lỗi. Thêm surface là thay đổi tự chứa, không phải sửa lõi.
- Công bằng nhiều lớp, không tập trung (mỗi lớp bắt một kiểu áp lực mà lớp trước bỏ sót):
- Negative-lookup cache: lưu câu trả lời "không có subscription" với TTL ngắn, chỉ cho loại event có tỉ lệ không khớp cao. TTL là cần gạt giữa hit rate và độ tươi khi có subscription mới.
- Cô lập queue: mỗi pipeline (product event, lifecycle, repository hook, audit log...) có queue và fleet worker riêng, deploy riêng, nên burst của pipeline này không có đường làm chậm pipeline kia. Tiêu chí tách là đặc tính vận hành (timeout, retry, cách hồi phục), không phải nhãn "nhanh/chậm".
- Rate limit theo app+môi trường và theo tenant, dịch vụ dùng chung toàn cụm, hai bucket đều phải còn. Hết thì hoãn, không bỏ.
- Giới hạn concurrency theo recipient: recipient trả lời 10 giây mỗi request không bao giờ chạm rate limit, nhưng chiếm dần năng lực giao. Đếm số delivery đang bay trong counter dùng chung toàn cụm; mỗi lượt giữ chỗ có hạn ngắn để pod chết không làm kẹt vĩnh viễn.
- Giữ cho delivery tin cậy:
- mỗi delivery có ID ổn định qua các lần retry (theo event, không theo lần thử) để recipient khử trùng;
- retry exponential backoff có jitter, vài lần rồi vào DLQ riêng mỗi pipeline để xem và replay;
- backpressure bất đối xứng: lùi nhanh, mở lại chậm, giống congestion control của TCP;
- circuit breaker quanh các upstream nội bộ (tra extension, phân giải đích, rate limiter) để fail fast thay vì treo thread.
- Log có cấu trúc (giới hạn kích thước), metric gắn region, loại event, lớp recipient, kết quả; trace xuyên hai stage.
Vì sao (theo Atlassian)
Lưu lượng tăng 18% mỗi tháng và có hai nguồn tải mới: AI agent thao tác trong sản phẩm (dồn theo đợt, nhịp máy, dày hơn người một bậc) và khách chuyển từ Data Center lên Cloud (ngày migrate tạo bước nhảy tải của tenant). Công bằng phải là tính chất được thiết kế sẵn, không phải thứ hệ thống "lớn dần lên" sau mỗi sự cố.
Bài học cho SWE
- Với pub-sub chọn lọc, tối ưu đường không khớp trước đường khớp: nó chạy nhiều hơn. Cache kết quả âm là cách rẻ nhất (Cache và invalidation).
- Đặt queue giữa hai stage có đặc tính scale khác nhau (CPU vs network) để mỗi bên scale theo tín hiệu của nó và lỗi phía sau không chặn phía trước (Event-driven, message queue và Kafka).
- Rate limit không bắt được recipient chậm; cần thêm giới hạn concurrency theo đích (Load shedding, rate limit và backpressure).
- Hoãn thay vì bỏ, ID ổn định qua retry, DLQ riêng mỗi pipeline: bộ tối thiểu cho giao event at-least-once tới bên thứ ba.
Liên quan
Event-driven, message queue và KafkaLoad shedding, rate limit và backpressureMulti-tenancy và cô lập tenant
Được nhắc tới trong
Chủ đề (3)
Công ty (1)
Bài tóm tắt (5)