-
Notifications
You must be signed in to change notification settings - Fork 8.8k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
AT和TCC模式混合使用场景的问题咨询 #7163
Comments
@funky-eyes 哥 这个问题帮忙看看 我之前描述的有点啰嗦了 就是发现tcc模式下,在prepare接口调用第三方服务时也会把全局事务头带过去,这如果第三方服务也接入seata,那会导致第三方同时以tcc、At两种模式加入全局事务,此时如果回滚的话,at模式的回滚和cancel回滚都会被调用。这是符合预期的设计吗?是否可以考虑tcc模式下应该抑制全局事务消息的传递? |
When TCC and AT use the same data source, the |
我在下游添加了@GlobalLock注解,但是在上游来看 下游仍然同时是以at和tcc两种模式一起运行: 这是下游at的分支注册: 这是上游tcc的分支注册和commit方法被调用成功的记录: 这里我理解@GlobalLock是被设计出来在非全局事务范围进行锁冲突的校验,似乎不能改变分支的模式,我这里是希望下游只以tcc模式运行,我目前是采用的方案prepare方法中rpc下游前,临时退出全局事务
但是这似乎成了每次tcc调用下游必须做的事,按我目前理解,tcc模式应该就是分支的终点,全局事务的信息就不应该继续往下传播了。 |
接入TCC的方法,不会被AT代理的,所以只会创建TCC分支,没有AT分支。执行的b2Prepare方法,最多只会本地事务回滚,不会被AT回滚。 |
是的 上游发起tcc的应用,tcc中的prepare方法里的执行是不会被at代理。但是我这里的主要疑问是,如果我在上游tcc的prepare方法中通过feign rpc下游应用,这时候头信息会被带到下游,下游会加入到at模式中运行,这时下游同时被at模式和tcc所控制,比如如果发生全局事务的回滚,对于下游则同时运行在at模式的回滚和tcc的cancel方法下。 |
我明白了,你是在一个tcc分支内去远程调用了一个AT分支是吧?所以你的cancel里就会又去调用AT的远程分支,那是不是你把cancel里的调用远程的AT分支代码去掉就好了?因为远程分支自己会回滚。 |
对!差不多是这个意思,但是问题在于,并不是我去调用了一个at分支,而是远程调用默认下游就以at模式加入了,目前头信息里也是不带有分支类型的吧。另外这里之所以用tcc去调用,就是因为下游无法完全以at模式运作,具体来说就是下游的下游是python写的,所以这里通过tcc方式接入,在cancel方法里是要调用下游提供的回滚接口,光以at回滚会导致数据不一致。 |
下游是python写的,为什么会加入at模式? |
是下游的下游是python,比如A->B->C这样的调用链,C是python写的,然后现在是B基于C对外提供了一些能力,提供了正向计算和回滚的接口。从A这里发起调用,就是以TCC的方式分别在prepare和cancel中调用B的计算和回滚接口。 |
可以将顺序改动一下,比如先执行AT分支,然后让调用python服务的方法单独成为一个tcc分支,不要将代码都写在tcc的 prepare中 |
我举个简单例子
或者:
|
辛苦老哥答疑,是这样的你举的两个例子中,其中例子1在我们的场景下由于服务按职责分层,是不能直接访问c3接口的,而是要访问b2(),b2提供了完整的服务(他在内部会调用c3)。但是如果对b2发起tcc就会有我说的,应用B被同时加入到了tcc和at分支下。 例子2中,其实就是由b发起tcc,这么做现在是ok的,但是有个隐患,如果未来c切换成java,那么b2就得改,否则就是应用c被同时加入到tcc和at分支下。 |
就算以后c变成java了,依然可以用tcc,除非你不准备用tcc了才需要改代码。 |
我理解这不仅仅是使用规不规范的问题,即使放在provider上并且都是原子性动作仍然会有问题,以c从python到java来说明。 现状:c现在是python,现在假设tcc的注解是放在了b上,就是放在provider上,也就是下面这个写法。然后我们再假设,call c3 python应用接口1()、 call c3 python应用接口3();都是原子的,假设c3是订单业务为例
未来:如果c变成了java,并且接入seata,那么上面原先c3方法(prepare)再次被调用时,通过call c3 python应用接口1();会将全局事务头信息带到c的接口里,c的接口执行时里会认为当前是处于全局事务的at模式下,执行“插入订单表一条数据,状态未待确认”操作时,会注册at模式分支,如果此时全局回滚,对b而言会call c3 python应用接口1();去“删除订单信息”,而c注册的at模式也会回滚这条插入订单的数据。 这样不就重复执行了回滚吗? |
tcc要单一原则,怎么能在下面又有远程调用又有业务逻辑? |
就是单一的“插入订单表一条数据,状态未待确认” 这些注释是指的call c3 python应用接口1();这个远程调用做的事 |
那你只要换成java的时候,提供一个v2版本接口给服务B即可,理论上接口迭代都应该有版本,或者分组等信息来做灰度,再逐步全量换为新版本的接口。所以根据正常的业务需求迭代和架构规范,不可能在原接口上,直接从python换java,即便要换,也应该是java的先不用AT模式,依旧以tcc运行,先发布第一版,后续再迭代第二版接口,也就是 |
差异就在这,当切成java后提供的v2版本接口时,实际c业务逻辑做的事是和python一样的,仍是提供三个接口这是没变的,但是C却需要增加tcc的注解。现实来看A、B、C都是不同组甚至是不同部门的人管,很难保证他在切成java后能够评估到这个接口需要增加这些注解这件事,这无形增加了依赖,很难感知到这点。 而且从这个过程中可以发现,因为在tcc中能够向下游传递事务头信息,所以tcc提供者必须在最下游(java调用链的最下游) |
我无法理解为什么在tcc中rpc下游需要传递xid信息,首先在tcc方法执行过程中at模式是不生效的,另外tcc中的try-cancel是对应的,cancel就是负责回滚try的处理。我没明白是什么场景需要将通过tcc将事务消息带到下游,让下游也加入到全局事务中。 |
你在try阶段做了N个事情,这已经脱离了tcc范畴了。 |
就是因为每个应用有不同的负责,只有对应应用的负责团队才应该决定是否使用tcc,还是at,而不是调用方来决定 |
已上面的例子为例,c提供的是python接口只做往db插入一行订单记录的事,而b在tcc只做rpc c一件事,并没有做了n件事啊。 |
按我目前的理解,现在相当于有个约束,如果你在tcc方法中rpc第三方接口(第三方也接入seata),那么第三方接口的逻辑中就不能包含有写db的操作。 |
老哥 感觉我们已经聊了好几天了,是不是可以升级一下沟通方式,提高一下效率,是否可以通过腾讯会议、或者其他软件会议的手段进一步聊一下这个问题。 |
我不认为这是个问题,或者是个bug,纯粹是使用方式不正确而已。如果按你说的B只做了rpc到c,未来c变成java应用也没有影响,因为可以通过不同接口和版本的方式来迭代从python到java的迭代过程。 |
正确的使用方式,只在B调C时,用TCC,C提供prepare、cancel、commit三个接口,对应让B的TCC模式的三个方法去调C的三个接口。 B都接入了seata了,上游服务A在调用B时就不要用TCC。用TCC就是重复提交或回滚了。 |
一、背景介绍
有三个应用A、B、C,A、B能够接入seata,C为python开发无法seata。应用A提供了两个接口a1、a2,a1的调用链路为A、B因此使用的是AT模式,a2的调用链为A、B、C,因此使用的AT+Tcc模式。a2方法简化的相关代码如下:
我遇到的问题是在应用A中,b2Prepare方法通过rpc访问B的b2接口时候,全局的事务的头信息会被传递到B中,b2中的saveDb方法会以at模式运行,会生成前后镜像,也会注册at分支。然后我的疑问是,如果这时候在a2中抛出了异常,那么对于at的部分b2中saveDb的数据会被清除,tcc部分rollbackB2方法也会被调用,这对于B而言相当于执行了两次回滚,这是符合预期的吗?是否在tcc分支下应该抑制全局事务消息的传递?
The text was updated successfully, but these errors were encountered: