apache / apache/rocketmq-spring
[Bug] RocketMQClientTemplate.receiveAsync closes the shared SimpleConsumer, breaking all subsequent operations
- Dominant language
- Java
- Stars
- 2.3k
- Forks
- 943
- PR merge metrics
- No merged PRs in 30d
Description
### Describe the Bug
`RocketMQClientTemplate.receiveAsync(...)` (rocketmq-v5-client-spring-boot) closes the **shared** `SimpleConsumer` right after starting an asynchronous receive:
```java
SimpleConsumer simpleConsumer = this.getSimpleConsumer();
CompletableFuture> future = simpleConsumer.receiveAsync(maxMessageNum, invisibleDuration);
simpleConsumer.close(); // closes the shared singleton
return future;
```
The `SimpleConsumer` is a template-level singleton whose lifecycle belongs to the Spring bean's `destroy()` (which already closes it). Closing it here (1) disrupts the in-flight async receive it just started, and (2) permanently breaks the template: every subsequent `receive`/`receiveAsync`/`ack`/`changeInvisibleDuration` on the same template fails because the consumer is already closed. The synchronous `receive()` sibling does not close the consumer.
### Steps to Reproduce
Call `receiveAsync` once, then any other consumer operation on the same template — it fails on the closed consumer.
### What Did You Expect to See?
`receiveAsync` behaves like `receive()`: uses the shared consumer and leaves its lifecycle to `destroy()`.
### What Did You See Instead?
The shared consumer is closed after the first `receiveAsync`, breaking the async path and everything after it.
### Additional Context
Fix incoming: remove the `simpleConsumer.close()` line; `destroy()` keeps its close. Includes a regression test with a recording fake consumer proving close is no longer invoked and the consumer stays usable.
Contributor guide
No contributing guide indexed for this repository
Research direction
Start at RocketMQClientTemplate.receiveAsync(...) in rocketmq-v5-client-spring-boot and compare it with receive() and destroy(). Run the described regression test with a recording fake consumer; done means the shared consumer remains usable after receiveAsync and its close is handled by destroy().
Written by the indexing model from the issue text.
Assessment
- Tech stack
- java
- Domain
- backend, distributed-systems
- Issue type
- Bug
- Difficulty
- 2/5
- Estimated time
- 1-3 hours
- Activity status
- Active
- Clarity
- Clearly specified
- Newbie friendliness
- 88/100