java - @Retryable only retrying once in integration test - Stack Overflow

I've started using the @Retryable annotation in my Spring Boot application (Spring Boot 2.7.18, JD

I've started using the @Retryable annotation in my Spring Boot application (Spring Boot 2.7.18, JDK 21) and it works perfectly fine!

Below are some relevant extracts from the code:

@EnableRetry
@EnableTransactionManagement
@SpringBootApplication
public class MyApplication {
    public static void main(String[] args) {
        SpringApplication.run(MyApplication.class, args);
    }
}
@Service
public class MyService {

    @Retryable(
        retryFor = {LockAcquisitionException.class, SQLServerException.class},
        maxAttempts = 5,
        backoff = @Backoff(delay = 500),
        listeners = "myRetryListener"
    )
    @Transactional
    @Override
    public void doSomething() throws MyException {
        // Does something
    }
}
public class MyRetryListener implements RetryListener {

    @Override
    public <T, E extends Throwable> boolean open(RetryContext context, RetryCallback<T, E> callback) {
        log.info("Retry open.");
        return true;
    }

    @Override
    public <T, E extends Throwable> void close(RetryContext context, RetryCallback<T, E> callback, Throwable throwable) {
        int attempt = context.getRetryCount();
        if (throwable == null) {
            log.info("Operation succeeded after {} attempts.", attempt);
        } else {
            log.info("Operation failed after {} attempts.", attempt);
        }
    }

    @Override
    public <T, E extends Throwable> void onError(RetryContext context, RetryCallback<T, E> callback, Throwable throwable) {
        log.warn("Inner retry {} of {} failed due to: {}", context.getRetryCount(), context.getAttribute("context.max-attempts"), throwable.getMessage());
    }
}

HOWEVER, when I run an integration test for the application, although the retry mechanism works, it only retries once. I suspect I am missing some initialization in the test, but I cannot figure out what it is.

Has anyone experienced anything similar?

@SpringBootTest
@ActiveProfiles("test")
@EnableAutoConfiguration
@AutoConfigureMockMvc
class MyApplicationIntegrationTest {
}

The application writes some records to a MS SQL Server database, which I have simulated in test with an H2 in memory database. In the integration test I throw a SQLException via a 'BEFORE INSERT' trigger in the H2 table.

The output of the application is, as expected:

2025-01-29 12:05:14  INFO .MyRetryListener    : Retry open.
2025-01-29 12:05:15  WARN SqlExceptionHelper  : SQL Error: 50000, SQLState: S0001
2025-01-29 12:05:15 ERROR SqlExceptionHelper  : Simulated SQLException for testing
2025-01-29 12:05:15  WARN .MyRetryListener    : Inner retry 1 of 5 failed due to: Could not persist entities
2025-01-29 12:05:15  WARN SqlExceptionHelper  : SQL Error: 50000, SQLState: S0001
2025-01-29 12:05:15 ERROR SqlExceptionHelper  : Simulated SQLException for testing
2025-01-29 12:05:15  WARN .MyRetryListener    : Inner retry 2 of 5 failed due to: Could not persist entities
2025-01-29 12:05:16  WARN SqlExceptionHelper  : SQL Error: 50000, SQLState: S0001
2025-01-29 12:05:16 ERROR SqlExceptionHelper  : Simulated SQLException for testing
2025-01-29 12:05:16  WARN .MyRetryListener    : Inner retry 3 of 5 failed due to: Could not persist entities
2025-01-29 12:05:16  WARN SqlExceptionHelper  : SQL Error: 50000, SQLState: S0001
2025-01-29 12:05:16 ERROR SqlExceptionHelper  : Simulated SQLException for testing
2025-01-29 12:05:16  WARN .MyRetryListener    : Inner retry 4 of 5 failed due to: Could not persist entities
2025-01-29 12:05:17  WARN SqlExceptionHelper  : SQL Error: 50000, SQLState: S0001
2025-01-29 12:05:17 ERROR SqlExceptionHelper  : Simulated SQLException for testing
2025-01-29 12:05:17  WARN .MyRetryListener    : Inner retry 5 of 5 failed due to: Could not persist entities
2025-01-29 12:05:17  INFO .MyRetryListener    : Operation failed after 5 attempts.

while the output of the test is, sadly:

2025:01:29 12:12:37  INFO  .MyRetryListener   : Retry open.
2025:01:29 12:12:37  WARN  TriggerImpl        : Executing database trigger H2TriggerImpl: Simulated SQLException for testing
2025:01:29 12:12:37  WARN  SqlExceptionHelper : SQL Error: 0, SQLState: null
2025:01:29 12:12:37 ERROR  SqlExceptionHelper : Simulated SQLException for testing
2025:01:29 12:12:37  INFO  .AbstractBatchImpl : HHH000010: On release of batch it still contained JDBC statements
2025:01:29 12:12:38  WARN  .MyRetryListener   : Inner retry 1 of 5 failed due to: Could not persist entities
2025:01:29 12:12:38  INFO  .MyRetryListener   : Operation failed after 1 attempts.

I've started using the @Retryable annotation in my Spring Boot application (Spring Boot 2.7.18, JDK 21) and it works perfectly fine!

Below are some relevant extracts from the code:

@EnableRetry
@EnableTransactionManagement
@SpringBootApplication
public class MyApplication {
    public static void main(String[] args) {
        SpringApplication.run(MyApplication.class, args);
    }
}
@Service
public class MyService {

    @Retryable(
        retryFor = {LockAcquisitionException.class, SQLServerException.class},
        maxAttempts = 5,
        backoff = @Backoff(delay = 500),
        listeners = "myRetryListener"
    )
    @Transactional
    @Override
    public void doSomething() throws MyException {
        // Does something
    }
}
public class MyRetryListener implements RetryListener {

    @Override
    public <T, E extends Throwable> boolean open(RetryContext context, RetryCallback<T, E> callback) {
        log.info("Retry open.");
        return true;
    }

    @Override
    public <T, E extends Throwable> void close(RetryContext context, RetryCallback<T, E> callback, Throwable throwable) {
        int attempt = context.getRetryCount();
        if (throwable == null) {
            log.info("Operation succeeded after {} attempts.", attempt);
        } else {
            log.info("Operation failed after {} attempts.", attempt);
        }
    }

    @Override
    public <T, E extends Throwable> void onError(RetryContext context, RetryCallback<T, E> callback, Throwable throwable) {
        log.warn("Inner retry {} of {} failed due to: {}", context.getRetryCount(), context.getAttribute("context.max-attempts"), throwable.getMessage());
    }
}

HOWEVER, when I run an integration test for the application, although the retry mechanism works, it only retries once. I suspect I am missing some initialization in the test, but I cannot figure out what it is.

Has anyone experienced anything similar?

@SpringBootTest
@ActiveProfiles("test")
@EnableAutoConfiguration
@AutoConfigureMockMvc
class MyApplicationIntegrationTest {
}

The application writes some records to a MS SQL Server database, which I have simulated in test with an H2 in memory database. In the integration test I throw a SQLException via a 'BEFORE INSERT' trigger in the H2 table.

The output of the application is, as expected:

2025-01-29 12:05:14  INFO .MyRetryListener    : Retry open.
2025-01-29 12:05:15  WARN SqlExceptionHelper  : SQL Error: 50000, SQLState: S0001
2025-01-29 12:05:15 ERROR SqlExceptionHelper  : Simulated SQLException for testing
2025-01-29 12:05:15  WARN .MyRetryListener    : Inner retry 1 of 5 failed due to: Could not persist entities
2025-01-29 12:05:15  WARN SqlExceptionHelper  : SQL Error: 50000, SQLState: S0001
2025-01-29 12:05:15 ERROR SqlExceptionHelper  : Simulated SQLException for testing
2025-01-29 12:05:15  WARN .MyRetryListener    : Inner retry 2 of 5 failed due to: Could not persist entities
2025-01-29 12:05:16  WARN SqlExceptionHelper  : SQL Error: 50000, SQLState: S0001
2025-01-29 12:05:16 ERROR SqlExceptionHelper  : Simulated SQLException for testing
2025-01-29 12:05:16  WARN .MyRetryListener    : Inner retry 3 of 5 failed due to: Could not persist entities
2025-01-29 12:05:16  WARN SqlExceptionHelper  : SQL Error: 50000, SQLState: S0001
2025-01-29 12:05:16 ERROR SqlExceptionHelper  : Simulated SQLException for testing
2025-01-29 12:05:16  WARN .MyRetryListener    : Inner retry 4 of 5 failed due to: Could not persist entities
2025-01-29 12:05:17  WARN SqlExceptionHelper  : SQL Error: 50000, SQLState: S0001
2025-01-29 12:05:17 ERROR SqlExceptionHelper  : Simulated SQLException for testing
2025-01-29 12:05:17  WARN .MyRetryListener    : Inner retry 5 of 5 failed due to: Could not persist entities
2025-01-29 12:05:17  INFO .MyRetryListener    : Operation failed after 5 attempts.

