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

qiaojialin pushed a commit to branch rel/0.12
in repository https://gitbox.apache.org/repos/asf/iotdb.git


The following commit(s) were added to refs/heads/rel/0.12 by this push:
     new d725ab3  [To rel/0.12][IOTDB-1903] Fix IndexOutOfRangeException when 
starting IoTDB (#4312)
d725ab3 is described below

commit d725ab3158537567a792151b3223ccafc6d62296
Author: liuxuxin <[email protected]>
AuthorDate: Fri Nov 5 21:33:48 2021 +0800

    [To rel/0.12][IOTDB-1903] Fix IndexOutOfRangeException when starting IoTDB 
(#4312)
---
 pom.xml                                            |  56 +-
 .../iotdb/tsfile/read/TsFileSequenceReader.java    |  17 +-
 .../iotdb/tsfile/write/writer/TsFileIOWriter.java  |   4 +-
 .../read/TsFileSequenceReaderSelfCheckTest.java    | 799 +++++++++++++++++++++
 4 files changed, 852 insertions(+), 24 deletions(-)

diff --git a/pom.xml b/pom.xml
index f50d5eb..c8dd8a9 100644
--- a/pom.xml
+++ b/pom.xml
@@ -19,7 +19,8 @@
     under the License.
 
 -->
-<project xmlns="http://maven.apache.org/POM/4.0.0"; 
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"; 
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 
http://maven.apache.org/xsd/maven-4.0.0.xsd";>
+<project xmlns="http://maven.apache.org/POM/4.0.0"; 
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance";
+         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 
http://maven.apache.org/xsd/maven-4.0.0.xsd";>
     <modelVersion>4.0.0</modelVersion>
     <parent>
         <groupId>org.apache</groupId>
@@ -31,7 +32,9 @@
     <version>0.12.4-SNAPSHOT</version>
     <packaging>pom</packaging>
     <name>Apache IoTDB Project Parent POM</name>
-    <description>This is the top level project that builds, packages the 
tsfile, iotdb engine, jdbc, and integration libs.</description>
+    <description>This is the top level project that builds, packages the 
tsfile, iotdb engine, jdbc, and integration
+        libs.
+    </description>
     <licenses>
         <license>
             <name>The Apache License, Version 2.0</name>
@@ -147,7 +150,8 @@
         <!-- Exclude all generated code -->
         <sonar.exclusions>**/generated-sources</sonar.exclusions>
         
<sonar.java.checkstyle.reportPaths>target/checkstyle-report.xml</sonar.java.checkstyle.reportPaths>
-        
<sonar.coverage.jacoco.xmlReportPaths>target/jacoco-merged-reports/jacoco.xml</sonar.coverage.jacoco.xmlReportPaths>
+        
<sonar.coverage.jacoco.xmlReportPaths>target/jacoco-merged-reports/jacoco.xml
+        </sonar.coverage.jacoco.xmlReportPaths>
         
<sonar.junit.reportPaths>target/surefire-reports,target/failsafe-reports</sonar.junit.reportPaths>
         <!-- By default, the argLine is empty-->
         <gson.version>2.8.6</gson.version>
@@ -806,7 +810,8 @@
                                 </requireJavaVersion>
                                 <!-- Disabled for now as it breaks the ability 
to build single modules -->
                                 <!--reactorModuleConvergence/-->
-                                <banVulnerable 
implementation="org.sonatype.ossindex.maven.enforcer.BanVulnerableDependencies"/>
+                                <banVulnerable
+                                        
implementation="org.sonatype.ossindex.maven.enforcer.BanVulnerableDependencies"/>
                             </rules>
                         </configuration>
                     </execution>
@@ -1014,7 +1019,9 @@
             </activation>
             <properties>
                 <os.classifier>windows-x86_64</os.classifier>
-                
<thrift.download-url>http://artfiles.org/apache.org/thrift/${thrift.version}/thrift-${thrift.version}.exe</thrift.download-url>
+                <thrift.download-url>
+                    
http://artfiles.org/apache.org/thrift/${thrift.version}/thrift-${thrift.version}.exe
+                </thrift.download-url>
                 
<thrift.executable>thrift-${thrift.version}-win-x86_64.exe</thrift.executable>
                 
<thrift.skip-making-executable>true</thrift.skip-making-executable>
                 <thrift.exec-cmd.executable>echo</thrift.exec-cmd.executable>
@@ -1031,7 +1038,9 @@
             </activation>
             <properties>
                 <os.classifier>linux-x86_64</os.classifier>
-                
<thrift.download-url>https://github.com/apache/iotdb-bin-resources/raw/main/compile-tools/thrift-0.14-ubuntu</thrift.download-url>
+                <thrift.download-url>
+                    
https://github.com/apache/iotdb-bin-resources/raw/main/compile-tools/thrift-0.14-ubuntu
+                </thrift.download-url>
                 <thrift.executable>thrift_0.14.1_linux.exe</thrift.executable>
                 
<thrift.skip-making-executable>false</thrift.skip-making-executable>
                 <thrift.exec-cmd.executable>chmod</thrift.exec-cmd.executable>
@@ -1047,7 +1056,9 @@
             </activation>
             <properties>
                 <os.classifier>mac-x86_64</os.classifier>
-                
<thrift.download-url>https://github.com/apache/iotdb-bin-resources/raw/main/compile-tools/thrift-0.14-MacOS</thrift.download-url>
+                <thrift.download-url>
+                    
https://github.com/apache/iotdb-bin-resources/raw/main/compile-tools/thrift-0.14-MacOS
+                </thrift.download-url>
                 <thrift.executable>thrift_0.14.1_mac.exe</thrift.executable>
                 
<thrift.skip-making-executable>false</thrift.skip-making-executable>
                 <thrift.exec-cmd.executable>chmod</thrift.exec-cmd.executable>
@@ -1097,7 +1108,8 @@
                 </file>
             </activation>
             <properties>
-                
<thrift.exec.absolute.path>${project.build.directory}/tools/${thrift.executable}</thrift.exec.absolute.path>
+                
<thrift.exec.absolute.path>${project.build.directory}/tools/${thrift.executable}
+                </thrift.exec.absolute.path>
             </properties>
             <build>
                 <plugins>
@@ -1166,7 +1178,8 @@
                                     <generator>py</generator>
                                     
<thriftExecutable>${thrift.exec.absolute.path}</thriftExecutable>
                                     
<thriftSourceRoot>${basedir}/src/main/thrift</thriftSourceRoot>
-                                    
<outputDirectory>${project.build.directory}/generated-sources-python/</outputDirectory>
+                                    
<outputDirectory>${project.build.directory}/generated-sources-python/
+                                    </outputDirectory>
                                 </configuration>
                             </execution>
                             <execution>
@@ -1318,7 +1331,9 @@
                                     <goal>prepare-agent</goal>
                                 </goals>
                                 <configuration>
-                                    
<destFile>${project.build.directory}/${project.build.finalName}-jacoco-unit-tests.exec</destFile>
+                                    <destFile>
+                                        
${project.build.directory}/${project.build.finalName}-jacoco-unit-tests.exec
+                                    </destFile>
                                     
<propertyName>surefire.jacoco.args</propertyName>
                                 </configuration>
                             </execution>
@@ -1331,7 +1346,9 @@
                                     <goal>check</goal>
                                 </goals>
                                 <configuration>
-                                    
<dataFile>${project.build.directory}/${project.build.finalName}-jacoco-unit-tests.exec</dataFile>
+                                    <dataFile>
+                                        
${project.build.directory}/${project.build.finalName}-jacoco-unit-tests.exec
+                                    </dataFile>
                                     
<outputDirectory>${project.build.directory}/jacoco-unit-reports</outputDirectory>
                                 </configuration>
                             </execution>
@@ -1343,7 +1360,9 @@
                                     <goal>prepare-agent</goal>
                                 </goals>
                                 <configuration>
-                                    
<destFile>${project.build.directory}/${project.build.finalName}-jacoco-integration-tests.exec</destFile>
+                                    <destFile>
+                                        
${project.build.directory}/${project.build.finalName}-jacoco-integration-tests.exec
+                                    </destFile>
                                     
<propertyName>failsafe.jacoco.args</propertyName>
                                 </configuration>
                             </execution>
@@ -1355,8 +1374,11 @@
                                     <goal>check</goal>
                                 </goals>
                                 <configuration>
-                                    
<dataFile>${project.build.directory}/${project.build.finalName}-jacoco-integration-tests.exec</dataFile>
-                                    
<outputDirectory>${project.build.directory}/jacoco-integration-reports</outputDirectory>
+                                    <dataFile>
+                                        
${project.build.directory}/${project.build.finalName}-jacoco-integration-tests.exec
+                                    </dataFile>
+                                    
<outputDirectory>${project.build.directory}/jacoco-integration-reports
+                                    </outputDirectory>
                                 </configuration>
                             </execution>
                             <execution>
@@ -1374,7 +1396,8 @@
                                             </includes>
                                         </fileSet>
                                     </fileSets>
-                                    
<destFile>${project.build.directory}/${project.build.finalName}-merged.exec</destFile>
+                                    
<destFile>${project.build.directory}/${project.build.finalName}-merged.exec
+                                    </destFile>
                                 </configuration>
                             </execution>
                             <execution>
@@ -1385,7 +1408,8 @@
                                     <goal>check</goal>
                                 </goals>
                                 <configuration>
-                                    
<dataFile>${project.build.directory}/${project.build.finalName}-merged.exec</dataFile>
+                                    
<dataFile>${project.build.directory}/${project.build.finalName}-merged.exec
+                                    </dataFile>
                                     
<outputDirectory>${project.build.directory}/jacoco-merged-reports</outputDirectory>
                                 </configuration>
                             </execution>
diff --git 
a/tsfile/src/main/java/org/apache/iotdb/tsfile/read/TsFileSequenceReader.java 
b/tsfile/src/main/java/org/apache/iotdb/tsfile/read/TsFileSequenceReader.java
index aee2d92..d174ca0 100644
--- 
a/tsfile/src/main/java/org/apache/iotdb/tsfile/read/TsFileSequenceReader.java
+++ 
b/tsfile/src/main/java/org/apache/iotdb/tsfile/read/TsFileSequenceReader.java
@@ -1136,6 +1136,7 @@ public class TsFileSequenceReader implements 
AutoCloseable {
         }
         // last chunk group Metadata
         chunkGroupMetadataList.add(new ChunkGroupMetadata(lastDeviceId, 
chunkMetadataList));
+        lastDeviceId = null;
       }
       truncatedSize = this.position() - 1;
     } catch (Exception e) {
@@ -1145,12 +1146,16 @@ public class TsFileSequenceReader implements 
AutoCloseable {
           this.position(),
           e.getMessage());
     }
