【问题标题】:Retry not working when Spring Batch managed transaction rolls backSpring Batch托管事务回滚时重试不起作用
【发布时间】:2020-08-06 01:20:11
【问题描述】:

在我的 Spring Batch 作业的其中一个步骤中,我正在尝试对其进行配置,以便当 ObjectOptimisticLockingFailureException 发生时,可以重试该步骤,并希望重试能够奏效。

  @Bean
  public Step myStep(StaxEventItemReader<Response> staxEventResponseReader,
      ItemWriter<Response> itemWriter,
      ItemProcessor<? super Response, ? extends Response> responseProcessor) {
    return stepBuilderFactory.get("myStep")
        .<Response, Response>chunk(1)
        .reader(staxEventResponseReader)
        .processor(responseProcessor)
        .writer(itemWriter)
        //.faultTolerant().retryLimit(3).retry(Exception.class)
        .build();
  }

该步骤的 writer 逻辑非常简单:它尝试从数据库中读取一行,一旦找到该行,它就会更新它。我能够通过在 find 方法之后设置断点来重现ObjectOptimisticLockingFailureException,手动碰撞数据库中行的version 列并提交它,然后继续。

但是,在我的步骤中取消注释重试定义后,没有尝试重试。经过一些调试,似乎 Spring 重试逻辑在块的事务中;但由于 ObjectOptimisticLockingFailureException 不是由我在编写器中的代码抛出的,而是由 Spring 的块事务提交逻辑抛出的,因此根本没有尝试重试。

Chunk Transaction Begin
    Begin Retry loop in FaultTolerantChunkProcessor.write()
        Writer logic in my Step
    End Retry loop
Chunk Transaction Commit - Throws ObjectOptimisticLockingFailureException

当我尝试在我的 writer 中显式抛出 ObjectOptimisticLockingFailureException 时,重试逻辑按预期完美运行。我的问题是:

  1. 如果在步骤中我的编写器代码没有抛出异常,但在 Spring Batch 提交块事务时,如何使重试逻辑工作?
  2. 另一个奇怪的行为是,当我通过碰撞数据库中的版本列手动导致ObjectOptimisticLockingFailureException 时,在步骤中注释了重试定义,步骤的最终状态是预期的失败。但是在未注释重试定义的情况下,该步骤的最终状态是 COMPLETE。这是为什么呢?

【问题讨论】:

    标签: java spring-boot spring-batch spring-retry


    【解决方案1】:
    1. 如果在步骤中我的编写器代码没有抛出异常,但在 Spring Batch 提交块事务时,如何使重试逻辑工作?

    这里有一个未解决的问题:https://github.com/spring-projects/spring-batch/issues/1826。解决方法是(尝试预测并)抛出任何在编写器提交时可能发生的异常。这是您已经尝试过的方法,并确认当您说 When I tried to explicitly throw ObjectOptimisticLockingFailureException in my writer, the retry logic worked perfectly as expected 时有效。

    1. 另一个奇怪的行为是,当我通过碰撞数据库中的版本列手动导致 ObjectOptimisticLockingFailureException 时,在步骤中注释了重试定义时,步骤的最终状态为 FAILED,这是预期的。但是在未注释重试定义的情况下,该步骤的最终状态是 COMPLETE。这是为什么呢?

    这与上一个问题有关,但由另一个问题引起:https://github.com/spring-projects/spring-batch/issues/1189。也就是说,可以在调试会话期间使用 version 字段来了解事情是如何工作的,但我建议更改代码中的 version 列。此列的 Spring Batch relies 重度采用乐观锁定策略,在用户代码中不应更改此列的值,否则可能会发生意外行为。

    【讨论】:

    • 感谢@Mahmoud Ben Hassine!解决方法奏效了。我需要做的就是在更新后调用entityManager.flush();(预测你所说的异常),ObjectOptimisticLockingFailureException 将被直接扔到这一行。关于手动 version 字段更改,这只是重现异常的更快方法。我曾经在另一个应用程序中设置一个断点,该应用程序可以通过其他更新触及同一行并让该事务首先完成。感谢您提醒我们永远不要直接在代码中使用 version 字段。
    • 只想提一下,通过解决方法,我不再看到我的第二个问题的行为。
    • 不客气,很高兴为您提供帮助。 All I need to do is to call entityManager.flush()。对于 JPA,是的,这就是这里建议的解决方法:github.com/spring-projects/spring-batch/issues/…。事实上,它可以解决这两个问题。
    猜你喜欢
    • 1970-01-01
    • 2013-04-19
    • 1970-01-01
    • 1970-01-01
    • 2018-05-24
    • 2018-12-19
    • 1970-01-01
    • 2011-01-17
    • 1970-01-01
    相关资源
    最近更新 更多