存在@transactional和@retryable问题

zbsbpyhn  于 2021-07-09  发布在  Java
关注(0)|答案(1)|浏览(1831)

如果添加 @Retryable 从spring库。我的代码结构是这样的:

public class ExpireAndSaveTrades {
    @Transactional(rollbackFor = MyException.class)
    public void expireAndSaveTrades(List<Trade> trades) {
        try {
            // these two MUST be executed in one transaction
            trades.forEach(trade -> dao.expireTrades(trade));
            dao.saveTrades(trades);
        } catch (Exception e) {
            throw new MyException(e.getMessage(), e);
        }
    }
}

public class Dao {
    @Retryable(value = CannotAcquireLockException.class,
            maxAttempts = 3,
            stateful = true,
            backoff = @Backoff(delay = 300, multiplier = 3))
    public void expireTrades(Trade trade) {
    try {
          tradeRepository.expire(trade.getId(), trade.getNewStopDate());
    } catch (CannotAcquireLockException e) {
          expireTrade(trade);
        }

    }

    @Retryable(value = CannotAcquireLockException.class,
            maxAttempts = 3,
            stateful = true,
            backoff = @Backoff(delay = 300, multiplier = 3))
    public void saveTrades(List<Trades> trades) {
    try {
          tradeRepository.saveAll(trades)
    } catch (CannotAcquireLockException e) {
              saveTrades(trades);
            }
    }
}

public interface TradeRepository extends JpaRepository<Trade, Integer> {
    @Modifying
    @Query(value = "update trade set stop_date=:new_stop_date where id=:id", nativeQuery = true)
    void expire(@Param("id") int id, @Param("new_stop_date") String newStopDate);
}

我现在的处境是:
不使用stateful(即stateful设置为 false 默认情况下)-重试成功,但在结束时,我看到以下异常: org.springframework.transaction.UnexpectedRollbackException: Transaction silently rolled back because it has been marked as rollback-only 并且多次重试后更新/保存的数据回滚到数据库表中
stateful=true-重试不再发生
我浏览了很多帖子和博客,但都找不到解决问题的方法。这里有人能帮我吗?
编辑:更新了我的问题,添加了try-catch块,这个spring重试不起作用(我知道是因为我添加了一个侦听器) @Retryable 记录 retryContext . 我没看到日志被打印出来。如果出现cannotacquirelockexception,事务也会自动回滚

@Override
    public <T, E extends Throwable> void onError(RetryContext context, RetryCallback<T, E> callback, Throwable throwable) {
        LOGGER.info("Retry Context - {}", context);
    }
krugob8w

krugob8w1#

您正在事务中重试;这是错误的,会产生你所看到的结果;您需要交换它并在重试中执行事务。这就是为什么在不使用stateful时会出现回滚错误。
如果使用有状态重试,则 @Retryable 不属于保留状态;retryable的调用者必须一直调用,直到成功或重试。
编辑
下面是一个使用有状态重试的示例

@Component
class ServiceCaller {

    @Autowired
    Service service;

    public void call() {
        try {
            this.service.process();
        }
        catch (IllegalStateException e) {
            System.out.println("retrying...");
            call();
        }
        catch (RuntimeException e) {
            throw e;
        }
    }

}

@Component
class Service {

    @Autowired
    Retryer retryable;

    @Transactional
    public void process() {
        retryable.invoke();
    }

}

@Component
class Retryer {

    @Retryable(maxAttempts = 3, stateful = true)
    public void invoke() {
        System.out.println("Invoked");
        throw new IllegalStateException("failed");
    }

    @Recover
    public void recover(IllegalStateException e) {
        System.out.println("Retries exhausted");
        throw new RuntimeException(e);
    }

}
Invoked
retrying...
Invoked
retrying...
Invoked
retrying...
Retries exhausted
...
Caused by: java.lang.RuntimeException: java.lang.IllegalStateException: failed
    at com.example.demo.Retryer.recover(So67197577Application.java:84) ~[classes/:na]
...
Caused by: java.lang.IllegalStateException: failed

而且,如果没有 @Recover 方法。。。

Invoked
retrying...
Invoked
retrying...
Invoked
retrying...
...
Caused by: org.springframework.retry.ExhaustedRetryException: Retry exhausted after last attempt with no recovery path; nested exception is java.lang.IllegalStateException: failed

相关问题