-    if (loadLastChunkMetadata
-        && !chunkGroupMetadataList
-            .get(chunkGroupMetadataList.size() - 1)
-            .getDevice()
-            .equals(lastDeviceId)) {
-      chunkGroupMetadataList.add(new ChunkGroupMetadata(lastDeviceId, 
chunkMetadataList));
+    if (loadLastChunkMetadata && lastDeviceId != null) {
+      // add last chunk group metadata list
+      if (chunkMetadataList.size() > 0) {
+        chunkGroupMetadataList.add(new ChunkGroupMetadata(lastDeviceId, 
chunkMetadataList));
+        if (newSchema != null) {
+          for (MeasurementSchema tsSchema : measurementSchemaList) {
+            newSchema.putIfAbsent(new Path(lastDeviceId, 
tsSchema.getMeasurementId()), tsSchema);
+          }
+        }
+      }
     }
     // Despite the completeness of the data section, we will discard current 
FileMetadata
     // so that we can continue to write data into this tsfile.
diff --git 
a/tsfile/src/main/java/org/apache/iotdb/tsfile/write/writer/TsFileIOWriter.java 
b/tsfile/src/main/java/org/apache/iotdb/tsfile/write/writer/TsFileIOWriter.java
index d0818f3..b009d09 100644
--- 
a/tsfile/src/main/java/org/apache/iotdb/tsfile/write/writer/TsFileIOWriter.java
+++ 
b/tsfile/src/main/java/org/apache/iotdb/tsfile/write/writer/TsFileIOWriter.java
@@ -377,11 +377,11 @@ public class TsFileIOWriter {
     out.close();
   }
 
-  void writeSeparatorMaskForTest() throws IOException {
+  public void writeSeparatorMaskForTest() throws IOException {
     out.write(new byte[] {MetaMarker.SEPARATOR});
   }
 
-  void writeChunkGroupMarkerForTest() throws IOException {
+  public void writeChunkGroupMarkerForTest() throws IOException {
     out.write(new byte[] {MetaMarker.CHUNK_GROUP_HEADER});
   }
 
diff --git 
a/tsfile/src/test/java/org/apache/iotdb/tsfile/read/TsFileSequenceReaderSelfCheckTest.java
 
b/tsfile/src/test/java/org/apache/iotdb/tsfile/read/TsFileSequenceReaderSelfCheckTest.java
new file mode 100644
index 0000000..ac8274f
--- /dev/null
+++ 
b/tsfile/src/test/java/org/apache/iotdb/tsfile/read/TsFileSequenceReaderSelfCheckTest.java
@@ -0,0 +1,799 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.iotdb.tsfile.read;
+
+import org.apache.iotdb.tsfile.common.conf.TSFileConfig;
+import org.apache.iotdb.tsfile.constant.TestConstant;
+import org.apache.iotdb.tsfile.file.MetaMarker;
+import org.apache.iotdb.tsfile.file.metadata.ChunkGroupMetadata;
+import org.apache.iotdb.tsfile.file.metadata.enums.CompressionType;
+import org.apache.iotdb.tsfile.file.metadata.enums.TSDataType;
+import org.apache.iotdb.tsfile.file.metadata.enums.TSEncoding;
+import org.apache.iotdb.tsfile.file.metadata.statistics.FloatStatistics;
+import org.apache.iotdb.tsfile.read.common.Path;
+import org.apache.iotdb.tsfile.utils.TsFileGeneratorForTest;
+import org.apache.iotdb.tsfile.write.TsFileWriter;
+import org.apache.iotdb.tsfile.write.record.TSRecord;
+import org.apache.iotdb.tsfile.write.record.datapoint.FloatDataPoint;
+import org.apache.iotdb.tsfile.write.schema.MeasurementSchema;
+
+import org.junit.After;
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Test;
+
+import java.io.File;
+import java.io.FileWriter;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+public class TsFileSequenceReaderSelfCheckTest {
+  private static final String FILE_NAME =
+      TestConstant.BASE_OUTPUT_PATH.concat(System.currentTimeMillis() + 
"-1-0-0.tsfile");
+  File file = new File(FILE_NAME);
+
+  @Before
+  public void setUp() throws IOException {
+    if (!file.getParentFile().exists()) {
+      Assert.assertTrue(file.getParentFile().mkdirs());
+    }
+    if (file.exists()) {
+      Assert.assertTrue(file.delete());
+    }
+  }
+
+  @After
+  public void tearDown() {
+    if (file.exists()) {
+      Assert.assertTrue(file.delete());
+    }
+  }
+
+  // File with wrong head magic string
+  @Test
+  public void testCheckWithBadHeadMagic() throws Exception {
+    try (FileWriter writer = new FileWriter(file)) {
+      writer.write("TEST_STRING");
+    }
+    try (TsFileSequenceReader reader = new 
TsFileSequenceReader(file.getPath())) {
+      Map<Path, MeasurementSchema> newSchema = new HashMap<>();
+      List<ChunkGroupMetadata> chunkGroupMetadataList = new ArrayList<>();
+      long truncateSize = reader.selfCheck(newSchema, chunkGroupMetadataList, 
false, true);
+      Assert.assertEquals(truncateSize, TsFileCheckStatus.INCOMPATIBLE_FILE);
+      Assert.assertEquals(newSchema.size(), 0);
+      Assert.assertEquals(chunkGroupMetadataList.size(), 0);
+    }
+  }
+
+  // test file with magic string and version number
+  @Test
+  public void testCheckWithOnlyHeadMagic() throws Exception {
+    TsFileWriter writer = new TsFileWriter(file);
+    // we have to flush using inner API.
+    writer.close();
+    try (TsFileSequenceReader reader = new 
TsFileSequenceReader(file.getPath())) {
+      Map<Path, MeasurementSchema> newSchema = new HashMap<>();
+      List<ChunkGroupMetadata> chunkGroupMetadataList = new ArrayList<>();
+      long checkStatus = reader.selfCheck(newSchema, chunkGroupMetadataList, 
false, true);
+      Assert.assertEquals(checkStatus, 
TSFileConfig.MAGIC_STRING.getBytes().length + Byte.BYTES);
+      Assert.assertEquals(newSchema.size(), 0);
+      Assert.assertEquals(chunkGroupMetadataList.size(), 0);
+    }
+  }
+
+  // test file with magic string, version number and a marker of ChunkHeader
+  @Test
+  public void testCheckWithOnlyFirstMask() throws Exception {
+    TsFileWriter writer = new TsFileWriter(file);
+    // we have to flush using inner API.
+    writer.getIOWriter().getIOWriterOut().write(new byte[] 
{MetaMarker.CHUNK_HEADER});
+    writer.getIOWriter().close();
+    try (TsFileSequenceReader reader = new 
TsFileSequenceReader(file.getPath())) {
+      Map<Path, MeasurementSchema> newSchema = new HashMap<>();
+      List<ChunkGroupMetadata> chunkGroupMetadataList = new ArrayList<>();
+      long checkStatus = reader.selfCheck(newSchema, chunkGroupMetadataList, 
false, true);
+      Assert.assertEquals(checkStatus, 
TSFileConfig.MAGIC_STRING.getBytes().length + Byte.BYTES);
+      Assert.assertEquals(newSchema.size(), 0);
+      Assert.assertEquals(chunkGroupMetadataList.size(), 0);
+    }
+  }
+
+  // Test file with magic string, version number, marker of ChunkHeader, 
incomplete chunk header
+  @Test
+  public void testCheckWithIncompleteChunkHeader() throws Exception {
+    TsFileGeneratorForTest.writeFileWithOneIncompleteChunkHeader(file);
+    try (TsFileSequenceReader reader = new 
TsFileSequenceReader(file.getPath())) {
+      Map<Path, MeasurementSchema> newSchema = new HashMap<>();
+      List<ChunkGroupMetadata> chunkGroupMetadataList = new ArrayList<>();
+      long checkStatus = reader.selfCheck(newSchema, chunkGroupMetadataList, 
false, true);
+      Assert.assertEquals(checkStatus, 
TSFileConfig.MAGIC_STRING.getBytes().length + Byte.BYTES);
+      Assert.assertEquals(newSchema.size(), 0);
+      Assert.assertEquals(chunkGroupMetadataList.size(), 0);
+    }
+  }
+
+  // Test file with magic string, version number, marker of ChunkHeader, one 
chunkHeader
+  // And do not load the last chunk group metadata
+  @Test
+  public void testCheckWithOnlyOneChunkHeader() throws Exception {
+    TsFileWriter writer = new TsFileWriter(file);
+    writer.getIOWriter().startChunkGroup("root.sg1.d1");
+    writer
+        .getIOWriter()
+        .startFlushChunk(
+            new MeasurementSchema("s1", TSDataType.FLOAT, TSEncoding.PLAIN),
+            CompressionType.SNAPPY,
+            TSDataType.FLOAT,
+            TSEncoding.PLAIN,
+            new FloatStatistics(),
+            100,
+            10);
+    writer.getIOWriter().close();
+
+    try (TsFileSequenceReader reader = new 
TsFileSequenceReader(file.getPath())) {
+      Map<Path, MeasurementSchema> newSchema = new HashMap<>();
+      List<ChunkGroupMetadata> chunkGroupMetadataList = new ArrayList<>();
+      long checkStatus = reader.selfCheck(newSchema, chunkGroupMetadataList, 
false, false);
+      Assert.assertEquals(checkStatus, 
TSFileConfig.MAGIC_STRING.getBytes().length + Byte.BYTES);
+      Assert.assertEquals(newSchema.size(), 0);
+      Assert.assertEquals(chunkGroupMetadataList.size(), 0);
+    }
+  }
+
+  // Test file with magic string, version number, marker of ChunkHeader, one 
chunkHeader
+  // And load the last chunk group metadata
+  @Test
+  public void testCheckWithOnlyOneChunkHeaderAndLoadLastChunkGroupMetadata() 
throws Exception {
+    TsFileWriter writer = new TsFileWriter(file);
+    writer.getIOWriter().startChunkGroup("root.sg1.d1");
+    writer
+        .getIOWriter()
+        .startFlushChunk(
+            new MeasurementSchema("s1", TSDataType.FLOAT, TSEncoding.PLAIN),
+            CompressionType.SNAPPY,
+            TSDataType.FLOAT,
+            TSEncoding.PLAIN,
+            new FloatStatistics(),
+            100,
+            10);
+    writer.getIOWriter().close();
+
+    try (TsFileSequenceReader reader = new 
TsFileSequenceReader(file.getPath())) {
+      Map<Path, MeasurementSchema> newSchema = new HashMap<>();
+      List<ChunkGroupMetadata> chunkGroupMetadataList = new ArrayList<>();
+      long checkStatus = reader.selfCheck(newSchema, chunkGroupMetadataList, 
false, true);
+      Assert.assertEquals(checkStatus, 
TSFileConfig.MAGIC_STRING.getBytes().length + Byte.BYTES);
+      Assert.assertEquals(newSchema.size(), 0);
+      Assert.assertEquals(chunkGroupMetadataList.size(), 0);
+    }
+  }
+
+  /*
+   * test check with tsfile with one ChunkGroup, and not marker behind this 
chunk group
+   */
+  @Test
+  public void testCheckWithOnlyOneChunkGroupAndNoMarker() throws Exception {
+    TsFileWriter writer = new TsFileWriter(file);
+    writer.registerTimeseries(
+        new Path("d1", "s1"), new MeasurementSchema("s1", TSDataType.FLOAT, 
TSEncoding.RLE));
+    writer.registerTimeseries(
+        new Path("d1", "s2"), new MeasurementSchema("s2", TSDataType.FLOAT, 
TSEncoding.RLE));
+    writer.write(
+        new TSRecord(1, "d1")
+            .addTuple(new FloatDataPoint("s1", 5))
+            .addTuple(new FloatDataPoint("s2", 4)));
+    writer.write(
+        new TSRecord(2, "d1")
+            .addTuple(new FloatDataPoint("s1", 5))
+            .addTuple(new FloatDataPoint("s2", 4)));
+    writer.flushAllChunkGroups();
+    writer.getIOWriter().close();
+    try (TsFileSequenceReader reader = new 
TsFileSequenceReader(file.getPath())) {
+      Map<Path, MeasurementSchema> newSchema = new HashMap<>();
+      List<ChunkGroupMetadata> chunkGroupMetadataList = new ArrayList<>();
+      long checkStatus = reader.selfCheck(newSchema, chunkGroupMetadataList, 
false, false);
+      Assert.assertEquals(TSFileConfig.MAGIC_STRING.getBytes().length + 
Byte.BYTES, checkStatus);
+      Assert.assertEquals(newSchema.size(), 0);
+      Assert.assertEquals(chunkGroupMetadataList.size(), 0);
+    }
+  }
+
+  /*
+   * test check with tsfile with one ChunkGroup, and not marker behind this 
chunk group. And load the last chunk group metadata.
+   */
+  @Test
+  public void 
testCheckWithOnlyOneChunkGroupAndNoMarkerAndLoadLastChunkGroupMetadata()
+      throws Exception {
+    TsFileWriter writer = new TsFileWriter(file);
+    writer.registerTimeseries(
+        new Path("d1", "s1"), new MeasurementSchema("s1", TSDataType.FLOAT, 
TSEncoding.RLE));
+    writer.registerTimeseries(
+        new Path("d1", "s2"), new MeasurementSchema("s2", TSDataType.FLOAT, 
TSEncoding.RLE));
+    writer.write(
+        new TSRecord(1, "d1")
+            .addTuple(new FloatDataPoint("s1", 5))
+            .addTuple(new FloatDataPoint("s2", 4)));
+    writer.write(
+        new TSRecord(2, "d1")
+            .addTuple(new FloatDataPoint("s1", 5))
+            .addTuple(new FloatDataPoint("s2", 4)));
+    writer.flushAllChunkGroups();
+    writer.getIOWriter().close();
+    try (TsFileSequenceReader reader = new 
TsFileSequenceReader(file.getPath())) {
+      Map<Path, MeasurementSchema> newSchema = new HashMap<>();
+      List<ChunkGroupMetadata> chunkGroupMetadataList = new ArrayList<>();
+      long checkStatus = reader.selfCheck(newSchema, chunkGroupMetadataList, 
false, true);
+      Assert.assertEquals(TSFileConfig.MAGIC_STRING.getBytes().length + 
Byte.BYTES, checkStatus);
+      Assert.assertEquals(newSchema.size(), 2);
+      Assert.assertEquals(chunkGroupMetadataList.size(), 1);
+      ChunkGroupMetadata chunkGroupMetadata = chunkGroupMetadataList.get(0);
+      Assert.assertEquals(chunkGroupMetadata.getChunkMetadataList().size(), 2);
+    }
+  }
+
+  /** Test check with one complete chunk group and marker */
+  @Test
+  public void testCheckWithOneCompleteChunkGroupAndMarker() throws Exception {
+    TsFileWriter writer = new TsFileWriter(file);
+    writer.registerTimeseries(
+        new Path("d1", "s1"), new MeasurementSchema("s1", TSDataType.FLOAT, 
TSEncoding.RLE));
+    writer.registerTimeseries(
+        new Path("d1", "s2"), new MeasurementSchema("s2", TSDataType.FLOAT, 
TSEncoding.RLE));
+    writer.write(
+        new TSRecord(1, "d1")
+            .addTuple(new FloatDataPoint("s1", 5))
+            .addTuple(new FloatDataPoint("s2", 4)));
+    writer.write(
+        new TSRecord(2, "d1")
+            .addTuple(new FloatDataPoint("s1", 5))
+            .addTuple(new FloatDataPoint("s2", 4)));
+    writer.flushAllChunkGroups();
+    long pos = writer.getIOWriter().getPos();
+    writer.getIOWriter().writeChunkGroupMarkerForTest();
+    writer.getIOWriter().close();
+    try (TsFileSequenceReader reader = new 
TsFileSequenceReader(file.getPath())) {
+      Map<Path, MeasurementSchema> newSchema = new HashMap<>();
+      List<ChunkGroupMetadata> chunkGroupMetadataList = new ArrayList<>();
+      long checkStatus = reader.selfCheck(newSchema, chunkGroupMetadataList, 
false, false);
+      Assert.assertEquals(pos, checkStatus);
+      Assert.assertEquals(newSchema.size(), 2);
+      Assert.assertEquals(chunkGroupMetadataList.size(), 1);
+      ChunkGroupMetadata chunkGroupMetadata = chunkGroupMetadataList.get(0);
+      Assert.assertEquals(chunkGroupMetadata.getChunkMetadataList().size(), 2);
+    }
+  }
+
+  /** Test check with one complete chunk group and marker, and load the last 
chunk group */
+  @Test
+  public void 
testCheckWithOneCompleteChunkGroupAndMarkerAndLoadLastChunkGroup() throws 
Exception {
+    TsFileWriter writer = new TsFileWriter(file);
+    writer.registerTimeseries(
+        new Path("d1", "s1"), new MeasurementSchema("s1", TSDataType.FLOAT, 
TSEncoding.RLE));
+    writer.registerTimeseries(
+        new Path("d1", "s2"), new MeasurementSchema("s2", TSDataType.FLOAT, 
TSEncoding.RLE));
+    writer.write(
+        new TSRecord(1, "d1")
+            .addTuple(new FloatDataPoint("s1", 5))
+            .addTuple(new FloatDataPoint("s2", 4)));
+    writer.write(
+        new TSRecord(2, "d1")
+            .addTuple(new FloatDataPoint("s1", 5))
+            .addTuple(new FloatDataPoint("s2", 4)));
+    writer.flushAllChunkGroups();
+    long pos = writer.getIOWriter().getPos();
+    writer.getIOWriter().writeChunkGroupMarkerForTest();
+    writer.getIOWriter().close();
+    try (TsFileSequenceReader reader = new 
TsFileSequenceReader(file.getPath())) {
+      Map<Path, MeasurementSchema> newSchema = new HashMap<>();
+      List<ChunkGroupMetadata> chunkGroupMetadataList = new ArrayList<>();
+      long checkStatus = reader.selfCheck(newSchema, chunkGroupMetadataList, 
false, true);
+      Assert.assertEquals(pos, checkStatus);
+      Assert.assertEquals(newSchema.size(), 2);
+      Assert.assertEquals(chunkGroupMetadataList.size(), 1);
+      ChunkGroupMetadata chunkGroupMetadata = chunkGroupMetadataList.get(0);
+      Assert.assertEquals(chunkGroupMetadata.getChunkMetadataList().size(), 2);
+    }
+  }
+
+  /*
+   * Test file with magic string, version number, marker of ChunkHeader, one 
complete chunk group, one chunk group with the last chunk uncompleted. And do 
not load the last chunk metadata.
+   */
+  @Test
+  public void testCheckWithOneCompleteChunkGroupAndOneUncompletedChunkGroup() 
throws Exception {
+    TsFileWriter writer = new TsFileWriter(file);
+    writer.registerTimeseries(
+        new Path("d1", "s1"), new MeasurementSchema("s1", TSDataType.FLOAT, 
TSEncoding.RLE));
+    writer.registerTimeseries(
+        new Path("d1", "s2"), new MeasurementSchema("s2", TSDataType.FLOAT, 
TSEncoding.RLE));
+    writer.write(
+        new TSRecord(1, "d1")
+            .addTuple(new FloatDataPoint("s1", 5))
+            .addTuple(new FloatDataPoint("s2", 4)));
+    writer.write(
+        new TSRecord(2, "d1")
+            .addTuple(new FloatDataPoint("s1", 5))
+            .addTuple(new FloatDataPoint("s2", 4)));
+    writer.flushAllChunkGroups();
+    long pos1 = writer.getIOWriter().getPos();
+    writer.registerTimeseries(
+        new Path("d2", "s1"), new MeasurementSchema("s1", TSDataType.FLOAT, 
TSEncoding.RLE));
+    writer.registerTimeseries(
+        new Path("d2", "s2"), new MeasurementSchema("s2", TSDataType.FLOAT, 
TSEncoding.RLE));
+    writer.write(
+        new TSRecord(1, "d1")
+            .addTuple(new FloatDataPoint("s1", 5))
+            .addTuple(new FloatDataPoint("s2", 4)));
+    writer.flushAllChunkGroups();
+    long pos2 = writer.getIOWriter().getPos();
+    // let's delete one byte. the version is broken
+    writer.getIOWriter().getIOWriterOut().truncate(pos2 - 1);
+    writer.getIOWriter().close();
+
+    try (TsFileSequenceReader reader = new 
TsFileSequenceReader(file.getPath())) {
+      Map<Path, MeasurementSchema> newSchema = new HashMap<>();
+      List<ChunkGroupMetadata> chunkGroupMetadataList = new ArrayList<>();
+      long checkStatus = reader.selfCheck(newSchema, chunkGroupMetadataList, 
false, false);
+      Assert.assertEquals(checkStatus, pos1);
+      Assert.assertEquals(newSchema.size(), 2);
+      Assert.assertEquals(chunkGroupMetadataList.size(), 1);
+      ChunkGroupMetadata chunkGroupMetadata = chunkGroupMetadataList.get(0);
+      Assert.assertEquals(chunkGroupMetadata.getChunkMetadataList().size(), 2);
+    }
+  }
+
+  /*
+   * Test file with magic string, version number, marker of ChunkHeader, one 
complete chunk group, one chunk group with the last chunk uncompleted. And load 
the last chunk metadata.
+   */
+  @Test
+  public void
+      
testCheckWithOneCompleteChunkGroupAndOneUncompletedChunkGroupAndLoadLastChunkGroupMetadata()
+          throws Exception {
+    TsFileWriter writer = new TsFileWriter(file);
+    writer.registerTimeseries(
+        new Path("d1", "s1"), new MeasurementSchema("s1", TSDataType.FLOAT, 
TSEncoding.RLE));
+    writer.registerTimeseries(
+        new Path("d1", "s2"), new MeasurementSchema("s2", TSDataType.FLOAT, 
TSEncoding.RLE));
+    writer.write(
+        new TSRecord(1, "d1")
+            .addTuple(new FloatDataPoint("s1", 5))
+            .addTuple(new FloatDataPoint("s2", 4)));
+    writer.write(
+        new TSRecord(2, "d1")
+            .addTuple(new FloatDataPoint("s1", 5))
+            .addTuple(new FloatDataPoint("s2", 4)));
+    writer.flushAllChunkGroups();
+    long pos1 = writer.getIOWriter().getPos();
+    writer.registerTimeseries(
+        new Path("d2", "s1"), new MeasurementSchema("s1", TSDataType.FLOAT, 
TSEncoding.RLE));
+    writer.registerTimeseries(
+        new Path("d2", "s2"), new MeasurementSchema("s2", TSDataType.FLOAT, 
TSEncoding.RLE));
+    writer.write(
+        new TSRecord(1, "d1")
+            .addTuple(new FloatDataPoint("s1", 5))
+            .addTuple(new FloatDataPoint("s2", 4)));
+    writer.flushAllChunkGroups();
+    long pos2 = writer.getIOWriter().getPos();
+    // let's delete one byte. the version is broken
+    writer.getIOWriter().getIOWriterOut().truncate(pos2 - 1);
+    writer.getIOWriter().close();
+
+    try (TsFileSequenceReader reader = new 
TsFileSequenceReader(file.getPath())) {
+      Map<Path, MeasurementSchema> newSchema = new HashMap<>();
+      List<ChunkGroupMetadata> chunkGroupMetadataList = new ArrayList<>();
+      long checkStatus = reader.selfCheck(newSchema, chunkGroupMetadataList, 
false, true);
+      Assert.assertEquals(checkStatus, pos1);
+      Assert.assertEquals(newSchema.size(), 2);
+      Assert.assertEquals(chunkGroupMetadataList.size(), 2);
+      ChunkGroupMetadata firstChunkGroupMetadata = 
chunkGroupMetadataList.get(0);
+      ChunkGroupMetadata secondChunkGroupMetadata = 
chunkGroupMetadataList.get(1);
+      // the first chunk group contains the chunk metadata for two chunks
+      Assert.assertEquals(2, 
firstChunkGroupMetadata.getChunkMetadataList().size());
+      // the second chunk group contains only chunk metadata for the first 
chunk
+      Assert.assertEquals(1, 
secondChunkGroupMetadata.getChunkMetadataList().size());
+    }
+  }
+  /*
+   * Test check with two chunk group and marker
+   */
+  @Test
+  public void testCheckWithTwoChunkGroupAndMarker() throws Exception {
+    TsFileWriter writer = new TsFileWriter(file);
+    writer.registerTimeseries(
+        new Path("d1", "s1"), new MeasurementSchema("s1", TSDataType.FLOAT, 
TSEncoding.RLE));
+    writer.registerTimeseries(
+        new Path("d1", "s2"), new MeasurementSchema("s2", TSDataType.FLOAT, 
TSEncoding.RLE));
+    writer.registerTimeseries(
+        new Path("d2", "s1"), new MeasurementSchema("s1", TSDataType.FLOAT, 
TSEncoding.RLE));
+    writer.registerTimeseries(
+        new Path("d2", "s2"), new MeasurementSchema("s2", TSDataType.FLOAT, 
TSEncoding.RLE));
+    writer.write(
+        new TSRecord(1, "d1")
+            .addTuple(new FloatDataPoint("s1", 5))
+            .addTuple(new FloatDataPoint("s2", 4)));
+    writer.write(
+        new TSRecord(2, "d1")
+            .addTuple(new FloatDataPoint("s1", 5))
+            .addTuple(new FloatDataPoint("s2", 4)));
+
+    writer.write(
+        new TSRecord(1, "d2")
+            .addTuple(new FloatDataPoint("s1", 6))
+            .addTuple(new FloatDataPoint("s2", 4)));
+    writer.write(
+        new TSRecord(2, "d2")
+            .addTuple(new FloatDataPoint("s1", 6))
+            .addTuple(new FloatDataPoint("s2", 4)));
+    writer.flushAllChunkGroups();
+    long pos1 = writer.getIOWriter().getPos();
+    writer.getIOWriter().writeChunkGroupMarkerForTest();
+    writer.getIOWriter().close();
+    try (TsFileSequenceReader reader = new 
TsFileSequenceReader(file.getPath())) {
+      Map<Path, MeasurementSchema> newSchema = new HashMap<>();
+      List<ChunkGroupMetadata> chunkGroupMetadataList = new ArrayList<>();
+      long checkStatus = reader.selfCheck(newSchema, chunkGroupMetadataList, 
false, false);
+      Assert.assertEquals(checkStatus, pos1);
+      Assert.assertEquals(newSchema.size(), 4);
+      Assert.assertEquals(chunkGroupMetadataList.size(), 2);
+      ChunkGroupMetadata firstChunkGroupMetadata = 
chunkGroupMetadataList.get(0);
+      ChunkGroupMetadata secondChunkGroupMetadata = 
chunkGroupMetadataList.get(1);
+      // the first chunk group contains the chunk metadata for two chunks
+      Assert.assertEquals(2, 
firstChunkGroupMetadata.getChunkMetadataList().size());
+      // the second chunk group contains only chunk metadata for the first 
chunk
+      Assert.assertEquals(2, 
secondChunkGroupMetadata.getChunkMetadataList().size());
+    }
+  }
+
+  /*
+   * Test check with two chunk group and marker, and load last chunk group
+   */
+  @Test
+  public void testCheckWithTwoChunkGroupAndMarkerAndLoadLastChunkGroup() 
throws Exception {
+    TsFileWriter writer = new TsFileWriter(file);
+    writer.registerTimeseries(
+        new Path("d1", "s1"), new MeasurementSchema("s1", TSDataType.FLOAT, 
TSEncoding.RLE));
+    writer.registerTimeseries(
+        new Path("d1", "s2"), new MeasurementSchema("s2", TSDataType.FLOAT, 
TSEncoding.RLE));
+    writer.registerTimeseries(
+        new Path("d2", "s1"), new MeasurementSchema("s1", TSDataType.FLOAT, 
TSEncoding.RLE));
+    writer.registerTimeseries(
+        new Path("d2", "s2"), new MeasurementSchema("s2", TSDataType.FLOAT, 
TSEncoding.RLE));
+    writer.write(
+        new TSRecord(1, "d1")
+            .addTuple(new FloatDataPoint("s1", 5))
+            .addTuple(new FloatDataPoint("s2", 4)));
+    writer.write(
+        new TSRecord(2, "d1")
+            .addTuple(new FloatDataPoint("s1", 5))
+            .addTuple(new FloatDataPoint("s2", 4)));
+
+    writer.write(
+        new TSRecord(1, "d2")
+            .addTuple(new FloatDataPoint("s1", 6))
+            .addTuple(new FloatDataPoint("s2", 4)));
+    writer.write(
+        new TSRecord(2, "d2")
+            .addTuple(new FloatDataPoint("s1", 6))
+            .addTuple(new FloatDataPoint("s2", 4)));
+    writer.flushAllChunkGroups();
+    long pos1 = writer.getIOWriter().getPos();
+    writer.getIOWriter().writeChunkGroupMarkerForTest();
+    writer.getIOWriter().close();
+    try (TsFileSequenceReader reader = new 
TsFileSequenceReader(file.getPath())) {
+      Map<Path, MeasurementSchema> newSchema = new HashMap<>();
+      List<ChunkGroupMetadata> chunkGroupMetadataList = new ArrayList<>();
+      long checkStatus = reader.selfCheck(newSchema, chunkGroupMetadataList, 
false, true);
+      Assert.assertEquals(checkStatus, pos1);
+      Assert.assertEquals(newSchema.size(), 4);
+      Assert.assertEquals(chunkGroupMetadataList.size(), 2);
+      ChunkGroupMetadata firstChunkGroupMetadata = 
chunkGroupMetadataList.get(0);
+      ChunkGroupMetadata secondChunkGroupMetadata = 
chunkGroupMetadataList.get(1);
+      // the first chunk group contains the chunk metadata for two chunks
+      Assert.assertEquals(2, 
firstChunkGroupMetadata.getChunkMetadataList().size());
+      // the second chunk group contains only chunk metadata for the first 
chunk
+      Assert.assertEquals(2, 
secondChunkGroupMetadata.getChunkMetadataList().size());
+    }
+  }
+
+  /*
+   * Test check with two chunk group, and no marker behind the last chunk group
+   */
+  @Test
+  public void testCheckWithTwoChunkGroupAndNotMarkerBehind() throws Exception {
+    TsFileWriter writer = new TsFileWriter(file);
+    writer.registerTimeseries(
+        new Path("d1", "s1"), new MeasurementSchema("s1", TSDataType.FLOAT, 
TSEncoding.RLE));
+    writer.registerTimeseries(
+        new Path("d1", "s2"), new MeasurementSchema("s2", TSDataType.FLOAT, 
TSEncoding.RLE));
+    writer.registerTimeseries(
+        new Path("d2", "s1"), new MeasurementSchema("s1", TSDataType.FLOAT, 
TSEncoding.RLE));
+    writer.registerTimeseries(
+        new Path("d2", "s2"), new MeasurementSchema("s2", TSDataType.FLOAT, 
TSEncoding.RLE));
+    writer.write(
+        new TSRecord(1, "d1")
+            .addTuple(new FloatDataPoint("s1", 5))
+            .addTuple(new FloatDataPoint("s2", 4)));
+    writer.write(
+        new TSRecord(2, "d1")
+            .addTuple(new FloatDataPoint("s1", 5))
+            .addTuple(new FloatDataPoint("s2", 4)));
+
+    writer.write(
+        new TSRecord(1, "d2")
+            .addTuple(new FloatDataPoint("s1", 6))
+            .addTuple(new FloatDataPoint("s2", 4)));
+    writer.write(
+        new TSRecord(2, "d2")
+            .addTuple(new FloatDataPoint("s1", 6))
+            .addTuple(new FloatDataPoint("s2", 4)));
+    writer.flushAllChunkGroups();
+    long pos1 = writer.getIOWriter().getPos();
+    writer.getIOWriter().writeSeparatorMaskForTest();
+    writer.getIOWriter().close();
+    try (TsFileSequenceReader reader = new 
TsFileSequenceReader(file.getPath())) {
+      Map<Path, MeasurementSchema> newSchema = new HashMap<>();
+      List<ChunkGroupMetadata> chunkGroupMetadataList = new ArrayList<>();
+      long checkStatus = reader.selfCheck(newSchema, chunkGroupMetadataList, 
false, false);
+      Assert.assertEquals(checkStatus, pos1);
+      Assert.assertEquals(newSchema.size(), 4);
+      Assert.assertEquals(chunkGroupMetadataList.size(), 2);
+      ChunkGroupMetadata firstChunkGroupMetadata = 
chunkGroupMetadataList.get(0);
+      ChunkGroupMetadata secondChunkGroupMetadata = 
chunkGroupMetadataList.get(1);
+      // the first chunk group contains the chunk metadata for two chunks
+      Assert.assertEquals(2, 
firstChunkGroupMetadata.getChunkMetadataList().size());
+      // the second chunk group contains only chunk metadata for the first 
chunk
+      Assert.assertEquals(2, 
secondChunkGroupMetadata.getChunkMetadataList().size());
+    }
+  }
+
+  /**
+   * Test check with two chunk group, and no marker behind the last chunk 
group, and load the last
+   * chunk group metadata
+   */
+  @Test
+  public void 
testCheckWithTwoChunkGroupAndNotMarkerBehindAndLoadLastChunkGroupMetadata()
+      throws Exception {
+    TsFileWriter writer = new TsFileWriter(file);
+    writer.registerTimeseries(
+        new Path("d1", "s1"), new MeasurementSchema("s1", TSDataType.FLOAT, 
TSEncoding.RLE));
+    writer.registerTimeseries(
+        new Path("d1", "s2"), new MeasurementSchema("s2", TSDataType.FLOAT, 
TSEncoding.RLE));
+    writer.registerTimeseries(
+        new Path("d2", "s1"), new MeasurementSchema("s1", TSDataType.FLOAT, 
TSEncoding.RLE));
+    writer.registerTimeseries(
+        new Path("d2", "s2"), new MeasurementSchema("s2", TSDataType.FLOAT, 
TSEncoding.RLE));
+    writer.write(
+        new TSRecord(1, "d1")
+            .addTuple(new FloatDataPoint("s1", 5))
+            .addTuple(new FloatDataPoint("s2", 4)));
+    writer.write(
+        new TSRecord(2, "d1")
+            .addTuple(new FloatDataPoint("s1", 5))
+            .addTuple(new FloatDataPoint("s2", 4)));
+
+    writer.write(
+        new TSRecord(1, "d2")
+            .addTuple(new FloatDataPoint("s1", 6))
+            .addTuple(new FloatDataPoint("s2", 4)));
+    writer.write(
+        new TSRecord(2, "d2")
+            .addTuple(new FloatDataPoint("s1", 6))
+            .addTuple(new FloatDataPoint("s2", 4)));
+    writer.flushAllChunkGroups();
+    long pos1 = writer.getIOWriter().getPos();
+    writer.getIOWriter().writeSeparatorMaskForTest();
+    writer.getIOWriter().close();
+    try (TsFileSequenceReader reader = new 
TsFileSequenceReader(file.getPath())) {
+      Map<Path, MeasurementSchema> newSchema = new HashMap<>();
+      List<ChunkGroupMetadata> chunkGroupMetadataList = new ArrayList<>();
+      long checkStatus = reader.selfCheck(newSchema, chunkGroupMetadataList, 
false, true);
+      Assert.assertEquals(checkStatus, pos1);
+      Assert.assertEquals(newSchema.size(), 4);
+      Assert.assertEquals(chunkGroupMetadataList.size(), 2);
+      ChunkGroupMetadata firstChunkGroupMetadata = 
chunkGroupMetadataList.get(0);
+      ChunkGroupMetadata secondChunkGroupMetadata = 
chunkGroupMetadataList.get(1);
+      // the first chunk group contains the chunk metadata for two chunks
+      Assert.assertEquals(2, 
firstChunkGroupMetadata.getChunkMetadataList().size());
+      // the second chunk group contains only chunk metadata for the first 
chunk
+      Assert.assertEquals(2, 
secondChunkGroupMetadata.getChunkMetadataList().size());
+    }
+  }
+
+  /**
+   * Test check with two chunk group, and a marker of FileMetadata
+   *
+   * @throws Exception
+   */
+  @Test
+  public void testCheckWithTwoChunkGroupAndFileMetadataMarker() throws 
Exception {
+    TsFileWriter writer = new TsFileWriter(file);
+    writer.registerTimeseries(
+        new Path("d1", "s1"), new MeasurementSchema("s1", TSDataType.FLOAT, 
TSEncoding.RLE));
+    writer.registerTimeseries(
+        new Path("d1", "s2"), new MeasurementSchema("s2", TSDataType.FLOAT, 
TSEncoding.RLE));
+    writer.registerTimeseries(
+        new Path("d2", "s1"), new MeasurementSchema("s1", TSDataType.FLOAT, 
TSEncoding.RLE));
+    writer.registerTimeseries(
+        new Path("d2", "s2"), new MeasurementSchema("s2", TSDataType.FLOAT, 
TSEncoding.RLE));
+    writer.write(
+        new TSRecord(1, "d1")
+            .addTuple(new FloatDataPoint("s1", 5))
+            .addTuple(new FloatDataPoint("s2", 4)));
+    writer.write(
+        new TSRecord(2, "d1")
+            .addTuple(new FloatDataPoint("s1", 5))
+            .addTuple(new FloatDataPoint("s2", 4)));
+
+    writer.write(
+        new TSRecord(1, "d2")
+            .addTuple(new FloatDataPoint("s1", 6))
+            .addTuple(new FloatDataPoint("s2", 4)));
+    writer.write(
+        new TSRecord(2, "d2")
+            .addTuple(new FloatDataPoint("s1", 6))
+            .addTuple(new FloatDataPoint("s2", 4)));
+    writer.flushAllChunkGroups();
+    long pos1 = writer.getIOWriter().getPos();
+    writer.getIOWriter().writeSeparatorMaskForTest();
+    writer.getIOWriter().writeSeparatorMaskForTest();
+    writer.getIOWriter().close();
+    try (TsFileSequenceReader reader = new 
TsFileSequenceReader(file.getPath())) {
+      Map<Path, MeasurementSchema> newSchema = new HashMap<>();
+      List<ChunkGroupMetadata> chunkGroupMetadataList = new ArrayList<>();
+      long checkStatus = reader.selfCheck(newSchema, chunkGroupMetadataList, 
false, false);
+      Assert.assertEquals(checkStatus, pos1);
+      Assert.assertEquals(newSchema.size(), 4);
+      Assert.assertEquals(chunkGroupMetadataList.size(), 2);
+      ChunkGroupMetadata firstChunkGroupMetadata = 
chunkGroupMetadataList.get(0);
+      ChunkGroupMetadata secondChunkGroupMetadata = 
chunkGroupMetadataList.get(1);
+      // the first chunk group contains the chunk metadata for two chunks
+      Assert.assertEquals(2, 
firstChunkGroupMetadata.getChunkMetadataList().size());
+      // the second chunk group contains only chunk metadata for the first 
chunk
+      Assert.assertEquals(2, 
secondChunkGroupMetadata.getChunkMetadataList().size());
+    }
+  }
+
+  /**
+   * Test check with two chunk group, and a marker of FileMetadata, and load 
the last chunk group
+   * metadata
+   *
+   * @throws Exception
+   */
+  @Test
+  public void 
testCheckWithTwoChunkGroupAndFileMetadataMarkerAndLoadLastChunkGroup()
+      throws Exception {
+    TsFileWriter writer = new TsFileWriter(file);
+    writer.registerTimeseries(
+        new Path("d1", "s1"), new MeasurementSchema("s1", TSDataType.FLOAT, 
TSEncoding.RLE));
+    writer.registerTimeseries(
+        new Path("d1", "s2"), new MeasurementSchema("s2", TSDataType.FLOAT, 
TSEncoding.RLE));
+    writer.registerTimeseries(
+        new Path("d2", "s1"), new MeasurementSchema("s1", TSDataType.FLOAT, 
TSEncoding.RLE));
+    writer.registerTimeseries(
+        new Path("d2", "s2"), new MeasurementSchema("s2", TSDataType.FLOAT, 
TSEncoding.RLE));
+    writer.write(
+        new TSRecord(1, "d1")
+            .addTuple(new FloatDataPoint("s1", 5))
+            .addTuple(new FloatDataPoint("s2", 4)));
+    writer.write(
+        new TSRecord(2, "d1")
+            .addTuple(new FloatDataPoint("s1", 5))
+            .addTuple(new FloatDataPoint("s2", 4)));
+
+    writer.write(
+        new TSRecord(1, "d2")
+            .addTuple(new FloatDataPoint("s1", 6))
+            .addTuple(new FloatDataPoint("s2", 4)));
+    writer.write(
+        new TSRecord(2, "d2")
+            .addTuple(new FloatDataPoint("s1", 6))
+            .addTuple(new FloatDataPoint("s2", 4)));
+    writer.flushAllChunkGroups();
+    long pos1 = writer.getIOWriter().getPos();
+    writer.getIOWriter().writeSeparatorMaskForTest();
+    writer.getIOWriter().writeSeparatorMaskForTest();
+    writer.getIOWriter().close();
+    try (TsFileSequenceReader reader = new 
TsFileSequenceReader(file.getPath())) {
+      Map<Path, MeasurementSchema> newSchema = new HashMap<>();
+      List<ChunkGroupMetadata> chunkGroupMetadataList = new ArrayList<>();
+      long checkStatus = reader.selfCheck(newSchema, chunkGroupMetadataList, 
false, true);
+      Assert.assertEquals(checkStatus, pos1);
+      Assert.assertEquals(newSchema.size(), 4);
+      Assert.assertEquals(chunkGroupMetadataList.size(), 2);
+      ChunkGroupMetadata firstChunkGroupMetadata = 
chunkGroupMetadataList.get(0);
+      ChunkGroupMetadata secondChunkGroupMetadata = 
chunkGroupMetadataList.get(1);
+      // the first chunk group contains the chunk metadata for two chunks
+      Assert.assertEquals(2, 
firstChunkGroupMetadata.getChunkMetadataList().size());
+      // the second chunk group contains only chunk metadata for the first 
chunk
+      Assert.assertEquals(2, 
secondChunkGroupMetadata.getChunkMetadataList().size());
+    }
+  }
+
+  /**
+   * Test check with a complete file
+   *
+   * @throws Exception
+   */
+  @Test
+  public void testCheckWithCompleteFile() throws Exception {
+    TsFileWriter writer = new TsFileWriter(file);
+    writer.registerTimeseries(
+        new Path("d1", "s1"), new MeasurementSchema("s1", TSDataType.FLOAT, 
TSEncoding.RLE));
+    writer.registerTimeseries(
+        new Path("d1", "s2"), new MeasurementSchema("s2", TSDataType.FLOAT, 
TSEncoding.RLE));
+    writer.write(
+        new TSRecord(1, "d1")
+            .addTuple(new FloatDataPoint("s1", 5))
+            .addTuple(new FloatDataPoint("s2", 4)));
+    writer.write(
+        new TSRecord(2, "d1")
+            .addTuple(new FloatDataPoint("s1", 5))
+            .addTuple(new FloatDataPoint("s2", 4)));
+    writer.flushAllChunkGroups();
+    long pos = writer.getIOWriter().getPos();
+    writer.close();
+    try (TsFileSequenceReader reader = new 
TsFileSequenceReader(file.getPath())) {
+      Map<Path, MeasurementSchema> newSchema = new HashMap<>();
+      List<ChunkGroupMetadata> chunkGroupMetadataList = new ArrayList<>();
+      long checkStatus = reader.selfCheck(newSchema, chunkGroupMetadataList, 
false, false);
+      Assert.assertEquals(checkStatus, pos);
+      Assert.assertEquals(newSchema.size(), 2);
+      Assert.assertEquals(chunkGroupMetadataList.size(), 1);
+      ChunkGroupMetadata firstChunkGroupMetadata = 
chunkGroupMetadataList.get(0);
+      Assert.assertEquals(2, 
firstChunkGroupMetadata.getChunkMetadataList().size());
+    }
+  }
+
+  /**
+   * Test check with a complete file, and load last chunk group metadata
+   *
+   * @throws Exception
+   */
+  @Test
+  public void testCheckWithCompleteFileAndLoadLastChunkGroupMetadata() throws 
Exception {
+    TsFileWriter writer = new TsFileWriter(file);
+    writer.registerTimeseries(
+        new Path("d1", "s1"), new MeasurementSchema("s1", TSDataType.FLOAT, 
TSEncoding.RLE));
+    writer.registerTimeseries(
+        new Path("d1", "s2"), new MeasurementSchema("s2", TSDataType.FLOAT, 
TSEncoding.RLE));
+    writer.write(
+        new TSRecord(1, "d1")
+            .addTuple(new FloatDataPoint("s1", 5))
+            .addTuple(new FloatDataPoint("s2", 4)));
+    writer.write(
+        new TSRecord(2, "d1")
+            .addTuple(new FloatDataPoint("s1", 5))
+            .addTuple(new FloatDataPoint("s2", 4)));
+    writer.flushAllChunkGroups();
+    long pos = writer.getIOWriter().getPos();
+    writer.close();
+    try (TsFileSequenceReader reader = new 
TsFileSequenceReader(file.getPath())) {
+      Map<Path, MeasurementSchema> newSchema = new HashMap<>();
+      List<ChunkGroupMetadata> chunkGroupMetadataList = new ArrayList<>();
+      long checkStatus = reader.selfCheck(newSchema, chunkGroupMetadataList, 
false, true);
+      Assert.assertEquals(checkStatus, pos);
+      Assert.assertEquals(newSchema.size(), 2);
+      Assert.assertEquals(chunkGroupMetadataList.size(), 1);
+      ChunkGroupMetadata firstChunkGroupMetadata = 
chunkGroupMetadataList.get(0);
+      Assert.assertEquals(2, 
firstChunkGroupMetadata.getChunkMetadataList().size());
+    }
+  }
+}

Reply via email to