apache / apache/incubator-seata
AT模式下数据回滚异常
- Dominant language
- Java
- Stars
- 26k
- Forks
- 8.8k
- Avg merge
- 1d 8h
- Merged PRs (30d)
- 4
Description
- [x] I have searched the [issues](https://github.com/seata/seata/issues) of this repository and believe that this is not a duplicate.
### Ⅰ. Issue Description
有abcd 4个库,每个库对应一个服务。 a调用b,b调用c,a还调用d。在bc 调用成功后,正在调用d的时候 。有其它服务调用了b,执行成功了。这个时候 d 异常,数据全局回滚。就会出现 b 回滚异常的情况
### Ⅱ. Describe what happened
If there is an exception, please attach the exception trace:
```
Creating a new SqlSession
SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@502f2114] was not registered for synchronization because synchronization is not active
JDBC Connection [io.seata.rm.datasource.ConnectionProxy@365b6a35] will not be managed by Spring
==> Preparing: SELECT id,money,user_id FROM account_tbl WHERE (user_id = ?)
==> Parameters: 1(String)
<== Columns: id, money, user_id
<== Row: 1, 1000, 1
<== Total: 1
Closing non transactional SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@502f2114]
Creating a new SqlSession
SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@4ee4898c] was not registered for synchronization because synchronization is not active
JDBC Connection [io.seata.rm.datasource.ConnectionProxy@7ffa3015] will not be managed by Spring
==> Preparing: UPDATE account_tbl SET money=?, user_id=? WHERE id=?
==> Parameters: 990(Integer), 1(String), 1(Long)
<== Updates: 1
Closing non transactional SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@4ee4898c]
2021-03-04 16:35:34.057 [Account-Service - 8081] WARN 14892 --- [nio-8081-exec-7] c.a.c.seata.web.SeataHandlerInterceptor : xid in change during RPC from 172.25.104.15:8091:110778839956779008 to null
2021-03-04 16:35:35.022 [Account-Service - 8081] INFO 14892 --- [nio-8081-exec-8] i.seata.tm.api.DefaultGlobalTransaction : Begin new global transaction [172.25.104.15:8091:110778845598117888]
Creating a new SqlSession
SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@321bb851] was not registered for synchronization because synchronization is not active
JDBC Connection [io.seata.rm.datasource.ConnectionProxy@13094d25] will not be managed by Spring
==> Preparing: SELECT id,money,user_id FROM account_tbl WHERE (user_id = ?)
==> Parameters: 1(String)
<== Columns: id, money, user_id
<== Row: 1, 990, 1
<== Total: 1
Closing non transactional SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@321bb851]
Creating a new SqlSession
SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@4b01dde8] was not registered for synchronization because synchronization is not active
JDBC Connection [io.seata.rm.datasource.ConnectionProxy@4efeae0a] will not be managed by Spring
==> Preparing: UPDATE account_tbl SET money=?, user_id=? WHERE id=?
==> Parameters: 1090(Integer), 1(String), 1(Long)
<== Updates: 1
Closing non transactional SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@4b01dde8]
2021-03-04 16:35:35.232 [Account-Service - 8081] INFO 14892 --- [nio-8081-exec-8] i.seata.tm.api.DefaultGlobalTransaction : [172.25.104.15:8091:110778845598117888] commit status: Committed
2021-03-04 16:35:44.131 [Account-Service - 8081] INFO 14892 --- [_RMROLE_1_10_12] i.s.c.r.p.c.RmBranchRollbackProcessor : rm handle branch rollback process:xid=172.25.104.15:8091:110778839956779008,branchId=110778840980189185,branchType=AT,resourceId=jdbc:mysql://127.0.0.1:3306/account,applicationData=null
2021-03-04 16:35:44.131 [Account-Service - 8081] INFO 14892 --- [_RMROLE_1_10_12] io.seata.rm.AbstractRMHandler : Branch Rollbacking: 172.25.104.15:8091:110778839956779008 110778840980189185 jdbc:mysql://127.0.0.1:3306/account
2021-03-04 16:35:44.133 [Account-Service - 8081] INFO 14892 --- [_RMROLE_1_10_12] i.s.r.d.undo.AbstractUndoExecutor : Field not equals, name money, old value 990, new value 1090
2021-03-04 16:35:44.134 [Account-Service - 8081] INFO 14892 --- [_RMROLE_1_10_12] i.seata.rm.datasource.DataSourceManager : [stacktrace]branchRollback failed. branchType:[[AT, 172.25.104.15:8091:110778839956779008, 110778840980189185, jdbc:mysql://127.0.0.1:3306/account, null, Branch session rollback failed and try again later xid = 172.25.104.15:8091:110778839956779008 branchId = 110778840980189185 Has dirty records when undo.]], xid:[{}], branchId:[{}], resourceId:[{}], applicationData:[{}]. reason:[{}]
io.seata.core.exception.BranchTransactionException: Branch session rollback failed and try again later xid = 172.25.104.15:8091:110778839956779008 branchId = 110778840980189185 Has dirty records when undo.
at io.seata.rm.datasource.undo.AbstractUndoLogManager.undo(AbstractUndoLogManager.java:339) ~[seata-all-1.4.1.jar:1.4.1]
at io.seata.rm.datasource.DataSourceManager.branchRollback(DataSourceManager.java:152) ~[seata-all-1.4.1.jar:1.4.1]
at io.seata.rm.AbstractRMHandler.doBranchRollback(AbstractRMHandler.java:125) [seata-all-1.4.1.jar:1.4.1]
at io.seata.rm.AbstractRMHandler$2.execute(AbstractRMHandler.java:67) [seata-all-1.4.1.jar:1.4.1]
at io.seata.rm.AbstractRMHandler$2.execute(AbstractRMHandler.java:63) [seata-all-1.4.1.jar:1.4.1]
at io.seata.core.exception.AbstractExceptionHandler.exceptionHandleTemplate(AbstractExceptionHandler.java:116) [seata-all-1.4.1.jar:1.4.1]
at io.seata.rm.AbstractRMHandler.handle(AbstractRMHandler.java:63) [seata-all-1.4.1.jar:1.4.1]
at io.seata.rm.DefaultRMHandler.handle(DefaultRMHandler.java:62) [seata-all-1.4.1.jar:1.4.1]
at io.seata.core.protocol.transaction.BranchRollbackRequest.handle(BranchRollbackRequest.java:35) [seata-all-1.4.1.jar:1.4.1]
at io.seata.rm.AbstractRMHandler.onRequest(AbstractRMHandler.java:150) [seata-all-1.4.1.jar:1.4.1]
at io.seata.core.rpc.processor.client.RmBranchRollbackProcessor.handleBranchRollback(RmBranchRollbackProcessor.java:63) [seata-all-1.4.1.jar:1.4.1]
at io.seata.core.rpc.processor.client.RmBranchRollbackProcessor.process(RmBranchRollbackProcessor.java:58) [seata-all-1.4.1.jar:1.4.1]
at io.seata.core.rpc.netty.AbstractNettyRemoting.lambda$processMessage$2(AbstractNettyRemoting.java:278) [seata-all-1.4.1.jar:1.4.1]
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149) ~[na:1.8.0_192]
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624) ~[na:1.8.0_192]
at io.netty.util.concurrent.FastThreadLocalRunnable.run(FastThreadLocalRunnable.java:30) ~[netty-all-4.1.52.Final.jar:4.1.52.Final]
at java.lang.Thread.run(Thread.java:748) ~[na:1.8.0_192]
Caused by: java.sql.SQLException: Has dirty records when undo.
at io.seata.rm.datasource.undo.AbstractUndoExecutor.dataValidationAndGoOn(AbstractUndoExecutor.java:268) ~[seata-all-1.4.1.jar:1.4.1]
at io.seata.rm.datasource.undo.AbstractUndoExecutor.executeOn(AbstractUndoExecutor.java:116) ~[seata-all-1.4.1.jar:1.4.1]
at io.seata.rm.datasource.undo.AbstractUndoLogManager.undo(AbstractUndoLogManager.java:291) ~[seata-all-1.4.1.jar:1.4.1]
... 16 common frames omitted
2021-03-04 16:35:44.135 [Account-Service - 8081] INFO 14892 --- [_RMROLE_1_10_12] io.seata.rm.AbstractRMHandler : Branch Rollbacked result: PhaseTwo_RollbackFailed_Retryable
2021-03-04 16:35:45.156 [Account-Service - 8081] INFO 14892 --- [_RMROLE_1_11_12] i.s.c.r.p.c.RmBranchRollbackProcessor : rm handle branch rollback process:xid=172.25.104.15:8091:110778839956779008,branchId=110778840980189185,branchType=AT,resourceId=jdbc:mysql://127.0.0.1:3306/account,applicationData=null
2021-03-04 16:35:45.156 [Account-Service - 8081] INFO 14892 --- [_RMROLE_1_11_12] io.seata.rm.AbstractRMHandler : Branch Rollbacking: 172.25.104.15:8091:110778839956779008 110778840980189185 jdbc:mysql://127.0.0.1:3306/account
2021-03-04 16:35:45.157 [Account-Service - 8081] INFO 14892 --- [_RMROLE_1_11_12] i.s.r.d.undo.AbstractUndoExecutor : Field not equals, name money, old value 990, new value 1090
2021-03-04 16:35:45.158 [Account-Service - 8081] INFO 14892 --- [_RMROLE_1_11_12] i.seata.rm.datasource.DataSourceManager : branchRollback failed. branchType:[AT], xid:[172.25.104.15:8091:110778839956779008], branchId:[110778840980189185], resourceId:[jdbc:mysql://127.0.0.1:3306/account], applicationData:[null]. reason:[Branch session rollback failed and try again later xid = 172.25.104.15:8091:110778839956779008 branchId = 110778840980189185 Has dirty records when undo.]
2021-03-04 16:35:45.158 [Account-Service - 8081] INFO 14892 --- [_RMROLE_1_11_12] io.seata.rm.AbstractRMHandler : Branch Rollbacked result: PhaseTwo_RollbackFailed_Retryable
2021-03-04 16:35:46.158 [Account-Service - 8081] INFO 14892 --- [_RMROLE_1_12_12] i.s.c.r.p.c.RmBranchRollbackProcessor : rm handle branch rollback process:xid=172.25.104.15:8091:110778839956779008,branchId=110778840980189185,branchType=AT,resourceId=jdbc:mysql://127.0.0.1:3306/account,applicationData=null
2021-03-04 16:35:46.159 [Account-Service - 8081] INFO 14892 --- [_RMROLE_1_12_12] io.seata.rm.AbstractRMHandler : Branch Rollbacking: 172.25.104.15:8091:110778839956779008 110778840980189185 jdbc:mysql://127.0.0.1:3306/account
2021-03-04 16:35:46.164 [Account-Service - 8081] INFO 14892 --- [_RMROLE_1_12_12] i.s.r.d.undo.AbstractUndoExecutor : Field not equals, name money, old value 990, new value 1090
2021-03-04 16:35:46.166 [Account-Service - 8081] INFO 14892 --- [_RMROLE_1_12_12] i.seata.rm.datasource.DataSourceManager : branchRollback failed. branchType:[AT], xid:[172.25.104.15:8091:110778839956779008], branchId:[110778840980189185], resourceId:[jdbc:mysql://127.0.0.1:3306/account], applicationData:[null]. reason:[Branch session rollback failed and try again later xid = 172.25.104.15:8091:110778839956779008 branchId = 110778840980189185 Has dirty records when undo.]
```
### Ⅲ. Describe what you expected to happen
一直在重试。数据回滚正常
### Ⅳ. How to reproduce it (as minimally and precisely as possible)
1. A服务
```
@GlobalTransactional
public boolean business(OrderVo orderVo) {
orderFeign.insertOrder(orderVo);
try {
Thread.sleep(10000);
}catch (Exception e){
}
storageFeign.subStorage(orderVo.getCommodityCode(), orderVo.getCount());
return true;
}
```
2. B服务
```
@GlobalTransactional
public boolean subAccount(String userId, Integer money) {
LambdaQueryWrapper wrapper = Wrappers.lambdaQuery();
wrapper.eq(Account::getUserId, userId);
List accounts = accountDao.selectList(wrapper);
Account account = accounts.get(0);
Integer m = account.getMoney() - money;
if (m < 0) {
throw new RuntimeException("余额不足!!");
}
account.setMoney(m);
this.updateById(account);
return true;
}
```
3. 其它几个服务也是一样
### Ⅴ. Anything else we need to know?
```
seata:
# 开启自动装配
enabled: true
# 本客户端的微服务名称
application-id: ${spring.application.name}
# 读取哪个事务分组
tx-service-group: my_test_tx_group
# 配置中心设置
config:
type: nacos
nacos:
# nacos 的ip和端口
server-addr: ${nacos-server-addr}
# 与上面的命名空间相同
namespace: seata-dev
# 读取的配置分组
group: SEATA_GROUP
username: nacos
password: nacos
# 注册中心设置
registry:
type: nacos
nacos:
# SEATA服务中心的微服务名,此处与服务端保持一致
application: seata-server
# nacos 的ip和端口
server-addr: ${nacos-server-addr}
# 与上面的命名空间相同
namespace: seata-dev
# 读取的配置分组
group: SEATA_GROUP
username: nacos
password: nacos
```
```
com.alibaba.cloud
spring-cloud-starter-alibaba-seata
io.seata
seata-spring-boot-starter
io.seata
seata-spring-boot-starter
1.4.1
```
### Ⅵ. Environment:
- JDK version :1.8
- OS :win10
- Others:nacos 1.4.0
- seata 1.4.1
-
Contributor guide
Research direction
Start with AbstractUndoExecutor.java and AbstractUndoLogManager.java, where the stack trace reports dirty records during AT rollback, then trace the branch rollback path through DataSourceManager.java and AbstractRMHandler.java. Reproduce the concurrent update scenario from the A and B service examples and verify that the global transaction can roll back successfully instead of retrying indefinitely.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- java, mysql
- Domain
- databases, distributed-systems
- Issue type
- Bug
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Activity status
- Stale
- Clarity
- Mostly clear
- Newbie friendliness
- 35/100