yx9o commented on code in PR #11480:
URL: https://github.com/apache/rocketmq/pull/11480#discussion_r4407959337


##########
client/src/main/java/org/apache/rocketmq/client/impl/consumer/ConsumeMessagePopOrderlyService.java:
##########
@@ -311,16 +452,178 @@ public int getShardingKeyIndex() {
             return shardingKeyIndex;
         }
 
+        private void mergeFrom(final ConsumeRequest other) {
+            PopConsumeBatch batch;
+            while ((batch = other.pendingBatches.poll()) != null) {
+                this.pendingBatches.add(batch);
+            }
+        }
+
+        private void releaseWaitAck(int count) {
+            if (count > 0) {
+                this.processQueue.decFoundMsg(-count);
+            }
+        }
+
+        private void releaseWaitAckForPendingBatches() {
+            PopConsumeBatch batch;
+            while ((batch = this.pendingBatches.poll()) != null) {
+                releaseWaitAck(batch.getMsgs().size());
+            }
+        }
+
+        /**
+         * Remove this exact request from the shared set. ConsumeRequest 
equality is based on the
+         * queue/shard, so a plain remove could delete a successor that was 
submitted after this
+         * request had already been unregistered. Callers must hold the 
consume request lock.
+         */
+        private void removeFromConsumeRequestSet() {
+            for (ConsumeRequest request : consumeRequestSet) {
+                if (request == this) {
+                    consumeRequestSet.remove(request);
+                    return;
+                }
+            }
+        }
+
+        private void abortRemainingBatches() {
+            Object lock = 
consumeRequestLock.fetchLockObject(this.messageQueue, this.shardingKeyIndex);
+            synchronized (lock) {
+                removeFromConsumeRequestSet();
+                releaseWaitAckForPendingBatches();
+            }
+        }
+
         @Override
         public void run() {
+            try {
+                for (;;) {
+                    PopConsumeBatch batch = this.pendingBatches.poll();
+                    if (batch == null) {
+                        // Check for work that was merged in while the last 
batch was being consumed,
+                        // then remove the request atomically under the 
submission lock. Without this
+                        // lock a concurrently submitted request could be 
appended after the empty
+                        // check but before removal and would never be 
consumed.
+                        Object lock = 
consumeRequestLock.fetchLockObject(this.messageQueue, this.shardingKeyIndex);
+                        synchronized (lock) {
+                            batch = this.pendingBatches.poll();
+                            if (batch == null) {
+                                removeFromConsumeRequestSet();
+                                return;
+                            }
+                        }
+                    }
+
+                    if (!consumeBatch(batch)) {
+                        abortRemainingBatches();
+                        return;
+                    }
+                }
+            } finally {
+                abortRemainingBatches();
+            }
+        }
+
+        private boolean consumeBatch(final PopConsumeBatch batch) {
+            final List<MessageExt> msgs = batch.getMsgs();
             if (this.processQueue.isDropped()) {
                 log.warn("run, message queue not be able to consume, because 
it's dropped. {}", this.messageQueue);
-                
ConsumeMessagePopOrderlyService.this.removeConsumeRequest(this);
-                return;
+                releaseWaitAck(msgs.size());
+                return false;
+            }
+
+            if (batch.isPopTimeout()) {
+                log.info("the pop message time out so abort consume. 
popTime={} invisibleTime={}, group={} {}",
+                    batch.getPopTime(), batch.getInvisibleTime(), 
consumerGroup, this.messageQueue);
+                releaseWaitAck(msgs.size());
+                return false;
             }
 
-            // lock on sharding key index
-            final Object objLock = 
messageQueueLock.fetchLockObject(this.messageQueue, shardingKeyIndex);
+            // Ordered consumption must serialize all batches of the same 
queue/shard. The sharding
+            // key index stays 0 because POP consumes the queue as a single 
ordered lane.
+            final Object objLock = 
messageQueueLock.fetchLockObject(this.messageQueue, this.shardingKeyIndex);
+            synchronized (objLock) {
+                if (this.processQueue.isDropped()) {
+                    log.warn("message queue not be able to consume, because 
it's dropped. {}", this.messageQueue);
+                    releaseWaitAck(msgs.size());
+                    return false;
+                }
+
+                if (batch.isPopTimeout()) {
+                    log.info("the pop message time out before consume. 
popTime={} invisibleTime={}, group={} {}",
+                        batch.getPopTime(), batch.getInvisibleTime(), 
consumerGroup, this.messageQueue);
+                    releaseWaitAck(msgs.size());
+                    return false;
+                }
+
+                defaultMQPushConsumerImpl.resetRetryAndNamespace(msgs, 
consumerGroup);
+
+                ConsumeOrderlyContext context = new 
ConsumeOrderlyContext(this.messageQueue);
+                ConsumeMessageContext consumeMessageContext = null;
+                if (defaultMQPushConsumerImpl.hasHook()) {
+                    consumeMessageContext = new ConsumeMessageContext();
+                    
consumeMessageContext.setNamespace(defaultMQPushConsumer.getNamespace());
+                    
consumeMessageContext.setConsumerGroup(defaultMQPushConsumer.getConsumerGroup());
+                    consumeMessageContext.setProps(new HashMap<>());
+                    consumeMessageContext.setMq(this.messageQueue);
+                    consumeMessageContext.setMsgList(msgs);
+                    consumeMessageContext.setSuccess(false);
+                    
defaultMQPushConsumerImpl.executeHookBefore(consumeMessageContext);
+                }
+
+                long beginTimestamp = System.currentTimeMillis();
+                boolean hasException = false;
+                ConsumeReturnType returnType = ConsumeReturnType.SUCCESS;
+                ConsumeOrderlyStatus status = null;
+                try {
+                    for (MessageExt msg : msgs) {
+                        MessageAccessor.setConsumeStartTimeStamp(msg, 
String.valueOf(System.currentTimeMillis()));
+                    }
+                    status = 
messageListener.consumeMessage(Collections.unmodifiableList(msgs), context);

Review Comment:
   Could we split the POP result by `consumeMessageBatchMaxSize` and consume 
the sub-batches in order? With the default batch size of 1, a listener may 
process only `msgs.get(0)`, but returning `SUCCESS` here acknowledges the 
entire POP result.



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]

Reply via email to