You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@iotdb.apache.org by xi...@apache.org on 2023/09/05 06:16:11 UTC
[iotdb] branch master updated: Added file overlap analysis tool (#11030)
This is an automated email from the ASF dual-hosted git repository.
xingtanzjr 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 43aa7db32f3 Added file overlap analysis tool (#11030)
43aa7db32f3 is described below
commit 43aa7db32f3541d08c4b0005893ee833655a6673
Author: Zhijia Cao <ca...@126.com>
AuthorDate: Tue Sep 5 14:16:04 2023 +0800
Added file overlap analysis tool (#11030)
---
.../tools/tsfile/overlap-statistic-tool.bat | 62 ++++++
.../tools/tsfile/overlap-statistic-tool.sh | 51 +++++
.../dataregion/compaction/tool/ITimeRange.java | 30 +++
.../dataregion/compaction/tool/Interval.java | 49 ++++
.../compaction/tool/ListTimeRangeImpl.java | 89 ++++++++
.../compaction/tool/OverlapStatistic.java | 91 ++++++++
.../compaction/tool/OverlapStatisticTool.java | 248 +++++++++++++++++++++
.../dataregion/compaction/tool/PrintUtil.java | 209 +++++++++++++++++
.../tool/SequenceFileSubTaskThreadExecutor.java | 41 ++++
.../compaction/tool/SequenceFileTaskSummary.java | 58 +++++
.../compaction/tool/SingleSequenceFileTask.java | 104 +++++++++
.../compaction/tool/TimePartitionProcessTask.java | 143 ++++++++++++
.../tool/TimePartitionProcessWorker.java | 58 +++++
.../compaction/tool/TsFileStatisticReader.java | 96 ++++++++
.../compaction/tool/UnseqSpaceStatistics.java | 84 +++++++
.../compaction/tools/ListTimeRangeImplTest.java | 138 ++++++++++++
.../compaction/tools/UnseqSpaceStatisticsTest.java | 63 ++++++
17 files changed, 1614 insertions(+)
diff --git a/iotdb-core/datanode/src/assembly/resources/tools/tsfile/overlap-statistic-tool.bat b/iotdb-core/datanode/src/assembly/resources/tools/tsfile/overlap-statistic-tool.bat
new file mode 100644
index 00000000000..500c3c7d12a
--- /dev/null
+++ b/iotdb-core/datanode/src/assembly/resources/tools/tsfile/overlap-statistic-tool.bat
@@ -0,0 +1,62 @@
+@REM
+@REM Licensed to the Apache Software Foundation (ASF) under one
+@REM or more contributor license agreements. See the NOTICE file
+@REM distributed with this work for additional information
+@REM regarding copyright ownership. The ASF licenses this file
+@REM to you under the Apache License, Version 2.0 (the
+@REM "License"); you may not use this file except in compliance
+@REM with the License. You may obtain a copy of the License at
+@REM
+@REM http://www.apache.org/licenses/LICENSE-2.0
+@REM
+@REM Unless required by applicable law or agreed to in writing,
+@REM software distributed under the License is distributed on an
+@REM "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+@REM KIND, either express or implied. See the License for the
+@REM specific language governing permissions and limitations
+@REM under the License.
+@REM
+
+
+@echo off
+echo ````````````````````````
+echo Starting Validating the TsFile
+echo ````````````````````````
+
+if "%OS%" == "Windows_NT" setlocal
+
+pushd %~dp0..\..
+if NOT DEFINED IOTDB_HOME set IOTDB_HOME=%CD%
+popd
+
+if NOT DEFINED MAIN_CLASS set MAIN_CLASS=org.apache.iotdb.db.storageengine.dataregion.compaction.tool.OverlapStatisticTool
+if NOT DEFINED JAVA_HOME goto :err
+
+@REM -----------------------------------------------------------------------------
+@REM ***** CLASSPATH library setting *****
+@REM Ensure that any user defined CLASSPATH variables are not used on startup
+set CLASSPATH="%IOTDB_HOME%\lib\*"
+
+goto okClasspath
+
+:append
+set CLASSPATH=%CLASSPATH%;%1
+goto :eof
+
+@REM -----------------------------------------------------------------------------
+:okClasspath
+
+"%JAVA_HOME%\bin\java" -Xmx16G -cp "%CLASSPATH%" %MAIN_CLASS% %*
+
+goto finally
+
+
+:err
+echo JAVA_HOME environment variable must be set!
+pause
+
+
+@REM -----------------------------------------------------------------------------
+:finally
+
+ENDLOCAL
diff --git a/iotdb-core/datanode/src/assembly/resources/tools/tsfile/overlap-statistic-tool.sh b/iotdb-core/datanode/src/assembly/resources/tools/tsfile/overlap-statistic-tool.sh
new file mode 100644
index 00000000000..cd34eab61a6
--- /dev/null
+++ b/iotdb-core/datanode/src/assembly/resources/tools/tsfile/overlap-statistic-tool.sh
@@ -0,0 +1,51 @@
+#!/bin/bash
+#
+# 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.
+#
+
+echo ---------------------
+echo Starting Validating the TsFile
+echo ---------------------
+
+source "$(dirname "$0")/../../sbin/iotdb-common.sh"
+#get_iotdb_include and checkAllVariables is in iotdb-common.sh
+VARS=$(get_iotdb_include "$*")
+checkAllVariables
+export IOTDB_HOME="${IOTDB_HOME}/.."
+eval set -- "$VARS"
+
+if [ -n "$JAVA_HOME" ]; then
+ for java in "$JAVA_HOME"/bin/amd64/java "$JAVA_HOME"/bin/java; do
+ if [ -x "$java" ]; then
+ JAVA="$java"
+ break
+ fi
+ done
+else
+ JAVA=java
+fi
+
+CLASSPATH=""
+for f in ${IOTDB_HOME}/lib/*.jar; do
+ CLASSPATH=${CLASSPATH}":"$f
+done
+
+MAIN_CLASS=org.apache.iotdb.db.storageengine.dataregion.compaction.tool.OverlapStatisticTool
+
+"$JAVA" -Xmx16G -cp "$CLASSPATH" "$MAIN_CLASS" "$@"
+exit $?
diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/storageengine/dataregion/compaction/tool/ITimeRange.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/storageengine/dataregion/compaction/tool/ITimeRange.java
new file mode 100644
index 00000000000..a04288da465
--- /dev/null
+++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/storageengine/dataregion/compaction/tool/ITimeRange.java
@@ -0,0 +1,30 @@
+/*
+ * 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.db.storageengine.dataregion.compaction.tool;
+
+public interface ITimeRange {
+
+ // Add a time period to the current time range object. The increase process requires maintenance
+ // of the current TimeRange to facilitate efficient overlap check in the future
+ void addInterval(Interval interval);
+
+ // Determines whether the incoming time range overlaps with the current time range
+ boolean isOverlapped(Interval interval);
+}
diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/storageengine/dataregion/compaction/tool/Interval.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/storageengine/dataregion/compaction/tool/Interval.java
new file mode 100644
index 00000000000..8f2a2da301d
--- /dev/null
+++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/storageengine/dataregion/compaction/tool/Interval.java
@@ -0,0 +1,49 @@
+/*
+ * 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.db.storageengine.dataregion.compaction.tool;
+
+public class Interval {
+ private long start;
+ private long end;
+
+ public Interval(long start, long end) {
+ this.start = start;
+ this.end = end;
+ if (end < start) {
+ throw new IllegalArgumentException("end must greater than start");
+ }
+ }
+
+ public long getStart() {
+ return start;
+ }
+
+ public long getEnd() {
+ return end;
+ }
+
+ public void setStart(long start) {
+ this.start = start;
+ }
+
+ public void setEnd(long end) {
+ this.end = end;
+ }
+}
diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/storageengine/dataregion/compaction/tool/ListTimeRangeImpl.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/storageengine/dataregion/compaction/tool/ListTimeRangeImpl.java
new file mode 100644
index 00000000000..bca7f72aa63
--- /dev/null
+++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/storageengine/dataregion/compaction/tool/ListTimeRangeImpl.java
@@ -0,0 +1,89 @@
+/*
+ * 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.db.storageengine.dataregion.compaction.tool;
+
+import java.util.ArrayList;
+import java.util.LinkedList;
+import java.util.List;
+
+public class ListTimeRangeImpl implements ITimeRange {
+
+ List<Interval> intervalList = new LinkedList<>();
+ // 0-10, 20-30, 50-70
+ // 25-60
+ // 0-10. 20-70
+
+ @Override
+ public void addInterval(Interval interval) {
+ List<Interval> mergedIntervals = new ArrayList<>();
+ int index = 0;
+
+ // 1. elements that do not overlap with the newly added element are placed directly in the
+ // result
+ while (index < intervalList.size() && intervalList.get(index).getEnd() < interval.getStart()) {
+ mergedIntervals.add(intervalList.get(index));
+ index++;
+ }
+
+ // 2. if the element overlaps with an existing element, start equals the minimum value of the
+ // overlap and end equals the maximum value of the overlap
+ while (index < intervalList.size() && intervalList.get(index).getStart() <= interval.getEnd()) {
+ interval.setStart(Math.min(intervalList.get(index).getStart(), interval.getStart()));
+ interval.setEnd(Math.max(intervalList.get(index).getEnd(), interval.getEnd()));
+ index++;
+ }
+ mergedIntervals.add(interval);
+
+ // 3. add the remaining elements to the result set
+ while (index < intervalList.size()) {
+ mergedIntervals.add(intervalList.get(index));
+ index++;
+ }
+
+ intervalList.clear();
+ intervalList.addAll(mergedIntervals);
+ }
+
+ public List<Interval> getIntervalList() {
+ return intervalList;
+ }
+
+ /**
+ * case 1: interval.getStart() <= currentInterval.getEnd()
+ *
+ * <p>currentInterval: [5,10], interval: [6,15],[1,7],[0,5],[10,15]
+ *
+ * <p>case 2: interval.getEnd() <= currentInterval.getEnd()
+ *
+ * <p>currentInterval: [5,10], interval:[1,9],[0,9],[1,10]
+ */
+ @Override
+ public boolean isOverlapped(Interval interval) {
+ for (Interval currentInterval : intervalList) {
+ boolean isOverlap =
+ interval.getStart() <= currentInterval.getEnd()
+ && interval.getEnd() >= currentInterval.getStart();
+ if (isOverlap) {
+ return true;
+ }
+ }
+ return false;
+ }
+}
diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/storageengine/dataregion/compaction/tool/OverlapStatistic.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/storageengine/dataregion/compaction/tool/OverlapStatistic.java
new file mode 100644
index 00000000000..2701e379554
--- /dev/null
+++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/storageengine/dataregion/compaction/tool/OverlapStatistic.java
@@ -0,0 +1,91 @@
+/*
+ * 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.db.storageengine.dataregion.compaction.tool;
+
+import java.util.HashSet;
+
+public class OverlapStatistic {
+ long totalSequenceFile;
+ long totalSequenceFileSize;
+ long totalChunkGroupsInSequenceFile;
+ long totalChunksInSequenceFile;
+
+ HashSet<String> sequenceNumber = new HashSet<>();
+ long sequenceMinStartTime = Long.MAX_VALUE;
+ long sequenceMaxEndTime = Long.MIN_VALUE;
+
+ long totalUnsequenceFile;
+ long totalUnsequenceFileSize;
+ long totalChunkGroupsInUnSequenceFile;
+ long totalChunksInUnSequenceFile;
+ long unSequenceMinStartTime = Long.MAX_VALUE;
+ long unSequenceMaxEndTime = Long.MIN_VALUE;
+
+ long overlappedSequenceFiles;
+ long overlappedChunkGroupsInSequenceFile;
+ long overlappedChunksInSequenceFile;
+
+ public void merge(OverlapStatistic other) {
+ this.totalSequenceFile += other.totalSequenceFile;
+ this.totalSequenceFileSize += other.totalSequenceFileSize;
+ this.totalChunkGroupsInSequenceFile += other.totalChunkGroupsInSequenceFile;
+ this.totalChunksInSequenceFile += other.totalChunksInSequenceFile;
+ this.sequenceMinStartTime = Math.min(this.sequenceMinStartTime, other.sequenceMinStartTime);
+ this.sequenceMaxEndTime = Math.max(this.sequenceMaxEndTime, other.sequenceMaxEndTime);
+
+ this.totalUnsequenceFile += other.totalUnsequenceFile;
+ this.totalUnsequenceFileSize += other.totalUnsequenceFileSize;
+ this.totalChunkGroupsInUnSequenceFile += other.totalChunkGroupsInUnSequenceFile;
+ this.totalChunksInUnSequenceFile += other.totalChunksInUnSequenceFile;
+ this.unSequenceMinStartTime =
+ Math.min(this.unSequenceMinStartTime, other.unSequenceMinStartTime);
+ this.unSequenceMaxEndTime = Math.max(this.unSequenceMaxEndTime, other.unSequenceMaxEndTime);
+
+ this.overlappedSequenceFiles += other.overlappedSequenceFiles;
+ this.overlappedChunkGroupsInSequenceFile += other.overlappedChunkGroupsInSequenceFile;
+ this.overlappedChunksInSequenceFile += other.overlappedChunksInSequenceFile;
+ }
+
+ public void mergeSingleSequenceFileTaskResult(SequenceFileTaskSummary summary) {
+ if (summary.equals(new SequenceFileTaskSummary())) {
+ return;
+ }
+ if (summary.overlapChunkGroup > 0) {
+ this.overlappedSequenceFiles += 1;
+ }
+ this.overlappedChunkGroupsInSequenceFile += summary.overlapChunkGroup;
+ this.totalChunkGroupsInSequenceFile += summary.totalChunkGroups;
+ this.overlappedChunksInSequenceFile += summary.overlapChunk;
+ this.totalChunksInSequenceFile += summary.totalChunks;
+ this.totalSequenceFile += 1;
+ this.totalSequenceFileSize += summary.fileSize;
+ this.sequenceMinStartTime = Math.min(this.sequenceMinStartTime, summary.minStartTime);
+ this.sequenceMaxEndTime = Math.max(this.sequenceMaxEndTime, summary.maxEndTime);
+ }
+
+ public void mergeUnSeqSpaceStatistics(UnseqSpaceStatistics statistics) {
+ this.totalUnsequenceFile += statistics.unsequenceFileNum;
+ this.totalUnsequenceFileSize += statistics.unsequenceFileSize;
+ this.totalChunksInUnSequenceFile += statistics.unsequenceChunkNum;
+ this.totalChunkGroupsInUnSequenceFile += statistics.unsequenceChunkGroupNum;
+ this.unSequenceMinStartTime = Math.min(this.unSequenceMinStartTime, statistics.minStartTime);
+ this.unSequenceMaxEndTime = Math.max(this.unSequenceMaxEndTime, statistics.maxEndTime);
+ }
+}
diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/storageengine/dataregion/compaction/tool/OverlapStatisticTool.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/storageengine/dataregion/compaction/tool/OverlapStatisticTool.java
new file mode 100644
index 00000000000..39ce1587877
--- /dev/null
+++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/storageengine/dataregion/compaction/tool/OverlapStatisticTool.java
@@ -0,0 +1,248 @@
+/*
+ * 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.db.storageengine.dataregion.compaction.tool;
+
+import org.apache.iotdb.db.storageengine.dataregion.tsfile.TsFileResource;
+import org.apache.iotdb.tsfile.common.constant.TsFileConstant;
+import org.apache.iotdb.tsfile.utils.Pair;
+
+import org.apache.commons.cli.CommandLine;
+import org.apache.commons.cli.CommandLineParser;
+import org.apache.commons.cli.DefaultParser;
+import org.apache.commons.cli.Option;
+import org.apache.commons.cli.Options;
+import org.apache.commons.cli.ParseException;
+
+import java.io.File;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Objects;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.locks.Lock;
+import java.util.concurrent.locks.ReentrantLock;
+
+public class OverlapStatisticTool {
+
+ private static final String WORKER_NUM_ARG = "worker_num";
+ public static final int DEFAULT_WORKER_NUM = 4;
+ private static final String SUB_TASK_NUM_ARG = "sub_task_num";
+ public static final int DEFAULT_WORKER_SUB_TASK_NUM = 1;
+ private static final String DATA_DIRS_ARG = "data_dirs";
+
+ public static int workerNum;
+ public static int subTaskNum;
+ public static List<String> dataDirs;
+
+ public static Lock outputInfolock = new ReentrantLock();
+ public static long seqFileCount = 0;
+ public static long processedTimePartitionCount = 0;
+ public static long processedSeqFileCount = 0;
+ public static final Map<String, Pair<List<String>, List<String>>> timePartitionFileMap =
+ new HashMap<>();
+
+ public static void main(String[] args) throws InterruptedException {
+ // process parameters to get the path to the data directory from the input
+ parseArgs(args);
+
+ OverlapStatisticTool tool = new OverlapStatisticTool();
+ long startTime = System.currentTimeMillis();
+ tool.process(dataDirs);
+ System.out.printf(
+ "Total time cost: %.2fs\n", ((double) System.currentTimeMillis() - startTime) / 1000);
+ }
+
+ public static void parseArgs(String[] args) {
+ Options options = createOptions();
+ CommandLineParser parser = new DefaultParser();
+ CommandLine commandLine;
+ try {
+ commandLine = parser.parse(options, args);
+ } catch (ParseException e) {
+ throw new RuntimeException(e);
+ }
+ workerNum =
+ Integer.parseInt(
+ getArgOrDefault(commandLine, WORKER_NUM_ARG, String.valueOf(DEFAULT_WORKER_NUM)));
+ subTaskNum =
+ Integer.parseInt(
+ getArgOrDefault(
+ commandLine, SUB_TASK_NUM_ARG, String.valueOf(DEFAULT_WORKER_SUB_TASK_NUM)));
+ String[] dataDirsParam = commandLine.getOptionValues(DATA_DIRS_ARG);
+
+ if (dataDirsParam == null || dataDirsParam.length == 0) {
+ throw new RuntimeException("data_dirs must not be empty");
+ }
+ dataDirs = Arrays.asList(dataDirsParam);
+ }
+
+ private static Options createOptions() {
+ Options options = new Options();
+ options
+ .addOption(
+ Option.builder()
+ .argName(WORKER_NUM_ARG)
+ .longOpt(WORKER_NUM_ARG)
+ .hasArg()
+ .desc("Concurrent time partition num(default: 10)")
+ .build())
+ .addOption(
+ Option.builder()
+ .argName(SUB_TASK_NUM_ARG)
+ .longOpt(SUB_TASK_NUM_ARG)
+ .hasArg()
+ .desc("Concurrent file num in one time partition(default: 10)")
+ .build())
+ .addOption(
+ Option.builder()
+ .argName(DATA_DIRS_ARG)
+ .longOpt(DATA_DIRS_ARG)
+ .hasArg()
+ .desc("Data dirs(Required)")
+ .required()
+ .build());
+ return options;
+ }
+
+ private static String getArgOrDefault(CommandLine commandLine, String arg, String defaultValue) {
+ String value = commandLine.getOptionValue(arg);
+ return value == null ? defaultValue : value;
+ }
+
+ public void process(List<String> dataDirs) throws InterruptedException {
+ processDataDirs(dataDirs);
+
+ int workerNum = Math.min(timePartitionFileMap.size(), OverlapStatisticTool.workerNum);
+ TimePartitionProcessWorker[] workers = constructWorkers(workerNum);
+
+ CountDownLatch countDownLatch = new CountDownLatch(workerNum);
+ for (TimePartitionProcessWorker worker : workers) {
+ worker.run(countDownLatch);
+ }
+ countDownLatch.await();
+
+ OverlapStatistic statistic = new OverlapStatistic();
+ for (TimePartitionProcessWorker worker : workers) {
+ for (OverlapStatistic partialRet : worker.getWorkerResults()) {
+ statistic.merge(partialRet);
+ }
+ }
+ PrintUtil.printOneStatistics(statistic, "All EXECUTED");
+ }
+
+ public TimePartitionProcessWorker[] constructWorkers(int workerNum) {
+ TimePartitionProcessWorker[] workers = new TimePartitionProcessWorker[workerNum];
+
+ int workerIdx = 0;
+ for (Map.Entry<String, Pair<List<String>, List<String>>> timePartitionFilesEntry :
+ timePartitionFileMap.entrySet()) {
+ String timePartition = timePartitionFilesEntry.getKey();
+ Pair<List<String>, List<String>> timePartitionFiles = timePartitionFilesEntry.getValue();
+
+ if (workers[workerIdx] == null) {
+ workers[workerIdx] = new TimePartitionProcessWorker();
+ }
+
+ workers[workerIdx].addTask(new TimePartitionProcessTask(timePartition, timePartitionFiles));
+ workerIdx = (workerIdx + 1) % workerNum;
+ }
+ return workers;
+ }
+
+ private void processDataDirs(List<String> dataDirs) {
+ // 1. Traverse all time partitions and construct timePartitions
+ // 2. Count the total number of sequential files
+ for (String dataDirPath : dataDirs) {
+ File dataDir = new File(dataDirPath);
+ if (!dataDir.exists() || !dataDir.isDirectory()) {
+ continue;
+ }
+ processDataDirWithIsSeq(dataDirPath, true);
+ processDataDirWithIsSeq(dataDirPath, false);
+ }
+ }
+
+ private void processDataDirWithIsSeq(String dataDirPath, boolean isSeq) {
+ String dataDirWithIsSeq;
+ if (isSeq) {
+ dataDirWithIsSeq = dataDirPath + File.separator + "sequence";
+ } else {
+ dataDirWithIsSeq = dataDirPath + File.separator + "unsequence";
+ }
+ File dataDirWithIsSequence = new File(dataDirWithIsSeq);
+ if (!dataDirWithIsSequence.exists() || !dataDirWithIsSequence.isDirectory()) {
+ System.out.println(dataDirWithIsSequence + " is not a correct path");
+ return;
+ }
+
+ for (File storageGroupDir : Objects.requireNonNull(dataDirWithIsSequence.listFiles())) {
+ if (!storageGroupDir.isDirectory()) {
+ continue;
+ }
+ String storageGroup = storageGroupDir.getName();
+ for (File dataRegionDir : Objects.requireNonNull(storageGroupDir.listFiles())) {
+ if (!dataRegionDir.isDirectory()) {
+ continue;
+ }
+ String dataRegion = dataRegionDir.getName();
+ for (File timePartitionDir : Objects.requireNonNull(dataRegionDir.listFiles())) {
+ if (!timePartitionDir.isDirectory()) {
+ continue;
+ }
+
+ String timePartitionKey =
+ calculateTimePartitionKey(storageGroup, dataRegion, timePartitionDir.getName());
+ Pair<List<String>, List<String>> timePartitionFiles =
+ timePartitionFileMap.computeIfAbsent(
+ timePartitionKey, v -> new Pair<>(new ArrayList<>(), new ArrayList<>()));
+ for (File file : Objects.requireNonNull(timePartitionDir.listFiles())) {
+ if (!file.isFile()) {
+ continue;
+ }
+ if (!file.getName().endsWith(TsFileConstant.TSFILE_SUFFIX)) {
+ continue;
+ }
+ String resourceFilePath = file.getAbsolutePath() + TsFileResource.RESOURCE_SUFFIX;
+ if (!new File(resourceFilePath).exists()) {
+ System.out.println(
+ resourceFilePath
+ + " is not exist, the tsfile is skipped because it is not closed.");
+ continue;
+ }
+ String filePath = file.getAbsolutePath();
+ if (isSeq) {
+ timePartitionFiles.left.add(filePath);
+ seqFileCount++;
+ } else {
+ timePartitionFiles.right.add(filePath);
+ }
+ }
+ }
+ }
+ }
+ }
+
+ private String calculateTimePartitionKey(
+ String storageGroup, String dataRegion, String timePartition) {
+ return storageGroup + "-" + dataRegion + "-" + timePartition;
+ }
+}
diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/storageengine/dataregion/compaction/tool/PrintUtil.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/storageengine/dataregion/compaction/tool/PrintUtil.java
new file mode 100644
index 00000000000..7df1bffc014
--- /dev/null
+++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/storageengine/dataregion/compaction/tool/PrintUtil.java
@@ -0,0 +1,209 @@
+/*
+ * 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.db.storageengine.dataregion.compaction.tool;
+
+class PrintUtil {
+ static String[] header_1 = {"", "Total", "Overlap", "Overlap/Total"};
+ static String[] header_2 = {"", "Total", "Sequence", "UnSequence", "UnSequence/Total"};
+
+ static long MSize = 1024 * 1024;
+
+ public static void printOneStatistics(OverlapStatistic overlapStatistic, String label) {
+ System.out.println();
+ printTableLog(overlapStatistic);
+ printProgressLog(label, overlapStatistic);
+ }
+
+ private static void printProgressLog(String label, OverlapStatistic statistic) {
+ String[][] log = {
+ {
+ "File Number",
+ statistic.totalSequenceFile + statistic.totalUnsequenceFile + "",
+ statistic.totalSequenceFile + "",
+ statistic.totalUnsequenceFile + "",
+ String.format(
+ "%.2f%%",
+ statistic.totalUnsequenceFile
+ * 100d
+ / (statistic.totalSequenceFile + statistic.totalUnsequenceFile))
+ },
+ {
+ "File Size(MB)",
+ (statistic.totalSequenceFileSize + statistic.totalUnsequenceFileSize) / MSize + "",
+ statistic.totalSequenceFileSize / MSize + "",
+ statistic.totalUnsequenceFileSize / MSize + "",
+ String.format(
+ "%.2f%%",
+ statistic.totalUnsequenceFileSize
+ * 100d
+ / (statistic.totalSequenceFileSize + statistic.totalUnsequenceFileSize))
+ },
+ {
+ "Duration",
+ Math.max(statistic.sequenceMaxEndTime, statistic.unSequenceMaxEndTime)
+ - Math.min(statistic.sequenceMinStartTime, statistic.unSequenceMinStartTime)
+ + "",
+ statistic.sequenceMaxEndTime - statistic.sequenceMinStartTime + "",
+ statistic.unSequenceMaxEndTime - statistic.unSequenceMinStartTime + "",
+ String.format(
+ "%.2f%%",
+ (statistic.unSequenceMaxEndTime - statistic.unSequenceMinStartTime)
+ * 100d
+ / (Math.max(statistic.sequenceMaxEndTime, statistic.unSequenceMaxEndTime)
+ - Math.min(statistic.sequenceMinStartTime, statistic.unSequenceMinStartTime)))
+ }
+ };
+ System.out.println(System.getProperty("line.separator") + "Data Table:");
+ printStaticsTable(log);
+
+ System.out.printf(
+ "Progress: %s\n" + "Sequence File progress: %d/%d\n" + "Partition progress: %d/%d %s",
+ label,
+ OverlapStatisticTool.processedSeqFileCount,
+ OverlapStatisticTool.seqFileCount,
+ OverlapStatisticTool.processedTimePartitionCount,
+ OverlapStatisticTool.timePartitionFileMap.size(),
+ System.getProperty("line.separator"));
+ }
+
+ private static void printTableLog(OverlapStatistic overlapStatistic) {
+ double overlappedSeqFilePercentage =
+ calculatePercentage(
+ overlapStatistic.overlappedSequenceFiles, overlapStatistic.totalSequenceFile);
+ double overlappedChunkGroupPercentage =
+ calculatePercentage(
+ overlapStatistic.overlappedChunkGroupsInSequenceFile,
+ overlapStatistic.totalChunkGroupsInSequenceFile);
+ double overlappedChunkPercentage =
+ calculatePercentage(
+ overlapStatistic.overlappedChunksInSequenceFile,
+ overlapStatistic.totalChunksInSequenceFile);
+ String[][] log = {
+ {
+ "Sequence File",
+ overlapStatistic.totalSequenceFile + "",
+ overlapStatistic.overlappedSequenceFiles + "",
+ String.format("%.2f%%", overlappedSeqFilePercentage)
+ },
+ {
+ "ChunkGroup In Sequence File",
+ overlapStatistic.totalChunkGroupsInSequenceFile + "",
+ overlapStatistic.overlappedChunkGroupsInSequenceFile + "",
+ String.format("%.2f%%", overlappedChunkGroupPercentage)
+ },
+ {
+ "Chunk In Sequence File",
+ overlapStatistic.totalChunksInSequenceFile + "",
+ overlapStatistic.overlappedChunksInSequenceFile + "",
+ String.format("%.2f%%", overlappedChunkPercentage)
+ }
+ };
+ System.out.println("Overlap Table:");
+ printOverlapTable(log);
+ }
+
+ private static double calculatePercentage(long numerator, long denominator) {
+ return denominator != 0 ? (double) numerator / denominator * 100 : 0;
+ }
+
+ public static void printOverlapTable(String[][] data) {
+ int numRows = data.length;
+ int[] maxCellWidths = calculateMaxCellWidths(header_1, data);
+
+ printTopBorder(maxCellWidths);
+ printRow(header_1, maxCellWidths);
+
+ for (int row = 0; row < numRows; row++) {
+ printSeparator(maxCellWidths);
+ printRow(data[row], maxCellWidths);
+ }
+
+ printBottomBorder(maxCellWidths);
+ }
+
+ public static void printStaticsTable(String[][] data) {
+ int numRows = data.length;
+ int[] maxCellWidths = calculateMaxCellWidths(header_2, data);
+
+ printTopBorder(maxCellWidths);
+ printRow(header_2, maxCellWidths);
+
+ for (int row = 0; row < numRows; row++) {
+ printSeparator(maxCellWidths);
+ printRow(data[row], maxCellWidths);
+ }
+
+ printBottomBorder(maxCellWidths);
+ }
+
+ private static int[] calculateMaxCellWidths(String[] header, String[][] data) {
+ int numCols = header.length;
+ int[] maxCellWidths = new int[numCols];
+
+ for (int col = 0; col < numCols; col++) {
+ maxCellWidths[col] = header[col].length();
+ for (String[] row : data) {
+ maxCellWidths[col] = Math.max(maxCellWidths[col], row[col].length());
+ }
+ }
+
+ return maxCellWidths;
+ }
+
+ private static void printTopBorder(int[] maxCellWidths) {
+ System.out.print("┌");
+ for (int width : maxCellWidths) {
+ printRepeat("─", width + 2);
+ System.out.print("┬");
+ }
+ System.out.println();
+ }
+
+ private static void printSeparator(int[] maxCellWidths) {
+ System.out.print("├");
+ for (int width : maxCellWidths) {
+ printRepeat("─", width + 2);
+ System.out.print("┼");
+ }
+ System.out.println();
+ }
+
+ private static void printBottomBorder(int[] maxCellWidths) {
+ System.out.print("└");
+ for (int width : maxCellWidths) {
+ printRepeat("─", width + 2);
+ System.out.print("┴");
+ }
+ System.out.println();
+ }
+
+ private static void printRow(String[] row, int[] maxCellWidths) {
+ for (int col = 0; col < row.length; col++) {
+ System.out.printf("│ %-" + maxCellWidths[col] + "s ", row[col]);
+ }
+ System.out.println("│");
+ }
+
+ private static void printRepeat(String value, int times) {
+ for (int i = 0; i < times; i++) {
+ System.out.print(value);
+ }
+ }
+}
diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/storageengine/dataregion/compaction/tool/SequenceFileSubTaskThreadExecutor.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/storageengine/dataregion/compaction/tool/SequenceFileSubTaskThreadExecutor.java
new file mode 100644
index 00000000000..1e8512e69d0
--- /dev/null
+++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/storageengine/dataregion/compaction/tool/SequenceFileSubTaskThreadExecutor.java
@@ -0,0 +1,41 @@
+/*
+ * 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.db.storageengine.dataregion.compaction.tool;
+
+import java.util.concurrent.Callable;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.concurrent.Future;
+
+public class SequenceFileSubTaskThreadExecutor {
+ private ExecutorService executor;
+
+ public SequenceFileSubTaskThreadExecutor(int threadCount) {
+ executor = Executors.newFixedThreadPool(threadCount);
+ }
+
+ public Future<SequenceFileTaskSummary> submit(Callable<SequenceFileTaskSummary> task) {
+ return executor.submit(task);
+ }
+
+ public void shutdown() {
+ executor.shutdown();
+ }
+}
diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/storageengine/dataregion/compaction/tool/SequenceFileTaskSummary.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/storageengine/dataregion/compaction/tool/SequenceFileTaskSummary.java
new file mode 100644
index 00000000000..c27f41fe301
--- /dev/null
+++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/storageengine/dataregion/compaction/tool/SequenceFileTaskSummary.java
@@ -0,0 +1,58 @@
+/*
+ * 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.db.storageengine.dataregion.compaction.tool;
+
+import java.util.Objects;
+
+public class SequenceFileTaskSummary {
+ public long overlapChunk = 0;
+ public long overlapChunkGroup = 0;
+ public long totalChunks = 0;
+ public long totalChunkGroups = 0;
+ public long fileSize = 0;
+
+ public long minStartTime = Long.MAX_VALUE;
+ public long maxEndTime = Long.MIN_VALUE;
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+ SequenceFileTaskSummary that = (SequenceFileTaskSummary) o;
+ return overlapChunk == that.overlapChunk
+ && overlapChunkGroup == that.overlapChunkGroup
+ && totalChunks == that.totalChunks
+ && totalChunkGroups == that.totalChunkGroups
+ && fileSize == that.fileSize;
+ }
+
+ public void setMaxEndTime(long maxEndTime) {
+ this.maxEndTime = Math.max(this.maxEndTime, maxEndTime);
+ }
+
+ public void setMinStartTime(long minStartTime) {
+ this.minStartTime = Math.min(this.minStartTime, minStartTime);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(overlapChunk, overlapChunkGroup, totalChunks, totalChunkGroups, fileSize);
+ }
+}
diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/storageengine/dataregion/compaction/tool/SingleSequenceFileTask.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/storageengine/dataregion/compaction/tool/SingleSequenceFileTask.java
new file mode 100644
index 00000000000..8addc0efc72
--- /dev/null
+++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/storageengine/dataregion/compaction/tool/SingleSequenceFileTask.java
@@ -0,0 +1,104 @@
+/*
+ * 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.db.storageengine.dataregion.compaction.tool;
+
+import org.apache.iotdb.tsfile.file.metadata.ChunkMetadata;
+
+import java.io.File;
+import java.io.IOException;
+import java.nio.file.NoSuchFileException;
+import java.util.List;
+import java.util.concurrent.Callable;
+
+public class SingleSequenceFileTask implements Callable<SequenceFileTaskSummary> {
+ private UnseqSpaceStatistics unseqSpaceStatistics;
+ private String seqFile;
+
+ public SingleSequenceFileTask(UnseqSpaceStatistics unseqSpaceStatistics, String seqFile) {
+ this.unseqSpaceStatistics = unseqSpaceStatistics;
+ this.seqFile = seqFile;
+ }
+
+ @Override
+ public SequenceFileTaskSummary call() throws Exception {
+ return checkSeqFile(unseqSpaceStatistics, seqFile);
+ }
+
+ private SequenceFileTaskSummary checkSeqFile(
+ UnseqSpaceStatistics unseqSpaceStatistics, String seqFile) {
+ SequenceFileTaskSummary summary = new SequenceFileTaskSummary();
+ File f = new File(seqFile);
+ if (!f.exists()) {
+ return summary;
+ }
+ summary.fileSize += f.length();
+ try (TsFileStatisticReader reader = new TsFileStatisticReader(seqFile)) {
+ // statistics sequence file information and updates to overlapStatistic
+ List<TsFileStatisticReader.ChunkGroupStatistics> chunkGroupStatisticsList =
+ reader.getChunkGroupStatisticsList();
+ for (TsFileStatisticReader.ChunkGroupStatistics chunkGroupStatistics :
+ chunkGroupStatisticsList) {
+ summary.totalChunks += chunkGroupStatistics.getTotalChunkNum();
+ String deviceId = chunkGroupStatistics.getDeviceID();
+
+ long deviceStartTime = Long.MAX_VALUE, deviceEndTime = Long.MIN_VALUE;
+
+ for (ChunkMetadata chunkMetadata : chunkGroupStatistics.getChunkMetadataList()) {
+ // skip empty chunk
+ if (chunkMetadata.getStartTime() > chunkMetadata.getEndTime()) {
+ continue;
+ }
+ // update device start time and end time
+ deviceStartTime = Math.min(deviceStartTime, chunkMetadata.getStartTime());
+ deviceEndTime = Math.max(deviceEndTime, chunkMetadata.getEndTime());
+
+ summary.setMinStartTime(deviceStartTime);
+ summary.setMaxEndTime(deviceEndTime);
+
+ // check chunk overlap
+ Interval interval =
+ new Interval(chunkMetadata.getStartTime(), chunkMetadata.getEndTime());
+ String measurementId = chunkMetadata.getMeasurementUid();
+ if (unseqSpaceStatistics.chunkHasOverlap(deviceId, measurementId, interval)) {
+ summary.overlapChunk++;
+ }
+ }
+ // check device overlap
+ if (deviceStartTime > deviceEndTime) {
+ continue;
+ }
+ Interval deviceInterval = new Interval(deviceStartTime, deviceEndTime);
+ if (!unseqSpaceStatistics.chunkGroupHasOverlap(deviceId, deviceInterval)) {
+ continue;
+ }
+ summary.overlapChunkGroup++;
+ }
+ summary.totalChunkGroups = chunkGroupStatisticsList.size();
+ } catch (IOException e) {
+ if (e instanceof NoSuchFileException) {
+ System.out.println(seqFile + " is not exist");
+ return new SequenceFileTaskSummary();
+ }
+ e.printStackTrace();
+ return new SequenceFileTaskSummary();
+ }
+ return summary;
+ }
+}
diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/storageengine/dataregion/compaction/tool/TimePartitionProcessTask.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/storageengine/dataregion/compaction/tool/TimePartitionProcessTask.java
new file mode 100644
index 00000000000..ca37b5f8ce4
--- /dev/null
+++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/storageengine/dataregion/compaction/tool/TimePartitionProcessTask.java
@@ -0,0 +1,143 @@
+/*
+ * 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.db.storageengine.dataregion.compaction.tool;
+
+import org.apache.iotdb.tsfile.file.metadata.ChunkMetadata;
+import org.apache.iotdb.tsfile.utils.Pair;
+
+import java.io.File;
+import java.io.IOException;
+import java.nio.file.NoSuchFileException;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.concurrent.Future;
+
+public class TimePartitionProcessTask {
+ private final String timePartition;
+ private final Pair<List<String>, List<String>> timePartitionFiles;
+ private long sequenceSpaceCost = 0;
+ private long unsequenceSpaceCost = 0;
+
+ public TimePartitionProcessTask(
+ String timePartition, Pair<List<String>, List<String>> timePartitionFiles) {
+ this.timePartition = timePartition;
+ this.timePartitionFiles = timePartitionFiles;
+ }
+
+ public OverlapStatistic processTimePartition(SequenceFileSubTaskThreadExecutor fileTaskExecutor) {
+ long startTime = System.currentTimeMillis();
+ UnseqSpaceStatistics unseqSpaceStatistics = buildUnseqSpaceStatistics(timePartitionFiles.right);
+
+ OverlapStatistic partialRet =
+ processSequenceSpaceAsync(fileTaskExecutor, unseqSpaceStatistics, timePartitionFiles.left);
+ OverlapStatisticTool.outputInfolock.lock();
+ OverlapStatisticTool.processedTimePartitionCount += 1;
+ OverlapStatisticTool.processedSeqFileCount += partialRet.totalSequenceFile;
+ PrintUtil.printOneStatistics(partialRet, timePartition);
+ System.out.printf(
+ "Worker"
+ + Thread.currentThread().getName()
+ + " Time cost: %.2fs, Sequence space cost: %.2fs, Build unsequence space cost: %.2fs.\n",
+ ((double) System.currentTimeMillis() - startTime) / 1000,
+ ((double) sequenceSpaceCost / 1000),
+ ((double) unsequenceSpaceCost / 1000));
+
+ OverlapStatisticTool.outputInfolock.unlock();
+
+ return partialRet;
+ }
+
+ private UnseqSpaceStatistics buildUnseqSpaceStatistics(List<String> unseqFiles) {
+ UnseqSpaceStatistics unseqSpaceStatistics = new UnseqSpaceStatistics();
+
+ long startTime = System.currentTimeMillis();
+ for (String unseqFile : unseqFiles) {
+ File f = new File(unseqFile);
+ if (!f.exists()) {
+ continue;
+ }
+ unseqSpaceStatistics.unsequenceFileSize += f.length();
+ try (TsFileStatisticReader reader = new TsFileStatisticReader(unseqFile)) {
+ List<TsFileStatisticReader.ChunkGroupStatistics> chunkGroupStatisticsList =
+ reader.getChunkGroupStatisticsList();
+ unseqSpaceStatistics.unsequenceChunkGroupNum += chunkGroupStatisticsList.size();
+
+ for (TsFileStatisticReader.ChunkGroupStatistics statistics : chunkGroupStatisticsList) {
+ long deviceStartTime = Long.MAX_VALUE, deviceEndTime = Long.MIN_VALUE;
+
+ for (ChunkMetadata chunkMetadata : statistics.getChunkMetadataList()) {
+ unseqSpaceStatistics.unsequenceChunkNum += chunkMetadata.getNumOfPoints();
+ deviceStartTime = Math.min(deviceStartTime, chunkMetadata.getStartTime());
+ deviceEndTime = Math.max(deviceEndTime, chunkMetadata.getEndTime());
+
+ unseqSpaceStatistics.setMinStartTime(deviceStartTime);
+ unseqSpaceStatistics.setMaxEndTime(deviceEndTime);
+
+ if (chunkMetadata.getStartTime() > chunkMetadata.getEndTime()) {
+ continue;
+ }
+ unseqSpaceStatistics.updateMeasurement(
+ statistics.getDeviceID(),
+ chunkMetadata.getMeasurementUid(),
+ new Interval(chunkMetadata.getStartTime(), chunkMetadata.getEndTime()));
+ }
+ if (deviceStartTime > deviceEndTime) {
+ continue;
+ }
+ unseqSpaceStatistics.updateDevice(
+ statistics.getDeviceID(), new Interval(deviceStartTime, deviceEndTime));
+ }
+ } catch (IOException e) {
+ if (e instanceof NoSuchFileException) {
+ System.out.println(((NoSuchFileException) e).getFile() + " is not exist");
+ continue;
+ }
+ e.printStackTrace();
+ }
+ }
+ unsequenceSpaceCost += (System.currentTimeMillis() - startTime);
+ unseqSpaceStatistics.unsequenceFileNum += unseqFiles.size();
+ return unseqSpaceStatistics;
+ }
+
+ public OverlapStatistic processSequenceSpaceAsync(
+ SequenceFileSubTaskThreadExecutor executor,
+ UnseqSpaceStatistics unseqSpaceStatistics,
+ List<String> seqFiles) {
+ long startTime = System.currentTimeMillis();
+ OverlapStatistic overlapStatistic = new OverlapStatistic();
+ List<Future<SequenceFileTaskSummary>> futures = new ArrayList<>();
+ for (String seqFile : seqFiles) {
+ futures.add(executor.submit(new SingleSequenceFileTask(unseqSpaceStatistics, seqFile)));
+ }
+ for (Future<SequenceFileTaskSummary> future : futures) {
+ try {
+ SequenceFileTaskSummary sequenceFileTaskSummary = future.get();
+ overlapStatistic.mergeSingleSequenceFileTaskResult(sequenceFileTaskSummary);
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ }
+ overlapStatistic.mergeUnSeqSpaceStatistics(unseqSpaceStatistics);
+
+ sequenceSpaceCost += (System.currentTimeMillis() - startTime);
+ return overlapStatistic;
+ }
+}
diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/storageengine/dataregion/compaction/tool/TimePartitionProcessWorker.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/storageengine/dataregion/compaction/tool/TimePartitionProcessWorker.java
new file mode 100644
index 00000000000..a8e621a7c36
--- /dev/null
+++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/storageengine/dataregion/compaction/tool/TimePartitionProcessWorker.java
@@ -0,0 +1,58 @@
+/*
+ * 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.db.storageengine.dataregion.compaction.tool;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.concurrent.CountDownLatch;
+
+public class TimePartitionProcessWorker {
+ private final List<TimePartitionProcessTask> workerTaskList;
+ private final List<OverlapStatistic> workerResults;
+
+ public TimePartitionProcessWorker() {
+ workerTaskList = new ArrayList<>();
+ workerResults = new ArrayList<>();
+ }
+
+ public void addTask(TimePartitionProcessTask task) {
+ workerTaskList.add(task);
+ }
+
+ public void run(CountDownLatch latch) {
+ new Thread(
+ () -> {
+ SequenceFileSubTaskThreadExecutor fileProcessTaskExecutor =
+ new SequenceFileSubTaskThreadExecutor(OverlapStatisticTool.subTaskNum);
+ while (!workerTaskList.isEmpty()) {
+ TimePartitionProcessTask task = workerTaskList.remove(0);
+ OverlapStatistic partialRet = task.processTimePartition(fileProcessTaskExecutor);
+ workerResults.add(partialRet);
+ }
+ latch.countDown();
+ fileProcessTaskExecutor.shutdown();
+ })
+ .start();
+ }
+
+ public List<OverlapStatistic> getWorkerResults() {
+ return workerResults;
+ }
+}
diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/storageengine/dataregion/compaction/tool/TsFileStatisticReader.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/storageengine/dataregion/compaction/tool/TsFileStatisticReader.java
new file mode 100644
index 00000000000..7b414a54fa6
--- /dev/null
+++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/storageengine/dataregion/compaction/tool/TsFileStatisticReader.java
@@ -0,0 +1,96 @@
+/*
+ * 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.db.storageengine.dataregion.compaction.tool;
+
+import org.apache.iotdb.tsfile.file.metadata.ChunkMetadata;
+import org.apache.iotdb.tsfile.read.TsFileDeviceIterator;
+import org.apache.iotdb.tsfile.read.TsFileSequenceReader;
+import org.apache.iotdb.tsfile.utils.Pair;
+
+import java.io.Closeable;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+
+public class TsFileStatisticReader implements Closeable {
+
+ private final TsFileSequenceReader reader;
+
+ public TsFileStatisticReader(String filePath) throws IOException {
+ reader = new TsFileSequenceReader(filePath);
+ }
+
+ public List<ChunkGroupStatistics> getChunkGroupStatisticsList() throws IOException {
+ TsFileDeviceIterator allDevicesIteratorWithIsAligned =
+ reader.getAllDevicesIteratorWithIsAligned();
+ List<ChunkGroupStatistics> chunkGroupStatisticsList = new ArrayList<>();
+ while (allDevicesIteratorWithIsAligned.hasNext()) {
+ Pair<String, Boolean> deviceWithIsAligned = allDevicesIteratorWithIsAligned.next();
+ String deviceId = deviceWithIsAligned.left;
+
+ ChunkGroupStatistics chunkGroupStatistics = new ChunkGroupStatistics(deviceId);
+ Iterator<Map<String, List<ChunkMetadata>>> measurementChunkMetadataListMapIterator =
+ reader.getMeasurementChunkMetadataListMapIterator(deviceId);
+
+ while (measurementChunkMetadataListMapIterator.hasNext()) {
+ Map<String, List<ChunkMetadata>> measurementChunkMetadataListMap =
+ measurementChunkMetadataListMapIterator.next();
+ for (Map.Entry<String, List<ChunkMetadata>> measurementChunkMetadataList :
+ measurementChunkMetadataListMap.entrySet()) {
+ List<ChunkMetadata> chunkMetadataList = measurementChunkMetadataList.getValue();
+ chunkGroupStatistics.chunkMetadataList.addAll(chunkMetadataList);
+ chunkGroupStatistics.totalChunkNum += chunkMetadataList.size();
+ }
+ }
+ chunkGroupStatisticsList.add(chunkGroupStatistics);
+ }
+ return chunkGroupStatisticsList;
+ }
+
+ @Override
+ public void close() throws IOException {
+ this.reader.close();
+ }
+
+ public static class ChunkGroupStatistics {
+ private final String deviceID;
+ private final List<ChunkMetadata> chunkMetadataList;
+ private int totalChunkNum = 0;
+
+ private ChunkGroupStatistics(String deviceId) {
+ this.deviceID = deviceId;
+ this.chunkMetadataList = new ArrayList<>();
+ }
+
+ public String getDeviceID() {
+ return deviceID;
+ }
+
+ public List<ChunkMetadata> getChunkMetadataList() {
+ return chunkMetadataList;
+ }
+
+ public int getTotalChunkNum() {
+ return totalChunkNum;
+ }
+ }
+}
diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/storageengine/dataregion/compaction/tool/UnseqSpaceStatistics.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/storageengine/dataregion/compaction/tool/UnseqSpaceStatistics.java
new file mode 100644
index 00000000000..2b929bb23e7
--- /dev/null
+++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/storageengine/dataregion/compaction/tool/UnseqSpaceStatistics.java
@@ -0,0 +1,84 @@
+/*
+ * 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.db.storageengine.dataregion.compaction.tool;
+
+import java.util.HashMap;
+import java.util.Map;
+
+public class UnseqSpaceStatistics {
+ public long unsequenceFileNum = 0;
+ public long unsequenceFileSize = 0;
+
+ public long unsequenceChunkNum = 0;
+ public long unsequenceChunkGroupNum = 0;
+
+ public long minStartTime = Long.MAX_VALUE;
+
+ public long maxEndTime = Long.MIN_VALUE;
+ private Map<String, Map<String, ITimeRange>> chunkStatisticMap = new HashMap<>();
+
+ private Map<String, ITimeRange> chunkGroupStatisticMap = new HashMap<>();
+
+ public void updateMeasurement(String device, String measurementUID, Interval interval) {
+ chunkStatisticMap
+ .computeIfAbsent(device, key -> new HashMap<>())
+ .computeIfAbsent(measurementUID, key -> new ListTimeRangeImpl())
+ .addInterval(interval);
+ }
+
+ public void updateDevice(String device, Interval interval) {
+ chunkGroupStatisticMap
+ .computeIfAbsent(device, key -> new ListTimeRangeImpl())
+ .addInterval(interval);
+ }
+
+ public boolean chunkHasOverlap(String device, String measurementUID, Interval interval) {
+ if (!chunkStatisticMap.containsKey(device)) {
+ return false;
+ }
+ if (!chunkStatisticMap.get(device).containsKey(measurementUID)) {
+ return false;
+ }
+ return chunkStatisticMap.get(device).get(measurementUID).isOverlapped(interval);
+ }
+
+ public boolean chunkGroupHasOverlap(String device, Interval interval) {
+ if (!chunkGroupStatisticMap.containsKey(device)) {
+ return false;
+ }
+ return chunkGroupStatisticMap.get(device).isOverlapped(interval);
+ }
+
+ public Map<String, Map<String, ITimeRange>> getChunkStatisticMap() {
+ return chunkStatisticMap;
+ }
+
+ public Map<String, ITimeRange> getChunkGroupStatisticMap() {
+ return chunkGroupStatisticMap;
+ }
+
+ public void setMaxEndTime(long maxEndTime) {
+ this.maxEndTime = Math.max(this.maxEndTime, maxEndTime);
+ }
+
+ public void setMinStartTime(long minStartTime) {
+ this.minStartTime = Math.min(this.minStartTime, minStartTime);
+ }
+}
diff --git a/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/storageengine/dataregion/compaction/tools/ListTimeRangeImplTest.java b/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/storageengine/dataregion/compaction/tools/ListTimeRangeImplTest.java
new file mode 100644
index 00000000000..c3caaa16d77
--- /dev/null
+++ b/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/storageengine/dataregion/compaction/tools/ListTimeRangeImplTest.java
@@ -0,0 +1,138 @@
+/*
+ * 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.db.storageengine.dataregion.compaction.tools;
+
+import org.apache.iotdb.db.storageengine.dataregion.compaction.tool.Interval;
+import org.apache.iotdb.db.storageengine.dataregion.compaction.tool.ListTimeRangeImpl;
+
+import org.junit.Assert;
+import org.junit.Test;
+
+public class ListTimeRangeImplTest {
+
+ ListTimeRangeImpl listTimeRange = new ListTimeRangeImpl();
+
+ @Test
+ public void test01() {
+ listTimeRange.addInterval(new Interval(30, 40));
+ Assert.assertEquals(1, listTimeRange.getIntervalList().size());
+ Assert.assertEquals(30, listTimeRange.getIntervalList().get(0).getStart());
+ Assert.assertEquals(40, listTimeRange.getIntervalList().get(0).getEnd());
+ }
+
+ @Test
+ public void test02() {
+ listTimeRange.addInterval(new Interval(30, 40));
+ listTimeRange.addInterval(new Interval(10, 20));
+ listTimeRange.addInterval(new Interval(15, 20));
+ listTimeRange.addInterval(new Interval(50, 60));
+ Assert.assertEquals(3, listTimeRange.getIntervalList().size());
+ }
+
+ @Test
+ public void test03() {
+ listTimeRange.addInterval(new Interval(30, 40));
+ listTimeRange.addInterval(new Interval(10, 20));
+ listTimeRange.addInterval(new Interval(15, 20));
+ listTimeRange.addInterval(new Interval(50, 60));
+ listTimeRange.addInterval(new Interval(1, 100));
+ Assert.assertEquals(1, listTimeRange.getIntervalList().size());
+ Assert.assertEquals(1, listTimeRange.getIntervalList().get(0).getStart());
+ Assert.assertEquals(100, listTimeRange.getIntervalList().get(0).getEnd());
+ }
+
+ @Test
+ public void test04() {
+ listTimeRange.addInterval(new Interval(30, 40));
+ listTimeRange.addInterval(new Interval(10, 20));
+ listTimeRange.addInterval(new Interval(15, 20));
+ listTimeRange.addInterval(new Interval(50, 60));
+ listTimeRange.addInterval(new Interval(5, 100));
+ Assert.assertFalse(listTimeRange.isOverlapped(new Interval(1, 1)));
+ Assert.assertFalse(listTimeRange.isOverlapped(new Interval(101, 103)));
+ }
+
+ @Test
+ public void test05() {
+ listTimeRange.addInterval(new Interval(30, 40));
+ listTimeRange.addInterval(new Interval(10, 20));
+ listTimeRange.addInterval(new Interval(20, 30));
+ Assert.assertEquals(1, listTimeRange.getIntervalList().size());
+ }
+
+ @Test
+ public void test06() {
+ listTimeRange.addInterval(new Interval(1, 100));
+ listTimeRange.addInterval(new Interval(1, 2000));
+ Assert.assertEquals(1, listTimeRange.getIntervalList().size());
+ }
+
+ @Test
+ public void test07() {
+ listTimeRange.addInterval(new Interval(1, 10));
+ listTimeRange.addInterval(new Interval(60, 70));
+ listTimeRange.addInterval(new Interval(51, 55));
+ Assert.assertEquals(51, listTimeRange.getIntervalList().get(1).getStart());
+ }
+
+ @Test
+ public void testNoOverlap() {
+ ListTimeRangeImpl listTimeRange = new ListTimeRangeImpl();
+ listTimeRange.addInterval(new Interval(3, 5));
+ Assert.assertFalse(listTimeRange.isOverlapped(new Interval(6, 10)));
+ Assert.assertFalse(listTimeRange.isOverlapped(new Interval(1, 2)));
+ }
+
+ @Test
+ public void testStartTimeOverlap() {
+ ListTimeRangeImpl listTimeRange = new ListTimeRangeImpl();
+ listTimeRange.addInterval(new Interval(1, 5));
+ Assert.assertTrue(listTimeRange.isOverlapped(new Interval(4, 8)));
+ }
+
+ @Test
+ public void testEndTimeOverlap() {
+ ListTimeRangeImpl listTimeRange = new ListTimeRangeImpl();
+ listTimeRange.addInterval(new Interval(1, 5));
+ Assert.assertTrue(listTimeRange.isOverlapped(new Interval(0, 4)));
+ }
+
+ @Test
+ public void testFullyOverlap() {
+ ListTimeRangeImpl listTimeRange = new ListTimeRangeImpl();
+ listTimeRange.addInterval(new Interval(2, 4));
+ Assert.assertTrue(listTimeRange.isOverlapped(new Interval(1, 5)));
+ }
+
+ @Test
+ public void testIntervalInsideCurrentInterval() {
+ ListTimeRangeImpl listTimeRange = new ListTimeRangeImpl();
+ listTimeRange.addInterval(new Interval(1, 5));
+ Assert.assertTrue(listTimeRange.isOverlapped(new Interval(2, 4)));
+ }
+
+ @Test
+ public void testBoundary() {
+ ListTimeRangeImpl listTimeRange = new ListTimeRangeImpl();
+ listTimeRange.addInterval(new Interval(3, 5));
+ Assert.assertTrue(listTimeRange.isOverlapped(new Interval(1, 3)));
+ Assert.assertTrue(listTimeRange.isOverlapped(new Interval(5, 6)));
+ }
+}
diff --git a/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/storageengine/dataregion/compaction/tools/UnseqSpaceStatisticsTest.java b/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/storageengine/dataregion/compaction/tools/UnseqSpaceStatisticsTest.java
new file mode 100644
index 00000000000..aad3ef312cd
--- /dev/null
+++ b/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/storageengine/dataregion/compaction/tools/UnseqSpaceStatisticsTest.java
@@ -0,0 +1,63 @@
+/*
+ * 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.db.storageengine.dataregion.compaction.tools;
+
+import org.apache.iotdb.db.storageengine.dataregion.compaction.tool.Interval;
+import org.apache.iotdb.db.storageengine.dataregion.compaction.tool.UnseqSpaceStatistics;
+
+import org.junit.Assert;
+import org.junit.Test;
+
+public class UnseqSpaceStatisticsTest {
+
+ @Test
+ public void test01() {
+ UnseqSpaceStatistics unseqSpaceStatistics = new UnseqSpaceStatistics();
+ unseqSpaceStatistics.updateMeasurement("root.db.d1", "s1", new Interval(1, 10));
+ unseqSpaceStatistics.updateMeasurement("root.db.d1", "s1", new Interval(5, 15));
+ unseqSpaceStatistics.updateMeasurement("root.db.d1", "s2", new Interval(1, 10));
+ unseqSpaceStatistics.updateMeasurement("root.db.d2", "s2", new Interval(1, 10));
+
+ Assert.assertEquals(2, unseqSpaceStatistics.getChunkStatisticMap().size());
+ Assert.assertEquals(2, unseqSpaceStatistics.getChunkStatisticMap().get("root.db.d1").size());
+ Assert.assertEquals(1, unseqSpaceStatistics.getChunkStatisticMap().get("root.db.d2").size());
+ }
+
+ @Test
+ public void test02() {
+ UnseqSpaceStatistics unseqSpaceStatistics = new UnseqSpaceStatistics();
+ unseqSpaceStatistics.updateMeasurement("root.db.d1", "s1", new Interval(1, 10));
+ unseqSpaceStatistics.updateMeasurement("root.db.d1", "s1", new Interval(5, 15));
+ unseqSpaceStatistics.updateMeasurement("root.db.d1", "s2", new Interval(1, 10));
+ unseqSpaceStatistics.updateMeasurement("root.db.d2", "s2", new Interval(1, 10));
+
+ Assert.assertTrue(
+ unseqSpaceStatistics.chunkHasOverlap("root.db.d1", "s1", new Interval(1, 10)));
+ Assert.assertFalse(
+ unseqSpaceStatistics.chunkHasOverlap("root.db.d1", "s4", new Interval(1, 10)));
+ Assert.assertFalse(
+ unseqSpaceStatistics.chunkHasOverlap("root.db.d2", "s1", new Interval(1, 10)));
+
+ Assert.assertFalse(
+ unseqSpaceStatistics.chunkHasOverlap("root.db.d3", "s1", new Interval(1, 10)));
+ Assert.assertFalse(
+ unseqSpaceStatistics.chunkHasOverlap("root.db.d1", "s1", new Interval(21, 30)));
+ }
+}