[Bug] DefaultMQPushConsumer.shutdown() never stops the pop consume service, so its non-daemon threads keep the JVM alive
Maintainers usually reply within 1 day
Assessment
- Difficulty
- 2/5
- Estimated time
- 1-3 hours
- Newbie friendliness
- 65/100
Research direction
Start in client/.../impl/consumer/DefaultMQPushConsumerImpl.java: the normal shutdown path around line 908 stops only consumeMessageService, and the failed-start branch near lines 991-993 does the same. Add a shutdown call on consumeMessagePopService in both places, following the non-pop service. Done when DefaultMQPushConsumerImplTest, using its mocked consumeMessagePopService and testShutdown, verifies the pop service's shutdown is called. Check the two comments first, since the reporter says they plan to open the PR themselves.
Written by the indexing model from the issue text.
Description
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
OS: any (client side, DefaultMQPushConsumer)
RocketMQ version
branch: develop
Git commit id: a7edeecfb
JDK Version
Compiler: Temurin 1.8.0_504 (target 1.8, same as CI)
Describe the Bug
DefaultMQPushConsumerImpl.start() creates and starts two consume services, but shutdown() stops only the first one:
this.consumeMessageService.start();
// POPTODO
this.consumeMessagePopService.start(); // DefaultMQPushConsumerImpl.java:984-986
case RUNNING:
this.consumeMessageService.shutdown(awaitTerminateMillis); // :908 — the pop service is never stopped
The pop service is not idle after it is started: ConsumeMessagePopOrderlyService.start() schedules lockMQPeriodically() at a fixed rate on a single-thread executor created with new ThreadFactoryImpl("ConsumeMessageScheduledThread_") (client/.../ConsumeMessagePopOrderlyService.java:64-77), and ThreadFactoryImpl creates non-daemon threads by default (common/.../ThreadFactoryImpl.java:35, :59). ConsumeMessagePopConcurrentlyService.start() likewise starts its consume executor. Both services implement shutdown(long) (ConsumeMessagePopOrderlyService.java:80-88, ConsumeMessagePopConcurrentlyService.java:70), and no code in the client module ever calls it — the only references to the field are the two assignments, start() and submitPopConsumeRequest.
The same omission exists in the failed-start branch, which stops only consumeMessageService when the consumer group is already registered (DefaultMQPushConsumerImpl.java:991-993).
Steps to Reproduce
DefaultMQPushConsumer consumer = new DefaultMQPushConsumer("group"); consumer.subscribe("topic", "*"); consumer.registerMessageListener(listener);consumer.start(); consumer.shutdown();- The pop service is still running: for CLUSTERING (the default) the
ConsumeMessageScheduledThread_thread keeps firinglockMQPeriodically()everyREBALANCE_LOCK_INTERVALon an empty process-queue table, the threads are non-daemon, so a JVM that relies on the consumer's shutdown to release its resources never terminates.
At the unit level the existing harness covers it: DefaultMQPushConsumerImplTest already injects a mocked consumeMessagePopService (line ~250-254) and has a testShutdown, so asserting that the service is shut down with shutdown() fails deterministically today.
What Did You Expect to See?
DefaultMQPushConsumer.shutdown() "shut down this client and releasing underlying resources" (client/.../consumer/DefaultMQPushConsumer.java:789) — i.e. both consume services are stopped, exactly like the non-pop one in the same method. The pop services' own shutdown(long) methods are the proof that this was the intent.
What Did You See Instead?
After shutdown() the consumer's pop scheduler (and, for pop consumers, the pop consume executor) keeps running with non-daemon threads; the client appears stopped (serviceState = SHUTDOWN_ALREADY) while resources are still held, which can leave the JVM unable to exit.
Additional Context
Minimal fix: stop consumeMessagePopService next to consumeMessageService in both places — the normal shutdown path and the register-failure path.
I will open a PR with those two one-line changes plus a regression test in DefaultMQPushConsumerImplTest (red before: the pop service receives no shutdown call).
- Dominant language
- Java
- Stars
- 22.6k
- Forks
- 12k
- Avg merge
- 6d 16h
- Merged PRs (30d)
- 18
Getting set up
- No Dockerfile or Docker Compose file
- Has a pull request template
- Read the contributing guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
More from apache/rocketmq
-
[Bug] mqadmin createUser/updateUser trim the ACL password, storing a secret the caller never typedPossibly taken @iloveeyjafjalla claimed this 1 day ago. Open
Difficulty 2/5 1-3 hours Newbie friendliness 78/100
apache/rocketmq#11446 · 1 comment ·
Maintainers usually reply within 1 day
-
[Bug] notifyMasterOnline aborts the long-polling wake-up with a NullPointerException when an arrival drains a bucket firstPossibly taken @iloveeyjafjalla claimed this 3 days ago. Open
Difficulty 2/5 1-3 hours Newbie friendliness 82/100
apache/rocketmq#11441 · 1 comment ·
Maintainers usually reply within 1 day
-
[Enhancement] FilterFactory duplicate-registration and unregister semantics lack testsPossibly taken @chennaji9 claimed this 8 days ago. Open
Difficulty 2/5 1-3 hours Newbie friendliness 84/100
apache/rocketmq#11340 · 2 comments ·
Maintainers usually reply within 1 day
-
[Enhancement] LogicExpression three-valued truth table and short-circuit behavior lack direct testsPossibly taken @chennaji9 claimed this 8 days ago. Open
Difficulty 2/5 1-3 hours Newbie friendliness 78/100
apache/rocketmq#11339 · 2 comments ·
Maintainers usually reply within 1 day
-
[Enhancement] SyncStateInfo defensive-copy and epoch semantics lack direct testsPossibly taken @chennaji9 claimed this 8 days ago. Open
Difficulty 2/5 1-3 hours Newbie friendliness 78/100
apache/rocketmq#11338 · 2 comments ·
Maintainers usually reply within 1 day
Similar issues
-
Difficulty 2/5 1-3 hours Newbie friendliness 68/100
Vakore/ZappierGames#147 ·
-
spigotmc
Difficulty 1/5 Under an hour Newbie friendliness 92/100
Dans-Plugins/Wild-Pets#356 ·
Maintainers usually reply within 1 day
-
spigotmc
Difficulty 1/5 Under an hour Newbie friendliness 85/100
Dans-Plugins/Medieval-Roleplay-Engine#371 ·
Maintainers usually reply within 1 day
-
bug
Difficulty 2/5 1-3 hours Newbie friendliness 84/100
eddyizm/tempus#1220 · 1 comment ·
Maintainers usually reply within 2 days
-
ReplaceSchema画面のSQL表示で先頭/末尾に余分な空白が入るPossibly taken A pull request linked to this issue is open or already merged. Openbug javascript
Difficulty 1/5 Under an hour Newbie friendliness 95/100
dbflute/dbflute-intro#690 ·
Maintainers usually reply within 1 day