Skip to content

Commit f4f59e5

Browse files
Caideyipi陈哲涵
andauthored
[To dev/1.3] Load: preserve pending tablets when parser fails (#18487) (#18509) (#18604)
* [To dev/1.3] Load: preserve pending tablets when parser fails (#18487) (#18509) * Fix missing historical source test helper --------- Co-authored-by: 陈哲涵 <zhehan.chen@timecho.com>
1 parent f4832ee commit f4f59e5

3 files changed

Lines changed: 115 additions & 3 deletions

File tree

‎iotdb-core/datanode/src/main/java/org/apache/iotdb/db/storageengine/load/converter/LoadTreeStatementDataTypeConvertExecutionVisitor.java‎

Lines changed: 47 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@
4343
import java.util.ArrayList;
4444
import java.util.List;
4545
import java.util.Optional;
46+
import java.util.function.Function;
4647
import java.util.stream.Collectors;
4748

4849
import static org.apache.iotdb.db.pipe.resource.memory.PipeMemoryWeightUtil.calculateTabletSizeInBytes;
@@ -58,6 +59,7 @@ public class LoadTreeStatementDataTypeConvertExecutionVisitor
5859
.getLoadTsFileTabletConversionBatchMemorySizeInBytes();
5960

6061
private final StatementExecutor statementExecutor;
62+
private final Function<File, LoadTreeTsFileTabletIterator> tabletIteratorFactory;
6163

6264
@FunctionalInterface
6365
public interface StatementExecutor {
@@ -66,7 +68,14 @@ public interface StatementExecutor {
6668

6769
public LoadTreeStatementDataTypeConvertExecutionVisitor(
6870
final StatementExecutor statementExecutor) {
71+
this(statementExecutor, file -> new LoadTreeTsFileTabletIterator(file, true));
72+
}
73+
74+
LoadTreeStatementDataTypeConvertExecutionVisitor(
75+
final StatementExecutor statementExecutor,
76+
final Function<File, LoadTreeTsFileTabletIterator> tabletIteratorFactory) {
6977
this.statementExecutor = statementExecutor;
78+
this.tabletIteratorFactory = tabletIteratorFactory;
7079
}
7180

7281
@Override
@@ -89,7 +98,7 @@ public Optional<TSStatus> visitLoadFile(
8998
try {
9099
for (final File file : loadTsFileStatement.getTsFiles()) {
91100
try (final LoadTreeTsFileTabletIterator tabletIterator =
92-
new LoadTreeTsFileTabletIterator(file, true)) {
101+
tabletIteratorFactory.apply(file)) {
93102
for (final Pair<Tablet, Boolean> tabletWithIsAligned : tabletIterator) {
94103
final PipeTransferTabletRawReq tabletRawReq =
95104
PipeTransferTabletRawReq.toTPipeTransferRawReq(
@@ -123,9 +132,29 @@ public Optional<TSStatus> visitLoadFile(
123132
} catch (final Exception e) {
124133
LOGGER.warn(
125134
"Failed to convert data type for LoadTsFileStatement: {}.", loadTsFileStatement, e);
126-
return Optional.of(
135+
final TSStatus status =
127136
loadTsFileStatement.accept(
128-
LoadTsFileDataTypeConverter.STATEMENT_EXCEPTION_VISITOR, e));
137+
LoadTsFileDataTypeConverter.STATEMENT_EXCEPTION_VISITOR, e);
138+
139+
// A parser can fail after producing tablets that are still waiting for the next batch
140+
// boundary. Submit those tablets before reporting the parser error so the error does not
141+
// discard successfully converted data.
142+
if (!isRetryableConversionException(e) && !tabletRawReqs.isEmpty()) {
143+
final TSStatus flushStatus =
144+
executeInsertMultiTabletsWithRetry(
145+
tabletRawReqs, loadTsFileStatement.isConvertOnTypeMismatch());
146+
147+
for (final long memoryCost : tabletRawReqSizes) {
148+
block.reduceMemoryUsage(memoryCost);
149+
}
150+
tabletRawReqs.clear();
151+
tabletRawReqSizes.clear();
152+
153+
if (!handleTSStatus(flushStatus, loadTsFileStatement)) {
154+
return Optional.of(flushStatus);
155+
}
156+
}
157+
return Optional.of(status);
129158
}
130159
}
131160

@@ -179,6 +208,21 @@ public Optional<TSStatus> visitLoadFile(
179208
return Optional.of(new TSStatus(TSStatusCode.SUCCESS_STATUS.getStatusCode()));
180209
}
181210

211+
private static boolean isRetryableConversionException(final Throwable throwable) {
212+
if (LoadTsFileDataTypeConverter.isMemoryPressureException(throwable)) {
213+
return true;
214+
}
215+
216+
Throwable current = throwable;
217+
while (current != null) {
218+
if (current instanceof InterruptedException) {
219+
return true;
220+
}
221+
current = current.getCause();
222+
}
223+
return false;
224+
}
225+
182226
private TSStatus executeInsertMultiTabletsWithRetry(
183227
final List<PipeTransferTabletRawReq> tabletRawReqs, boolean isConvertOnTypeMismatch) {
184228
final InsertMultiTabletsStatement batchStatement = new InsertMultiTabletsStatement();

‎iotdb-core/datanode/src/test/java/org/apache/iotdb/db/pipe/source/dataregion/historical/PipeHistoricalDataRegionTsFileSourceTest.java‎

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@
4040
import org.junit.Test;
4141

4242
import java.io.File;
43+
import java.io.IOException;
4344
import java.lang.reflect.Field;
4445
import java.lang.reflect.Method;
4546
import java.nio.file.Files;
@@ -198,6 +199,13 @@ private static void assertMayTsFileContainUnprocessedData(
198199
source, createClosedTsFileResource(tempDir, fileName, resourceProgressIndex)));
199200
}
200201

202+
private static TsFileResource createTsFileResource(final File tempDir, final String fileName)
203+
throws IOException {
204+
final File file = new File(tempDir, fileName);
205+
Assert.assertTrue(file.createNewFile());
206+
return new TsFileResource(file);
207+
}
208+
201209
private static TsFileResource createClosedTsFileResource(
202210
final File tempDir, final String fileName, final ProgressIndex progressIndex)
203211
throws Exception {

‎iotdb-core/datanode/src/test/java/org/apache/iotdb/db/storageengine/load/converter/LoadTreeStatementDataTypeConvertExecutionVisitorTest.java‎

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@
3838
import org.apache.tsfile.read.TsFileSequenceReader;
3939
import org.apache.tsfile.read.common.Path;
4040
import org.apache.tsfile.utils.BitMap;
41+
import org.apache.tsfile.utils.Pair;
4142
import org.apache.tsfile.write.TsFileWriter;
4243
import org.apache.tsfile.write.record.Tablet;
4344
import org.apache.tsfile.write.schema.MeasurementSchema;
@@ -110,6 +111,33 @@ public void testFallbackToQueryForRemainingDevicesWhenScanParserHitsCorruption()
110111
Assert.assertEquals(loadedPointCountBeforeCorruption, loadedPointCountAfterFallback);
111112
}
112113

114+
@Test
115+
public void testFlushesPendingTabletsWhenIteratorFails() throws Exception {
116+
tsFile = File.createTempFile("load-tree-pending-tablet", ".tsfile");
117+
final List<MeasurementSchema> schemaList =
118+
Arrays.asList(new MeasurementSchema("s0", TSDataType.INT64, TSEncoding.PLAIN));
119+
final Tablet tablet = new Tablet(DEVICE_0, schemaList, 1);
120+
tablet.addTimestamp(0, 1);
121+
tablet.addValue("s0", 0, 1L);
122+
tablet.rowSize = 1;
123+
124+
final Map<String, Integer> pointCountByDevice = new HashMap<>();
125+
final LoadTreeStatementDataTypeConvertExecutionVisitor visitor =
126+
new LoadTreeStatementDataTypeConvertExecutionVisitor(
127+
statement -> {
128+
collectLoadedPoints(statement, pointCountByDevice);
129+
return new TSStatus(TSStatusCode.SUCCESS_STATUS.getStatusCode());
130+
},
131+
file -> new ThrowingTabletIterator(file, tablet));
132+
133+
final Optional<TSStatus> status =
134+
visitor.visitLoadFile(LoadTsFileStatement.createUnchecked(tsFile.getAbsolutePath()), null);
135+
136+
Assert.assertTrue(status.isPresent());
137+
Assert.assertEquals(TSStatusCode.LOAD_FILE_ERROR.getStatusCode(), status.get().getCode());
138+
Assert.assertEquals(1, pointCountByDevice.getOrDefault(DEVICE_0, 0).intValue());
139+
}
140+
113141
@Test
114142
public void testFallbackToQueryWhenFirstNonAlignedDeviceIsCorrupted() throws Exception {
115143
tsFile = new File("load-tree-query-fallback-corrupted-first-non-aligned-device.tsfile");
@@ -393,4 +421,36 @@ private static void setPipeMemoryManagementEnabled(final boolean enabled) {
393421
CommonDescriptor.getInstance().getConfig().setPipeMemoryManagementEnabled(enabled);
394422
}
395423
}
424+
425+
private static class ThrowingTabletIterator extends LoadTreeTsFileTabletIterator {
426+
private final Pair<Tablet, Boolean> tabletWithIsAligned;
427+
private boolean tabletAvailable = true;
428+
429+
private ThrowingTabletIterator(final File file, final Tablet tablet) {
430+
super(file, true);
431+
tabletWithIsAligned = new Pair<>(tablet, false);
432+
}
433+
434+
@Override
435+
public boolean hasNext() {
436+
if (tabletAvailable) {
437+
return true;
438+
}
439+
throw new IllegalStateException("synthetic parser failure");
440+
}
441+
442+
@Override
443+
public Pair<Tablet, Boolean> next() {
444+
if (!tabletAvailable) {
445+
throw new IllegalStateException("synthetic parser failure");
446+
}
447+
tabletAvailable = false;
448+
return tabletWithIsAligned;
449+
}
450+
451+
@Override
452+
public void close() {
453+
// No parser resources are allocated by this test iterator.
454+
}
455+
}
396456
}

0 commit comments

Comments
 (0)