[Bug] Bare lock busy-wait without yield in ack/changeInvisibleTime processors pins ack executor threads

Đang mở
#11,383 1 bình luận 0 reaction 0 người được giao Xem trên GitHub

Maintainer thường phản hồi trong vòng 1 ngày

@messere1 đang làm issue này rồi.

Từ ngày 12/10/2026.

  • #11384 của @messere1 — đang mở

Đánh giá

Độ khó
2/5
Thời gian dự kiến
1-3 giờ
Mức phù hợp với người mới
35/100
Loại issue
Lỗi
Độ rõ ràng
Đặc tả rõ ràng
Mức độ hoạt động
Đình trệ
Công nghệ
java

Hướng nghiên cứu

Read the listed lock loops in AckMessageProcessor.java and ChangeInvisibleTimeProcessor.java, then compare them with PopConsumerService#tryLockForPop and the ackMessageExecutor registration in BrokerController.java. Check the linked pull request #11384 for current work. Done means the listed contended paths yield while preserving their lock behavior.

Do mô hình lập chỉ mục viết ra từ nội dung của issue.

Mô tả

Before Creating the Bug Report
  • I found a bug. The issue list contains no issue matching my bug.
Your Environment
  • RocketMQ version: 5.4.x (current develop)
Describe the Bug

Several pop/ack paths acquire the consumer/queue lock with a bare busy-wait that has no Thread.yield():

  • AckMessageProcessor#ackOrderly — while (!queueLockManager.tryLock(lockKey)) {} (broker/src/main/java/org/apache/rocketmq/broker/processor/AckMessageProcessor.java:405)
  • AckMessageProcessor#ackOrderly (KV service path) — while (!consumerLockService.tryLock(consumeGroup, topic)) {} (AckMessageProcessor.java:448)
  • AckMessageProcessor#ackLite — while (!consumerLockService.tryLock(lockKey)) {} (AckMessageProcessor.java:538)
  • ChangeInvisibleTimeProcessor#changeInvisibilityDuration — same shape (ChangeInvisibleTimeProcessor.java:203, :235, :389)

All of these are registered on the shared ackMessageExecutor (BrokerController.java:1204-1213), so each contending request pins one worker thread at 100% CPU for the entire contention window:

  1. Under a (group, topic) contention burst, several executor workers can be stuck spinning at once, cutting the executor's effective parallelism for every other ack / changeInvisibleTime request on the broker.
  2. On core-saturated brokers the spinning threads compete for CPU with the very lock holders that must finish for the spin to exit, extending the wait beyond the actual critical-section length.

The codebase already establishes the correct pattern for this lock service: PopConsumerService#tryLockForPop (added by #10828) spins with Thread.yield() and a comment explaining why the spin is kept — same consumerLockService, same contention shape, but it yields.

Steps to Reproduce

Code inspection + any workload where two consumers of the same group ack/changeInvisibleTime concurrently for the same topic: the losing request's thread spins in the while loop with no yield until the holder releases (or PopConsumerLockService.removeTimeout() self-heals a stale entry).

What Did You Expect to See?

The spin semantics preserved (these paths must hold the lock to commit offsets atomically) but with Thread.yield() in the loop, matching tryLockForPop — so a contending worker yields its core to the holder instead of burning it.

Additional Context

None

Ngôn ngữ chính
Java
Star
22.7k
Fork
12k
Merge trung bình
4 ngày 20 giờ
Pull request đã merge (30 ngày)
11

Chuẩn bị môi trường

Bắt đầu từ đâu

  1. Đọc hết issue, rồi đọc hướng dẫn đóng góp của dự án.
  2. Bình luận trên issue rằng bạn sẽ nhận — tránh hai người làm cùng một việc.
  3. Fork repository và làm thay đổi trên một nhánh.
  4. Mở pull request có tham chiếu số hiệu của issue.

Issue khác của apache/rocketmq

Tất cả issue của apache/rocketmq

Issue tương tự

Thêm issue về Java

Nhận issue mới trong hộp thư của bạn

Bản tóm tắt ngắn những issue GitHub phù hợp với người mới.