Improve failure-safety of Cursor.
Any failures during opening the cursor or scanning now close the cursor and release associated resources. Closes #2414
This commit is contained in:
@@ -21,13 +21,14 @@ import java.util.NoSuchElementException;
|
||||
|
||||
import org.springframework.dao.InvalidDataAccessApiUsageException;
|
||||
import org.springframework.lang.Nullable;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.CollectionUtils;
|
||||
|
||||
/**
|
||||
* Redis client agnostic {@link Cursor} implementation continuously loading additional results from Redis server until
|
||||
* reaching its starting point {@code zero}. <br />
|
||||
* <strong>Note:</strong> Please note that the {@link ScanCursor} has to be initialized ({@link #open()} prior to usage.
|
||||
* Any failures during scanning will {@link #close() close} the cursor and release any associated resources such as
|
||||
* connections.
|
||||
*
|
||||
* @author Christoph Strobl
|
||||
* @author Thomas Darimont
|
||||
@@ -85,8 +86,16 @@ public abstract class ScanCursor<T> implements Cursor<T> {
|
||||
|
||||
private void scan(long cursorId) {
|
||||
|
||||
ScanIteration<T> result = doScan(cursorId, this.scanOptions);
|
||||
processScanResult(result);
|
||||
try {
|
||||
processScanResult(doScan(cursorId, this.scanOptions));
|
||||
} catch (RuntimeException e) {
|
||||
try {
|
||||
close();
|
||||
} catch (RuntimeException nested) {
|
||||
e.addSuppressed(nested);
|
||||
}
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -233,6 +233,21 @@ class ScanCursorUnitTests {
|
||||
assertThat(cursor.isClosed()).isTrue();
|
||||
}
|
||||
|
||||
@Test // GH-2414
|
||||
void shouldCloseCursorOnScanFailure() {
|
||||
|
||||
KeyBoundCursor<String> cursor = new KeyBoundCursor<String>("foo".getBytes(), 0, null) {
|
||||
@Override
|
||||
protected ScanIteration<String> doScan(byte[] key, long cursorId, ScanOptions options) {
|
||||
throw new IllegalStateException();
|
||||
}
|
||||
};
|
||||
|
||||
assertThatIllegalStateException().isThrownBy(cursor::open);
|
||||
assertThat(cursor.isOpen()).isFalse();
|
||||
assertThat(cursor.isClosed()).isTrue();
|
||||
}
|
||||
|
||||
private CapturingCursorDummy initCursor(Queue<ScanIteration<String>> values) {
|
||||
CapturingCursorDummy cursor = new CapturingCursorDummy(values);
|
||||
cursor.open();
|
||||
|
||||
Reference in New Issue
Block a user