Quorum queue là câu trả lời hiện đại của RabbitMQ cho tính sẵn sàng cao, thay cho cơ chế nhân bản classic kiểu cũ. Bài này đo cái giá của nó trong điều kiện dễ chịu nhất có thể: một node duy nhất, không có nhân bản nào qua mạng. Con số ở đây là sàn — cụm thật sẽ đắt hơn.

Quorum queue từ chối những gì

Khai báo Kết quả
durable=false 541 INTERNAL_ERROR - transient_nonexcl_queues is deprecated
exclusive=true 406 - invalid property 'exclusive-owner'
autoDelete=true 406 - invalid property 'auto-delete'
x-max-priority=10 406 - invalid arg 'x-max-priority'
x-message-ttl=1000 cho phép
x-max-length=100 cho phép

Ba dòng đầu hợp lý: hàng đợi nhân bản mà lại tạm thời hoặc chết theo connection thì vô nghĩa. Dòng thứ tư mới đáng nhớ — quorum queue không có ưu tiên, nên nếu bạn dựa vào priority như phần 7 đo, chuyển sang quorum là mất tính năng đó, và phải tách thành nhiều hàng đợi.

Ngược lại, x-delivery-limit chỉ có ở quorum — phần 20 đo được classic từ chối thẳng tham số này.

Thông lượng và độ trễ

50 000 thông điệp 200 byte, persistent, có publisher confirms, trung vị 3 lượt, một node:

Thông lượng Độ trễ một thông điệp
classic 127 800 msg/s p50 0,40 ms · p95 0,49 ms
quorum 81 967 msg/s (64%) p50 0,61 ms · p95 1,66 ms

Thông lượng còn 64%. Độ trễ trung vị tăng 50%, nhưng cột p95 mới là chỗ đáng nhìn: gấp 3,4 lần. Đó là hình dạng đặc trưng của một hệ đồng thuận — phần lớn thao tác nhanh, nhưng thỉnh thoảng phải chờ một vòng ghi nhật ký Raft hoàn tất.

Nhắc lại: đây là một node. Không có gói tin nào qua mạng, không có node nào phải đồng ý. Toàn bộ chênh lệch trên là chi phí của bản thân cơ chế Raft — ghi nhật ký, đánh chỉ số, xác nhận. Trên cụm ba node thật, cộng thêm độ trễ mạng nhân với số lần phải đợi đa số.

Bộ nhớ

100 000 thông điệp, cùng dữ liệu, đo tiến trình hàng đợi:

Bộ nhớ tiến trình hàng đợi messages_ram
classic 0,41 MiB 1
quorum 2,78 MiB 0

Gấp 6,8 lần. Quorum queue giữ nhật ký Raft cùng chỉ mục của nó trong bộ nhớ, và phần trước cho thấy classic thì gần như không giữ gì. Với vài chục hàng đợi thì không sao; với vài nghìn hàng đợi quorum thì đây là con số phải nhân lên trước khi quyết định.

Vậy mua được gì

Không mất thông điệp khi một node chết. Đây là toàn bộ lý do tồn tại của nó. Thông điệp chỉ được xác nhận sau khi đa số node đã ghi, nên mất một node trong cụm ba node không mất gì cả — thứ mà cơ chế nhân bản classic kiểu cũ không bảo đảm được trong mọi tình huống.

Hành vi rõ ràng khi phân vùng mạng. Raft có luật đa số; bên thiểu số biết mình là thiểu số và ngừng phục vụ, thay vì hai bên cùng nhận thông điệp rồi phải hoà giải sau.

x-delivery-limit chạy thật. Phần 20 đo được cơ chế này bắt đúng trường hợp consumer sập — thứ không bộ đếm nào trong mã của bạn bắt được, vì mã đó đã chết.

Khi nào vẫn dùng classic

Khi chỉ có một node. Quorum trên một node là trả toàn bộ chi phí Raft mà không nhận được gì — bảng trên chính là hoá đơn đó.

Khi cần priority. Không có đường vòng nào ngoài việc tách thành nhiều hàng đợi và tự chọn.

Khi hàng đợi là tạm hoặc chết theo connection — hàng đợi trả lời trong mẫu RPC, hàng đợi theo dõi của một client. Quorum từ chối thẳng những tổ hợp cờ đó.

Khi có rất nhiều hàng đợi. Nhân 2,78 MiB với số hàng đợi trước khi quyết định.

Ngoài bốn trường hợp trên, với hệ thống nhiều node thì quorum là mặc định đúng: cái giá 36% thông lượng đổi lấy việc không mất dữ liệu khi một máy chết là món hời ở gần như mọi nơi.

Bài sau: stream — kiểu hàng đợi thứ ba, với mô hình hoàn toàn khác hai cái này.

Thử ba mươi giây

# hang doi cua ban thuoc kieu nao, va ton bao nhieu bo nho
docker exec -u rabbitmq rmq rabbitmqctl -q list_queues name type messages memory \
  | sort -k4 -rn | head

Nếu thấy hàng đợi quorum trên một broker chỉ có một node, bạn đang trả giá Raft mà không nhận được tính sẵn sàng nào.