Skip to content

Commit 0cf2acf

Browse files
zkyomacherrylzhao
authored andcommitted
TAC模式读已提交的隔离级别bug修复 (#364)
* TAC模式读已提交的隔离级别bug修复, 读隔离查询时只需要检查全局锁, 增加重试机制, 修改对应的测试demo。 * 优化tac demo代码, 使用enum代替int值来区分代码逻辑 * 优化tac demo中enum的命名
1 parent 2e08d82 commit 0cf2acf

File tree

13 files changed

+306
-42
lines changed

13 files changed

+306
-42
lines changed

hmily-config/hmily-config-api/src/main/java/org/dromara/hmily/config/api/entity/HmilyConfig.java

+10
Original file line numberDiff line numberDiff line change
@@ -138,6 +138,16 @@ public class HmilyConfig extends AbstractConfig {
138138
* tac sqlRevert.
139139
*/
140140
private String sqlRevert = "default";
141+
142+
/**
143+
* global lock retry interval(unit: ms).
144+
*/
145+
private int lockRetryInterval = 10;
146+
147+
/**
148+
* global lock retry times.
149+
*/
150+
private int lockRetryTimes = 30;
141151

142152
@Override
143153
public String prefix() {
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
package org.dromara.hmily.demo.tac.dubbo.order.enums;
2+
3+
import lombok.Getter;
4+
import lombok.RequiredArgsConstructor;
5+
6+
/**
7+
* The enum transaction enum.
8+
*
9+
* @author zhangzhi
10+
*/
11+
@RequiredArgsConstructor
12+
@Getter
13+
public enum ReadCommittedTransactionEnum {
14+
15+
TRANSACTION_READ_WRITE(1, "读已提交隔离级别的事务, 包括更新、查询操作"),
16+
17+
TRANSACTION_READ_ONLY(2, "读已提交隔离级别的事务, 只有查询操作");
18+
19+
private final int code;
20+
21+
private final String desc;
22+
}

hmily-demo/hmily-demo-tac/hmily-demo-tac-dubbo/hmily-demo-tac-dubbo-order/src/main/java/org/dromara/hmily/demo/tac/dubbo/order/service/PaymentService.java

+3-1
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
package org.dromara.hmily.demo.tac.dubbo.order.service;
1818

1919
import org.dromara.hmily.demo.common.order.entity.Order;
20+
import org.dromara.hmily.demo.tac.dubbo.order.enums.ReadCommittedTransactionEnum;
2021

2122
/**
2223
* The interface Payment service.
@@ -97,6 +98,7 @@ public interface PaymentService {
9798
* 订单支付.
9899
*
99100
* @param order 订单实体
101+
* @param readCommittedTransactionEnum 事务类型
100102
*/
101-
String makePaymentWithReadCommitted(Order order);
103+
String makePaymentWithReadCommitted(Order order, ReadCommittedTransactionEnum readCommittedTransactionEnum);
102104
}

hmily-demo/hmily-demo-tac/hmily-demo-tac-dubbo/hmily-demo-tac-dubbo-order/src/main/java/org/dromara/hmily/demo/tac/dubbo/order/service/impl/OrderServiceImpl.java

+18-2
Original file line numberDiff line numberDiff line change
@@ -17,10 +17,10 @@
1717
package org.dromara.hmily.demo.tac.dubbo.order.service.impl;
1818

1919
import org.dromara.hmily.common.utils.IdWorkerUtils;
20-
import org.dromara.hmily.demo.common.account.api.AccountService;
2120
import org.dromara.hmily.demo.common.order.entity.Order;
2221
import org.dromara.hmily.demo.common.order.enums.OrderStatusEnum;
2322
import org.dromara.hmily.demo.common.order.mapper.OrderMapper;
23+
import org.dromara.hmily.demo.tac.dubbo.order.enums.ReadCommittedTransactionEnum;
2424
import org.dromara.hmily.demo.tac.dubbo.order.service.OrderService;
2525
import org.dromara.hmily.demo.tac.dubbo.order.service.PaymentService;
2626
import org.slf4j.Logger;
@@ -165,7 +165,23 @@ private Order saveOrder(Integer count, BigDecimal amount) {
165165
public String orderPayWithReadCommitted(Integer count, BigDecimal amount) {
166166
Order order = saveOrder(count, amount);
167167
long start = System.currentTimeMillis();
168-
paymentService.makePaymentWithReadCommitted(order);
168+
// 开启一个事务
169+
new Thread(() -> {
170+
try {
171+
paymentService.makePaymentWithReadCommitted(order, ReadCommittedTransactionEnum.TRANSACTION_READ_WRITE);
172+
} catch (Exception e) {
173+
System.out.println(e.getMessage());
174+
}
175+
}, "global trans2").start();
176+
try {
177+
// 确保第一个事务先执行
178+
Thread.sleep(1000);
179+
} catch (InterruptedException e) {
180+
e.printStackTrace();
181+
}
182+
// 开启另一个事务
183+
paymentService.makePaymentWithReadCommitted(order, ReadCommittedTransactionEnum.TRANSACTION_READ_ONLY);
184+
169185
System.out.println("切面耗时:" + (System.currentTimeMillis() - start));
170186
return "success";
171187
}

hmily-demo/hmily-demo-tac/hmily-demo-tac-dubbo/hmily-demo-tac-dubbo-order/src/main/java/org/dromara/hmily/demo/tac/dubbo/order/service/impl/PaymentServiceImpl.java

+29-16
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,9 @@
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
66
* You may obtain a copy of the License at
7-
*
7+
*
88
* http://www.apache.org/licenses/LICENSE-2.0
9-
*
9+
*
1010
* Unless required by applicable law or agreed to in writing, software
1111
* distributed under the License is distributed on an "AS IS" BASIS,
1212
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
@@ -27,19 +27,21 @@
2727
import org.dromara.hmily.demo.common.order.entity.Order;
2828
import org.dromara.hmily.demo.common.order.enums.OrderStatusEnum;
2929
import org.dromara.hmily.demo.common.order.mapper.OrderMapper;
30+
import org.dromara.hmily.demo.tac.dubbo.order.enums.ReadCommittedTransactionEnum;
3031
import org.dromara.hmily.demo.tac.dubbo.order.service.PaymentService;
3132
import org.slf4j.Logger;
3233
import org.slf4j.LoggerFactory;
3334
import org.springframework.beans.factory.annotation.Autowired;
3435
import org.springframework.stereotype.Service;
3536

37+
3638
/**
3739
* PaymentServiceImpl.
3840
* @author xiaoyu
3941
*/
4042
@Service
4143
public class PaymentServiceImpl implements PaymentService {
42-
44+
4345
private static final Logger LOGGER = LoggerFactory.getLogger(PaymentServiceImpl.class);
4446

4547
private final OrderMapper orderMapper;
@@ -56,7 +58,7 @@ public PaymentServiceImpl(final OrderMapper orderMapper,
5658
this.accountService = accountService;
5759
this.inventoryService = inventoryService;
5860
}
59-
61+
6062
@Override
6163
@HmilyTAC
6264
public void makePayment(final Order order) {
@@ -66,7 +68,7 @@ public void makePayment(final Order order) {
6668
//进入扣减库存操作
6769
inventoryService.decrease(buildInventoryDTO(order));
6870
}
69-
71+
7072
@Override
7173
public void testMakePayment(final Order order) {
7274
updateOrderStatus(order, OrderStatusEnum.PAYING);
@@ -87,7 +89,7 @@ public void makePaymentWithNested(final Order order) {
8789
//扣除用户余额
8890
accountService.paymentWithNested(buildAccountNestedDTO(order));
8991
}
90-
92+
9193
@Override
9294
@HmilyTAC
9395
public void makePaymentWithNestedException(final Order order) {
@@ -99,7 +101,7 @@ public void makePaymentWithNestedException(final Order order) {
99101
//扣除用户余额
100102
accountService.paymentWithNestedException(buildAccountNestedDTO(order));
101103
}
102-
104+
103105
@Override
104106
@HmilyTAC
105107
public String mockPaymentInventoryWithTryException(final Order order) {
@@ -109,7 +111,7 @@ public String mockPaymentInventoryWithTryException(final Order order) {
109111
inventoryService.mockWithTryException(buildInventoryDTO(order));
110112
return "success";
111113
}
112-
114+
113115
@Override
114116
@HmilyTAC
115117
public String mockPaymentInventoryWithTryTimeout(final Order order) {
@@ -119,23 +121,23 @@ public String mockPaymentInventoryWithTryTimeout(final Order order) {
119121
inventoryService.mockWithTryTimeout(buildInventoryDTO(order));
120122
return "success";
121123
}
122-
124+
123125
@Override
124126
@HmilyTAC
125127
public String mockPaymentAccountWithTryException(final Order order) {
126128
updateOrderStatus(order, OrderStatusEnum.PAYING);
127129
accountService.mockTryPaymentException(buildAccountDTO(order));
128130
return "success";
129131
}
130-
132+
131133
@Override
132134
@HmilyTAC
133135
public String mockPaymentAccountWithTryTimeout(final Order order) {
134136
updateOrderStatus(order, OrderStatusEnum.PAYING);
135137
accountService.mockTryPaymentTimeout(buildAccountDTO(order));
136138
return "success";
137139
}
138-
140+
139141
@Override
140142
@HmilyTAC
141143
public String mockPaymentInventoryWithConfirmTimeout(final Order order) {
@@ -151,13 +153,24 @@ public String mockPaymentInventoryWithConfirmTimeout(final Order order) {
151153

152154
@Override
153155
@HmilyTAC
154-
public String makePaymentWithReadCommitted(Order order) {
156+
public String makePaymentWithReadCommitted(Order order, ReadCommittedTransactionEnum transactionEnum) {
157+
//第二个事务查询相同账户信息, 获取不到全局锁, 会进行回滚
158+
if (ReadCommittedTransactionEnum.TRANSACTION_READ_ONLY.equals(transactionEnum)) {
159+
accountService.findByUserId(order.getUserId());
160+
return "success";
161+
}
155162
updateOrderStatus(order, OrderStatusEnum.PAY_SUCCESS);
156163
//扣除用户余额
157164
accountService.payment(buildAccountDTO(order));
158-
//查询账户信息, 读已提交, 此时该事务未结束, 获取全局锁失败, 将会回滚
165+
//查询账户信息, 读已提交隔离级别, 但是在统一全局事务中, 所以可见
159166
accountService.findByUserId(order.getUserId());
160167
//进入扣减库存操作
168+
try {
169+
// 延时第一个事务, 确保第一个事务还没结束, 第二个事务执行查询
170+
Thread.sleep(1200);
171+
} catch (InterruptedException e) {
172+
e.printStackTrace();
173+
}
161174
inventoryService.decrease(buildInventoryDTO(order));
162175
return "success";
163176
}
@@ -166,14 +179,14 @@ private void updateOrderStatus(final Order order, final OrderStatusEnum orderSta
166179
order.setStatus(orderStatus.getCode());
167180
orderMapper.update(order);
168181
}
169-
182+
170183
private AccountDTO buildAccountDTO(final Order order) {
171184
AccountDTO accountDTO = new AccountDTO();
172185
accountDTO.setAmount(order.getTotalAmount());
173186
accountDTO.setUserId(order.getUserId());
174187
return accountDTO;
175188
}
176-
189+
177190
private AccountNestedDTO buildAccountNestedDTO(final Order order) {
178191
AccountNestedDTO nestedDTO = new AccountNestedDTO();
179192
nestedDTO.setAmount(order.getTotalAmount());
@@ -182,7 +195,7 @@ private AccountNestedDTO buildAccountNestedDTO(final Order order) {
182195
nestedDTO.setCount(order.getCount());
183196
return nestedDTO;
184197
}
185-
198+
186199
private InventoryDTO buildInventoryDTO(final Order order) {
187200
InventoryDTO inventoryDTO = new InventoryDTO();
188201
inventoryDTO.setCount(order.getCount());
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
package org.dromara.hmily.demo.springcloud.order.enums;
2+
3+
import lombok.Getter;
4+
import lombok.RequiredArgsConstructor;
5+
6+
/**
7+
* The enum transaction enum.
8+
*
9+
* @author zhangzhi
10+
*/
11+
@RequiredArgsConstructor
12+
@Getter
13+
public enum ReadCommittedTransactionEnum {
14+
15+
TRANSACTION_READ_WRITE(1, "读已提交隔离级别的事务, 包括更新、查询操作"),
16+
17+
TRANSACTION_READ_ONLY(2, "读已提交隔离级别的事务, 只有查询操作");
18+
19+
private final int code;
20+
21+
private final String desc;
22+
}

hmily-demo/hmily-demo-tac/hmily-demo-tac-springcloud/hmily-demo-tac-springcloud-order/src/main/java/org/dromara/hmily/demo/springcloud/order/service/PaymentService.java

+3-1
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818

1919

2020
import org.dromara.hmily.demo.common.order.entity.Order;
21+
import org.dromara.hmily.demo.springcloud.order.enums.ReadCommittedTransactionEnum;
2122

2223
/**
2324
* PaymentService.
@@ -100,6 +101,7 @@ public interface PaymentService {
100101
* 订单支付.
101102
*
102103
* @param order 订单实体
104+
* @param readCommittedTransactionEnum 读已提交事务类型
103105
*/
104-
String makePaymentWithReadCommitted(Order order);
106+
String makePaymentWithReadCommitted(Order order, ReadCommittedTransactionEnum readCommittedTransactionEnum);
105107
}

hmily-demo/hmily-demo-tac/hmily-demo-tac-springcloud/hmily-demo-tac-springcloud-order/src/main/java/org/dromara/hmily/demo/springcloud/order/service/impl/OrderServiceImpl.java

+18-1
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
import org.dromara.hmily.demo.common.order.entity.Order;
2121
import org.dromara.hmily.demo.common.order.enums.OrderStatusEnum;
2222
import org.dromara.hmily.demo.common.order.mapper.OrderMapper;
23+
import org.dromara.hmily.demo.springcloud.order.enums.ReadCommittedTransactionEnum;
2324
import org.dromara.hmily.demo.springcloud.order.service.OrderService;
2425
import org.dromara.hmily.demo.springcloud.order.service.PaymentService;
2526
import org.slf4j.Logger;
@@ -133,7 +134,23 @@ public void updateOrderStatus(Order order) {
133134
public String orderPayWithReadCommitted(Integer count, BigDecimal amount) {
134135
Order order = saveOrder(count, amount);
135136
long start = System.currentTimeMillis();
136-
paymentService.makePaymentWithReadCommitted(order);
137+
// 开启一个事务
138+
new Thread(() -> {
139+
try {
140+
paymentService.makePaymentWithReadCommitted(order, ReadCommittedTransactionEnum.TRANSACTION_READ_WRITE);
141+
} catch (Exception e) {
142+
System.out.println(e.getMessage());
143+
}
144+
}, "global trans2").start();
145+
try {
146+
// 确保第一个事务先执行
147+
Thread.sleep(1000);
148+
} catch (InterruptedException e) {
149+
e.printStackTrace();
150+
}
151+
// 开启另一个事务
152+
paymentService.makePaymentWithReadCommitted(order, ReadCommittedTransactionEnum.TRANSACTION_READ_ONLY);
153+
137154
System.out.println("切面耗时:" + (System.currentTimeMillis() - start));
138155
return "success";
139156
}

hmily-demo/hmily-demo-tac/hmily-demo-tac-springcloud/hmily-demo-tac-springcloud-order/src/main/java/org/dromara/hmily/demo/springcloud/order/service/impl/PaymentServiceImpl.java

+15-3
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,6 @@
1717
package org.dromara.hmily.demo.springcloud.order.service.impl;
1818

1919
import org.dromara.hmily.annotation.HmilyTAC;
20-
import org.dromara.hmily.annotation.HmilyTCC;
2120
import org.dromara.hmily.common.exception.HmilyRuntimeException;
2221
import org.dromara.hmily.demo.common.account.dto.AccountDTO;
2322
import org.dromara.hmily.demo.common.account.dto.AccountNestedDTO;
@@ -27,6 +26,7 @@
2726
import org.dromara.hmily.demo.common.order.mapper.OrderMapper;
2827
import org.dromara.hmily.demo.springcloud.order.client.AccountClient;
2928
import org.dromara.hmily.demo.springcloud.order.client.InventoryClient;
29+
import org.dromara.hmily.demo.springcloud.order.enums.ReadCommittedTransactionEnum;
3030
import org.dromara.hmily.demo.springcloud.order.service.PaymentService;
3131
import org.slf4j.Logger;
3232
import org.slf4j.LoggerFactory;
@@ -154,13 +154,24 @@ public String mockPaymentInventoryWithConfirmTimeout(final Order order) {
154154

155155
@Override
156156
@HmilyTAC
157-
public String makePaymentWithReadCommitted(Order order) {
157+
public String makePaymentWithReadCommitted(Order order, ReadCommittedTransactionEnum transactionEnum) {
158+
//第二个事务查询相同账户信息, 获取不到全局锁, 会进行回滚
159+
if (ReadCommittedTransactionEnum.TRANSACTION_READ_ONLY.equals(transactionEnum)) {
160+
accountClient.findByUserId(order.getUserId());
161+
return "success";
162+
}
158163
updateOrderStatus(order, OrderStatusEnum.PAY_SUCCESS);
159164
//扣除用户余额
160165
accountClient.payment(buildAccountDTO(order));
161-
//查询账户信息, 读已提交, 此时该事务未结束, 获取全局锁失败, 将会回滚
166+
//查询账户信息, 读已提交隔离级别, 但是在统一全局事务中, 所以可见
162167
accountClient.findByUserId(order.getUserId());
163168
//进入扣减库存操作
169+
try {
170+
// 延时第一个事务, 确保第一个事务还没结束, 第二个事务执行查询
171+
Thread.sleep(1200);
172+
} catch (InterruptedException e) {
173+
e.printStackTrace();
174+
}
164175
inventoryClient.decrease(buildInventoryDTO(order));
165176
return "success";
166177
}
@@ -192,4 +203,5 @@ private AccountNestedDTO buildAccountNestedDTO(Order order) {
192203
nestedDTO.setCount(order.getCount());
193204
return nestedDTO;
194205
}
206+
195207
}

0 commit comments

Comments
 (0)