Skip to content

Commit eb41f56

Browse files
authored
[to dev/1.3] Fix compaction writer size checkpointing
1 parent 96d55ed commit eb41f56

8 files changed

Lines changed: 331 additions & 20 deletions

File tree

iotdb-core/datanode/src/main/java/org/apache/iotdb/db/storageengine/dataregion/compaction/execute/utils/writer/AbstractCompactionWriter.java

Lines changed: 153 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -22,12 +22,15 @@
2222
import org.apache.iotdb.commons.conf.IoTDBConstant;
2323
import org.apache.iotdb.db.conf.IoTDBDescriptor;
2424
import org.apache.iotdb.db.storageengine.dataregion.compaction.execute.exception.CompactionLastTimeCheckFailedException;
25+
import org.apache.iotdb.db.storageengine.dataregion.compaction.execute.utils.executor.batch.utils.FollowingBatchCompactionAlignedChunkWriter;
2526
import org.apache.iotdb.db.storageengine.dataregion.compaction.execute.utils.executor.fast.element.AlignedPageElement;
2627
import org.apache.iotdb.db.storageengine.dataregion.compaction.execute.utils.executor.fast.element.ChunkMetadataElement;
2728
import org.apache.iotdb.db.storageengine.dataregion.compaction.execute.utils.writer.flushcontroller.AbstractCompactionFlushController;
2829
import org.apache.iotdb.db.storageengine.dataregion.compaction.io.CompactionTsFileWriter;
2930
import org.apache.iotdb.db.storageengine.dataregion.modification.Deletion;
3031

