Before Creating the Bug Report
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<SendResult>.
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
- Send a
SendMessageRequest with 3 messages through the proxy (gRPC v2, local or cluster mode).
- Observe the broker stored 3 messages, but
SendMessageResponse.entries has size 1 (offset = only the first message's, messageId = comma-joined batch ids).
- 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
Before Creating the Bug Report
Runtime platform environment
All platforms; reproduced with a deterministic unit test on the current
developbranch.RocketMQ version
developatff8f6f74cJDK Version
All
Describe the Bug
The v2 gRPC protocol defines
SendMessageResponse.entriesas oneSendResultEntryper message of the request. The proxy, however, sends an N-messageSendMessageRequestto the broker as one remoting batch send and forwards the single batchSendResulttoSendMessageActivity#convertToSendMessageResponse, which builds exactly one response entry:LocalMessageService#sendMessagecollapsesmsgList.size() > 1into oneMessageBatchand returns a single-elementList<SendResult>.ClusterMessageService#sendMessagecallssendMessageAsync(..., msgList, ...)formsgList.size() != 1, which also returns oneSendResult(comma-joined per-message ids, first message's queue offset).convertToSendMessageResponseiterates 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#send0fails the send future withInternalErrorException("[Bug] due to an unknown reason from remote, received send receipt's quantity ... is not equal to sent message's quantity ...")whensendReceipts.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
SendMessageRequestwith 3 messages through the proxy (gRPC v2, local or cluster mode).SendMessageResponse.entrieshas size 1 (offset = only the first message's, messageId = comma-joined batch ids).What Did You Expect to See?
SendMessageResponse.entriesmust contain one entry per request message, with the per-messagemessageIdandqueueOffset + 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-joinedmsgId/offsetMsgId,queueOffset + iper entry, and reuse the single result for inner-batch-message responses without per-message ids). Applying the same expansion inSendMessageActivity#convertToSendMessageResponse(where the original request is available) fixes local and cluster mode at once, and a regression test inSendMessageActivityTestfails on unmodifieddevelop.Corresponding PR