This is an automated email from the ASF dual-hosted git repository.
qiaojialin 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 9ee5aedc1d [IOTDB-2801] New storage engine framework (#5357)
9ee5aedc1d is described below
commit 9ee5aedc1d49540f7434a3ceef59705224670beb
Author: Haonan <[email protected]>
AuthorDate: Sat Apr 9 17:57:49 2022 +0800
[IOTDB-2801] New storage engine framework (#5357)
---
.../apache/iotdb/db/engine/StorageEngineV2.java | 638 +++++++++++++++++++++
.../iotdb/db/engine/memtable/AbstractMemTable.java | 198 +++++++
.../apache/iotdb/db/engine/memtable/IMemTable.java | 12 +
.../db/engine/storagegroup/TsFileProcessor.java | 209 +++++++
.../storagegroup/VirtualStorageGroupProcessor.java | 234 ++++++++
.../sql/planner/plan/node/write/InsertNode.java | 25 +-
.../planner/plan/node/write/InsertTabletNode.java | 6 +-
.../java/org/apache/iotdb/db/utils/MemUtils.java | 54 ++
.../db/writelog/node/ExclusiveWriteLogNode.java | 46 ++
.../iotdb/db/writelog/node/WriteLogNode.java | 9 +
10 files changed, 1421 insertions(+), 10 deletions(-)
diff --git
a/server/src/main/java/org/apache/iotdb/db/engine/StorageEngineV2.java
b/server/src/main/java/org/apache/iotdb/db/engine/StorageEngineV2.java
new file mode 100644
index 0000000000..538ae1ef67
--- /dev/null
+++ b/server/src/main/java/org/apache/iotdb/db/engine/StorageEngineV2.java
@@ -0,0 +1,638 @@
+/*
+ * 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.engine;
+
+import org.apache.iotdb.commons.concurrent.IoTDBThreadPoolFactory;
+import org.apache.iotdb.commons.concurrent.ThreadName;
+import org.apache.iotdb.commons.consensus.ConsensusGroupId;
+import org.apache.iotdb.commons.consensus.DataRegionId;
+import org.apache.iotdb.commons.exception.ShutdownException;
+import org.apache.iotdb.commons.service.IService;
+import org.apache.iotdb.commons.service.ServiceType;
+import org.apache.iotdb.commons.utils.TestOnly;
+import org.apache.iotdb.db.conf.IoTDBConfig;
+import org.apache.iotdb.db.conf.IoTDBDescriptor;
+import org.apache.iotdb.db.conf.ServerConfigConsistent;
+import org.apache.iotdb.db.engine.fileSystem.SystemFileFactory;
+import org.apache.iotdb.db.engine.flush.CloseFileListener;
+import org.apache.iotdb.db.engine.flush.FlushListener;
+import org.apache.iotdb.db.engine.flush.TsFileFlushPolicy;
+import org.apache.iotdb.db.engine.flush.TsFileFlushPolicy.DirectFlushPolicy;
+import org.apache.iotdb.db.engine.storagegroup.TsFileProcessor;
+import org.apache.iotdb.db.engine.storagegroup.VirtualStorageGroupProcessor;
+import org.apache.iotdb.db.exception.BatchProcessException;
+import org.apache.iotdb.db.exception.StorageEngineException;
+import org.apache.iotdb.db.exception.StorageGroupProcessorException;
+import org.apache.iotdb.db.exception.TsFileProcessorException;
+import org.apache.iotdb.db.exception.WriteProcessException;
+import org.apache.iotdb.db.exception.WriteProcessRejectException;
+import org.apache.iotdb.db.exception.metadata.MetadataException;
+import org.apache.iotdb.db.exception.runtime.StorageEngineFailureException;
+import org.apache.iotdb.db.mpp.sql.planner.plan.node.write.InsertRowNode;
+import org.apache.iotdb.db.mpp.sql.planner.plan.node.write.InsertTabletNode;
+import org.apache.iotdb.db.rescon.SystemInfo;
+import org.apache.iotdb.db.utils.ThreadUtils;
+import org.apache.iotdb.db.utils.UpgradeUtils;
+import org.apache.iotdb.rpc.RpcUtils;
+import org.apache.iotdb.rpc.TSStatusCode;
+import org.apache.iotdb.service.rpc.thrift.TSStatus;
+import org.apache.iotdb.tsfile.utils.FilePathUtils;
+import org.apache.iotdb.tsfile.utils.Pair;
+
+import org.apache.commons.io.FileUtils;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.ConcurrentModificationException;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.concurrent.*;
+import java.util.concurrent.atomic.AtomicBoolean;
+
+public class StorageEngineV2 implements IService {
+ private static final Logger logger =
LoggerFactory.getLogger(StorageEngineV2.class);
+
+ private static final IoTDBConfig config =
IoTDBDescriptor.getInstance().getConfig();
+ private static final long TTL_CHECK_INTERVAL = 60 * 1000L;
+
+ /**
+ * Time range for dividing storage group, the time unit is the same with
IoTDB's
+ * TimestampPrecision
+ */
+ @ServerConfigConsistent private static long timePartitionInterval = -1;
+ /** whether enable data partition if disabled, all data belongs to partition
0 */
+ @ServerConfigConsistent private static boolean enablePartition =
config.isEnablePartition();
+
+ private final boolean enableMemControl = config.isEnableMemControl();
+
+ /**
+ * a folder (system/storage_groups/ by default) that persist system info.
Each Storage Processor
+ * will have a subfolder under the systemDir.
+ */
+ private final String systemDir =
+ FilePathUtils.regularizePath(config.getSystemDir()) + "storage_groups";
+
+ /** DataRegionId -> DataRegion */
+ private final ConcurrentHashMap<ConsensusGroupId,
VirtualStorageGroupProcessor> dataRegionMap =
+ new ConcurrentHashMap<>();
+
+ private AtomicBoolean isAllSgReady = new AtomicBoolean(false);
+
+ private ScheduledExecutorService ttlCheckThread;
+ private ScheduledExecutorService seqMemtableTimedFlushCheckThread;
+ private ScheduledExecutorService unseqMemtableTimedFlushCheckThread;
+ private ScheduledExecutorService tsFileTimedCloseCheckThread;
+
+ private TsFileFlushPolicy fileFlushPolicy = new DirectFlushPolicy();
+ private ExecutorService recoveryThreadPool;
+ // add customized listeners here for flush and close events
+ private List<CloseFileListener> customCloseFileListeners = new ArrayList<>();
+ private List<FlushListener> customFlushListeners = new ArrayList<>();
+
+ private StorageEngineV2() {}
+
+ public static StorageEngineV2 getInstance() {
+ return InstanceHolder.INSTANCE;
+ }
+
+ private static void initTimePartition() {
+ timePartitionInterval =
+ convertMilliWithPrecision(
+ IoTDBDescriptor.getInstance().getConfig().getPartitionInterval() *
1000L);
+ }
+
+ public static long convertMilliWithPrecision(long milliTime) {
+ long result = milliTime;
+ String timePrecision =
IoTDBDescriptor.getInstance().getConfig().getTimestampPrecision();
+ switch (timePrecision) {
+ case "ns":
+ result = milliTime * 1000_000L;
+ break;
+ case "us":
+ result = milliTime * 1000L;
+ break;
+ default:
+ break;
+ }
+ return result;
+ }
+
+ public static long getTimePartitionInterval() {
+ if (timePartitionInterval == -1) {
+ initTimePartition();
+ }
+ return timePartitionInterval;
+ }
+
+ @TestOnly
+ public static void setTimePartitionInterval(long timePartitionInterval) {
+ StorageEngineV2.timePartitionInterval = timePartitionInterval;
+ }
+
+ public static long getTimePartition(long time) {
+ return enablePartition ? time / timePartitionInterval : 0;
+ }
+
+ public static boolean isEnablePartition() {
+ return enablePartition;
+ }
+
+ @TestOnly
+ public static void setEnablePartition(boolean enablePartition) {
+ StorageEngineV2.enablePartition = enablePartition;
+ }
+
+ /** block insertion if the insertion is rejected by memory control */
+ public static void blockInsertionIfReject(TsFileProcessor tsFileProcessor)
+ throws WriteProcessRejectException {
+ long startTime = System.currentTimeMillis();
+ while (SystemInfo.getInstance().isRejected()) {
+ if (tsFileProcessor != null && tsFileProcessor.shouldFlush()) {
+ break;
+ }
+ try {
+ TimeUnit.MILLISECONDS.sleep(config.getCheckPeriodWhenInsertBlocked());
+ if (System.currentTimeMillis() - startTime >
config.getMaxWaitingTimeWhenInsertBlocked()) {
+ throw new WriteProcessRejectException(
+ "System rejected over " + (System.currentTimeMillis() -
startTime) + "ms");
+ }
+ } catch (InterruptedException e) {
+ Thread.currentThread().interrupt();
+ }
+ }
+ }
+
+ public boolean isAllSgReady() {
+ return isAllSgReady.get();
+ }
+
+ public void setAllSgReady(boolean allSgReady) {
+ isAllSgReady.set(allSgReady);
+ }
+
+ public void recover() {
+ setAllSgReady(false);
+ recoveryThreadPool =
+ IoTDBThreadPoolFactory.newFixedThreadPool(
+ Runtime.getRuntime().availableProcessors(),
"Recovery-Thread-Pool");
+ try {
+ getLocalDataRegion();
+ } catch (Exception e) {
+ throw new StorageEngineFailureException("StorageEngine failed to
recover.", e);
+ }
+ List<Future<Void>> futures = new LinkedList<>();
+ asyncRecover(recoveryThreadPool, futures);
+
+ // operations after all virtual storage groups are recovered
+ Thread recoverEndTrigger =
+ new Thread(
+ () -> {
+ for (Future<Void> future : futures) {
+ try {
+ future.get();
+ } catch (ExecutionException e) {
+ throw new StorageEngineFailureException("StorageEngine
failed to recover.", e);
+ } catch (InterruptedException e) {
+ Thread.currentThread().interrupt();
+ throw new StorageEngineFailureException("StorageEngine
failed to recover.", e);
+ }
+ }
+ recoveryThreadPool.shutdown();
+ setAllSgReady(true);
+ });
+ recoverEndTrigger.start();
+ }
+
+ private void getLocalDataRegion() throws MetadataException,
StorageGroupProcessorException {
+ File system = SystemFileFactory.INSTANCE.getFile(systemDir);
+ File[] sgDirs = system.listFiles();
+ for (File sgDir : sgDirs) {
+ if (!sgDir.isDirectory()) {
+ continue;
+ }
+ String sg = sgDir.getName();
+ // TODO: need to get TTL Info from config node
+ long ttl = Integer.MAX_VALUE;
+ for (File dataRegionDir : sgDir.listFiles()) {
+ if (!dataRegionDir.isDirectory()) {
+ continue;
+ }
+ ConsensusGroupId dataRegionId = new
DataRegionId(Integer.parseInt(dataRegionDir.getName()));
+ VirtualStorageGroupProcessor dataRegion =
+ buildNewStorageGroupProcessor(sg, dataRegionDir.getName(), ttl);
+ dataRegionMap.putIfAbsent(dataRegionId, dataRegion);
+ }
+ }
+ }
+
+ private void asyncRecover(ExecutorService pool, List<Future<Void>> futures) {
+ for (VirtualStorageGroupProcessor processor : dataRegionMap.values()) {
+ Callable<Void> recoverVsgTask =
+ () -> {
+ processor.setReady(true);
+ return null;
+ };
+ futures.add(pool.submit(recoverVsgTask));
+ }
+ }
+
+ @Override
+ public void start() {
+ // build time Interval to divide time partition
+ if (!enablePartition) {
+ timePartitionInterval = Long.MAX_VALUE;
+ } else {
+ initTimePartition();
+ }
+
+ // create systemDir
+ try {
+ FileUtils.forceMkdir(SystemFileFactory.INSTANCE.getFile(systemDir));
+ } catch (IOException e) {
+ throw new StorageEngineFailureException(e);
+ }
+
+ // recover upgrade process
+ UpgradeUtils.recoverUpgrade();
+
+ recover();
+
+ ttlCheckThread =
IoTDBThreadPoolFactory.newSingleThreadScheduledExecutor("TTL-Check");
+ ttlCheckThread.scheduleAtFixedRate(
+ this::checkTTL, TTL_CHECK_INTERVAL, TTL_CHECK_INTERVAL,
TimeUnit.MILLISECONDS);
+ logger.info("start ttl check thread successfully.");
+
+ startTimedService();
+ }
+
+ private void checkTTL() {
+ try {
+ for (VirtualStorageGroupProcessor dataRegion : dataRegionMap.values()) {
+ if (dataRegion != null) {
+ dataRegion.checkFilesTTL();
+ }
+ }
+ } catch (ConcurrentModificationException e) {
+ // ignore
+ } catch (Exception e) {
+ logger.error("An error occurred when checking TTL", e);
+ }
+ }
+
+ private void startTimedService() {
+ // timed flush sequence memtable
+ if (config.isEnableTimedFlushSeqMemtable()) {
+ seqMemtableTimedFlushCheckThread =
+ IoTDBThreadPoolFactory.newSingleThreadScheduledExecutor(
+ ThreadName.TIMED_FlUSH_SEQ_MEMTABLE.getName());
+ seqMemtableTimedFlushCheckThread.scheduleAtFixedRate(
+ this::timedFlushSeqMemTable,
+ config.getSeqMemtableFlushCheckInterval(),
+ config.getSeqMemtableFlushCheckInterval(),
+ TimeUnit.MILLISECONDS);
+ logger.info("start sequence memtable timed flush check thread
successfully.");
+ }
+ // timed flush unsequence memtable
+ if (config.isEnableTimedFlushUnseqMemtable()) {
+ unseqMemtableTimedFlushCheckThread =
+ IoTDBThreadPoolFactory.newSingleThreadScheduledExecutor(
+ ThreadName.TIMED_FlUSH_UNSEQ_MEMTABLE.getName());
+ unseqMemtableTimedFlushCheckThread.scheduleAtFixedRate(
+ this::timedFlushUnseqMemTable,
+ config.getUnseqMemtableFlushCheckInterval(),
+ config.getUnseqMemtableFlushCheckInterval(),
+ TimeUnit.MILLISECONDS);
+ logger.info("start unsequence memtable timed flush check thread
successfully.");
+ }
+ // timed close tsfile
+ if (config.isEnableTimedCloseTsFile()) {
+ tsFileTimedCloseCheckThread =
+ IoTDBThreadPoolFactory.newSingleThreadScheduledExecutor(
+ ThreadName.TIMED_CLOSE_TSFILE.getName());
+ tsFileTimedCloseCheckThread.scheduleAtFixedRate(
+ this::timedCloseTsFileProcessor,
+ config.getCloseTsFileCheckInterval(),
+ config.getCloseTsFileCheckInterval(),
+ TimeUnit.MILLISECONDS);
+ logger.info("start tsfile timed close check thread successfully.");
+ }
+ }
+
+ private void timedFlushSeqMemTable() {
+ try {
+ for (VirtualStorageGroupProcessor dataRegion : dataRegionMap.values()) {
+ if (dataRegion != null) {
+ dataRegion.timedFlushSeqMemTable();
+ }
+ }
+ } catch (Exception e) {
+ logger.error("An error occurred when timed flushing sequence memtables",
e);
+ }
+ }
+
+ private void timedFlushUnseqMemTable() {
+ try {
+ for (VirtualStorageGroupProcessor dataRegion : dataRegionMap.values()) {
+ if (dataRegion != null) {
+ dataRegion.timedFlushUnseqMemTable();
+ }
+ }
+ } catch (Exception e) {
+ logger.error("An error occurred when timed flushing unsequence
memtables", e);
+ }
+ }
+
+ private void timedCloseTsFileProcessor() {
+ try {
+ for (VirtualStorageGroupProcessor dataRegion : dataRegionMap.values()) {
+ if (dataRegion != null) {
+ dataRegion.timedCloseTsFileProcessor();
+ }
+ }
+ } catch (Exception e) {
+ logger.error("An error occurred when timed closing tsfiles interval", e);
+ }
+ }
+
+ @Override
+ public void stop() {
+ for (VirtualStorageGroupProcessor vsg : dataRegionMap.values()) {
+ if (vsg != null) {
+ ThreadUtils.stopThreadPool(
+ vsg.getTimedCompactionScheduleTask(),
ThreadName.COMPACTION_SCHEDULE);
+ ThreadUtils.stopThreadPool(vsg.getWALTrimScheduleTask(),
ThreadName.WAL_TRIM);
+ }
+ }
+ syncCloseAllProcessor();
+ ThreadUtils.stopThreadPool(ttlCheckThread, ThreadName.TTL_CHECK_SERVICE);
+ ThreadUtils.stopThreadPool(
+ seqMemtableTimedFlushCheckThread, ThreadName.TIMED_FlUSH_SEQ_MEMTABLE);
+ ThreadUtils.stopThreadPool(
+ unseqMemtableTimedFlushCheckThread,
ThreadName.TIMED_FlUSH_UNSEQ_MEMTABLE);
+ ThreadUtils.stopThreadPool(tsFileTimedCloseCheckThread,
ThreadName.TIMED_CLOSE_TSFILE);
+ recoveryThreadPool.shutdownNow();
+ // TODO(Removed from new wal)
+ // for (PartialPath storageGroup :
IoTDB.schemaEngine.getAllStorageGroupPaths()) {
+ // this.releaseWalDirectByteBufferPoolInOneStorageGroup(storageGroup);
+ // }
+ dataRegionMap.clear();
+ }
+
+ @Override
+ public void shutdown(long milliseconds) throws ShutdownException {
+ try {
+ for (VirtualStorageGroupProcessor virtualStorageGroupProcessor :
dataRegionMap.values()) {
+ ThreadUtils.stopThreadPool(
+ virtualStorageGroupProcessor.getTimedCompactionScheduleTask(),
+ ThreadName.COMPACTION_SCHEDULE);
+ ThreadUtils.stopThreadPool(
+ virtualStorageGroupProcessor.getWALTrimScheduleTask(),
ThreadName.WAL_TRIM);
+ }
+ forceCloseAllProcessor();
+ } catch (TsFileProcessorException e) {
+ throw new ShutdownException(e);
+ }
+ shutdownTimedService(ttlCheckThread, "TTlCheckThread");
+ shutdownTimedService(seqMemtableTimedFlushCheckThread,
"SeqMemtableTimedFlushCheckThread");
+ shutdownTimedService(unseqMemtableTimedFlushCheckThread,
"UnseqMemtableTimedFlushCheckThread");
+ shutdownTimedService(tsFileTimedCloseCheckThread,
"TsFileTimedCloseCheckThread");
+ recoveryThreadPool.shutdownNow();
+ dataRegionMap.clear();
+ }
+
+ private void shutdownTimedService(ScheduledExecutorService pool, String
poolName) {
+ if (pool != null) {
+ pool.shutdownNow();
+ try {
+ pool.awaitTermination(30, TimeUnit.SECONDS);
+ } catch (InterruptedException e) {
+ logger.warn("{} still doesn't exit after 30s", poolName);
+ Thread.currentThread().interrupt();
+ }
+ }
+ }
+
+ private void stopTimedServiceAndThrow(ScheduledExecutorService pool, String
poolName)
+ throws ShutdownException {
+ if (pool != null) {
+ pool.shutdownNow();
+ try {
+ pool.awaitTermination(30, TimeUnit.SECONDS);
+ } catch (InterruptedException e) {
+ logger.warn("{} still doesn't exit after 30s", poolName);
+ throw new ShutdownException(e);
+ }
+ }
+ }
+
+ @Override
+ public ServiceType getID() {
+ return ServiceType.STORAGE_ENGINE_SERVICE;
+ }
+
+ /**
+ * build a new storage group processor
+ *
+ * @param virtualStorageGroupId virtual storage group id e.g. 1
+ * @param logicalStorageGroupName logical storage group name e.g. root.sg1
+ */
+ public VirtualStorageGroupProcessor buildNewStorageGroupProcessor(
+ String logicalStorageGroupName, String virtualStorageGroupId, long ttl)
+ throws StorageGroupProcessorException {
+ VirtualStorageGroupProcessor processor;
+ logger.info(
+ "construct a processor instance, the storage group is {}, Thread is
{}",
+ logicalStorageGroupName,
+ Thread.currentThread().getId());
+ processor =
+ new VirtualStorageGroupProcessor(
+ systemDir + File.separator + logicalStorageGroupName,
+ virtualStorageGroupId,
+ fileFlushPolicy,
+ logicalStorageGroupName);
+ processor.setDataTTL(ttl);
+ processor.setCustomFlushListeners(customFlushListeners);
+ processor.setCustomCloseFileListeners(customCloseFileListeners);
+ return processor;
+ }
+
+ /** This function is just for unit test. */
+ @TestOnly
+ public synchronized void reset() {
+ dataRegionMap.clear();
+ }
+
+ /**
+ * insert an InsertRowNode to a storage group.
+ *
+ * @param insertRowNode
+ */
+ // TODO:(New insert)
+ public void insert(ConsensusGroupId dataRegionId, InsertRowNode
insertRowNode)
+ throws StorageEngineException, MetadataException {
+ if (enableMemControl) {
+ try {
+ blockInsertionIfReject(null);
+ } catch (WriteProcessException e) {
+ throw new StorageEngineException(e);
+ }
+ }
+
+ VirtualStorageGroupProcessor dataRegion = dataRegionMap.get(dataRegionId);
+
+ try {
+ dataRegion.insert(insertRowNode);
+ } catch (WriteProcessException e) {
+ throw new StorageEngineException(e);
+ }
+ }
+
+ /** insert an InsertTabletNode to a storage group */
+ // TODO:(New insert)
+ public void insertTablet(ConsensusGroupId dataRegionId, InsertTabletNode
insertTabletNode)
+ throws StorageEngineException, BatchProcessException {
+ if (enableMemControl) {
+ try {
+ blockInsertionIfReject(null);
+ } catch (WriteProcessRejectException e) {
+ TSStatus[] results = new TSStatus[insertTabletNode.getRowCount()];
+ Arrays.fill(results,
RpcUtils.getStatus(TSStatusCode.WRITE_PROCESS_REJECT));
+ throw new BatchProcessException(results);
+ }
+ }
+ VirtualStorageGroupProcessor dataRegion = dataRegionMap.get(dataRegionId);
+ dataRegion.insertTablet(insertTabletNode);
+ }
+
+ /** flush command Sync asyncCloseOneProcessor all file node processors. */
+ public void syncCloseAllProcessor() {
+ logger.info("Start closing all storage group processor");
+ for (VirtualStorageGroupProcessor virtualStorageGroupProcessor :
dataRegionMap.values()) {
+ if (virtualStorageGroupProcessor != null) {
+ virtualStorageGroupProcessor.syncCloseAllWorkingTsFileProcessors();
+ }
+ }
+ }
+
+ public void forceCloseAllProcessor() throws TsFileProcessorException {
+ logger.info("Start force closing all storage group processor");
+ for (VirtualStorageGroupProcessor virtualStorageGroupProcessor :
dataRegionMap.values()) {
+ if (virtualStorageGroupProcessor != null) {
+ virtualStorageGroupProcessor.forceCloseAllWorkingTsFileProcessors();
+ }
+ }
+ }
+
+ public void setTTL(List<ConsensusGroupId> dataRegionIdList, long dataTTL) {
+ for (ConsensusGroupId dataRegionId : dataRegionIdList) {
+ VirtualStorageGroupProcessor dataRegion =
dataRegionMap.get(dataRegionId);
+ if (dataRegion != null) {
+ dataRegion.setDataTTL(dataTTL);
+ }
+ }
+ }
+
+ public void setFileFlushPolicy(TsFileFlushPolicy fileFlushPolicy) {
+ this.fileFlushPolicy = fileFlushPolicy;
+ }
+
+ /**
+ * Get a map indicating which storage groups have working TsFileProcessors
and its associated
+ * partitionId and whether it is sequence or not.
+ *
+ * @return storage group -> a list of partitionId-isSequence pairs
+ */
+ public Map<String, List<Pair<Long, Boolean>>>
getWorkingStorageGroupPartitions() {
+ Map<String, List<Pair<Long, Boolean>>> res = new ConcurrentHashMap<>();
+ for (Entry<ConsensusGroupId, VirtualStorageGroupProcessor> entry :
dataRegionMap.entrySet()) {
+ VirtualStorageGroupProcessor virtualStorageGroupProcessor =
entry.getValue();
+ if (virtualStorageGroupProcessor != null) {
+ List<Pair<Long, Boolean>> partitionIdList = new ArrayList<>();
+ for (TsFileProcessor tsFileProcessor :
+ virtualStorageGroupProcessor.getWorkSequenceTsFileProcessors()) {
+ Pair<Long, Boolean> tmpPair = new
Pair<>(tsFileProcessor.getTimeRangeId(), true);
+ partitionIdList.add(tmpPair);
+ }
+
+ for (TsFileProcessor tsFileProcessor :
+ virtualStorageGroupProcessor.getWorkUnsequenceTsFileProcessors()) {
+ Pair<Long, Boolean> tmpPair = new
Pair<>(tsFileProcessor.getTimeRangeId(), false);
+ partitionIdList.add(tmpPair);
+ }
+
+ res.put(virtualStorageGroupProcessor.getStorageGroupPath(),
partitionIdList);
+ }
+ }
+
+ return res;
+ }
+
+ /**
+ * Add a listener to listen flush start/end events. Notice that this
addition only applies to
+ * TsFileProcessors created afterwards.
+ *
+ * @param listener
+ */
+ public void registerFlushListener(FlushListener listener) {
+ customFlushListeners.add(listener);
+ }
+
+ /**
+ * Add a listener to listen file close events. Notice that this addition
only applies to
+ * TsFileProcessors created afterwards.
+ *
+ * @param listener
+ */
+ public void registerCloseFileListener(CloseFileListener listener) {
+ customCloseFileListeners.add(listener);
+ }
+
+ // When registering a new region, the coordinator needs to register the
corresponding region with
+ // the local engine before adding the corresponding consensusGroup to the
consensus layer
+ public VirtualStorageGroupProcessor createDataRegion(DataRegionId regionId,
String sg, long ttl)
+ throws StorageEngineException {
+ try {
+ VirtualStorageGroupProcessor dataRegion =
+ buildNewStorageGroupProcessor(sg, regionId.toString(), ttl);
+ dataRegionMap.put(regionId, dataRegion);
+ } catch (StorageGroupProcessorException e) {
+ throw new StorageEngineException(e);
+ }
+ return null;
+ }
+
+ public VirtualStorageGroupProcessor getDataRegion(DataRegionId regionId) {
+ return dataRegionMap.get(regionId);
+ }
+
+ static class InstanceHolder {
+
+ private static final StorageEngineV2 INSTANCE = new StorageEngineV2();
+
+ private InstanceHolder() {
+ // forbidding instantiation
+ }
+ }
+}
diff --git
a/server/src/main/java/org/apache/iotdb/db/engine/memtable/AbstractMemTable.java
b/server/src/main/java/org/apache/iotdb/db/engine/memtable/AbstractMemTable.java
index 06706b1361..aa5c5cc09b 100644
---
a/server/src/main/java/org/apache/iotdb/db/engine/memtable/AbstractMemTable.java
+++
b/server/src/main/java/org/apache/iotdb/db/engine/memtable/AbstractMemTable.java
@@ -26,6 +26,8 @@ import
org.apache.iotdb.db.exception.query.QueryProcessException;
import org.apache.iotdb.db.metadata.idtable.entry.DeviceIDFactory;
import org.apache.iotdb.db.metadata.idtable.entry.IDeviceID;
import org.apache.iotdb.db.metadata.path.PartialPath;
+import org.apache.iotdb.db.mpp.sql.planner.plan.node.write.InsertRowNode;
+import org.apache.iotdb.db.mpp.sql.planner.plan.node.write.InsertTabletNode;
import org.apache.iotdb.db.qp.physical.crud.InsertRowPlan;
import org.apache.iotdb.db.qp.physical.crud.InsertTabletPlan;
import org.apache.iotdb.db.service.metrics.Metric;
@@ -180,6 +182,49 @@ public abstract class AbstractMemTable implements
IMemTable {
}
}
+ @Override
+ public void insert(InsertRowNode insertRowNode) {
+ // if this insert plan isn't from storage engine (mainly from test), we
should set a temp device
+ // id for it
+ if (insertRowNode.getDeviceID() == null) {
+ insertRowNode.setDeviceID(
+
DeviceIDFactory.getInstance().getDeviceID(insertRowNode.getDevicePath()));
+ }
+
+ // updatePlanIndexes(insertRowNode.getIndex());
+ updatePlanIndexes(0);
+ String[] measurements = insertRowNode.getMeasurements();
+ Object[] values = insertRowNode.getValues();
+
+ List<IMeasurementSchema> schemaList = new ArrayList<>();
+ List<TSDataType> dataTypes = new ArrayList<>();
+ for (int i = 0; i < insertRowNode.getMeasurements().length; i++) {
+ if (measurements[i] == null) {
+ continue;
+ }
+ IMeasurementSchema schema = insertRowNode.getMeasurementSchemas()[i];
+ schemaList.add(schema);
+ dataTypes.add(schema.getType());
+ }
+ memSize += MemUtils.getRecordsSize(dataTypes, values, disableMemControl);
+ write(insertRowNode.getDeviceID(), schemaList, insertRowNode.getTime(),
values);
+
+ int pointsInserted = insertRowNode.getMeasurements().length;
+
+ totalPointsNum += pointsInserted;
+
+ if
(MetricConfigDescriptor.getInstance().getMetricConfig().getEnableMetric()) {
+ MetricsService.getInstance()
+ .getMetricManager()
+ .count(
+ pointsInserted,
+ Metric.QUANTITY.toString(),
+ MetricLevel.IMPORTANT,
+ Tag.NAME.toString(),
+ METRIC_POINT_IN);
+ }
+ }
+
@Override
public void insertAlignedRow(InsertRowPlan insertRowPlan) {
// if this insert plan isn't from storage engine, we should set a temp
device id for it
@@ -226,6 +271,52 @@ public abstract class AbstractMemTable implements
IMemTable {
}
}
+ @Override
+ public void insertAlignedRow(InsertRowNode insertRowNode) {
+ // if this insert node isn't from storage engine, we should set a temp
device id for it
+ if (insertRowNode.getDeviceID() == null) {
+ insertRowNode.setDeviceID(
+
DeviceIDFactory.getInstance().getDeviceID(insertRowNode.getDevicePath()));
+ }
+
+ // updatePlanIndexes(insertRowNode.getIndex());
+ updatePlanIndexes(0);
+ String[] measurements = insertRowNode.getMeasurements();
+ List<IMeasurementSchema> schemaList = new ArrayList<>();
+ List<TSDataType> dataTypes = new ArrayList<>();
+ for (int i = 0; i < insertRowNode.getMeasurements().length; i++) {
+ if (measurements[i] == null) {
+ continue;
+ }
+ IMeasurementSchema schema = insertRowNode.getMeasurementSchemas()[i];
+ schemaList.add(schema);
+ dataTypes.add(schema.getType());
+ }
+ if (schemaList.isEmpty()) {
+ return;
+ }
+ memSize +=
+ MemUtils.getAlignedRecordsSize(dataTypes, insertRowNode.getValues(),
disableMemControl);
+ writeAlignedRow(
+ insertRowNode.getDeviceID(),
+ schemaList,
+ insertRowNode.getTime(),
+ insertRowNode.getValues());
+ int pointsInserted = insertRowNode.getMeasurements().length;
+ totalPointsNum += pointsInserted;
+
+ if
(MetricConfigDescriptor.getInstance().getMetricConfig().getEnableMetric()) {
+ MetricsService.getInstance()
+ .getMetricManager()
+ .count(
+ pointsInserted,
+ Metric.QUANTITY.toString(),
+ MetricLevel.IMPORTANT,
+ Tag.NAME.toString(),
+ METRIC_POINT_IN);
+ }
+ }
+
@Override
public void insertTablet(InsertTabletPlan insertTabletPlan, int start, int
end)
throws WriteProcessException {
@@ -278,6 +369,58 @@ public abstract class AbstractMemTable implements
IMemTable {
}
}
+ @Override
+ public void insertTablet(InsertTabletNode insertTabletNode, int start, int
end)
+ throws WriteProcessException {
+ // TODO: PlanIndex
+ // updatePlanIndexes(insertTabletPlan.getIndex());
+ updatePlanIndexes(0);
+ try {
+ write(insertTabletNode, start, end);
+ memSize += MemUtils.getTabletSize(insertTabletNode, start, end,
disableMemControl);
+ int pointsInserted = insertTabletNode.getDataTypes().length * (end -
start);
+ totalPointsNum += pointsInserted;
+ if
(MetricConfigDescriptor.getInstance().getMetricConfig().getEnableMetric()) {
+ MetricsService.getInstance()
+ .getMetricManager()
+ .count(
+ pointsInserted,
+ Metric.QUANTITY.toString(),
+ MetricLevel.IMPORTANT,
+ Tag.NAME.toString(),
+ METRIC_POINT_IN);
+ }
+ } catch (RuntimeException e) {
+ throw new WriteProcessException(e);
+ }
+ }
+
+ @Override
+ public void insertAlignedTablet(InsertTabletNode insertTabletNode, int
start, int end)
+ throws WriteProcessException {
+ // TODO: PlanIndex
+ // updatePlanIndexes(insertTabletPlan.getIndex());
+ updatePlanIndexes(0);
+ try {
+ writeAlignedTablet(insertTabletNode, start, end);
+ memSize += MemUtils.getAlignedTabletSize(insertTabletNode, start, end,
disableMemControl);
+ int pointsInserted = insertTabletNode.getDataTypes().length * (end -
start);
+ totalPointsNum += pointsInserted;
+ if
(MetricConfigDescriptor.getInstance().getMetricConfig().getEnableMetric()) {
+ MetricsService.getInstance()
+ .getMetricManager()
+ .count(
+ pointsInserted,
+ Metric.QUANTITY.toString(),
+ MetricLevel.IMPORTANT,
+ Tag.NAME.toString(),
+ METRIC_POINT_IN);
+ }
+ } catch (RuntimeException e) {
+ throw new WriteProcessException(e);
+ }
+ }
+
@Override
public void write(
IDeviceID deviceId,
@@ -328,6 +471,32 @@ public abstract class AbstractMemTable implements
IMemTable {
end);
}
+ public void write(InsertTabletNode insertTabletNode, int start, int end) {
+ // if this insert plan isn't from storage engine, we should set a temp
device id for it
+ if (insertTabletNode.getDeviceID() == null) {
+ insertTabletNode.setDeviceID(
+
DeviceIDFactory.getInstance().getDeviceID(insertTabletNode.getDevicePath()));
+ }
+
+ List<IMeasurementSchema> schemaList = new ArrayList<>();
+ for (int i = 0; i < insertTabletNode.getMeasurementSchemas().length; i++) {
+ if (insertTabletNode.getColumns()[i] == null) {
+ continue;
+ }
+ IMeasurementSchema schema = insertTabletNode.getMeasurementSchemas()[i];
+ schemaList.add(schema);
+ }
+ IWritableMemChunkGroup memChunkGroup =
+ createMemChunkGroupIfNotExistAndGet(insertTabletNode.getDeviceID(),
schemaList);
+ memChunkGroup.writeValues(
+ insertTabletNode.getTimes(),
+ insertTabletNode.getColumns(),
+ insertTabletNode.getBitMaps(),
+ schemaList,
+ start,
+ end);
+ }
+
@Override
public void writeAlignedTablet(InsertTabletPlan insertTabletPlan, int start,
int end) {
// if this insert plan isn't from storage engine, we should set a temp
device id for it
@@ -358,6 +527,35 @@ public abstract class AbstractMemTable implements
IMemTable {
end);
}
+ public void writeAlignedTablet(InsertTabletNode insertTabletNode, int start,
int end) {
+ // if this insert plan isn't from storage engine, we should set a temp
device id for it
+ if (insertTabletNode.getDeviceID() == null) {
+ insertTabletNode.setDeviceID(
+
DeviceIDFactory.getInstance().getDeviceID(insertTabletNode.getDevicePath()));
+ }
+
+ List<IMeasurementSchema> schemaList = new ArrayList<>();
+ for (int i = 0; i < insertTabletNode.getMeasurementSchemas().length; i++) {
+ if (insertTabletNode.getColumns()[i] == null) {
+ continue;
+ }
+ IMeasurementSchema schema = insertTabletNode.getMeasurementSchemas()[i];
+ schemaList.add(schema);
+ }
+ if (schemaList.isEmpty()) {
+ return;
+ }
+ IWritableMemChunkGroup memChunkGroup =
+
createAlignedMemChunkGroupIfNotExistAndGet(insertTabletNode.getDeviceID(),
schemaList);
+ memChunkGroup.writeValues(
+ insertTabletNode.getTimes(),
+ insertTabletNode.getColumns(),
+ insertTabletNode.getBitMaps(),
+ schemaList,
+ start,
+ end);
+ }
+
@Override
public boolean checkIfChunkDoesNotExist(IDeviceID deviceId, String
measurement) {
IWritableMemChunkGroup memChunkGroup = memTableMap.get(deviceId);
diff --git
a/server/src/main/java/org/apache/iotdb/db/engine/memtable/IMemTable.java
b/server/src/main/java/org/apache/iotdb/db/engine/memtable/IMemTable.java
index 69aeb47ffe..9b5a2b3580 100644
--- a/server/src/main/java/org/apache/iotdb/db/engine/memtable/IMemTable.java
+++ b/server/src/main/java/org/apache/iotdb/db/engine/memtable/IMemTable.java
@@ -25,6 +25,8 @@ import
org.apache.iotdb.db.exception.metadata.MetadataException;
import org.apache.iotdb.db.exception.query.QueryProcessException;
import org.apache.iotdb.db.metadata.idtable.entry.IDeviceID;
import org.apache.iotdb.db.metadata.path.PartialPath;
+import org.apache.iotdb.db.mpp.sql.planner.plan.node.write.InsertRowNode;
+import org.apache.iotdb.db.mpp.sql.planner.plan.node.write.InsertTabletNode;
import org.apache.iotdb.db.qp.physical.crud.InsertRowPlan;
import org.apache.iotdb.db.qp.physical.crud.InsertTabletPlan;
import org.apache.iotdb.tsfile.utils.Pair;
@@ -100,6 +102,10 @@ public interface IMemTable {
void insertAlignedRow(InsertRowPlan insertRowPlan);
+ void insert(InsertRowNode insertRowNode);
+
+ void insertAlignedRow(InsertRowNode insertRowNode);
+
/**
* insert tablet into this memtable. The rows to be inserted are in the
range [start, end). Null
* value in each column values will be replaced by the subsequent non-null
value, e.g., {1, null,
@@ -115,6 +121,12 @@ public interface IMemTable {
void insertAlignedTablet(InsertTabletPlan insertTabletPlan, int start, int
end)
throws WriteProcessException;
+ void insertTablet(InsertTabletNode insertTabletNode, int start, int end)
+ throws WriteProcessException;
+
+ void insertAlignedTablet(InsertTabletNode insertTabletNode, int start, int
end)
+ throws WriteProcessException;
+
ReadOnlyMemChunk query(
PartialPath fullPath, long ttlLowerBound, List<Pair<Modification,
IMemTable>> modsToMemtable)
throws IOException, QueryProcessException, MetadataException;
diff --git
a/server/src/main/java/org/apache/iotdb/db/engine/storagegroup/TsFileProcessor.java
b/server/src/main/java/org/apache/iotdb/db/engine/storagegroup/TsFileProcessor.java
index df6e20bb88..5fd5aad7f5 100644
---
a/server/src/main/java/org/apache/iotdb/db/engine/storagegroup/TsFileProcessor.java
+++
b/server/src/main/java/org/apache/iotdb/db/engine/storagegroup/TsFileProcessor.java
@@ -46,6 +46,8 @@ import
org.apache.iotdb.db.metadata.idtable.entry.DeviceIDFactory;
import org.apache.iotdb.db.metadata.idtable.entry.IDeviceID;
import org.apache.iotdb.db.metadata.path.AlignedPath;
import org.apache.iotdb.db.metadata.path.PartialPath;
+import org.apache.iotdb.db.mpp.sql.planner.plan.node.write.InsertRowNode;
+import org.apache.iotdb.db.mpp.sql.planner.plan.node.write.InsertTabletNode;
import org.apache.iotdb.db.qp.physical.crud.InsertRowPlan;
import org.apache.iotdb.db.qp.physical.crud.InsertTabletPlan;
import org.apache.iotdb.db.query.context.QueryContext;
@@ -257,6 +259,64 @@ public class TsFileProcessor {
tsFileResource.updatePlanIndexes(insertRowPlan.getIndex());
}
+ /**
+ * insert data in an InsertRowNode into the workingMemtable.
+ *
+ * @param insertRowNode physical plan of insertion
+ */
+ public void insert(InsertRowNode insertRowNode) throws WriteProcessException
{
+
+ if (workMemTable == null) {
+ if (enableMemControl) {
+ workMemTable = new PrimitiveMemTable(enableMemControl);
+ MemTableManager.getInstance().addMemtableNumber();
+ } else {
+ workMemTable =
MemTableManager.getInstance().getAvailableMemTable(storageGroupName);
+ }
+ }
+
+ long[] memIncrements = null;
+ if (enableMemControl) {
+ if (insertRowNode.isAligned()) {
+ // memIncrements = checkAlignedMemCostAndAddToTspInfo(insertRowNode);
+ } else {
+ // memIncrements = checkMemCostAndAddToTspInfo(insertRowNode);
+ }
+ }
+
+ if (IoTDBDescriptor.getInstance().getConfig().isEnableWal()) {
+ try {
+ getLogNode().write(insertRowNode);
+ } catch (Exception e) {
+ if (enableMemControl && memIncrements != null) {
+ rollbackMemoryInfo(memIncrements);
+ }
+ throw new WriteProcessException(
+ String.format(
+ "%s: %s write WAL failed",
+ storageGroupName,
tsFileResource.getTsFile().getAbsolutePath()),
+ e);
+ }
+ }
+
+ if (insertRowNode.isAligned()) {
+ workMemTable.insertAlignedRow(insertRowNode);
+ } else {
+ workMemTable.insert(insertRowNode);
+ }
+
+ // update start time of this memtable
+ tsFileResource.updateStartTime(
+ insertRowNode.getDeviceID().toStringID(), insertRowNode.getTime());
+ // for sequence tsfile, we update the endTime only when the file is
prepared to be closed.
+ // for unsequence tsfile, we have to update the endTime for each insertion.
+ if (!sequence) {
+ tsFileResource.updateEndTime(
+ insertRowNode.getDeviceID().toStringID(), insertRowNode.getTime());
+ }
+ // tsFileResource.updatePlanIndexes(insertRowNode.getIndex());
+ }
+
/**
* insert batch data of insertTabletPlan into the workingMemtable. The rows
to be inserted are in
* the range [start, end). Null value in each column values will be replaced
by the subsequent
@@ -340,6 +400,93 @@ public class TsFileProcessor {
tsFileResource.updatePlanIndexes(insertTabletPlan.getIndex());
}
+ /**
+ * insert batch data of insertTabletPlan into the workingMemtable. The rows
to be inserted are in
+ * the range [start, end). Null value in each column values will be replaced
by the subsequent
+ * non-null value, e.g., {1, null, 3, null, 5} will be {1, 3, 5, null, 5}
+ *
+ * @param insertTabletNode insert a tablet of a device
+ * @param start start index of rows to be inserted in insertTabletPlan
+ * @param end end index of rows to be inserted in insertTabletPlan
+ * @param results result array
+ */
+ public void insertTablet(
+ InsertTabletNode insertTabletNode, int start, int end, TSStatus[]
results)
+ throws WriteProcessException {
+
+ if (workMemTable == null) {
+ if (enableMemControl) {
+ workMemTable = new PrimitiveMemTable(enableMemControl);
+ MemTableManager.getInstance().addMemtableNumber();
+ } else {
+ workMemTable =
MemTableManager.getInstance().getAvailableMemTable(storageGroupName);
+ }
+ }
+
+ long[] memIncrements = null;
+ try {
+ if (enableMemControl) {
+ if (insertTabletNode.isAligned()) {
+ memIncrements = checkAlignedMemCostAndAddToTsp(insertTabletNode,
start, end);
+ } else {
+ memIncrements = checkMemCostAndAddToTspInfo(insertTabletNode, start,
end);
+ }
+ }
+ } catch (WriteProcessException e) {
+ for (int i = start; i < end; i++) {
+ results[i] = RpcUtils.getStatus(TSStatusCode.WRITE_PROCESS_REJECT,
e.getMessage());
+ }
+ throw new WriteProcessException(e);
+ }
+
+ try {
+ if (IoTDBDescriptor.getInstance().getConfig().isEnableWal()) {
+ // TODO(WAL)
+ // Start and end should be removed from new WAL
+ // insertTabletNode.setStart(start);
+ // insertTabletNode.setEnd(end);
+ getLogNode().write(insertTabletNode);
+ }
+ } catch (Exception e) {
+ for (int i = start; i < end; i++) {
+ results[i] = RpcUtils.getStatus(TSStatusCode.INTERNAL_SERVER_ERROR,
e.getMessage());
+ }
+ if (enableMemControl && memIncrements != null) {
+ rollbackMemoryInfo(memIncrements);
+ }
+ throw new WriteProcessException(e);
+ }
+
+ try {
+ if (insertTabletNode.isAligned()) {
+ workMemTable.insertAlignedTablet(insertTabletNode, start, end);
+ } else {
+ workMemTable.insertTablet(insertTabletNode, start, end);
+ }
+ } catch (WriteProcessException e) {
+ for (int i = start; i < end; i++) {
+ results[i] = RpcUtils.getStatus(TSStatusCode.INTERNAL_SERVER_ERROR,
e.getMessage());
+ }
+ throw new WriteProcessException(e);
+ }
+
+ for (int i = start; i < end; i++) {
+ results[i] = RpcUtils.SUCCESS_STATUS;
+ }
+ tsFileResource.updateStartTime(
+ insertTabletNode.getDeviceID().toStringID(),
insertTabletNode.getTimes()[start]);
+
+ // for sequence tsfile, we update the endTime only when the file is
prepared to be closed.
+ // for unsequence tsfile, we have to update the endTime for each insertion.
+ if (!sequence) {
+ tsFileResource.updateEndTime(
+ insertTabletNode.getDeviceID().toStringID(),
insertTabletNode.getTimes()[end - 1]);
+ }
+ // TODO: PlanIndex
+ tsFileResource.updatePlanIndexes(0);
+ // tsFileResource.updatePlanIndexes(insertTabletPlan.getIndex());
+ }
+
@SuppressWarnings("squid:S3776") // high Cognitive Complexity
private long[] checkMemCostAndAddToTspInfo(InsertRowPlan insertRowPlan)
throws WriteProcessException {
@@ -471,6 +618,38 @@ public class TsFileProcessor {
return memIncrements;
}
+ private long[] checkMemCostAndAddToTspInfo(InsertTabletNode
insertTabletNode, int start, int end)
+ throws WriteProcessException {
+ if (start >= end) {
+ return new long[] {0, 0, 0};
+ }
+ long[] memIncrements = new long[3]; // memTable, text, chunk metadata
+
+ // get device id
+ IDeviceID deviceID = null;
+ try {
+ deviceID = getDeviceID(insertTabletNode.getDevicePath().getFullPath());
+ } catch (IllegalPathException e) {
+ throw new WriteProcessException(e);
+ }
+
+ for (int i = 0; i < insertTabletNode.getDataTypes().length; i++) {
+ // skip failed Measurements
+ TSDataType dataType = insertTabletNode.getDataTypes()[i];
+ String measurement =
insertTabletNode.getMeasurementSchemas()[i].getMeasurementId();
+ Object column = insertTabletNode.getColumns()[i];
+ if (dataType == null || column == null || measurement == null) {
+ continue;
+ }
+ updateMemCost(dataType, measurement, deviceID, start, end,
memIncrements, column);
+ }
+ long memTableIncrement = memIncrements[0];
+ long textDataIncrement = memIncrements[1];
+ long chunkMetadataIncrement = memIncrements[2];
+ updateMemoryInfo(memTableIncrement, chunkMetadataIncrement,
textDataIncrement);
+ return memIncrements;
+ }
+
private long[] checkAlignedMemCostAndAddToTsp(
InsertTabletPlan insertTabletPlan, int start, int end) throws
WriteProcessException {
if (start >= end) {
@@ -501,6 +680,36 @@ public class TsFileProcessor {
return memIncrements;
}
+ private long[] checkAlignedMemCostAndAddToTsp(
+ InsertTabletNode insertTabletNode, int start, int end) throws
WriteProcessException {
+ if (start >= end) {
+ return new long[] {0, 0, 0};
+ }
+ long[] memIncrements = new long[3]; // memTable, text, chunk metadata
+
+ // get device id
+ IDeviceID deviceID = null;
+ try {
+ deviceID = getDeviceID(insertTabletNode.getDevicePath().getFullPath());
+ } catch (IllegalPathException e) {
+ throw new WriteProcessException(e);
+ }
+
+ updateAlignedMemCost(
+ insertTabletNode.getDataTypes(),
+ deviceID,
+ insertTabletNode.getMeasurements(),
+ start,
+ end,
+ memIncrements,
+ insertTabletNode.getColumns());
+ long memTableIncrement = memIncrements[0];
+ long textDataIncrement = memIncrements[1];
+ long chunkMetadataIncrement = memIncrements[2];
+ updateMemoryInfo(memTableIncrement, chunkMetadataIncrement,
textDataIncrement);
+ return memIncrements;
+ }
+
private void updateMemCost(
TSDataType dataType,
String measurement,
diff --git
a/server/src/main/java/org/apache/iotdb/db/engine/storagegroup/VirtualStorageGroupProcessor.java
b/server/src/main/java/org/apache/iotdb/db/engine/storagegroup/VirtualStorageGroupProcessor.java
index 2f9de94ab4..3b95fad455 100755
---
a/server/src/main/java/org/apache/iotdb/db/engine/storagegroup/VirtualStorageGroupProcessor.java
+++
b/server/src/main/java/org/apache/iotdb/db/engine/storagegroup/VirtualStorageGroupProcessor.java
@@ -61,6 +61,8 @@ import org.apache.iotdb.db.metadata.idtable.IDTable;
import org.apache.iotdb.db.metadata.idtable.IDTableManager;
import org.apache.iotdb.db.metadata.mnode.IMeasurementMNode;
import org.apache.iotdb.db.metadata.path.PartialPath;
+import org.apache.iotdb.db.mpp.sql.planner.plan.node.write.InsertRowNode;
+import org.apache.iotdb.db.mpp.sql.planner.plan.node.write.InsertTabletNode;
import org.apache.iotdb.db.qp.physical.crud.DeletePlan;
import org.apache.iotdb.db.qp.physical.crud.InsertRowPlan;
import org.apache.iotdb.db.qp.physical.crud.InsertRowsOfOneDevicePlan;
@@ -116,6 +118,7 @@ import java.util.Set;
import java.util.TreeMap;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;
@@ -197,6 +200,8 @@ public class VirtualStorageGroupProcessor {
private AtomicInteger upgradeFileCount = new AtomicInteger();
+ private AtomicBoolean isSettling = new AtomicBoolean();
+
/** virtual storage group id */
private String virtualStorageGroupId;
/** logical storage group name */
@@ -452,6 +457,14 @@ public class VirtualStorageGroupProcessor {
return ret;
}
+ public AtomicBoolean getIsSettling() {
+ return isSettling;
+ }
+
+ public void setSettling(boolean isSettling) {
+ this.isSettling.set(isSettling);
+ }
+
/** this class is used to store recovering context */
private class RecoveryContext {
/** number of files to be recovered */
@@ -909,6 +922,44 @@ public class VirtualStorageGroupProcessor {
}
}
+ // TODO: (New Insert)
+ public void insert(InsertRowNode insertRowNode)
+ throws WriteProcessException, TriggerExecutionException {
+ // reject insertions that are out of ttl
+ if (!isAlive(insertRowNode.getTime())) {
+ throw new OutOfTTLException(insertRowNode.getTime(),
(System.currentTimeMillis() - dataTTL));
+ }
+ writeLock("InsertRow");
+ try {
+ // init map
+ long timePartitionId =
StorageEngine.getTimePartition(insertRowNode.getTime());
+
+ lastFlushTimeManager.ensureFlushedTimePartition(timePartitionId);
+
+ boolean isSequence =
+ insertRowNode.getTime()
+ > lastFlushTimeManager.getFlushedTime(
+ timePartitionId,
insertRowNode.getDevicePath().getFullPath());
+
+ // is unsequence and user set config to discard out of order data
+ if (!isSequence
+ &&
IoTDBDescriptor.getInstance().getConfig().isEnableDiscardOutOfOrderData()) {
+ return;
+ }
+
+ lastFlushTimeManager.ensureLastTimePartition(timePartitionId);
+
+ // fire trigger before insertion
+ // TriggerEngine.fire(TriggerEvent.BEFORE_INSERT, insertRowNode);
+ // insert to sequence or unSequence file
+ insertToTsFileProcessor(insertRowNode, isSequence, timePartitionId);
+ // fire trigger after insertion
+ // TriggerEngine.fire(TriggerEvent.AFTER_INSERT, insertRowNode);
+ } finally {
+ writeUnlock();
+ }
+ }
+
/**
* Insert a tablet (rows belonging to the same devices) into this storage
group.
*
@@ -1028,6 +1079,102 @@ public class VirtualStorageGroupProcessor {
}
}
+ /**
+ * Insert a tablet (rows belonging to the same devices) into this storage
group.
+ *
+ * @throws BatchProcessException if some of the rows failed to be inserted
+ */
+ @SuppressWarnings("squid:S3776") // Suppress high Cognitive Complexity
warning
+ public void insertTablet(InsertTabletNode insertTabletNode)
+ throws BatchProcessException, TriggerExecutionException {
+
+ writeLock("insertTablet");
+ try {
+ TSStatus[] results = new TSStatus[insertTabletNode.getRowCount()];
+ Arrays.fill(results, RpcUtils.SUCCESS_STATUS);
+ boolean noFailure = true;
+
+ /*
+ * assume that batch has been sorted by client
+ */
+ int loc = 0;
+ while (loc < insertTabletNode.getRowCount()) {
+ long currTime = insertTabletNode.getTimes()[loc];
+ // skip points that do not satisfy TTL
+ if (!isAlive(currTime)) {
+ results[loc] =
+ RpcUtils.getStatus(
+ TSStatusCode.OUT_OF_TTL_ERROR,
+ "time " + currTime + " in current line is out of TTL: " +
dataTTL);
+ loc++;
+ noFailure = false;
+ } else {
+ break;
+ }
+ }
+ // loc pointing at first legal position
+ if (loc == insertTabletNode.getRowCount()) {
+ throw new BatchProcessException(results);
+ }
+
+ // TODO(Trigger)// fire trigger before insertion
+ // final int firePosition = loc;
+ // TriggerEngine.fire(TriggerEvent.BEFORE_INSERT, insertTabletPlan,
firePosition);
+
+ // before is first start point
+ int before = loc;
+ // before time partition
+ long beforeTimePartition =
+ StorageEngine.getTimePartition(insertTabletNode.getTimes()[before]);
+ // init map
+ long lastFlushTime =
+ lastFlushTimeManager.ensureFlushedTimePartitionAndInit(
+ beforeTimePartition,
insertTabletNode.getDevicePath().getFullPath(), Long.MIN_VALUE);
+ // if is sequence
+ boolean isSequence = false;
+ while (loc < insertTabletNode.getRowCount()) {
+ long time = insertTabletNode.getTimes()[loc];
+ // always in some time partition
+ // judge if we should insert sequence
+ if (!isSequence && time > lastFlushTime) {
+ // insert into unsequence and then start sequence
+ if
(!IoTDBDescriptor.getInstance().getConfig().isEnableDiscardOutOfOrderData()) {
+ noFailure =
+ insertTabletToTsFileProcessor(
+ insertTabletNode, before, loc, false, results,
beforeTimePartition)
+ && noFailure;
+ }
+ before = loc;
+ isSequence = true;
+ }
+ loc++;
+ }
+
+ // do not forget last part
+ if (before < loc
+ && (isSequence
+ ||
!IoTDBDescriptor.getInstance().getConfig().isEnableDiscardOutOfOrderData())) {
+ noFailure =
+ insertTabletToTsFileProcessor(
+ insertTabletNode, before, loc, isSequence, results,
beforeTimePartition)
+ && noFailure;
+ }
+ long globalLatestFlushedTime =
+
lastFlushTimeManager.getGlobalFlushedTime(insertTabletNode.getDevicePath().getFullPath());
+ // TODO:LAST CACHE
+ // tryToUpdateBatchInsertLastCache(insertTabletNode,
globalLatestFlushedTime);
+
+ if (!noFailure) {
+ throw new BatchProcessException(results);
+ }
+
+ // TODO: trigger // fire trigger after insertion
+ // TriggerEngine.fire(TriggerEvent.AFTER_INSERT, insertTabletPlan,
firePosition);
+ } finally {
+ writeUnlock();
+ }
+ }
+
/** @return whether the given time falls in ttl */
private boolean isAlive(long time) {
return dataTTL == Long.MAX_VALUE || (System.currentTimeMillis() - time) <=
dataTTL;
@@ -1095,6 +1242,68 @@ public class VirtualStorageGroupProcessor {
return true;
}
+ /**
+ * insert batch to tsfile processor thread-safety that the caller need to
guarantee The rows to be
+ * inserted are in the range [start, end) Null value in each column values
will be replaced by the
+ * subsequent non-null value, e.g., {1, null, 3, null, 5} will be {1, 3, 5,
null, 5}
+ *
+ * @param insertTabletNode insert a tablet of a device
+ * @param sequence whether is sequence
+ * @param start start index of rows to be inserted in insertTabletPlan
+ * @param end end index of rows to be inserted in insertTabletPlan
+ * @param results result array
+ * @param timePartitionId time partition id
+ * @return false if any failure occurs when inserting the tablet, true
otherwise
+ */
+ private boolean insertTabletToTsFileProcessor(
+ InsertTabletNode insertTabletNode,
+ int start,
+ int end,
+ boolean sequence,
+ TSStatus[] results,
+ long timePartitionId) {
+ // return when start >= end
+ if (start >= end) {
+ return true;
+ }
+
+ TsFileProcessor tsFileProcessor =
getOrCreateTsFileProcessor(timePartitionId, sequence);
+ if (tsFileProcessor == null) {
+ for (int i = start; i < end; i++) {
+ results[i] =
+ RpcUtils.getStatus(
+ TSStatusCode.INTERNAL_SERVER_ERROR,
+ "can not create TsFileProcessor, timePartitionId: " +
timePartitionId);
+ }
+ return false;
+ }
+
+ try {
+ tsFileProcessor.insertTablet(insertTabletNode, start, end, results);
+ } catch (WriteProcessRejectException e) {
+ logger.warn("insert to TsFileProcessor rejected, {}", e.getMessage());
+ return false;
+ } catch (WriteProcessException e) {
+ logger.error("insert to TsFileProcessor error ", e);
+ return false;
+ }
+
+ lastFlushTimeManager.ensureLastTimePartition(timePartitionId);
+ // try to update the latest time of the device of this tsRecord
+ if (sequence) {
+ lastFlushTimeManager.updateLastTime(
+ timePartitionId,
+ insertTabletNode.getDevicePath().getFullPath(),
+ insertTabletNode.getTimes()[end - 1]);
+ }
+
+ // check memtable size and may async try to flush the work memtable
+ if (tsFileProcessor.shouldFlush()) {
+ fileFlushPolicy.apply(this, tsFileProcessor, sequence);
+ }
+ return true;
+ }
+
private void tryToUpdateBatchInsertLastCache(InsertTabletPlan plan, Long
latestFlushedTime) {
if (!IoTDBDescriptor.getInstance().getConfig().isLastCacheEnabled()) {
return;
@@ -1145,6 +1354,31 @@ public class VirtualStorageGroupProcessor {
}
}
+ private void insertToTsFileProcessor(
+ InsertRowNode insertRowNode, boolean sequence, long timePartitionId)
+ throws WriteProcessException {
+ TsFileProcessor tsFileProcessor =
getOrCreateTsFileProcessor(timePartitionId, sequence);
+ if (tsFileProcessor == null) {
+ return;
+ }
+
+ tsFileProcessor.insert(insertRowNode);
+
+ // try to update the latest time of the device of this tsRecord
+ lastFlushTimeManager.updateLastTime(
+ timePartitionId, insertRowNode.getDevicePath().getFullPath(),
insertRowNode.getTime());
+
+ long globalLatestFlushTime =
+
lastFlushTimeManager.getGlobalFlushedTime(insertRowNode.getDevicePath().getFullPath());
+
+ // tryToUpdateInsertLastCache(insertRowNode, globalLatestFlushTime);
+
+ // check memtable size and may asyncTryToFlush the work memtable
+ if (tsFileProcessor.shouldFlush()) {
+ fileFlushPolicy.apply(this, tsFileProcessor, sequence);
+ }
+ }
+
private void tryToUpdateInsertLastCache(InsertRowPlan plan, Long
latestFlushedTime) {
if (!IoTDBDescriptor.getInstance().getConfig().isLastCacheEnabled()) {
return;
diff --git
a/server/src/main/java/org/apache/iotdb/db/mpp/sql/planner/plan/node/write/InsertNode.java
b/server/src/main/java/org/apache/iotdb/db/mpp/sql/planner/plan/node/write/InsertNode.java
index 96ccd482c7..f0ec0e1a4b 100644
---
a/server/src/main/java/org/apache/iotdb/db/mpp/sql/planner/plan/node/write/InsertNode.java
+++
b/server/src/main/java/org/apache/iotdb/db/mpp/sql/planner/plan/node/write/InsertNode.java
@@ -39,7 +39,8 @@ public abstract class InsertNode extends PlanNode {
protected PartialPath devicePath;
protected boolean isAligned;
- protected MeasurementSchema[] measurements;
+ protected MeasurementSchema[] measurementSchemas;
+ protected String[] measurements;
protected TSDataType[] dataTypes;
// TODO(INSERT) need to change it to a function handle to update last time
value
// protected IMeasurementMNode[] measurementMNodes;
@@ -61,12 +62,12 @@ public abstract class InsertNode extends PlanNode {
PlanNodeId id,
PartialPath devicePath,
boolean isAligned,
- MeasurementSchema[] measurements,
+ MeasurementSchema[] measurementSchemas,
TSDataType[] dataTypes) {
super(id);
this.devicePath = devicePath;
this.isAligned = isAligned;
- this.measurements = measurements;
+ this.measurementSchemas = measurementSchemas;
this.dataTypes = dataTypes;
}
@@ -94,12 +95,22 @@ public abstract class InsertNode extends PlanNode {
isAligned = aligned;
}
- public MeasurementSchema[] getMeasurements() {
- return measurements;
+ public MeasurementSchema[] getMeasurementSchemas() {
+ return measurementSchemas;
+ }
+
+ public void setMeasurementSchemas(MeasurementSchema[] measurementSchemas) {
+ this.measurementSchemas = measurementSchemas;
}
- public void setMeasurements(MeasurementSchema[] measurements) {
- this.measurements = measurements;
+ public String[] getMeasurements() {
+ if (measurements == null) {
+ measurements = new String[measurementSchemas.length];
+ for (int i = 0; i < measurementSchemas.length; i++) {
+ measurements[i] = measurementSchemas[i].getMeasurementId();
+ }
+ }
+ return measurements;
}
public TSDataType[] getDataTypes() {
diff --git
a/server/src/main/java/org/apache/iotdb/db/mpp/sql/planner/plan/node/write/InsertTabletNode.java
b/server/src/main/java/org/apache/iotdb/db/mpp/sql/planner/plan/node/write/InsertTabletNode.java
index f48cf01272..700fdbab93 100644
---
a/server/src/main/java/org/apache/iotdb/db/mpp/sql/planner/plan/node/write/InsertTabletNode.java
+++
b/server/src/main/java/org/apache/iotdb/db/mpp/sql/planner/plan/node/write/InsertTabletNode.java
@@ -62,13 +62,13 @@ public class InsertTabletNode extends InsertNode {
PlanNodeId id,
PartialPath devicePath,
boolean isAligned,
- MeasurementSchema[] measurements,
+ MeasurementSchema[] measurementSchemas,
TSDataType[] dataTypes,
long[] times,
BitMap[] bitMaps,
Object[] columns,
int rowCount) {
- super(id, devicePath, isAligned, measurements, dataTypes);
+ super(id, devicePath, isAligned, measurementSchemas, dataTypes);
this.times = times;
this.bitMaps = bitMaps;
this.columns = columns;
@@ -228,7 +228,7 @@ public class InsertTabletNode extends InsertNode {
getPlanNodeId(),
devicePath,
isAligned,
- measurements,
+ measurementSchemas,
dataTypes,
subTimes,
bitMaps,
diff --git a/server/src/main/java/org/apache/iotdb/db/utils/MemUtils.java
b/server/src/main/java/org/apache/iotdb/db/utils/MemUtils.java
index 854fb0348a..66bfc6ab84 100644
--- a/server/src/main/java/org/apache/iotdb/db/utils/MemUtils.java
+++ b/server/src/main/java/org/apache/iotdb/db/utils/MemUtils.java
@@ -19,6 +19,7 @@
package org.apache.iotdb.db.utils;
import org.apache.iotdb.commons.conf.IoTDBConstant;
+import org.apache.iotdb.db.mpp.sql.planner.plan.node.write.InsertTabletNode;
import org.apache.iotdb.db.qp.physical.crud.InsertTabletPlan;
import org.apache.iotdb.tsfile.file.metadata.enums.TSDataType;
import org.apache.iotdb.tsfile.utils.Binary;
@@ -105,6 +106,33 @@ public class MemUtils {
return memSize;
}
+ /**
+ * If mem control enabled, do not add text data size here, the size will be
added to memtable
+ * before inserting.
+ */
+ public static long getTabletSize(
+ InsertTabletNode insertTabletNode, int start, int end, boolean
addingTextDataSize) {
+ if (start >= end) {
+ return 0L;
+ }
+ long memSize = 0;
+ for (int i = 0; i < insertTabletNode.getMeasurements().length; i++) {
+ if (insertTabletNode.getMeasurements()[i] == null) {
+ continue;
+ }
+ // time column memSize
+ memSize += (end - start) * 8L;
+ if (insertTabletNode.getDataTypes()[i] == TSDataType.TEXT &&
addingTextDataSize) {
+ for (int j = start; j < end; j++) {
+ memSize += getBinarySize(((Binary[])
insertTabletNode.getColumns()[i])[j]);
+ }
+ } else {
+ memSize += (end - start) *
insertTabletNode.getDataTypes()[i].getDataTypeSize();
+ }
+ }
+ return memSize;
+ }
+
/**
* If mem control enabled, do not add text data size here, the size will be
added to memtable
* before inserting.
@@ -158,6 +186,32 @@ public class MemUtils {
return memSize;
}
+ public static long getAlignedTabletSize(
+ InsertTabletNode insertTabletNode, int start, int end, boolean
addingTextDataSize) {
+ if (start >= end) {
+ return 0L;
+ }
+ long memSize = 0;
+ for (int i = 0; i < insertTabletNode.getMeasurements().length; i++) {
+ if (insertTabletNode.getMeasurements()[i] == null) {
+ continue;
+ }
+ TSDataType valueType;
+ // value columns memSize
+ valueType = insertTabletNode.getDataTypes()[i];
+ if (valueType == TSDataType.TEXT && addingTextDataSize) {
+ for (int j = start; j < end; j++) {
+ memSize += getBinarySize(((Binary[])
insertTabletNode.getColumns()[i])[j]);
+ }
+ } else {
+ memSize += (long) (end - start) * valueType.getDataTypeSize();
+ }
+ }
+ // time and index column memSize for vector
+ memSize += (end - start) * (8L + 4L);
+ return memSize;
+ }
+
/** Calculate how much memory will be used if the given record is written to
sequence file. */
public static long getTsRecordMem(TSRecord record) {
long memUsed = 8; // time
diff --git
a/server/src/main/java/org/apache/iotdb/db/writelog/node/ExclusiveWriteLogNode.java
b/server/src/main/java/org/apache/iotdb/db/writelog/node/ExclusiveWriteLogNode.java
index 5283cafaca..32c77afaf4 100644
---
a/server/src/main/java/org/apache/iotdb/db/writelog/node/ExclusiveWriteLogNode.java
+++
b/server/src/main/java/org/apache/iotdb/db/writelog/node/ExclusiveWriteLogNode.java
@@ -23,6 +23,7 @@ import org.apache.iotdb.db.conf.IoTDBConfig;
import org.apache.iotdb.db.conf.IoTDBDescriptor;
import org.apache.iotdb.db.conf.directories.DirectoryManager;
import org.apache.iotdb.db.engine.fileSystem.SystemFileFactory;
+import org.apache.iotdb.db.mpp.sql.planner.plan.node.PlanNode;
import org.apache.iotdb.db.qp.physical.PhysicalPlan;
import org.apache.iotdb.db.utils.MmapUtil;
import org.apache.iotdb.db.utils.ThreadUtils;
@@ -133,6 +134,33 @@ public class ExclusiveWriteLogNode implements
WriteLogNode, Comparable<Exclusive
}
}
+ @Override
+ public void write(PlanNode node) throws IOException {
+ if (deleted.get()) {
+ throw new IOException("WAL node deleted");
+ }
+ lock.lock();
+ try {
+ putLog(node);
+ if (bufferedLogNum >= config.getFlushWalThreshold()) {
+ sync();
+ }
+ } catch (BufferOverflowException e) {
+ // if the size of a single plan bigger than logBufferWorking
+ // we need to clear the buffer to drop something wrong that has written.
+ logBufferWorking.clear();
+ // TODO(WAL)
+ // int neededSize = node.getSerializedSize();
+ // throw new IOException(
+ // "Log cannot fit into the buffer, please increase
wal_buffer_size to more than
+ // "
+ // + neededSize * 2,
+ // e);
+ } finally {
+ lock.unlock();
+ }
+ }
+
private void putLog(PhysicalPlan plan) {
try {
plan.serialize(logBufferWorking);
@@ -149,6 +177,24 @@ public class ExclusiveWriteLogNode implements
WriteLogNode, Comparable<Exclusive
bufferedLogNum++;
}
+ private void putLog(PlanNode node) {
+ try {
+ // TODO(WAL)
+ // node.serialize(logBufferWorking);
+ } catch (BufferOverflowException e) {
+ bufferOverflowNum++;
+ if (bufferOverflowNum > 200) {
+ logger.info(
+ "WAL bytebuffer overflows too many times. If this occurs
frequently, please increase wal_buffer_size.");
+ bufferOverflowNum = 0;
+ }
+ sync();
+ // TODO(WAL)
+ // node.serialize(logBufferWorking);
+ }
+ bufferedLogNum++;
+ }
+
@Override
public void close() {
sync();
diff --git
a/server/src/main/java/org/apache/iotdb/db/writelog/node/WriteLogNode.java
b/server/src/main/java/org/apache/iotdb/db/writelog/node/WriteLogNode.java
index 952238f57e..e5b1f82e93 100644
--- a/server/src/main/java/org/apache/iotdb/db/writelog/node/WriteLogNode.java
+++ b/server/src/main/java/org/apache/iotdb/db/writelog/node/WriteLogNode.java
@@ -18,6 +18,7 @@
*/
package org.apache.iotdb.db.writelog.node;
+import org.apache.iotdb.db.mpp.sql.planner.plan.node.PlanNode;
import org.apache.iotdb.db.qp.physical.PhysicalPlan;
import org.apache.iotdb.db.writelog.io.ILogReader;
@@ -36,6 +37,14 @@ public interface WriteLogNode {
*/
void write(PhysicalPlan plan) throws IOException;
+ /**
+ * Write a wal for a PlanNode. First, the PhysicalPlan will be conveyed to
byte[]. Then the byte[]
+ * will be put into a cache. When the cache is full, the logs in the cache
will be synced to disk.
+ *
+ * @param plan - a PhysicalPlan
+ */
+ void write(PlanNode plan) throws IOException;
+
/** Sync and close streams. */
void close() throws IOException;