32+
import org.apache.tsfile.block.column.Column;
33+
import org.apache.tsfile.enums.TSDataType;
3134
import org.apache.tsfile.exception.write.PageException;
3235
import org.apache.tsfile.file.header.PageHeader;
3336
import org.apache.tsfile.file.metadata.ChunkMetadata;
@@ -63,6 +66,13 @@ public abstract class AbstractCompactionWriter implements AutoCloseable {
6366
// The index of the array corresponds to subTaskId.
6467
protected int[] chunkPointNumArray = new int[subTaskNum];
6568

69+
// Each sub task has estimated total size of written points in current chunk.
70+
// The index of the array corresponds to subTaskId.
71+
protected long[] writtenPointTotalSizeArray = new long[subTaskNum];
72+
73+
// Whether each sub task's current chunk writer contains TEXT, STRING, BLOB or OBJECT.
74+
protected boolean[] hasVariableLengthTypeArray = new boolean[subTaskNum];
75+
6676
// used to control the target chunk size
6777
protected long targetChunkSize = IoTDBDescriptor.getInstance().getConfig().getTargetChunkSize();
6878

@@ -74,7 +84,12 @@ public abstract class AbstractCompactionWriter implements AutoCloseable {
7484
@SuppressWarnings("squid:S1170")
7585
private final long checkPoint = (targetChunkPointNum >= 10 ? targetChunkPointNum : 10) / 10;
7686

77-
private long lastCheckIndex = 0;
87+
private final long[] lastCheckIndexArray = new long[subTaskNum];
88+
89+
// When estimated size of written points reaches check point, then check chunk size.
90+
private final long writtenPointTotalSizeCheckPoint = Math.max(targetChunkSize / 10, 1L);
91+
92+
private final long[] lastWrittenPointTotalSizeCheckIndexArray = new long[subTaskNum];
7893

7994
// if unsealed chunk size is lower then this, then deserialize next chunk no matter it is
8095
// overlapped or not
@@ -115,10 +130,24 @@ public Deletion getTTLLowerBoundForCurrentDevice() {
115130
}
116131

117132
public void startMeasurement(String measurement, IChunkWriter chunkWriter, int subTaskId) {
118-
lastCheckIndex = 0;
133+
resetChunkWriterStatistics(subTaskId);
119134
lastTimeSet[subTaskId] = false;
120135
chunkWriters[subTaskId] = chunkWriter;
121136
measurementId[subTaskId] = measurement;
137+
hasVariableLengthTypeArray[subTaskId] = containsVariableLengthType(chunkWriter);
138+
}
139+
140+
private boolean containsVariableLengthType(IChunkWriter chunkWriter) {
141+
if (chunkWriter instanceof ChunkWriterImpl) {
142+
return ((ChunkWriterImpl) chunkWriter).getDataType().isBinary();
143+
}
144+
AlignedChunkWriterImpl alignedChunkWriter = (AlignedChunkWriterImpl) chunkWriter;
145+
for (ValueChunkWriter valueChunkWriter : alignedChunkWriter.getValueChunkWriterList()) {
146+
if (valueChunkWriter.getDataType().isBinary()) {
147+
return true;
148+
}
149+
}
150+
return false;
122151
}
123152

124153
public abstract void endMeasurement(int subTaskId) throws IOException;
@@ -139,14 +168,17 @@ public void startMeasurement(String measurement, IChunkWriter chunkWriter, int s
139168
*/
140169
public abstract void checkAndMayFlushChunkMetadata() throws IOException;
141170

142-
protected void writeDataPoint(long timestamp, TsPrimitiveType value, IChunkWriter chunkWriter) {
171+
protected void writeDataPoint(
172+
long timestamp, TsPrimitiveType value, IChunkWriter chunkWriter, int subTaskId) {
173+
long writtenPointTotalSize = 0;
143174
if (chunkWriter instanceof ChunkWriterImpl) {
144175
ChunkWriterImpl chunkWriterImpl = (ChunkWriterImpl) chunkWriter;
145176
switch (chunkWriterImpl.getDataType()) {
146177
case TEXT:
147178
case STRING:
148179
case BLOB:
149180
chunkWriterImpl.write(timestamp, value.getBinary());
181+
writtenPointTotalSize += value.getBinary().getLength();
150182
break;
151183
case DOUBLE:
152184
chunkWriterImpl.write(timestamp, value.getDouble());
@@ -172,7 +204,85 @@ protected void writeDataPoint(long timestamp, TsPrimitiveType value, IChunkWrite
172204
} else {
173205
AlignedChunkWriterImpl alignedChunkWriter = (AlignedChunkWriterImpl) chunkWriter;
174206
alignedChunkWriter.write(timestamp, value.getVector());
207+
if (hasVariableLengthTypeArray[subTaskId]) {
208+
writtenPointTotalSize = estimateWrittenPointTotalSize(value);
209+
}
210+
}
211+
chunkPointNumArray[subTaskId]++;
212+
if (hasVariableLengthTypeArray[subTaskId]) {
213+
writtenPointTotalSizeArray[subTaskId] += writtenPointTotalSize;
214+
}
215+
}
216+
217+
private long estimateWrittenPointTotalSize(TsPrimitiveType value) {
218+
long size = Long.BYTES;
219+
TsPrimitiveType[] vector = value.getVector();
220+
for (TsPrimitiveType tsPrimitiveType : vector) {
221+
if (tsPrimitiveType == null) {
222+
continue;
223+
}
224+
TSDataType dataType = tsPrimitiveType.getDataType();
225+
switch (dataType) {
226+
case TEXT:
227+
case STRING:
228+
case BLOB:
229+
size += tsPrimitiveType.getBinary().getLength();
230+
break;
231+
case DOUBLE:
232+
case INT64:
233+
case TIMESTAMP:
234+
size += Long.BYTES;
235+
break;
236+
case INT32:
237+
case DATE:
238+
case FLOAT:
239+
size += Integer.BYTES;
240+
break;
241+
case BOOLEAN:
242+
size += 1;
243+
break;
244+
default:
245+
break;
246+
}
247+
}
248+
return size;
249+
}
250+
251+
protected long estimateWrittenPointTotalSize(TsBlock tsBlock) {
252+
int pointCount = tsBlock.getPositionCount();
253+
long size = (long) Long.BYTES * pointCount;
254+
Column[] columns = tsBlock.getValueColumns();
255+
for (Column column : columns) {
256+
TSDataType dataType = column.getDataType();
257+
if (dataType.isBinary()) {
258+
for (int j = 0; j < pointCount; j++) {
259+
if (column.isNull(j)) {
260+
continue;
261+
}
262+
size += column.getBinary(j).getLength();
263+
}
264+
continue;
265+
}
266+
// This is only used as a checkpoint estimate, so fixed-width values use count directly.
267+
switch (dataType) {
268+
case DOUBLE:
269+
case INT64:
270+
case TIMESTAMP:
271+
size += (long) Long.BYTES * pointCount;
272+
break;
273+
case INT32:
274+
case DATE:
275+
case FLOAT:
276+
size += (long) Integer.BYTES * pointCount;
277+
break;
278+
case BOOLEAN:
279+
size += pointCount;
280+
break;
281+
default:
282+
break;
283+
}
175284
}
285+
return size;
176286
}
177287

178288
@SuppressWarnings("squid:S2445")
@@ -182,7 +292,14 @@ protected void sealChunk(
182292
synchronized (targetWriter) {
183293
targetWriter.writeChunk(chunkWriter);
184294
}
295+
resetChunkWriterStatistics(subTaskId);
296+
}
297+
298+
private void resetChunkWriterStatistics(int subTaskId) {
185299
chunkPointNumArray[subTaskId] = 0;
300+
writtenPointTotalSizeArray[subTaskId] = 0;
301+
lastCheckIndexArray[subTaskId] = 0;
302+
lastWrittenPointTotalSizeCheckIndexArray[subTaskId] = 0;
186303
}
187304

188305
public abstract boolean flushNonAlignedChunk(
@@ -204,7 +321,7 @@ protected void flushNonAlignedChunkToFileWriter(
204321
synchronized (targetWriter) {
205322
// seal last chunk to file writer
206323
targetWriter.writeChunk(chunkWriters[subTaskId]);
207-
chunkPointNumArray[subTaskId] = 0;
324+
resetChunkWriterStatistics(subTaskId);
208325
targetWriter.writeChunk(chunk, chunkMetadata);
209326
}
210327
}
@@ -222,7 +339,7 @@ protected void flushAlignedChunkToFileWriter(
222339
AlignedChunkWriterImpl alignedChunkWriter = (AlignedChunkWriterImpl) chunkWriters[subTaskId];
223340
// seal last chunk to file writer
224341
targetWriter.writeChunk(alignedChunkWriter);
225-
chunkPointNumArray[subTaskId] = 0;
342+
resetChunkWriterStatistics(subTaskId);
226343

227344
targetWriter.markStartingWritingAligned();
228345

@@ -269,6 +386,9 @@ protected void flushNonAlignedPageToChunkWriter(
269386
chunkWriter.writePageHeaderAndDataIntoBuff(compressedPageData, pageHeader);
270387

271388
chunkPointNumArray[subTaskId] += pageHeader.getStatistics().getCount();
389+
if (hasVariableLengthTypeArray[subTaskId]) {
390+
writtenPointTotalSizeArray[subTaskId] += pageHeader.getSerializedPageSize();
391+
}
272392
}
273393

274394
public abstract boolean flushAlignedPage(AlignedPageElement alignedPageElement, int subTaskId)
@@ -293,29 +413,51 @@ protected void flushAlignedPageToChunkWriter(
293413
// flush new time page to chunk writer directly
294414
alignedChunkWriter.writePageHeaderAndDataIntoTimeBuff(compressedTimePageData, timePageHeader);
295415

416+
long writtenValuePageSize = 0;
296417
// flush new value pages to chunk writer directly
297418
for (int i = 0; i < valuePageHeaders.size(); i++) {
298-
if (valuePageHeaders.get(i) == null) {
419+
PageHeader valuePageHeader = valuePageHeaders.get(i);
420+
if (valuePageHeader == null) {
299421
// sub sensor does not exist in current file or value page has been deleted completely
300422
alignedChunkWriter.getValueChunkWriterByIndex(i).writeEmptyPageToPageBuffer();
301423
continue;
302424
}
303425
alignedChunkWriter.writePageHeaderAndDataIntoValueBuff(
304-
compressedValuePageDatas.get(i), valuePageHeaders.get(i), i);
426+
compressedValuePageDatas.get(i), valuePageHeader, i);
427+
if (hasVariableLengthTypeArray[subTaskId]) {
428+
writtenValuePageSize += valuePageHeader.getSerializedPageSize();
429+
}
305430
}
306431

307432
chunkPointNumArray[subTaskId] += timePageHeader.getStatistics().getCount();
433+
if (hasVariableLengthTypeArray[subTaskId]) {
434+
// Direct-flushed pages are already serialized, so use page size as checkpoint estimate.
435+
writtenPointTotalSizeArray[subTaskId] +=
436+
timePageHeader.getSerializedPageSize() + writtenValuePageSize;
437+
}
308438
}
309439

310440
protected void checkChunkSizeAndMayOpenANewChunk(
311441
CompactionTsFileWriter fileWriter, IChunkWriter chunkWriter, int subTaskId)
312442
throws IOException {
313-
if (chunkPointNumArray[subTaskId] >= (lastCheckIndex + 1) * checkPoint) {
314-
// if chunk point num reaches the check point, then check if the chunk size over threshold
315-
lastCheckIndex = chunkPointNumArray[subTaskId] / checkPoint;
443+
if (chunkWriter instanceof FollowingBatchCompactionAlignedChunkWriter
444+
&& chunkWriter.checkIsChunkSizeOverThreshold(targetChunkSize, targetChunkPointNum, false)) {
445+
sealChunk(fileWriter, chunkWriter, subTaskId);
446+
return;
447+
}
448+
boolean reachesPointCheckPoint =
449+
chunkPointNumArray[subTaskId] >= (lastCheckIndexArray[subTaskId] + 1) * checkPoint;
450+
boolean reachesSizeCheckPoint =
451+
hasVariableLengthTypeArray[subTaskId]
452+
&& writtenPointTotalSizeArray[subTaskId]
453+
>= (lastWrittenPointTotalSizeCheckIndexArray[subTaskId] + 1)
454+
* writtenPointTotalSizeCheckPoint;
455+
if (reachesPointCheckPoint || reachesSizeCheckPoint) {
456+
lastCheckIndexArray[subTaskId] = chunkPointNumArray[subTaskId] / checkPoint;
457+
lastWrittenPointTotalSizeCheckIndexArray[subTaskId] =
458+
writtenPointTotalSizeArray[subTaskId] / writtenPointTotalSizeCheckPoint;
316459
if (chunkWriter.checkIsChunkSizeOverThreshold(targetChunkSize, targetChunkPointNum, false)) {
317460
sealChunk(fileWriter, chunkWriter, subTaskId);
318-
lastCheckIndex = 0;
319461
}
320462
}
321463
}

iotdb-core/datanode/src/main/java/org/apache/iotdb/db/storageengine/dataregion/compaction/execute/utils/writer/AbstractCrossCompactionWriter.java

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -133,8 +133,7 @@ public void write(TimeValuePair timeValuePair, int subTaskId) throws IOException
133133

134134
checkTimeAndMayFlushChunkToCurrentFile(timestamp, subTaskId);
135135
int fileIndex = seqFileIndexArray[subTaskId];
136-
writeDataPoint(timestamp, value, chunkWriters[subTaskId]);
137-
chunkPointNumArray[subTaskId]++;
136+
writeDataPoint(timestamp, value, chunkWriters[subTaskId], subTaskId);
138137
checkChunkSizeAndMayOpenANewChunk(
139138
targetFileWriters.get(fileIndex), chunkWriters[subTaskId], subTaskId);
140139
isDeviceExistedInTargetFiles[fileIndex] = true;

iotdb-core/datanode/src/main/java/org/apache/iotdb/db/storageengine/dataregion/compaction/execute/utils/writer/AbstractInnerCompactionWriter.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -115,8 +115,8 @@ public void endMeasurement(int subTaskId) throws IOException {
115115
@Override
116116
public void write(TimeValuePair timeValuePair, int subTaskId) throws IOException {
117117
checkPreviousTimestamp(timeValuePair.getTimestamp(), subTaskId);
118-
writeDataPoint(timeValuePair.getTimestamp(), timeValuePair.getValue(), chunkWriters[subTaskId]);
119-
chunkPointNumArray[subTaskId]++;
118+
writeDataPoint(
119+
timeValuePair.getTimestamp(), timeValuePair.getValue(), chunkWriters[subTaskId], subTaskId);
120120
checkChunkSizeAndMayOpenANewChunk(fileWriter, chunkWriters[subTaskId], subTaskId);
121121
lastTime[subTaskId] = timeValuePair.getTimestamp();
122122
lastTimeSet[subTaskId] = true;

iotdb-core/datanode/src/main/java/org/apache/iotdb/db/storageengine/dataregion/compaction/execute/utils/writer/FastInnerCompactionWriter.java

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -182,6 +182,8 @@ public boolean flushAlignedPage(AlignedPageElement alignedPageElement, int subTa
182182
valuePageHeaders,
183183
subTaskId);
184184

185+
checkChunkSizeAndMayOpenANewChunk(fileWriter, chunkWriters[subTaskId], subTaskId);
186+
185187
lastTime[subTaskId] = timePageHeader.getEndTime();
186188
lastTimeSet[subTaskId] = true;
187189
return true;
@@ -218,6 +220,8 @@ public boolean flushBatchedValuePage(
218220
valuePageHeaders,
219221
subTaskId);
220222

223+
checkChunkSizeAndMayOpenANewChunk(fileWriter, chunkWriters[subTaskId], subTaskId);
224+
221225
lastTime[subTaskId] = timePageHeader.getEndTime();
222226
lastTimeSet[subTaskId] = true;
223227
return true;
@@ -228,10 +232,12 @@ public boolean flushBatchedValuePage(
228232
* successfully or not. Return false if the unsealed page is too small or the end time of page
229233
* exceeds the end time of file, else return true.
230234
*
235+
* @throws IOException if io errors occurred
231236
* @throws PageException if errors occurred when write data page header
232237
*/
233238
public boolean flushNonAlignedPage(
234-
ByteBuffer compressedPageData, PageHeader pageHeader, int subTaskId) throws PageException {
239+
ByteBuffer compressedPageData, PageHeader pageHeader, int subTaskId)
240+
throws IOException, PageException {
235241
checkPreviousTimestamp(pageHeader.getStartTime(), subTaskId);
236242
boolean isUnsealedPageOverThreshold =
237243
chunkWriters[subTaskId].checkIsUnsealedPageOverThreshold(
@@ -244,6 +250,8 @@ public boolean flushNonAlignedPage(
244250
flushNonAlignedPageToChunkWriter(
245251
(ChunkWriterImpl) chunkWriters[subTaskId], compressedPageData, pageHeader, subTaskId);
246252

253+
checkChunkSizeAndMayOpenANewChunk(fileWriter, chunkWriters[subTaskId], subTaskId);
254+
247255
lastTime[subTaskId] = pageHeader.getEndTime();
248256
lastTimeSet[subTaskId] = true;
249257
return true;

iotdb-core/datanode/src/main/java/org/apache/iotdb/db/storageengine/dataregion/compaction/execute/utils/writer/ReadPointCrossCompactionWriter.java

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -56,13 +56,16 @@ public void write(TsBlock tsBlock, int subTaskId) throws IOException {
5656
checkTimeAndMayFlushChunkToCurrentFile(timestamps.getStartTime(), subTaskId);
5757
AlignedChunkWriterImpl chunkWriter = (AlignedChunkWriterImpl) this.chunkWriters[subTaskId];
5858
chunkWriter.write(timestamps, columns, batchSize);
59+
chunkPointNumArray[subTaskId] += batchSize;
60+
if (hasVariableLengthTypeArray[subTaskId]) {
61+
writtenPointTotalSizeArray[subTaskId] += estimateWrittenPointTotalSize(tsBlock);
62+
}
5963
synchronized (this) {
6064
// we need to synchronized here to avoid multi-thread competition in sub-task
6165
TsFileResource resource = targetResources.get(seqFileIndexArray[subTaskId]);
6266
resource.updateStartTime(deviceId, timestamps.getStartTime());
6367
resource.updateEndTime(deviceId, timestamps.getEndTime());
6468
}
65-
chunkPointNumArray[subTaskId] += timestamps.getTimes().length;
6669
checkChunkSizeAndMayOpenANewChunk(
6770
targetFileWriters.get(seqFileIndexArray[subTaskId]), chunkWriter, subTaskId);
6871
isDeviceExistedInTargetFiles[seqFileIndexArray[subTaskId]] = true;

iotdb-core/datanode/src/main/java/org/apache/iotdb/db/storageengine/dataregion/compaction/execute/utils/writer/ReadPointInnerCompactionWriter.java

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,10 @@ public void write(TsBlock tsBlock, int subTaskId) throws IOException {
5454
int batchSize = tsBlock.getPositionCount();
5555
AlignedChunkWriterImpl chunkWriter = (AlignedChunkWriterImpl) this.chunkWriters[subTaskId];
5656
chunkWriter.write(timestamps, columns, batchSize);
57-
chunkPointNumArray[subTaskId] += timestamps.getTimes().length;
57+
chunkPointNumArray[subTaskId] += batchSize;
58+
if (hasVariableLengthTypeArray[subTaskId]) {
59+
writtenPointTotalSizeArray[subTaskId] += estimateWrittenPointTotalSize(tsBlock);
60+
}
5861
checkChunkSizeAndMayOpenANewChunk(fileWriter, chunkWriter, subTaskId);
5962
}
6063

iotdb-core/datanode/src/main/java/org/apache/iotdb/db/storageengine/dataregion/compaction/execute/utils/writer/RepairUnsortedFileCompactionWriter.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -74,8 +74,8 @@ public void endMeasurement(int subTaskId) throws IOException {
7474
}
7575

7676
private void writeToChunkWriter(TimeValuePair timeValuePair, int subTaskId) throws IOException {
77-
writeDataPoint(timeValuePair.getTimestamp(), timeValuePair.getValue(), chunkWriters[subTaskId]);
78-
chunkPointNumArray[subTaskId]++;
77+
writeDataPoint(
78+
timeValuePair.getTimestamp(), timeValuePair.getValue(), chunkWriters[subTaskId], subTaskId);
7979
checkChunkSizeAndMayOpenANewChunk(fileWriter, chunkWriters[subTaskId], subTaskId);
8080
}
8181

0 commit comments

Comments
 (0)