apache / apache/rocketmq-spring

[Bug] RocketMQClientTemplate.receiveAsync closes the shared SimpleConsumer, breaking all subsequent operations

Open Beginner friendly
#780 3 comments 0 reactions 0 assignees View on GitHub
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

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.