while the output of the test is, sadly:

2025:01:29 12:12:37  INFO  .MyRetryListener   : Retry open.
2025:01:29 12:12:37  WARN  TriggerImpl        : Executing database trigger H2TriggerImpl: Simulated SQLException for testing
2025:01:29 12:12:37  WARN  SqlExceptionHelper : SQL Error: 0, SQLState: null
2025:01:29 12:12:37 ERROR  SqlExceptionHelper : Simulated SQLException for testing
2025:01:29 12:12:37  INFO  .AbstractBatchImpl : HHH000010: On release of batch it still contained JDBC statements
2025:01:29 12:12:38  WARN  .MyRetryListener   : Inner retry 1 of 5 failed due to: Could not persist entities
2025:01:29 12:12:38  INFO  .MyRetryListener   : Operation failed after 1 attempts.
Share Improve this question asked Jan 29 at 12:23 GepGep 9283 gold badges16 silver badges33 bronze badges 4
  • 2 pls share the full test code and application-test.properties as well – Geii Lvov Commented Jan 29 at 12:35
  • 2 Your config is retryFor = {LockAcquisitionException.class, SQLServerException.class}, but probably your test environment doesn't not throw those exceptions. Some other instead, which is not retryable. – Artem Bilan Commented Jan 31 at 15:42
  • Maybe the OptimisticLockingFailureException is not retryable by your code. Probably related: stackoverflow/q/79228209 – Gábor Bakos Commented Feb 2 at 13:36
  • Thank you @ArtemBilan that was the actual issue. The test environment throws a SQLException, while the Retryable annotation expects a SQLServerException. I got confused by my own log statement while running the application (it should say "Simulated SQLServerException for testing"). – Gep Commented Feb 3 at 10:47
Add a comment  | 

2 Answers 2

Reset to default 1

According to your configuration only these exceptions are retryable:

retryFor = {LockAcquisitionException.class, SQLServerException.class},

So, make sure that your test environment throws one of those. All other exceptions are not retryable.

I do not have enough stackoverflow points so I am responding via an answer.

Given that you have LockAcquisition, I assume you are using a version column.

If you are using versioning, there are some additional annotations you might require depending on what type of locking approach you are taking.

For instance, for optimistic locking there's the annotation below where you can specify the exact lock type mode you want.

@Lock(LockModeType.OPTIMISTIC_FORCE_INCREMENT)

And also exceptions related to optimistic locking such as OptimisticLockException.class, CannotAcquireLockException.class, UnexpectedRollbackException.class, etc based on your requirements..

If this isn't the case, let me know and I can try to help as I have PROD experience with having to implement retryable.

发布者:admin,转转请注明出处:http://www.yc00.com/questions/1745298252a4621274.html

相关推荐

  • java - @Retryable only retrying once in integration test - Stack Overflow

    I've started using the @Retryable annotation in my Spring Boot application (Spring Boot 2.7.18, JD

    8小时前
    30

发表回复

评论列表(0条)

  • 暂无评论

联系我们

400-800-8888

在线咨询: QQ交谈

邮件:admin@example.com

工作时间:周一至周五,9:30-18:30,节假日休息

关注微信