How is the skipping implemented in Spring Batch?

Peter Wippermann picture Peter Wippermann · May 15, 2013 · Viewed 9.7k times · Source

I was wondering how I could determine in my ItemWriter, whether Spring Batch was currently in chunk-processing-mode or in the fallback single-item-processing-mode. In the first place I didn't find the information how this fallback mechanism is implemented anyway.

Even if I haven't found the solution to my actual problem yet, I'd like to share my knowledge about the fallback mechanism with you.

Feel free to add answers with additional information if I missed anything ;-)

Answer

Peter Wippermann picture Peter Wippermann · May 15, 2013

The implementation of the skip mechanism can be found in the FaultTolerantChunkProcessor and in the RetryTemplate.

Let's assume you configured skippable exceptions but no retryable exceptions. And there is a failing item in your current chunk causing an exception.

Now, first of all the whole chunk shall be written. In the processor's write() method you can see, that a RetryTemplate is called. It also gets two references to a RetryCallback and a RecoveryCallback.

Switch over to the RetryTemplate. Find the following method:

protected <T> T doExecute(RetryCallback<T> retryCallback, RecoveryCallback<T> recoveryCallback, RetryState state)

There you can see that the RetryTemplate is retried as long as it's not exhausted (i.e. exactly once in our configuration). Such a retry will be caused by a retryable exception. Non-retryable exceptions will immediately abort the retry mechanism here.

After the retries are exhausted or aborted, the RecoveryCallback will be called:

e = handleRetryExhausted(recoveryCallback, context, state);

That's where the single-item-processing mode will kick-in now!

The RecoveryCallback (which was defined in the processor's write() method!) will put a lock on the input chunk (inputs.setBusy(true)) and run its scan() method. There you can see, that a single item is taken from the chunk:

List<O> items = Collections.singletonList(outputIterator.next());

If this single item can be processed by the ItemWriter correctly, than the chunk will be finished and the ChunkOrientedTasklet will run another chunk (for the next single items). This will cause a regular call to the RetryCallback, but since the chunk has been locked by the RecoveryTemplate, the scan() method will be called immediately:

if (!inputs.isBusy()) {
    // ...
}
else {
    scan(contribution, inputs, outputs, chunkMonitor);
}

So another single item will be processed and this is repeated, until the original chunk has been processed item-by-item:

if (outputs.isEmpty()) {
    inputs.setBusy(false);

That's it. I hope you found this helpful. And I even more hope that you could find this easily via a search engine and didn't waste too much time, finding this out by yourself. ;-)