2222import org .apache .iotdb .commons .conf .IoTDBConstant ;
2323import org .apache .iotdb .db .conf .IoTDBDescriptor ;
2424import 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 ;
2526import org .apache .iotdb .db .storageengine .dataregion .compaction .execute .utils .executor .fast .element .AlignedPageElement ;
2627import org .apache .iotdb .db .storageengine .dataregion .compaction .execute .utils .executor .fast .element .ChunkMetadataElement ;
2728import org .apache .iotdb .db .storageengine .dataregion .compaction .execute .utils .writer .flushcontroller .AbstractCompactionFlushController ;
2829import org .apache .iotdb .db .storageengine .dataregion .compaction .io .CompactionTsFileWriter ;
2930import org .apache .iotdb .db .storageengine .dataregion .modification .Deletion ;
3031
32+ import org .apache .tsfile .block .column .Column ;
33+ import org .apache .tsfile .enums .TSDataType ;
3134import org .apache .tsfile .exception .write .PageException ;
3235import org .apache .tsfile .file .header .PageHeader ;
3336import 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 }
0 commit comments