Skip to content
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

TCC模式下,开启它的useTCCFence功能,在rollback方法中抛出一个异常,业务系统服务捕获到的异常信息为null #6665

Closed
iAmClever opened this issue Jul 12, 2024 · 5 comments · Fixed by #6666
Assignees
Labels

Comments

@iAmClever
Copy link
Contributor

Ⅰ. Issue Description
TCC模式下,开启它的useTCCFence功能,在rollback方法中抛出一个异常,业务系统服务捕获到的异常信息为null

Ⅱ. Describe what happened
TCC模式下,开启useTCCFence功能,在触发rollback的时候,会调到【io.seata.rm.tcc.TCCFenceHandler#updateStatusAndInvokeTargetMethod】,这里会使用jdk的反射去调用业务系统的rollback方法,如果rollback方法出现异常,业务系统的异常信息会被吃掉,因为jdk反射method.invoke()方法为了对异常做统一处理,会把业务异常统一包装成InvocationTargetException类型。

业务系统服务错误栈如下:

2024-07-12 09:52:31.079 ERROR 22908 --- [_RMROLE_1_19_24] io.seata.rm.AbstractResourceManager      : rollback TCC resource error, resourceId: updateInventoryAcquire, xid: 10.244.137.109:8091:8944687993233431500.

java.lang.reflect.InvocationTargetException: null
	at jdk.internal.reflect.GeneratedMethodAccessor56.invoke(Unknown Source)
	at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
	at java.base/java.lang.reflect.Method.invoke(Method.java:566)
	at io.seata.rm.tcc.TCCFenceHandler.updateStatusAndInvokeTargetMethod(TCCFenceHandler.java:255)
	at io.seata.rm.tcc.TCCFenceHandler.lambda$rollbackFence$2(TCCFenceHandler.java:212)
	at org.springframework.transaction.support.TransactionTemplate.execute(TransactionTemplate.java:140)
	at io.seata.rm.tcc.TCCFenceHandler.rollbackFence(TCCFenceHandler.java:187)
	at io.seata.rm.tcc.TCCResourceManager.branchRollback(TCCResourceManager.java:164)
	at io.seata.rm.AbstractRMHandler.doBranchRollback(AbstractRMHandler.java:125)
	at io.seata.rm.AbstractRMHandler$2.execute(AbstractRMHandler.java:67)
	at io.seata.rm.AbstractRMHandler$2.execute(AbstractRMHandler.java:63)
	at io.seata.core.exception.AbstractExceptionHandler.exceptionHandleTemplate(AbstractExceptionHandler.java:131)
	at io.seata.rm.AbstractRMHandler.handle(AbstractRMHandler.java:63)
	at io.seata.rm.DefaultRMHandler.handle(DefaultRMHandler.java:68)
	at io.seata.core.protocol.transaction.BranchRollbackRequest.handle(BranchRollbackRequest.java:35)
	at io.seata.rm.AbstractRMHandler.onRequest(AbstractRMHandler.java:150)
	at io.seata.core.rpc.processor.client.RmBranchRollbackProcessor.handleBranchRollback(RmBranchRollbackProcessor.java:63)
	at io.seata.core.rpc.processor.client.RmBranchRollbackProcessor.process(RmBranchRollbackProcessor.java:58)
	at io.seata.core.rpc.netty.AbstractNettyRemoting.lambda$processMessage$2(AbstractNettyRemoting.java:281)
	at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1128)
	at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:628)
	at io.netty.util.concurrent.FastThreadLocalRunnable.run(FastThreadLocalRunnable.java:30)
	at java.base/java.lang.Thread.run(Thread.java:829)
Caused by: java.lang.RuntimeException: 测试异常信息抛出
	at com.anycubic.cerp.inventory.service.inventory.action.UpdateInventoryAcquireActionImpl.rollback(UpdateInventoryAcquireActionImpl.java:105)
	at com.anycubic.cerp.inventory.service.inventory.action.UpdateInventoryAcquireActionImpl$$FastClassBySpringCGLIB$$147ec8e9.invoke(<generated>)
	at org.springframework.cglib.proxy.MethodProxy.invoke(MethodProxy.java:218)
	at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.invokeJoinpoint(CglibAopProxy.java:793)
	at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:163)
	at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.proceed(CglibAopProxy.java:763)
	at io.seata.spring.tcc.TccActionInterceptor.invoke(TccActionInterceptor.java:84)
	at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186)
	at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.proceed(CglibAopProxy.java:763)
	at org.springframework.aop.framework.CglibAopProxy$DynamicAdvisedInterceptor.intercept(CglibAopProxy.java:708)
	at com.anycubic.cerp.inventory.service.inventory.action.UpdateInventoryAcquireActionImpl$$EnhancerBySpringCGLIB$$a9d3d742.rollback(<generated>)
	... 23 common frames omitted

2024-07-12 09:52:31.079  INFO 22908 --- [_RMROLE_1_19_24] io.seata.rm.AbstractRMHandler            : Branch Rollbacked result: PhaseTwo_RollbackFailed_Retryable
2024-07-12 09:52:31.108  INFO 22908 --- [           main] i.seata.tm.api.DefaultGlobalTransaction  : transaction end, xid = 10.244.137.109:8091:8944687993233431500
2024-07-12 09:52:31.108  INFO 22908 --- [           main] i.seata.tm.api.DefaultGlobalTransaction  : [10.244.137.109:8091:8944687993233431500] rollback status: RollbackRetrying
2024-07-12 09:52:31.109  WARN 22908 --- [           main] i.s.tm.api.DefaultFailureHandlerImpl     : Retrying to rollback transaction[10.244.137.109:8091:8944687993233431500]

Ⅲ. Describe what you expected to happen

在rollback方法中如果抛出异常信息后,业务系统可以获取到更加直观的异常信息

Ⅳ. How to reproduce it (as minimally and precisely as possible)

参考mybatis【org.apache.ibatis.reflection.ExceptionUtil#unwrapThrowable】对 反射调用方法抛出异常【InvocationTargetException】 or 对代理对象调用方法抛出异常【UndeclaredThrowableException】的处理,在捕获到异常类型是【InvocationTargetException】 、【UndeclaredThrowableException】,调用异常对象的【getTargetException】方法,以获取到被包装的原始异常对象,然后再抛出这个原始异常对象,这样业务系统就可以获取到更加直观的异常信息

优化后抛出的异常信息如下:

2024-07-12 10:08:58.305 ERROR 3976 --- [_RMROLE_1_24_24] io.seata.rm.AbstractResourceManager      : rollback TCC resource error, resourceId: updateInventoryAcquire, xid: 10.244.137.109:8091:8944687993233386807.

java.lang.Exception: java.lang.RuntimeException: 测试异常信息抛出
	at io.seata.rm.tcc.TCCFenceHandler.updateStatusAndInvokeTargetMethod(TCCFenceHandler.java:270)
	at io.seata.rm.tcc.TCCFenceHandler.lambda$rollbackFence$2(TCCFenceHandler.java:212)
	at org.springframework.transaction.support.TransactionTemplate.execute(TransactionTemplate.java:140)
	at io.seata.rm.tcc.TCCFenceHandler.rollbackFence(TCCFenceHandler.java:187)
	at io.seata.rm.tcc.TCCResourceManager.branchRollback(TCCResourceManager.java:164)
	at io.seata.rm.AbstractRMHandler.doBranchRollback(AbstractRMHandler.java:125)
	at io.seata.rm.AbstractRMHandler$2.execute(AbstractRMHandler.java:67)
	at io.seata.rm.AbstractRMHandler$2.execute(AbstractRMHandler.java:63)
	at io.seata.core.exception.AbstractExceptionHandler.exceptionHandleTemplate(AbstractExceptionHandler.java:131)
	at io.seata.rm.AbstractRMHandler.handle(AbstractRMHandler.java:63)
	at io.seata.rm.DefaultRMHandler.handle(DefaultRMHandler.java:68)
	at io.seata.core.protocol.transaction.BranchRollbackRequest.handle(BranchRollbackRequest.java:35)
	at io.seata.rm.AbstractRMHandler.onRequest(AbstractRMHandler.java:150)
	at io.seata.core.rpc.processor.client.RmBranchRollbackProcessor.handleBranchRollback(RmBranchRollbackProcessor.java:63)
	at io.seata.core.rpc.processor.client.RmBranchRollbackProcessor.process(RmBranchRollbackProcessor.java:58)
	at io.seata.core.rpc.netty.AbstractNettyRemoting.lambda$processMessage$2(AbstractNettyRemoting.java:281)
	at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1128)
	at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:628)
	at io.netty.util.concurrent.FastThreadLocalRunnable.run(FastThreadLocalRunnable.java:30)
	at java.base/java.lang.Thread.run(Thread.java:829)
