com.redis.spring.batch.step.FlushingFaultTolerantChunkProvider Maven / Gradle / Ivy
The newest version!
package com.redis.spring.batch.step;
import org.springframework.batch.core.StepContribution;
import org.springframework.batch.item.Chunk;
import org.springframework.batch.core.step.item.SkipOverflowException;
import org.springframework.batch.core.step.skip.LimitCheckingItemSkipPolicy;
import org.springframework.batch.core.step.skip.NonSkippableReadException;
import org.springframework.batch.core.step.skip.SkipException;
import org.springframework.batch.core.step.skip.SkipListenerFailedException;
import org.springframework.batch.core.step.skip.SkipPolicy;
import org.springframework.batch.core.step.skip.SkipPolicyFailedException;
import org.springframework.batch.item.ItemReader;
import org.springframework.batch.repeat.RepeatOperations;
import org.springframework.classify.BinaryExceptionClassifier;
import org.springframework.classify.Classifier;
/**
* Fault-tolerant implementation of the ChunkProvider interface, that allows for skipping or retry of items that cause
* exceptions, as well as incomplete chunks when timeout is reached.
*/
public class FlushingFaultTolerantChunkProvider extends FlushingChunkProvider {
/**
* Hard limit for number of read skips in the same chunk. Should be sufficiently high that it is only encountered in a
* runaway step where all items are skipped before the chunk can complete (leading to a potential heap memory problem).
*/
public static final int DEFAULT_MAX_SKIPS_ON_READ = 100;
private SkipPolicy skipPolicy = new LimitCheckingItemSkipPolicy();
private Classifier rollbackClassifier = new BinaryExceptionClassifier(true);
private int maxSkipsOnRead = DEFAULT_MAX_SKIPS_ON_READ;
public FlushingFaultTolerantChunkProvider(ItemReader extends I> itemReader, RepeatOperations repeatOperations) {
super(itemReader, repeatOperations);
}
/**
* @param maxSkipsOnRead the maximum number of skips on read
*/
public void setMaxSkipsOnRead(int maxSkipsOnRead) {
this.maxSkipsOnRead = maxSkipsOnRead;
}
/**
* The policy that determines whether exceptions can be skipped on read.
*
* @param skipPolicy instance of {@link SkipPolicy} to be used by FaultTolerantChunkProvider.
*/
public void setSkipPolicy(SkipPolicy skipPolicy) {
this.skipPolicy = skipPolicy;
}
/**
* Classifier to determine whether exceptions have been marked as no-rollback (as opposed to skippable). If encountered they
* are simply ignored, unless also skippable.
*
* @param rollbackClassifier the rollback classifier to set
*/
public void setRollbackClassifier(Classifier rollbackClassifier) {
this.rollbackClassifier = rollbackClassifier;
}
@Override
protected I read(StepContribution contribution, Chunk chunk, long timeout) throws InterruptedException {
while (true) {
try {
return doRead(timeout);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
throw e;
} catch (Exception e) {
handleException(e, contribution, chunk);
}
}
}
private void handleException(Exception e, StepContribution contribution, Chunk chunk) {
if (shouldSkip(skipPolicy, e, contribution.getStepSkipCount())) {
// increment skip count and try again
contribution.incrementReadSkipCount();
chunk.skip(e);
if (chunk.getErrors().size() >= maxSkipsOnRead) {
throw new SkipOverflowException("Too many skips on read");
}
logger.debug("Skipping failed input", e);
} else {
if (Boolean.TRUE.equals(rollbackClassifier.classify(e))) {
throw new NonSkippableReadException("Non-skippable exception during read", e);
}
logger.debug("No-rollback for non-skippable exception (ignored)", e);
}
}
/**
* Convenience method for calling process skip policy.
*
* @param policy the skip policy
* @param e the cause of the skip
* @param skipCount the current skip count
*/
private boolean shouldSkip(SkipPolicy policy, Throwable e, long skipCount) {
try {
return policy.shouldSkip(e, skipCount);
} catch (SkipException ex) {
throw ex;
} catch (RuntimeException ex) {
throw new SkipPolicyFailedException("Fatal exception in SkipPolicy.", ex, e);
}
}
@Override
public void postProcess(StepContribution contribution, Chunk chunk) {
for (Exception e : chunk.getErrors()) {
try {
getListener().onSkipInRead(e);
} catch (RuntimeException ex) {
throw new SkipListenerFailedException("Fatal exception in SkipListener.", ex, e);
}
}
}
}