-
Notifications
You must be signed in to change notification settings - Fork 519
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
Spring-Retry @Retryable not working with Spring-data Repositories #214
Comments
If you are mocking the Show the rest of your test case. |
@garyrussell I am using And it is a part of the integration test, so the Spring context is present. If spring retry will not work with Mock here, what is the way to test Spring-retry over Spring-Jpa? We already have a question on Stackoverflow . |
Also, I tested this in real-time (not only in the test case), Spring-retry is not working on Spring-data repository. On retrying on any exception and returning a value from
|
As said in that article (and the
|
For the "real" case, it is because spring-retry is wrapping the JPA proxy in another proxy that only implements So, yes, |
Here is a work around - remove @SpringBootApplication
public class Srgh214Application {
private static final Logger log = LoggerFactory.getLogger(Srgh214Application.class);
public static void main(String[] args) {
SpringApplication.run(Srgh214Application.class, args);
}
@Bean
public ApplicationRunner runner(TestRepository repo) {
return args -> {
repo.findAll();
};
}
@Bean
static BPP bpp() {
return new BPP();
}
}
class BPP implements BeanPostProcessor, Ordered {
private static final Logger log = LoggerFactory.getLogger(BPP.class);
@Override
public int getOrder() {
return Integer.MAX_VALUE;
}
@Override
@Nullable
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
if (bean instanceof TestRepository) {
Advised advised = (Advised) bean;
RetryOperationsInterceptor interceptor = RetryInterceptorBuilder.stateless()
.maxAttempts(4)
.backOffOptions(2000, 1.0, 2000)
.recoverer((args, cause) -> {
log.info("retries exhausted: " + cause.getMessage());
throw (RuntimeException) cause;
})
.build();
advised.addAdvice(0, interceptor);
}
return bean;
}
} |
@garyrussell Thanks this works perfectly fine. |
adding support for that can be useful :) |
@garyrussell, the code you mentioned, will it be helpful to add retries on JPA repo which is doing DB calls present in the main |
What would be the best place for a generic Spring Data integration? A RepositoryFactoryBeanSupport factoryBean = …;
factoryBean.addRepositoryFactoryCustomizer(repositoryFactory -> {
repositoryFactory.addRepositoryProxyPostProcessor((proxyFactory, repositoryInformation) -> {
proxyFactory.addAdvisor(…);
});
}); Adding Spring Retry logic including annotation scanning to Spring Data isn't probably the best approach as we would duplicate already existing code to some extent. |
spring-retry currently uses an Injecting the repo fails because the outer proxy only implements We can somehow fix it by adding the retry interceptor advice to the existing proxy, but we would also need to prevent the auto proxy BPP from wrapping it in another proxy. I don't think this will need any changes in spring-jpa. I'll dig around some more... |
I have found a solution; will submit a PR soon. However, I have discovered that the root cause of the problem is Boot sets It works for me with
When it is true, the auto-proxy feature fails to copy the inner proxy's interface to the outer proxy; when it is false, the interfaces are copied. My solution (for when it is true) is to re-create a single proxy with a merged set of interfaces and advisors. |
And that is actually what we always do in Spring Integration. If that is already a proxy we add our advises to it otherwise create:
|
Yes; but it's more complicated here, because Spring AOP auto-proxy creates a new proxy unconditionally. It only copies the interfaces to the outer proxy if |
Resolves spring-projects#214 When `proxyTargetClass` is `true` (Boot's default), the JPA interfaces are not copied to the outer retry proxy created by auto-proxy. Add a BPP to fix up the proxy, when needed.
Resolves spring-projects#214 When `proxyTargetClass` is `true` (Boot's default), the JPA interfaces are not copied to the outer retry proxy created by auto-proxy. Add a BPP to fix up the proxy, when needed. Also, the pointcut matchers must match on the `SimpleJpaRepository`.
Resolves spring-projects#214 When `proxyTargetClass` is `true` (Boot's default), the JPA interfaces are not copied to the outer retry proxy created by auto-proxy. Add a BPP to fix up the proxy, when needed. Also, the pointcut filter must match on the `SimpleJpaRepository`.
Resolves spring-projects#214 When `proxyTargetClass` is `true` (Boot's default), the JPA interfaces are not copied to the outer retry proxy created by auto-proxy. Add a BPP to fix up the proxy, when needed. Also, the pointcut filter must match on the `SimpleJpaRepository`.
Hi, how are we to use the Retryable with Repository today ? is it still the same as #214 (comment) Just got bitten by it. |
Fixed in Spring Framework 5.3.9 spring-projects/spring-framework#27044 Current 5.3.x is 5.3.15 https://spring.io/projects/spring-framework#learn |
I was trying @retryable with Spring-data and testing it out using Mockito @MockBean but the retry for the repository is not working and it was always called only once.
Referring to this Stackoverflow answer
Spring-boot: 2.3.0.RELEASE
Spring-Retry: 1.2.5.RELEASE
Spring-data: 2.3.0.RELEASE
The text was updated successfully, but these errors were encountered: