Retryable processing errors can be attempted again, but exhausting the configured limit leaves the job failed.
Spring Batch retry exhaustion: a temporary error needs a finite stop
Prove recovery and exhaustion separately
In one checked run, receipt ID 3 fails processing once with TemporaryReceiptException, succeeds on a later attempt, and the job finishes with three accepted rows after one invalid row was skipped. Another run forces the temporary exception on every attempt. Its job status is FAILED and its target remains empty because the two-row chunk never commits.
Bound work at the right layer
The local fixture uses retryLimit(2) for the processor exception. A production import also needs a wall-clock deadline and backoff suited to its database, because a service-wide outage can make a fleet of workers retry at once. Client retry and Batch retry should share an attempt budget when a processor calls a remote service.
Preserve a stable write decision
A retried processor or writer can run more than once; avoid irreversible side effects inside either. The earlier writer lesson uses a source key to tolerate replay. This test uses in-memory H2 and a processor exception; it does not prove deadlock recovery against a production database.
Checked excerpt
.faultTolerant()
.retry(TemporaryReceiptException.class).retryLimit(2)
.skip(InvalidReceiptException.class).skipLimit(1)Cost and verification
Every extra attempt repeats processing and can extend lock time. Bound both attempts and elapsed time before retrying shared infrastructure. The source kit checks the stated local behavior.
Common Mistakes
- Do not retry a deterministic negative-unit input.
- Do not assume a retryable classification means the fault will eventually clear.
- Do not put a non-idempotent remote write inside retryable processing.
Read next
Spring Batch skip limits: the next invalid receipt fails the step, Spring Batch writers: use a stable source key when a chunk is replayed, Spring Cloud client retries: distinguish a safe read from an uncertain write, Spring Batch job identity: a manifest ID defines restart versus a new run.
