ByteTCC
ByteTCC copied to clipboard
分布式事务发起方try抛异常后,cancel方法没有得到调用
以下是消费端的try @Compensable(interfaceClass = PayService.class, confirmableKey = "confirmPayService", cancellableKey = "cancelPayService") @RestController public class TccPayController implements PayService{ /** * 分布式事务发起方的try方法 */ @RequestMapping(value = "/pay/{orderid}", method = RequestMethod.GET) @Transactional public Ordering payOrder(@PathVariable("orderid") Integer orderid) throws Exception{ // 支付 Ordering order=orderingService.getOrderById(orderid); // 修改支付状态 order.setState("正在支付"); orderingService.updateOrder(order); // 生成支付流水 String uuid = UUID.randomUUID().toString(); // 扣钱 tccAccountService.payAccountTcc(order, uuid); // 加积分 tccPointService.addPoint(order, uuid); Ordering ordering=orderingService.getOrderById(orderid); return ordering;
}
分支事务tccPointService.addPoint(order, uuid);方法调用失败,直接返回500,cancel方法不被调用。 但若catch调用失败的异常又会直接走confirm逻辑,请指教。
我感觉是不是主事务失败后直接回滚try操作,cancel方法永远不会执行。
所以主事务一旦失败,相当于什么都没做
Cancel逻辑并不一定会被执行。ByteTCC能直接通过DB本地事务回滚Try操作时,就不必调用其Cancel来回撤其影响。具体请参考用户指南里的说明。
我觉得这样设计并不太合理,因为这样子的话,主事务的cancel方法就白写了。实际上cancel阶段可以用来设置一下订单支付失败的状态,如果什么都不做,那订单的状态还是未支付。
还有一个地方不太合理,即使我们主事务的try阶段catch到了分支事务抛出的异常,也不该就此进入confirm阶段,因为毕竟分支事务调用是存在异常的。这种情况还是应该进入cancel阶段。
我觉得这样设计并不太合理,因为这样子的话,主事务的cancel方法就白写了。实际上cancel阶段可以用来设置一下订单支付失败的状态,如果什么都不做,那订单的状态还是未支付。
首先,有什么服务在编写的时候能确定自己一定不会被其他服务调用呢?即使有少部分服务确实如此,也可以忽略Cancel逻辑,ByteTCC并不强制一定提供Cancel逻辑,有就提供,没有可以不提供,ByteTCC-sample里就有这样的样例。 其次,主事务的Cancel逻辑也不是永远不会被执行。比如:服务A(Required) -------->服务B(RequiresNew) 这个场景,A与B在同一个应用中都属于主事务,但属于不同的DB本地事务(B使用了RequiresNew)。B执行成功,其Try操作提交后,A执行失败导致全局事务回滚,这时候ByteTCC就会调用B服务的Cancel操作。
还有一个地方不太合理,即使我们主事务的try阶段catch到了分支事务抛出的异常,也不该就此进入confirm阶段,因为毕竟分支事务调用是存在异常的。这种情况还是应该进入cancel阶段。
Consumer端服务(主事务)如果能够处理分支Provider端(分支事务)的异常,或者Provider端的异常不影响其整个处理流程,就捕捉异常;如果影响其处理流程而且自己又不能处理该异常,就不应该吞掉这个异常。 退一步说,如果Provider端抛出了异常,全局事务就回滚,那么Coonsumer捕捉该异常之后执行的其他操作(可能还调用了其他服务)也变得没有必要起来。
你说的这个想法,也并非完全不合理,只是没有特别合适的处理方式,后续版本也会考虑综合分支事务的状态来决定全局事务的完成方向的,敬请关注。