Skip to content

Commit ab4f44e

Browse files
authored
bugfix: fix ConcurrentModificationException in SessionConverter.convertBranchSession (apache#6943)
1 parent dbeba61 commit ab4f44e

File tree

5 files changed

+117
-4
lines changed

5 files changed

+117
-4
lines changed

README.md

+1-1
Original file line numberDiff line numberDiff line change
@@ -134,7 +134,7 @@ Contributors are welcomed to join the Seata project. Please check [CONTRIBUTING]
134134
* [email protected] , for dev/user discussion. [subscribe](mailto:[email protected]), [unsubscribe](mailto:[email protected]), [archive](https://lists.apache.org/[email protected])
135135
* Online chat:
136136

137-
| Dingtalk group | Wechat office account | QQ group | Wechat assistant |
137+
| Dingtalk group | Wechat official account | QQ group | Wechat assistant |
138138
|:---------------------------------------------------------------------------------------------------------------------------:|:----------------------------------------------------------------------------------------------------------------------------:|:---------------------------------------------------------------------------------------------------------------------:|:-------------------------------------------------------------------------------------------------------------------:|
139139
| <img src="https://seata.apache.org/zh-cn/assets/images/dingtalk-group-67f42c9466fb2268b6927bb16b549d6c.jpg" width="150" /> | <img src="https://seata.apache.org/zh-cn/assets/images/wechat-official-467d10305f5449e6b2096e65d23a9d02.jpg" width="150" /> | <img src="https://seata.apache.org/zh-cn/assets/images/qq-group-8d8a89699cdb9ba8818364069475ba96.jpg" width="150" /> | <img src="https://seata.apache.org/zh-cn/assets/images/wechat-f8a87a96973942b826e32d1aed9bc8d9.jpg" width="150" /> |
140140

changes/en-us/2.x.md

+1
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ Add changes here for all PR submitted to the 2.x branch.
1515
- [[#6923](https://github.com/apache/incubator-seata/pull/6923)] Enhance 401 Error Handling by Refreshing Token
1616
- [[#6925](https://github.com/apache/incubator-seata/pull/6925)] fix the issue in Raft model a follower's crash may lead to the continued use of expired tokens
1717
- [[#6932](https://github.com/apache/incubator-seata/pull/6932)] when enabling local transactions, the lock contention failure in file & raft mode does not exit, leading to a lingering lock
18+
- [[#6943](https://github.com/apache/incubator-seata/pull/6943)] fix the conversion error for `convertBranchSession` in concurrent environment.
1819

1920
### optimize:
2021
- [[#6826](https://github.com/apache/incubator-seata/pull/6826)] remove the branch registration operation of the XA read-only transaction

changes/zh-cn/2.x.md

+1-1
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@
1515
- [[#6923](https://github.com/apache/incubator-seata/pull/6923)] 增强 401 错误处理,通过刷新令牌
1616
- [[#6925](https://github.com/apache/incubator-seata/pull/6925)] 修复Raft模式下,Follower崩溃可能导致Client继续使用过期令牌的问题
1717
- [[#6932](https://github.com/apache/incubator-seata/pull/6932)] 修复开启本地事务时file&raft模式下锁争抢失败未退出导致可能出现残留锁
18-
18+
- [[#6943](https://github.com/apache/incubator-seata/pull/6943)] 修复并发状态下 `convertBranchSession` 转换报错问题
1919

2020
### optimize:
2121
- [[#6826](https://github.com/apache/incubator-seata/pull/6826)] 移除只读XA事务的分支注册操作

server/src/main/java/org/apache/seata/server/storage/SessionConverter.java

+3-2
Original file line numberDiff line numberDiff line change
@@ -213,9 +213,10 @@ public static Set<BranchSessionVO> convertBranchSession(List<BranchSession> bran
213213
return Collections.emptySet();
214214
}
215215

216-
final Set<BranchSessionVO> result = new HashSet<>(branchSessions.size());
216+
List<BranchSession> safeBranchSessions = new ArrayList<>(branchSessions);
217+
final Set<BranchSessionVO> result = new HashSet<>(safeBranchSessions.size());
217218

218-
for (BranchSession session : branchSessions) {
219+
for (BranchSession session : safeBranchSessions) {
219220
result.add(new BranchSessionVO(
220221
session.getXid(),
221222
session.getTransactionId(),
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,111 @@
1+
/*
2+
* Licensed to the Apache Software Foundation (ASF) under one or more
3+
* contributor license agreements. See the NOTICE file distributed with
4+
* this work for additional information regarding copyright ownership.
5+
* The ASF licenses this file to You under the Apache License, Version 2.0
6+
* (the "License"); you may not use this file except in compliance with
7+
* the License. You may obtain a copy of the License at
8+
*
9+
* http://www.apache.org/licenses/LICENSE-2.0
10+
*
11+
* Unless required by applicable law or agreed to in writing, software
12+
* distributed under the License is distributed on an "AS IS" BASIS,
13+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
* See the License for the specific language governing permissions and
15+
* limitations under the License.
16+
*/
17+
//package org.apache.seata.server.storage;
18+
//
19+
//import java.util.ArrayList;
20+
//import java.util.ConcurrentModificationException;
21+
//import java.util.List;
22+
//import java.util.concurrent.CountDownLatch;
23+
//import java.util.concurrent.ExecutorService;
24+
//import java.util.concurrent.Executors;
25+
//import java.util.concurrent.atomic.AtomicBoolean;
26+
//import org.apache.seata.core.model.BranchStatus;
27+
//import org.apache.seata.core.model.BranchType;
28+
//import org.apache.seata.server.session.BranchSession;
29+
//import org.junit.jupiter.api.RepeatedTest;
30+
//import org.junit.jupiter.api.extension.ExtendWith;
31+
//import org.mockito.junit.jupiter.MockitoExtension;
32+
//import org.springframework.boot.test.context.SpringBootTest;
33+
//
34+
//import static org.junit.jupiter.api.Assertions.assertFalse;
35+
//
36+
//@ExtendWith(MockitoExtension.class)
37+
//@SpringBootTest
38+
//public class SessionConverterTest {
39+
// // Repeat 100 for adding success per
40+
// @RepeatedTest(100)
41+
// public void testConcurrentModificationException() throws InterruptedException {
42+
// List<BranchSession> branchSessions = new ArrayList<>();
43+
// for (int i = 0; i < 1000; i++) {
44+
// branchSessions.add(createMockBranchSession(i));
45+
// }
46+
//
47+
// CountDownLatch startLatch = new CountDownLatch(1);
48+
// CountDownLatch endLatch = new CountDownLatch(2);
49+
// AtomicBoolean exceptionThrown = new AtomicBoolean(false);
50+
//
51+
// ExecutorService executorService = Executors.newFixedThreadPool(2);
52+
//
53+
// // Thread for converting branch sessions
54+
// executorService.submit(() -> {
55+
// try {
56+
// startLatch.await();
57+
// for (int i = 0; i < 100; i++) {
58+
// try {
59+
// SessionConverter.convertBranchSession(branchSessions);
60+
// } catch (ConcurrentModificationException e) {
61+
// exceptionThrown.set(true);
62+
// break;
63+
// }
64+
// }
65+
// } catch (InterruptedException e) {
66+
// Thread.currentThread().interrupt();
67+
// } finally {
68+
// endLatch.countDown();
69+
// }
70+
// });
71+
//
72+
// // Thread for modifying the list
73+
// executorService.submit(() -> {
74+
// try {
75+
// startLatch.await();
76+
// for (int i = 0; i < 1000; i++) {
77+
// branchSessions.add(createMockBranchSession(1000 + i));
78+
// if (i % 10 == 0) {
79+
// branchSessions.remove(0);
80+
// }
81+
// }
82+
// } catch (InterruptedException e) {
83+
// Thread.currentThread().interrupt();
84+
// } finally {
85+
// endLatch.countDown();
86+
// }
87+
// });
88+
// // Start both threads
89+
// startLatch.countDown();
90+
// // Wait for both threads to finish
91+
// endLatch.await();
92+
//
93+
// executorService.shutdown();
94+
//
95+
// assertFalse(exceptionThrown.get(), "ConcurrentModificationException was not thrown");
96+
// }
97+
//
98+
// private BranchSession createMockBranchSession(int id) {
99+
// BranchSession session = new BranchSession();
100+
// session.setXid("xid" + id);
101+
// session.setTransactionId(id);
102+
// session.setBranchId(id);
103+
// session.setResourceGroupId("resourceGroup" + id);
104+
// session.setResourceId("resource" + id);
105+
// session.setBranchType(BranchType.AT);
106+
// session.setStatus(BranchStatus.Registered);
107+
// session.setClientId("client" + id);
108+
// session.setApplicationData("data" + id);
109+
// return session;
110+
// }
111+
//}

0 commit comments

Comments
 (0)