How We Cut Kafka Consumer Deployment Costs by 83%

Bài tóm tắt trivago 2026 4. Điều chỉnh Độ khó: Trung bình

trivago · 2026 · 4. Điều chỉnh · Độ khó: Trung bình — bài gốc

Bối cảnh

  • PSE-kafka consume message giá khách sạn từ Kafka và đẩy quảng cáo sang một dịch vụ Ads bên ngoài, tức là tạo doanh thu trực tiếp. Mỗi giá có hạn dùng, nên lag càng lớn thì càng đẩy giá đã hết hạn.
  • Triệu chứng kéo dài: CPU production chỉ khoảng 10%; khởi động lâu mới vào được consumer group, kéo dài mọi lần deploy; lag cao, và số replica đã bằng số partition nên không thêm pod được nữa. Đã có 19 sự cố P1.

Điều tra

  • Tái hiện dễ ở môi trường test: đẩy đủ message thì lag tăng trong khi tốc độ consume và CPU giảm. Không chạm giới hạn phần cứng nên lỗi nằm ở phần mềm.
  • Log cho thấy poll() rất chậm, có lúc 10 giây giữa hai lần; ở production còn vượt max.poll.interval.ms, nên broker coi consumer đã chết và đá khỏi group.
  • BlockHound không thấy lời gọi blocking; metric scheduler không thấy pool nào đầy; JFR cho thấy mọi thread gần như rảnh.
  • Đổi sang spring-kafka thì hết lag, nhưng đó là mô hình push, mất backpressure của mô hình pull mà họ cần. Đổi sang reactor-kafka (commit ở thread riêng) cũng không đủ.

Nguyên nhân gốc: ba vấn đề cùng lúc

  1. Consumer tự viết (2019, khi reactor-kafka chưa ổn định) chỉ auto-commit lúc poll(); xử lý chạm trần thì nó ngừng gọi poll(), nên cũng ngừng commit.
  2. Một Mono.delayElement giữ mỗi message ít nhất 75 giây (để downstream kịp chuẩn bị dữ liệu); không blocking nhưng bị chặn trần bởi concurrency của flatMap (mặc định 256): tối đa khoảng concurrency / số giây trễ.
  3. Thread pool của gRPC client chỉ 15 thread trong khi server có latency trung bình 7 giây, tối đa 30 giây, throughput 250 msg/giây.

Mỗi vấn đề cắn ở một kịch bản khác nhau, nên sửa riêng từng cái gần như không đổi gì. Sửa cả ba (reactor-kafka, flatMap 10.000, pool gRPC 400) thì test đạt 8.000 msg/giây. Bản production: thêm cờ IsFinished để bỏ hẳn độ trễ trong RAM, chỉnh pool gRPC vừa phải.

Kết quả (theo trivago, 2026)

  • Pod từ 60 xuống 6 (chi phí deploy giảm 83%).
  • Hết lag, cảnh báo P1 về lag không quay lại.
  • Thời gian khởi động pod từ khoảng 60 xuống khoảng 10 giây.
  • Không ảnh hưởng kinh doanh.

Bài học cho SWE

  • Lag của consumer không phải lúc nào cũng chữa bằng thêm pod: số consumer trong group bị chặn bởi số partition (Event-driven, message queue và Kafka).
  • Mô hình pull giữ ổn định nhưng che giấu vấn đề: hệ chậm mà không thread nào bận.
  • Độ trễ cố ý trong RAM là trần throughput ẩn.
  • Mỗi lần P1 lại scale pod lên là trả tiền để không phải tìm gốc. Chỉ một đợt sửa có chủ đích mới tìm ra (Chi phí hạ tầng và build vs buy).
  • Nhiều lỗi cùng lúc thì thử từng cái riêng sẽ thấy "không có tác dụng"; cần thử tổ hợp.

Liên quan