Skip to content

Misleading logs in scan mode without retries #5527

Description

@se-meiser

Bug description

When an application uses a dedicated exception as control flow to skip an item, the exception is accepted by the
configured skip policy and the SkipListener is invoked exactly once. The job completes successfully.

The logging differs between the deprecated TaskletStep/FaultTolerantChunkProcessor path and the current
ChunkOrientedStep path:

  • The deprecated path logs the corresponding scan transition at DEBUG without an exception argument. This is
    effectively invisible with normal production logging.

  • ChunkOrientedStep logs the same expected control-flow transition at INFO and attaches a RetryException:

    if (this.faultTolerant && exception instanceof RetryException retryException
            && this.skipPolicy.shouldSkip(retryException.getCause(), -1)) {
        logger.info("Retry exhausted, entering scan mode for next transaction", retryException);
        // ...
    }
  • The rollback path also logs an unconditional ERROR:

    catch (Exception e) {
        logger.error("Rolling back chunk transaction", e);
        status.setRollbackOnly();
        // ...
    }

The wording Retry exhausted is misleading when no .retryable(...) exception is configured. In this case, the
default retry policy performs zero retries. The message means only that the write failed and the skip policy accepted
the exception. The rollback is also an expected part of the skip/scan flow, not an application failure.

Environment

  • Spring Batch: 6.0.5
  • Spring Boot: 4.1.1
  • Java: 17+ (the attached project was run with Java 25)
  • Build tool: Maven 3+
  • Database: none; the reproducer uses ResourcelessJobRepository and
    ResourcelessTransactionManager
  • Logging: Spring Boot starter logging with SLF4J and Logback
  • Operating system: macOS (the behavior is not expected to be OS-specific)

Steps to reproduce

  1. Download and extract the attached ZIP.

  2. Change into the extracted project directory.

  3. Run:

    mvn clean spring-boot:run -Dspring-boot.run.arguments=--spring.main.web-application-type=none
  4. Observe the logs for both taskletJob and chunkOrientedJob.

The application configures a writer that throws DeliberateSkipException for one item, configures that exception as
skippable, and does not configure any retryable exception. The SkipListener logs one application-level skip
message, and both jobs finish with COMPLETED.

The chunkOrientedJob output includes messages equivalent to:

INFO  ... ChunkOrientedStep - Retry exhausted, entering scan mode for next transaction
      org.springframework.core.retry.RetryException: Retry policy for operation 'Retryable write operation' exhausted; aborting execution
ERROR ... ChunkOrientedStep - Rolling back chunk transaction
      org.springframework.core.retry.RetryException: Retry policy for operation 'Retryable write operation' exhausted; aborting execution
INFO  ... ChunkOrientedStep - Rollback complete, scan will execute in next transaction
INFO  ... ChunkOrientedStep - Executing scan in new transaction after rollback

The taskletJob uses the deprecated chunk-builder overload and produces the corresponding scan transition without the
same INFO message and exception stack trace.

Expected behavior

An expected skip/scan transition should not emit misleading INFO and ERROR messages with exception stack traces
when no retry is configured and the job completes successfully. The diagnostic logging should reflect that this is an
accepted skip and expected rollback, rather than an exhausted retry or application failure.

In particular:

  • When no retryable exception is configured, the scan transition should not be described as Retry exhausted.
  • An exception accepted by the skip policy should not produce an unconditional ERROR log merely because the chunk
    transaction is rolled back as part of the expected skip/scan algorithm.
  • This diagnostic logging differs from the legacy FaultTolerantChunkProcessor path and is not described in the
    Spring Batch 6 reference documentation or migration guide.
  • The expected skip/scan transition should either be logged at an appropriate lower level without the internal
    RetryException, or use wording that accurately describes the accepted skip and expected transaction rollback.

The job should continue to invoke the SkipListener once and complete successfully, as it does in this reproducer.

Minimal Complete Reproducible example

The attached ZIP contains a complete Maven Spring Boot application. It contains:

  • pom.xml with spring-boot-starter-batch and Spring Batch 6.0.5 resolved by Spring Boot dependency management.
  • src/main/java/com/example/batch/SkipLoggingReproducerApplication.java with both execution paths.
  • An in-memory, database-free setup using ResourcelessJobRepository.
  • A writer that throws a dedicated exception for one item.
  • A skip policy and SkipListener that demonstrate that the item is skipped successfully.

The project was verified with:

mvn clean verify

Both jobs complete with status COMPLETED; the issue is the misleading and inconsistent logging during the expected
skip/scan transition, not a failed job.

spring-batch-skip-logging-reproducer.zip

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions