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
- Order global có cần không? (thường không)
- Concurrency × pods × pool DB có vỡ không?
- Lease ≥ p99 job duration?
- Handler idempotent khi redelivery?
- 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.