This is an automated email from the ASF dual-hosted git repository.

jt2594838 pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/iotdb.git


The following commit(s) were added to refs/heads/master by this push:
     new d29dcf8247a Track null value ratio of flushed MemTables (#18287)
d29dcf8247a is described below

commit d29dcf8247a61967d7d4542a7dbbaf7b0871ba7b
Author: Jiang Tian <[email protected]>
AuthorDate: Mon Jul 27 09:59:35 2026 +0800

    Track null value ratio of flushed MemTables (#18287)
    
    * Add null value statistics
    
    * Add null value statistics
    
    * Add metric Add null value ratio metric for flushed MemTables
    
    * Fix null ratio for failed aligned tablet rows
    
    ---------
    
    Co-authored-by: Caideyipi <[email protected]>
---
 .../iotdb/db/i18n/StorageEngineMessages.java       |  2 +
 .../iotdb/db/i18n/StorageEngineMessages.java       |  2 +
 .../iotdb/db/service/metrics/WritingMetrics.java   | 23 +++++-
 .../dataregion/memtable/AbstractMemTable.java      | 74 ++++++++++++++++--
 .../dataregion/memtable/IMemTable.java             |  9 +++
 .../dataregion/memtable/TsFileProcessor.java       | 14 +++-
 .../AbstractMemTablePartialInsertTest.java         | 87 +++++++++++++++++++++-
 7 files changed, 199 insertions(+), 12 deletions(-)

diff --git 
a/iotdb-core/datanode/src/main/i18n/en/org/apache/iotdb/db/i18n/StorageEngineMessages.java
 
b/iotdb-core/datanode/src/main/i18n/en/org/apache/iotdb/db/i18n/StorageEngineMessages.java
index 512879bfd3c..fcc7a754704 100644
--- 
a/iotdb-core/datanode/src/main/i18n/en/org/apache/iotdb/db/i18n/StorageEngineMessages.java
+++ 
b/iotdb-core/datanode/src/main/i18n/en/org/apache/iotdb/db/i18n/StorageEngineMessages.java
@@ -757,6 +757,8 @@ public final class StorageEngineMessages {
       "{}: {} is closed during flush, abandon flush task";
   public static final String 
STORAGE_LOG_THE_COMPRESSION_RATIO_OF_TSFILE_IS_TOTALMEMTABLESIZE_THE_8CE66BE3 =
       "The compression ratio of tsfile {} is {}, totalMemTableSize: {}, the 
file size: {}";
+  public static final String 
STORAGE_LOG_THE_COMPRESSION_RATIO_OF_TSFILE_IS_TOTALMEMTABLESIZE_THE_FILE_SIZE_NULL_VALUE_RATIO_46F3B1F7
 =
+      "The compression ratio of tsfile {} is {}, totalMemTableSize: {}, the 
file size: {}, null value ratio: {}";
   public static final String 
STORAGE_LOG_STORAGE_GROUP_CLOSE_AND_REMOVE_EMPTY_FILE_72D42293 =
       "Storage group {} close and remove empty file {}";
   public static final String 
STORAGE_LOG_PUT_THE_MEMTABLE_SIGNAL_OUT_OF_FLUSHINGMEMTABLES_BUT_IT_D78AF257 =
diff --git 
a/iotdb-core/datanode/src/main/i18n/zh/org/apache/iotdb/db/i18n/StorageEngineMessages.java
 
b/iotdb-core/datanode/src/main/i18n/zh/org/apache/iotdb/db/i18n/StorageEngineMessages.java
index 96d09c1c141..19a2e5f68f6 100644
--- 
a/iotdb-core/datanode/src/main/i18n/zh/org/apache/iotdb/db/i18n/StorageEngineMessages.java
+++ 
b/iotdb-core/datanode/src/main/i18n/zh/org/apache/iotdb/db/i18n/StorageEngineMessages.java
@@ -746,6 +746,8 @@ public final class StorageEngineMessages {
       "{}:{} 在 flush 期间已关闭,放弃 flush 任务";
   public static final String 
STORAGE_LOG_THE_COMPRESSION_RATIO_OF_TSFILE_IS_TOTALMEMTABLESIZE_THE_8CE66BE3 =
       "TsFile {} 的压缩率为 {},totalMemTableSize:{},文件大小:{}";
+  public static final String 
STORAGE_LOG_THE_COMPRESSION_RATIO_OF_TSFILE_IS_TOTALMEMTABLESIZE_THE_FILE_SIZE_NULL_VALUE_RATIO_46F3B1F7
 =
+      "TsFile {} 的压缩率为 {},totalMemTableSize:{},文件大小:{},空值比例:{}";
   public static final String 
STORAGE_LOG_STORAGE_GROUP_CLOSE_AND_REMOVE_EMPTY_FILE_72D42293 =
       "Storage group {} 关闭并移除空文件 {}";
   public static final String 
STORAGE_LOG_PUT_THE_MEMTABLE_SIGNAL_OUT_OF_FLUSHINGMEMTABLES_BUT_IT_D78AF257 =
diff --git 
a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/service/metrics/WritingMetrics.java
 
b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/service/metrics/WritingMetrics.java
index 1b6be1ab3db..88197d03cd6 100644
--- 
a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/service/metrics/WritingMetrics.java
+++ 
b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/service/metrics/WritingMetrics.java
@@ -434,6 +434,7 @@ public class WritingMetrics implements IMetricSet {
   public static final String SERIES_NUM = "series_num";
   public static final String AVG_SERIES_POINT_NUM = "avg_series_points_num";
   public static final String COMPRESSION_RATIO = "compression_ratio";
+  public static final String NULL_VALUE_RATIO = "null_value_ratio";
   public static final String EFFECTIVE_RATIO_INFO = "effective_ratio_info";
   public static final String OLDEST_MEM_TABLE_RAM_WHEN_CAUSE_SNAPSHOT =
       "oldest_mem_table_ram_when_cause_snapshot";
@@ -598,7 +599,13 @@ public class WritingMetrics implements IMetricSet {
   }
 
   public void createFlushingMemTableStatusMetrics(DataRegionId dataRegionId) {
-    Arrays.asList(MEM_TABLE_SIZE, SERIES_NUM, POINTS_NUM, COMPRESSION_RATIO, 
FLUSH_TSFILE_SIZE)
+    Arrays.asList(
+            MEM_TABLE_SIZE,
+            SERIES_NUM,
+            POINTS_NUM,
+            COMPRESSION_RATIO,
+            NULL_VALUE_RATIO,
+            FLUSH_TSFILE_SIZE)
         .forEach(
             name ->
                 MetricService.getInstance()
@@ -727,6 +734,7 @@ public class WritingMetrics implements IMetricSet {
             POINTS_NUM,
             AVG_SERIES_POINT_NUM,
             COMPRESSION_RATIO,
+            NULL_VALUE_RATIO,
             FLUSH_TSFILE_SIZE)
         .forEach(
             name ->
@@ -790,6 +798,19 @@ public class WritingMetrics implements IMetricSet {
             new DataRegionId(Integer.parseInt(dataRegionId)).toString());
   }
 
+  public void recordTsFileNullValueRatioOfFlushingMemTable(
+      String dataRegionId, double nullValueRatio) {
+    MetricService.getInstance()
+        .histogram(
+            (long) (nullValueRatio * 100),
+            Metric.FLUSHING_MEM_TABLE_STATUS.toString(),
+            MetricLevel.IMPORTANT,
+            Tag.NAME.toString(),
+            NULL_VALUE_RATIO,
+            Tag.REGION.toString(),
+            new DataRegionId(Integer.parseInt(dataRegionId)).toString());
+  }
+
   public void recordFlushingMemTableStatus(
       String storageGroup, long memSize, long seriesNum, long totalPointsNum, 
long avgSeriesNum) {
     DataRegionId dataRegionId = 
getDataRegionIdFromStorageGroupStr(storageGroup);
diff --git 
a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/storageengine/dataregion/memtable/AbstractMemTable.java
 
b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/storageengine/dataregion/memtable/AbstractMemTable.java
index eea69f98a3f..e24308cf505 100644
--- 
a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/storageengine/dataregion/memtable/AbstractMemTable.java
+++ 
b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/storageengine/dataregion/memtable/AbstractMemTable.java
@@ -47,6 +47,7 @@ import 
org.apache.iotdb.db.storageengine.dataregion.wal.utils.WALWriteUtils;
 import org.apache.iotdb.db.utils.EncryptDBUtils;
 import org.apache.iotdb.db.utils.MemUtils;
 import org.apache.iotdb.db.utils.ModificationUtils;
+import org.apache.iotdb.rpc.TSStatusCode;
 
 import org.apache.tsfile.enums.TSDataType;
 import org.apache.tsfile.file.metadata.ChunkMetadata;
@@ -104,6 +105,10 @@ public abstract class AbstractMemTable implements 
IMemTable {
 
   private long totalPointsNum = 0;
 
+  private long totalValueCount = 0;
+
+  private long nullValueCount = 0;
+
   private long totalPointsNumThreshold = 0;
 
   private long maxPlanIndex = Long.MIN_VALUE;
@@ -222,13 +227,16 @@ public abstract class AbstractMemTable implements 
IMemTable {
         MemUtils.getRowRecordSize(dataTypes, writableValues, 
insertRowNode.getColumnCategories());
     write(insertRowNode.getDeviceID(), schemaList, insertRowNode.getTime(), 
writableValues);
 
+    int validValueCount = insertRowNode.getValidMeasurementNumber(true);
     int pointsInserted =
-        insertRowNode.getValidMeasurementNumber(true)
+        validValueCount
             - 
(IoTDBDescriptor.getInstance().getConfig().isIncludeNullValueInWriteThroughputMetric()
                 ? 0
                 : nullPointsNumber);
 
     totalPointsNum += pointsInserted;
+    totalValueCount += validValueCount;
+    nullValueCount += nullPointsNumber;
     return pointsInserted;
   }
 
@@ -266,12 +274,15 @@ public abstract class AbstractMemTable implements 
IMemTable {
             dataTypes, writableValues, insertRowNode.getColumnCategories());
     writeAlignedRow(
         insertRowNode.getDeviceID(), schemaList, insertRowNode.getTime(), 
writableValues);
+    int validValueCount = insertRowNode.getValidMeasurementNumber(true);
     int pointsInserted =
-        insertRowNode.getValidMeasurementNumber(true)
+        validValueCount
             - 
(IoTDBDescriptor.getInstance().getConfig().isIncludeNullValueInWriteThroughputMetric()
                 ? 0
                 : nullPointsNumber);
     totalPointsNum += pointsInserted;
+    totalValueCount += validValueCount;
+    nullValueCount += nullPointsNumber;
     return pointsInserted;
   }
 
@@ -279,17 +290,21 @@ public abstract class AbstractMemTable implements 
IMemTable {
   public int insertTablet(InsertTabletNode insertTabletNode, int start, int 
end)
       throws WriteProcessException {
     try {
-      int nullPointsNumber = computeTabletNullPointsNumber(insertTabletNode, 
start, end, true);
+      int nullPointsNumber =
+          computeTabletNullPointsNumber(insertTabletNode, start, end, true, 
null);
       writeTabletNode(insertTabletNode, start, end);
       memSize += MemUtils.getTabletSize(insertTabletNode, start, end);
+      int validValueCount = insertTabletNode.getValidMeasurementNumber(true) * 
(end - start);
       int pointsInserted =
-          (insertTabletNode.getValidMeasurementNumber(true) * (end - start))
+          validValueCount
               - (IoTDBDescriptor.getInstance()
                       .getConfig()
                       .isIncludeNullValueInWriteThroughputMetric()
                   ? 0
                   : nullPointsNumber);
       totalPointsNum += pointsInserted;
+      totalValueCount += validValueCount;
+      nullValueCount += nullPointsNumber;
       return pointsInserted;
     } catch (RuntimeException e) {
       throw new WriteProcessException(e);
@@ -301,18 +316,24 @@ public abstract class AbstractMemTable implements 
IMemTable {
       InsertTabletNode insertTabletNode, int start, int end, TSStatus[] 
results)
       throws WriteProcessException {
     try {
-      int nullPointsNumber = computeTabletNullPointsNumber(insertTabletNode, 
start, end, true);
+      int nullPointsNumber =
+          computeTabletNullPointsNumber(insertTabletNode, start, end, true, 
results);
       writeAlignedTablet(insertTabletNode, start, end, results);
       // TODO-Table: what is the relation between this and 
TsFileProcessor.checkMemCost
       memSize += MemUtils.getAlignedTabletSize(insertTabletNode, start, end, 
results);
+      int validValueCount =
+          insertTabletNode.getValidMeasurementNumber(true)
+              * computeSuccessfulRowCount(results, start, end);
       int pointsInserted =
-          (insertTabletNode.getValidMeasurementNumber(true) * (end - start))
+          validValueCount
               - (IoTDBDescriptor.getInstance()
                       .getConfig()
                       .isIncludeNullValueInWriteThroughputMetric()
                   ? 0
                   : nullPointsNumber);
       totalPointsNum += pointsInserted;
+      totalValueCount += validValueCount;
+      nullValueCount += nullPointsNumber;
       return pointsInserted;
     } catch (RuntimeException e) {
       throw new WriteProcessException(e);
@@ -320,7 +341,11 @@ public abstract class AbstractMemTable implements 
IMemTable {
   }
 
   private static int computeTabletNullPointsNumber(
-      InsertTabletNode insertTabletNode, int start, int end, boolean 
countFieldOnly) {
+      InsertTabletNode insertTabletNode,
+      int start,
+      int end,
+      boolean countFieldOnly,
+      TSStatus[] results) {
     Object[] values = insertTabletNode.getBitMaps();
     int nullPointsNumber = 0;
     if (values != null) {
@@ -339,7 +364,7 @@ public abstract class AbstractMemTable implements IMemTable 
{
         BitMap bitMap = i < values.length ? (BitMap) values[i] : null;
         if (bitMap != null && !bitMap.isAllUnmarked()) {
           for (int j = start; j < end; j++) {
-            if (bitMap.isMarked(j)) {
+            if (!isFailedRow(results, j) && bitMap.isMarked(j)) {
               nullPointsNumber++;
             }
           }
@@ -349,6 +374,22 @@ public abstract class AbstractMemTable implements 
IMemTable {
     return nullPointsNumber;
   }
 
+  private static int computeSuccessfulRowCount(TSStatus[] results, int start, 
int end) {
+    int successfulRowCount = 0;
+    for (int i = start; i < end; i++) {
+      if (!isFailedRow(results, i)) {
+        successfulRowCount++;
+      }
+    }
+    return successfulRowCount;
+  }
+
+  private static boolean isFailedRow(TSStatus[] results, int index) {
+    return results != null
+        && results[index] != null
+        && results[index].getCode() != 
TSStatusCode.SUCCESS_STATUS.getStatusCode();
+  }
+
   @Override
   public void write(
       IDeviceID deviceId,
@@ -481,6 +522,21 @@ public abstract class AbstractMemTable implements 
IMemTable {
     return totalPointsNum;
   }
 
+  @Override
+  public long getTotalValueCount() {
+    return totalValueCount;
+  }
+
+  @Override
+  public long getNullValueCount() {
+    return nullValueCount;
+  }
+
+  @Override
+  public double getNullValueRatio() {
+    return totalValueCount == 0 ? 0 : (double) nullValueCount / 
totalValueCount;
+  }
+
   @Override
   public long size() {
     long sum = 0;
@@ -501,6 +557,8 @@ public abstract class AbstractMemTable implements IMemTable 
{
     memSize = 0;
     seriesNumber = 0;
     totalPointsNum = 0;
+    totalValueCount = 0;
+    nullValueCount = 0;
     totalPointsNumThreshold = 0;
     tvListRamCost = 0;
     maxPlanIndex = 0;
diff --git 
a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/storageengine/dataregion/memtable/IMemTable.java
 
b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/storageengine/dataregion/memtable/IMemTable.java
index b988fc94185..4d38fea2c11 100644
--- 
a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/storageengine/dataregion/memtable/IMemTable.java
+++ 
b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/storageengine/dataregion/memtable/IMemTable.java
@@ -90,6 +90,15 @@ public interface IMemTable extends WALEntryValue {
 
   long getTotalPointsNum();
 
+  long getTotalValueCount();
+
+  long getNullValueCount();
+
+  /**
+   * @return the ratio of null values to all valid values written into this 
MemTable
+   */
+  double getNullValueRatio();
+
   /**
    * insert into this memtable
    *
diff --git 
a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/storageengine/dataregion/memtable/TsFileProcessor.java
 
b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/storageengine/dataregion/memtable/TsFileProcessor.java
index 8b14459a760..bded5e1c627 100644
--- 
a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/storageengine/dataregion/memtable/TsFileProcessor.java
+++ 
b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/storageengine/dataregion/memtable/TsFileProcessor.java
@@ -196,6 +196,10 @@ public class TsFileProcessor {
   /** Total memtable size for mem control. */
   private long totalMemTableSize;
 
+  private long totalValueCount;
+
+  private long nullValueCount;
+
   private final AtomicBoolean isTotallyGeneratedByPipe = new 
AtomicBoolean(true);
 
   private static final String FLUSH_QUERY_WRITE_LOCKED = "{}: {} get 
flushQueryLock write lock";
@@ -1652,6 +1656,8 @@ public class TsFileProcessor {
 
     if (!(tobeFlushed.isSignalMemTable() || tobeFlushed.isEmpty())) {
       totalMemTableSize += tobeFlushed.memSize();
+      totalValueCount += tobeFlushed.getTotalValueCount();
+      nullValueCount += tobeFlushed.getNullValueCount();
     }
     WritingMetrics.getInstance()
         .recordMemTableLiveDuration(System.currentTimeMillis() - 
getWorkMemTableCreatedTime());
@@ -1942,16 +1948,20 @@ public class TsFileProcessor {
   private void updateCompressionRatio() {
     try {
       double compressionRatio = ((double) totalMemTableSize) / writer.getPos();
+      double nullValueRatio = totalValueCount == 0 ? 0 : (double) 
nullValueCount / totalValueCount;
       logger.info(
           StorageEngineMessages
-              
.STORAGE_LOG_THE_COMPRESSION_RATIO_OF_TSFILE_IS_TOTALMEMTABLESIZE_THE_8CE66BE3,
+              
.STORAGE_LOG_THE_COMPRESSION_RATIO_OF_TSFILE_IS_TOTALMEMTABLESIZE_THE_FILE_SIZE_NULL_VALUE_RATIO_46F3B1F7,
           writer.getFile().getAbsolutePath(),
           String.format("%.2f", compressionRatio),
           totalMemTableSize,
-          writer.getPos());
+          writer.getPos(),
+          nullValueRatio);
       String dataRegionId = 
dataRegionInfo.getDataRegion().getDataRegionIdString();
       WritingMetrics.getInstance()
           .recordTsFileCompressionRatioOfFlushingMemTable(dataRegionId, 
compressionRatio);
+      WritingMetrics.getInstance()
+          .recordTsFileNullValueRatioOfFlushingMemTable(dataRegionId, 
nullValueRatio);
       CompressionRatio.getInstance().updateRatio(totalMemTableSize, 
writer.getPos(), dataRegionId);
     } catch (IOException e) {
       logger.error(
diff --git 
a/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/storageengine/dataregion/memtable/AbstractMemTablePartialInsertTest.java
 
b/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/storageengine/dataregion/memtable/AbstractMemTablePartialInsertTest.java
index bdad14c20e3..6936fd3ad51 100644
--- 
a/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/storageengine/dataregion/memtable/AbstractMemTablePartialInsertTest.java
+++ 
b/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/storageengine/dataregion/memtable/AbstractMemTablePartialInsertTest.java
@@ -19,6 +19,7 @@
 
 package org.apache.iotdb.db.storageengine.dataregion.memtable;
 
+import org.apache.iotdb.common.rpc.thrift.TSStatus;
 import org.apache.iotdb.commons.exception.IllegalPathException;
 import org.apache.iotdb.commons.path.PartialPath;
 import org.apache.iotdb.commons.queryengine.plan.planner.plan.node.PlanNodeId;
@@ -27,6 +28,7 @@ import org.apache.iotdb.db.conf.IoTDBDescriptor;
 import org.apache.iotdb.db.exception.WriteProcessException;
 import 
org.apache.iotdb.db.queryengine.plan.planner.plan.node.write.InsertRowNode;
 import 
org.apache.iotdb.db.queryengine.plan.planner.plan.node.write.InsertTabletNode;
+import org.apache.iotdb.rpc.TSStatusCode;
 
 import org.apache.tsfile.enums.TSDataType;
 import org.apache.tsfile.utils.BitMap;
@@ -253,6 +255,7 @@ public class AbstractMemTablePartialInsertTest {
 
     assertEquals(3, points);
     assertEquals(3, memTable.getTotalPointsNum());
+    assertEquals(0, memTable.getNullValueRatio(), 0);
   }
 
   @Test
@@ -334,6 +337,82 @@ public class AbstractMemTablePartialInsertTest {
     assertEquals(6, memTable.getTotalPointsNum());
   }
 
+  /**
+   * Verifies that a non-aligned MemTable accumulates null values across row 
and tablet writes. The
+   * row contributes one null out of two values, and the tablet contributes 
one null out of four
+   * values, so the final null value ratio is 2 / 6.
+   */
+  @Test
+  public void testNullValueRatioIsAccumulatedForRowAndTabletWrites()
+      throws IllegalPathException, WriteProcessException {
+    InsertRowNode rowNode =
+        buildInsertRowNode(
+            new String[] {"s0", "s1"}, new Object[] {1, null}, false, -1 /* no 
failure */);
+    memTable.insert(rowNode);
+
+    BitMap[] bitMaps = new BitMap[2];
+    bitMaps[1] = new BitMap(2);
+    bitMaps[1].mark(0);
+    InsertTabletNode tabletNode =
+        buildInsertTabletNode(new String[] {"s0", "s1"}, 2, bitMaps, -1 /* no 
failure */);
+    memTable.insertTablet(tabletNode, 0, 2);
+
+    assertEquals(6, memTable.getTotalValueCount());
+    assertEquals(2, memTable.getNullValueCount());
+    assertEquals(1.0 / 3, memTable.getNullValueRatio(), 0.000001);
+  }
+
+  /**
+   * Verifies that an aligned MemTable accumulates null values across row and 
tablet writes. The row
+   * contributes one null out of two values, and the tablet contributes two 
nulls out of four
+   * values, so the final null value ratio is 3 / 6.
+   */
+  @Test
+  public void testNullValueRatioIsAccumulatedForAlignedWrites()
+      throws IllegalPathException, WriteProcessException {
+    InsertRowNode rowNode =
+        buildAlignedInsertRowNode(
+            new String[] {"s0", "s1"}, new Object[] {1, null}, -1 /* no 
failure */);
+    memTable.insertAlignedRow(rowNode);
+
+    BitMap[] bitMaps = new BitMap[2];
+    bitMaps[1] = new BitMap(2);
+    bitMaps[1].mark(0);
+    bitMaps[1].mark(1);
+    InsertTabletNode tabletNode =
+        buildInsertTabletNode(new String[] {"s0", "s1"}, 2, bitMaps, -1 /* no 
failure */);
+    memTable.insertAlignedTablet(tabletNode, 0, 2, null);
+
+    assertEquals(6, memTable.getTotalValueCount());
+    assertEquals(3, memTable.getNullValueCount());
+    assertEquals(0.5, memTable.getNullValueRatio(), 0.000001);
+  }
+
+  /**
+   * Verifies that failed aligned-tablet rows are excluded from null value 
statistics. The first row
+   * contributes one null out of two values, while the second row fails and 
contributes no values,
+   * so the final null value ratio is 1 / 2.
+   */
+  @Test
+  public void testNullValueRatioExcludesFailedAlignedTabletRows()
+      throws IllegalPathException, WriteProcessException {
+    BitMap[] bitMaps = new BitMap[2];
+    bitMaps[1] = new BitMap(2);
+    bitMaps[1].mark(0);
+    InsertTabletNode tabletNode =
+        buildInsertTabletNode(new String[] {"s0", "s1"}, 2, bitMaps, -1 /* no 
failure */);
+    TSStatus[] results = new TSStatus[2];
+    results[1] = new TSStatus(TSStatusCode.OUT_OF_TTL.getStatusCode());
+
+    int points = memTable.insertAlignedTablet(tabletNode, 0, 2, results);
+
+    assertEquals(1, points);
+    assertEquals(1, memTable.getTotalPointsNum());
+    assertEquals(2, memTable.getTotalValueCount());
+    assertEquals(1, memTable.getNullValueCount());
+    assertEquals(0.5, memTable.getNullValueRatio(), 0.000001);
+  }
+
   // =========================================================================
   // Helpers
   // =========================================================================
@@ -344,6 +423,12 @@ public class AbstractMemTablePartialInsertTest {
    */
   private static InsertRowNode buildAlignedInsertRowNode(
       String[] measurementNames, Object[] values, int failedIndex) throws 
IllegalPathException {
+    return buildInsertRowNode(measurementNames, values, true, failedIndex);
+  }
+
+  private static InsertRowNode buildInsertRowNode(
+      String[] measurementNames, Object[] values, boolean aligned, int 
failedIndex)
+      throws IllegalPathException {
     int n = measurementNames.length;
     TSDataType[] dataTypes = new TSDataType[n];
     MeasurementSchema[] schemas = new MeasurementSchema[n];
@@ -355,7 +440,7 @@ public class AbstractMemTablePartialInsertTest {
         new InsertRowNode(
             new PlanNodeId("test"),
             new PartialPath("root.sg.d1"),
-            true /* isAligned */,
+            aligned,
             measurementNames,
             dataTypes,
             schemas,

Reply via email to