Skip to content

[Bug] Shared mutable batchAckIndexList in PopBufferMergeService causes toStoreBits update failure when enablePopBatchAck and appendAckAsync are both enabled #10256

@LiyunZhang10

Description

@LiyunZhang10

Before Creating the Bug Report

  • I found a bug, not just asking a question, which should be created in GitHub Discussions.

  • I have searched the GitHub Issues and GitHub Discussions of this repository and believe that this is not a duplicate.

  • I have confirmed that this bug belongs to the current repository, not other repositories of RocketMQ.

Runtime platform environment

All platforms

RocketMQ version

develop branch

JDK Version

JDK 8+

Describe the Bug

In PopBufferMergeService.scan(), when enablePopBatchAck is true, the method collects ACK indices into a class-level shared variable batchAckIndexList, then passes this same list reference to putBatchAckToStore(). Immediately afterwards, it calls indexList.clear() in the finally block.

When appendAckAsync is also true, putBatchAckToStore() performs an asynchronous write. The async callback handleBatchAckPutMessageResult captures the same list reference. By the time the callback executes, the list has already been cleared (or contains stale data from the next loop iteration).

As a result, pointWrapper.getToStoreBits() is never updated correctly. This causes isCkDoneForFinish() to always return false, preventing PopCheckPointWrapper objects from ever being removed from the commitOffsets queue (an unbounded LinkedBlockingDeque). Over time, this leads to unbounded memory growth and eventually OOM.

Steps to Reproduce

Set enablePopBatchAck=true and appendAckAsync=true
Run a Pop consumer with moderate traffic
Observe that commitOffsets queue size grows indefinitely

What Did You Expect to See?

toStoreBits should be correctly updated after async IO completes. commitOffsets queue should be drained normally.

What Did You See Instead?

toStoreBits remains 0. commitOffsets queue grows without bound.

Additional Context

Fix: Pass a defensive copy new ArrayList<>(indexList) instead of the shared reference.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions