Repository: apache/rocketmq (Java) — Bug Fix

Issue:
ConcurrentHashMap#computeIfAbsent have performance problem in jdk1.8 QueueOffsetAssigner#assignQueueOffset use it.
![image](https://user-images.githubusercontent.com/15797831/188348276-afdcb5d0-ab66-413c-842f-5a09840619e7.png)
[openjdk bug report](https://bugs.openjdk.org/browse/JDK-8161372) . 
use JMH to test it:
```java
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import org.openjdk.jmh.annotations.Benchmark;
import org.openjdk.jmh.annotations.Fork;
import org.openjdk.jmh.annotations.Level;
import org.openjdk.jmh.annotations.Measurement;
import org.openjdk.jmh.annotations.Scope;
import org.openjdk.jmh.annotations.Setup;
import org.openjdk.jmh.annotations.State;
import org.openjdk.jmh.annotations.Threads;
import org.openjdk.jmh.annotations.Warmup;


@Warmup(iterations = 2, time = 5)
@Measurement(iterations = 3, time = 5)
@State(Scope.Benchmark)
@Fork(2)
public class HashMapBenchmark {

    private static final String KEY = "mxsm";

    private final Map<String, Object> concurrentMap = new ConcurrentHashMap<>();

    @Setup(Level.Iteration)
    public void setup() {
        concurrentMap.clear();
    }

    @Benchmark
    @Threads(16)
    public Object benchmarkGetBeforeComputeIfAbsent() {
        Object result = concurrentMap.get(KEY);
        if (null == result) {
            result = concurrentMap.computeIfAbsent(KEY, key -> 1);
        }
        return result;
    }

    @Benchmark
    @Threads(16)
    public Object benchmarkComputeIfAbsent() {
        return concurrentMap.computeIfAbsent(KEY, key -> 1);
    }

}
```
jdk version:
openjdk version "1.8.0_312"
OpenJDK Runtime Environment (build 1.8.0_312-8u312-b07-0ubuntu1~18.04-b07)
OpenJDK 64-Bit Server VM (build 25.312-b07, mixed mode)
```
root@GZYZFA00088591:/mnt/d/develop/github/benchmark/dledger-benchmark# java -jar target/dledger-benchmarks.jar com.github.mxsm.dledger.benchmark.ConcurrentHashMapBenchmark
# JMH version: 1.35
# VM version: JDK 1.8.0_312, OpenJDK 64-Bit Server VM, 25.312-b07
# VM invoker: /usr/lib/jvm/java-8-openjdk-amd64/jre/bin/java
# VM options: <none>
# Blackhole mode: full + dont-inline hint (auto-detected, use -Djmh.blackhole.autoDetect=false to disable)
# Warmup: 2 iterations, 5 s each
# Measurement: 3 iterations, 5 s each
# Timeout: 10 min per iteration
# Threads: 16 threads, will synchronize iterations
# Benchmark mode: Throughput, ops/time
# Benchmark: com.github.mxsm.dledger.benchmark.ConcurrentHashMapBenchmark.benchmarkComputeIfAbsent

# Run progress: 0.00% complete, ETA 00:01:40
# Fork: 1 of 2
# Warmup Iteration   1: 42984830.445 ops/s
# Warmup Iteration   2: 38416032.086 ops/s
Iteration   1: 39423217.883 ops/s
Iteration   2: 41176380.793 ops/s
Iteration   3: 41146374.512 ops/s

# Run progress: 25.00% complete, ETA 00:01:15
# Fork: 2 of 2
# Warmup Iteration   1: 42000026.386 ops/s
# Warmup Iteration   2: 37801042.365 ops/s
Iteration   1: 38829603.637 ops/s
Iteration   2: 39873697.470 ops/s
Iteration   3: 39745609.268 ops/s


Result "com.github.mxsm.dledger.benchmark.ConcurrentHashMapBenchmark.benchmarkComputeIfAbsent":
  40032480.594 ±(99.9%) 2652852.116 ops/s [Average]
  (min, avg, max) = (38829603.637, 40032480.594, 41176380.793), stdev = 946032.620
  CI (99.9%): [37379628.478, 42685332.710] (assumes normal distribution)


# JMH version: 1.35
# VM version: JDK 1.8.0_312, OpenJDK 64-Bit Server VM, 25.312-b07
# VM invoker: /usr/lib/jvm/java-8-openjdk-amd64/jre/bin/java
# VM options: <none>
# Blackhole mode: full + dont-inline hint (auto-detected, use -Djmh.blackhole.autoDetect=false to disable)
# Warmup: 2 iterations, 5 s each
# Measurement: 3 iterations, 5 s each
# Timeout: 10 min per iteration
# Threads: 16 threads, will synchronize iterations
# Benchmark mode: Throughput, ops/time
# Benchmark: com.github.mxsm.dledger.benchmark.ConcurrentHashMapBenchmark.benchmarkGetBeforeComputeIfAbsent

# Run progress: 50.00% complete, ETA 00:00:50
# Fork: 1 of 2
# Warmup Iteration   1: 867041643.912 ops/s
# Warmup Iteration   2: 879285059.514 ops/s
Iteration   1: 946381466.173 ops/s
Iteration   2: 829405048.842 ops/s
Iteration   3: 904068960.001 ops/s

# Run progress: 75.00% complete, ETA 00:00:25
# Fork: 2 of 2
# Warmup Iteration   1: 801375284.635 ops/s
# Warmup Iteration   2: 1023301849.860 ops/s
Iteration   1: 864155832.112 ops/s
Iteration   2: 939504012.429 ops/s
Iteration   3: 913166819.166 ops/s


Result "com.github.mxsm.dledger.benchmark.ConcurrentHashMapBenchmark.benchmarkGetBeforeComputeIfAbsent":
  899447023.121 ±(99.9%) 126458224.528 ops/s [Average]
  (min, avg, max) = (829405048.842, 899447023.121, 946381466.173), stdev = 45096221.050
  CI (99.9%): [772988798.593, 1025905247.649] (assumes normal distribution)


# Run complete. Total time: 00:01:41

REMEMBER: The numbers below are just data. To gain reusable insights, you need to follow up on
why the numbers are the way they are. Use profilers (see -prof, -lprof), design factorial
experiments, perform baseline and negative tests that provide experimental control, make sure
the benchmarking environment is safe on JVM/OS/HW level, ask for reviews from the domain experts.
Do not assume the numbers tell you what you want them to tell.

Benchmark                                                      Mode  Cnt          Score           Error  Units
ConcurrentHashMapBenchmark.benchmarkComputeIfAbsent           thrpt    6   40032480.594 ±   2652852.116  ops/s
ConcurrentHashMapBenchmark.benchmarkGetBeforeComputeIfAbsent  thrpt    6  899447023.121 ± 126458224.528  ops/s
```
jdk11 version：
openjdk version "11.0.14.1" 2022-02-08
OpenJDK Runtime Environment (build 11.0.14.1+1-Ubuntu-0ubuntu1.18.04)
OpenJDK 64-Bit Server VM (build 11.0.14.1+1-Ubuntu-0ubuntu1.18.04, mixed mode, sharing)
```
root@GZYZFA00088591:/mnt/d/develop/github/benchmark/dledger-benchmark# java -jar target/dledger-benchmarks.jar com.github.mxsm.dledger.benchmark.ConcurrentHashMapBenchmark
WARNING: An illegal reflective access operation has occurred
WARNING: Illegal reflective access by org.openjdk.jmh.util.Utils (file:/mnt/d/develop/github/benchmark/dledger-benchmark/target/dledger-benchmarks.jar) to method java.io.Console.encoding()
WARNING: Please consider reporting this to the maintainers of org.openjdk.jmh.util.Utils
WARNING: Use --illegal-access=warn to enable warnings of further illegal reflective access operations
WARNING: All illegal access operations will be denied in a future release
# JMH version: 1.35
# VM version: JDK 11.0.14.1, OpenJDK 64-Bit Server VM, 11.0.14.1+1-Ubuntu-0ubuntu1.18.04
# VM invoker: /usr/lib/jvm/java-11-openjdk-amd64/bin/java
# VM options: <none>
# Blackhole mode: full + dont-inline hint (auto-detected, use -Djmh.blackhole.autoDetect=false to disable)
# Warmup: 2 iterations, 5 s each
# Measurement: 3 iterations, 5 s each
# Timeout: 10 min per iteration
# Threads: 16 threads, will synchronize iterations
# Benchmark mode: Throughput, ops/time
# Benchmark: com.github.mxsm.dledger.benchmark.ConcurrentHashMapBenchmark.benchmarkComputeIfAbsent

# Run progress: 0.00% complete, ETA 00:01:40
# Fork: 1 of 2
# Warmup Iteration   1: 734790850.730 ops/s
# Warmup Iteration   2: 723040919.893 ops/s
Iteration   1: 641529840.743 ops/s
Iteration   2: 711719044.738 ops/s
Iteration   3: 683114525.450 ops/s

# Run progress: 25.00% complete, ETA 00:01:16
# Fork: 2 of 2
# Warmup Iteration   1: 658869896.647 ops/s
# Warmup Iteration   2: 634635199.677 ops/s
Iteration   1: 682154939.057 ops/s
Iteration   2: 684056273.846 ops/s
Iteration   3: 684466801.465 ops/s


Result "com.github.mxsm.dledger.benchmark.ConcurrentHashMapBenchmark.benchmarkComputeIfAbsent":
  681173570.883 ±(99.9%) 63060380.600 ops/s [Average]
  (min, avg, max) = (641529840.743, 681173570.883, 711719044.738), stdev = 22487939.188
  CI (99.9%): [618113190.283, 744233951.483] (assumes normal distribution)


# JMH version: 1.35
# VM version: JDK 11.0.14.1, OpenJDK 64-Bit Server VM, 11.0.14.1+1-Ubuntu-0ubuntu1.18.04
# VM invoker: /usr/lib/jvm/java-11-openjdk-amd64/bin/java
# VM options: <none>
# Blackhole mode: full + dont-inline hint (auto-detected, use -Djmh.blackhole.autoDetect=false to disable)
# Warmup: 2 iterations, 5 s each
# Measurement: 3 iterations, 5 s each
# Timeout: 10 min per iteration
# Threads: 16 threads, will synchronize iterations
# Benchmark mode: Throughput, ops/time
# Benchmark: com.github.mxsm.dledger.benchmark.ConcurrentHashMapBenchmark.benchmarkGetBeforeComputeIfAbsent

# Run progress: 50.00% complete, ETA 00:00:50
# Fork: 1 of 2
# Warmup Iteration   1: 913175624.693 ops/s
# Warmup Iteration   2: 889999077.476 ops/s
Iteration   1: 755423064.811 ops/s
Iteration   2: 819973081.401 ops/s
Iteration   3: 819069238.312 ops/s

# Run progress: 75.00% complete, ETA 00:00:25
# Fork: 2 of 2
# Warmup Iteration   1: 755788304.744 ops/s
# Warmup Iteration   2: 749372817.128 ops/s
Iteration   1: 840515776.127 ops/s
Iteration   2: 872478443.625 ops/s
Iteration   3: 848340384.022 ops/s


Result "com.github.mxsm.dledger.benchmark.ConcurrentHashMapBenchmark.benchmarkGetBeforeComputeIfAbsent":
  825966664.716 ±(99.9%) 111714396.868 ops/s [Average]
  (min, avg, max) = (755423064.811, 825966664.716, 872478443.625), stdev = 39838430.078
  CI (99.9%): [714252267.848, 937681061.585] (assumes normal distribution)


# Run complete. Total time: 00:01:41

REMEMBER: The numbers below are just data. To gain reusable insights, you need to follow up on
why the numbers are the way they are. Use profilers (see -prof, -lprof), design factorial
experiments, perform baseline and negative tests that provide experimental control, make sure
the benchmarking environment is safe on JVM/OS/HW level, ask for reviews from the domain experts.
Do not assume the numbers tell you what you want them to tell.

Benchmark                                                      Mode  Cnt          Score           Error  Units
ConcurrentHashMapBenchmark.benchmarkComputeIfAbsent           thrpt    6  681173570.883 ±  63060380.600  ops/s
ConcurrentHashMapBenchmark.benchmarkGetBeforeComputeIfAbsent  thrpt    6  825966664.716 ± 111714396.868  ops/s
```
jdk version >= 9 has fixed it.  RocketMQ run on jdk8+ so we need to solve it.


Discussion:
Good catch. Do you have the plan to fix it?
> Good catch. Do you have the plan to fix it?

I will submit a PR to fix it

Resolving patch:
diff --git a/srvutil/src/main/java/org/apache/rocketmq/srvutil/ConcurrentHashMapUtil.java b/common/src/main/java/org/apache/rocketmq/common/utils/ConcurrentHashMapUtils.java
similarity index 77%
rename from srvutil/src/main/java/org/apache/rocketmq/srvutil/ConcurrentHashMapUtil.java
rename to common/src/main/java/org/apache/rocketmq/common/utils/ConcurrentHashMapUtils.java
--- a/srvutil/src/main/java/org/apache/rocketmq/srvutil/ConcurrentHashMapUtil.java
+++ b/common/src/main/java/org/apache/rocketmq/common/utils/ConcurrentHashMapUtils.java
@@ -14,23 +14,21 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package org.apache.rocketmq.srvutil;
+package org.apache.rocketmq.common.utils;
 
 import java.util.concurrent.ConcurrentMap;
 import java.util.function.Function;
 
-public class ConcurrentHashMapUtil {
+public abstract class ConcurrentHashMapUtils {
+
     private static final boolean IS_JDK8;
 
     static {
-        // Java 8 or lower: 1.6.0_23, 1.7.0, 1.7.0_80, 1.8.0_211
-        // Java 9 or higher: 9.0.1, 11.0.4, 12, 12.0.1
+        // Java 8
+        // Java 9+: 9,11,17
         IS_JDK8 = System.getProperty("java.version").startsWith("1.8.");
     }
 
-    private ConcurrentHashMapUtil() {
-    }
-
     /**
      * A temporary workaround for Java 8 specific performance issue JDK-8161372 .<br> Use implementation of
      * ConcurrentMap.computeIfAbsent instead.
@@ -39,10 +37,11 @@ private ConcurrentHashMapUtil() {
      */
     public static <K, V> V computeIfAbsent(ConcurrentMap<K, V> map, K key, Function<? super K, ? extends V> func) {
         if (IS_JDK8) {
-            V v, newValue;
-            return ((v = map.get(key)) == null &&
-                (newValue = func.apply(key)) != null &&
-                (v = map.putIfAbsent(key, newValue)) == null) ? newValue : v;
+            V v = map.get(key);
+            if (null == v) {
+                v = map.computeIfAbsent(key, func);
+            }
+            return v;
         } else {
             return map.computeIfAbsent(key, func);
         }
diff --git a/namesrv/src/main/java/org/apache/rocketmq/namesrv/routeinfo/RouteInfoManager.java b/namesrv/src/main/java/org/apache/rocketmq/namesrv/routeinfo/RouteInfoManager.java
--- a/namesrv/src/main/java/org/apache/rocketmq/namesrv/routeinfo/RouteInfoManager.java
+++ b/namesrv/src/main/java/org/apache/rocketmq/namesrv/routeinfo/RouteInfoManager.java
@@ -47,6 +47,7 @@
 import org.apache.rocketmq.common.protocol.RequestCode;
 import org.apache.rocketmq.common.protocol.body.TopicConfigAndMappingSerializeWrapper;
 import org.apache.rocketmq.common.topic.TopicValidator;
+import org.apache.rocketmq.common.utils.ConcurrentHashMapUtils;
 import org.apache.rocketmq.logging.InternalLogger;
 import org.apache.rocketmq.logging.InternalLoggerFactory;
 import org.apache.rocketmq.common.namesrv.RegisterBrokerResult;
@@ -201,16 +202,16 @@ public TopicList getAllTopicList() {
     }
 
     public RegisterBrokerResult registerBroker(
-            final String clusterName,
-            final String brokerAddr,
-            final String brokerName,
-            final long brokerId,
-            final String haServerAddr,
-            final String zoneName,
-            final Long timeoutMillis,
-            final TopicConfigSerializeWrapper topicConfigWrapper,
-            final List<String> filterServerList,
-            final Channel channel) {
+        final String clusterName,
+        final String brokerAddr,
+        final String brokerName,
+        final long brokerId,
+        final String haServerAddr,
+        final String zoneName,
+        final Long timeoutMillis,
+        final TopicConfigSerializeWrapper topicConfigWrapper,
+        final List<String> filterServerList,
+        final Channel channel) {
         return registerBroker(clusterName, brokerAddr, brokerName, brokerId, haServerAddr, zoneName, timeoutMillis, false, topicConfigWrapper, filterServerList, channel);
     }
 
@@ -231,7 +232,7 @@ public RegisterBrokerResult registerBroker(
             this.lock.writeLock().lockInterruptibly();
 
             //init or update the cluster info
-            Set<String> brokerNames = this.clusterAddrTable.computeIfAbsent(clusterName, k -> new HashSet<>());
+            Set<String> brokerNames = ConcurrentHashMapUtils.computeIfAbsent((ConcurrentHashMap<String, Set<String>>) this.clusterAddrTable, clusterName, k -> new HashSet<>());
             brokerNames.add(brokerName);
 
             boolean registerFirst = false;
@@ -273,8 +274,8 @@ public RegisterBrokerResult registerBroker(
                     long newStateVersion = topicConfigWrapper.getDataVersion().getStateVersion();
                     if (oldStateVersion > newStateVersion) {
                         log.warn("Registered Broker conflicts with the existed one, just ignore.: Cluster:{}, BrokerName:{}, BrokerId:{}, " +
-                                        "Old BrokerAddr:{}, Old Version:{}, New BrokerAddr:{}, New Version:{}.",
-                                clusterName, brokerName, brokerId, oldBrokerAddr, oldStateVersion, brokerAddr, newStateVersion);
+                                "Old BrokerAddr:{}, Old Version:{}, New BrokerAddr:{}, New Version:{}.",
+                            clusterName, brokerName, brokerId, oldBrokerAddr, oldStateVersion, brokerAddr, newStateVersion);
                         //Remove the rejected brokerAddr from brokerLiveTable.
                         brokerLiveTable.remove(new BrokerAddrInfo(clusterName, brokerAddr));
                         return result;
@@ -284,7 +285,7 @@ public RegisterBrokerResult registerBroker(
 
             if (!brokerAddrsMap.containsKey(brokerId) && topicConfigWrapper.getTopicConfigTable().size() == 1) {
                 log.warn("Can't register topicConfigWrapper={} because broker[{}]={} has not registered.",
-                        topicConfigWrapper.getTopicConfigTable(), brokerId, brokerAddr);
+                    topicConfigWrapper.getTopicConfigTable(), brokerId, brokerAddr);
                 return null;
             }
 
@@ -293,17 +294,17 @@ public RegisterBrokerResult registerBroker(
 
             boolean isMaster = MixAll.MASTER_ID == brokerId;
             boolean isPrimeSlave = !isOldVersionBroker && !isMaster
-                    && brokerId == Collections.min(brokerAddrsMap.keySet());
+                && brokerId == Collections.min(brokerAddrsMap.keySet());
 
             if (null != topicConfigWrapper && (isMaster || isPrimeSlave)) {
 
                 ConcurrentMap<String, TopicConfig> tcTable =
-                        topicConfigWrapper.getTopicConfigTable();
+                    topicConfigWrapper.getTopicConfigTable();
                 if (tcTable != null) {
                     for (Map.Entry<String, TopicConfig> entry : tcTable.entrySet()) {
                         if (registerFirst || this.isTopicConfigChanged(clusterName, brokerAddr,
-                                topicConfigWrapper.getDataVersion(), brokerName,
-                                entry.getValue().getTopicName())) {
+                            topicConfigWrapper.getDataVersion(), brokerName,
+                            entry.getValue().getTopicName())) {
                             final TopicConfig topicConfig = entry.getValue();
                             if (isPrimeSlave) {
                                 // Wipe write perm for prime slave
@@ -331,12 +332,12 @@ public RegisterBrokerResult registerBroker(
 
             BrokerAddrInfo brokerAddrInfo = new BrokerAddrInfo(clusterName, brokerAddr);
             BrokerLiveInfo prevBrokerLiveInfo = this.brokerLiveTable.put(brokerAddrInfo,
-                    new BrokerLiveInfo(
-                            System.currentTimeMillis(),
-                            timeoutMillis == null ? DEFAULT_BROKER_CHANNEL_EXPIRED_TIME : timeoutMillis,
-                            topicConfigWrapper == null ? new DataVersion() : topicConfigWrapper.getDataVersion(),
-                            channel,
-                            haServerAddr));
+                new BrokerLiveInfo(
+                    System.currentTimeMillis(),
+                    timeoutMillis == null ? DEFAULT_BROKER_CHANNEL_EXPIRED_TIME : timeoutMillis,
+                    topicConfigWrapper == null ? new DataVersion() : topicConfigWrapper.getDataVersion(),
+                    channel,
+                    haServerAddr));
             if (null == prevBrokerLiveInfo) {
                 log.info("new broker registered, {} HAService: {}", brokerAddrInfo, haServerAddr);
             }
@@ -363,7 +364,7 @@ public RegisterBrokerResult registerBroker(
 
             if (isMinBrokerIdChanged && namesrvConfig.isNotifyMinBrokerIdChanged()) {
                 notifyMinBrokerIdChanged(brokerAddrsMap, null,
-                        this.brokerLiveTable.get(brokerAddrInfo).getHaServerAddr());
+                    this.brokerLiveTable.get(brokerAddrInfo).getHaServerAddr());
             }
         } catch (Exception e) {
             log.error("registerBroker Exception", e);
@@ -679,9 +680,9 @@ public TopicRouteData pickupTopicRouteData(final String topic) {
                         continue;
                     }
                     BrokerData brokerDataClone = new BrokerData(brokerData.getCluster(),
-                            brokerData.getBrokerName(),
-                            (HashMap<Long, String>) brokerData.getBrokerAddrs().clone(),
-                            brokerData.isEnableActingMaster(), brokerData.getZoneName());
+                        brokerData.getBrokerName(),
+                        (HashMap<Long, String>) brokerData.getBrokerAddrs().clone(),
+                        brokerData.isEnableActingMaster(), brokerData.getZoneName());
 
                     brokerDataList.add(brokerDataClone);
                     foundBrokerData = true;
@@ -1023,7 +1024,7 @@ public TopicList getUnitTopics() {
                 String topic = topicEntry.getKey();
                 Map<String, QueueData> queueDatas = topicEntry.getValue();
                 if (queueDatas != null && queueDatas.size() > 0
-                        && TopicSysFlag.hasUnitFlag(queueDatas.values().iterator().next().getTopicSysFlag())) {
+                    && TopicSysFlag.hasUnitFlag(queueDatas.values().iterator().next().getTopicSysFlag())) {
                     topicList.getTopicList().add(topic);
                 }
             }
@@ -1044,7 +1045,7 @@ public TopicList getHasUnitSubTopicList() {
                 String topic = topicEntry.getKey();
                 Map<String, QueueData> queueDatas = topicEntry.getValue();
                 if (queueDatas != null && queueDatas.size() > 0
-                        && TopicSysFlag.hasUnitSubFlag(queueDatas.values().iterator().next().getTopicSysFlag())) {
+                    && TopicSysFlag.hasUnitSubFlag(queueDatas.values().iterator().next().getTopicSysFlag())) {
                     topicList.getTopicList().add(topic);
                 }
             }
@@ -1065,8 +1066,8 @@ public TopicList getHasUnitSubUnUnitTopicList() {
                 String topic = topicEntry.getKey();
                 Map<String, QueueData> queueDatas = topicEntry.getValue();
                 if (queueDatas != null && queueDatas.size() > 0
-                        && !TopicSysFlag.hasUnitFlag(queueDatas.values().iterator().next().getTopicSysFlag())
-                        && TopicSysFlag.hasUnitSubFlag(queueDatas.values().iterator().next().getTopicSysFlag())) {
+                    && !TopicSysFlag.hasUnitFlag(queueDatas.values().iterator().next().getTopicSysFlag())
+                    && TopicSysFlag.hasUnitSubFlag(queueDatas.values().iterator().next().getTopicSysFlag())) {
                     topicList.getTopicList().add(topic);
                 }
             }
diff --git a/proxy/src/main/java/org/apache/rocketmq/proxy/common/ReceiptHandleGroup.java b/proxy/src/main/java/org/apache/rocketmq/proxy/common/ReceiptHandleGroup.java
--- a/proxy/src/main/java/org/apache/rocketmq/proxy/common/ReceiptHandleGroup.java
+++ b/proxy/src/main/java/org/apache/rocketmq/proxy/common/ReceiptHandleGroup.java
@@ -26,6 +26,7 @@
 import java.util.concurrent.TimeUnit;
 import java.util.concurrent.atomic.AtomicReference;
 import java.util.function.Function;
+import org.apache.rocketmq.common.utils.ConcurrentHashMapUtils;
 import org.apache.rocketmq.proxy.config.ConfigurationManager;
 
 public class ReceiptHandleGroup {
@@ -74,7 +75,8 @@ public String toString() {
 
     public void put(String msgID, String handle, MessageReceiptHandle value) {
         long timeout = ConfigurationManager.getProxyConfig().getLockTimeoutMsInHandleGroup();
-        Map<String, HandleData> handleMap = receiptHandleMap.computeIfAbsent(msgID, msgIDKey -> new ConcurrentHashMap<>());
+        Map<String, HandleData> handleMap = ConcurrentHashMapUtils.computeIfAbsent((ConcurrentHashMap<String, Map<String, HandleData>>) this.receiptHandleMap,
+            msgID, msgIDKey -> new ConcurrentHashMap<>());
         handleMap.compute(handle, (handleKey, handleData) -> {
             if (handleData == null || handleData.needRemove) {
                 return new HandleData(value);
diff --git a/proxy/src/main/java/org/apache/rocketmq/proxy/processor/ReceiptHandleProcessor.java b/proxy/src/main/java/org/apache/rocketmq/proxy/processor/ReceiptHandleProcessor.java
--- a/proxy/src/main/java/org/apache/rocketmq/proxy/processor/ReceiptHandleProcessor.java
+++ b/proxy/src/main/java/org/apache/rocketmq/proxy/processor/ReceiptHandleProcessor.java
@@ -40,6 +40,7 @@
 import org.apache.rocketmq.common.subscription.RetryPolicy;
 import org.apache.rocketmq.common.subscription.SubscriptionGroupConfig;
 import org.apache.rocketmq.common.thread.ThreadPoolMonitor;
+import org.apache.rocketmq.common.utils.ConcurrentHashMapUtils;
 import org.apache.rocketmq.proxy.common.AbstractStartAndShutdown;
 import org.apache.rocketmq.proxy.common.MessageReceiptHandle;
 import org.apache.rocketmq.proxy.common.ProxyContext;
@@ -239,7 +240,7 @@ protected void addReceiptHandle(ReceiptHandleGroupKey key, String msgID, String
         if (key == null) {
             return;
         }
-        receiptHandleGroupMap.computeIfAbsent(key,
+        ConcurrentHashMapUtils.computeIfAbsent(this.receiptHandleGroupMap, key,
             k -> new ReceiptHandleGroup()).put(msgID, receiptHandle, messageReceiptHandle);
     }
 
diff --git a/proxy/src/main/java/org/apache/rocketmq/proxy/service/channel/ChannelManager.java b/proxy/src/main/java/org/apache/rocketmq/proxy/service/channel/ChannelManager.java
--- a/proxy/src/main/java/org/apache/rocketmq/proxy/service/channel/ChannelManager.java
+++ b/proxy/src/main/java/org/apache/rocketmq/proxy/service/channel/ChannelManager.java
@@ -23,6 +23,7 @@
 import java.util.concurrent.ConcurrentHashMap;
 import java.util.concurrent.ConcurrentMap;
 import org.apache.rocketmq.common.constant.LoggerName;
+import org.apache.rocketmq.common.utils.ConcurrentHashMapUtils;
 import org.apache.rocketmq.proxy.common.ProxyContext;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
@@ -37,8 +38,7 @@ public SimpleChannel createChannel(ProxyContext context) {
             log.warn("ClientId is unexpected null or empty");
             return createChannelInner(context);
         }
-
-        SimpleChannel channel = clientIdChannelMap.computeIfAbsent(clientId, k -> createChannelInner(context));
+        SimpleChannel channel = ConcurrentHashMapUtils.computeIfAbsent(this.clientIdChannelMap,clientId, k -> createChannelInner(context));
         channel.updateLastAccessTime();
         return channel;
     }
diff --git a/store/src/main/java/org/apache/rocketmq/store/ha/autoswitch/AutoSwitchHAService.java b/store/src/main/java/org/apache/rocketmq/store/ha/autoswitch/AutoSwitchHAService.java
--- a/store/src/main/java/org/apache/rocketmq/store/ha/autoswitch/AutoSwitchHAService.java
+++ b/store/src/main/java/org/apache/rocketmq/store/ha/autoswitch/AutoSwitchHAService.java
@@ -33,6 +33,7 @@
 import org.apache.rocketmq.common.ThreadFactoryImpl;
 import org.apache.rocketmq.common.constant.LoggerName;
 import org.apache.rocketmq.common.protocol.body.HARuntimeInfo;
+import org.apache.rocketmq.common.utils.ConcurrentHashMapUtils;
 import org.apache.rocketmq.logging.InternalLogger;
 import org.apache.rocketmq.logging.InternalLoggerFactory;
 import org.apache.rocketmq.store.DefaultMessageStore;
@@ -237,7 +238,7 @@ public void maybeExpandInSyncStateSet(final String slaveAddress, final long slav
     }
 
     public void updateConnectionLastCaughtUpTime(final String slaveAddress, final long lastCaughtUpTimeMs) {
-        long prevTime = this.connectionCaughtUpTimeTable.computeIfAbsent(slaveAddress, k -> 0L);
+        Long prevTime = ConcurrentHashMapUtils.computeIfAbsent(this.connectionCaughtUpTimeTable, slaveAddress, k -> 0L);
         this.connectionCaughtUpTimeTable.put(slaveAddress, Math.max(prevTime, lastCaughtUpTimeMs));
     }
 
diff --git a/store/src/main/java/org/apache/rocketmq/store/queue/QueueOffsetAssigner.java b/store/src/main/java/org/apache/rocketmq/store/queue/QueueOffsetAssigner.java
--- a/store/src/main/java/org/apache/rocketmq/store/queue/QueueOffsetAssigner.java
+++ b/store/src/main/java/org/apache/rocketmq/store/queue/QueueOffsetAssigner.java
@@ -20,12 +20,12 @@
 import java.util.concurrent.ConcurrentHashMap;
 import java.util.concurrent.ConcurrentMap;
 import org.apache.rocketmq.common.constant.LoggerName;
+import org.apache.rocketmq.common.utils.ConcurrentHashMapUtils;
 import org.apache.rocketmq.logging.InternalLogger;
 import org.apache.rocketmq.logging.InternalLoggerFactory;
 
 /**
  * QueueOffsetAssigner is a component for assigning offsets for queues.
- *
  */
 public class QueueOffsetAssigner {
     private static final InternalLogger log = InternalLoggerFactory.getLogger(LoggerName.STORE_LOGGER_NAME);
@@ -35,7 +35,7 @@ public class QueueOffsetAssigner {
     private ConcurrentMap<String/* topic-queueid */, Long/* offset */> lmqTopicQueueTable = new ConcurrentHashMap<>(1024);
 
     public long assignQueueOffset(String topicQueueKey, short messageNum) {
-        long queueOffset = this.topicQueueTable.computeIfAbsent(topicQueueKey, k -> 0L);
+        Long queueOffset = ConcurrentHashMapUtils.computeIfAbsent(this.topicQueueTable, topicQueueKey, k -> 0L);
         this.topicQueueTable.put(topicQueueKey, queueOffset + messageNum);
         return queueOffset;
     }
@@ -45,13 +45,13 @@ public void updateQueueOffset(String topicQueueKey, long offset) {
     }
 
     public long assignBatchQueueOffset(String topicQueueKey, short messageNum) {
-        Long topicOffset = this.batchTopicQueueTable.computeIfAbsent(topicQueueKey, k -> 0L);
+        Long topicOffset = ConcurrentHashMapUtils.computeIfAbsent(this.batchTopicQueueTable, topicQueueKey, k -> 0L);
         this.batchTopicQueueTable.put(topicQueueKey, topicOffset + messageNum);
         return topicOffset;
     }
 
     public long assignLmqOffset(String topicQueueKey, short messageNum) {
-        long topicOffset = this.lmqTopicQueueTable.computeIfAbsent(topicQueueKey, k -> 0L);
+        Long topicOffset = ConcurrentHashMapUtils.computeIfAbsent(this.lmqTopicQueueTable, topicQueueKey, k -> 0L);
         this.lmqTopicQueueTable.put(topicQueueKey, topicOffset + messageNum);
         return topicOffset;
     }


Test patch:
diff --git a/common/src/test/java/org/apache/rocketmq/common/utils/ConcurrentHashMapUtilsTest.java b/common/src/test/java/org/apache/rocketmq/common/utils/ConcurrentHashMapUtilsTest.java
new file mode 100644
--- /dev/null
+++ b/common/src/test/java/org/apache/rocketmq/common/utils/ConcurrentHashMapUtilsTest.java
@@ -0,0 +1,39 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.rocketmq.common.utils;
+
+import java.util.concurrent.ConcurrentHashMap;
+import org.junit.Test;
+
+import static org.junit.Assert.*;
+
+public class ConcurrentHashMapUtilsTest {
+
+    @Test
+    public void computeIfAbsent() {
+
+        ConcurrentHashMap<String, String> map = new ConcurrentHashMap<>();
+        map.put("123", "1111");
+        String value = ConcurrentHashMapUtils.computeIfAbsent(map, "123", k -> "234");
+        assertEquals("1111", value);
+        String value1 = ConcurrentHashMapUtils.computeIfAbsent(map, "1232", k -> "2342");
+        assertEquals("2342", value1);
+        String value2 = ConcurrentHashMapUtils.computeIfAbsent(map, "123", k -> "2342");
+        assertEquals("1111", value2);
+    }
+}
\ No newline at end of file