Product Management
Đăng nhập
ESC

Nhập từ khóa để tìm kiếm

↑↓ Di chuyển
Enter Mở
ESC Đóng

Bài 4 — Queue: xử lý tác vụ dài bất đồng bộ đúng cách

Đã đọc 0/11 mục · còn khoảng 3 phút
Cách chúng tôi ghi nhận

Một mục được tính là đã đọc khi nó nằm giữa màn hình đủ lâu so với độ dài của nó (bảng và sơ đồ tính một nửa), bạn vẫn đang cuộn hoặc bấm phím, và bạn đã thấy tới cuối mục. Lướt nhanh hoặc để tab mở không được tính. Chúng tôi chỉ lưu thời gian đọc từng mục — không lưu gì khác.

Queue (SQS, RabbitMQ, Redis Streams, Cloud Tasks) là trái tim của mọi triển khai agent nghiêm túc. Nó tách lớp tiếp nhận nhanh khỏi lớp xử lý chậm, chống mất việc, và cho phép scale mượt. Bài này chỉ cách dùng queue đúng để không mất hoặc lặp job.

Vì sao agent cần queue

  • Không chặn người dùng: API trả job_id trong 100ms, agent chạy nền 5 phút.
  • Chịu tải gai: 1000 job đổ vào lúc cao điểm → queue giữ lại, worker xử dần thay vì sập.
  • Không mất việc: worker chết giữa chừng, job quay lại queue để chạy lại.
  • Điều tiết chi phí LLM: giới hạn số worker = giới hạn tốc độ gọi LLM.

Luồng producer–consumer

[API] --enqueue(job)--> [Queue] --pull--> [Worker chạy agent] --ack--> xong
                                              |lỗi
                                              +--> nack -> retry -> ... -> [DLQ]
  • Producer (API): validate, tạo record status=queued, đẩy message chứa job_id (không nhét cả file lớn vào message — lưu ở DB/S3, chỉ truyền tham chiếu).
  • Consumer (worker): pull job, đặt status=processing, chạy agent, ghi kết quả, ack.
  • Nếu lỗi: nack để retry; quá số lần → đẩy sang Dead Letter Queue (DLQ).

Ba tính chất phải nắm

1. Idempotency (chống chạy trùng)

Hầu hết queue đảm bảo "at-least-once" → một job có thể được giao 2 lần. Nếu agent tạo đơn hàng hay gửi email, chạy 2 lần là thảm họa. Giải pháp: mỗi job có khóa duy nhất, kiểm tra trước khi làm.

def handle(job):
    if db.exists(job.id, status="done"):
        return  # đã xử lý rồi -> bỏ qua, an toàn khi giao trùng
    result = run_agent(job)
    db.save(job.id, result, status="done")
    queue.ack(job)

2. Visibility timeout

Khi worker pull job, job bị "ẩn" trong X giây. Nếu agent chạy lâu hơn visibility timeout mà chưa ack, queue tưởng worker chết và giao lại job cho worker khác → chạy trùng. Với agent chạy 5 phút, phải đặt visibility timeout > 5 phút, hoặc gia hạn (heartbeat) định kỳ.

3. Retry có backoff + DLQ

Không retry ngay lập tức (LLM đang 429 thì retry ngay cũng 429). Dùng exponential backoff. Sau N lần thất bại, đẩy vào DLQ để điều tra thủ công thay vì retry vô hạn đốt tiền.

Checklist thiết kế queue cho agent

  • [ ] Message chỉ chứa tham chiếu, không chứa payload nặng.
  • [ ] Có idempotency key và kiểm tra trước khi tác động.
  • [ ] Visibility timeout > p95 thời gian agent + biên.
  • [ ] Heartbeat gia hạn cho job siêu dài.
  • [ ] Retry exponential backoff, giới hạn số lần.
  • [ ] Có DLQ và cảnh báo khi DLQ có message.
  • [ ] Lưu status job ở DB để client polling/hiển thị.

Ví dụ cụ thể

Agent "tạo báo cáo thị trường" chạy trung bình 3 phút, đôi khi 8 phút. Bạn đặt visibility timeout 30 giây → job liên tục bị giao lại, worker chạy chồng chéo, hóa đơn LLM x3. Sửa: đặt visibility 10 phút + heartbeat mỗi 60 giây; thêm idempotency để job giao trùng không tạo báo cáo đôi.

Các bước triển khai

  • Tách API (producer) và worker (consumer) thành hai phần rõ ràng.
  • Thiết kế bảng jobs với id, status, result, attempts.
  • Thêm idempotency guard đầu hàm xử lý.
  • Đặt visibility timeout theo p95 + heartbeat cho job dài.
  • Cấu hình retry backoff và DLQ + alert.
  • Test: giết worker giữa chừng, xác nhận job được chạy lại đúng một lần hiệu lực.

Sai lầm thường gặp

  • Visibility timeout ngắn hơn thời gian agent: job bị giao lại, chạy trùng, đốt token.
  • Không idempotent: giao trùng → gửi email/đơn hàng 2 lần.
  • Nhét file/PDF lớn vào message: vượt giới hạn kích thước message, chậm queue.
  • Retry vô hạn không DLQ: một job lỗi vĩnh viễn quay vòng đốt tiền mãi.
  • Không lưu status: client không biết job xong chưa, phải đoán.
  • Retry không backoff: dồn thêm request vào LLM đang quá tải.
Học xong bài này rồi? Tạo tài khoản miễn phí để lưu lại — lần sau vào là biết ngay đang dở ở đâu, và học hết khóa thì có chứng chỉ. Lưu tiến độ của tôi