apache / apache/rocketmq

[Bug] Transactional message escape retry uses 2 ^ n (XOR) instead of exponential backoff, including a zero-delay retry

Open Beginner friendly
#10,989 1 comment 0 reactions 0 assignees View on GitHub
Dominant language
Java
Stars
22.6k
Forks
12k
Avg merge
3d 1h
Merged PRs (30d)
27

Description

### Before Creating the Bug Report

- [x] I found a bug, not just asking a question, which should be created in [GitHub Discussions](https://github.com/apache/rocketmq/discussions).
- [x] I have searched the [GitHub Issues](https://github.com/apache/rocketmq/issues) and [GitHub Discussions](https://github.com/apache/rocketmq/discussions) of this repository and believe this is not a duplicate.
- [x] I have confirmed that this bug belongs to the current repository, not other repositories of RocketMQ.

### Runtime platform environment

- OS: Linux
- Component: Broker (`TransactionalMessageServiceImpl`)

### RocketMQ version

- branch: develop
- Git commit id: e348efa66

### JDK Version

JDK 8

### Describe the Bug

In `TransactionalMessageServiceImpl#check`, when a broker configured with `enableSlaveActingMaster` fails to escape a transactional half message, the retry delay is computed with the XOR operator instead of exponentiation:

```java
if (escapeFailCnt < MAX_RETRY_TIMES_FOR_ESCAPE) {
escapeFailCnt++;
Thread.sleep(100L * (2 ^ escapeFailCnt)); // '^' is XOR in Java, not power
}
```

The actual sleep sequence for `escapeFailCnt` = 1..10 is `300, 0, 100, 600, 700, 400, 500, 1000, 1100, 800` ms instead of the intended exponential `200, 400, 800, ...`. In particular the second consecutive failure sleeps **0 ms**, so the check loop immediately hammers the store with another full put attempt, defeating the purpose of the backoff (this code was introduced with the escaping feature in #5012 / ISSUE #5012).

### Steps to Reproduce

`Thread.sleep(100L * (2 ^ 2))` evaluates to `sleep(0)` because `2 ^ 2 == 0`. Print `100L * (2 ^ n)` for n = 1..10 to see the non-monotonic sequence.

### What Did You Expect to See?

An exponential backoff between escape retries: `100L * (1 << escapeFailCnt)` ms, i.e. 200, 400, 800, ... ms.

### What Did You See Instead?

A non-monotonic sequence produced by XOR, including a 0 ms delay, so failed escape attempts are retried without any backoff.

### Additional Context

I extracted the computation into a small package-private helper so the backoff sequence is unit-testable, and will submit a PR with the test.

Contributor guide

Open the contributing guide

Research direction

Start at TransactionalMessageServiceImpl#check and trace the escape retry path for brokers with enableSlaveActingMaster. Extract the backoff computation into the mentioned package-private helper, then add a unit test covering the expected exponential sequence and the absence of zero-delay retries.

Written by the indexing model from the issue text.

Assessment

Tech stack
java
Domain
distributed-systems
Issue type
Bug
Difficulty
2/5
Estimated time
1-3 hours
Activity status
Active
Clarity
Clearly specified
Newbie friendliness
78/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.