unbridled-41 opened a new issue, #11047: URL: https://github.com/apache/rocketmq/issues/11047
### Before Creating the Bug Report - [x] I found a bug, not just a question. - [x] I searched open GitHub Issues and pull requests and found no duplicate. - [x] I confirmed that this bug belongs to Apache RocketMQ. ### Runtime platform environment All platforms; reproduced with a deterministic unit test on the current `develop` branch. ### RocketMQ version `develop` at `ff8f6f74c` ### JDK Version All ### Describe the Bug When a batch of timer requests is enqueued, `TimerEnqueuePutService#fetchAndPutTimerRequest` retries the **whole** batch after any single request fails. The retry loop re-invokes `putMessageToTimerWheel` for every request in `trs` without checking `TimerRequest#isSucc`, so requests whose `doEnqueue` already succeeded in an earlier round are enqueued **again** — each retry round appends another `TimerLog` unit for the same message into the same timer-wheel slot. On dequeue every unit is an independent `MAGIC_DEFAULT` record, so the same scheduled message is converted and delivered to the real topic multiple times (and the slot `num` counter is inflated, skewing `getAllNum`/`isReject` flow-control decisions). A partial batch failure is realistic: `TimerLog#append` returns -1 when a new mapped file cannot be allocated in time (IO pressure at file rollover), and any unexpected throwable while `timerSkipUnknownError=false` fails the current request only — the other requests of the batch have already succeeded. ### Steps to Reproduce 1. Put two timer requests A and B into `enqueuePutQueue` (same or different delay times). 2. Make `doEnqueue` fail exactly once — for A's first attempt only (equivalent to a transient `TimerLog#append` failure). 3. Drive `TimerEnqueuePutService#fetchAndPutTimerRequest` once. 4. Observe that A's `doEnqueue` runs twice: the successful first attempt is re-processed by the retry round, appending a second `TimerLog` unit for the same message. ### What Did You Expect to See? The retry round must only re-process the requests that did not succeed; already-succeeded requests are never re-enqueued, so the message is delivered exactly once. ### What Did You See Instead? Round 2 re-enqueues A and the scheduled message is delivered twice (once per appended `TimerLog` unit). ### Additional Context Proposed fix: filter the batch on `TimerRequest#isSucc()` before each retry round (requests routed to the dequeue path are released with `succ=true` before the shared latch completes, so they are never re-put either). A regression test in `TimerMessageStoreTest` counts `doEnqueue` invocations per physical offset and fails on unmodified `develop`. -- 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]
