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:
Mark Paluch
2022-09-30 11:45:59 +02:00
parent 94f48ff2e0
commit 01aa6c4d31
2 changed files with 27 additions and 3 deletions

View File

@@ -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;
}
}
/**

View File

@@ -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();