liuyangming / ByteTCC

ByteTCC is a distributed transaction manager based on the TCC(Try/Confirm/Cancel) mechanism. It’s compatible with the JTA specification. User guide: https://github.com/liuyangming/ByteTCC/wiki
https://www.bytesoft.org/
GNU Lesser General Public License v3.0
2.9k stars 911 forks source link

当发生异常的时候没有调用Cancel类方法 #8

Open liugh1974 opened 7 years ago

liugh1974 commented 7 years ago

Hi, 你好! 当我在运行你的示例代码的时候,发现一个问题,请看下面代码: sample-consumer module:

public class MultiDsTransferServiceImpl implements ITransferService {
    ...
    @Transactional(rollbackFor = ServiceException.class)
    public void transfer(String sourceAcctId, String targetAcctId, double amount) throws ServiceException {

         this.nativeAccountService.decreaseAmount(sourceAcctId, amount);
         this.increaseAmount(targetAcctId, amount);

        throw new ServiceException("rollback");
    }
    ...
}

我把你注释的那个异常打开,按我拉理解,应该是AccountServiceCancel.decreaseAmount 和 TransferServiceCancel.transfer 两个方法都应该被调用, 但实际只是AccountServiceCancel.decreaseAmount 被触发, 而TransferServiceCancel.transfer 没有被调用。请看下面日志:

com.bytesvc.service.impl.AccountServiceImpl: exec decrease: acct= 1001, amount=    1.00
com.bytesvc.service.impl.MultiDsTransferServiceImpl: exec increase: acct= 2001, amount=    1.00
com.bytesvc.service.cancel.AccountServiceCancel: undo decrease: acct= 1001, amount=    1.00
com.bytesvc.ServiceException: rollback
     at com.bytesvc.service.impl.MultiDsTransferServiceImpl.transfer(MultiDsTransferServiceImpl.java:29)
       # ...

不知道这是bug还是我的理解有误? 能否解释一下。 谢谢。

liuyangming commented 7 years ago

[你好,这个是正常情况(你可以检查一下业务数据,看数据是否不一致),ByteTCC的cancel操作并不总是会被执行,这点在用户指南的“3.3、cancel实现类”章节中有说明。

这个例子中:AccountServiceImpl执行decrease;MultiDsTransferServiceImpl执行increase; 因为全局事务是MultiDsTransferServiceImpl发起的,且异常是在MultiDsTransferServiceImpl中抛出的。MultiDsTransferServiceImpl所在的事务管理器很明确的知道自己节点上的分支事务需要回滚,因此回滚本节点上try阶段的操作可以直接通过DB事务的rollback来完成(而不需要借助cancel业务逻辑)。

liugh1974 commented 7 years ago

多谢!

shenzhanwang commented 6 years ago

那照这么说主事务的cancel代码永远不会执行了,因为直接被失败的try阶段回滚了

liuyangming commented 6 years ago

Cancel逻辑当然不是永远不会被执行。比如:         服务A(Required) -------->服务B(RequiresNew) 这个场景,A与B在同一个应用中但属于不同的本地事务(B使用了RequiresNew)。这种情况下,B执行成功,其Try操作提交后,A执行失败导致全局事务回滚,这时候ByteTCC就会调用B服务的Cancel操作。

Cancel逻辑的提供是用来回撤Try操作的,只有Try操作产生了影响时才应该被执行。 ByteTCC能直接通过DB本地事务回滚Try操作时,就不必调用其Cancel逻辑。具体请参考用户指南里的说明。