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

rong 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 e17d9eb1795 [IOTDB-6249] Load: Streaming read timeseries metadata from 
tsfile when auto creating or verifying schema (#11540)
e17d9eb1795 is described below

commit e17d9eb1795a8fff9b344323b95a58cbe85cf167
Author: Itami Sho <[email protected]>
AuthorDate: Tue Nov 21 20:31:09 2023 +0800

    [IOTDB-6249] Load: Streaming read timeseries metadata from tsfile when auto 
creating or verifying schema (#11540)
---
 .../plan/analyze/LoadTsfileAnalyzer.java           | 225 ++++++++-------------
 ...eReaderTimeseriesMetadataIteratorException.java |  27 +++
 .../file/metadata/enums/MetadataIndexNodeType.java |   2 +-
 ...leSequenceReaderTimeseriesMetadataIterator.java | 220 ++++++++++++++++++++
 ...quenceReaderTimeseriesMetadataIteratorTest.java |  62 ++++++
 5 files changed, 391 insertions(+), 145 deletions(-)

diff --git 
a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/analyze/LoadTsfileAnalyzer.java
 
b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/analyze/LoadTsfileAnalyzer.java
index 2ef08186e88..ee1ad09336c 100644
--- 
a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/analyze/LoadTsfileAnalyzer.java
+++ 
b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/analyze/LoadTsfileAnalyzer.java
@@ -34,7 +34,6 @@ import 
org.apache.iotdb.commons.service.metric.PerformanceOverviewMetrics;
 import org.apache.iotdb.confignode.rpc.thrift.TGetDatabaseReq;
 import org.apache.iotdb.confignode.rpc.thrift.TShowDatabaseResp;
 import org.apache.iotdb.db.auth.AuthorityChecker;
-import org.apache.iotdb.db.conf.IoTDBConfig;
 import org.apache.iotdb.db.conf.IoTDBDescriptor;
 import org.apache.iotdb.db.exception.LoadFileException;
 import org.apache.iotdb.db.exception.LoadReadOnlyException;
@@ -68,6 +67,7 @@ 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.read.TsFileSequenceReader;
+import 
org.apache.iotdb.tsfile.read.TsFileSequenceReaderTimeseriesMetadataIterator;
 import org.apache.iotdb.tsfile.utils.Pair;
 import org.apache.iotdb.tsfile.write.schema.MeasurementSchema;
 
@@ -82,7 +82,6 @@ import java.util.Arrays;
 import java.util.Collections;
 import java.util.HashMap;
 import java.util.HashSet;
-import java.util.Iterator;
 import java.util.List;
 import java.util.Map;
 import java.util.Set;
@@ -92,8 +91,6 @@ public class LoadTsfileAnalyzer {
 
   private static final Logger LOGGER = 
LoggerFactory.getLogger(LoadTsfileAnalyzer.class);
 
-  private static final IoTDBConfig CONFIG = 
IoTDBDescriptor.getInstance().getConfig();
-
   private static final IClientManager<ConfigRegionId, ConfigNodeClient> 
CONFIG_NODE_CLIENT_MANAGER =
       ConfigNodeClientManager.getInstance();
 
@@ -188,42 +185,50 @@ public class LoadTsfileAnalyzer {
   private void analyzeSingleTsFile(File tsFile) throws IOException, 
AuthException {
     try (final TsFileSequenceReader reader = new 
TsFileSequenceReader(tsFile.getAbsolutePath())) {
       // can be reused when constructing tsfile resource
-      final Map<String, List<TimeseriesMetadata>> device2TimeseriesMetadata =
-          reader.getAllTimeseriesMetadata(true);
-      if (device2TimeseriesMetadata.isEmpty()) {
-        LOGGER.warn("device2TimeseriesMetadata is empty, because maybe the 
tsfile is empty");
-        return;
-      }
-
-      // auto create or verify schema
-      if (IoTDBDescriptor.getInstance().getConfig().isAutoCreateSchemaEnabled()
-          || loadTsFileStatement.isVerifySchema()) {
-
-        final TimeSeriesIterator timeSeriesIterator =
-            new TimeSeriesIterator(tsFile, device2TimeseriesMetadata);
-        while (timeSeriesIterator.hasNext()) {
-          schemaAutoCreatorAndVerifier.autoCreateAndVerify(reader, 
timeSeriesIterator);
-        }
+      final TsFileSequenceReaderTimeseriesMetadataIterator 
timeseriesMetadataIterator =
+          new TsFileSequenceReaderTimeseriesMetadataIterator(reader, true);
 
-        
schemaAutoCreatorAndVerifier.flushAndClearDeviceIsAlignedCacheIfNecessary();
-      }
+      long writePointCount = 0;
 
       // construct tsfile resource
       final TsFileResource tsFileResource = new TsFileResource(tsFile);
       if (!tsFileResource.resourceFileExists()) {
         // it will be serialized in LoadSingleTsFileNode
-        FileLoaderUtils.updateTsFileResource(device2TimeseriesMetadata, 
tsFileResource);
         tsFileResource.updatePlanIndexes(reader.getMinPlanIndex());
         tsFileResource.updatePlanIndexes(reader.getMaxPlanIndex());
       } else {
         tsFileResource.deserialize();
       }
 
+      // auto create or verify schema
+      if (IoTDBDescriptor.getInstance().getConfig().isAutoCreateSchemaEnabled()
+          || loadTsFileStatement.isVerifySchema()) {
+        // check if the tsfile is empty
+        if (!timeseriesMetadataIterator.hasNext()) {
+          LOGGER.warn("device2TimeseriesMetadata is empty, because maybe the 
tsfile is empty");
+          return;
+        }
+
+        while (timeseriesMetadataIterator.hasNext()) {
+          Map<String, List<TimeseriesMetadata>> device2TimeseriesMetadata =
+              timeseriesMetadataIterator.next();
+
+          schemaAutoCreatorAndVerifier.autoCreateAndVerify(reader, 
device2TimeseriesMetadata);
+
+          if (!tsFileResource.resourceFileExists()) {
+            FileLoaderUtils.updateTsFileResource(device2TimeseriesMetadata, 
tsFileResource);
+          }
+          writePointCount += getWritePointCount(device2TimeseriesMetadata);
+        }
+
+        
schemaAutoCreatorAndVerifier.flushAndClearDeviceIsAlignedCacheIfNecessary();
+      }
+
       
TimestampPrecisionUtils.checkTimestampPrecision(tsFileResource.getFileEndTime());
       tsFileResource.setStatus(TsFileResourceStatus.NORMAL);
 
       loadTsFileStatement.addTsFileResource(tsFileResource);
-      
loadTsFileStatement.addWritePointCount(getWritePointCount(device2TimeseriesMetadata));
+      loadTsFileStatement.addWritePointCount(writePointCount);
     }
   }
 
@@ -234,142 +239,76 @@ public class LoadTsfileAnalyzer {
         .sum();
   }
 
-  private static final class TimeSeriesIterator
-      implements Iterator<Pair<String, TimeseriesMetadata>> {
-
-    private static final Logger LOGGER = 
LoggerFactory.getLogger(TimeSeriesIterator.class);
-
-    private static final long LOG_PRINT_INTERVAL = 10000;
-    private long returnedTimeseriesCount = 0;
-    private boolean lastLogHasPrinted = false;
-
-    private final File tsFile;
-    private final Iterator<Map.Entry<String, List<TimeseriesMetadata>>>
-        device2TimeseriesMetadataIterator;
-
-    private String currentDevice;
-    private Iterator<TimeseriesMetadata> timeseriesMetadataIterator;
-
-    public TimeSeriesIterator(
-        File tsFile, Map<String, List<TimeseriesMetadata>> 
device2TimeseriesMetadata) {
-      this.tsFile = tsFile;
-      this.device2TimeseriesMetadataIterator = 
device2TimeseriesMetadata.entrySet().iterator();
-    }
-
-    @Override
-    public boolean hasNext() {
-      if (timeseriesMetadataIterator == null || 
!timeseriesMetadataIterator.hasNext()) {
-        if (device2TimeseriesMetadataIterator.hasNext()) {
-          final Map.Entry<String, List<TimeseriesMetadata>> next =
-              device2TimeseriesMetadataIterator.next();
-          currentDevice = next.getKey();
-          timeseriesMetadataIterator = next.getValue().iterator();
-        } else {
-          if (!lastLogHasPrinted) {
-            LOGGER.info(
-                "Analyzing TsFile {}, all {} timeseries has been returned to 
analyzer.",
-                tsFile.getAbsolutePath(),
-                returnedTimeseriesCount);
-            lastLogHasPrinted = true;
-          }
-          return false;
-        }
-      }
-      return timeseriesMetadataIterator.hasNext();
-    }
-
-    @Override
-    public Pair<String, TimeseriesMetadata> next() {
-      if (returnedTimeseriesCount == 0) {
-        LOGGER.info(
-            "Analyzing TsFile {}, start to return timeseries to analyzer.",
-            tsFile.getAbsolutePath());
-      } else if (returnedTimeseriesCount % LOG_PRINT_INTERVAL == 0) {
-        LOGGER.info(
-            "Analyzing TsFile {}, until now {} timeseries has been returned to 
analyzer.",
-            tsFile.getAbsolutePath(),
-            returnedTimeseriesCount);
-      }
-      returnedTimeseriesCount++;
-
-      return new Pair<>(currentDevice, timeseriesMetadataIterator.next());
-    }
-  }
-
   private final class SchemaAutoCreatorAndVerifier {
 
     private final Map<String, Boolean> tsfileDevice2IsAligned = new 
HashMap<>();
-
-    private final int maxTimeseriesNumberPerBatch = 
CONFIG.getMaxLoadingTimeseriesNumber();
     private final Map<String, Set<MeasurementSchema>> 
currentBatchDevice2TimeseriesSchemas =
         new HashMap<>();
-    private int currentBatchTimeseriesCount = 0;
 
     private final Set<PartialPath> alreadySetDatabases = new HashSet<>();
 
     private SchemaAutoCreatorAndVerifier() {}
 
     public void autoCreateAndVerify(
-        TsFileSequenceReader reader, TimeSeriesIterator timeSeriesIterator)
+        TsFileSequenceReader reader,
+        Map<String, List<TimeseriesMetadata>> device2TimeseriesMetadataList)
         throws IOException, AuthException {
-      while (currentBatchTimeseriesCount < maxTimeseriesNumberPerBatch
-          && timeSeriesIterator.hasNext()) {
-        final Pair<String, TimeseriesMetadata> pair = 
timeSeriesIterator.next();
-        final String device = pair.left;
-        final TimeseriesMetadata timeseriesMetadata = pair.right;
-        final TSDataType dataType = timeseriesMetadata.getTsDataType();
-        if (dataType.equals(TSDataType.VECTOR)) {
-          tsfileDevice2IsAligned.put(device, true);
-
-          // not a timeseries, skip ++currentBatchTimeseriesCount
-        } else {
-          // check WRITE_DATA permission of timeseries
-          long startTime = System.nanoTime();
-          try {
-            String userName = context.getSession().getUserName();
-            if (!AuthorityChecker.SUPER_USER.equals(userName)) {
-              TSStatus status;
-              try {
-                List<PartialPath> paths =
-                    Collections.singletonList(
-                        new PartialPath(device, 
timeseriesMetadata.getMeasurementId()));
-                status =
-                    AuthorityChecker.getTSStatus(
-                        AuthorityChecker.checkFullPathListPermission(
-                            userName, paths, 
PrivilegeType.WRITE_DATA.ordinal()),
-                        paths,
-                        PrivilegeType.WRITE_DATA);
-              } catch (IllegalPathException e) {
-                throw new RuntimeException(e);
-              }
-              if (status.getCode() != 
TSStatusCode.SUCCESS_STATUS.getStatusCode()) {
-                throw new AuthException(
-                    TSStatusCode.representOf(status.getCode()), 
status.getMessage());
+      for (final Map.Entry<String, List<TimeseriesMetadata>> entry :
+          device2TimeseriesMetadataList.entrySet()) {
+        final String device = entry.getKey();
+
+        for (final TimeseriesMetadata timeseriesMetadata : entry.getValue()) {
+          final TSDataType dataType = timeseriesMetadata.getTsDataType();
+          if (TSDataType.VECTOR.equals(dataType)) {
+            tsfileDevice2IsAligned.put(device, true);
+
+            // not a timeseries, skip
+          } else {
+            // check WRITE_DATA permission of timeseries
+            long startTime = System.nanoTime();
+            try {
+              String userName = context.getSession().getUserName();
+              if (!AuthorityChecker.SUPER_USER.equals(userName)) {
+                TSStatus status;
+                try {
+                  List<PartialPath> paths =
+                      Collections.singletonList(
+                          new PartialPath(device, 
timeseriesMetadata.getMeasurementId()));
+                  status =
+                      AuthorityChecker.getTSStatus(
+                          AuthorityChecker.checkFullPathListPermission(
+                              userName, paths, 
PrivilegeType.WRITE_DATA.ordinal()),
+                          paths,
+                          PrivilegeType.WRITE_DATA);
+                } catch (IllegalPathException e) {
+                  throw new RuntimeException(e);
+                }
+                if (status.getCode() != 
TSStatusCode.SUCCESS_STATUS.getStatusCode()) {
+                  throw new AuthException(
+                      TSStatusCode.representOf(status.getCode()), 
status.getMessage());
+                }
               }
+            } finally {
+              PerformanceOverviewMetrics.getInstance()
+                  .recordAuthCost(System.nanoTime() - startTime);
             }
-          } finally {
-            
PerformanceOverviewMetrics.getInstance().recordAuthCost(System.nanoTime() - 
startTime);
+            final Pair<CompressionType, TSEncoding> compressionEncodingPair =
+                
reader.readTimeseriesCompressionTypeAndEncoding(timeseriesMetadata);
+            currentBatchDevice2TimeseriesSchemas
+                .computeIfAbsent(device, o -> new HashSet<>())
+                .add(
+                    new MeasurementSchema(
+                        timeseriesMetadata.getMeasurementId(),
+                        dataType,
+                        compressionEncodingPair.getRight(),
+                        compressionEncodingPair.getLeft()));
+
+            tsfileDevice2IsAligned.putIfAbsent(device, false);
           }
-          final Pair<CompressionType, TSEncoding> compressionEncodingPair =
-              
reader.readTimeseriesCompressionTypeAndEncoding(timeseriesMetadata);
-          currentBatchDevice2TimeseriesSchemas
-              .computeIfAbsent(device, o -> new HashSet<>())
-              .add(
-                  new MeasurementSchema(
-                      timeseriesMetadata.getMeasurementId(),
-                      dataType,
-                      compressionEncodingPair.getRight(),
-                      compressionEncodingPair.getLeft()));
-
-          tsfileDevice2IsAligned.putIfAbsent(device, false);
-
-          ++currentBatchTimeseriesCount;
         }
       }
 
-      if (currentBatchTimeseriesCount == maxTimeseriesNumberPerBatch) {
-        flush();
-      }
+      flush();
     }
 
     /**
@@ -389,7 +328,6 @@ public class LoadTsfileAnalyzer {
       doAutoCreateAndVerify();
 
       currentBatchDevice2TimeseriesSchemas.clear();
-      currentBatchTimeseriesCount = 0;
     }
 
     private void doAutoCreateAndVerify() throws SemanticException {
@@ -662,7 +600,6 @@ public class LoadTsfileAnalyzer {
     public void clear() {
       tsfileDevice2IsAligned.clear();
       currentBatchDevice2TimeseriesSchemas.clear();
-      currentBatchTimeseriesCount = 0;
       alreadySetDatabases.clear();
     }
   }
diff --git 
a/iotdb-core/tsfile/src/main/java/org/apache/iotdb/tsfile/exception/TsFileSequenceReaderTimeseriesMetadataIteratorException.java
 
b/iotdb-core/tsfile/src/main/java/org/apache/iotdb/tsfile/exception/TsFileSequenceReaderTimeseriesMetadataIteratorException.java
new file mode 100644
index 00000000000..a7e2dfc9042
--- /dev/null
+++ 
b/iotdb-core/tsfile/src/main/java/org/apache/iotdb/tsfile/exception/TsFileSequenceReaderTimeseriesMetadataIteratorException.java
@@ -0,0 +1,27 @@
+/*
+ * 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.exception;
+
+public class TsFileSequenceReaderTimeseriesMetadataIteratorException extends 
RuntimeException {
+
+  public TsFileSequenceReaderTimeseriesMetadataIteratorException(String 
message) {
+    super(message);
+  }
+}
diff --git 
a/iotdb-core/tsfile/src/main/java/org/apache/iotdb/tsfile/file/metadata/enums/MetadataIndexNodeType.java
 
b/iotdb-core/tsfile/src/main/java/org/apache/iotdb/tsfile/file/metadata/enums/MetadataIndexNodeType.java
index 20fdc46c3b8..b117a0b632a 100644
--- 
a/iotdb-core/tsfile/src/main/java/org/apache/iotdb/tsfile/file/metadata/enums/MetadataIndexNodeType.java
+++ 
b/iotdb-core/tsfile/src/main/java/org/apache/iotdb/tsfile/file/metadata/enums/MetadataIndexNodeType.java
@@ -41,7 +41,7 @@ public enum MetadataIndexNodeType {
   /** INTERNAL_MEASUREMENT. */
   INTERNAL_MEASUREMENT((byte) 2),
 
-  /** INTERNAL_MEASUREMENT. */
+  /** LEAF_MEASUREMENT. */
   LEAF_MEASUREMENT((byte) 3);
 
   private final byte type;
diff --git 
a/iotdb-core/tsfile/src/main/java/org/apache/iotdb/tsfile/read/TsFileSequenceReaderTimeseriesMetadataIterator.java
 
b/iotdb-core/tsfile/src/main/java/org/apache/iotdb/tsfile/read/TsFileSequenceReaderTimeseriesMetadataIterator.java
new file mode 100644
index 00000000000..7648e402e97
--- /dev/null
+++ 
b/iotdb-core/tsfile/src/main/java/org/apache/iotdb/tsfile/read/TsFileSequenceReaderTimeseriesMetadataIterator.java
@@ -0,0 +1,220 @@
+/*
+ * 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.exception.TsFileSequenceReaderTimeseriesMetadataIteratorException;
+import org.apache.iotdb.tsfile.file.metadata.MetadataIndexEntry;
+import org.apache.iotdb.tsfile.file.metadata.MetadataIndexNode;
+import org.apache.iotdb.tsfile.file.metadata.TimeseriesMetadata;
+import org.apache.iotdb.tsfile.file.metadata.enums.MetadataIndexNodeType;
+
+import java.io.IOException;
+import java.nio.ByteBuffer;
+import java.util.ArrayDeque;
+import java.util.ArrayList;
+import java.util.Deque;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.NoSuchElementException;
+
+public class TsFileSequenceReaderTimeseriesMetadataIterator
+    implements Iterator<Map<String, List<TimeseriesMetadata>>> {
+
+  private static final int MAX_TIMESERIES_METADATA_COUNT = 2000;
+  private final TsFileSequenceReader reader;
+  private final boolean needChunkMetadata;
+  private ByteBuffer currentBuffer = null;
+  private final Deque<MetadataIndexEntryInfo> metadataIndexEntryStack = new 
ArrayDeque<>();
+  private String currentDeviceId;
+  private int currentTimeseriesMetadataCount = 0;
+
+  public TsFileSequenceReaderTimeseriesMetadataIterator(
+      TsFileSequenceReader reader, boolean needChunkMetadata) throws 
IOException {
+    this.reader = reader;
+
+    if (this.reader.tsFileMetaData == null) {
+      this.reader.readFileMetadata();
+    }
+
+    final MetadataIndexNode metadataIndexNode = 
reader.tsFileMetaData.getMetadataIndex();
+    long curEntryEndOffset = metadataIndexNode.getEndOffset();
+    List<MetadataIndexEntry> metadataIndexEntryList = 
metadataIndexNode.getChildren();
+    this.needChunkMetadata = needChunkMetadata;
+
+    for (int i = metadataIndexEntryList.size() - 1; i >= 0; i--) {
+      metadataIndexEntryStack.push(
+          new MetadataIndexEntryInfo(
+              metadataIndexEntryList.get(i), metadataIndexNode.getNodeType(), 
curEntryEndOffset));
+      curEntryEndOffset = metadataIndexEntryList.get(i).getOffset();
+    }
+  }
+
+  @Override
+  public boolean hasNext() {
+    return !metadataIndexEntryStack.isEmpty()
+        || (currentBuffer != null && currentBuffer.hasRemaining());
+  }
+
+  @Override
+  public Map<String, List<TimeseriesMetadata>> next() {
+    if (!hasNext()) {
+      throw new NoSuchElementException();
+    }
+
+    final Map<String, List<TimeseriesMetadata>> timeseriesMetadataMap = new 
HashMap<>();
+
+    while (currentTimeseriesMetadataCount < MAX_TIMESERIES_METADATA_COUNT) {
+      // 1. Check Buffer
+      // currentTimeseriesMetadataCount has reached the limit in the previous
+      // loop and maybe there is still some data that remains in the buffer.
+      if (currentBuffer != null && currentBuffer.hasRemaining()) {
+        timeseriesMetadataMap
+            .computeIfAbsent(currentDeviceId, k -> new ArrayList<>())
+            .addAll(deserializeTimeseriesMetadata());
+      }
+
+      if (currentTimeseriesMetadataCount >= MAX_TIMESERIES_METADATA_COUNT
+          || metadataIndexEntryStack.isEmpty()) {
+        break;
+      }
+
+      // 2. Deserialize MetadataIndexEntry
+      final MetadataIndexEntryInfo indexEntryInfo = 
metadataIndexEntryStack.pop();
+
+      try {
+        deserializeMetadataIndexEntry(indexEntryInfo, timeseriesMetadataMap);
+      } catch (IOException e) {
+        throw new TsFileSequenceReaderTimeseriesMetadataIteratorException(
+            String.format(
+                "TsFileSequenceReaderTimeseriesMetadataIterator: 
deserializeMetadataIndexEntry failed, "
+                    + "MetadataIndexEntryInfo: %s, "
+                    + e.getMessage(),
+                indexEntryInfo));
+      }
+    }
+
+    // 3. Reset currentTimeseriesMetadataCount
+    if (currentTimeseriesMetadataCount >= MAX_TIMESERIES_METADATA_COUNT) {
+      currentTimeseriesMetadataCount = 0;
+    }
+
+    return timeseriesMetadataMap;
+  }
+
+  private void deserializeMetadataIndexEntry(
+      MetadataIndexEntryInfo metadataIndexEntryInfo,
+      Map<String, List<TimeseriesMetadata>> timeseriesMetadataMap)
+      throws IOException {
+    if (metadataIndexEntryInfo
+        .getMetadataIndexNodeType()
+        .equals(MetadataIndexNodeType.LEAF_MEASUREMENT)) {
+      deserializeLeafMeasurement(
+          metadataIndexEntryInfo.getMetadataIndexEntry(),
+          metadataIndexEntryInfo.getEndOffset(),
+          timeseriesMetadataMap);
+
+    } else {
+      deserializeInternalNode(
+          metadataIndexEntryInfo.getMetadataIndexEntry(),
+          metadataIndexEntryInfo.getEndOffset(),
+          metadataIndexEntryInfo
+              .getMetadataIndexNodeType()
+              .equals(MetadataIndexNodeType.LEAF_DEVICE));
+    }
+  }
+
+  private void deserializeLeafMeasurement(
+      MetadataIndexEntry metadataIndexEntry,
+      long endOffset,
+      Map<String, List<TimeseriesMetadata>> timeseriesMetadataMap)
+      throws IOException {
+    if (currentBuffer != null && currentBuffer.hasRemaining()) {
+      throw new TsFileSequenceReaderTimeseriesMetadataIteratorException(
+          "currentBuffer still has some data left before 
deserializeLeafMeasurement");
+    }
+
+    currentBuffer = reader.readData(metadataIndexEntry.getOffset(), endOffset);
+
+    timeseriesMetadataMap
+        .computeIfAbsent(currentDeviceId, k -> new ArrayList<>())
+        .addAll(deserializeTimeseriesMetadata());
+  }
+
+  private List<TimeseriesMetadata> deserializeTimeseriesMetadata() {
+    final List<TimeseriesMetadata> timeseriesMetadataList = new ArrayList<>();
+    while (currentBuffer.hasRemaining()
+        && currentTimeseriesMetadataCount < MAX_TIMESERIES_METADATA_COUNT) {
+      timeseriesMetadataList.add(
+          TimeseriesMetadata.deserializeFrom(currentBuffer, 
needChunkMetadata));
+      currentTimeseriesMetadataCount++;
+    }
+    return timeseriesMetadataList;
+  }
+
+  private void deserializeInternalNode(
+      MetadataIndexEntry metadataIndexEntry, long endOffset, boolean 
isLeafDevice)
+      throws IOException {
+    if (isLeafDevice) {
+      currentDeviceId = metadataIndexEntry.getName();
+    }
+
+    final MetadataIndexNode metadataIndexNode =
+        MetadataIndexNode.deserializeFrom(
+            reader.readData(metadataIndexEntry.getOffset(), endOffset));
+    MetadataIndexNodeType metadataIndexNodeType = 
metadataIndexNode.getNodeType();
+    List<MetadataIndexEntry> children = metadataIndexNode.getChildren();
+    long curEntryEndOffset = metadataIndexNode.getEndOffset();
+
+    for (int i = children.size() - 1; i >= 0; i--) {
+      metadataIndexEntryStack.push(
+          new MetadataIndexEntryInfo(children.get(i), metadataIndexNodeType, 
curEntryEndOffset));
+      curEntryEndOffset = children.get(i).getOffset();
+    }
+  }
+
+  private static class MetadataIndexEntryInfo {
+    private final MetadataIndexEntry metadataIndexEntry;
+    private final MetadataIndexNodeType metadataIndexNodeType;
+    private final long endOffset;
+
+    public MetadataIndexEntryInfo(
+        MetadataIndexEntry metadataIndexEntry,
+        MetadataIndexNodeType metadataIndexNodeType,
+        long endOffset) {
+      this.metadataIndexEntry = metadataIndexEntry;
+      this.metadataIndexNodeType = metadataIndexNodeType;
+      this.endOffset = endOffset;
+    }
+
+    public MetadataIndexEntry getMetadataIndexEntry() {
+      return metadataIndexEntry;
+    }
+
+    public MetadataIndexNodeType getMetadataIndexNodeType() {
+      return metadataIndexNodeType;
+    }
+
+    public long getEndOffset() {
+      return endOffset;
+    }
+  }
+}
diff --git 
a/iotdb-core/tsfile/src/test/java/org/apache/iotdb/tsfile/read/TsFileSequenceReaderTimeseriesMetadataIteratorTest.java
 
b/iotdb-core/tsfile/src/test/java/org/apache/iotdb/tsfile/read/TsFileSequenceReaderTimeseriesMetadataIteratorTest.java
new file mode 100644
index 00000000000..54ddaabf03c
--- /dev/null
+++ 
b/iotdb-core/tsfile/src/test/java/org/apache/iotdb/tsfile/read/TsFileSequenceReaderTimeseriesMetadataIteratorTest.java
@@ -0,0 +1,62 @@
+/*
+ * 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.utils.FileGenerator;
+
+import org.junit.After;
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Test;
+
+import java.io.IOException;
+import java.util.List;
+
+public class TsFileSequenceReaderTimeseriesMetadataIteratorTest {
+
+  private static final String FILE_PATH = FileGenerator.outputDataFile;
+  private TsFileReader tsFile;
+
+  @Before
+  public void before() throws IOException {
+    // create 2020 timeseries, 101 measurements per device.
+    FileGenerator.generateFile(100, 20, 101);
+    TsFileSequenceReader fileReader = new TsFileSequenceReader(FILE_PATH);
+    tsFile = new TsFileReader(fileReader);
+  }
+
+  @After
+  public void after() throws IOException {
+    tsFile.close();
+    FileGenerator.after();
+  }
+
+  @Test
+  public void testReadTsFileTimeseriesMetadata() throws IOException {
+    TsFileSequenceReader fileReader = new TsFileSequenceReader(FILE_PATH);
+    TsFileSequenceReaderTimeseriesMetadataIterator iterator =
+        new TsFileSequenceReaderTimeseriesMetadataIterator(fileReader, false);
+
+    Assert.assertTrue(iterator.hasNext());
+    Assert.assertEquals(2000, 
iterator.next().values().stream().mapToLong(List::size).sum());
+    Assert.assertTrue(iterator.hasNext());
+    Assert.assertEquals(20, 
iterator.next().values().stream().mapToLong(List::size).sum());
+  }
+}

Reply via email to