mirror of
https://github.com/apache/druid.git
synced 2025-02-17 07:25:02 +00:00
Do not eagerly close inner iterators in CloseableIterator#flatMap (#14986)
This commit is contained in:
parent
0fc5d5405a
commit
39d95955f5
@ -1033,14 +1033,15 @@ public class S3InputSourceTest extends InitializedNullHandlingTest
|
|||||||
new CsvInputFormat(ImmutableList.of("time", "dim1", "dim2"), "|", false, null, 0),
|
new CsvInputFormat(ImmutableList.of("time", "dim1", "dim2"), "|", false, null, 0),
|
||||||
temporaryFolder.newFolder()
|
temporaryFolder.newFolder()
|
||||||
);
|
);
|
||||||
|
try (CloseableIterator<InputRow> readerIterator = reader.read()) {
|
||||||
final IllegalStateException e = Assert.assertThrows(IllegalStateException.class, reader::read);
|
final IllegalStateException e = Assert.assertThrows(IllegalStateException.class, readerIterator::hasNext);
|
||||||
MatcherAssert.assertThat(e.getCause(), CoreMatchers.instanceOf(IOException.class));
|
MatcherAssert.assertThat(e.getCause(), CoreMatchers.instanceOf(IOException.class));
|
||||||
MatcherAssert.assertThat(e.getCause().getCause(), CoreMatchers.instanceOf(SdkClientException.class));
|
MatcherAssert.assertThat(e.getCause().getCause(), CoreMatchers.instanceOf(SdkClientException.class));
|
||||||
MatcherAssert.assertThat(
|
MatcherAssert.assertThat(
|
||||||
e.getCause().getCause().getMessage(),
|
e.getCause().getCause().getMessage(),
|
||||||
CoreMatchers.startsWith("Data read has a different length than the expected")
|
CoreMatchers.startsWith("Data read has a different length than the expected")
|
||||||
);
|
);
|
||||||
|
}
|
||||||
|
|
||||||
EasyMock.verify(S3_CLIENT);
|
EasyMock.verify(S3_CLIENT);
|
||||||
}
|
}
|
||||||
|
@ -19,7 +19,6 @@
|
|||||||
|
|
||||||
package org.apache.druid.java.util.common.parsers;
|
package org.apache.druid.java.util.common.parsers;
|
||||||
|
|
||||||
import javax.annotation.Nullable;
|
|
||||||
import java.io.Closeable;
|
import java.io.Closeable;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.UncheckedIOException;
|
import java.io.UncheckedIOException;
|
||||||
@ -62,37 +61,37 @@ public interface CloseableIterator<T> extends Iterator<T>, Closeable
|
|||||||
|
|
||||||
default <R> CloseableIterator<R> flatMap(Function<T, CloseableIterator<R>> function)
|
default <R> CloseableIterator<R> flatMap(Function<T, CloseableIterator<R>> function)
|
||||||
{
|
{
|
||||||
final CloseableIterator<T> delegate = this;
|
final CloseableIterator<T> outerIterator = this;
|
||||||
|
|
||||||
return new CloseableIterator<R>()
|
return new CloseableIterator<R>()
|
||||||
{
|
{
|
||||||
CloseableIterator<R> iterator = findNextIteratorIfNecessary();
|
CloseableIterator<R> currInnerIterator = null;
|
||||||
|
|
||||||
@Nullable
|
private void findNextIteratorIfNecessary()
|
||||||
private CloseableIterator<R> findNextIteratorIfNecessary()
|
|
||||||
{
|
{
|
||||||
while ((iterator == null || !iterator.hasNext()) && delegate.hasNext()) {
|
while ((currInnerIterator == null || !currInnerIterator.hasNext()) && outerIterator.hasNext()) {
|
||||||
if (iterator != null) {
|
if (currInnerIterator != null) {
|
||||||
try {
|
try {
|
||||||
iterator.close();
|
currInnerIterator.close();
|
||||||
iterator = null;
|
currInnerIterator = null;
|
||||||
}
|
}
|
||||||
catch (IOException e) {
|
catch (IOException e) {
|
||||||
throw new UncheckedIOException(e);
|
throw new UncheckedIOException(e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
iterator = function.apply(delegate.next());
|
currInnerIterator = function.apply(outerIterator.next());
|
||||||
if (iterator.hasNext()) {
|
if (currInnerIterator.hasNext()) {
|
||||||
return iterator;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return null;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean hasNext()
|
public boolean hasNext()
|
||||||
{
|
{
|
||||||
return iterator != null && iterator.hasNext();
|
// closes the current iterator if it is finished, and opens a new non-empty iterator if possible
|
||||||
|
findNextIteratorIfNecessary();
|
||||||
|
return currInnerIterator != null && currInnerIterator.hasNext();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -101,21 +100,16 @@ public interface CloseableIterator<T> extends Iterator<T>, Closeable
|
|||||||
if (!hasNext()) {
|
if (!hasNext()) {
|
||||||
throw new NoSuchElementException();
|
throw new NoSuchElementException();
|
||||||
}
|
}
|
||||||
try {
|
return currInnerIterator.next();
|
||||||
return iterator.next();
|
|
||||||
}
|
|
||||||
finally {
|
|
||||||
findNextIteratorIfNecessary();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void close() throws IOException
|
public void close() throws IOException
|
||||||
{
|
{
|
||||||
delegate.close();
|
outerIterator.close();
|
||||||
if (iterator != null) {
|
if (currInnerIterator != null) {
|
||||||
iterator.close();
|
currInnerIterator.close();
|
||||||
iterator = null;
|
currInnerIterator = null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@ -137,9 +137,11 @@ public class InputEntityIteratingReaderTest extends InitializedNullHandlingTest
|
|||||||
).iterator(),
|
).iterator(),
|
||||||
temporaryFolder.newFolder()
|
temporaryFolder.newFolder()
|
||||||
);
|
);
|
||||||
String expectedMessage = "Error occurred while trying to read uri: testscheme://some/path";
|
|
||||||
Exception exception = Assert.assertThrows(RuntimeException.class, firehose::read);
|
|
||||||
|
|
||||||
|
try (CloseableIterator<InputRow> readIterator = firehose.read()) {
|
||||||
|
String expectedMessage = "Error occurred while trying to read uri: testscheme://some/path";
|
||||||
|
Exception exception = Assert.assertThrows(RuntimeException.class, readIterator::hasNext);
|
||||||
Assert.assertTrue(exception.getMessage().contains(expectedMessage));
|
Assert.assertTrue(exception.getMessage().contains(expectedMessage));
|
||||||
}
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -120,6 +120,47 @@ public class CloseableIteratorTest
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testFlatMapInnerClose() throws IOException
|
||||||
|
{
|
||||||
|
List<CloseTrackingCloseableIterator<Integer>> innerIterators = new ArrayList<>();
|
||||||
|
// the nested iterators is : [ [], [0], [0, 1] ]
|
||||||
|
try (final CloseTrackingCloseableIterator<Integer> actual = new CloseTrackingCloseableIterator<>(
|
||||||
|
generateTestIterator(3)
|
||||||
|
.flatMap(list -> {
|
||||||
|
CloseTrackingCloseableIterator<Integer> inner =
|
||||||
|
new CloseTrackingCloseableIterator<>(CloseableIterators.withEmptyBaggage(list.iterator()));
|
||||||
|
innerIterators.add(inner);
|
||||||
|
return inner;
|
||||||
|
})
|
||||||
|
)) {
|
||||||
|
final Iterator<Integer> expected = IntStream
|
||||||
|
.range(0, 3)
|
||||||
|
.flatMap(i -> IntStream.range(0, i))
|
||||||
|
.iterator();
|
||||||
|
|
||||||
|
int iterCount = 0, innerIteratorIdx = 0;
|
||||||
|
while (actual.hasNext()) {
|
||||||
|
iterCount++;
|
||||||
|
if (iterCount == 1) {
|
||||||
|
Assert.assertEquals(2, innerIterators.size()); //empty iterator and single element iterator
|
||||||
|
innerIteratorIdx++;
|
||||||
|
} else if (iterCount == 2) {
|
||||||
|
Assert.assertEquals(3, innerIterators.size()); //empty iterator + single element iterator + double element iterator
|
||||||
|
innerIteratorIdx++;
|
||||||
|
}
|
||||||
|
Assert.assertEquals(expected.next(), actual.next()); // assert expected value to the iterator's value
|
||||||
|
for (int i = 0; i < innerIteratorIdx; i++) {
|
||||||
|
Assert.assertEquals(1, innerIterators.get(i).closeCount); // expect all previous iterators to be closed
|
||||||
|
}
|
||||||
|
// never expect the current iterator to be closed, even after doing the last next call on it
|
||||||
|
Assert.assertEquals(0, innerIterators.get(innerIteratorIdx).closeCount);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// check the last inner iterator is closed
|
||||||
|
Assert.assertEquals(1, innerIterators.get(2).closeCount);
|
||||||
|
}
|
||||||
|
|
||||||
private static CloseableIterator<List<Integer>> generateTestIterator(int numIterates)
|
private static CloseableIterator<List<Integer>> generateTestIterator(int numIterates)
|
||||||
{
|
{
|
||||||
return new CloseableIterator<List<Integer>>()
|
return new CloseableIterator<List<Integer>>()
|
||||||
|
Loading…
x
Reference in New Issue
Block a user