Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,7 @@
* @author Mahmoud Ben Hassine
* @author Andrey Litvitski
* @author xeounxzxu
* @author Minchul Son
* @since 6.0
*/
public class ChunkOrientedStep<I, O> extends AbstractStep {
Expand Down Expand Up @@ -375,6 +376,11 @@ protected void doExecute(StepExecution stepExecution) throws Exception {

// Skip update during rollback to avoid OptimisticLockingFailureException
if (transactionStatus.isRollbackOnly()) {
// Explicitly mark as locally rollback-only to prevent
// UnexpectedRollbackException when the transaction manager
// (eg JpaTransactionManager) has marked it as globally rollback-only
// (eg after a JPA flush failure) but not locally rollback-only.
transactionStatus.setRollbackOnly();
chunkTransactionEvent.transactionStatus = BatchMetrics.STATUS_ROLLED_BACK;
chunkTransactionEvent.commit();
return;
Expand Down Expand Up @@ -408,17 +414,26 @@ private void processChunkConcurrently(TransactionStatus status, StepContribution
try {
if (tracker.isScanMode()) {
logger.info("Executing scan in new transaction after rollback");
Chunk<O> pendingChunk = tracker.getPendingChunk();
if (pendingChunk != null) {
O item = tracker.pollNextScanItem();
if (item != null) {
Chunk<O> singleItemChunk = new Chunk<>(item);
ChunkScanEvent chunkScanEvent = new ChunkScanEvent(stepExecution.getStepName(),
stepExecution.getId());
chunkScanEvent.begin();
scan(pendingChunk, contribution);
compositeChunkListener.beforeChunk(singleItemChunk);
scan(singleItemChunk, contribution);
if (!status.isRollbackOnly()) {
compositeChunkListener.afterChunk(singleItemChunk);
}
chunkScanEvent.skipCount = contribution.getSkipCount();
chunkScanEvent.commit();
}
if (!tracker.hasPendingScanItems()) {
logger.info("Chunk scan completed");
tracker.exitScanMode();
stepExecution.incrementCommitCount();
if (!status.isRollbackOnly()) {
stepExecution.incrementCommitCount();
}
}
return;
}
Expand Down Expand Up @@ -486,19 +501,26 @@ private void processChunkSequentially(TransactionStatus status, StepContribution
try {
if (tracker.isScanMode()) {
logger.info("Executing scan in new transaction after rollback");
Chunk<O> pendingChunk = tracker.getPendingChunk();
if (pendingChunk != null) {
O item = tracker.pollNextScanItem();
if (item != null) {
Chunk<O> singleItemChunk = new Chunk<>(item);
ChunkScanEvent chunkScanEvent = new ChunkScanEvent(stepExecution.getStepName(),
stepExecution.getId());
chunkScanEvent.begin();
compositeChunkListener.beforeChunk(new Chunk<>());
scan(pendingChunk, contribution);
compositeChunkListener.afterChunk(pendingChunk);
compositeChunkListener.beforeChunk(singleItemChunk);
scan(singleItemChunk, contribution);
if (!status.isRollbackOnly()) {
compositeChunkListener.afterChunk(singleItemChunk);
}
chunkScanEvent.skipCount = contribution.getSkipCount();
chunkScanEvent.commit();
}
if (!tracker.hasPendingScanItems()) {
logger.info("Chunk scan completed");
tracker.exitScanMode();
stepExecution.incrementCommitCount();
if (!status.isRollbackOnly()) {
stepExecution.incrementCommitCount();
}
}
return;
}
Expand Down Expand Up @@ -838,12 +860,12 @@ static <T> ChunkTracker<T> create() {

private boolean scanMode;

@Nullable private Chunk<O> pendingChunk;
@Nullable private LinkedList<O> pendingScanItems;

void init() {
this.moreItems = true;
this.scanMode = false;
this.pendingChunk = null;
this.pendingScanItems = null;
}

void reset() {
Expand All @@ -856,20 +878,24 @@ boolean moreItems() {

void enterScanMode(Chunk<O> chunk) {
this.scanMode = true;
this.pendingChunk = new Chunk<>(chunk.getItems());
this.pendingScanItems = new LinkedList<>(chunk.getItems());
}

boolean isScanMode() {
return this.scanMode;
}

@Nullable Chunk<O> getPendingChunk() {
return this.pendingChunk;
@Nullable O pollNextScanItem() {
return (this.pendingScanItems != null) ? this.pendingScanItems.poll() : null;
}

boolean hasPendingScanItems() {
return this.pendingScanItems != null && !this.pendingScanItems.isEmpty();
}

void exitScanMode() {
this.scanMode = false;
this.pendingChunk = null;
this.pendingScanItems = null;
}

}
Expand Down
Loading