Preview Bản nháp chờ review — title, nội dung, link có thể sửa trước khi bỏ nhãn.

Hàng đợi việc: mail, resize, webhook. Một worker chậm → backlog. Thêm worker: mỗi message được một consumer lấy (cạnh tranh) — throughput tăng. Pattern: competing consumers.

BullMQ concurrency, Rabbit competing, SQS multi-worker, Kafka trong một partition thì không “nhiều consumer cùng message” theo nghĩa queue — partition gán 1 consumer group member. Đừng nhầm topology.

Được gì / mất gì

Được  — scale ngang xử lý; tận dụng nhiều core/pod
Mất   — order global giữa các message
      — “message A trước B” không đảm bảo nếu A,B khác worker
      — race nếu hai message cùng aggregate (trừ lock/idempotent design)

Cần order per entity → partition key / stream, hoặc queue per key, không chỉ “thêm concurrency”. Order budget.

Prefetch / concurrency = inventory

Worker concurrency: 20 hoặc prefetch 20 = tối đa 20 message in-flight / process. Quá lớn: RAM, DB pool, redelivery storm khi crash. Quá nhỏ: under-utilize.

  • Align với DB pool.
  • Job dài: giảm concurrency; đừng bù bằng prefetch khổng lồ.

Lease & redelivery

Worker chết giữa chừng → message hiện lại ( visibility / lease). Competing + redelivery ⇒ bắt buộc idempotent handler / EOS thực dụng.

Lease < thời gian job chậm nhất → duplicate trong lúc job còn chạy (hai worker cùng việc).

Poison

Một message fail mãi trên một worker không chặn worker khác nếu queue đưa message khác — khác partition stream bị head-of-line. Vẫn cần max retry + DLQ / unblock để không đốt forever.

Checklist scale worker

  1. Order global có cần không? (thường không)
  2. Concurrency × pods × pool DB có vỡ không?
  3. Lease ≥ p99 job duration?
  4. Handler idempotent khi redelivery?
  5. Metric: active jobs, lag/depth, fail rate, DLQ

Một câu

Competing consumers = thêm tay làm — đổi lấy order toàn cục.
Prefetch là inventory; lease và idempotency là lưới an toàn.