[Bug] Proxy gRPC batch send returns one receipt for N messages, so SDKs fail the send although the broker stored the batch
- Dominant language
- Java
- Stars
- 22.6k
- Forks
- 12k
- Avg merge
- 3d 1h
- Merged PRs (30d)
- 27
Description
### Before Creating the Bug Report
- [x] I found a bug, not just a question.
- [x] I searched open GitHub Issues and pull requests and found no duplicate.
- [x] I confirmed that this bug belongs to Apache RocketMQ.
### Runtime platform environment
All platforms; reproduced with a deterministic unit test on the current `develop` branch.
### RocketMQ version
`develop` at `ff8f6f74c`
### JDK Version
All
### Describe the Bug
The v2 gRPC protocol defines `SendMessageResponse.entries` as one `SendResultEntry` per message of the request. The proxy, however, sends an N-message `SendMessageRequest` to the broker as **one** remoting batch send and forwards the single batch `SendResult` to `SendMessageActivity#convertToSendMessageResponse`, which builds exactly **one** response entry:
- `LocalMessageService#sendMessage` collapses `msgList.size() > 1` into one `MessageBatch` and returns a single-element `List`.
- `ClusterMessageService#sendMessage` calls `sendMessageAsync(..., msgList, ...)` for `msgList.size() != 1`, which also returns one `SendResult` (comma-joined per-message ids, first message's queue offset).
- `convertToSendMessageResponse` iterates the result list one-to-one, so an N-message request gets a 1-entry response.
Every official v2 SDK validates the receipt count: rocketmq-clients Java `ProducerImpl#send0` fails the send future with `InternalErrorException("[Bug] due to an unknown reason from remote, received send receipt's quantity ... is not equal to sent message's quantity ...")` when `sendReceipts.size() != messages.size()`. So although the broker has **already stored all N messages**, the caller sees the send fail — and the SDK's automatic retry sends the whole batch again, duplicating every message.
### Steps to Reproduce
1. Send a `SendMessageRequest` with 3 messages through the proxy (gRPC v2, local or cluster mode).
2. Observe the broker stored 3 messages, but `SendMessageResponse.entries` has size 1 (offset = only the first message's, messageId = comma-joined batch ids).
3. rocketmq-clients Java/Go/C++/C# producers reject the response with the receipt-quantity error and retry, storing the batch a second time.
### What Did You Expect to See?
`SendMessageResponse.entries` must contain one entry per request message, with the per-message `messageId` and `queueOffset + i`, the same way the remoting client expands batch results (`ProduceAccumulator#splitSendResults`).
### What Did You See Instead?
One entry for the whole batch; SDKs fail the send although it was persisted, and retries duplicate the batch.
### Additional Context
The client module already contains the intended expansion algorithm (`ProduceAccumulator#splitSendResults`: split comma-joined `msgId`/`offsetMsgId`, `queueOffset + i` per entry, and reuse the single result for inner-batch-message responses without per-message ids). Applying the same expansion in `SendMessageActivity#convertToSendMessageResponse` (where the original request is available) fixes local and cluster mode at once, and a regression test in `SendMessageActivityTest` fails on unmodified `develop`.
### Corresponding PR
- PR apache/rocketmq#11050
Contributor guide
Research direction
Start in SendMessageActivity#convertToSendMessageResponse and read SendMessageActivityTest, then compare the existing ProduceAccumulator#splitSendResults behavior. Run the regression test for a three-message gRPC v2 request in local and cluster modes. Done means the response has one receipt per request message with the expected message IDs and queue offsets, without breaking inner-batch responses.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- grpc, java
- Domain
- api, backend
- Issue type
- Bug
- Difficulty
- 3/5
- Estimated time
- 1-2 days
- Activity status
- Stale
- Clarity
- Clearly specified
- Newbie friendliness
- 25/100