This is an automated email from the ASF dual-hosted git repository.
jiangtian pushed a commit to branch load_v2
in repository https://gitbox.apache.org/repos/asf/iotdb.git
The following commit(s) were added to refs/heads/load_v2 by this push:
new cd3890a55de add BatchedTsFileExtractor
cd3890a55de is described below
commit cd3890a55de1865f34a1fde8b35e8bb48c5fcce0
Author: Tian Jiang <[email protected]>
AuthorDate: Mon Oct 16 10:42:10 2023 +0800
add BatchedTsFileExtractor
---
.../dml/insertion/TsFileBatchInsertionEvent.java | 37 +++
.../tsfile/PipeBatchTsFileInsertionEvent.java | 240 +++++++++++++++++++
.../tsfile/TsFileListInsertionDataContainer.java | 261 +++++++++++++++++++++
.../historical/BatchedTsFileExtractor.java | 85 +++++++
.../PipeHistoricalDataRegionTsFileExtractor.java | 18 +-
5 files changed, 632 insertions(+), 9 deletions(-)
diff --git
a/iotdb-api/pipe-api/src/main/java/org/apache/iotdb/pipe/api/event/dml/insertion/TsFileBatchInsertionEvent.java
b/iotdb-api/pipe-api/src/main/java/org/apache/iotdb/pipe/api/event/dml/insertion/TsFileBatchInsertionEvent.java
new file mode 100644
index 00000000000..bc2b682d82f
--- /dev/null
+++
b/iotdb-api/pipe-api/src/main/java/org/apache/iotdb/pipe/api/event/dml/insertion/TsFileBatchInsertionEvent.java
@@ -0,0 +1,37 @@
+/*
+ * 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.pipe.api.event.dml.insertion;
+
+import org.apache.iotdb.pipe.api.event.Event;
+
+/**
+ * {@link TsFileBatchInsertionEvent} is used to define the event of writing a
batch of TsFiles.
+ * Event data stores in disks, which is compressed and encoded, and requires
IO cost for
+ * computational processing.
+ */
+public interface TsFileBatchInsertionEvent extends Event, AutoCloseable {
+
+ /**
+ * The method is used to convert the TsFileBatchInsertionEvent into several
TabletInsertionEvents.
+ *
+ * @return {@code Iterable<TabletInsertionEvent>} the list of
TabletInsertionEvent
+ */
+ Iterable<TabletInsertionEvent> toTabletInsertionEvents();
+}
diff --git
a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/pipe/event/common/tsfile/PipeBatchTsFileInsertionEvent.java
b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/pipe/event/common/tsfile/PipeBatchTsFileInsertionEvent.java
new file mode 100644
index 00000000000..284c84096ce
--- /dev/null
+++
b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/pipe/event/common/tsfile/PipeBatchTsFileInsertionEvent.java
@@ -0,0 +1,240 @@
+/*
+ * 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.pipe.event.common.tsfile;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.List;
+import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.stream.Collectors;
+import org.apache.iotdb.commons.consensus.index.ProgressIndex;
+import org.apache.iotdb.commons.consensus.index.impl.MinimumProgressIndex;
+import org.apache.iotdb.commons.pipe.task.meta.PipeTaskMeta;
+import org.apache.iotdb.db.pipe.event.EnrichedEvent;
+import org.apache.iotdb.db.pipe.resource.PipeResourceManager;
+import org.apache.iotdb.db.storageengine.dataregion.memtable.TsFileProcessor;
+import org.apache.iotdb.db.storageengine.dataregion.tsfile.TsFileResource;
+import org.apache.iotdb.pipe.api.event.dml.insertion.TabletInsertionEvent;
+import org.apache.iotdb.pipe.api.event.dml.insertion.TsFileBatchInsertionEvent;
+import org.apache.iotdb.pipe.api.event.dml.insertion.TsFileInsertionEvent;
+import org.apache.iotdb.pipe.api.exception.PipeException;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class PipeBatchTsFileInsertionEvent extends EnrichedEvent implements
+ TsFileBatchInsertionEvent {
+
+ private static final Logger LOGGER =
LoggerFactory.getLogger(PipeBatchTsFileInsertionEvent.class);
+
+ // used to filter data
+ private final long startTime;
+ private final long endTime;
+ private final boolean needParseTime;
+
+ private final List<TsFileResource> resources;
+ private List<File> tsFiles;
+
+ private final boolean isGeneratedByPipe;
+
+ private final AtomicBoolean[] isClosed;
+
+ private TsFileListInsertionDataContainer dataContainer;
+
+ public PipeBatchTsFileInsertionEvent(List<TsFileResource> resources, boolean
isGeneratedByPipe) {
+ this(resources, isGeneratedByPipe, null, null, Long.MIN_VALUE,
Long.MAX_VALUE, false);
+ }
+
+ public PipeBatchTsFileInsertionEvent(
+ List<TsFileResource> resources,
+ boolean isGeneratedByPipe,
+ PipeTaskMeta pipeTaskMeta,
+ String pattern,
+ long startTime,
+ long endTime,
+ boolean needParseTime) {
+ super(pipeTaskMeta, pattern);
+
+ this.startTime = startTime;
+ this.endTime = endTime;
+ this.needParseTime = needParseTime;
+
+ if (needParseTime) {
+ this.isPatternAndTimeParsed = false;
+ }
+
+ this.resources = resources;
+ tsFiles =
resources.stream().map(TsFileResource::getTsFile).collect(Collectors.toList());
+ isClosed = new AtomicBoolean[resources.size()];
+
+ this.isGeneratedByPipe = isGeneratedByPipe;
+
+ for (int i = 0; i < isClosed.length; i++) {
+ TsFileResource resource = resources.get(i);
+ isClosed[i] = new AtomicBoolean(resource.isClosed());
+ if (!isClosed[i].get()) {
+ final TsFileProcessor processor = resource.getProcessor();
+ if (processor != null) {
+ int finalI = i;
+ processor.addCloseFileListener(
+ o -> {
+ synchronized (isClosed[finalI]) {
+ isClosed[finalI].set(true);
+ isClosed[finalI].notifyAll();
+ }
+ });
+ }
+ }
+ // check again after register close listener in case TsFile is closed
during the process
+ isClosed[i].set(resource.isClosed());
+ }
+ }
+
+ public void waitForTsFileClose() throws InterruptedException {
+ for (AtomicBoolean signal : isClosed) {
+ if (!signal.get()) {
+ synchronized (signal) {
+ while (!signal.get()) {
+ isClosed.wait();
+ }
+ }
+ }
+ }
+ }
+
+ public List<File> getTsFiles() {
+ return tsFiles;
+ }
+
+ /////////////////////////// EnrichedEvent ///////////////////////////
+
+ @Override
+ public boolean internallyIncreaseResourceReferenceCount(String
holderMessage) {
+ for (int i = 0; i < tsFiles.size(); i++) {
+ File tsFile = tsFiles.get(i);
+ try {
+ tsFiles.set(i,
PipeResourceManager.tsfile().increaseFileReference(tsFile, true));
+ } catch (Exception e) {
+ LOGGER.warn(
+ String.format(
+ "Increase reference count for TsFile %s error. Holder Message:
%s",
+ tsFile.getPath(), holderMessage),
+ e);
+ return false;
+ }
+ }
+ return true;
+ }
+
+ @Override
+ public boolean internallyDecreaseResourceReferenceCount(String
holderMessage) {
+ for (File tsFile : tsFiles) {
+ try {
+ PipeResourceManager.tsfile().decreaseFileReference(tsFile);
+ } catch (Exception e) {
+ LOGGER.warn(
+ String.format(
+ "Decrease reference count for TsFile %s error. Holder Message:
%s",
+ tsFile.getPath(), holderMessage),
+ e);
+ return false;
+ }
+ }
+ return true;
+ }
+
+ @Override
+ public ProgressIndex getProgressIndex() {
+ try {
+ waitForTsFileClose();
+ return resources.get(resources.size() -
1).getMaxProgressIndexAfterClose();
+ } catch (InterruptedException e) {
+ LOGGER.warn(
+ String.format(
+ "Interrupted when waiting for closing TsFiles %s.", resources));
+ Thread.currentThread().interrupt();
+ return MinimumProgressIndex.INSTANCE;
+ }
+ }
+
+ @Override
+ public PipeBatchTsFileInsertionEvent
shallowCopySelfAndBindPipeTaskMetaForProgressReport(
+ PipeTaskMeta pipeTaskMeta, String pattern) {
+ return new PipeBatchTsFileInsertionEvent(
+ resources, isGeneratedByPipe, pipeTaskMeta, pattern, startTime,
endTime, needParseTime);
+ }
+
+ @Override
+ public boolean isGeneratedByPipe() {
+ return isGeneratedByPipe;
+ }
+
+ /////////////////////////// TsFileInsertionEvent ///////////////////////////
+
+ @Override
+ public Iterable<TabletInsertionEvent> toTabletInsertionEvents() {
+ try {
+ if (dataContainer == null) {
+ waitForTsFileClose();
+ dataContainer =
+ new TsFileListInsertionDataContainer(
+ tsFiles, getPattern(), startTime, endTime, pipeTaskMeta, this);
+ }
+ return dataContainer.toTabletInsertionEvents();
+ } catch (InterruptedException e) {
+ Thread.currentThread().interrupt();
+ close();
+
+ final String errorMsg =
+ String.format(
+ "Interrupted when waiting for closing TsFiles %s.", resources);
+ LOGGER.warn(errorMsg, e);
+ throw new PipeException(errorMsg);
+ } catch (IOException e) {
+ close();
+
+ final String errorMsg = String.format("Read TsFiles %s error.",
resources);
+ LOGGER.warn(errorMsg, e);
+ throw new PipeException(errorMsg);
+ }
+ }
+
+ /** Release the resource of data container. */
+ @Override
+ public void close() {
+ if (dataContainer != null) {
+ dataContainer.close();
+ dataContainer = null;
+ }
+ }
+
+ /////////////////////////// Object ///////////////////////////
+
+ @Override
+ public String toString() {
+ return "PipeTsFileInsertionEvent{"
+ + "resources="
+ + resources
+ + ", tsFiles="
+ + tsFiles
+ + ", isClosed="
+ + isClosed
+ + '}';
+ }
+}
diff --git
a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/pipe/event/common/tsfile/TsFileListInsertionDataContainer.java
b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/pipe/event/common/tsfile/TsFileListInsertionDataContainer.java
new file mode 100644
index 00000000000..6055f6f845d
--- /dev/null
+++
b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/pipe/event/common/tsfile/TsFileListInsertionDataContainer.java
@@ -0,0 +1,261 @@
+/*
+ * 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.pipe.event.common.tsfile;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.NoSuchElementException;
+import org.apache.iotdb.commons.pipe.task.meta.PipeTaskMeta;
+import org.apache.iotdb.db.pipe.event.EnrichedEvent;
+import
org.apache.iotdb.db.pipe.event.common.tablet.PipeRawTabletInsertionEvent;
+import org.apache.iotdb.pipe.api.event.dml.insertion.TabletInsertionEvent;
+import org.apache.iotdb.pipe.api.exception.PipeException;
+import org.apache.iotdb.tsfile.common.constant.TsFileConstant;
+import org.apache.iotdb.tsfile.file.metadata.enums.TSDataType;
+import org.apache.iotdb.tsfile.read.TsFileDeviceIterator;
+import org.apache.iotdb.tsfile.read.TsFileReader;
+import org.apache.iotdb.tsfile.read.TsFileSequenceReader;
+import org.apache.iotdb.tsfile.read.expression.IExpression;
+import org.apache.iotdb.tsfile.read.expression.impl.BinaryExpression;
+import org.apache.iotdb.tsfile.read.expression.impl.GlobalTimeExpression;
+import org.apache.iotdb.tsfile.read.filter.TimeFilter;
+import org.apache.iotdb.tsfile.utils.Pair;
+import org.apache.iotdb.tsfile.write.record.Tablet;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class TsFileListInsertionDataContainer implements AutoCloseable {
+
+ private static final Logger LOGGER = LoggerFactory.getLogger(
+ TsFileListInsertionDataContainer.class);
+
+ private final String pattern; // used to filter data
+ private final IExpression timeFilterExpression; // used to filter data
+
+ private final PipeTaskMeta pipeTaskMeta; // used to report progress
+ private final EnrichedEvent sourceEvent; // used to report progress
+
+ private final List<TsFileSequenceReader> tsFileSequenceReaders;
+ private final List<TsFileReader> tsFileReaders;
+ private int currFileIndex = 0;
+
+ private final List<Iterator<Map.Entry<String, List<String>>>>
deviceMeasurementsMapIterators;
+ private final List<Map<String, Boolean>> deviceIsAlignedMaps;
+ private final List<Map<String, TSDataType>> measurementDataTypeMaps;
+
+ public TsFileListInsertionDataContainer(List<File> tsFiles, String pattern,
long startTime,
+ long endTime)
+ throws IOException {
+ this(tsFiles, pattern, startTime, endTime, null, null);
+ }
+
+ public TsFileListInsertionDataContainer(
+ List<File> tsFiles,
+ String pattern,
+ long startTime,
+ long endTime,
+ PipeTaskMeta pipeTaskMeta,
+ EnrichedEvent sourceEvent)
+ throws IOException {
+ this.pattern = pattern;
+ timeFilterExpression =
+ (startTime == Long.MIN_VALUE && endTime == Long.MAX_VALUE)
+ ? null
+ : BinaryExpression.and(
+ new GlobalTimeExpression(TimeFilter.gtEq(startTime)),
+ new GlobalTimeExpression(TimeFilter.ltEq(endTime)));
+
+ this.pipeTaskMeta = pipeTaskMeta;
+ this.sourceEvent = sourceEvent;
+
+ try {
+ tsFileSequenceReaders = new ArrayList<>();
+ tsFileReaders = new ArrayList<>();
+ for (File tsFile : tsFiles) {
+ TsFileSequenceReader tsFileSequenceReader = new TsFileSequenceReader(
+ tsFile.getAbsolutePath(), true, true);
+ tsFileSequenceReaders.add(tsFileSequenceReader);
+ tsFileReaders.add(new TsFileReader(tsFileSequenceReader));
+ }
+
+ deviceMeasurementsMapIterators = new ArrayList<>();
+ deviceIsAlignedMaps = new ArrayList<>();
+ for (int i = 0; i < tsFiles.size(); i++) {
+ deviceMeasurementsMapIterators.add(
+ filterDeviceMeasurementsMapByPattern(i).entrySet().iterator());
+ deviceIsAlignedMaps.add(readDeviceIsAlignedMap(i));
+ }
+ measurementDataTypeMaps = new ArrayList<>();
+ for (TsFileSequenceReader tsFileSequenceReader : tsFileSequenceReaders) {
+
measurementDataTypeMaps.add(tsFileSequenceReader.getFullPathDataTypeMap());
+ }
+ } catch (Exception e) {
+ close();
+ throw e;
+ }
+ }
+
+ private Map<String, List<String>> filterDeviceMeasurementsMapByPattern(int
i) throws IOException {
+ final Map<String, List<String>> filteredDeviceMeasurementsMap = new
HashMap<>();
+
+ for (Map.Entry<String, List<String>> entry :
+ tsFileSequenceReaders.get(i).getDeviceMeasurementsMap().entrySet()) {
+ final String deviceId = entry.getKey();
+
+ // case 1: for example, pattern is root.a.b or pattern is null and
device is root.a.b.c
+ // in this case, all data can be matched without checking the
measurements
+ if (pattern == null
+ || pattern.length() <= deviceId.length() &&
deviceId.startsWith(pattern)) {
+ if (!entry.getValue().isEmpty()) {
+ filteredDeviceMeasurementsMap.put(deviceId, entry.getValue());
+ }
+ }
+
+ // case 2: for example, pattern is root.a.b.c and device is root.a.b
+ // in this case, we need to check the full path
+ else if (pattern.length() > deviceId.length() &&
pattern.startsWith(deviceId)) {
+ final List<String> filteredMeasurements = new ArrayList<>();
+
+ for (final String measurement : entry.getValue()) {
+ // low cost check comes first
+ if (pattern.length() == deviceId.length() + measurement.length() + 1
+ // high cost check comes later
+ && pattern.endsWith(TsFileConstant.PATH_SEPARATOR +
measurement)) {
+ filteredMeasurements.add(measurement);
+ }
+ }
+
+ if (!filteredMeasurements.isEmpty()) {
+ filteredDeviceMeasurementsMap.put(deviceId, filteredMeasurements);
+ }
+ }
+ }
+
+ return filteredDeviceMeasurementsMap;
+ }
+
+ private Map<String, Boolean> readDeviceIsAlignedMap(int i) throws
IOException {
+ final Map<String, Boolean> deviceIsAlignedResultMap = new HashMap<>();
+ final TsFileDeviceIterator deviceIsAlignedIterator =
+ tsFileSequenceReaders.get(i).getAllDevicesIteratorWithIsAligned();
+ while (deviceIsAlignedIterator.hasNext()) {
+ final Pair<String, Boolean> deviceIsAlignedPair =
deviceIsAlignedIterator.next();
+ deviceIsAlignedResultMap.put(deviceIsAlignedPair.getLeft(),
deviceIsAlignedPair.getRight());
+ }
+ return deviceIsAlignedResultMap;
+ }
+
+ /**
+ * @return TabletInsertionEvent in a streaming way
+ */
+ public Iterable<TabletInsertionEvent> toTabletInsertionEvents() {
+ return () ->
+ new Iterator<TabletInsertionEvent>() {
+
+ private TsFileInsertionDataTabletIterator tabletIterator = null;
+
+ @Override
+ public boolean hasNext() {
+ while (tabletIterator == null || !tabletIterator.hasNext()) {
+ if
(!deviceMeasurementsMapIterators.get(currFileIndex).hasNext()) {
+ if (currFileIndex >= tsFileReaders.size()) {
+ close();
+ return false;
+ } else {
+ currFileIndex++;
+ continue;
+ }
+ }
+
+ final Map.Entry<String, List<String>> entry =
deviceMeasurementsMapIterators.get(
+ currFileIndex).next();
+
+ try {
+ tabletIterator =
+ new TsFileInsertionDataTabletIterator(
+ tsFileReaders.get(currFileIndex),
+ measurementDataTypeMaps.get(currFileIndex),
+ entry.getKey(),
+ entry.getValue(),
+ timeFilterExpression);
+ } catch (IOException e) {
+ close();
+ throw new PipeException("failed to create
TsFileInsertionDataTabletIterator", e);
+ }
+ }
+
+ return true;
+ }
+
+ @Override
+ public TabletInsertionEvent next() {
+ if (!hasNext()) {
+ close();
+ throw new NoSuchElementException();
+ }
+
+ final Tablet tablet = tabletIterator.next();
+ final boolean isAligned = deviceIsAlignedMaps.get(currFileIndex)
+ .getOrDefault(tablet.deviceId, false);
+
+ final TabletInsertionEvent next;
+ if (!hasNext()) {
+ next =
+ new PipeRawTabletInsertionEvent(
+ tablet, isAligned, pipeTaskMeta, sourceEvent, true);
+ close();
+ } else {
+ next =
+ new PipeRawTabletInsertionEvent(
+ tablet, isAligned, pipeTaskMeta, sourceEvent, false);
+ }
+ return next;
+ }
+ };
+ }
+
+ @Override
+ public void close() {
+ for (TsFileReader tsFileReader : tsFileReaders) {
+ try {
+ if (tsFileReader != null) {
+ tsFileReader.close();
+ }
+ } catch (IOException e) {
+ LOGGER.warn("Failed to close TsFileReader", e);
+ }
+ }
+
+ for (TsFileSequenceReader tsFileSequenceReader : tsFileSequenceReaders) {
+ try {
+ if (tsFileSequenceReader != null) {
+ tsFileSequenceReader.close();
+ }
+ } catch (IOException e) {
+ LOGGER.warn("Failed to close TsFileSequenceReader", e);
+ }
+ }
+ }
+}
diff --git
a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/pipe/extractor/historical/BatchedTsFileExtractor.java
b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/pipe/extractor/historical/BatchedTsFileExtractor.java
new file mode 100644
index 00000000000..463c529fedb
--- /dev/null
+++
b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/pipe/extractor/historical/BatchedTsFileExtractor.java
@@ -0,0 +1,85 @@
+/*
+ * 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.pipe.extractor.historical;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+import
org.apache.iotdb.db.pipe.event.common.tsfile.PipeBatchTsFileInsertionEvent;
+import org.apache.iotdb.db.pipe.resource.PipeResourceManager;
+import org.apache.iotdb.db.storageengine.dataregion.tsfile.TsFileResource;
+import org.apache.iotdb.pipe.api.event.Event;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Similar to the base class, but it batches several files as an event to
enable further
+ * optimization during the latter transfer.
+ */
+public class BatchedTsFileExtractor extends
PipeHistoricalDataRegionTsFileExtractor {
+
+ private static final Logger LOGGER =
LoggerFactory.getLogger(BatchedTsFileExtractor.class);
+ private int maxBatchSize;
+
+ public BatchedTsFileExtractor(int maxBatchSize) {
+ this.maxBatchSize = Math.max(1, maxBatchSize);
+ }
+
+ @Override
+ public synchronized Event supply() {
+ if (pendingQueue == null) {
+ return null;
+ }
+ TsFileResource resource = pendingQueue.poll();
+ if (resource == null) {
+ return null;
+ }
+ List<TsFileResource> tsFileResourceList = new ArrayList<>(maxBatchSize);
+ tsFileResourceList.add(resource);
+ while (!pendingQueue.isEmpty() && tsFileResourceList.size() <
maxBatchSize) {
+ tsFileResourceList.add(resource);
+ }
+
+ final PipeBatchTsFileInsertionEvent event = new
PipeBatchTsFileInsertionEvent(
+ tsFileResourceList,
+ false,
+ pipeTaskMeta,
+ pattern,
+ historicalDataExtractionStartTime,
+ historicalDataExtractionEndTime,
+ !(isTsFileResourceCoveredByTimeRange(tsFileResourceList.get(0))
+ && isTsFileResourceCoveredByTimeRange(
+ tsFileResourceList.get(tsFileResourceList.size() - 1))));
+
+ event.increaseReferenceCount(BatchedTsFileExtractor.class.getName());
+
+ for (TsFileResource res : tsFileResourceList) {
+ try {
+ PipeResourceManager.tsfile().unpinTsFileResource(res);
+ } catch (IOException e) {
+ LOGGER.warn(
+ "Pipe: failed to unpin TsFileResource after creating event,
original path: {}",
+ resource.getTsFilePath());
+ }
+ }
+
+ return event;
+ }
+
+}
diff --git
a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/pipe/extractor/historical/PipeHistoricalDataRegionTsFileExtractor.java
b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/pipe/extractor/historical/PipeHistoricalDataRegionTsFileExtractor.java
index 6094b9766a0..4227e5315c1 100644
---
a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/pipe/extractor/historical/PipeHistoricalDataRegionTsFileExtractor.java
+++
b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/pipe/extractor/historical/PipeHistoricalDataRegionTsFileExtractor.java
@@ -62,19 +62,19 @@ public class PipeHistoricalDataRegionTsFileExtractor
implements PipeHistoricalDa
private static final Map<Integer, Long>
DATA_REGION_ID_TO_PIPE_FLUSHED_TIME_MAP = new HashMap<>();
private static final long PIPE_MIN_FLUSH_INTERVAL_IN_MS = 2000;
- private PipeTaskMeta pipeTaskMeta;
- private ProgressIndex startIndex;
+ protected PipeTaskMeta pipeTaskMeta;
+ protected ProgressIndex startIndex;
- private int dataRegionId;
+ protected int dataRegionId;
- private String pattern;
+ protected String pattern;
- private long historicalDataExtractionStartTime; // Event time
- private long historicalDataExtractionEndTime; // Event time
+ protected long historicalDataExtractionStartTime; // Event time
+ protected long historicalDataExtractionEndTime; // Event time
- private long historicalDataExtractionTimeLowerBound; // Arrival time
+ protected long historicalDataExtractionTimeLowerBound; // Arrival time
- private Queue<TsFileResource> pendingQueue;
+ protected Queue<TsFileResource> pendingQueue;
@Override
public void validate(PipeParameterValidator validator) {
@@ -249,7 +249,7 @@ public class PipeHistoricalDataRegionTsFileExtractor
implements PipeHistoricalDa
|| historicalDataExtractionEndTime < resource.getFileStartTime());
}
- private boolean isTsFileResourceCoveredByTimeRange(TsFileResource resource) {
+ protected boolean isTsFileResourceCoveredByTimeRange(TsFileResource
resource) {
return historicalDataExtractionStartTime <= resource.getFileStartTime()
&& historicalDataExtractionEndTime >= resource.getFileEndTime();
}