Caused by: java.lang.RuntimeException: 测试异常信息抛出
	at com.anycubic.cerp.inventory.service.inventory.action.UpdateInventoryAcquireActionImpl.rollback(UpdateInventoryAcquireActionImpl.java:105)
	at com.anycubic.cerp.inventory.service.inventory.action.UpdateInventoryAcquireActionImpl$$FastClassBySpringCGLIB$$147ec8e9.invoke(<generated>)
	at org.springframework.cglib.proxy.MethodProxy.invoke(MethodProxy.java:218)
	at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.invokeJoinpoint(CglibAopProxy.java:793)
	at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:163)
	at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.proceed(CglibAopProxy.java:763)
	at io.seata.spring.tcc.TccActionInterceptor.invoke(TccActionInterceptor.java:84)
	at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186)
	at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.proceed(CglibAopProxy.java:763)
	at org.springframework.aop.framework.CglibAopProxy$DynamicAdvisedInterceptor.intercept(CglibAopProxy.java:708)
	at com.anycubic.cerp.inventory.service.inventory.action.UpdateInventoryAcquireActionImpl$$EnhancerBySpringCGLIB$$e4bf58ac.rollback(<generated>)
	at jdk.internal.reflect.GeneratedMethodAccessor57.invoke(Unknown Source)
	at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
	at java.base/java.lang.reflect.Method.invoke(Method.java:566)
	at io.seata.rm.tcc.TCCFenceHandler.updateStatusAndInvokeTargetMethod(TCCFenceHandler.java:256)
	... 19 common frames omitted

Ⅴ. Anything else we need to know?

我的想法是在seata-tcc模块里面新建一个ExceptionUtil类,并提供一个unwarp方法,在有反射方法调用的地方,需要捕获这个异常,并调用【ExceptionUtil】工具类做一层异常转换后再抛出这个异常。

如果认定这是一个 优化,我可以尝试提交修改的 PR ~~~

Ⅵ. Environment:

JDK version(e.g. java -version): 11
Seata client/server version: 1.8.0
Database version: 8.0.29

@funky-eyes
Copy link
Contributor

#6090

@funky-eyes
Copy link
Contributor

抱歉我误认为一阶段的异常了。不过这个二阶段的下发是异步的,即便按照你说的方式进行处理,业务又要如何感知呢?
Sorry, I mistook the first-stage exception. But the delivery of this second-stage is asynchronization/asynchronous. Even if it is handled in the way you said, how can the business perceive it?

@iAmClever
Copy link
Contributor Author

抱歉我误认为一阶段的异常了。不过这个二阶段的下发是异步的,即便按照你说的方式进行处理,业务又要如何感知呢? Sorry, I mistook the first-stage exception. But the delivery of this second-stage is asynchronization/asynchronous. Even if it is handled in the way you said, how can the business perceive it?

是的,rollback是异步的,业务系统捕获不到,但在看日志的时候,如果这样处理能更直观的看到错误栈

@funky-eyes
Copy link
Contributor

我觉得可以,欢迎你提交一个pr来优化这个问题
I think it's okay, you are welcome to commit a PR to optimize this problem.

@iAmClever
Copy link
Contributor Author

我觉得可以,欢迎你提交一个pr来优化这个问题 I think it's okay, you are welcome to commit a PR to optimize this problem.

好,我会尝试提一个pr进行优化

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

Successfully merging a pull request may close this issue.

2 participants