RockteMQ-AI commented on code in PR #10528:
URL: https://github.com/apache/rocketmq/pull/10528#discussion_r3867553364


##########
store/src/main/java/org/apache/rocketmq/store/index/IndexService.java:
##########
@@ -215,10 +216,12 @@ public QueryOffsetResult queryOffset(String topic, String 
key, int maxNum, long
     }
 
     private String buildKey(final String topic, final String key) {
-        return topic + "#" + key;
+        reusableKeyBuilder.setLength(0);

Review Comment:
   **[Critical] Thread-safety bug**: `reusableKeyBuilder` is an instance field 
shared across threads, but `buildKey()` is called from `queryOffset()` which 
can be invoked concurrently by multiple admin/consumer query threads. 
StringBuilder is NOT thread-safe — concurrent calls to `setLength(0)` and 
`append()` will interleave, producing corrupted keys like `topic1#key2` instead 
of `topic1#key1`.
   
   This will cause index lookups to return wrong results or miss entries 
entirely.
   
   **Fix**: Use `ThreadLocal<StringBuilder>` instead of a shared instance 
field, or revert to string concatenation (the JIT compiler optimizes it well).



##########
store/src/main/java/org/apache/rocketmq/store/timer/TimerWheel.java:
##########
@@ -128,17 +130,25 @@ public void flush() {
         if (mappedByteBuffer == null) {
             return;
         }
+        if (!dirty) {

Review Comment:
   **[Critical] Thread-safety bug**: The `dirty` flag is instance-level (shared 
across threads), but `localBuffer` is `ThreadLocal<ByteBuffer>` (per-thread). 
This creates a race condition:
   
   1. Thread A calls `putSlot()` → sets `dirty = true`
   2. Thread B calls `flush()` → sees `dirty = true` → flushes Thread B's local 
buffer
   3. Thread B's buffer may be uninitialized (all zeros) or contain stale data
   4. Flushing zeros over the mapped file corrupts the timer wheel
   
   **Fix**: Make `dirty` a `ThreadLocal<Boolean>` to match the per-thread 
buffer, or use a synchronized block around flush operations.



##########
store/src/main/java/org/apache/rocketmq/store/queue/QueueOffsetOperator.java:
##########
@@ -123,10 +132,18 @@ public void setLmqTopicQueueTable(ConcurrentMap<String, 
Long> lmqTopicQueueTable
     }
 
     public ConcurrentMap<String, Long> getTopicQueueTable() {
-        return topicQueueTable;
+        ConcurrentMap<String, Long> snapshot = new 
ConcurrentHashMap<>(this.topicQueueTable.size() * 2);

Review Comment:
   **[Warning] Behavioral change**: `getTopicQueueTable()` now returns a 
snapshot copy instead of the live map. While this is safer for thread safety, 
it's a behavioral change that could affect callers who expected to modify the 
map directly.
   
   If any code relies on modifying the returned map and seeing those changes 
reflected in the internal state, it will break silently.
   
   **Recommendation**: Document this change in the method's Javadoc, or 
consider whether callers actually need live access.



-- 
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]

Reply via email to