You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@iotdb.apache.org by yo...@apache.org on 2025/09/28 04:05:14 UTC
(iotdb) 01/02: reformat procedure
This is an automated email from the ASF dual-hosted git repository.
yongzao pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/iotdb.git
commit 8160a18727ac0d06846725e59ea8666277a608c3
Author: Yongzao <53...@qq.com>
AuthorDate: Sun Sep 28 11:59:43 2025 +0800
reformat procedure
---
.../iotdb/confignode/manager/ProcedureManager.java | 2000 ++++++++++----------
.../procedure/CompletedProcedureContainer.java | 73 +-
.../procedure/CompletedProcedureRecycler.java | 169 +-
.../confignode/procedure/InternalProcedure.java | 99 +-
.../procedure/PartitionTableAutoCleaner.java | 225 +--
.../iotdb/confignode/procedure/Procedure.java | 1647 ++++++++--------
.../confignode/procedure/ProcedureExecutor.java | 1881 +++++++++---------
.../confignode/procedure/ProcedureMetrics.java | 369 ++--
.../confignode/procedure/RootProcedureStack.java | 355 ++--
.../confignode/procedure/StoppableThread.java | 111 +-
.../procedure/TimeoutExecutorThread.java | 215 +--
.../procedure/env/ConfigNodeProcedureEnv.java | 1789 ++++++++---------
.../procedure/env/RegionMaintainHandler.java | 1175 ++++++------
.../procedure/env/RemoveDataNodeHandler.java | 1363 ++++++-------
.../procedure/exception/ProcedureException.java | 89 +-
.../scheduler/AbstractProcedureScheduler.java | 441 ++---
.../confignode/procedure/scheduler/LockQueue.java | 119 +-
.../procedure/scheduler/ProcedureScheduler.java | 223 +--
.../scheduler/SimpleProcedureScheduler.java | 159 +-
.../procedure/store/ConfigProcedureStore.java | 363 ++--
.../procedure/store/IProcedureFactory.java | 61 +-
.../procedure/store/IProcedureStore.java | 117 +-
.../procedure/store/ProcedureFactory.java | 1119 +++++------
.../confignode/procedure/store/ProcedureType.java | 377 ++--
.../confignode/procedure/store/ProcedureWAL.java | 153 +-
25 files changed, 7358 insertions(+), 7334 deletions(-)
diff --git a/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/manager/ProcedureManager.java b/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/manager/ProcedureManager.java
index 3dcb01de273..96ee4e1c680 100644
--- a/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/manager/ProcedureManager.java
+++ b/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/manager/ProcedureManager.java
@@ -1,1003 +1,1003 @@
-/*
- * 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.confignode.manager;
-
-import org.apache.iotdb.common.rpc.thrift.Model;
-import org.apache.iotdb.common.rpc.thrift.TConsensusGroupId;
-import org.apache.iotdb.common.rpc.thrift.TConsensusGroupType;
-import org.apache.iotdb.common.rpc.thrift.TDataNodeConfiguration;
-import org.apache.iotdb.common.rpc.thrift.TDataNodeLocation;
-import org.apache.iotdb.common.rpc.thrift.TEndPoint;
-import org.apache.iotdb.common.rpc.thrift.TSStatus;
-import org.apache.iotdb.commons.cluster.NodeStatus;
-import org.apache.iotdb.commons.conf.CommonConfig;
-import org.apache.iotdb.commons.conf.CommonDescriptor;
-import org.apache.iotdb.commons.conf.IoTDBConstant;
-import org.apache.iotdb.commons.exception.IoTDBException;
-import org.apache.iotdb.commons.path.PartialPath;
-import org.apache.iotdb.commons.path.PathDeserializeUtil;
-import org.apache.iotdb.commons.path.PathPatternTree;
-import org.apache.iotdb.commons.pipe.agent.plugin.meta.PipePluginMeta;
-import org.apache.iotdb.commons.schema.table.TsTable;
-import org.apache.iotdb.commons.schema.table.column.TsTableColumnSchemaUtil;
-import org.apache.iotdb.commons.schema.view.viewExpression.ViewExpression;
-import org.apache.iotdb.commons.service.metric.MetricService;
-import org.apache.iotdb.commons.trigger.TriggerInformation;
-import org.apache.iotdb.commons.utils.StatusUtils;
-import org.apache.iotdb.commons.utils.TestOnly;
-import org.apache.iotdb.confignode.conf.ConfigNodeConfig;
-import org.apache.iotdb.confignode.conf.ConfigNodeDescriptor;
-import org.apache.iotdb.confignode.consensus.request.write.ainode.RemoveAINodePlan;
-import org.apache.iotdb.confignode.consensus.request.write.auth.AuthorPlan;
-import org.apache.iotdb.confignode.consensus.request.write.confignode.RemoveConfigNodePlan;
-import org.apache.iotdb.confignode.consensus.request.write.database.SetTTLPlan;
-import org.apache.iotdb.confignode.consensus.request.write.datanode.RemoveDataNodePlan;
-import org.apache.iotdb.confignode.consensus.request.write.procedure.UpdateProcedurePlan;
-import org.apache.iotdb.confignode.consensus.request.write.region.CreateRegionGroupsPlan;
-import org.apache.iotdb.confignode.manager.partition.PartitionManager;
-import org.apache.iotdb.confignode.persistence.ProcedureInfo;
-import org.apache.iotdb.confignode.procedure.PartitionTableAutoCleaner;
-import org.apache.iotdb.confignode.procedure.Procedure;
-import org.apache.iotdb.confignode.procedure.ProcedureExecutor;
-import org.apache.iotdb.confignode.procedure.ProcedureMetrics;
-import org.apache.iotdb.confignode.procedure.env.ConfigNodeProcedureEnv;
-import org.apache.iotdb.confignode.procedure.env.RegionMaintainHandler;
-import org.apache.iotdb.confignode.procedure.env.RemoveDataNodeHandler;
-import org.apache.iotdb.confignode.procedure.impl.cq.CreateCQProcedure;
-import org.apache.iotdb.confignode.procedure.impl.model.CreateModelProcedure;
-import org.apache.iotdb.confignode.procedure.impl.model.DropModelProcedure;
-import org.apache.iotdb.confignode.procedure.impl.node.AddConfigNodeProcedure;
-import org.apache.iotdb.confignode.procedure.impl.node.RemoveAINodeProcedure;
-import org.apache.iotdb.confignode.procedure.impl.node.RemoveConfigNodeProcedure;
-import org.apache.iotdb.confignode.procedure.impl.node.RemoveDataNodesProcedure;
-import org.apache.iotdb.confignode.procedure.impl.pipe.plugin.CreatePipePluginProcedure;
-import org.apache.iotdb.confignode.procedure.impl.pipe.plugin.DropPipePluginProcedure;
-import org.apache.iotdb.confignode.procedure.impl.pipe.runtime.PipeHandleLeaderChangeProcedure;
-import org.apache.iotdb.confignode.procedure.impl.pipe.runtime.PipeHandleMetaChangeProcedure;
-import org.apache.iotdb.confignode.procedure.impl.pipe.runtime.PipeMetaSyncProcedure;
-import org.apache.iotdb.confignode.procedure.impl.pipe.task.AlterPipeProcedureV2;
-import org.apache.iotdb.confignode.procedure.impl.pipe.task.CreatePipeProcedureV2;
-import org.apache.iotdb.confignode.procedure.impl.pipe.task.DropPipeProcedureV2;
-import org.apache.iotdb.confignode.procedure.impl.pipe.task.StartPipeProcedureV2;
-import org.apache.iotdb.confignode.procedure.impl.pipe.task.StopPipeProcedureV2;
-import org.apache.iotdb.confignode.procedure.impl.region.AddRegionPeerProcedure;
-import org.apache.iotdb.confignode.procedure.impl.region.CreateRegionGroupsProcedure;
-import org.apache.iotdb.confignode.procedure.impl.region.ReconstructRegionProcedure;
-import org.apache.iotdb.confignode.procedure.impl.region.RegionMigrateProcedure;
-import org.apache.iotdb.confignode.procedure.impl.region.RegionMigrationPlan;
-import org.apache.iotdb.confignode.procedure.impl.region.RegionOperationProcedure;
-import org.apache.iotdb.confignode.procedure.impl.region.RemoveRegionPeerProcedure;
-import org.apache.iotdb.confignode.procedure.impl.schema.AlterLogicalViewProcedure;
-import org.apache.iotdb.confignode.procedure.impl.schema.DeactivateTemplateProcedure;
-import org.apache.iotdb.confignode.procedure.impl.schema.DeleteDatabaseProcedure;
-import org.apache.iotdb.confignode.procedure.impl.schema.DeleteLogicalViewProcedure;
-import org.apache.iotdb.confignode.procedure.impl.schema.DeleteTimeSeriesProcedure;
-import org.apache.iotdb.confignode.procedure.impl.schema.SetTTLProcedure;
-import org.apache.iotdb.confignode.procedure.impl.schema.SetTemplateProcedure;
-import org.apache.iotdb.confignode.procedure.impl.schema.UnsetTemplateProcedure;
-import org.apache.iotdb.confignode.procedure.impl.schema.table.AbstractAlterOrDropTableProcedure;
-import org.apache.iotdb.confignode.procedure.impl.schema.table.AddTableColumnProcedure;
-import org.apache.iotdb.confignode.procedure.impl.schema.table.CreateTableProcedure;
-import org.apache.iotdb.confignode.procedure.impl.schema.table.DeleteDevicesProcedure;
-import org.apache.iotdb.confignode.procedure.impl.schema.table.DropTableColumnProcedure;
-import org.apache.iotdb.confignode.procedure.impl.schema.table.DropTableProcedure;
-import org.apache.iotdb.confignode.procedure.impl.schema.table.RenameTableColumnProcedure;
-import org.apache.iotdb.confignode.procedure.impl.schema.table.RenameTableProcedure;
-import org.apache.iotdb.confignode.procedure.impl.schema.table.SetTablePropertiesProcedure;
-import org.apache.iotdb.confignode.procedure.impl.schema.table.view.AddViewColumnProcedure;
-import org.apache.iotdb.confignode.procedure.impl.schema.table.view.CreateTableViewProcedure;
-import org.apache.iotdb.confignode.procedure.impl.schema.table.view.DropViewColumnProcedure;
-import org.apache.iotdb.confignode.procedure.impl.schema.table.view.DropViewProcedure;
-import org.apache.iotdb.confignode.procedure.impl.schema.table.view.RenameViewColumnProcedure;
-import org.apache.iotdb.confignode.procedure.impl.schema.table.view.RenameViewProcedure;
-import org.apache.iotdb.confignode.procedure.impl.schema.table.view.SetViewPropertiesProcedure;
-import org.apache.iotdb.confignode.procedure.impl.subscription.consumer.CreateConsumerProcedure;
-import org.apache.iotdb.confignode.procedure.impl.subscription.consumer.DropConsumerProcedure;
-import org.apache.iotdb.confignode.procedure.impl.subscription.consumer.runtime.ConsumerGroupMetaSyncProcedure;
-import org.apache.iotdb.confignode.procedure.impl.subscription.subscription.CreateSubscriptionProcedure;
-import org.apache.iotdb.confignode.procedure.impl.subscription.subscription.DropSubscriptionProcedure;
-import org.apache.iotdb.confignode.procedure.impl.subscription.topic.CreateTopicProcedure;
-import org.apache.iotdb.confignode.procedure.impl.subscription.topic.DropTopicProcedure;
-import org.apache.iotdb.confignode.procedure.impl.subscription.topic.runtime.TopicMetaSyncProcedure;
-import org.apache.iotdb.confignode.procedure.impl.sync.AuthOperationProcedure;
-import org.apache.iotdb.confignode.procedure.impl.testonly.AddNeverFinishSubProcedureProcedure;
-import org.apache.iotdb.confignode.procedure.impl.testonly.CreateManyDatabasesProcedure;
-import org.apache.iotdb.confignode.procedure.impl.trigger.CreateTriggerProcedure;
-import org.apache.iotdb.confignode.procedure.impl.trigger.DropTriggerProcedure;
-import org.apache.iotdb.confignode.procedure.scheduler.ProcedureScheduler;
-import org.apache.iotdb.confignode.procedure.scheduler.SimpleProcedureScheduler;
-import org.apache.iotdb.confignode.procedure.store.ConfigProcedureStore;
-import org.apache.iotdb.confignode.procedure.store.IProcedureStore;
-import org.apache.iotdb.confignode.procedure.store.ProcedureFactory;
-import org.apache.iotdb.confignode.procedure.store.ProcedureType;
-import org.apache.iotdb.confignode.rpc.thrift.TAlterLogicalViewReq;
-import org.apache.iotdb.confignode.rpc.thrift.TAlterOrDropTableReq;
-import org.apache.iotdb.confignode.rpc.thrift.TAlterPipeReq;
-import org.apache.iotdb.confignode.rpc.thrift.TCloseConsumerReq;
-import org.apache.iotdb.confignode.rpc.thrift.TConfigNodeRegisterReq;
-import org.apache.iotdb.confignode.rpc.thrift.TCreateCQReq;
-import org.apache.iotdb.confignode.rpc.thrift.TCreateConsumerReq;
-import org.apache.iotdb.confignode.rpc.thrift.TCreatePipeReq;
-import org.apache.iotdb.confignode.rpc.thrift.TCreateTopicReq;
-import org.apache.iotdb.confignode.rpc.thrift.TDatabaseSchema;
-import org.apache.iotdb.confignode.rpc.thrift.TDeleteLogicalViewReq;
-import org.apache.iotdb.confignode.rpc.thrift.TDeleteTableDeviceReq;
-import org.apache.iotdb.confignode.rpc.thrift.TDeleteTableDeviceResp;
-import org.apache.iotdb.confignode.rpc.thrift.TDropPipePluginReq;
-import org.apache.iotdb.confignode.rpc.thrift.TExtendRegionReq;
-import org.apache.iotdb.confignode.rpc.thrift.TMigrateRegionReq;
-import org.apache.iotdb.confignode.rpc.thrift.TReconstructRegionReq;
-import org.apache.iotdb.confignode.rpc.thrift.TRemoveRegionReq;
-import org.apache.iotdb.confignode.rpc.thrift.TSubscribeReq;
-import org.apache.iotdb.confignode.rpc.thrift.TUnsubscribeReq;
-import org.apache.iotdb.consensus.ConsensusFactory;
-import org.apache.iotdb.db.exception.BatchProcessException;
-import org.apache.iotdb.db.schemaengine.template.Template;
-import org.apache.iotdb.db.utils.constant.SqlConstant;
-import org.apache.iotdb.rpc.RpcUtils;
-import org.apache.iotdb.rpc.TSStatusCode;
-
-import org.apache.ratis.util.AutoCloseableLock;
-import org.apache.tsfile.utils.Binary;
-import org.apache.tsfile.utils.Pair;
-import org.apache.tsfile.utils.ReadWriteIOUtils;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-import javax.annotation.Nonnull;
-import javax.annotation.Nullable;
-
-import java.io.IOException;
-import java.nio.ByteBuffer;
-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.Optional;
-import java.util.Set;
-import java.util.concurrent.ScheduledExecutorService;
-import java.util.concurrent.locks.ReentrantLock;
-import java.util.function.BiFunction;
-import java.util.stream.Collectors;
-import java.util.stream.Stream;
-
-public class ProcedureManager {
- private static final Logger LOGGER = LoggerFactory.getLogger(ProcedureManager.class);
-
- private static final ConfigNodeConfig CONFIG_NODE_CONFIG =
- ConfigNodeDescriptor.getInstance().getConf();
- private static final CommonConfig COMMON_CONFIG = CommonDescriptor.getInstance().getConfig();
-
- public static final long PROCEDURE_WAIT_TIME_OUT = COMMON_CONFIG.getDnConnectionTimeoutInMS();
- public static final int PROCEDURE_WAIT_RETRY_TIMEOUT = 10;
- private static final String PROCEDURE_TIMEOUT_MESSAGE =
- "Timed out to wait for procedure return. The procedure is still running.";
-
- private final ConfigManager configManager;
- private ProcedureExecutor<ConfigNodeProcedureEnv> executor;
- private ProcedureScheduler scheduler;
- private IProcedureStore store;
- private ConfigNodeProcedureEnv env;
-
- private final long planSizeLimit;
- private ProcedureMetrics procedureMetrics;
-
- private final PartitionTableAutoCleaner partitionTableCleaner;
-
- private final ReentrantLock tableLock = new ReentrantLock();
-
- public ProcedureManager(ConfigManager configManager, ProcedureInfo procedureInfo) {
- this.configManager = configManager;
- this.scheduler = new SimpleProcedureScheduler();
- this.store = new ConfigProcedureStore(configManager, procedureInfo);
- this.env = new ConfigNodeProcedureEnv(configManager, scheduler);
- this.executor = new ProcedureExecutor<>(env, store, scheduler);
- this.planSizeLimit =
- ConfigNodeDescriptor.getInstance()
- .getConf()
- .getConfigNodeRatisConsensusLogAppenderBufferSize()
- - IoTDBConstant.RAFT_LOG_BASIC_SIZE;
- this.procedureMetrics = new ProcedureMetrics(this);
- this.partitionTableCleaner = new PartitionTableAutoCleaner<>(configManager);
- }
-
- public void startExecutor() {
- if (!executor.isRunning()) {
- executor.init(CONFIG_NODE_CONFIG.getProcedureCoreWorkerThreadsCount());
- executor.startWorkers();
- executor.startCompletedCleaner(
- CONFIG_NODE_CONFIG.getProcedureCompletedCleanInterval(),
- CONFIG_NODE_CONFIG.getProcedureCompletedEvictTTL());
- executor.addInternalProcedure(partitionTableCleaner);
- store.start();
- LOGGER.info("ProcedureManager is started successfully.");
- }
- }
-
- public void stopExecutor() {
- if (executor.isRunning()) {
- executor.stop();
- if (!executor.isRunning()) {
- executor.join();
- store.stop();
- LOGGER.info("ProcedureManager is stopped successfully.");
- }
- executor.removeInternalProcedure(partitionTableCleaner);
- }
- }
-
- @TestOnly
- public TSStatus createManyDatabases() {
- this.executor.submitProcedure(new CreateManyDatabasesProcedure());
- return StatusUtils.OK;
- }
-
- @TestOnly
- public TSStatus testSubProcedure() {
- this.executor.submitProcedure(new AddNeverFinishSubProcedureProcedure());
- return StatusUtils.OK;
- }
-
- public TSStatus deleteDatabases(
- final List<TDatabaseSchema> deleteSgSchemaList, final boolean isGeneratedByPipe) {
- final List<DeleteDatabaseProcedure> procedures = new ArrayList<>();
- final long startCheckTimeForProcedures = System.currentTimeMillis();
- for (final TDatabaseSchema databaseSchema : deleteSgSchemaList) {
- final String database = databaseSchema.getName();
- boolean hasOverlappedTask = false;
- synchronized (this) {
- while (executor.isRunning()
- && System.currentTimeMillis() - startCheckTimeForProcedures < PROCEDURE_WAIT_TIME_OUT) {
- final Pair<Long, Boolean> procedureIdDuplicatePair =
- checkDuplicateTableTask(
- database, null, null, null, null, ProcedureType.DELETE_DATABASE_PROCEDURE);
- hasOverlappedTask = procedureIdDuplicatePair.getRight();
-
- if (Boolean.FALSE.equals(procedureIdDuplicatePair.getRight())) {
- DeleteDatabaseProcedure procedure =
- new DeleteDatabaseProcedure(databaseSchema, isGeneratedByPipe);
- this.executor.submitProcedure(procedure);
- procedures.add(procedure);
- break;
- }
- try {
- wait(PROCEDURE_WAIT_RETRY_TIMEOUT);
- } catch (final InterruptedException e) {
- Thread.currentThread().interrupt();
- }
- }
- if (hasOverlappedTask) {
- return RpcUtils.getStatus(
- TSStatusCode.OVERLAP_WITH_EXISTING_TASK,
- String.format(
- "Some other task is operating table under the database %s, please retry after the procedure finishes.",
- database));
- }
- }
- }
- List<TSStatus> results = new ArrayList<>(procedures.size());
- procedures.forEach(procedure -> results.add(waitingProcedureFinished(procedure)));
- // Clear the previously deleted regions
- final PartitionManager partitionManager = getConfigManager().getPartitionManager();
- partitionManager.getRegionMaintainer().submit(partitionManager::maintainRegionReplicas);
- if (results.stream()
- .allMatch(result -> result.getCode() == TSStatusCode.SUCCESS_STATUS.getStatusCode())) {
- return StatusUtils.OK;
- } else {
- return RpcUtils.getStatus(results);
- }
- }
-
- public TSStatus deleteTimeSeries(
- String queryId, PathPatternTree patternTree, boolean isGeneratedByPipe) {
- DeleteTimeSeriesProcedure procedure = null;
- synchronized (this) {
- boolean hasOverlappedTask = false;
- ProcedureType type;
- DeleteTimeSeriesProcedure deleteTimeSeriesProcedure;
- for (Procedure<?> runningProcedure : executor.getProcedures().values()) {
- type = ProcedureFactory.getProcedureType(runningProcedure);
- if (type == null || !type.equals(ProcedureType.DELETE_TIMESERIES_PROCEDURE)) {
- continue;
- }
- deleteTimeSeriesProcedure = ((DeleteTimeSeriesProcedure) runningProcedure);
- if (queryId.equals(deleteTimeSeriesProcedure.getQueryId())) {
- procedure = deleteTimeSeriesProcedure;
- break;
- }
- if (patternTree.isOverlapWith(deleteTimeSeriesProcedure.getPatternTree())) {
- hasOverlappedTask = true;
- break;
- }
- }
-
- if (procedure == null) {
- if (hasOverlappedTask) {
- return RpcUtils.getStatus(
- TSStatusCode.OVERLAP_WITH_EXISTING_TASK,
- "Some other task is deleting some target timeseries.");
- }
- procedure = new DeleteTimeSeriesProcedure(queryId, patternTree, isGeneratedByPipe);
- this.executor.submitProcedure(procedure);
- }
- }
- return waitingProcedureFinished(procedure);
- }
-
- public TSStatus deleteLogicalView(TDeleteLogicalViewReq req) {
- String queryId = req.getQueryId();
- PathPatternTree patternTree =
- PathPatternTree.deserialize(ByteBuffer.wrap(req.getPathPatternTree()));
- DeleteLogicalViewProcedure procedure = null;
- synchronized (this) {
- boolean hasOverlappedTask = false;
- ProcedureType type;
- DeleteLogicalViewProcedure deleteLogicalViewProcedure;
- for (Procedure<?> runningProcedure : executor.getProcedures().values()) {
- type = ProcedureFactory.getProcedureType(runningProcedure);
- if (type == null || !type.equals(ProcedureType.DELETE_LOGICAL_VIEW_PROCEDURE)) {
- continue;
- }
- deleteLogicalViewProcedure = ((DeleteLogicalViewProcedure) runningProcedure);
- if (queryId.equals(deleteLogicalViewProcedure.getQueryId())) {
- procedure = deleteLogicalViewProcedure;
- break;
- }
- if (patternTree.isOverlapWith(deleteLogicalViewProcedure.getPatternTree())) {
- hasOverlappedTask = true;
- break;
- }
- }
-
- if (procedure == null) {
- if (hasOverlappedTask) {
- return RpcUtils.getStatus(
- TSStatusCode.OVERLAP_WITH_EXISTING_TASK,
- "Some other task is deleting some target views.");
- }
- procedure =
- new DeleteLogicalViewProcedure(
- queryId, patternTree, req.isSetIsGeneratedByPipe() && req.isIsGeneratedByPipe());
- this.executor.submitProcedure(procedure);
- }
- }
- return waitingProcedureFinished(procedure);
- }
-
- public TSStatus alterLogicalView(final TAlterLogicalViewReq req) {
- final String queryId = req.getQueryId();
- final ByteBuffer byteBuffer = ByteBuffer.wrap(req.getViewBinary());
- final Map<PartialPath, ViewExpression> viewPathToSourceMap = new HashMap<>();
- final int size = byteBuffer.getInt();
- PartialPath path;
- ViewExpression viewExpression;
- for (int i = 0; i < size; i++) {
- path = (PartialPath) PathDeserializeUtil.deserialize(byteBuffer);
- viewExpression = ViewExpression.deserialize(byteBuffer);
- viewPathToSourceMap.put(path, viewExpression);
- }
-
- AlterLogicalViewProcedure procedure = null;
- synchronized (this) {
- ProcedureType type;
- AlterLogicalViewProcedure alterLogicalViewProcedure;
- for (Procedure<?> runningProcedure : executor.getProcedures().values()) {
- type = ProcedureFactory.getProcedureType(runningProcedure);
- if (type == null || !type.equals(ProcedureType.ALTER_LOGICAL_VIEW_PROCEDURE)) {
- continue;
- }
- alterLogicalViewProcedure = ((AlterLogicalViewProcedure) runningProcedure);
- if (queryId.equals(alterLogicalViewProcedure.getQueryId())) {
- procedure = alterLogicalViewProcedure;
- break;
- }
- }
-
- if (procedure == null) {
- procedure =
- new AlterLogicalViewProcedure(
- queryId,
- viewPathToSourceMap,
- req.isSetIsGeneratedByPipe() && req.isIsGeneratedByPipe());
- this.executor.submitProcedure(procedure);
- }
- }
- return waitingProcedureFinished(procedure);
- }
-
- public TSStatus setSchemaTemplate(
- String queryId, String templateName, String templateSetPath, boolean isGeneratedByPipe) {
- SetTemplateProcedure procedure = null;
- synchronized (this) {
- boolean hasOverlappedTask = false;
- ProcedureType type;
- SetTemplateProcedure setTemplateProcedure;
- for (Procedure<?> runningProcedure : executor.getProcedures().values()) {
- type = ProcedureFactory.getProcedureType(runningProcedure);
- if (type == null || !type.equals(ProcedureType.SET_TEMPLATE_PROCEDURE)) {
- continue;
- }
- setTemplateProcedure = (SetTemplateProcedure) runningProcedure;
- if (queryId.equals(setTemplateProcedure.getQueryId())) {
- procedure = setTemplateProcedure;
- break;
- }
- if (templateSetPath.equals(setTemplateProcedure.getTemplateSetPath())) {
- hasOverlappedTask = true;
- break;
- }
- }
-
- if (procedure == null) {
- if (hasOverlappedTask) {
- return RpcUtils.getStatus(
- TSStatusCode.OVERLAP_WITH_EXISTING_TASK,
- "Some other task is setting template on target path.");
- }
- procedure =
- new SetTemplateProcedure(queryId, templateName, templateSetPath, isGeneratedByPipe);
- this.executor.submitProcedure(procedure);
- }
- }
- return waitingProcedureFinished(procedure);
- }
-
- public TSStatus deactivateTemplate(
- String queryId, Map<PartialPath, List<Template>> templateSetInfo, boolean isGeneratedByPipe) {
- DeactivateTemplateProcedure procedure = null;
- synchronized (this) {
- boolean hasOverlappedTask = false;
- ProcedureType type;
- DeactivateTemplateProcedure deactivateTemplateProcedure;
- for (Procedure<?> runningProcedure : executor.getProcedures().values()) {
- type = ProcedureFactory.getProcedureType(runningProcedure);
- if (type == null || !type.equals(ProcedureType.DEACTIVATE_TEMPLATE_PROCEDURE)) {
- continue;
- }
- deactivateTemplateProcedure = (DeactivateTemplateProcedure) runningProcedure;
- if (queryId.equals(deactivateTemplateProcedure.getQueryId())) {
- procedure = deactivateTemplateProcedure;
- break;
- }
- for (PartialPath pattern : templateSetInfo.keySet()) {
- for (PartialPath existingPattern :
- deactivateTemplateProcedure.getTemplateSetInfo().keySet()) {
- if (pattern.overlapWith(existingPattern)) {
- hasOverlappedTask = true;
- break;
- }
- }
- if (hasOverlappedTask) {
- break;
- }
- }
- if (hasOverlappedTask) {
- break;
- }
- }
-
- if (procedure == null) {
- if (hasOverlappedTask) {
- return RpcUtils.getStatus(
- TSStatusCode.OVERLAP_WITH_EXISTING_TASK,
- "Some other task is deactivating some target template from target path.");
- }
- procedure = new DeactivateTemplateProcedure(queryId, templateSetInfo, isGeneratedByPipe);
- this.executor.submitProcedure(procedure);
- }
- }
- return waitingProcedureFinished(procedure);
- }
-
- public TSStatus unsetSchemaTemplate(
- String queryId, Template template, PartialPath path, boolean isGeneratedByPipe) {
- UnsetTemplateProcedure procedure = null;
- synchronized (this) {
- boolean hasOverlappedTask = false;
- ProcedureType type;
- UnsetTemplateProcedure unsetTemplateProcedure;
- for (Procedure<?> runningProcedure : executor.getProcedures().values()) {
- type = ProcedureFactory.getProcedureType(runningProcedure);
- if (type == null || !type.equals(ProcedureType.UNSET_TEMPLATE_PROCEDURE)) {
- continue;
- }
- unsetTemplateProcedure = (UnsetTemplateProcedure) runningProcedure;
- if (queryId.equals(unsetTemplateProcedure.getQueryId())) {
- procedure = unsetTemplateProcedure;
- break;
- }
- if (template.getId() == unsetTemplateProcedure.getTemplateId()
- && path.equals(unsetTemplateProcedure.getPath())) {
- hasOverlappedTask = true;
- break;
- }
- }
-
- if (procedure == null) {
- if (hasOverlappedTask) {
- return RpcUtils.getStatus(
- TSStatusCode.OVERLAP_WITH_EXISTING_TASK,
- "Some other task is unsetting target template from target path "
- + path.getFullPath());
- }
- procedure = new UnsetTemplateProcedure(queryId, template, path, isGeneratedByPipe);
- this.executor.submitProcedure(procedure);
- }
- }
- return waitingProcedureFinished(procedure);
- }
-
- /**
- * Generate an {@link AddConfigNodeProcedure}, and serially execute all the {@link
- * AddConfigNodeProcedure}s.
- */
- public void addConfigNode(TConfigNodeRegisterReq req) {
- final AddConfigNodeProcedure addConfigNodeProcedure =
- new AddConfigNodeProcedure(req.getConfigNodeLocation(), req.getVersionInfo());
- this.executor.submitProcedure(addConfigNodeProcedure);
- }
-
- /**
- * Generate a {@link RemoveConfigNodeProcedure}, and serially execute all the {@link
- * RemoveConfigNodeProcedure}s.
- */
- public void removeConfigNode(RemoveConfigNodePlan removeConfigNodePlan) {
- final RemoveConfigNodeProcedure removeConfigNodeProcedure =
- new RemoveConfigNodeProcedure(removeConfigNodePlan.getConfigNodeLocation());
- this.executor.submitProcedure(removeConfigNodeProcedure);
- LOGGER.info("Submit RemoveConfigNodeProcedure successfully: {}", removeConfigNodePlan);
- }
-
- /**
- * Generate {@link RemoveDataNodesProcedure}s, and serially execute all the {@link
- * RemoveDataNodesProcedure}s.
- */
- public boolean removeDataNode(RemoveDataNodePlan removeDataNodePlan) {
- Map<Integer, NodeStatus> nodeStatusMap = new HashMap<>();
- removeDataNodePlan
- .getDataNodeLocations()
- .forEach(
- datanode ->
- nodeStatusMap.put(
- datanode.getDataNodeId(),
- configManager.getLoadManager().getNodeStatus(datanode.getDataNodeId())));
- this.executor.submitProcedure(
- new RemoveDataNodesProcedure(removeDataNodePlan.getDataNodeLocations(), nodeStatusMap));
- LOGGER.info(
- "Submit RemoveDataNodesProcedure successfully, {}",
- removeDataNodePlan.getDataNodeLocations());
- return true;
- }
-
- public boolean removeAINode(RemoveAINodePlan removeAINodePlan) {
- this.executor.submitProcedure(new RemoveAINodeProcedure(removeAINodePlan.getAINodeLocation()));
- LOGGER.info(
- "Submit RemoveAINodeProcedure successfully, {}", removeAINodePlan.getAINodeLocation());
- return true;
- }
-
- public TSStatus checkRemoveDataNodes(List<TDataNodeLocation> dataNodeLocations) {
- // 1. Only one RemoveDataNodesProcedure is allowed in the cluster
- Optional<Procedure<ConfigNodeProcedureEnv>> anotherRemoveProcedure =
- getExecutor().getProcedures().values().stream()
- .filter(
- procedure -> {
- if (procedure instanceof RemoveDataNodesProcedure) {
- return !procedure.isFinished();
- }
- return false;
- })
- .findAny();
-
- String failMessage = null;
- if (anotherRemoveProcedure.isPresent()) {
- List<TDataNodeLocation> anotherRemoveDataNodes =
- ((RemoveDataNodesProcedure) anotherRemoveProcedure.get()).getRemovedDataNodes();
- failMessage =
- String.format(
- "Submit RemoveDataNodesProcedure failed, "
- + "because another RemoveDataNodesProcedure %s is already in processing. "
- + "IoTDB is able to have at most 1 RemoveDataNodesProcedure at the same time. "
- + "For further information, please search [pid%d] in log. ",
- anotherRemoveDataNodes, anotherRemoveProcedure.get().getProcId());
- }
-
- // 2. Check if the RemoveDataNodesProcedure conflicts with the RegionMigrateProcedure
- Set<TConsensusGroupId> removedDataNodesRegionSet =
- getEnv().getRemoveDataNodeHandler().getRemovedDataNodesRegionSet(dataNodeLocations);
- Optional<Procedure<ConfigNodeProcedureEnv>> conflictRegionMigrateProcedure =
- getExecutor().getProcedures().values().stream()
- .filter(
- procedure -> {
- if (procedure instanceof RegionMigrateProcedure) {
- RegionMigrateProcedure regionMigrateProcedure =
- (RegionMigrateProcedure) procedure;
- if (regionMigrateProcedure.isFinished()) {
- return false;
- }
- return removedDataNodesRegionSet.contains(regionMigrateProcedure.getRegionId())
- || dataNodeLocations.contains(regionMigrateProcedure.getDestDataNode());
- }
- return false;
- })
- .findAny();
- if (conflictRegionMigrateProcedure.isPresent()) {
- failMessage =
- String.format(
- "Submit RemoveDataNodesProcedure failed, "
- + "because another RegionMigrateProcedure %s is already in processing which conflicts with this RemoveDataNodesProcedure. "
- + "The RegionMigrateProcedure is migrating the region %s to the DataNode %s. "
- + "For further information, please search [pid%d] in log. ",
- conflictRegionMigrateProcedure.get().getProcId(),
- ((RegionMigrateProcedure) conflictRegionMigrateProcedure.get()).getRegionId(),
- ((RegionMigrateProcedure) conflictRegionMigrateProcedure.get()).getDestDataNode(),
- conflictRegionMigrateProcedure.get().getProcId());
- }
- // 3. Check if the RegionMigrateProcedure generated by RemoveDataNodesProcedure conflicts with
- // each other
- List<RegionMigrationPlan> regionMigrationPlans =
- getEnv().getRemoveDataNodeHandler().getRegionMigrationPlans(dataNodeLocations);
- removedDataNodesRegionSet.clear();
- for (RegionMigrationPlan regionMigrationPlan : regionMigrationPlans) {
- if (removedDataNodesRegionSet.contains(regionMigrationPlan.getRegionId())) {
- failMessage =
- String.format(
- "Submit RemoveDataNodesProcedure failed, "
- + "because the RegionMigrateProcedure generated by this RemoveDataNodesProcedure conflicts with each other. "
- + "Only one replica of the same consensus group is allowed to be migrated at the same time."
- + "The conflict region id is %s . ",
- regionMigrationPlan.getRegionId());
- break;
- }
- removedDataNodesRegionSet.add(regionMigrationPlan.getRegionId());
- }
-
- // 4. Check if there are any other unknown or readonly DataNodes in the consensus group that are
- // not the remove DataNodes
-
- for (TDataNodeLocation removeDataNode : dataNodeLocations) {
- Set<TDataNodeLocation> relatedDataNodes =
- getEnv().getRemoveDataNodeHandler().getRelatedDataNodeLocations(removeDataNode);
- relatedDataNodes.remove(removeDataNode);
-
- for (TDataNodeLocation relatedDataNode : relatedDataNodes) {
- NodeStatus nodeStatus =
- getConfigManager().getLoadManager().getNodeStatus(relatedDataNode.getDataNodeId());
- if (nodeStatus == NodeStatus.Unknown || nodeStatus == NodeStatus.ReadOnly) {
- failMessage =
- String.format(
- "Submit RemoveDataNodesProcedure failed, "
- + "because when there are other unknown or readonly nodes in the consensus group that are not remove nodes, "
- + "the remove operation cannot be performed for security reasons. "
- + "Please check the status of the node %s and ensure it is running.",
- relatedDataNode.getDataNodeId());
- }
- }
- }
-
- if (failMessage != null) {
- LOGGER.warn(failMessage);
- TSStatus failStatus = new TSStatus(TSStatusCode.REMOVE_DATANODE_ERROR.getStatusCode());
- failStatus.setMessage(failMessage);
- return failStatus;
- }
- return new TSStatus(TSStatusCode.SUCCESS_STATUS.getStatusCode());
- }
-
- // region region operation related check
-
- /**
- * Checks whether region migration is allowed.
- *
- * @param migrateRegionReq the migration request details
- * @param regionGroupId the ID of the consensus group for the region
- * @param originalDataNode the original DataNode location from which the region is being migrated
- * @param destDataNode the destination DataNode location to which the region is being migrated
- * @param coordinatorForAddPeer the DataNode location acting as the coordinator for adding a peer
- * @return the status of the migration request (TSStatus)
- */
- private TSStatus checkMigrateRegion(
- TMigrateRegionReq migrateRegionReq,
- TConsensusGroupId regionGroupId,
- TDataNodeLocation originalDataNode,
- TDataNodeLocation destDataNode,
- TDataNodeLocation coordinatorForAddPeer) {
- String failMessage;
- if ((failMessage =
- regionOperationCommonCheck(
- regionGroupId,
- destDataNode,
- Arrays.asList(
- new Pair<>("Original DataNode", originalDataNode),
- new Pair<>("Destination DataNode", destDataNode),
- new Pair<>("Coordinator for add peer", coordinatorForAddPeer)),
- migrateRegionReq.getModel()))
- != null) {
- // do nothing
- } else if (configManager
- .getPartitionManager()
- .getAllReplicaSets(originalDataNode.getDataNodeId())
- .stream()
- .noneMatch(replicaSet -> replicaSet.getRegionId().equals(regionGroupId))) {
- failMessage =
- String.format(
- "Submit RegionMigrateProcedure failed, because the original DataNode %s doesn't contain Region %s",
- migrateRegionReq.getFromId(), migrateRegionReq.getRegionId());
- } else if (configManager
- .getPartitionManager()
- .getAllReplicaSets(destDataNode.getDataNodeId())
- .stream()
- .anyMatch(replicaSet -> replicaSet.getRegionId().equals(regionGroupId))) {
- failMessage =
- String.format(
- "Submit RegionMigrateProcedure failed, because the target DataNode %s already contains Region %s",
- migrateRegionReq.getToId(), migrateRegionReq.getRegionId());
- }
-
- if (failMessage != null) {
- LOGGER.warn(failMessage);
- TSStatus failStatus = new TSStatus(TSStatusCode.MIGRATE_REGION_ERROR.getStatusCode());
- failStatus.setMessage(failMessage);
- return failStatus;
- }
- return new TSStatus(TSStatusCode.SUCCESS_STATUS.getStatusCode());
- }
-
- private TSStatus checkReconstructRegion(
- TReconstructRegionReq req,
- TConsensusGroupId regionId,
- TDataNodeLocation targetDataNode,
- TDataNodeLocation coordinator) {
- String failMessage =
- regionOperationCommonCheck(
- regionId,
- targetDataNode,
- Arrays.asList(
- new Pair<>("Target DataNode", targetDataNode),
- new Pair<>("Coordinator", coordinator)),
- req.getModel());
-
- ConfigNodeConfig conf = ConfigNodeDescriptor.getInstance().getConf();
- if (configManager
- .getPartitionManager()
- .getAllReplicaSetsMap(regionId.getType())
- .get(regionId)
- .getDataNodeLocationsSize()
- == 1) {
- failMessage = String.format("%s only has 1 replica, it cannot be reconstructed", regionId);
- } else if (configManager
- .getPartitionManager()
- .getAllReplicaSets(targetDataNode.getDataNodeId())
- .stream()
- .noneMatch(replicaSet -> replicaSet.getRegionId().equals(regionId))) {
- failMessage =
- String.format(
- "Submit ReconstructRegionProcedure failed, because the target DataNode %s doesn't contain Region %s",
- req.getDataNodeId(), regionId);
- }
-
- if (failMessage != null) {
- LOGGER.warn(failMessage);
- TSStatus failStatus = new TSStatus(TSStatusCode.RECONSTRUCT_REGION_ERROR.getStatusCode());
- failStatus.setMessage(failMessage);
- return failStatus;
- }
- return new TSStatus(TSStatusCode.SUCCESS_STATUS.getStatusCode());
- }
-
- private TSStatus checkExtendRegion(
- TExtendRegionReq req,
- TConsensusGroupId regionId,
- TDataNodeLocation targetDataNode,
- TDataNodeLocation coordinator) {
- String failMessage =
- regionOperationCommonCheck(
- regionId,
- targetDataNode,
- Arrays.asList(
- new Pair<>("Target DataNode", targetDataNode),
- new Pair<>("Coordinator", coordinator)),
- req.getModel());
- if (configManager
- .getPartitionManager()
- .getAllReplicaSets(targetDataNode.getDataNodeId())
- .stream()
- .anyMatch(replicaSet -> replicaSet.getRegionId().equals(regionId))) {
- failMessage =
- String.format(
- "Target DataNode %s already contains region %s",
- targetDataNode.getDataNodeId(), regionId);
- }
-
- if (failMessage != null) {
- LOGGER.warn(failMessage);
- TSStatus failStatus = new TSStatus(TSStatusCode.RECONSTRUCT_REGION_ERROR.getStatusCode());
- failStatus.setMessage(failMessage);
- return failStatus;
- }
- return new TSStatus(TSStatusCode.SUCCESS_STATUS.getStatusCode());
- }
-
- private TSStatus checkRemoveRegion(
- TRemoveRegionReq req,
- TConsensusGroupId regionId,
- @Nullable TDataNodeLocation targetDataNode,
- TDataNodeLocation coordinator) {
- String failMessage =
- regionOperationCommonCheck(
- regionId,
- targetDataNode,
- Arrays.asList(new Pair<>("Coordinator", coordinator)),
- req.getModel());
-
- if (configManager
- .getPartitionManager()
- .getAllReplicaSetsMap(regionId.getType())
- .get(regionId)
- .getDataNodeLocationsSize()
- == 1) {
- failMessage = String.format("%s only has 1 replica, it cannot be removed", regionId);
- } else if (targetDataNode != null
- && configManager
- .getPartitionManager()
- .getAllReplicaSets(targetDataNode.getDataNodeId())
- .stream()
- .noneMatch(replicaSet -> replicaSet.getRegionId().equals(regionId))) {
- failMessage =
- String.format(
- "Target DataNode %s doesn't contain Region %s", req.getDataNodeId(), regionId);
- }
-
- if (failMessage != null) {
- LOGGER.warn(failMessage);
- TSStatus failStatus = new TSStatus(TSStatusCode.REMOVE_REGION_PEER_ERROR.getStatusCode());
- failStatus.setMessage(failMessage);
- return failStatus;
- }
- return new TSStatus(TSStatusCode.SUCCESS_STATUS.getStatusCode());
- }
-
- /**
- * The common checks of all region operations, include migration, reconstruction, extension,
- * removing
- *
- * @param regionId region group id, also called consensus group id
- * @param targetDataNode DataNode should in Running status
- * @param relatedDataNodes Pair<Identity, Node Location>
- * @return The reason if check failed, or null if check pass
- */
- private String regionOperationCommonCheck(
- TConsensusGroupId regionId,
- TDataNodeLocation targetDataNode,
- List<Pair<String, TDataNodeLocation>> relatedDataNodes,
- Model model) {
- String failMessage;
- ConfigNodeConfig conf = ConfigNodeDescriptor.getInstance().getConf();
-
- if (TConsensusGroupType.DataRegion == regionId.getType()
- && ConsensusFactory.SIMPLE_CONSENSUS.equals(conf.getDataRegionConsensusProtocolClass())) {
- failMessage = "SimpleConsensus not supports region operation.";
- } else if (TConsensusGroupType.SchemaRegion == regionId.getType()
- && ConsensusFactory.SIMPLE_CONSENSUS.equals(conf.getSchemaRegionConsensusProtocolClass())) {
- failMessage = "SimpleConsensus not supports region operation.";
- } else if ((failMessage = checkRegionOperationDuplication(regionId)) != null) {
- // need to do nothing more
- } else if (relatedDataNodes.stream().anyMatch(pair -> pair.getRight() == null)) {
- Pair<String, TDataNodeLocation> nullPair =
- relatedDataNodes.stream().filter(pair -> pair.getRight() == null).findAny().get();
- failMessage = String.format("Cannot find %s", nullPair.getLeft());
- } else if (targetDataNode != null
- && !configManager.getNodeManager().filterDataNodeThroughStatus(NodeStatus.Running).stream()
- .map(TDataNodeConfiguration::getLocation)
- .map(TDataNodeLocation::getDataNodeId)
- .collect(Collectors.toSet())
- .contains(targetDataNode.getDataNodeId())) {
- // Here we only check Running DataNode to implement migration, because removing nodes may not
- // exist when add peer is performing
- failMessage =
- String.format(
- "Target DataNode %s is not in Running status.", targetDataNode.getDataNodeId());
- } else if ((failMessage = checkRegionOperationWithRemoveDataNode(regionId, targetDataNode))
- != null) {
- // need to do nothing more
- } else if ((failMessage = checkRegionOperationModelCorrectness(regionId, model)) != null) {
- // need to do nothing more
- }
-
- return failMessage;
- }
-
- private String checkRegionOperationWithRemoveDataNode(
- TConsensusGroupId regionId, TDataNodeLocation targetDataNode) {
- Optional<Procedure<ConfigNodeProcedureEnv>> conflictRemoveDataNodesProcedure =
- getExecutor().getProcedures().values().stream()
- .filter(
- procedure -> {
- if (procedure instanceof RemoveDataNodesProcedure) {
- return !procedure.isFinished();
- }
- return false;
- })
- .findAny();
-
- if (conflictRemoveDataNodesProcedure.isPresent()) {
- RemoveDataNodeHandler removeDataNodeHandler = env.getRemoveDataNodeHandler();
- List<TDataNodeLocation> removedDataNodes =
- ((RemoveDataNodesProcedure) conflictRemoveDataNodesProcedure.get()).getRemovedDataNodes();
- Set<TConsensusGroupId> removedDataNodesRegionSet =
- removeDataNodeHandler.getRemovedDataNodesRegionSet(removedDataNodes);
- if (removedDataNodesRegionSet.contains(regionId)) {
- return String.format(
- "Another RemoveDataNodesProcedure %s is already in processing which conflicts with this procedure. "
- + "The RemoveDataNodesProcedure is removing the DataNodes %s which contains the region %s. "
- + "For further information, please search [pid%d] in log. ",
- conflictRemoveDataNodesProcedure.get().getProcId(),
- removedDataNodes,
- regionId,
- conflictRemoveDataNodesProcedure.get().getProcId());
- } else if (removedDataNodes.contains(targetDataNode)) {
- return String.format(
- "Another RemoveDataNodesProcedure %s is already in processing which conflicts with this procedure. "
- + "The RemoveDataNodesProcedure is removing the target DataNode %s. "
- + "For further information, please search [pid%d] in log. ",
- conflictRemoveDataNodesProcedure.get().getProcId(),
- targetDataNode,
- conflictRemoveDataNodesProcedure.get().getProcId());
- }
- }
- return null;
- }
-
- private String checkRegionOperationDuplication(TConsensusGroupId regionId) {
- List<? extends RegionOperationProcedure<?>> otherRegionMemberChangeProcedures =
- getRegionOperationProcedures()
- .filter(
- regionMemberChangeProcedure ->
- regionId.equals(regionMemberChangeProcedure.getRegionId()))
- .collect(Collectors.toList());
- if (!otherRegionMemberChangeProcedures.isEmpty()) {
- return String.format(
- "%s has some other region operation procedures in progress, their procedure id is: %s",
- regionId, otherRegionMemberChangeProcedures);
- }
- return null;
- }
-
- public List<TConsensusGroupId> getRegionOperationConsensusIds() {
- return getRegionOperationProcedures()
- .map(RegionOperationProcedure::getRegionId)
- .distinct()
- .collect(Collectors.toList());
- }
-
- private Stream<RegionOperationProcedure<?>> getRegionOperationProcedures() {
- return getExecutor().getProcedures().values().stream()
- .filter(procedure -> !procedure.isFinished())
- .filter(procedure -> procedure instanceof RegionOperationProcedure)
- .map(procedure -> (RegionOperationProcedure<?>) procedure);
- }
-
- private String checkRegionOperationModelCorrectness(TConsensusGroupId regionId, Model model) {
- String databaseName = configManager.getPartitionManager().getRegionDatabase(regionId);
+1/*
+1 * Licensed to the Apache Software Foundation (ASF) under one
+1 * or more contributor license agreements. See the NOTICE file
+1 * distributed with this work for additional information
+1 * regarding copyright ownership. The ASF licenses this file
+1 * to you under the Apache License, Version 2.0 (the
+1 * "License"); you may not use this file except in compliance
+1 * with the License. You may obtain a copy of the License at
+1 *
+1 * http://www.apache.org/licenses/LICENSE-2.0
+1 *
+1 * Unless required by applicable law or agreed to in writing,
+1 * software distributed under the License is distributed on an
+1 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+1 * KIND, either express or implied. See the License for the
+1 * specific language governing permissions and limitations
+1 * under the License.
+1 */
+1
+1package org.apache.iotdb.confignode.manager;
+1
+1import org.apache.iotdb.common.rpc.thrift.Model;
+1import org.apache.iotdb.common.rpc.thrift.TConsensusGroupId;
+1import org.apache.iotdb.common.rpc.thrift.TConsensusGroupType;
+1import org.apache.iotdb.common.rpc.thrift.TDataNodeConfiguration;
+1import org.apache.iotdb.common.rpc.thrift.TDataNodeLocation;
+1import org.apache.iotdb.common.rpc.thrift.TEndPoint;
+1import org.apache.iotdb.common.rpc.thrift.TSStatus;
+1import org.apache.iotdb.commons.cluster.NodeStatus;
+1import org.apache.iotdb.commons.conf.CommonConfig;
+1import org.apache.iotdb.commons.conf.CommonDescriptor;
+1import org.apache.iotdb.commons.conf.IoTDBConstant;
+1import org.apache.iotdb.commons.exception.IoTDBException;
+1import org.apache.iotdb.commons.path.PartialPath;
+1import org.apache.iotdb.commons.path.PathDeserializeUtil;
+1import org.apache.iotdb.commons.path.PathPatternTree;
+1import org.apache.iotdb.commons.pipe.agent.plugin.meta.PipePluginMeta;
+1import org.apache.iotdb.commons.schema.table.TsTable;
+1import org.apache.iotdb.commons.schema.table.column.TsTableColumnSchemaUtil;
+1import org.apache.iotdb.commons.schema.view.viewExpression.ViewExpression;
+1import org.apache.iotdb.commons.service.metric.MetricService;
+1import org.apache.iotdb.commons.trigger.TriggerInformation;
+1import org.apache.iotdb.commons.utils.StatusUtils;
+1import org.apache.iotdb.commons.utils.TestOnly;
+1import org.apache.iotdb.confignode.conf.ConfigNodeConfig;
+1import org.apache.iotdb.confignode.conf.ConfigNodeDescriptor;
+1import org.apache.iotdb.confignode.consensus.request.write.ainode.RemoveAINodePlan;
+1import org.apache.iotdb.confignode.consensus.request.write.auth.AuthorPlan;
+1import org.apache.iotdb.confignode.consensus.request.write.confignode.RemoveConfigNodePlan;
+1import org.apache.iotdb.confignode.consensus.request.write.database.SetTTLPlan;
+1import org.apache.iotdb.confignode.consensus.request.write.datanode.RemoveDataNodePlan;
+1import org.apache.iotdb.confignode.consensus.request.write.procedure.UpdateProcedurePlan;
+1import org.apache.iotdb.confignode.consensus.request.write.region.CreateRegionGroupsPlan;
+1import org.apache.iotdb.confignode.manager.partition.PartitionManager;
+1import org.apache.iotdb.confignode.persistence.ProcedureInfo;
+1import org.apache.iotdb.confignode.procedure.PartitionTableAutoCleaner;
+1import org.apache.iotdb.confignode.procedure.Procedure;
+1import org.apache.iotdb.confignode.procedure.ProcedureExecutor;
+1import org.apache.iotdb.confignode.procedure.ProcedureMetrics;
+1import org.apache.iotdb.confignode.procedure.env.ConfigNodeProcedureEnv;
+1import org.apache.iotdb.confignode.procedure.env.RegionMaintainHandler;
+1import org.apache.iotdb.confignode.procedure.env.RemoveDataNodeHandler;
+1import org.apache.iotdb.confignode.procedure.impl.cq.CreateCQProcedure;
+1import org.apache.iotdb.confignode.procedure.impl.model.CreateModelProcedure;
+1import org.apache.iotdb.confignode.procedure.impl.model.DropModelProcedure;
+1import org.apache.iotdb.confignode.procedure.impl.node.AddConfigNodeProcedure;
+1import org.apache.iotdb.confignode.procedure.impl.node.RemoveAINodeProcedure;
+1import org.apache.iotdb.confignode.procedure.impl.node.RemoveConfigNodeProcedure;
+1import org.apache.iotdb.confignode.procedure.impl.node.RemoveDataNodesProcedure;
+1import org.apache.iotdb.confignode.procedure.impl.pipe.plugin.CreatePipePluginProcedure;
+1import org.apache.iotdb.confignode.procedure.impl.pipe.plugin.DropPipePluginProcedure;
+1import org.apache.iotdb.confignode.procedure.impl.pipe.runtime.PipeHandleLeaderChangeProcedure;
+1import org.apache.iotdb.confignode.procedure.impl.pipe.runtime.PipeHandleMetaChangeProcedure;
+1import org.apache.iotdb.confignode.procedure.impl.pipe.runtime.PipeMetaSyncProcedure;
+1import org.apache.iotdb.confignode.procedure.impl.pipe.task.AlterPipeProcedureV2;
+1import org.apache.iotdb.confignode.procedure.impl.pipe.task.CreatePipeProcedureV2;
+1import org.apache.iotdb.confignode.procedure.impl.pipe.task.DropPipeProcedureV2;
+1import org.apache.iotdb.confignode.procedure.impl.pipe.task.StartPipeProcedureV2;
+1import org.apache.iotdb.confignode.procedure.impl.pipe.task.StopPipeProcedureV2;
+1import org.apache.iotdb.confignode.procedure.impl.region.AddRegionPeerProcedure;
+1import org.apache.iotdb.confignode.procedure.impl.region.CreateRegionGroupsProcedure;
+1import org.apache.iotdb.confignode.procedure.impl.region.ReconstructRegionProcedure;
+1import org.apache.iotdb.confignode.procedure.impl.region.RegionMigrateProcedure;
+1import org.apache.iotdb.confignode.procedure.impl.region.RegionMigrationPlan;
+1import org.apache.iotdb.confignode.procedure.impl.region.RegionOperationProcedure;
+1import org.apache.iotdb.confignode.procedure.impl.region.RemoveRegionPeerProcedure;
+1import org.apache.iotdb.confignode.procedure.impl.schema.AlterLogicalViewProcedure;
+1import org.apache.iotdb.confignode.procedure.impl.schema.DeactivateTemplateProcedure;
+1import org.apache.iotdb.confignode.procedure.impl.schema.DeleteDatabaseProcedure;
+1import org.apache.iotdb.confignode.procedure.impl.schema.DeleteLogicalViewProcedure;
+1import org.apache.iotdb.confignode.procedure.impl.schema.DeleteTimeSeriesProcedure;
+1import org.apache.iotdb.confignode.procedure.impl.schema.SetTTLProcedure;
+1import org.apache.iotdb.confignode.procedure.impl.schema.SetTemplateProcedure;
+1import org.apache.iotdb.confignode.procedure.impl.schema.UnsetTemplateProcedure;
+1import org.apache.iotdb.confignode.procedure.impl.schema.table.AbstractAlterOrDropTableProcedure;
+1import org.apache.iotdb.confignode.procedure.impl.schema.table.AddTableColumnProcedure;
+1import org.apache.iotdb.confignode.procedure.impl.schema.table.CreateTableProcedure;
+1import org.apache.iotdb.confignode.procedure.impl.schema.table.DeleteDevicesProcedure;
+1import org.apache.iotdb.confignode.procedure.impl.schema.table.DropTableColumnProcedure;
+1import org.apache.iotdb.confignode.procedure.impl.schema.table.DropTableProcedure;
+1import org.apache.iotdb.confignode.procedure.impl.schema.table.RenameTableColumnProcedure;
+1import org.apache.iotdb.confignode.procedure.impl.schema.table.RenameTableProcedure;
+1import org.apache.iotdb.confignode.procedure.impl.schema.table.SetTablePropertiesProcedure;
+1import org.apache.iotdb.confignode.procedure.impl.schema.table.view.AddViewColumnProcedure;
+1import org.apache.iotdb.confignode.procedure.impl.schema.table.view.CreateTableViewProcedure;
+1import org.apache.iotdb.confignode.procedure.impl.schema.table.view.DropViewColumnProcedure;
+1import org.apache.iotdb.confignode.procedure.impl.schema.table.view.DropViewProcedure;
+1import org.apache.iotdb.confignode.procedure.impl.schema.table.view.RenameViewColumnProcedure;
+1import org.apache.iotdb.confignode.procedure.impl.schema.table.view.RenameViewProcedure;
+1import org.apache.iotdb.confignode.procedure.impl.schema.table.view.SetViewPropertiesProcedure;
+1import org.apache.iotdb.confignode.procedure.impl.subscription.consumer.CreateConsumerProcedure;
+1import org.apache.iotdb.confignode.procedure.impl.subscription.consumer.DropConsumerProcedure;
+1import org.apache.iotdb.confignode.procedure.impl.subscription.consumer.runtime.ConsumerGroupMetaSyncProcedure;
+1import org.apache.iotdb.confignode.procedure.impl.subscription.subscription.CreateSubscriptionProcedure;
+1import org.apache.iotdb.confignode.procedure.impl.subscription.subscription.DropSubscriptionProcedure;
+1import org.apache.iotdb.confignode.procedure.impl.subscription.topic.CreateTopicProcedure;
+1import org.apache.iotdb.confignode.procedure.impl.subscription.topic.DropTopicProcedure;
+1import org.apache.iotdb.confignode.procedure.impl.subscription.topic.runtime.TopicMetaSyncProcedure;
+1import org.apache.iotdb.confignode.procedure.impl.sync.AuthOperationProcedure;
+1import org.apache.iotdb.confignode.procedure.impl.testonly.AddNeverFinishSubProcedureProcedure;
+1import org.apache.iotdb.confignode.procedure.impl.testonly.CreateManyDatabasesProcedure;
+1import org.apache.iotdb.confignode.procedure.impl.trigger.CreateTriggerProcedure;
+1import org.apache.iotdb.confignode.procedure.impl.trigger.DropTriggerProcedure;
+1import org.apache.iotdb.confignode.procedure.scheduler.ProcedureScheduler;
+1import org.apache.iotdb.confignode.procedure.scheduler.SimpleProcedureScheduler;
+1import org.apache.iotdb.confignode.procedure.store.ConfigProcedureStore;
+1import org.apache.iotdb.confignode.procedure.store.IProcedureStore;
+1import org.apache.iotdb.confignode.procedure.store.ProcedureFactory;
+1import org.apache.iotdb.confignode.procedure.store.ProcedureType;
+1import org.apache.iotdb.confignode.rpc.thrift.TAlterLogicalViewReq;
+1import org.apache.iotdb.confignode.rpc.thrift.TAlterOrDropTableReq;
+1import org.apache.iotdb.confignode.rpc.thrift.TAlterPipeReq;
+1import org.apache.iotdb.confignode.rpc.thrift.TCloseConsumerReq;
+1import org.apache.iotdb.confignode.rpc.thrift.TConfigNodeRegisterReq;
+1import org.apache.iotdb.confignode.rpc.thrift.TCreateCQReq;
+1import org.apache.iotdb.confignode.rpc.thrift.TCreateConsumerReq;
+1import org.apache.iotdb.confignode.rpc.thrift.TCreatePipeReq;
+1import org.apache.iotdb.confignode.rpc.thrift.TCreateTopicReq;
+1import org.apache.iotdb.confignode.rpc.thrift.TDatabaseSchema;
+1import org.apache.iotdb.confignode.rpc.thrift.TDeleteLogicalViewReq;
+1import org.apache.iotdb.confignode.rpc.thrift.TDeleteTableDeviceReq;
+1import org.apache.iotdb.confignode.rpc.thrift.TDeleteTableDeviceResp;
+1import org.apache.iotdb.confignode.rpc.thrift.TDropPipePluginReq;
+1import org.apache.iotdb.confignode.rpc.thrift.TExtendRegionReq;
+1import org.apache.iotdb.confignode.rpc.thrift.TMigrateRegionReq;
+1import org.apache.iotdb.confignode.rpc.thrift.TReconstructRegionReq;
+1import org.apache.iotdb.confignode.rpc.thrift.TRemoveRegionReq;
+1import org.apache.iotdb.confignode.rpc.thrift.TSubscribeReq;
+1import org.apache.iotdb.confignode.rpc.thrift.TUnsubscribeReq;
+1import org.apache.iotdb.consensus.ConsensusFactory;
+1import org.apache.iotdb.db.exception.BatchProcessException;
+1import org.apache.iotdb.db.schemaengine.template.Template;
+1import org.apache.iotdb.db.utils.constant.SqlConstant;
+1import org.apache.iotdb.rpc.RpcUtils;
+1import org.apache.iotdb.rpc.TSStatusCode;
+1
+1import org.apache.ratis.util.AutoCloseableLock;
+1import org.apache.tsfile.utils.Binary;
+1import org.apache.tsfile.utils.Pair;
+1import org.apache.tsfile.utils.ReadWriteIOUtils;
+1import org.slf4j.Logger;
+1import org.slf4j.LoggerFactory;
+1
+1import javax.annotation.Nonnull;
+1import javax.annotation.Nullable;
+1
+1import java.io.IOException;
+1import java.nio.ByteBuffer;
+1import java.util.ArrayList;
+1import java.util.Arrays;
+1import java.util.HashMap;
+1import java.util.List;
+1import java.util.Map;
+1import java.util.Objects;
+1import java.util.Optional;
+1import java.util.Set;
+1import java.util.concurrent.ScheduledExecutorService;
+1import java.util.concurrent.locks.ReentrantLock;
+1import java.util.function.BiFunction;
+1import java.util.stream.Collectors;
+1import java.util.stream.Stream;
+1
+1public class ProcedureManager {
+1 private static final Logger LOGGER = LoggerFactory.getLogger(ProcedureManager.class);
+1
+1 private static final ConfigNodeConfig CONFIG_NODE_CONFIG =
+1 ConfigNodeDescriptor.getInstance().getConf();
+1 private static final CommonConfig COMMON_CONFIG = CommonDescriptor.getInstance().getConfig();
+1
+1 public static final long PROCEDURE_WAIT_TIME_OUT = COMMON_CONFIG.getDnConnectionTimeoutInMS();
+1 public static final int PROCEDURE_WAIT_RETRY_TIMEOUT = 10;
+1 private static final String PROCEDURE_TIMEOUT_MESSAGE =
+1 "Timed out to wait for procedure return. The procedure is still running.";
+1
+1 private final ConfigManager configManager;
+1 private ProcedureExecutor<ConfigNodeProcedureEnv> executor;
+1 private ProcedureScheduler scheduler;
+1 private IProcedureStore store;
+1 private ConfigNodeProcedureEnv env;
+1
+1 private final long planSizeLimit;
+1 private ProcedureMetrics procedureMetrics;
+1
+1 private final PartitionTableAutoCleaner partitionTableCleaner;
+1
+1 private final ReentrantLock tableLock = new ReentrantLock();
+1
+1 public ProcedureManager(ConfigManager configManager, ProcedureInfo procedureInfo) {
+1 this.configManager = configManager;
+1 this.scheduler = new SimpleProcedureScheduler();
+1 this.store = new ConfigProcedureStore(configManager, procedureInfo);
+1 this.env = new ConfigNodeProcedureEnv(configManager, scheduler);
+1 this.executor = new ProcedureExecutor<>(env, store, scheduler);
+1 this.planSizeLimit =
+1 ConfigNodeDescriptor.getInstance()
+1 .getConf()
+1 .getConfigNodeRatisConsensusLogAppenderBufferSize()
+1 - IoTDBConstant.RAFT_LOG_BASIC_SIZE;
+1 this.procedureMetrics = new ProcedureMetrics(this);
+1 this.partitionTableCleaner = new PartitionTableAutoCleaner<>(configManager);
+1 }
+1
+1 public void startExecutor() {
+1 if (!executor.isRunning()) {
+1 executor.init(CONFIG_NODE_CONFIG.getProcedureCoreWorkerThreadsCount());
+1 executor.startWorkers();
+1 executor.startCompletedCleaner(
+1 CONFIG_NODE_CONFIG.getProcedureCompletedCleanInterval(),
+1 CONFIG_NODE_CONFIG.getProcedureCompletedEvictTTL());
+1 executor.addInternalProcedure(partitionTableCleaner);
+1 store.start();
+1 LOGGER.info("ProcedureManager is started successfully.");
+1 }
+1 }
+1
+1 public void stopExecutor() {
+1 if (executor.isRunning()) {
+1 executor.stop();
+1 if (!executor.isRunning()) {
+1 executor.join();
+1 store.stop();
+1 LOGGER.info("ProcedureManager is stopped successfully.");
+1 }
+1 executor.removeInternalProcedure(partitionTableCleaner);
+1 }
+1 }
+1
+1 @TestOnly
+1 public TSStatus createManyDatabases() {
+1 this.executor.submitProcedure(new CreateManyDatabasesProcedure());
+1 return StatusUtils.OK;
+1 }
+1
+1 @TestOnly
+1 public TSStatus testSubProcedure() {
+1 this.executor.submitProcedure(new AddNeverFinishSubProcedureProcedure());
+1 return StatusUtils.OK;
+1 }
+1
+1 public TSStatus deleteDatabases(
+1 final List<TDatabaseSchema> deleteSgSchemaList, final boolean isGeneratedByPipe) {
+1 final List<DeleteDatabaseProcedure> procedures = new ArrayList<>();
+1 final long startCheckTimeForProcedures = System.currentTimeMillis();
+1 for (final TDatabaseSchema databaseSchema : deleteSgSchemaList) {
+1 final String database = databaseSchema.getName();
+1 boolean hasOverlappedTask = false;
+1 synchronized (this) {
+1 while (executor.isRunning()
+1 && System.currentTimeMillis() - startCheckTimeForProcedures < PROCEDURE_WAIT_TIME_OUT) {
+1 final Pair<Long, Boolean> procedureIdDuplicatePair =
+1 checkDuplicateTableTask(
+1 database, null, null, null, null, ProcedureType.DELETE_DATABASE_PROCEDURE);
+1 hasOverlappedTask = procedureIdDuplicatePair.getRight();
+1
+1 if (Boolean.FALSE.equals(procedureIdDuplicatePair.getRight())) {
+1 DeleteDatabaseProcedure procedure =
+1 new DeleteDatabaseProcedure(databaseSchema, isGeneratedByPipe);
+1 this.executor.submitProcedure(procedure);
+1 procedures.add(procedure);
+1 break;
+1 }
+1 try {
+1 wait(PROCEDURE_WAIT_RETRY_TIMEOUT);
+1 } catch (final InterruptedException e) {
+1 Thread.currentThread().interrupt();
+1 }
+1 }
+1 if (hasOverlappedTask) {
+1 return RpcUtils.getStatus(
+1 TSStatusCode.OVERLAP_WITH_EXISTING_TASK,
+1 String.format(
+1 "Some other task is operating table under the database %s, please retry after the procedure finishes.",
+1 database));
+1 }
+1 }
+1 }
+1 List<TSStatus> results = new ArrayList<>(procedures.size());
+1 procedures.forEach(procedure -> results.add(waitingProcedureFinished(procedure)));
+1 // Clear the previously deleted regions
+1 final PartitionManager partitionManager = getConfigManager().getPartitionManager();
+1 partitionManager.getRegionMaintainer().submit(partitionManager::maintainRegionReplicas);
+1 if (results.stream()
+1 .allMatch(result -> result.getCode() == TSStatusCode.SUCCESS_STATUS.getStatusCode())) {
+1 return StatusUtils.OK;
+1 } else {
+1 return RpcUtils.getStatus(results);
+1 }
+1 }
+1
+1 public TSStatus deleteTimeSeries(
+1 String queryId, PathPatternTree patternTree, boolean isGeneratedByPipe) {
+1 DeleteTimeSeriesProcedure procedure = null;
+1 synchronized (this) {
+1 boolean hasOverlappedTask = false;
+1 ProcedureType type;
+1 DeleteTimeSeriesProcedure deleteTimeSeriesProcedure;
+1 for (Procedure<?> runningProcedure : executor.getProcedures().values()) {
+1 type = ProcedureFactory.getProcedureType(runningProcedure);
+1 if (type == null || !type.equals(ProcedureType.DELETE_TIMESERIES_PROCEDURE)) {
+1 continue;
+1 }
+1 deleteTimeSeriesProcedure = ((DeleteTimeSeriesProcedure) runningProcedure);
+1 if (queryId.equals(deleteTimeSeriesProcedure.getQueryId())) {
+1 procedure = deleteTimeSeriesProcedure;
+1 break;
+1 }
+1 if (patternTree.isOverlapWith(deleteTimeSeriesProcedure.getPatternTree())) {
+1 hasOverlappedTask = true;
+1 break;
+1 }
+1 }
+1
+1 if (procedure == null) {
+1 if (hasOverlappedTask) {
+1 return RpcUtils.getStatus(
+1 TSStatusCode.OVERLAP_WITH_EXISTING_TASK,
+1 "Some other task is deleting some target timeseries.");
+1 }
+1 procedure = new DeleteTimeSeriesProcedure(queryId, patternTree, isGeneratedByPipe);
+1 this.executor.submitProcedure(procedure);
+1 }
+1 }
+1 return waitingProcedureFinished(procedure);
+1 }
+1
+1 public TSStatus deleteLogicalView(TDeleteLogicalViewReq req) {
+1 String queryId = req.getQueryId();
+1 PathPatternTree patternTree =
+1 PathPatternTree.deserialize(ByteBuffer.wrap(req.getPathPatternTree()));
+1 DeleteLogicalViewProcedure procedure = null;
+1 synchronized (this) {
+1 boolean hasOverlappedTask = false;
+1 ProcedureType type;
+1 DeleteLogicalViewProcedure deleteLogicalViewProcedure;
+1 for (Procedure<?> runningProcedure : executor.getProcedures().values()) {
+1 type = ProcedureFactory.getProcedureType(runningProcedure);
+1 if (type == null || !type.equals(ProcedureType.DELETE_LOGICAL_VIEW_PROCEDURE)) {
+1 continue;
+1 }
+1 deleteLogicalViewProcedure = ((DeleteLogicalViewProcedure) runningProcedure);
+1 if (queryId.equals(deleteLogicalViewProcedure.getQueryId())) {
+1 procedure = deleteLogicalViewProcedure;
+1 break;
+1 }
+1 if (patternTree.isOverlapWith(deleteLogicalViewProcedure.getPatternTree())) {
+1 hasOverlappedTask = true;
+1 break;
+1 }
+1 }
+1
+1 if (procedure == null) {
+1 if (hasOverlappedTask) {
+1 return RpcUtils.getStatus(
+1 TSStatusCode.OVERLAP_WITH_EXISTING_TASK,
+1 "Some other task is deleting some target views.");
+1 }
+1 procedure =
+1 new DeleteLogicalViewProcedure(
+1 queryId, patternTree, req.isSetIsGeneratedByPipe() && req.isIsGeneratedByPipe());
+1 this.executor.submitProcedure(procedure);
+1 }
+1 }
+1 return waitingProcedureFinished(procedure);
+1 }
+1
+1 public TSStatus alterLogicalView(final TAlterLogicalViewReq req) {
+1 final String queryId = req.getQueryId();
+1 final ByteBuffer byteBuffer = ByteBuffer.wrap(req.getViewBinary());
+1 final Map<PartialPath, ViewExpression> viewPathToSourceMap = new HashMap<>();
+1 final int size = byteBuffer.getInt();
+1 PartialPath path;
+1 ViewExpression viewExpression;
+1 for (int i = 0; i < size; i++) {
+1 path = (PartialPath) PathDeserializeUtil.deserialize(byteBuffer);
+1 viewExpression = ViewExpression.deserialize(byteBuffer);
+1 viewPathToSourceMap.put(path, viewExpression);
+1 }
+1
+1 AlterLogicalViewProcedure procedure = null;
+1 synchronized (this) {
+1 ProcedureType type;
+1 AlterLogicalViewProcedure alterLogicalViewProcedure;
+1 for (Procedure<?> runningProcedure : executor.getProcedures().values()) {
+1 type = ProcedureFactory.getProcedureType(runningProcedure);
+1 if (type == null || !type.equals(ProcedureType.ALTER_LOGICAL_VIEW_PROCEDURE)) {
+1 continue;
+1 }
+1 alterLogicalViewProcedure = ((AlterLogicalViewProcedure) runningProcedure);
+1 if (queryId.equals(alterLogicalViewProcedure.getQueryId())) {
+1 procedure = alterLogicalViewProcedure;
+1 break;
+1 }
+1 }
+1
+1 if (procedure == null) {
+1 procedure =
+1 new AlterLogicalViewProcedure(
+1 queryId,
+1 viewPathToSourceMap,
+1 req.isSetIsGeneratedByPipe() && req.isIsGeneratedByPipe());
+1 this.executor.submitProcedure(procedure);
+1 }
+1 }
+1 return waitingProcedureFinished(procedure);
+1 }
+1
+1 public TSStatus setSchemaTemplate(
+1 String queryId, String templateName, String templateSetPath, boolean isGeneratedByPipe) {
+1 SetTemplateProcedure procedure = null;
+1 synchronized (this) {
+1 boolean hasOverlappedTask = false;
+1 ProcedureType type;
+1 SetTemplateProcedure setTemplateProcedure;
+1 for (Procedure<?> runningProcedure : executor.getProcedures().values()) {
+1 type = ProcedureFactory.getProcedureType(runningProcedure);
+1 if (type == null || !type.equals(ProcedureType.SET_TEMPLATE_PROCEDURE)) {
+1 continue;
+1 }
+1 setTemplateProcedure = (SetTemplateProcedure) runningProcedure;
+1 if (queryId.equals(setTemplateProcedure.getQueryId())) {
+1 procedure = setTemplateProcedure;
+1 break;
+1 }
+1 if (templateSetPath.equals(setTemplateProcedure.getTemplateSetPath())) {
+1 hasOverlappedTask = true;
+1 break;
+1 }
+1 }
+1
+1 if (procedure == null) {
+1 if (hasOverlappedTask) {
+1 return RpcUtils.getStatus(
+1 TSStatusCode.OVERLAP_WITH_EXISTING_TASK,
+1 "Some other task is setting template on target path.");
+1 }
+1 procedure =
+1 new SetTemplateProcedure(queryId, templateName, templateSetPath, isGeneratedByPipe);
+1 this.executor.submitProcedure(procedure);
+1 }
+1 }
+1 return waitingProcedureFinished(procedure);
+1 }
+1
+1 public TSStatus deactivateTemplate(
+1 String queryId, Map<PartialPath, List<Template>> templateSetInfo, boolean isGeneratedByPipe) {
+1 DeactivateTemplateProcedure procedure = null;
+1 synchronized (this) {
+1 boolean hasOverlappedTask = false;
+1 ProcedureType type;
+1 DeactivateTemplateProcedure deactivateTemplateProcedure;
+1 for (Procedure<?> runningProcedure : executor.getProcedures().values()) {
+1 type = ProcedureFactory.getProcedureType(runningProcedure);
+1 if (type == null || !type.equals(ProcedureType.DEACTIVATE_TEMPLATE_PROCEDURE)) {
+1 continue;
+1 }
+1 deactivateTemplateProcedure = (DeactivateTemplateProcedure) runningProcedure;
+1 if (queryId.equals(deactivateTemplateProcedure.getQueryId())) {
+1 procedure = deactivateTemplateProcedure;
+1 break;
+1 }
+1 for (PartialPath pattern : templateSetInfo.keySet()) {
+1 for (PartialPath existingPattern :
+1 deactivateTemplateProcedure.getTemplateSetInfo().keySet()) {
+1 if (pattern.overlapWith(existingPattern)) {
+1 hasOverlappedTask = true;
+1 break;
+1 }
+1 }
+1 if (hasOverlappedTask) {
+1 break;
+1 }
+1 }
+1 if (hasOverlappedTask) {
+1 break;
+1 }
+1 }
+1
+1 if (procedure == null) {
+1 if (hasOverlappedTask) {
+1 return RpcUtils.getStatus(
+1 TSStatusCode.OVERLAP_WITH_EXISTING_TASK,
+1 "Some other task is deactivating some target template from target path.");
+1 }
+1 procedure = new DeactivateTemplateProcedure(queryId, templateSetInfo, isGeneratedByPipe);
+1 this.executor.submitProcedure(procedure);
+1 }
+1 }
+1 return waitingProcedureFinished(procedure);
+1 }
+1
+1 public TSStatus unsetSchemaTemplate(
+1 String queryId, Template template, PartialPath path, boolean isGeneratedByPipe) {
+1 UnsetTemplateProcedure procedure = null;
+1 synchronized (this) {
+1 boolean hasOverlappedTask = false;
+1 ProcedureType type;
+1 UnsetTemplateProcedure unsetTemplateProcedure;
+1 for (Procedure<?> runningProcedure : executor.getProcedures().values()) {
+1 type = ProcedureFactory.getProcedureType(runningProcedure);
+1 if (type == null || !type.equals(ProcedureType.UNSET_TEMPLATE_PROCEDURE)) {
+1 continue;
+1 }
+1 unsetTemplateProcedure = (UnsetTemplateProcedure) runningProcedure;
+1 if (queryId.equals(unsetTemplateProcedure.getQueryId())) {
+1 procedure = unsetTemplateProcedure;
+1 break;
+1 }
+1 if (template.getId() == unsetTemplateProcedure.getTemplateId()
+1 && path.equals(unsetTemplateProcedure.getPath())) {
+1 hasOverlappedTask = true;
+1 break;
+1 }
+1 }
+1
+1 if (procedure == null) {
+1 if (hasOverlappedTask) {
+1 return RpcUtils.getStatus(
+1 TSStatusCode.OVERLAP_WITH_EXISTING_TASK,
+1 "Some other task is unsetting target template from target path "
+1 + path.getFullPath());
+1 }
+1 procedure = new UnsetTemplateProcedure(queryId, template, path, isGeneratedByPipe);
+1 this.executor.submitProcedure(procedure);
+1 }
+1 }
+1 return waitingProcedureFinished(procedure);
+1 }
+1
+1 /**
+1 * Generate an {@link AddConfigNodeProcedure}, and serially execute all the {@link
+1 * AddConfigNodeProcedure}s.
+1 */
+1 public void addConfigNode(TConfigNodeRegisterReq req) {
+1 final AddConfigNodeProcedure addConfigNodeProcedure =
+1 new AddConfigNodeProcedure(req.getConfigNodeLocation(), req.getVersionInfo());
+1 this.executor.submitProcedure(addConfigNodeProcedure);
+1 }
+1
+1 /**
+1 * Generate a {@link RemoveConfigNodeProcedure}, and serially execute all the {@link
+1 * RemoveConfigNodeProcedure}s.
+1 */
+1 public void removeConfigNode(RemoveConfigNodePlan removeConfigNodePlan) {
+1 final RemoveConfigNodeProcedure removeConfigNodeProcedure =
+1 new RemoveConfigNodeProcedure(removeConfigNodePlan.getConfigNodeLocation());
+1 this.executor.submitProcedure(removeConfigNodeProcedure);
+1 LOGGER.info("Submit RemoveConfigNodeProcedure successfully: {}", removeConfigNodePlan);
+1 }
+1
+1 /**
+1 * Generate {@link RemoveDataNodesProcedure}s, and serially execute all the {@link
+1 * RemoveDataNodesProcedure}s.
+1 */
+1 public boolean removeDataNode(RemoveDataNodePlan removeDataNodePlan) {
+1 Map<Integer, NodeStatus> nodeStatusMap = new HashMap<>();
+1 removeDataNodePlan
+1 .getDataNodeLocations()
+1 .forEach(
+1 datanode ->
+1 nodeStatusMap.put(
+1 datanode.getDataNodeId(),
+1 configManager.getLoadManager().getNodeStatus(datanode.getDataNodeId())));
+1 this.executor.submitProcedure(
+1 new RemoveDataNodesProcedure(removeDataNodePlan.getDataNodeLocations(), nodeStatusMap));
+1 LOGGER.info(
+1 "Submit RemoveDataNodesProcedure successfully, {}",
+1 removeDataNodePlan.getDataNodeLocations());
+1 return true;
+1 }
+1
+1 public boolean removeAINode(RemoveAINodePlan removeAINodePlan) {
+1 this.executor.submitProcedure(new RemoveAINodeProcedure(removeAINodePlan.getAINodeLocation()));
+1 LOGGER.info(
+1 "Submit RemoveAINodeProcedure successfully, {}", removeAINodePlan.getAINodeLocation());
+1 return true;
+1 }
+1
+1 public TSStatus checkRemoveDataNodes(List<TDataNodeLocation> dataNodeLocations) {
+1 // 1. Only one RemoveDataNodesProcedure is allowed in the cluster
+1 Optional<Procedure<ConfigNodeProcedureEnv>> anotherRemoveProcedure =
+1 getExecutor().getProcedures().values().stream()
+1 .filter(
+1 procedure -> {
+1 if (procedure instanceof RemoveDataNodesProcedure) {
+1 return !procedure.isFinished();
+1 }
+1 return false;
+1 })
+1 .findAny();
+1
+1 String failMessage = null;
+1 if (anotherRemoveProcedure.isPresent()) {
+1 List<TDataNodeLocation> anotherRemoveDataNodes =
+1 ((RemoveDataNodesProcedure) anotherRemoveProcedure.get()).getRemovedDataNodes();
+1 failMessage =
+1 String.format(
+1 "Submit RemoveDataNodesProcedure failed, "
+1 + "because another RemoveDataNodesProcedure %s is already in processing. "
+1 + "IoTDB is able to have at most 1 RemoveDataNodesProcedure at the same time. "
+1 + "For further information, please search [pid%d] in log. ",
+1 anotherRemoveDataNodes, anotherRemoveProcedure.get().getProcId());
+1 }
+1
+1 // 2. Check if the RemoveDataNodesProcedure conflicts with the RegionMigrateProcedure
+1 Set<TConsensusGroupId> removedDataNodesRegionSet =
+1 getEnv().getRemoveDataNodeHandler().getRemovedDataNodesRegionSet(dataNodeLocations);
+1 Optional<Procedure<ConfigNodeProcedureEnv>> conflictRegionMigrateProcedure =
+1 getExecutor().getProcedures().values().stream()
+1 .filter(
+1 procedure -> {
+1 if (procedure instanceof RegionMigrateProcedure) {
+1 RegionMigrateProcedure regionMigrateProcedure =
+1 (RegionMigrateProcedure) procedure;
+1 if (regionMigrateProcedure.isFinished()) {
+1 return false;
+1 }
+1 return removedDataNodesRegionSet.contains(regionMigrateProcedure.getRegionId())
+1 || dataNodeLocations.contains(regionMigrateProcedure.getDestDataNode());
+1 }
+1 return false;
+1 })
+1 .findAny();
+1 if (conflictRegionMigrateProcedure.isPresent()) {
+1 failMessage =
+1 String.format(
+1 "Submit RemoveDataNodesProcedure failed, "
+1 + "because another RegionMigrateProcedure %s is already in processing which conflicts with this RemoveDataNodesProcedure. "
+1 + "The RegionMigrateProcedure is migrating the region %s to the DataNode %s. "
+1 + "For further information, please search [pid%d] in log. ",
+1 conflictRegionMigrateProcedure.get().getProcId(),
+1 ((RegionMigrateProcedure) conflictRegionMigrateProcedure.get()).getRegionId(),
+1 ((RegionMigrateProcedure) conflictRegionMigrateProcedure.get()).getDestDataNode(),
+1 conflictRegionMigrateProcedure.get().getProcId());
+1 }
+1 // 3. Check if the RegionMigrateProcedure generated by RemoveDataNodesProcedure conflicts with
+1 // each other
+1 List<RegionMigrationPlan> regionMigrationPlans =
+1 getEnv().getRemoveDataNodeHandler().getRegionMigrationPlans(dataNodeLocations);
+1 removedDataNodesRegionSet.clear();
+1 for (RegionMigrationPlan regionMigrationPlan : regionMigrationPlans) {
+1 if (removedDataNodesRegionSet.contains(regionMigrationPlan.getRegionId())) {
+1 failMessage =
+1 String.format(
+1 "Submit RemoveDataNodesProcedure failed, "
+1 + "because the RegionMigrateProcedure generated by this RemoveDataNodesProcedure conflicts with each other. "
+1 + "Only one replica of the same consensus group is allowed to be migrated at the same time."
+1 + "The conflict region id is %s . ",
+1 regionMigrationPlan.getRegionId());
+1 break;
+1 }
+1 removedDataNodesRegionSet.add(regionMigrationPlan.getRegionId());
+1 }
+1
+1 // 4. Check if there are any other unknown or readonly DataNodes in the consensus group that are
+1 // not the remove DataNodes
+1
+1 for (TDataNodeLocation removeDataNode : dataNodeLocations) {
+1 Set<TDataNodeLocation> relatedDataNodes =
+1 getEnv().getRemoveDataNodeHandler().getRelatedDataNodeLocations(removeDataNode);
+1 relatedDataNodes.remove(removeDataNode);
+1
+1 for (TDataNodeLocation relatedDataNode : relatedDataNodes) {
+1 NodeStatus nodeStatus =
+1 getConfigManager().getLoadManager().getNodeStatus(relatedDataNode.getDataNodeId());
+1 if (nodeStatus == NodeStatus.Unknown || nodeStatus == NodeStatus.ReadOnly) {
+1 failMessage =
+1 String.format(
+1 "Submit RemoveDataNodesProcedure failed, "
+1 + "because when there are other unknown or readonly nodes in the consensus group that are not remove nodes, "
+1 + "the remove operation cannot be performed for security reasons. "
+1 + "Please check the status of the node %s and ensure it is running.",
+1 relatedDataNode.getDataNodeId());
+1 }
+1 }
+1 }
+1
+1 if (failMessage != null) {
+1 LOGGER.warn(failMessage);
+1 TSStatus failStatus = new TSStatus(TSStatusCode.REMOVE_DATANODE_ERROR.getStatusCode());
+1 failStatus.setMessage(failMessage);
+1 return failStatus;
+1 }
+1 return new TSStatus(TSStatusCode.SUCCESS_STATUS.getStatusCode());
+1 }
+1
+1 // region region operation related check
+1
+1 /**
+1 * Checks whether region migration is allowed.
+1 *
+1 * @param migrateRegionReq the migration request details
+1 * @param regionGroupId the ID of the consensus group for the region
+1 * @param originalDataNode the original DataNode location from which the region is being migrated
+1 * @param destDataNode the destination DataNode location to which the region is being migrated
+1 * @param coordinatorForAddPeer the DataNode location acting as the coordinator for adding a peer
+1 * @return the status of the migration request (TSStatus)
+1 */
+1 private TSStatus checkMigrateRegion(
+1 TMigrateRegionReq migrateRegionReq,
+1 TConsensusGroupId regionGroupId,
+1 TDataNodeLocation originalDataNode,
+1 TDataNodeLocation destDataNode,
+1 TDataNodeLocation coordinatorForAddPeer) {
+1 String failMessage;
+1 if ((failMessage =
+1 regionOperationCommonCheck(
+1 regionGroupId,
+1 destDataNode,
+1 Arrays.asList(
+1 new Pair<>("Original DataNode", originalDataNode),
+1 new Pair<>("Destination DataNode", destDataNode),
+1 new Pair<>("Coordinator for add peer", coordinatorForAddPeer)),
+1 migrateRegionReq.getModel()))
+1 != null) {
+1 // do nothing
+1 } else if (configManager
+1 .getPartitionManager()
+1 .getAllReplicaSets(originalDataNode.getDataNodeId())
+1 .stream()
+1 .noneMatch(replicaSet -> replicaSet.getRegionId().equals(regionGroupId))) {
+1 failMessage =
+1 String.format(
+1 "Submit RegionMigrateProcedure failed, because the original DataNode %s doesn't contain Region %s",
+1 migrateRegionReq.getFromId(), migrateRegionReq.getRegionId());
+1 } else if (configManager
+1 .getPartitionManager()
+1 .getAllReplicaSets(destDataNode.getDataNodeId())
+1 .stream()
+1 .anyMatch(replicaSet -> replicaSet.getRegionId().equals(regionGroupId))) {
+1 failMessage =
+1 String.format(
+1 "Submit RegionMigrateProcedure failed, because the target DataNode %s already contains Region %s",
+1 migrateRegionReq.getToId(), migrateRegionReq.getRegionId());
+1 }
+1
+1 if (failMessage != null) {
+1 LOGGER.warn(failMessage);
+1 TSStatus failStatus = new TSStatus(TSStatusCode.MIGRATE_REGION_ERROR.getStatusCode());
+1 failStatus.setMessage(failMessage);
+1 return failStatus;
+1 }
+1 return new TSStatus(TSStatusCode.SUCCESS_STATUS.getStatusCode());
+1 }
+1
+1 private TSStatus checkReconstructRegion(
+1 TReconstructRegionReq req,
+1 TConsensusGroupId regionId,
+1 TDataNodeLocation targetDataNode,
+1 TDataNodeLocation coordinator) {
+1 String failMessage =
+1 regionOperationCommonCheck(
+1 regionId,
+1 targetDataNode,
+1 Arrays.asList(
+1 new Pair<>("Target DataNode", targetDataNode),
+1 new Pair<>("Coordinator", coordinator)),
+1 req.getModel());
+1
+1 ConfigNodeConfig conf = ConfigNodeDescriptor.getInstance().getConf();
+1 if (configManager
+1 .getPartitionManager()
+1 .getAllReplicaSetsMap(regionId.getType())
+1 .get(regionId)
+1 .getDataNodeLocationsSize()
+1 == 1) {
+1 failMessage = String.format("%s only has 1 replica, it cannot be reconstructed", regionId);
+1 } else if (configManager
+1 .getPartitionManager()
+1 .getAllReplicaSets(targetDataNode.getDataNodeId())
+1 .stream()
+1 .noneMatch(replicaSet -> replicaSet.getRegionId().equals(regionId))) {
+1 failMessage =
+1 String.format(
+1 "Submit ReconstructRegionProcedure failed, because the target DataNode %s doesn't contain Region %s",
+1 req.getDataNodeId(), regionId);
+1 }
+1
+1 if (failMessage != null) {
+1 LOGGER.warn(failMessage);
+1 TSStatus failStatus = new TSStatus(TSStatusCode.RECONSTRUCT_REGION_ERROR.getStatusCode());
+1 failStatus.setMessage(failMessage);
+1 return failStatus;
+1 }
+1 return new TSStatus(TSStatusCode.SUCCESS_STATUS.getStatusCode());
+1 }
+1
+1 private TSStatus checkExtendRegion(
+1 TExtendRegionReq req,
+1 TConsensusGroupId regionId,
+1 TDataNodeLocation targetDataNode,
+1 TDataNodeLocation coordinator) {
+1 String failMessage =
+1 regionOperationCommonCheck(
+1 regionId,
+1 targetDataNode,
+1 Arrays.asList(
+1 new Pair<>("Target DataNode", targetDataNode),
+1 new Pair<>("Coordinator", coordinator)),
+1 req.getModel());
+1 if (configManager
+1 .getPartitionManager()
+1 .getAllReplicaSets(targetDataNode.getDataNodeId())
+1 .stream()
+1 .anyMatch(replicaSet -> replicaSet.getRegionId().equals(regionId))) {
+1 failMessage =
+1 String.format(
+1 "Target DataNode %s already contains region %s",
+1 targetDataNode.getDataNodeId(), regionId);
+1 }
+1
+1 if (failMessage != null) {
+1 LOGGER.warn(failMessage);
+1 TSStatus failStatus = new TSStatus(TSStatusCode.RECONSTRUCT_REGION_ERROR.getStatusCode());
+1 failStatus.setMessage(failMessage);
+1 return failStatus;
+1 }
+1 return new TSStatus(TSStatusCode.SUCCESS_STATUS.getStatusCode());
+1 }
+1
+1 private TSStatus checkRemoveRegion(
+1 TRemoveRegionReq req,
+1 TConsensusGroupId regionId,
+1 @Nullable TDataNodeLocation targetDataNode,
+1 TDataNodeLocation coordinator) {
+1 String failMessage =
+1 regionOperationCommonCheck(
+1 regionId,
+1 targetDataNode,
+1 Arrays.asList(new Pair<>("Coordinator", coordinator)),
+1 req.getModel());
+1
+1 if (configManager
+1 .getPartitionManager()
+1 .getAllReplicaSetsMap(regionId.getType())
+1 .get(regionId)
+1 .getDataNodeLocationsSize()
+1 == 1) {
+1 failMessage = String.format("%s only has 1 replica, it cannot be removed", regionId);
+1 } else if (targetDataNode != null
+1 && configManager
+1 .getPartitionManager()
+1 .getAllReplicaSets(targetDataNode.getDataNodeId())
+1 .stream()
+1 .noneMatch(replicaSet -> replicaSet.getRegionId().equals(regionId))) {
+1 failMessage =
+1 String.format(
+1 "Target DataNode %s doesn't contain Region %s", req.getDataNodeId(), regionId);
+1 }
+1
+1 if (failMessage != null) {
+1 LOGGER.warn(failMessage);
+1 TSStatus failStatus = new TSStatus(TSStatusCode.REMOVE_REGION_PEER_ERROR.getStatusCode());
+1 failStatus.setMessage(failMessage);
+1 return failStatus;
+1 }
+1 return new TSStatus(TSStatusCode.SUCCESS_STATUS.getStatusCode());
+1 }
+1
+1 /**
+1 * The common checks of all region operations, include migration, reconstruction, extension,
+1 * removing
+1 *
+1 * @param regionId region group id, also called consensus group id
+1 * @param targetDataNode DataNode should in Running status
+1 * @param relatedDataNodes Pair<Identity, Node Location>
+1 * @return The reason if check failed, or null if check pass
+1 */
+1 private String regionOperationCommonCheck(
+1 TConsensusGroupId regionId,
+1 TDataNodeLocation targetDataNode,
+1 List<Pair<String, TDataNodeLocation>> relatedDataNodes,
+1 Model model) {
+1 String failMessage;
+1 ConfigNodeConfig conf = ConfigNodeDescriptor.getInstance().getConf();
+1
+1 if (TConsensusGroupType.DataRegion == regionId.getType()
+1 && ConsensusFactory.SIMPLE_CONSENSUS.equals(conf.getDataRegionConsensusProtocolClass())) {
+1 failMessage = "SimpleConsensus not supports region operation.";
+1 } else if (TConsensusGroupType.SchemaRegion == regionId.getType()
+1 && ConsensusFactory.SIMPLE_CONSENSUS.equals(conf.getSchemaRegionConsensusProtocolClass())) {
+1 failMessage = "SimpleConsensus not supports region operation.";
+1 } else if ((failMessage = checkRegionOperationDuplication(regionId)) != null) {
+1 // need to do nothing more
+1 } else if (relatedDataNodes.stream().anyMatch(pair -> pair.getRight() == null)) {
+1 Pair<String, TDataNodeLocation> nullPair =
+1 relatedDataNodes.stream().filter(pair -> pair.getRight() == null).findAny().get();
+1 failMessage = String.format("Cannot find %s", nullPair.getLeft());
+1 } else if (targetDataNode != null
+1 && !configManager.getNodeManager().filterDataNodeThroughStatus(NodeStatus.Running).stream()
+1 .map(TDataNodeConfiguration::getLocation)
+1 .map(TDataNodeLocation::getDataNodeId)
+1 .collect(Collectors.toSet())
+1 .contains(targetDataNode.getDataNodeId())) {
+1 // Here we only check Running DataNode to implement migration, because removing nodes may not
+1 // exist when add peer is performing
+1 failMessage =
+1 String.format(
+1 "Target DataNode %s is not in Running status.", targetDataNode.getDataNodeId());
+1 } else if ((failMessage = checkRegionOperationWithRemoveDataNode(regionId, targetDataNode))
+1 != null) {
+1 // need to do nothing more
+1 } else if ((failMessage = checkRegionOperationModelCorrectness(regionId, model)) != null) {
+1 // need to do nothing more
+1 }
+1
+1 return failMessage;
+1 }
+1
+1 private String checkRegionOperationWithRemoveDataNode(
+1 TConsensusGroupId regionId, TDataNodeLocation targetDataNode) {
+1 Optional<Procedure<ConfigNodeProcedureEnv>> conflictRemoveDataNodesProcedure =
+1 getExecutor().getProcedures().values().stream()
+1 .filter(
+1 procedure -> {
+1 if (procedure instanceof RemoveDataNodesProcedure) {
+1 return !procedure.isFinished();
+1 }
+1 return false;
+1 })
+1 .findAny();
+1
+1 if (conflictRemoveDataNodesProcedure.isPresent()) {
+1 RemoveDataNodeHandler removeDataNodeHandler = env.getRemoveDataNodeHandler();
+1 List<TDataNodeLocation> removedDataNodes =
+1 ((RemoveDataNodesProcedure) conflictRemoveDataNodesProcedure.get()).getRemovedDataNodes();
+1 Set<TConsensusGroupId> removedDataNodesRegionSet =
+1 removeDataNodeHandler.getRemovedDataNodesRegionSet(removedDataNodes);
+1 if (removedDataNodesRegionSet.contains(regionId)) {
+1 return String.format(
+1 "Another RemoveDataNodesProcedure %s is already in processing which conflicts with this procedure. "
+1 + "The RemoveDataNodesProcedure is removing the DataNodes %s which contains the region %s. "
+1 + "For further information, please search [pid%d] in log. ",
+1 conflictRemoveDataNodesProcedure.get().getProcId(),
+1 removedDataNodes,
+1 regionId,
+1 conflictRemoveDataNodesProcedure.get().getProcId());
+1 } else if (removedDataNodes.contains(targetDataNode)) {
+1 return String.format(
+1 "Another RemoveDataNodesProcedure %s is already in processing which conflicts with this procedure. "
+1 + "The RemoveDataNodesProcedure is removing the target DataNode %s. "
+1 + "For further information, please search [pid%d] in log. ",
+1 conflictRemoveDataNodesProcedure.get().getProcId(),
+1 targetDataNode,
+1 conflictRemoveDataNodesProcedure.get().getProcId());
+1 }
+1 }
+1 return null;
+1 }
+1
+1 private String checkRegionOperationDuplication(TConsensusGroupId regionId) {
+1 List<? extends RegionOperationProcedure<?>> otherRegionMemberChangeProcedures =
+1 getRegionOperationProcedures()
+1 .filter(
+1 regionMemberChangeProcedure ->
+1 regionId.equals(regionMemberChangeProcedure.getRegionId()))
+1 .collect(Collectors.toList());
+1 if (!otherRegionMemberChangeProcedures.isEmpty()) {
+1 return String.format(
+1 "%s has some other region operation procedures in progress, their procedure id is: %s",
+1 regionId, otherRegionMemberChangeProcedures);
+1 }
+1 return null;
+1 }
+1
+1 public List<TConsensusGroupId> getRegionOperationConsensusIds() {
+1 return getRegionOperationProcedures()
+1 .map(RegionOperationProcedure::getRegionId)
+1 .distinct()
+1 .collect(Collectors.toList());
+1 }
+1
+1 private Stream<RegionOperationProcedure<?>> getRegionOperationProcedures() {
+1 return getExecutor().getProcedures().values().stream()
+1 .filter(procedure -> !procedure.isFinished())
+1 .filter(procedure -> procedure instanceof RegionOperationProcedure)
+1 .map(procedure -> (RegionOperationProcedure<?>) procedure);
+1 }
+1
+1 private String checkRegionOperationModelCorrectness(TConsensusGroupId regionId, Model model) {
+1 String databaseName = configManager.getPartitionManager().getRegionDatabase(regionId);
boolean isTreeModelDatabase = databaseName.startsWith(SqlConstant.TREE_MODEL_DATABASE_PREFIX);
if (Model.TREE == model && isTreeModelDatabase
|| Model.TABLE == model && !isTreeModelDatabase) {
diff --git a/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/procedure/CompletedProcedureContainer.java b/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/procedure/CompletedProcedureContainer.java
index ba6712a983d..b61cdf46535 100644
--- a/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/procedure/CompletedProcedureContainer.java
+++ b/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/procedure/CompletedProcedureContainer.java
@@ -1,36 +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.confignode.procedure;
-
-public class CompletedProcedureContainer<Env> {
- private final Procedure<Env> procedure;
-
- public CompletedProcedureContainer(Procedure<Env> procedure) {
- this.procedure = procedure;
- }
-
- public Procedure<Env> getProcedure() {
- return procedure;
- }
-
- public boolean isExpired(long now, long evictTtl) {
- return (now - procedure.getLastUpdate()) >= evictTtl;
- }
-}
+1/*
+1 * Licensed to the Apache Software Foundation (ASF) under one
+1 * or more contributor license agreements. See the NOTICE file
+1 * distributed with this work for additional information
+1 * regarding copyright ownership. The ASF licenses this file
+1 * to you under the Apache License, Version 2.0 (the
+1 * "License"); you may not use this file except in compliance
+1 * with the License. You may obtain a copy of the License at
+1 *
+1 * http://www.apache.org/licenses/LICENSE-2.0
+1 *
+1 * Unless required by applicable law or agreed to in writing,
+1 * software distributed under the License is distributed on an
+1 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+1 * KIND, either express or implied. See the License for the
+1 * specific language governing permissions and limitations
+1 * under the License.
+1 */
+1
+1package org.apache.iotdb.confignode.procedure;
+1
+1public class CompletedProcedureContainer<Env> {
+1 private final Procedure<Env> procedure;
+1
+1 public CompletedProcedureContainer(Procedure<Env> procedure) {
+1 this.procedure = procedure;
+1 }
+1
+1 public Procedure<Env> getProcedure() {
+1 return procedure;
+1 }
+1
+1 public boolean isExpired(long now, long evictTtl) {
+1 return (now - procedure.getLastUpdate()) >= evictTtl;
+1 }
+1}
+1
\ No newline at end of file
diff --git a/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/procedure/CompletedProcedureRecycler.java b/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/procedure/CompletedProcedureRecycler.java
index cea15a1bfb6..aea91dbdb22 100644
--- a/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/procedure/CompletedProcedureRecycler.java
+++ b/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/procedure/CompletedProcedureRecycler.java
@@ -1,84 +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.confignode.procedure;
-
-import org.apache.iotdb.confignode.procedure.store.IProcedureStore;
-
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-import java.util.Iterator;
-import java.util.Map;
-import java.util.concurrent.TimeUnit;
-
-/** Internal cleaner that removes the completed procedure results after a TTL. */
-public class CompletedProcedureRecycler<Env> extends InternalProcedure<Env> {
- private static final Logger LOG = LoggerFactory.getLogger(CompletedProcedureRecycler.class);
- private static final int DEFAULT_BATCH_SIZE = 8;
- private final long evictTTL;
- private final Map<Long, CompletedProcedureContainer<Env>> completed;
- private final IProcedureStore<Env> store;
-
- public CompletedProcedureRecycler(
- IProcedureStore<Env> store,
- Map<Long, CompletedProcedureContainer<Env>> completedMap,
- long cleanTimeInterval,
- long evictTTL) {
- super(TimeUnit.SECONDS.toMillis(cleanTimeInterval));
- this.completed = completedMap;
- this.store = store;
- this.evictTTL = evictTTL;
- }
-
- @Override
- protected void periodicExecute(final Env env) {
- if (completed.isEmpty()) {
- if (LOG.isTraceEnabled()) {
- LOG.trace("No completed procedures to cleanup.");
- }
- return;
- }
-
- final long[] batchIds = new long[DEFAULT_BATCH_SIZE];
- int batchCount = 0;
-
- final long now = System.currentTimeMillis();
- final Iterator<Map.Entry<Long, CompletedProcedureContainer<Env>>> it =
- completed.entrySet().iterator();
- while (it.hasNext() && store.isRunning()) {
- final Map.Entry<Long, CompletedProcedureContainer<Env>> entry = it.next();
- final CompletedProcedureContainer<Env> retainer = entry.getValue();
- final Procedure<?> proc = retainer.getProcedure();
- if (retainer.isExpired(now, evictTTL)) {
- // Failed procedures aren't persisted in WAL.
- batchIds[batchCount++] = entry.getKey();
- if (batchCount == batchIds.length) {
- store.delete(batchIds, 0, batchCount);
- batchCount = 0;
- }
- it.remove();
- LOG.trace("Evict completed {}", proc);
- }
- }
- if (batchCount > 0) {
- store.delete(batchIds, 0, batchCount);
- }
- }
-}
+1/*
+1 * Licensed to the Apache Software Foundation (ASF) under one
+1 * or more contributor license agreements. See the NOTICE file
+1 * distributed with this work for additional information
+1 * regarding copyright ownership. The ASF licenses this file
+1 * to you under the Apache License, Version 2.0 (the
+1 * "License"); you may not use this file except in compliance
+1 * with the License. You may obtain a copy of the License at
+1 *
+1 * http://www.apache.org/licenses/LICENSE-2.0
+1 *
+1 * Unless required by applicable law or agreed to in writing,
+1 * software distributed under the License is distributed on an
+1 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+1 * KIND, either express or implied. See the License for the
+1 * specific language governing permissions and limitations
+1 * under the License.
+1 */
+1
+1package org.apache.iotdb.confignode.procedure;
+1
+1import org.apache.iotdb.confignode.procedure.store.IProcedureStore;
+1
+1import org.slf4j.Logger;
+1import org.slf4j.LoggerFactory;
+1
+1import java.util.Iterator;
+1import java.util.Map;
+1import java.util.concurrent.TimeUnit;
+1
+1/** Internal cleaner that removes the completed procedure results after a TTL. */
+1public class CompletedProcedureRecycler<Env> extends InternalProcedure<Env> {
+1 private static final Logger LOG = LoggerFactory.getLogger(CompletedProcedureRecycler.class);
+1 private static final int DEFAULT_BATCH_SIZE = 8;
+1 private final long evictTTL;
+1 private final Map<Long, CompletedProcedureContainer<Env>> completed;
+1 private final IProcedureStore<Env> store;
+1
+1 public CompletedProcedureRecycler(
+1 IProcedureStore<Env> store,
+1 Map<Long, CompletedProcedureContainer<Env>> completedMap,
+1 long cleanTimeInterval,
+1 long evictTTL) {
+1 super(TimeUnit.SECONDS.toMillis(cleanTimeInterval));
+1 this.completed = completedMap;
+1 this.store = store;
+1 this.evictTTL = evictTTL;
+1 }
+1
+1 @Override
+1 protected void periodicExecute(final Env env) {
+1 if (completed.isEmpty()) {
+1 if (LOG.isTraceEnabled()) {
+1 LOG.trace("No completed procedures to cleanup.");
+1 }
+1 return;
+1 }
+1
+1 final long[] batchIds = new long[DEFAULT_BATCH_SIZE];
+1 int batchCount = 0;
+1
+1 final long now = System.currentTimeMillis();
+1 final Iterator<Map.Entry<Long, CompletedProcedureContainer<Env>>> it =
+1 completed.entrySet().iterator();
+1 while (it.hasNext() && store.isRunning()) {
+1 final Map.Entry<Long, CompletedProcedureContainer<Env>> entry = it.next();
+1 final CompletedProcedureContainer<Env> retainer = entry.getValue();
+1 final Procedure<?> proc = retainer.getProcedure();
+1 if (retainer.isExpired(now, evictTTL)) {
+1 // Failed procedures aren't persisted in WAL.
+1 batchIds[batchCount++] = entry.getKey();
+1 if (batchCount == batchIds.length) {
+1 store.delete(batchIds, 0, batchCount);
+1 batchCount = 0;
+1 }
+1 it.remove();
+1 LOG.trace("Evict completed {}", proc);
+1 }
+1 }
+1 if (batchCount > 0) {
+1 store.delete(batchIds, 0, batchCount);
+1 }
+1 }
+1}
+1
\ No newline at end of file
diff --git a/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/procedure/InternalProcedure.java b/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/procedure/InternalProcedure.java
index f9ff68bde7e..4a70aed89d0 100644
--- a/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/procedure/InternalProcedure.java
+++ b/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/procedure/InternalProcedure.java
@@ -1,49 +1,50 @@
-/*
- * 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.confignode.procedure;
-
-import java.io.IOException;
-import java.nio.ByteBuffer;
-
-/**
- * Internal Procedure, do some periodic job for framework.
- *
- * @param <Env>
- */
-public abstract class InternalProcedure<Env> extends Procedure<Env> {
- protected InternalProcedure(long toMillis) {
- setTimeout(toMillis);
- }
-
- protected abstract void periodicExecute(final Env env);
-
- @Override
- protected Procedure<Env>[] execute(Env env) throws InterruptedException {
- throw new UnsupportedOperationException();
- }
-
- @Override
- protected void rollback(Env env) throws IOException, InterruptedException {
- throw new UnsupportedOperationException();
- }
-
- @Override
- public void deserialize(ByteBuffer byteBuffer) {}
-}
+1/*
+1 * Licensed to the Apache Software Foundation (ASF) under one
+1 * or more contributor license agreements. See the NOTICE file
+1 * distributed with this work for additional information
+1 * regarding copyright ownership. The ASF licenses this file
+1 * to you under the Apache License, Version 2.0 (the
+1 * "License"); you may not use this file except in compliance
+1 * with the License. You may obtain a copy of the License at
+1 *
+1 * http://www.apache.org/licenses/LICENSE-2.0
+1 *
+1 * Unless required by applicable law or agreed to in writing,
+1 * software distributed under the License is distributed on an
+1 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+1 * KIND, either express or implied. See the License for the
+1 * specific language governing permissions and limitations
+1 * under the License.
+1 */
+1
+1package org.apache.iotdb.confignode.procedure;
+1
+1import java.io.IOException;
+1import java.nio.ByteBuffer;
+1
+1/**
+1 * Internal Procedure, do some periodic job for framework.
+1 *
+1 * @param <Env>
+1 */
+1public abstract class InternalProcedure<Env> extends Procedure<Env> {
+1 protected InternalProcedure(long toMillis) {
+1 setTimeout(toMillis);
+1 }
+1
+1 protected abstract void periodicExecute(final Env env);
+1
+1 @Override
+1 protected Procedure<Env>[] execute(Env env) throws InterruptedException {
+1 throw new UnsupportedOperationException();
+1 }
+1
+1 @Override
+1 protected void rollback(Env env) throws IOException, InterruptedException {
+1 throw new UnsupportedOperationException();
+1 }
+1
+1 @Override
+1 public void deserialize(ByteBuffer byteBuffer) {}
+1}
+1
\ No newline at end of file
diff --git a/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/procedure/PartitionTableAutoCleaner.java b/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/procedure/PartitionTableAutoCleaner.java
index 2ab15283207..4d2d8a23256 100644
--- a/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/procedure/PartitionTableAutoCleaner.java
+++ b/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/procedure/PartitionTableAutoCleaner.java
@@ -1,112 +1,113 @@
-/*
- * 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.confignode.procedure;
-
-import org.apache.iotdb.common.rpc.thrift.TTimePartitionSlot;
-import org.apache.iotdb.commons.conf.CommonConfig;
-import org.apache.iotdb.commons.conf.CommonDescriptor;
-import org.apache.iotdb.commons.utils.PathUtils;
-import org.apache.iotdb.confignode.consensus.request.write.partition.AutoCleanPartitionTablePlan;
-import org.apache.iotdb.confignode.manager.ConfigManager;
-import org.apache.iotdb.consensus.exception.ConsensusException;
-
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-import java.util.List;
-import java.util.Map;
-import java.util.TreeMap;
-
-import static org.apache.iotdb.confignode.manager.partition.PartitionManager.CONSENSUS_WRITE_ERROR;
-
-/** A cleaner that automatically deletes the expired mapping within the partition table. */
-public class PartitionTableAutoCleaner<Env> extends InternalProcedure<Env> {
-
- private static final Logger LOGGER = LoggerFactory.getLogger(PartitionTableAutoCleaner.class);
-
- private static final CommonConfig COMMON_CONFIG = CommonDescriptor.getInstance().getConfig();
-
- private static final String timestampPrecision =
- CommonDescriptor.getInstance().getConfig().getTimestampPrecision();
-
- private final ConfigManager configManager;
-
- public PartitionTableAutoCleaner(ConfigManager configManager) {
- super(COMMON_CONFIG.getTTLCheckInterval());
- this.configManager = configManager;
- LOGGER.info(
- "[PartitionTableCleaner] The PartitionTableAutoCleaner is started with cycle={}ms",
- COMMON_CONFIG.getTTLCheckInterval());
- }
-
- @Override
- protected void periodicExecute(Env env) {
- List<String> databases = configManager.getClusterSchemaManager().getDatabaseNames(null);
- Map<String, Long> databaseTTLMap = new TreeMap<>();
- for (String database : databases) {
- long databaseTTL =
- PathUtils.isTableModelDatabase(database)
- ? configManager.getClusterSchemaManager().getDatabaseMaxTTL(database)
- : configManager.getTTLManager().getDatabaseMaxTTL(database);
- databaseTTLMap.put(database, databaseTTL);
- }
- LOGGER.info(
- "[PartitionTableCleaner] Periodically activate PartitionTableAutoCleaner, databaseTTL: {}",
- databaseTTLMap);
- for (String database : databases) {
- long databaseTTL = databaseTTLMap.get(database);
- if (!configManager.getPartitionManager().isDatabaseExist(database)
- || databaseTTL < 0
- || databaseTTL == Long.MAX_VALUE) {
- // Remove the entry if the database or the TTL does not exist
- databaseTTLMap.remove(database);
- }
- }
- if (!databaseTTLMap.isEmpty()) {
- LOGGER.info(
- "[PartitionTableCleaner] Periodically activate PartitionTableAutoCleaner for: {}",
- databaseTTLMap);
- // Only clean the partition table when necessary
- TTimePartitionSlot currentTimePartitionSlot = getCurrentTimePartitionSlot();
- try {
- configManager
- .getConsensusManager()
- .write(new AutoCleanPartitionTablePlan(databaseTTLMap, currentTimePartitionSlot));
- } catch (ConsensusException e) {
- LOGGER.warn(CONSENSUS_WRITE_ERROR, e);
- }
- }
- }
-
- /**
- * @return The time partition slot corresponding to current timestamp. Note that we do not shift
- * the start time to the correct starting point, since this interface only constructs a time
- * reference position for the partition table cleaner.
- */
- private static TTimePartitionSlot getCurrentTimePartitionSlot() {
- if ("ms".equals(timestampPrecision)) {
- return new TTimePartitionSlot(System.currentTimeMillis());
- } else if ("us".equals(timestampPrecision)) {
- return new TTimePartitionSlot(System.currentTimeMillis() * 1000);
- } else {
- return new TTimePartitionSlot(System.currentTimeMillis() * 1000_000);
- }
- }
-}
+1/*
+1 * Licensed to the Apache Software Foundation (ASF) under one
+1 * or more contributor license agreements. See the NOTICE file
+1 * distributed with this work for additional information
+1 * regarding copyright ownership. The ASF licenses this file
+1 * to you under the Apache License, Version 2.0 (the
+1 * "License"); you may not use this file except in compliance
+1 * with the License. You may obtain a copy of the License at
+1 *
+1 * http://www.apache.org/licenses/LICENSE-2.0
+1 *
+1 * Unless required by applicable law or agreed to in writing,
+1 * software distributed under the License is distributed on an
+1 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+1 * KIND, either express or implied. See the License for the
+1 * specific language governing permissions and limitations
+1 * under the License.
+1 */
+1
+1package org.apache.iotdb.confignode.procedure;
+1
+1import org.apache.iotdb.common.rpc.thrift.TTimePartitionSlot;
+1import org.apache.iotdb.commons.conf.CommonConfig;
+1import org.apache.iotdb.commons.conf.CommonDescriptor;
+1import org.apache.iotdb.commons.utils.PathUtils;
+1import org.apache.iotdb.confignode.consensus.request.write.partition.AutoCleanPartitionTablePlan;
+1import org.apache.iotdb.confignode.manager.ConfigManager;
+1import org.apache.iotdb.consensus.exception.ConsensusException;
+1
+1import org.slf4j.Logger;
+1import org.slf4j.LoggerFactory;
+1
+1import java.util.List;
+1import java.util.Map;
+1import java.util.TreeMap;
+1
+1import static org.apache.iotdb.confignode.manager.partition.PartitionManager.CONSENSUS_WRITE_ERROR;
+1
+1/** A cleaner that automatically deletes the expired mapping within the partition table. */
+1public class PartitionTableAutoCleaner<Env> extends InternalProcedure<Env> {
+1
+1 private static final Logger LOGGER = LoggerFactory.getLogger(PartitionTableAutoCleaner.class);
+1
+1 private static final CommonConfig COMMON_CONFIG = CommonDescriptor.getInstance().getConfig();
+1
+1 private static final String timestampPrecision =
+1 CommonDescriptor.getInstance().getConfig().getTimestampPrecision();
+1
+1 private final ConfigManager configManager;
+1
+1 public PartitionTableAutoCleaner(ConfigManager configManager) {
+1 super(COMMON_CONFIG.getTTLCheckInterval());
+1 this.configManager = configManager;
+1 LOGGER.info(
+1 "[PartitionTableCleaner] The PartitionTableAutoCleaner is started with cycle={}ms",
+1 COMMON_CONFIG.getTTLCheckInterval());
+1 }
+1
+1 @Override
+1 protected void periodicExecute(Env env) {
+1 List<String> databases = configManager.getClusterSchemaManager().getDatabaseNames(null);
+1 Map<String, Long> databaseTTLMap = new TreeMap<>();
+1 for (String database : databases) {
+1 long databaseTTL =
+1 PathUtils.isTableModelDatabase(database)
+1 ? configManager.getClusterSchemaManager().getDatabaseMaxTTL(database)
+1 : configManager.getTTLManager().getDatabaseMaxTTL(database);
+1 databaseTTLMap.put(database, databaseTTL);
+1 }
+1 LOGGER.info(
+1 "[PartitionTableCleaner] Periodically activate PartitionTableAutoCleaner, databaseTTL: {}",
+1 databaseTTLMap);
+1 for (String database : databases) {
+1 long databaseTTL = databaseTTLMap.get(database);
+1 if (!configManager.getPartitionManager().isDatabaseExist(database)
+1 || databaseTTL < 0
+1 || databaseTTL == Long.MAX_VALUE) {
+1 // Remove the entry if the database or the TTL does not exist
+1 databaseTTLMap.remove(database);
+1 }
+1 }
+1 if (!databaseTTLMap.isEmpty()) {
+1 LOGGER.info(
+1 "[PartitionTableCleaner] Periodically activate PartitionTableAutoCleaner for: {}",
+1 databaseTTLMap);
+1 // Only clean the partition table when necessary
+1 TTimePartitionSlot currentTimePartitionSlot = getCurrentTimePartitionSlot();
+1 try {
+1 configManager
+1 .getConsensusManager()
+1 .write(new AutoCleanPartitionTablePlan(databaseTTLMap, currentTimePartitionSlot));
+1 } catch (ConsensusException e) {
+1 LOGGER.warn(CONSENSUS_WRITE_ERROR, e);
+1 }
+1 }
+1 }
+1
+1 /**
+1 * @return The time partition slot corresponding to current timestamp. Note that we do not shift
+1 * the start time to the correct starting point, since this interface only constructs a time
+1 * reference position for the partition table cleaner.
+1 */
+1 private static TTimePartitionSlot getCurrentTimePartitionSlot() {
+1 if ("ms".equals(timestampPrecision)) {
+1 return new TTimePartitionSlot(System.currentTimeMillis());
+1 } else if ("us".equals(timestampPrecision)) {
+1 return new TTimePartitionSlot(System.currentTimeMillis() * 1000);
+1 } else {
+1 return new TTimePartitionSlot(System.currentTimeMillis() * 1000_000);
+1 }
+1 }
+1}
+1
\ No newline at end of file
diff --git a/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/procedure/Procedure.java b/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/procedure/Procedure.java
index 91af03d3971..7eb9eaea987 100644
--- a/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/procedure/Procedure.java
+++ b/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/procedure/Procedure.java
@@ -1,823 +1,824 @@
-/*
- * 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.confignode.procedure;
-
-import org.apache.iotdb.confignode.procedure.env.ConfigNodeProcedureEnv;
-import org.apache.iotdb.confignode.procedure.exception.ProcedureException;
-import org.apache.iotdb.confignode.procedure.state.ProcedureLockState;
-import org.apache.iotdb.confignode.procedure.state.ProcedureState;
-import org.apache.iotdb.confignode.procedure.store.IProcedureStore;
-
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-import java.io.DataOutputStream;
-import java.io.IOException;
-import java.nio.ByteBuffer;
-import java.nio.charset.StandardCharsets;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.List;
-import java.util.concurrent.atomic.AtomicReference;
-
-/**
- * Abstract class of all procedures.
- *
- * @param <Env>
- */
-public abstract class Procedure<Env> implements Comparable<Procedure<Env>> {
- private static final Logger LOG = LoggerFactory.getLogger(Procedure.class);
- public static final long NO_PROC_ID = -1;
- public static final long NO_TIMEOUT = -1;
-
- private long parentProcId = NO_PROC_ID;
- private long rootProcId = NO_PROC_ID;
- private long procId = NO_PROC_ID;
- private long submittedTime;
-
- private ProcedureState state = ProcedureState.INITIALIZING;
- private int childrenLatch = 0;
- private ProcedureException exception;
-
- private volatile long timeout = NO_TIMEOUT;
- private volatile long lastUpdate;
-
- private final AtomicReference<byte[]> result = new AtomicReference<>();
- private volatile boolean locked = false;
- private boolean lockedWhenLoading = false;
-
- private int[] stackIndexes = null;
-
- public final boolean hasLock() {
- return locked;
- }
-
- // User level code, override it if necessary
-
- /**
- * The main code of the procedure. It must be idempotent since execute() may be called multiple
- * times in case of machine failure in the middle of the execution.
- *
- * @param env the environment passed to the ProcedureExecutor
- * @return a set of sub-procedures to run or ourselves if there is more work to do or null if the
- * procedure is done.
- * @throws InterruptedException the procedure will be added back to the queue and retried later.
- */
- protected abstract Procedure<Env>[] execute(Env env) throws InterruptedException;
-
- /**
- * The code to undo what was done by the execute() code. It is called when the procedure or one of
- * the sub-procedures failed or an abort was requested. It should cleanup all the resources
- * created by the execute() call. The implementation must be idempotent since rollback() may be
- * called multiple time in case of machine failure in the middle of the execution.
- *
- * @param env the environment passed to the ProcedureExecutor
- * @throws IOException temporary failure, the rollback will retry later
- * @throws InterruptedException the procedure will be added back to the queue and retried later
- */
- protected abstract void rollback(Env env)
- throws IOException, InterruptedException, ProcedureException;
-
- public void serialize(DataOutputStream stream) throws IOException {
- // procid
- stream.writeLong(this.procId);
- // state
- stream.writeInt(this.state.ordinal());
- // submit time
- stream.writeLong(this.submittedTime);
- // last updated
- stream.writeLong(this.lastUpdate);
- // parent id
- stream.writeLong(this.parentProcId);
- // time out
- stream.writeLong(this.timeout);
- // stack indexes
- if (stackIndexes != null) {
- stream.writeInt(stackIndexes.length);
- for (int index : stackIndexes) {
- stream.writeInt(index);
- }
- } else {
- stream.writeInt(-1);
- }
-
- // exceptions
- if (hasException()) {
- // symbol of exception
- stream.write((byte) 1);
- // exception's name
- String exceptionClassName = exception.getClass().getName();
- byte[] exceptionClassNameBytes = exceptionClassName.getBytes(StandardCharsets.UTF_8);
- stream.writeInt(exceptionClassNameBytes.length);
- stream.write(exceptionClassNameBytes);
- // exception's message
- String message = this.exception.getMessage();
- if (message != null) {
- byte[] messageBytes = message.getBytes(StandardCharsets.UTF_8);
- stream.writeInt(messageBytes.length);
- stream.write(messageBytes);
- } else {
- stream.writeInt(-1);
- }
- } else {
- // symbol of no exception
- stream.write((byte) 0);
- }
-
- // result
- if (result.get() != null) {
- stream.writeInt(result.get().length);
- stream.write(result.get());
- } else {
- stream.writeInt(-1);
- }
-
- // Has lock
- stream.write(this.hasLock() ? (byte) 1 : (byte) 0);
- }
-
- public void deserialize(ByteBuffer byteBuffer) {
- // procid
- this.setProcId(byteBuffer.getLong());
- // state
- this.setState(ProcedureState.values()[byteBuffer.getInt()]);
- // submit time
- this.setSubmittedTime(byteBuffer.getLong());
- // last updated
- this.setLastUpdate(byteBuffer.getLong());
- // parent id
- this.setParentProcId(byteBuffer.getLong());
- // time out
- this.setTimeout(byteBuffer.getLong());
- // stack index
- int stackIndexesLen = byteBuffer.getInt();
- if (stackIndexesLen >= 0) {
- List<Integer> indexList = new ArrayList<>(stackIndexesLen);
- for (int i = 0; i < stackIndexesLen; i++) {
- indexList.add(byteBuffer.getInt());
- }
- this.setStackIndexes(indexList);
- }
-
- // exception
- if (byteBuffer.get() == 1) {
- deserializeTypeInfoForCompatibility(byteBuffer);
- int messageBytesLength = byteBuffer.getInt();
- String errMsg = null;
- if (messageBytesLength > 0) {
- byte[] messageBytes = new byte[messageBytesLength];
- byteBuffer.get(messageBytes);
- errMsg = new String(messageBytes, StandardCharsets.UTF_8);
- }
- setFailure(new ProcedureException(errMsg));
- }
-
- // result
- int resultLen = byteBuffer.getInt();
- if (resultLen > 0) {
- byte[] resultArr = new byte[resultLen];
- byteBuffer.get(resultArr);
- }
- // has lock
- if (byteBuffer.get() == 1) {
- this.lockedWhenLoading();
- }
- }
-
- /**
- * Deserialize class Name and load class
- *
- * @param byteBuffer bytebuffer
- * @return Procedure
- */
- @Deprecated
- public static void deserializeTypeInfoForCompatibility(ByteBuffer byteBuffer) {
- int classNameBytesLen = byteBuffer.getInt();
- byte[] classNameBytes = new byte[classNameBytesLen];
- byteBuffer.get(classNameBytes);
- }
-
- /**
- * Acquire a lock, user should override it if necessary.
- *
- * @param env environment
- * @return state of lock
- */
- protected ProcedureLockState acquireLock(Env env) {
- return ProcedureLockState.LOCK_ACQUIRED;
- }
-
- /**
- * Release a lock, user should override it if necessary.
- *
- * @param env env
- */
- protected void releaseLock(Env env) {
- // no op
- }
-
- /**
- * Used to keep procedure lock even when the procedure is yielded or suspended.
- *
- * @param env env
- * @return true if hold the lock
- */
- protected boolean holdLock(Env env) {
- return false;
- }
-
- /**
- * To make executor yield between each execution step to give other procedures a chance to run.
- *
- * @param env environment
- * @return return true if yield is allowed.
- */
- protected boolean isYieldAfterExecution(Env env) {
- return false;
- }
-
- // -------------------------Internal methods - called by the procedureExecutor------------------
- /**
- * Internal method called by the ProcedureExecutor that starts the user-level code execute().
- *
- * @param env execute environment
- * @return sub procedures
- */
- protected Procedure<Env>[] doExecute(Env env) throws InterruptedException {
- try {
- updateTimestamp();
- return execute(env);
- } finally {
- updateTimestamp();
- }
- }
-
- /**
- * Internal method called by the ProcedureExecutor that starts the user-level code rollback().
- *
- * @param env execute environment
- * @throws IOException ioe
- * @throws InterruptedException interrupted exception
- */
- public void doRollback(Env env) throws IOException, InterruptedException, ProcedureException {
- try {
- updateTimestamp();
- rollback(env);
- } finally {
- updateTimestamp();
- }
- }
-
- /**
- * Internal method called by the ProcedureExecutor that starts the user-level code acquireLock().
- *
- * @param env environment
- * @param store ProcedureStore
- * @return ProcedureLockState
- */
- public final ProcedureLockState doAcquireLock(Env env, IProcedureStore store) {
- if (lockedWhenLoading) {
- lockedWhenLoading = false;
- locked = true;
- return ProcedureLockState.LOCK_ACQUIRED;
- }
- ProcedureLockState state = acquireLock(env);
- if (state == ProcedureLockState.LOCK_ACQUIRED) {
- locked = true;
- store.update(this);
- }
- return state;
- }
-
- /**
- * Presist lock state of the procedure
- *
- * @param env environment
- * @param store ProcedureStore
- */
- public final void doReleaseLock(Env env, IProcedureStore store) {
- locked = false;
- if (getState() != ProcedureState.ROLLEDBACK) {
- store.update(this);
- }
- releaseLock(env);
- }
-
- public final void restoreLock(Env env) {
- if (!lockedWhenLoading) {
- LOG.debug("{} didn't hold the lock before restarting, skip acquiring lock", this);
- return;
- }
- if (isFinished()) {
- LOG.debug("{} is already bypassed, skip acquiring lock.", this);
- return;
- }
- if (getState() == ProcedureState.WAITING && !holdLock(env)) {
- LOG.debug("{} is in WAITING STATE, and holdLock= false , skip acquiring lock.", this);
- return;
- }
- LOG.debug("{} held the lock before restarting, call acquireLock to restore it.", this);
- acquireLock(env);
- }
-
- @Override
- public String toString() {
- // Return the simple String presentation of the procedure.
- return toStringSimpleSB().toString();
- }
-
- /**
- * Build the StringBuilder for the simple form of procedure string.
- *
- * @return the StringBuilder
- */
- protected StringBuilder toStringSimpleSB() {
- final StringBuilder sb = new StringBuilder();
-
- sb.append("pid=");
- sb.append(getProcId());
-
- if (hasParent()) {
- sb.append(", ppid=");
- sb.append(getParentProcId());
- }
-
- /*
- * TODO
- * Enable later when this is being used.
- * Currently owner not used.
- if (hasOwner()) {
- sb.append(", owner=");
- sb.append(getOwner());
- }*/
-
- sb.append(", state="); // pState for Procedure State as opposed to any other kind.
- toStringState(sb);
-
- // Only print out locked if actually locked. Most of the time it is not.
- if (this.locked) {
- sb.append(", locked=").append(locked);
- }
- if (hasException()) {
- sb.append(", exception=" + getException());
- }
-
- sb.append("; ");
- toStringClassDetails(sb);
-
- return sb;
- }
-
- /** Extend the toString() information with more procedure details */
- public String toStringDetails() {
- final StringBuilder sb = toStringSimpleSB();
-
- sb.append(" submittedTime=");
- sb.append(getSubmittedTime());
-
- sb.append(", lastUpdate=");
- sb.append(getLastUpdate());
-
- final int[] stackIndices = getStackIndexes();
- if (stackIndices != null) {
- sb.append("\n");
- sb.append("stackIndexes=");
- sb.append(Arrays.toString(stackIndices));
- }
-
- return sb.toString();
- }
-
- /**
- * Called from {@link #toString()} when interpolating {@link Procedure} State. Allows decorating
- * generic Procedure State with Procedure particulars.
- *
- * @param builder Append current {@link ProcedureState}
- */
- protected void toStringState(StringBuilder builder) {
- builder.append(getState());
- }
-
- /**
- * Extend the toString() information with the procedure details e.g. className and parameters
- *
- * @param builder the string builder to use to append the proc specific information
- */
- protected void toStringClassDetails(StringBuilder builder) {
- builder.append(getClass().getName());
- }
-
- // ==========================================================================
- // Those fields are unchanged after initialization.
- //
- // Each procedure will get created from the user or during
- // ProcedureExecutor.start() during the load() phase and then submitted
- // to the executor. these fields will never be changed after initialization
- // ==========================================================================
- public long getProcId() {
- return procId;
- }
-
- public boolean hasParent() {
- return parentProcId != NO_PROC_ID;
- }
-
- public long getParentProcId() {
- return parentProcId;
- }
-
- public long getRootProcId() {
- return rootProcId;
- }
-
- public String getProcType() {
- return getClass().getSimpleName();
- }
-
- public long getSubmittedTime() {
- return submittedTime;
- }
-
- /** Called by the ProcedureExecutor to assign the ID to the newly created procedure. */
- public void setProcId(long procId) {
- this.procId = procId;
- }
-
- public void setProcRunnable() {
- this.submittedTime = System.currentTimeMillis();
- setState(ProcedureState.RUNNABLE);
- }
-
- /** Called by the ProcedureExecutor to assign the parent to the newly created procedure. */
- protected void setParentProcId(long parentProcId) {
- this.parentProcId = parentProcId;
- }
-
- protected void setRootProcId(long rootProcId) {
- this.rootProcId = rootProcId;
- }
-
- /**
- * Called on store load to initialize the Procedure internals after the creation/deserialization.
- */
- protected void setSubmittedTime(long submittedTime) {
- this.submittedTime = submittedTime;
- }
-
- // ==========================================================================
- // runtime state - timeout related
- // ==========================================================================
- /**
- * @param timeout timeout interval in msec
- */
- protected void setTimeout(long timeout) {
- this.timeout = timeout;
- }
-
- public boolean hasTimeout() {
- return timeout != NO_TIMEOUT;
- }
-
- /**
- * @return the timeout in msec
- */
- public long getTimeout() {
- return timeout;
- }
-
- /**
- * Called on store load to initialize the Procedure internals after the creation/deserialization.
- */
- protected void setLastUpdate(long lastUpdate) {
- this.lastUpdate = lastUpdate;
- }
-
- /** Called by ProcedureExecutor after each time a procedure step is executed. */
- protected void updateTimestamp() {
- this.lastUpdate = System.currentTimeMillis();
- }
-
- public long getLastUpdate() {
- return lastUpdate;
- }
-
- /**
- * Timeout of the next timeout. Called by the ProcedureExecutor if the procedure has timeout set
- * and the procedure is in the waiting queue.
- *
- * @return the timestamp of the next timeout.
- */
- protected long getTimeoutTimestamp() {
- return getLastUpdate() + getTimeout();
- }
-
- // ==========================================================================
- // runtime state
- // ==========================================================================
- /**
- * @return the time elapsed between the last update and the start time of the procedure.
- */
- public long elapsedTime() {
- return getLastUpdate() - getSubmittedTime();
- }
-
- /**
- * @return the serialized result if any, otherwise null
- */
- public byte[] getResult() {
- return result.get();
- }
-
- /**
- * The procedure may leave a "result" on completion.
- *
- * @param result the serialized result that will be passed to the client
- */
- protected void setResult(byte[] result) {
- this.result.set(result);
- }
-
- /**
- * Will only be called when loading procedures from procedure store, where we need to record
- * whether the procedure has already held a lock. Later we will call {@link #restoreLock(Object)}
- * to actually acquire the lock.
- */
- final void lockedWhenLoading() {
- this.lockedWhenLoading = true;
- }
-
- /**
- * Can only be called when restarting, before the procedure actually being executed, as after we
- * actually call the {@link #doAcquireLock(Object, IProcedureStore)} method, we will reset {@link
- * #lockedWhenLoading} to false.
- *
- * <p>Now it is only used in the ProcedureScheduler to determine whether we should put a Procedure
- * in front of a queue.
- */
- public boolean isLockedWhenLoading() {
- return lockedWhenLoading;
- }
-
- // ==============================================================================================
- // Runtime state, updated every operation by the ProcedureExecutor
- //
- // There is always 1 thread at the time operating on the state of the procedure.
- // The ProcedureExecutor may check and set states, or some Procecedure may
- // update its own state. but no concurrent updates. we use synchronized here
- // just because the procedure can get scheduled on different executor threads on each step.
- // ==============================================================================================
-
- /**
- * @return true if the procedure is in a RUNNABLE state.
- */
- public synchronized boolean isRunnable() {
- return state == ProcedureState.RUNNABLE;
- }
-
- public synchronized boolean isInitializing() {
- return state == ProcedureState.INITIALIZING;
- }
-
- /**
- * @return true if the procedure has failed. It may or may not have rolled back.
- */
- public synchronized boolean isFailed() {
- return state == ProcedureState.FAILED || state == ProcedureState.ROLLEDBACK;
- }
-
- /**
- * @return true if the procedure is finished successfully.
- */
- public synchronized boolean isSuccess() {
- return state == ProcedureState.SUCCESS && !hasException();
- }
-
- /**
- * @return true if the procedure is finished. The Procedure may be completed successfully or
- * rolledback.
- */
- public synchronized boolean isFinished() {
- return isSuccess() || state == ProcedureState.ROLLEDBACK;
- }
-
- /**
- * @return true if the procedure is waiting for a child to finish or for an external event.
- */
- public synchronized boolean isWaiting() {
- switch (state) {
- case WAITING:
- case WAITING_TIMEOUT:
- return true;
- default:
- break;
- }
- return false;
- }
-
- protected synchronized void setState(final ProcedureState state) {
- this.state = state;
- updateTimestamp();
- }
-
- public synchronized ProcedureState getState() {
- return state;
- }
-
- protected synchronized void setFailure(final String source, final Throwable cause) {
- setFailure(new ProcedureException(source, cause));
- }
-
- protected synchronized void setFailure(final ProcedureException exception) {
- this.exception = exception;
- if (!isFinished()) {
- setState(ProcedureState.FAILED);
- }
- }
-
- /**
- * Called by the ProcedureExecutor when the timeout set by setTimeout() is expired.
- *
- * @return true to let the framework handle the timeout as abort, false in case the procedure
- * handled the timeout itself.
- */
- protected synchronized boolean setTimeoutFailure(Env env) {
- if (state == ProcedureState.WAITING_TIMEOUT) {
- long timeDiff = System.currentTimeMillis() - lastUpdate;
- setFailure(
- "ProcedureExecutor",
- new ProcedureException("Operation timed out after " + timeDiff + " ms."));
- return true;
- }
- return false;
- }
-
- public synchronized boolean hasException() {
- return exception != null;
- }
-
- public synchronized ProcedureException getException() {
- return exception;
- }
-
- /** Called by the ProcedureExecutor on procedure-load to restore the latch state */
- protected synchronized void setChildrenLatch(int numChildren) {
- this.childrenLatch = numChildren;
- if (LOG.isTraceEnabled()) {
- LOG.trace("CHILD LATCH INCREMENT SET " + this.childrenLatch, new Throwable(this.toString()));
- }
- }
-
- /** Called by the ProcedureExecutor on procedure-load to restore the latch state */
- protected synchronized void incChildrenLatch() {
- // TODO: can this be inferred from the stack? I think so...
- this.childrenLatch++;
- if (LOG.isTraceEnabled()) {
- LOG.trace("CHILD LATCH INCREMENT " + this.childrenLatch, new Throwable(this.toString()));
- }
- }
-
- /** Called by the ProcedureExecutor to notify that one of the sub-procedures has completed. */
- private synchronized boolean childrenCountDown() {
- assert childrenLatch > 0 : this;
- boolean b = --childrenLatch == 0;
- if (LOG.isTraceEnabled()) {
- LOG.trace("CHILD LATCH DECREMENT " + childrenLatch, new Throwable(this.toString()));
- }
- return b;
- }
-
- /**
- * Try to set this procedure into RUNNABLE state. Succeeds if all subprocedures/children are done.
- *
- * @return True if we were able to move procedure to RUNNABLE state.
- */
- synchronized boolean tryRunnable() {
- // Don't use isWaiting in the below; it returns true for WAITING and WAITING_TIMEOUT
- if (getState() == ProcedureState.WAITING && childrenCountDown()) {
- setState(ProcedureState.RUNNABLE);
- return true;
- } else {
- return false;
- }
- }
-
- protected synchronized boolean hasChildren() {
- return childrenLatch > 0;
- }
-
- protected synchronized int getChildrenLatch() {
- return childrenLatch;
- }
-
- /**
- * Called by the RootProcedureState on procedure execution. Each procedure store its stack-index
- * positions.
- */
- protected synchronized void addStackIndex(final int index) {
- if (stackIndexes == null) {
- stackIndexes = new int[] {index};
- } else {
- int count = stackIndexes.length;
- stackIndexes = Arrays.copyOf(stackIndexes, count + 1);
- stackIndexes[count] = index;
- }
- }
-
- protected synchronized boolean removeStackIndex() {
- if (stackIndexes != null && stackIndexes.length > 1) {
- stackIndexes = Arrays.copyOf(stackIndexes, stackIndexes.length - 1);
- return false;
- } else {
- stackIndexes = null;
- return true;
- }
- }
-
- /**
- * Called on store load to initialize the Procedure internals after the creation/deserialization.
- */
- protected synchronized void setStackIndexes(final List<Integer> stackIndexes) {
- this.stackIndexes = new int[stackIndexes.size()];
- for (int i = 0; i < this.stackIndexes.length; ++i) {
- this.stackIndexes[i] = stackIndexes.get(i);
- }
- }
-
- protected synchronized boolean wasExecuted() {
- return stackIndexes != null;
- }
-
- protected synchronized int[] getStackIndexes() {
- return stackIndexes;
- }
-
- public void setRootProcedureId(long rootProcedureId) {
- this.rootProcId = rootProcedureId;
- }
-
- /**
- * @param a the first procedure to be compared.
- * @param b the second procedure to be compared.
- * @return true if the two procedures have the same parent
- */
- public static boolean haveSameParent(Procedure<?> a, Procedure<?> b) {
- return a.hasParent() && b.hasParent() && (a.getParentProcId() == b.getParentProcId());
- }
-
- @Override
- public int compareTo(Procedure<Env> other) {
- return Long.compare(getProcId(), other.getProcId());
- }
-
- /**
- * This function will be called just when procedure is submitted for execution.
- *
- * @param env The environment passed to the procedure executor
- */
- protected void updateMetricsOnSubmit(Env env) {
- if (env instanceof ConfigNodeProcedureEnv) {
- ((ConfigNodeProcedureEnv) env)
- .getConfigManager()
- .getProcedureManager()
- .getProcedureMetrics()
- .updateMetricsOnSubmit(getProcType());
- }
- }
-
- /**
- * This function will be called just after procedure execution is finished. Override this method
- * to update metrics at the end of the procedure. The default implementation adds runtime of a
- * procedure to a time histogram for successfully completed procedures. Increments failed counter
- * for failed procedures.
- *
- * @param env The environment passed to the procedure executor
- * @param runtime Runtime of the procedure in milliseconds
- * @param success true if procedure is completed successfully
- */
- protected void updateMetricsOnFinish(Env env, long runtime, boolean success) {
- if (env instanceof ConfigNodeProcedureEnv) {
- ((ConfigNodeProcedureEnv) env)
- .getConfigManager()
- .getProcedureManager()
- .getProcedureMetrics()
- .updateMetricsOnFinish(getProcType(), runtime, success);
- }
- }
-}
+1/*
+1 * Licensed to the Apache Software Foundation (ASF) under one
+1 * or more contributor license agreements. See the NOTICE file
+1 * distributed with this work for additional information
+1 * regarding copyright ownership. The ASF licenses this file
+1 * to you under the Apache License, Version 2.0 (the
+1 * "License"); you may not use this file except in compliance
+1 * with the License. You may obtain a copy of the License at
+1 *
+1 * http://www.apache.org/licenses/LICENSE-2.0
+1 *
+1 * Unless required by applicable law or agreed to in writing,
+1 * software distributed under the License is distributed on an
+1 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+1 * KIND, either express or implied. See the License for the
+1 * specific language governing permissions and limitations
+1 * under the License.
+1 */
+1
+1package org.apache.iotdb.confignode.procedure;
+1
+1import org.apache.iotdb.confignode.procedure.env.ConfigNodeProcedureEnv;
+1import org.apache.iotdb.confignode.procedure.exception.ProcedureException;
+1import org.apache.iotdb.confignode.procedure.state.ProcedureLockState;
+1import org.apache.iotdb.confignode.procedure.state.ProcedureState;
+1import org.apache.iotdb.confignode.procedure.store.IProcedureStore;
+1
+1import org.slf4j.Logger;
+1import org.slf4j.LoggerFactory;
+1
+1import java.io.DataOutputStream;
+1import java.io.IOException;
+1import java.nio.ByteBuffer;
+1import java.nio.charset.StandardCharsets;
+1import java.util.ArrayList;
+1import java.util.Arrays;
+1import java.util.List;
+1import java.util.concurrent.atomic.AtomicReference;
+1
+1/**
+1 * Abstract class of all procedures.
+1 *
+1 * @param <Env>
+1 */
+1public abstract class Procedure<Env> implements Comparable<Procedure<Env>> {
+1 private static final Logger LOG = LoggerFactory.getLogger(Procedure.class);
+1 public static final long NO_PROC_ID = -1;
+1 public static final long NO_TIMEOUT = -1;
+1
+1 private long parentProcId = NO_PROC_ID;
+1 private long rootProcId = NO_PROC_ID;
+1 private long procId = NO_PROC_ID;
+1 private long submittedTime;
+1
+1 private ProcedureState state = ProcedureState.INITIALIZING;
+1 private int childrenLatch = 0;
+1 private ProcedureException exception;
+1
+1 private volatile long timeout = NO_TIMEOUT;
+1 private volatile long lastUpdate;
+1
+1 private final AtomicReference<byte[]> result = new AtomicReference<>();
+1 private volatile boolean locked = false;
+1 private boolean lockedWhenLoading = false;
+1
+1 private int[] stackIndexes = null;
+1
+1 public final boolean hasLock() {
+1 return locked;
+1 }
+1
+1 // User level code, override it if necessary
+1
+1 /**
+1 * The main code of the procedure. It must be idempotent since execute() may be called multiple
+1 * times in case of machine failure in the middle of the execution.
+1 *
+1 * @param env the environment passed to the ProcedureExecutor
+1 * @return a set of sub-procedures to run or ourselves if there is more work to do or null if the
+1 * procedure is done.
+1 * @throws InterruptedException the procedure will be added back to the queue and retried later.
+1 */
+1 protected abstract Procedure<Env>[] execute(Env env) throws InterruptedException;
+1
+1 /**
+1 * The code to undo what was done by the execute() code. It is called when the procedure or one of
+1 * the sub-procedures failed or an abort was requested. It should cleanup all the resources
+1 * created by the execute() call. The implementation must be idempotent since rollback() may be
+1 * called multiple time in case of machine failure in the middle of the execution.
+1 *
+1 * @param env the environment passed to the ProcedureExecutor
+1 * @throws IOException temporary failure, the rollback will retry later
+1 * @throws InterruptedException the procedure will be added back to the queue and retried later
+1 */
+1 protected abstract void rollback(Env env)
+1 throws IOException, InterruptedException, ProcedureException;
+1
+1 public void serialize(DataOutputStream stream) throws IOException {
+1 // procid
+1 stream.writeLong(this.procId);
+1 // state
+1 stream.writeInt(this.state.ordinal());
+1 // submit time
+1 stream.writeLong(this.submittedTime);
+1 // last updated
+1 stream.writeLong(this.lastUpdate);
+1 // parent id
+1 stream.writeLong(this.parentProcId);
+1 // time out
+1 stream.writeLong(this.timeout);
+1 // stack indexes
+1 if (stackIndexes != null) {
+1 stream.writeInt(stackIndexes.length);
+1 for (int index : stackIndexes) {
+1 stream.writeInt(index);
+1 }
+1 } else {
+1 stream.writeInt(-1);
+1 }
+1
+1 // exceptions
+1 if (hasException()) {
+1 // symbol of exception
+1 stream.write((byte) 1);
+1 // exception's name
+1 String exceptionClassName = exception.getClass().getName();
+1 byte[] exceptionClassNameBytes = exceptionClassName.getBytes(StandardCharsets.UTF_8);
+1 stream.writeInt(exceptionClassNameBytes.length);
+1 stream.write(exceptionClassNameBytes);
+1 // exception's message
+1 String message = this.exception.getMessage();
+1 if (message != null) {
+1 byte[] messageBytes = message.getBytes(StandardCharsets.UTF_8);
+1 stream.writeInt(messageBytes.length);
+1 stream.write(messageBytes);
+1 } else {
+1 stream.writeInt(-1);
+1 }
+1 } else {
+1 // symbol of no exception
+1 stream.write((byte) 0);
+1 }
+1
+1 // result
+1 if (result.get() != null) {
+1 stream.writeInt(result.get().length);
+1 stream.write(result.get());
+1 } else {
+1 stream.writeInt(-1);
+1 }
+1
+1 // Has lock
+1 stream.write(this.hasLock() ? (byte) 1 : (byte) 0);
+1 }
+1
+1 public void deserialize(ByteBuffer byteBuffer) {
+1 // procid
+1 this.setProcId(byteBuffer.getLong());
+1 // state
+1 this.setState(ProcedureState.values()[byteBuffer.getInt()]);
+1 // submit time
+1 this.setSubmittedTime(byteBuffer.getLong());
+1 // last updated
+1 this.setLastUpdate(byteBuffer.getLong());
+1 // parent id
+1 this.setParentProcId(byteBuffer.getLong());
+1 // time out
+1 this.setTimeout(byteBuffer.getLong());
+1 // stack index
+1 int stackIndexesLen = byteBuffer.getInt();
+1 if (stackIndexesLen >= 0) {
+1 List<Integer> indexList = new ArrayList<>(stackIndexesLen);
+1 for (int i = 0; i < stackIndexesLen; i++) {
+1 indexList.add(byteBuffer.getInt());
+1 }
+1 this.setStackIndexes(indexList);
+1 }
+1
+1 // exception
+1 if (byteBuffer.get() == 1) {
+1 deserializeTypeInfoForCompatibility(byteBuffer);
+1 int messageBytesLength = byteBuffer.getInt();
+1 String errMsg = null;
+1 if (messageBytesLength > 0) {
+1 byte[] messageBytes = new byte[messageBytesLength];
+1 byteBuffer.get(messageBytes);
+1 errMsg = new String(messageBytes, StandardCharsets.UTF_8);
+1 }
+1 setFailure(new ProcedureException(errMsg));
+1 }
+1
+1 // result
+1 int resultLen = byteBuffer.getInt();
+1 if (resultLen > 0) {
+1 byte[] resultArr = new byte[resultLen];
+1 byteBuffer.get(resultArr);
+1 }
+1 // has lock
+1 if (byteBuffer.get() == 1) {
+1 this.lockedWhenLoading();
+1 }
+1 }
+1
+1 /**
+1 * Deserialize class Name and load class
+1 *
+1 * @param byteBuffer bytebuffer
+1 * @return Procedure
+1 */
+1 @Deprecated
+1 public static void deserializeTypeInfoForCompatibility(ByteBuffer byteBuffer) {
+1 int classNameBytesLen = byteBuffer.getInt();
+1 byte[] classNameBytes = new byte[classNameBytesLen];
+1 byteBuffer.get(classNameBytes);
+1 }
+1
+1 /**
+1 * Acquire a lock, user should override it if necessary.
+1 *
+1 * @param env environment
+1 * @return state of lock
+1 */
+1 protected ProcedureLockState acquireLock(Env env) {
+1 return ProcedureLockState.LOCK_ACQUIRED;
+1 }
+1
+1 /**
+1 * Release a lock, user should override it if necessary.
+1 *
+1 * @param env env
+1 */
+1 protected void releaseLock(Env env) {
+1 // no op
+1 }
+1
+1 /**
+1 * Used to keep procedure lock even when the procedure is yielded or suspended.
+1 *
+1 * @param env env
+1 * @return true if hold the lock
+1 */
+1 protected boolean holdLock(Env env) {
+1 return false;
+1 }
+1
+1 /**
+1 * To make executor yield between each execution step to give other procedures a chance to run.
+1 *
+1 * @param env environment
+1 * @return return true if yield is allowed.
+1 */
+1 protected boolean isYieldAfterExecution(Env env) {
+1 return false;
+1 }
+1
+1 // -------------------------Internal methods - called by the procedureExecutor------------------
+1 /**
+1 * Internal method called by the ProcedureExecutor that starts the user-level code execute().
+1 *
+1 * @param env execute environment
+1 * @return sub procedures
+1 */
+1 protected Procedure<Env>[] doExecute(Env env) throws InterruptedException {
+1 try {
+1 updateTimestamp();
+1 return execute(env);
+1 } finally {
+1 updateTimestamp();
+1 }
+1 }
+1
+1 /**
+1 * Internal method called by the ProcedureExecutor that starts the user-level code rollback().
+1 *
+1 * @param env execute environment
+1 * @throws IOException ioe
+1 * @throws InterruptedException interrupted exception
+1 */
+1 public void doRollback(Env env) throws IOException, InterruptedException, ProcedureException {
+1 try {
+1 updateTimestamp();
+1 rollback(env);
+1 } finally {
+1 updateTimestamp();
+1 }
+1 }
+1
+1 /**
+1 * Internal method called by the ProcedureExecutor that starts the user-level code acquireLock().
+1 *
+1 * @param env environment
+1 * @param store ProcedureStore
+1 * @return ProcedureLockState
+1 */
+1 public final ProcedureLockState doAcquireLock(Env env, IProcedureStore store) {
+1 if (lockedWhenLoading) {
+1 lockedWhenLoading = false;
+1 locked = true;
+1 return ProcedureLockState.LOCK_ACQUIRED;
+1 }
+1 ProcedureLockState state = acquireLock(env);
+1 if (state == ProcedureLockState.LOCK_ACQUIRED) {
+1 locked = true;
+1 store.update(this);
+1 }
+1 return state;
+1 }
+1
+1 /**
+1 * Presist lock state of the procedure
+1 *
+1 * @param env environment
+1 * @param store ProcedureStore
+1 */
+1 public final void doReleaseLock(Env env, IProcedureStore store) {
+1 locked = false;
+1 if (getState() != ProcedureState.ROLLEDBACK) {
+1 store.update(this);
+1 }
+1 releaseLock(env);
+1 }
+1
+1 public final void restoreLock(Env env) {
+1 if (!lockedWhenLoading) {
+1 LOG.debug("{} didn't hold the lock before restarting, skip acquiring lock", this);
+1 return;
+1 }
+1 if (isFinished()) {
+1 LOG.debug("{} is already bypassed, skip acquiring lock.", this);
+1 return;
+1 }
+1 if (getState() == ProcedureState.WAITING && !holdLock(env)) {
+1 LOG.debug("{} is in WAITING STATE, and holdLock= false , skip acquiring lock.", this);
+1 return;
+1 }
+1 LOG.debug("{} held the lock before restarting, call acquireLock to restore it.", this);
+1 acquireLock(env);
+1 }
+1
+1 @Override
+1 public String toString() {
+1 // Return the simple String presentation of the procedure.
+1 return toStringSimpleSB().toString();
+1 }
+1
+1 /**
+1 * Build the StringBuilder for the simple form of procedure string.
+1 *
+1 * @return the StringBuilder
+1 */
+1 protected StringBuilder toStringSimpleSB() {
+1 final StringBuilder sb = new StringBuilder();
+1
+1 sb.append("pid=");
+1 sb.append(getProcId());
+1
+1 if (hasParent()) {
+1 sb.append(", ppid=");
+1 sb.append(getParentProcId());
+1 }
+1
+1 /*
+1 * TODO
+1 * Enable later when this is being used.
+1 * Currently owner not used.
+1 if (hasOwner()) {
+1 sb.append(", owner=");
+1 sb.append(getOwner());
+1 }*/
+1
+1 sb.append(", state="); // pState for Procedure State as opposed to any other kind.
+1 toStringState(sb);
+1
+1 // Only print out locked if actually locked. Most of the time it is not.
+1 if (this.locked) {
+1 sb.append(", locked=").append(locked);
+1 }
+1 if (hasException()) {
+1 sb.append(", exception=" + getException());
+1 }
+1
+1 sb.append("; ");
+1 toStringClassDetails(sb);
+1
+1 return sb;
+1 }
+1
+1 /** Extend the toString() information with more procedure details */
+1 public String toStringDetails() {
+1 final StringBuilder sb = toStringSimpleSB();
+1
+1 sb.append(" submittedTime=");
+1 sb.append(getSubmittedTime());
+1
+1 sb.append(", lastUpdate=");
+1 sb.append(getLastUpdate());
+1
+1 final int[] stackIndices = getStackIndexes();
+1 if (stackIndices != null) {
+1 sb.append("\n");
+1 sb.append("stackIndexes=");
+1 sb.append(Arrays.toString(stackIndices));
+1 }
+1
+1 return sb.toString();
+1 }
+1
+1 /**
+1 * Called from {@link #toString()} when interpolating {@link Procedure} State. Allows decorating
+1 * generic Procedure State with Procedure particulars.
+1 *
+1 * @param builder Append current {@link ProcedureState}
+1 */
+1 protected void toStringState(StringBuilder builder) {
+1 builder.append(getState());
+1 }
+1
+1 /**
+1 * Extend the toString() information with the procedure details e.g. className and parameters
+1 *
+1 * @param builder the string builder to use to append the proc specific information
+1 */
+1 protected void toStringClassDetails(StringBuilder builder) {
+1 builder.append(getClass().getName());
+1 }
+1
+1 // ==========================================================================
+1 // Those fields are unchanged after initialization.
+1 //
+1 // Each procedure will get created from the user or during
+1 // ProcedureExecutor.start() during the load() phase and then submitted
+1 // to the executor. these fields will never be changed after initialization
+1 // ==========================================================================
+1 public long getProcId() {
+1 return procId;
+1 }
+1
+1 public boolean hasParent() {
+1 return parentProcId != NO_PROC_ID;
+1 }
+1
+1 public long getParentProcId() {
+1 return parentProcId;
+1 }
+1
+1 public long getRootProcId() {
+1 return rootProcId;
+1 }
+1
+1 public String getProcType() {
+1 return getClass().getSimpleName();
+1 }
+1
+1 public long getSubmittedTime() {
+1 return submittedTime;
+1 }
+1
+1 /** Called by the ProcedureExecutor to assign the ID to the newly created procedure. */
+1 public void setProcId(long procId) {
+1 this.procId = procId;
+1 }
+1
+1 public void setProcRunnable() {
+1 this.submittedTime = System.currentTimeMillis();
+1 setState(ProcedureState.RUNNABLE);
+1 }
+1
+1 /** Called by the ProcedureExecutor to assign the parent to the newly created procedure. */
+1 protected void setParentProcId(long parentProcId) {
+1 this.parentProcId = parentProcId;
+1 }
+1
+1 protected void setRootProcId(long rootProcId) {
+1 this.rootProcId = rootProcId;
+1 }
+1
+1 /**
+1 * Called on store load to initialize the Procedure internals after the creation/deserialization.
+1 */
+1 protected void setSubmittedTime(long submittedTime) {
+1 this.submittedTime = submittedTime;
+1 }
+1
+1 // ==========================================================================
+1 // runtime state - timeout related
+1 // ==========================================================================
+1 /**
+1 * @param timeout timeout interval in msec
+1 */
+1 protected void setTimeout(long timeout) {
+1 this.timeout = timeout;
+1 }
+1
+1 public boolean hasTimeout() {
+1 return timeout != NO_TIMEOUT;
+1 }
+1
+1 /**
+1 * @return the timeout in msec
+1 */
+1 public long getTimeout() {
+1 return timeout;
+1 }
+1
+1 /**
+1 * Called on store load to initialize the Procedure internals after the creation/deserialization.
+1 */
+1 protected void setLastUpdate(long lastUpdate) {
+1 this.lastUpdate = lastUpdate;
+1 }
+1
+1 /** Called by ProcedureExecutor after each time a procedure step is executed. */
+1 protected void updateTimestamp() {
+1 this.lastUpdate = System.currentTimeMillis();
+1 }
+1
+1 public long getLastUpdate() {
+1 return lastUpdate;
+1 }
+1
+1 /**
+1 * Timeout of the next timeout. Called by the ProcedureExecutor if the procedure has timeout set
+1 * and the procedure is in the waiting queue.
+1 *
+1 * @return the timestamp of the next timeout.
+1 */
+1 protected long getTimeoutTimestamp() {
+1 return getLastUpdate() + getTimeout();
+1 }
+1
+1 // ==========================================================================
+1 // runtime state
+1 // ==========================================================================
+1 /**
+1 * @return the time elapsed between the last update and the start time of the procedure.
+1 */
+1 public long elapsedTime() {
+1 return getLastUpdate() - getSubmittedTime();
+1 }
+1
+1 /**
+1 * @return the serialized result if any, otherwise null
+1 */
+1 public byte[] getResult() {
+1 return result.get();
+1 }
+1
+1 /**
+1 * The procedure may leave a "result" on completion.
+1 *
+1 * @param result the serialized result that will be passed to the client
+1 */
+1 protected void setResult(byte[] result) {
+1 this.result.set(result);
+1 }
+1
+1 /**
+1 * Will only be called when loading procedures from procedure store, where we need to record
+1 * whether the procedure has already held a lock. Later we will call {@link #restoreLock(Object)}
+1 * to actually acquire the lock.
+1 */
+1 final void lockedWhenLoading() {
+1 this.lockedWhenLoading = true;
+1 }
+1
+1 /**
+1 * Can only be called when restarting, before the procedure actually being executed, as after we
+1 * actually call the {@link #doAcquireLock(Object, IProcedureStore)} method, we will reset {@link
+1 * #lockedWhenLoading} to false.
+1 *
+1 * <p>Now it is only used in the ProcedureScheduler to determine whether we should put a Procedure
+1 * in front of a queue.
+1 */
+1 public boolean isLockedWhenLoading() {
+1 return lockedWhenLoading;
+1 }
+1
+1 // ==============================================================================================
+1 // Runtime state, updated every operation by the ProcedureExecutor
+1 //
+1 // There is always 1 thread at the time operating on the state of the procedure.
+1 // The ProcedureExecutor may check and set states, or some Procecedure may
+1 // update its own state. but no concurrent updates. we use synchronized here
+1 // just because the procedure can get scheduled on different executor threads on each step.
+1 // ==============================================================================================
+1
+1 /**
+1 * @return true if the procedure is in a RUNNABLE state.
+1 */
+1 public synchronized boolean isRunnable() {
+1 return state == ProcedureState.RUNNABLE;
+1 }
+1
+1 public synchronized boolean isInitializing() {
+1 return state == ProcedureState.INITIALIZING;
+1 }
+1
+1 /**
+1 * @return true if the procedure has failed. It may or may not have rolled back.
+1 */
+1 public synchronized boolean isFailed() {
+1 return state == ProcedureState.FAILED || state == ProcedureState.ROLLEDBACK;
+1 }
+1
+1 /**
+1 * @return true if the procedure is finished successfully.
+1 */
+1 public synchronized boolean isSuccess() {
+1 return state == ProcedureState.SUCCESS && !hasException();
+1 }
+1
+1 /**
+1 * @return true if the procedure is finished. The Procedure may be completed successfully or
+1 * rolledback.
+1 */
+1 public synchronized boolean isFinished() {
+1 return isSuccess() || state == ProcedureState.ROLLEDBACK;
+1 }
+1
+1 /**
+1 * @return true if the procedure is waiting for a child to finish or for an external event.
+1 */
+1 public synchronized boolean isWaiting() {
+1 switch (state) {
+1 case WAITING:
+1 case WAITING_TIMEOUT:
+1 return true;
+1 default:
+1 break;
+1 }
+1 return false;
+1 }
+1
+1 protected synchronized void setState(final ProcedureState state) {
+1 this.state = state;
+1 updateTimestamp();
+1 }
+1
+1 public synchronized ProcedureState getState() {
+1 return state;
+1 }
+1
+1 protected synchronized void setFailure(final String source, final Throwable cause) {
+1 setFailure(new ProcedureException(source, cause));
+1 }
+1
+1 protected synchronized void setFailure(final ProcedureException exception) {
+1 this.exception = exception;
+1 if (!isFinished()) {
+1 setState(ProcedureState.FAILED);
+1 }
+1 }
+1
+1 /**
+1 * Called by the ProcedureExecutor when the timeout set by setTimeout() is expired.
+1 *
+1 * @return true to let the framework handle the timeout as abort, false in case the procedure
+1 * handled the timeout itself.
+1 */
+1 protected synchronized boolean setTimeoutFailure(Env env) {
+1 if (state == ProcedureState.WAITING_TIMEOUT) {
+1 long timeDiff = System.currentTimeMillis() - lastUpdate;
+1 setFailure(
+1 "ProcedureExecutor",
+1 new ProcedureException("Operation timed out after " + timeDiff + " ms."));
+1 return true;
+1 }
+1 return false;
+1 }
+1
+1 public synchronized boolean hasException() {
+1 return exception != null;
+1 }
+1
+1 public synchronized ProcedureException getException() {
+1 return exception;
+1 }
+1
+1 /** Called by the ProcedureExecutor on procedure-load to restore the latch state */
+1 protected synchronized void setChildrenLatch(int numChildren) {
+1 this.childrenLatch = numChildren;
+1 if (LOG.isTraceEnabled()) {
+1 LOG.trace("CHILD LATCH INCREMENT SET " + this.childrenLatch, new Throwable(this.toString()));
+1 }
+1 }
+1
+1 /** Called by the ProcedureExecutor on procedure-load to restore the latch state */
+1 protected synchronized void incChildrenLatch() {
+1 // TODO: can this be inferred from the stack? I think so...
+1 this.childrenLatch++;
+1 if (LOG.isTraceEnabled()) {
+1 LOG.trace("CHILD LATCH INCREMENT " + this.childrenLatch, new Throwable(this.toString()));
+1 }
+1 }
+1
+1 /** Called by the ProcedureExecutor to notify that one of the sub-procedures has completed. */
+1 private synchronized boolean childrenCountDown() {
+1 assert childrenLatch > 0 : this;
+1 boolean b = --childrenLatch == 0;
+1 if (LOG.isTraceEnabled()) {
+1 LOG.trace("CHILD LATCH DECREMENT " + childrenLatch, new Throwable(this.toString()));
+1 }
+1 return b;
+1 }
+1
+1 /**
+1 * Try to set this procedure into RUNNABLE state. Succeeds if all subprocedures/children are done.
+1 *
+1 * @return True if we were able to move procedure to RUNNABLE state.
+1 */
+1 synchronized boolean tryRunnable() {
+1 // Don't use isWaiting in the below; it returns true for WAITING and WAITING_TIMEOUT
+1 if (getState() == ProcedureState.WAITING && childrenCountDown()) {
+1 setState(ProcedureState.RUNNABLE);
+1 return true;
+1 } else {
+1 return false;
+1 }
+1 }
+1
+1 protected synchronized boolean hasChildren() {
+1 return childrenLatch > 0;
+1 }
+1
+1 protected synchronized int getChildrenLatch() {
+1 return childrenLatch;
+1 }
+1
+1 /**
+1 * Called by the RootProcedureState on procedure execution. Each procedure store its stack-index
+1 * positions.
+1 */
+1 protected synchronized void addStackIndex(final int index) {
+1 if (stackIndexes == null) {
+1 stackIndexes = new int[] {index};
+1 } else {
+1 int count = stackIndexes.length;
+1 stackIndexes = Arrays.copyOf(stackIndexes, count + 1);
+1 stackIndexes[count] = index;
+1 }
+1 }
+1
+1 protected synchronized boolean removeStackIndex() {
+1 if (stackIndexes != null && stackIndexes.length > 1) {
+1 stackIndexes = Arrays.copyOf(stackIndexes, stackIndexes.length - 1);
+1 return false;
+1 } else {
+1 stackIndexes = null;
+1 return true;
+1 }
+1 }
+1
+1 /**
+1 * Called on store load to initialize the Procedure internals after the creation/deserialization.
+1 */
+1 protected synchronized void setStackIndexes(final List<Integer> stackIndexes) {
+1 this.stackIndexes = new int[stackIndexes.size()];
+1 for (int i = 0; i < this.stackIndexes.length; ++i) {
+1 this.stackIndexes[i] = stackIndexes.get(i);
+1 }
+1 }
+1
+1 protected synchronized boolean wasExecuted() {
+1 return stackIndexes != null;
+1 }
+1
+1 protected synchronized int[] getStackIndexes() {
+1 return stackIndexes;
+1 }
+1
+1 public void setRootProcedureId(long rootProcedureId) {
+1 this.rootProcId = rootProcedureId;
+1 }
+1
+1 /**
+1 * @param a the first procedure to be compared.
+1 * @param b the second procedure to be compared.
+1 * @return true if the two procedures have the same parent
+1 */
+1 public static boolean haveSameParent(Procedure<?> a, Procedure<?> b) {
+1 return a.hasParent() && b.hasParent() && (a.getParentProcId() == b.getParentProcId());
+1 }
+1
+1 @Override
+1 public int compareTo(Procedure<Env> other) {
+1 return Long.compare(getProcId(), other.getProcId());
+1 }
+1
+1 /**
+1 * This function will be called just when procedure is submitted for execution.
+1 *
+1 * @param env The environment passed to the procedure executor
+1 */
+1 protected void updateMetricsOnSubmit(Env env) {
+1 if (env instanceof ConfigNodeProcedureEnv) {
+1 ((ConfigNodeProcedureEnv) env)
+1 .getConfigManager()
+1 .getProcedureManager()
+1 .getProcedureMetrics()
+1 .updateMetricsOnSubmit(getProcType());
+1 }
+1 }
+1
+1 /**
+1 * This function will be called just after procedure execution is finished. Override this method
+1 * to update metrics at the end of the procedure. The default implementation adds runtime of a
+1 * procedure to a time histogram for successfully completed procedures. Increments failed counter
+1 * for failed procedures.
+1 *
+1 * @param env The environment passed to the procedure executor
+1 * @param runtime Runtime of the procedure in milliseconds
+1 * @param success true if procedure is completed successfully
+1 */
+1 protected void updateMetricsOnFinish(Env env, long runtime, boolean success) {
+1 if (env instanceof ConfigNodeProcedureEnv) {
+1 ((ConfigNodeProcedureEnv) env)
+1 .getConfigManager()
+1 .getProcedureManager()
+1 .getProcedureMetrics()
+1 .updateMetricsOnFinish(getProcType(), runtime, success);
+1 }
+1 }
+1}
+1
\ No newline at end of file
diff --git a/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/procedure/ProcedureExecutor.java b/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/procedure/ProcedureExecutor.java
index 0d8368583b4..4c7421885f7 100644
--- a/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/procedure/ProcedureExecutor.java
+++ b/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/procedure/ProcedureExecutor.java
@@ -1,940 +1,941 @@
-/*
- * 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.confignode.procedure;
-
-import org.apache.iotdb.commons.concurrent.ThreadName;
-import org.apache.iotdb.commons.utils.TestOnly;
-import org.apache.iotdb.confignode.procedure.env.ConfigNodeProcedureEnv;
-import org.apache.iotdb.confignode.procedure.exception.ProcedureException;
-import org.apache.iotdb.confignode.procedure.scheduler.ProcedureScheduler;
-import org.apache.iotdb.confignode.procedure.scheduler.SimpleProcedureScheduler;
-import org.apache.iotdb.confignode.procedure.state.ProcedureLockState;
-import org.apache.iotdb.confignode.procedure.state.ProcedureState;
-import org.apache.iotdb.confignode.procedure.store.IProcedureStore;
-
-import com.google.common.base.Preconditions;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-import java.io.IOException;
-import java.util.ArrayDeque;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Deque;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Objects;
-import java.util.Set;
-import java.util.concurrent.ConcurrentHashMap;
-import java.util.concurrent.CopyOnWriteArrayList;
-import java.util.concurrent.TimeUnit;
-import java.util.concurrent.atomic.AtomicBoolean;
-import java.util.concurrent.atomic.AtomicInteger;
-import java.util.concurrent.atomic.AtomicLong;
-import java.util.concurrent.atomic.AtomicReference;
-
-import static org.apache.iotdb.confignode.procedure.Procedure.NO_PROC_ID;
-
-public class ProcedureExecutor<Env> {
- private static final Logger LOG = LoggerFactory.getLogger(ProcedureExecutor.class);
-
- private final ConcurrentHashMap<Long, CompletedProcedureContainer<Env>> completed =
- new ConcurrentHashMap<>();
-
- private final ConcurrentHashMap<Long, RootProcedureStack<Env>> rollbackStack =
- new ConcurrentHashMap<>();
-
- private final ConcurrentHashMap<Long, Procedure<Env>> procedures = new ConcurrentHashMap<>();
-
- private ThreadGroup threadGroup;
-
- private CopyOnWriteArrayList<WorkerThread> workerThreads;
-
- private TimeoutExecutorThread<Env> timeoutExecutor;
-
- private TimeoutExecutorThread<Env> workerMonitorExecutor;
-
- private int corePoolSize;
- private int maxPoolSize;
-
- private final ProcedureScheduler scheduler;
-
- private final AtomicLong workId = new AtomicLong(0);
- private final AtomicInteger activeExecutorCount = new AtomicInteger(0);
- private final AtomicBoolean running = new AtomicBoolean(false);
- private final Env environment;
- private final IProcedureStore<Env> store;
-
- public ProcedureExecutor(
- final Env environment, final IProcedureStore<Env> store, final ProcedureScheduler scheduler) {
- this.environment = environment;
- this.scheduler = scheduler;
- this.store = store;
- }
-
- @TestOnly
- public ProcedureExecutor(final Env environment, final IProcedureStore<Env> store) {
- this(environment, store, new SimpleProcedureScheduler());
- }
-
- public void init(int numThreads) {
- this.corePoolSize = numThreads;
- this.maxPoolSize = 10 * numThreads;
- this.threadGroup = new ThreadGroup(ThreadName.CONFIG_NODE_PROCEDURE_WORKER.getName());
- this.timeoutExecutor =
- new TimeoutExecutorThread<>(
- this, threadGroup, ThreadName.CONFIG_NODE_TIMEOUT_EXECUTOR.getName());
- this.workerMonitorExecutor =
- new TimeoutExecutorThread<>(
- this, threadGroup, ThreadName.CONFIG_NODE_WORKER_THREAD_MONITOR.getName());
- workId.set(0);
- workerThreads = new CopyOnWriteArrayList<>();
- for (int i = 0; i < corePoolSize; i++) {
- workerThreads.add(new WorkerThread(threadGroup));
- }
- // Add worker monitor
- workerMonitorExecutor.add(new WorkerMonitor());
-
- scheduler.start();
- recover();
- }
-
- private void recover() {
- // 1.Build rollback stack
- List<Procedure<Env>> procedureList = getProcedureListFromDifferentVersion();
- // Load procedure wal file
- for (Procedure<Env> proc : procedureList) {
- if (proc.isFinished()) {
- completed.putIfAbsent(proc.getProcId(), new CompletedProcedureContainer<>(proc));
- } else {
- if (!proc.hasParent()) {
- rollbackStack.put(proc.getProcId(), new RootProcedureStack<>());
- }
- procedures.putIfAbsent(proc.getProcId(), proc);
- }
- }
- List<Procedure<Env>> runnableList = new ArrayList<>();
- List<Procedure<Env>> failedList = new ArrayList<>();
- List<Procedure<Env>> waitingList = new ArrayList<>();
- List<Procedure<Env>> waitingTimeoutList = new ArrayList<>();
- for (Procedure<Env> proc : procedureList) {
- if (proc.isFinished() && !proc.hasParent()) {
- continue;
- }
- long rootProcedureId = getRootProcedureId(proc);
- if (proc.hasParent()) {
- Procedure<Env> parent = procedures.get(proc.getParentProcId());
- if (parent != null && !proc.isFinished()) {
- parent.incChildrenLatch();
- }
- }
- RootProcedureStack<Env> rootStack = rollbackStack.get(rootProcedureId);
- if (rootStack != null) {
- rootStack.loadStack(proc);
- }
- proc.setRootProcedureId(rootProcedureId);
- switch (proc.getState()) {
- case RUNNABLE:
- runnableList.add(proc);
- break;
- case FAILED:
- failedList.add(proc);
- break;
- case WAITING:
- waitingList.add(proc);
- break;
- case WAITING_TIMEOUT:
- waitingTimeoutList.add(proc);
- break;
- case ROLLEDBACK:
- case INITIALIZING:
- LOG.error("Unexpected state:{} for {}", proc.getState(), proc);
- throw new UnsupportedOperationException("Unexpected state");
- default:
- break;
- }
- }
-
- waitingList.forEach(
- procedure -> {
- if (!procedure.hasChildren()) {
- // Normally, WAITING procedures should be wakened by its children.
- // But, there is a case that, all the children are successful, and before
- // they can wake up their parent procedure, the master was killed.
- // So, during recovering the procedures from ProcedureWal, its children
- // are not loaded because of their SUCCESS state.
- // So we need to continue to run this WAITING procedure. Before
- // executing, we need to set its state to RUNNABLE.
- procedure.setState(ProcedureState.RUNNABLE);
- runnableList.add(procedure);
- }
- });
- restoreLocks();
-
- waitingTimeoutList.forEach(timeoutExecutor::add);
-
- failedList.forEach(scheduler::addBack);
- runnableList.forEach(scheduler::addBack);
- scheduler.signalAll();
- }
-
- private List<Procedure<Env>> getProcedureListFromDifferentVersion() {
- if (store.isOldVersionProcedureStore()) {
- LOG.info("Old procedure directory detected, upgrade beginning...");
- return store.load();
- } else {
- return store.getProcedures();
- }
- }
-
- /**
- * Helper to look up the root Procedure ID.
- *
- * @param proc given a specified procedure.
- */
- Long getRootProcedureId(Procedure<Env> proc) {
- while (proc.hasParent()) {
- proc = procedures.get(proc.getParentProcId());
- if (proc == null) {
- return NO_PROC_ID;
- }
- }
- return proc.getProcId();
- }
-
- private void releaseLock(Procedure<Env> procedure, boolean force) {
- if (force || !procedure.holdLock(this.environment) || procedure.isFinished()) {
- procedure.doReleaseLock(this.environment, store);
- }
- }
-
- private void restoreLock(Procedure procedure, Set<Long> restored) {
- procedure.restoreLock(environment);
- restored.add(procedure.getProcId());
- }
-
- private void restoreLocks(Deque<Procedure<Env>> stack, Set<Long> restored) {
- while (!stack.isEmpty()) {
- restoreLock(stack.pop(), restored);
- }
- }
-
- private void restoreLocks() {
- Set<Long> restored = new HashSet<>();
- Deque<Procedure<Env>> stack = new ArrayDeque<>();
- procedures
- .values()
- .forEach(
- procedure -> {
- while (procedure != null) {
- if (restored.contains(procedure.getProcId())) {
- restoreLocks(stack, restored);
- return;
- }
- if (!procedure.hasParent()) {
- restoreLock(procedure, restored);
- restoreLocks(stack, restored);
- return;
- }
- stack.push(procedure);
- procedure = procedures.get(procedure.getParentProcId());
- }
- });
- }
-
- public void startWorkers() {
- if (!running.compareAndSet(false, true)) {
- LOG.warn("Already running");
- return;
- }
- timeoutExecutor.start();
- workerMonitorExecutor.start();
- for (WorkerThread workerThread : workerThreads) {
- workerThread.start();
- }
- LOG.info("{} procedure workers are started.", workerThreads.size());
- }
-
- public void startCompletedCleaner(long cleanTimeInterval, long cleanEvictTTL) {
- addInternalProcedure(
- new CompletedProcedureRecycler(store, completed, cleanTimeInterval, cleanEvictTTL));
- }
-
- public void addInternalProcedure(InternalProcedure interalProcedure) {
- if (interalProcedure == null) {
- return;
- }
- interalProcedure.setState(ProcedureState.WAITING_TIMEOUT);
- timeoutExecutor.add(interalProcedure);
- }
-
- public boolean removeInternalProcedure(InternalProcedure internalProcedure) {
- if (internalProcedure == null) {
- return true;
- }
- internalProcedure.setState(ProcedureState.SUCCESS);
- return timeoutExecutor.remove(internalProcedure);
- }
-
- /**
- * Executes procedure
- *
- * <p>Calls doExecute() if success and return subprocedures submit sub procs set the state to
- * WAITING, wait for all sub procs completed. else if no sub procs procedure completed
- * successfully set procedure's parent to RUNNABLE in case of failure start rollback of the
- * procedure.
- *
- * @param proc procedure
- */
- private void executeProcedure(Procedure<Env> proc) {
- if (proc.isFinished()) {
- LOG.debug("{} is already finished.", proc);
- return;
- }
- final Long rootProcId = getRootProcedureId(proc);
- if (rootProcId == null) {
- LOG.warn("Rollback because parent is done/rolledback, proc is {}", proc);
- executeRollback(proc);
- return;
- }
- RootProcedureStack<Env> rootProcStack = rollbackStack.get(rootProcId);
- if (rootProcStack == null) {
- LOG.warn("Rollback stack is null for {}", proc.getProcId());
- return;
- }
- ProcedureLockState lockState = null;
- try {
- do {
- if (!rootProcStack.acquire()) {
- if (rootProcStack.setRollback()) {
- lockState = executeRootStackRollback(rootProcId, rootProcStack);
- switch (lockState) {
- case LOCK_ACQUIRED:
- break;
- case LOCK_EVENT_WAIT:
- LOG.info("LOCK_EVENT_WAIT rollback {}", proc);
- rootProcStack.unsetRollback();
- break;
- case LOCK_YIELD_WAIT:
- rootProcStack.unsetRollback();
- scheduler.yield(proc);
- break;
- default:
- throw new UnsupportedOperationException();
- }
- } else {
- if (!proc.wasExecuted()) {
- switch (executeRollback(proc)) {
- case LOCK_ACQUIRED:
- break;
- case LOCK_EVENT_WAIT:
- LOG.info("LOCK_EVENT_WAIT can't rollback child running for {}", proc);
- break;
- case LOCK_YIELD_WAIT:
- scheduler.yield(proc);
- break;
- default:
- throw new UnsupportedOperationException();
- }
- }
- }
- break;
- }
- lockState = acquireLock(proc);
- switch (lockState) {
- case LOCK_ACQUIRED:
- executeProcedure(rootProcStack, proc);
- break;
- case LOCK_YIELD_WAIT:
- case LOCK_EVENT_WAIT:
- LOG.info("{} lockstate is {}", proc, lockState);
- break;
- default:
- throw new UnsupportedOperationException();
- }
- rootProcStack.release();
-
- if (proc.isSuccess()) {
- // update metrics on finishing the procedure
- proc.updateMetricsOnFinish(getEnvironment(), proc.elapsedTime(), true);
- LOG.debug("{} finished in {}ms successfully.", proc, proc.elapsedTime());
- if (proc.getProcId() == rootProcId) {
- rootProcedureCleanup(proc);
- } else {
- executeCompletionCleanup(proc);
- }
- return;
- }
-
- } while (rootProcStack.isFailed());
- } finally {
- // Only after procedure has completed execution can it be allowed to be rescheduled to prevent
- // data races
- if (Objects.equals(lockState, ProcedureLockState.LOCK_EVENT_WAIT)) {
- LOG.info("procedureId {} wait for lock.", proc.getProcId());
- ((ConfigNodeProcedureEnv) this.environment).getNodeLock().waitProcedure(proc);
- }
- }
- }
-
- /**
- * execute procedure and submit its children
- *
- * @param rootProcStack procedure's root proc stack
- * @param proc procedure
- */
- private void executeProcedure(RootProcedureStack rootProcStack, Procedure<Env> proc) {
- if (proc.getState() != ProcedureState.RUNNABLE) {
- LOG.error(
- "The executing procedure should in RUNNABLE state, but it's not. Procedure is {}", proc);
- return;
- }
- boolean reExecute;
-
- Procedure<Env>[] subprocs = null;
- do {
- reExecute = false;
- try {
- subprocs = proc.doExecute(this.environment);
- if (subprocs != null && subprocs.length == 0) {
- subprocs = null;
- }
- } catch (InterruptedException e) {
- LOG.warn("Interrupt during execution, suspend or retry it later.", e);
- yieldProcedure(proc);
- } catch (Throwable e) {
- LOG.error("CODE-BUG:{}", proc, e);
- proc.setFailure(new ProcedureException(e.getMessage(), e));
- }
-
- if (!proc.isFailed()) {
- if (subprocs != null) {
- if (subprocs.length == 1 && subprocs[0] == proc) {
- subprocs = null;
- reExecute = true;
- } else {
- subprocs = initializeChildren(rootProcStack, proc, subprocs);
- LOG.info("Initialized sub procs:{}", Arrays.toString(subprocs));
- }
- } else if (proc.getState() == ProcedureState.WAITING_TIMEOUT) {
- LOG.info("Added into timeoutExecutor {}", proc);
- } else {
- proc.setState(ProcedureState.SUCCESS);
- }
- }
- // add procedure into rollback stack.
- rootProcStack.addRollbackStep(proc);
-
- updateStoreOnExecution(rootProcStack, proc, subprocs);
-
- if (!store.isRunning()) {
- return;
- }
-
- if (proc.isRunnable() && proc.isYieldAfterExecution(this.environment)) {
- yieldProcedure(proc);
- return;
- }
- } while (reExecute);
-
- if (subprocs != null && !proc.isFailed()) {
- submitChildrenProcedures(subprocs);
- }
-
- releaseLock(proc, false);
- if (proc.isFinished() && proc.hasParent()) {
- countDownChildren(rootProcStack, proc);
- }
- }
-
- /**
- * Serve as a countdown latch to check whether all children have already completed.
- *
- * @param rootProcStack root procedure stack
- * @param proc proc
- */
- private void countDownChildren(RootProcedureStack rootProcStack, Procedure<Env> proc) {
- Procedure<Env> parent = procedures.get(proc.getParentProcId());
- if (parent == null && rootProcStack.isRollingback()) {
- return;
- }
- if (parent != null && parent.tryRunnable()) {
- // If success, means all its children have completed, move parent to front of the queue.
- store.update(parent);
- scheduler.addFront(parent);
- LOG.info(
- "Finished subprocedure pid={}, resume processing ppid={}",
- proc.getProcId(),
- parent.getProcId());
- }
- }
-
- /**
- * Submit children procedures.
- *
- * @param subprocs children procedures
- */
- private void submitChildrenProcedures(Procedure<Env>[] subprocs) {
- for (Procedure<Env> subproc : subprocs) {
- subproc.updateMetricsOnSubmit(getEnvironment());
- procedures.put(subproc.getProcId(), subproc);
- scheduler.addFront(subproc);
- LOG.info("Sub-Procedure pid={} has been submitted", subproc.getProcId());
- }
- }
-
- private void updateStoreOnExecution(
- RootProcedureStack rootProcStack, Procedure<Env> proc, Procedure<Env>[] subprocs) {
- if (subprocs != null && !proc.isFailed()) {
- if (LOG.isDebugEnabled()) {
- LOG.debug("Stored {}, children {}", proc, Arrays.toString(subprocs));
- }
- store.update(subprocs);
- } else {
- LOG.debug("Store update {}", proc);
- if (proc.isFinished() && !proc.hasParent()) {
- final long[] childProcIds = rootProcStack.getSubprocedureIds();
- if (childProcIds != null) {
- store.delete(childProcIds);
- for (long childProcId : childProcIds) {
- procedures.remove(childProcId);
- }
- } else {
- store.update(proc);
- }
- } else {
- store.update(proc);
- }
- }
- }
-
- private Procedure<Env>[] initializeChildren(
- RootProcedureStack rootProcStack, Procedure<Env> proc, Procedure<Env>[] subprocs) {
- final long rootProcedureId = getRootProcedureId(proc);
- for (int i = 0; i < subprocs.length; i++) {
- Procedure<Env> subproc = subprocs[i];
- if (subproc == null) {
- String errMsg = "subproc[" + i + "] is null, aborting procedure";
- proc.setFailure(new ProcedureException((errMsg), new IllegalArgumentException(errMsg)));
- return null;
- }
- subproc.setParentProcId(proc.getProcId());
- subproc.setRootProcId(rootProcedureId);
- subproc.setProcId(store.getNextProcId());
- subproc.setProcRunnable();
- rootProcStack.addSubProcedure(subproc);
- }
-
- if (!proc.isFailed()) {
- proc.setChildrenLatch(subprocs.length);
- switch (proc.getState()) {
- case RUNNABLE:
- proc.setState(ProcedureState.WAITING);
- break;
- case WAITING_TIMEOUT:
- timeoutExecutor.add(proc);
- break;
- default:
- break;
- }
- }
- return subprocs;
- }
-
- private void yieldProcedure(Procedure<Env> proc) {
- releaseLock(proc, false);
- scheduler.yield(proc);
- }
-
- /**
- * Rollback full root procedure stack.
- *
- * @param rootProcId root procedure id
- * @param procedureStack root procedure stack
- * @return lock state
- */
- private ProcedureLockState executeRootStackRollback(
- Long rootProcId, RootProcedureStack procedureStack) {
- Procedure<Env> rootProcedure = procedures.get(rootProcId);
- ProcedureException exception = rootProcedure.getException();
- if (exception == null) {
- exception = procedureStack.getException();
- rootProcedure.setFailure(exception);
- store.update(rootProcedure);
- }
- List<Procedure<Env>> subprocStack = procedureStack.getSubproceduresStack();
- int stackTail = subprocStack.size();
- while (stackTail-- > 0) {
- Procedure<Env> procedure = subprocStack.get(stackTail);
- if (procedure.isSuccess()) {
- subprocStack.remove(stackTail);
- cleanupAfterRollback(procedure);
- continue;
- }
- ProcedureLockState lockState = acquireLock(procedure);
- if (lockState != ProcedureLockState.LOCK_ACQUIRED) {
- return lockState;
- }
- lockState = executeRollback(procedure);
- releaseLock(procedure, false);
-
- boolean abortRollback = lockState != ProcedureLockState.LOCK_ACQUIRED;
- abortRollback |= !isRunning() || !store.isRunning();
- if (abortRollback) {
- return lockState;
- }
-
- if (!procedure.isFinished() && procedure.isYieldAfterExecution(this.environment)) {
- return ProcedureLockState.LOCK_YIELD_WAIT;
- }
-
- if (procedure != rootProcedure) {
- executeCompletionCleanup(procedure);
- }
- }
-
- LOG.info("Rolled back {}, time duration is {}", rootProcedure, rootProcedure.elapsedTime());
- rootProcedureCleanup(rootProcedure);
- return ProcedureLockState.LOCK_ACQUIRED;
- }
-
- private ProcedureLockState acquireLock(Procedure<Env> proc) {
- if (proc.hasLock()) {
- return ProcedureLockState.LOCK_ACQUIRED;
- }
- return proc.doAcquireLock(this.environment, store);
- }
-
- /**
- * do execute defined in procedure and then update store or remove completely in case it is a
- * child.
- *
- * @param procedure procedure
- * @return procedure lock state
- */
- private ProcedureLockState executeRollback(Procedure<Env> procedure) {
- try {
- procedure.doRollback(this.environment);
- } catch (IOException e) {
- LOG.error("Roll back failed for {}", procedure, e);
- } catch (InterruptedException e) {
- LOG.warn("Interrupted exception occured for {}", procedure, e);
- } catch (Throwable t) {
- LOG.error("CODE-BUG: runtime exception for {}", procedure, t);
- }
- cleanupAfterRollback(procedure);
- return ProcedureLockState.LOCK_ACQUIRED;
- }
-
- private void cleanupAfterRollback(Procedure<Env> procedure) {
- if (procedure.removeStackIndex()) {
- if (!procedure.isSuccess()) {
- procedure.setState(ProcedureState.ROLLEDBACK);
- }
-
- // update metrics on finishing the procedure (fail)
- procedure.updateMetricsOnFinish(getEnvironment(), procedure.elapsedTime(), false);
-
- if (procedure.hasParent()) {
- store.delete(procedure.getProcId());
- procedures.remove(procedure.getProcId());
- } else {
- final long[] childProcIds = rollbackStack.get(procedure.getProcId()).getSubprocedureIds();
- if (childProcIds != null) {
- store.delete(childProcIds);
- } else {
- store.update(procedure);
- }
- }
- } else {
- store.update(procedure);
- }
- }
-
- private void executeCompletionCleanup(Procedure<Env> proc) {
- if (proc.hasLock()) {
- releaseLock(proc, true);
- }
- }
-
- private void rootProcedureCleanup(Procedure<Env> proc) {
- executeCompletionCleanup(proc);
- CompletedProcedureContainer<Env> retainer = new CompletedProcedureContainer<>(proc);
- completed.put(proc.getProcId(), retainer);
- rollbackStack.remove(proc.getProcId());
- procedures.remove(proc.getProcId());
- }
-
- /**
- * Add a Procedure to executor.
- *
- * @param procedure procedure
- * @return procedure id
- */
- private long pushProcedure(Procedure<Env> procedure) {
- final long currentProcId = procedure.getProcId();
- // Update metrics on start of a procedure
- procedure.updateMetricsOnSubmit(getEnvironment());
- RootProcedureStack<Env> stack = new RootProcedureStack<>();
- rollbackStack.put(currentProcId, stack);
- procedures.put(currentProcId, procedure);
- scheduler.addBack(procedure);
- return procedure.getProcId();
- }
-
- private class WorkerThread extends StoppableThread {
- private final AtomicLong startTime = new AtomicLong(Long.MAX_VALUE);
- private final AtomicReference<Procedure<Env>> activeProcedure = new AtomicReference<>();
- protected long keepAliveTime = -1;
-
- public WorkerThread(ThreadGroup threadGroup) {
- this(threadGroup, "ProcedureCoreWorker-");
- }
-
- public WorkerThread(ThreadGroup threadGroup, String prefix) {
- super(threadGroup, prefix + workId.incrementAndGet());
- setDaemon(true);
- }
-
- @Override
- public void sendStopSignal() {
- scheduler.signalAll();
- }
-
- @Override
- public void run() {
- long lastUpdated = System.currentTimeMillis();
- try {
- while (isRunning() && keepAlive(lastUpdated)) {
- Procedure<Env> procedure = scheduler.poll(keepAliveTime, TimeUnit.MILLISECONDS);
- if (procedure == null) {
- Thread.sleep(1000);
- continue;
- }
- this.activeProcedure.set(procedure);
- activeExecutorCount.incrementAndGet();
- startTime.set(System.currentTimeMillis());
- executeProcedure(procedure);
- activeExecutorCount.decrementAndGet();
- LOG.trace(
- "Halt pid={}, activeCount={}", procedure.getProcId(), activeExecutorCount.get());
- this.activeProcedure.set(null);
- lastUpdated = System.currentTimeMillis();
- startTime.set(lastUpdated);
- }
-
- } catch (Exception e) {
- if (this.activeProcedure.get() != null) {
- LOG.warn(
- "Exception happened when worker {} execute procedure {}",
- getName(),
- this.activeProcedure.get(),
- e);
- }
- } finally {
- LOG.info("Procedure worker {} terminated.", getName());
- }
- workerThreads.remove(this);
- }
-
- protected boolean keepAlive(long lastUpdated) {
- return true;
- }
-
- @Override
- public String toString() {
- Procedure<?> p = this.activeProcedure.get();
- return getName() + "(pid=" + (p == null ? NO_PROC_ID : p.getProcId() + ")");
- }
-
- /**
- * @return the time since the current procedure is running
- */
- public long getCurrentRunTime() {
- return System.currentTimeMillis() - startTime.get();
- }
- }
-
- // A temporary worker thread will be launched when too many core workers are stuck.
- // They will timeout after keepAliveTime if there is no procedure to run.
- private final class TemporaryWorkerThread extends WorkerThread {
-
- public TemporaryWorkerThread(ThreadGroup group) {
- super(group, "ProcedureTemporaryWorker-");
- this.keepAliveTime = TimeUnit.SECONDS.toMillis(10);
- }
-
- @Override
- protected boolean keepAlive(long lastUpdate) {
- return System.currentTimeMillis() - lastUpdate < keepAliveTime;
- }
- }
-
- private final class WorkerMonitor extends InternalProcedure<Env> {
- private static final int DEFAULT_WORKER_MONITOR_INTERVAL = 30000; // 30sec
-
- private static final int DEFAULT_WORKER_STUCK_THRESHOLD = 60000; // 60sec
-
- private static final float DEFAULT_WORKER_ADD_STUCK_PERCENTAGE = 0.5f; // 50% stuck
-
- public WorkerMonitor() {
- super(DEFAULT_WORKER_MONITOR_INTERVAL);
- updateTimestamp();
- }
-
- private int calculateRunningAndStuckWorkers() {
- // Check if any of the worker is stuck
- int runningCount = 0, stuckCount = 0;
- for (WorkerThread worker : workerThreads) {
- final Procedure<?> proc = worker.activeProcedure.get();
- if (proc == null) {
- continue;
- }
- runningCount++;
- // WARN the worker is stuck
- if (worker.getCurrentRunTime() > DEFAULT_WORKER_STUCK_THRESHOLD) {
- stuckCount++;
- LOG.warn(
- "Worker stuck {}({}), run time {} ms",
- worker,
- proc.getProcType(),
- worker.getCurrentRunTime());
- }
- LOG.info(
- "Procedure workers: {} is running, {} is running and stuck", runningCount, stuckCount);
- }
- return stuckCount;
- }
-
- private void checkThreadCount(final int stuckCount) {
- // Nothing to do if there are no runnable tasks
- if (stuckCount < 1 || !scheduler.hasRunnables()) {
- return;
- }
- // Add a new thread if the worker stuck percentage exceed the threshold limit
- // and every handler is active.
- final float stuckPerc = ((float) stuckCount) / workerThreads.size();
- // Let's add new worker thread more aggressively, as they will timeout finally if there is no
- // work to do.
- if (stuckPerc >= DEFAULT_WORKER_ADD_STUCK_PERCENTAGE && workerThreads.size() < maxPoolSize) {
- final TemporaryWorkerThread worker = new TemporaryWorkerThread(threadGroup);
- workerThreads.add(worker);
- worker.start();
- LOG.debug("Added new worker thread {}", worker);
- }
- }
-
- @Override
- protected void periodicExecute(Env env) {
- final int stuckCount = calculateRunningAndStuckWorkers();
- checkThreadCount(stuckCount);
- updateTimestamp();
- }
- }
-
- public int getWorkerThreadCount() {
- return workerThreads.size();
- }
-
- public long getActiveWorkerThreadCount() {
- return workerThreads.stream().filter(worker -> worker.activeProcedure.get() != null).count();
- }
-
- public boolean isRunning() {
- return running.get();
- }
-
- public void stop() {
- if (!running.getAndSet(false)) {
- return;
- }
- LOG.info("Stopping");
- scheduler.stop();
- timeoutExecutor.sendStopSignal();
- }
-
- public void join() {
- timeoutExecutor.awaitTermination();
- workerMonitorExecutor.awaitTermination();
- for (WorkerThread workerThread : workerThreads) {
- workerThread.awaitTermination();
- }
- try {
- threadGroup.destroy();
- } catch (IllegalThreadStateException e) {
- LOG.warn(
- "ProcedureExecutor threadGroup {} contains running threads which are used by non-procedure module.",
- this.threadGroup);
- this.threadGroup.list();
- }
- }
-
- public boolean isStarted(long procId) {
- Procedure<Env> procedure = procedures.get(procId);
- if (procedure == null) {
- return completed.get(procId) != null;
- }
- return procedure.wasExecuted();
- }
-
- public boolean isFinished(final long procId) {
- return !procedures.containsKey(procId);
- }
-
- public ConcurrentHashMap<Long, Procedure<Env>> getProcedures() {
- return procedures;
- }
-
- // -----------------------------CLIENT IMPLEMENTATION-----------------------------------
- /**
- * Submit a new root-procedure to the executor, called by client.
- *
- * @param procedure root procedure
- * @return procedure id
- */
- public long submitProcedure(Procedure<Env> procedure) {
- Preconditions.checkArgument(procedure.getState() == ProcedureState.INITIALIZING);
- Preconditions.checkArgument(!procedure.hasParent(), "Unexpected parent", procedure);
- // Initialize the procedure
- procedure.setProcId(store.getNextProcId());
- procedure.setProcRunnable();
- // Commit the transaction
- store.update(procedure);
- LOG.debug("{} is stored.", procedure);
- // Add the procedure to the executor
- return pushProcedure(procedure);
- }
-
- public ProcedureScheduler getScheduler() {
- return scheduler;
- }
-
- public Env getEnvironment() {
- return environment;
- }
-
- public IProcedureStore getStore() {
- return store;
- }
-
- public RootProcedureStack<Env> getRollbackStack(long rootProcId) {
- return rollbackStack.get(rootProcId);
- }
-}
+1/*
+1 * Licensed to the Apache Software Foundation (ASF) under one
+1 * or more contributor license agreements. See the NOTICE file
+1 * distributed with this work for additional information
+1 * regarding copyright ownership. The ASF licenses this file
+1 * to you under the Apache License, Version 2.0 (the
+1 * "License"); you may not use this file except in compliance
+1 * with the License. You may obtain a copy of the License at
+1 *
+1 * http://www.apache.org/licenses/LICENSE-2.0
+1 *
+1 * Unless required by applicable law or agreed to in writing,
+1 * software distributed under the License is distributed on an
+1 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+1 * KIND, either express or implied. See the License for the
+1 * specific language governing permissions and limitations
+1 * under the License.
+1 */
+1
+1package org.apache.iotdb.confignode.procedure;
+1
+1import org.apache.iotdb.commons.concurrent.ThreadName;
+1import org.apache.iotdb.commons.utils.TestOnly;
+1import org.apache.iotdb.confignode.procedure.env.ConfigNodeProcedureEnv;
+1import org.apache.iotdb.confignode.procedure.exception.ProcedureException;
+1import org.apache.iotdb.confignode.procedure.scheduler.ProcedureScheduler;
+1import org.apache.iotdb.confignode.procedure.scheduler.SimpleProcedureScheduler;
+1import org.apache.iotdb.confignode.procedure.state.ProcedureLockState;
+1import org.apache.iotdb.confignode.procedure.state.ProcedureState;
+1import org.apache.iotdb.confignode.procedure.store.IProcedureStore;
+1
+1import com.google.common.base.Preconditions;
+1import org.slf4j.Logger;
+1import org.slf4j.LoggerFactory;
+1
+1import java.io.IOException;
+1import java.util.ArrayDeque;
+1import java.util.ArrayList;
+1import java.util.Arrays;
+1import java.util.Deque;
+1import java.util.HashSet;
+1import java.util.List;
+1import java.util.Objects;
+1import java.util.Set;
+1import java.util.concurrent.ConcurrentHashMap;
+1import java.util.concurrent.CopyOnWriteArrayList;
+1import java.util.concurrent.TimeUnit;
+1import java.util.concurrent.atomic.AtomicBoolean;
+1import java.util.concurrent.atomic.AtomicInteger;
+1import java.util.concurrent.atomic.AtomicLong;
+1import java.util.concurrent.atomic.AtomicReference;
+1
+1import static org.apache.iotdb.confignode.procedure.Procedure.NO_PROC_ID;
+1
+1public class ProcedureExecutor<Env> {
+1 private static final Logger LOG = LoggerFactory.getLogger(ProcedureExecutor.class);
+1
+1 private final ConcurrentHashMap<Long, CompletedProcedureContainer<Env>> completed =
+1 new ConcurrentHashMap<>();
+1
+1 private final ConcurrentHashMap<Long, RootProcedureStack<Env>> rollbackStack =
+1 new ConcurrentHashMap<>();
+1
+1 private final ConcurrentHashMap<Long, Procedure<Env>> procedures = new ConcurrentHashMap<>();
+1
+1 private ThreadGroup threadGroup;
+1
+1 private CopyOnWriteArrayList<WorkerThread> workerThreads;
+1
+1 private TimeoutExecutorThread<Env> timeoutExecutor;
+1
+1 private TimeoutExecutorThread<Env> workerMonitorExecutor;
+1
+1 private int corePoolSize;
+1 private int maxPoolSize;
+1
+1 private final ProcedureScheduler scheduler;
+1
+1 private final AtomicLong workId = new AtomicLong(0);
+1 private final AtomicInteger activeExecutorCount = new AtomicInteger(0);
+1 private final AtomicBoolean running = new AtomicBoolean(false);
+1 private final Env environment;
+1 private final IProcedureStore<Env> store;
+1
+1 public ProcedureExecutor(
+1 final Env environment, final IProcedureStore<Env> store, final ProcedureScheduler scheduler) {
+1 this.environment = environment;
+1 this.scheduler = scheduler;
+1 this.store = store;
+1 }
+1
+1 @TestOnly
+1 public ProcedureExecutor(final Env environment, final IProcedureStore<Env> store) {
+1 this(environment, store, new SimpleProcedureScheduler());
+1 }
+1
+1 public void init(int numThreads) {
+1 this.corePoolSize = numThreads;
+1 this.maxPoolSize = 10 * numThreads;
+1 this.threadGroup = new ThreadGroup(ThreadName.CONFIG_NODE_PROCEDURE_WORKER.getName());
+1 this.timeoutExecutor =
+1 new TimeoutExecutorThread<>(
+1 this, threadGroup, ThreadName.CONFIG_NODE_TIMEOUT_EXECUTOR.getName());
+1 this.workerMonitorExecutor =
+1 new TimeoutExecutorThread<>(
+1 this, threadGroup, ThreadName.CONFIG_NODE_WORKER_THREAD_MONITOR.getName());
+1 workId.set(0);
+1 workerThreads = new CopyOnWriteArrayList<>();
+1 for (int i = 0; i < corePoolSize; i++) {
+1 workerThreads.add(new WorkerThread(threadGroup));
+1 }
+1 // Add worker monitor
+1 workerMonitorExecutor.add(new WorkerMonitor());
+1
+1 scheduler.start();
+1 recover();
+1 }
+1
+1 private void recover() {
+1 // 1.Build rollback stack
+1 List<Procedure<Env>> procedureList = getProcedureListFromDifferentVersion();
+1 // Load procedure wal file
+1 for (Procedure<Env> proc : procedureList) {
+1 if (proc.isFinished()) {
+1 completed.putIfAbsent(proc.getProcId(), new CompletedProcedureContainer<>(proc));
+1 } else {
+1 if (!proc.hasParent()) {
+1 rollbackStack.put(proc.getProcId(), new RootProcedureStack<>());
+1 }
+1 procedures.putIfAbsent(proc.getProcId(), proc);
+1 }
+1 }
+1 List<Procedure<Env>> runnableList = new ArrayList<>();
+1 List<Procedure<Env>> failedList = new ArrayList<>();
+1 List<Procedure<Env>> waitingList = new ArrayList<>();
+1 List<Procedure<Env>> waitingTimeoutList = new ArrayList<>();
+1 for (Procedure<Env> proc : procedureList) {
+1 if (proc.isFinished() && !proc.hasParent()) {
+1 continue;
+1 }
+1 long rootProcedureId = getRootProcedureId(proc);
+1 if (proc.hasParent()) {
+1 Procedure<Env> parent = procedures.get(proc.getParentProcId());
+1 if (parent != null && !proc.isFinished()) {
+1 parent.incChildrenLatch();
+1 }
+1 }
+1 RootProcedureStack<Env> rootStack = rollbackStack.get(rootProcedureId);
+1 if (rootStack != null) {
+1 rootStack.loadStack(proc);
+1 }
+1 proc.setRootProcedureId(rootProcedureId);
+1 switch (proc.getState()) {
+1 case RUNNABLE:
+1 runnableList.add(proc);
+1 break;
+1 case FAILED:
+1 failedList.add(proc);
+1 break;
+1 case WAITING:
+1 waitingList.add(proc);
+1 break;
+1 case WAITING_TIMEOUT:
+1 waitingTimeoutList.add(proc);
+1 break;
+1 case ROLLEDBACK:
+1 case INITIALIZING:
+1 LOG.error("Unexpected state:{} for {}", proc.getState(), proc);
+1 throw new UnsupportedOperationException("Unexpected state");
+1 default:
+1 break;
+1 }
+1 }
+1
+1 waitingList.forEach(
+1 procedure -> {
+1 if (!procedure.hasChildren()) {
+1 // Normally, WAITING procedures should be wakened by its children.
+1 // But, there is a case that, all the children are successful, and before
+1 // they can wake up their parent procedure, the master was killed.
+1 // So, during recovering the procedures from ProcedureWal, its children
+1 // are not loaded because of their SUCCESS state.
+1 // So we need to continue to run this WAITING procedure. Before
+1 // executing, we need to set its state to RUNNABLE.
+1 procedure.setState(ProcedureState.RUNNABLE);
+1 runnableList.add(procedure);
+1 }
+1 });
+1 restoreLocks();
+1
+1 waitingTimeoutList.forEach(timeoutExecutor::add);
+1
+1 failedList.forEach(scheduler::addBack);
+1 runnableList.forEach(scheduler::addBack);
+1 scheduler.signalAll();
+1 }
+1
+1 private List<Procedure<Env>> getProcedureListFromDifferentVersion() {
+1 if (store.isOldVersionProcedureStore()) {
+1 LOG.info("Old procedure directory detected, upgrade beginning...");
+1 return store.load();
+1 } else {
+1 return store.getProcedures();
+1 }
+1 }
+1
+1 /**
+1 * Helper to look up the root Procedure ID.
+1 *
+1 * @param proc given a specified procedure.
+1 */
+1 Long getRootProcedureId(Procedure<Env> proc) {
+1 while (proc.hasParent()) {
+1 proc = procedures.get(proc.getParentProcId());
+1 if (proc == null) {
+1 return NO_PROC_ID;
+1 }
+1 }
+1 return proc.getProcId();
+1 }
+1
+1 private void releaseLock(Procedure<Env> procedure, boolean force) {
+1 if (force || !procedure.holdLock(this.environment) || procedure.isFinished()) {
+1 procedure.doReleaseLock(this.environment, store);
+1 }
+1 }
+1
+1 private void restoreLock(Procedure procedure, Set<Long> restored) {
+1 procedure.restoreLock(environment);
+1 restored.add(procedure.getProcId());
+1 }
+1
+1 private void restoreLocks(Deque<Procedure<Env>> stack, Set<Long> restored) {
+1 while (!stack.isEmpty()) {
+1 restoreLock(stack.pop(), restored);
+1 }
+1 }
+1
+1 private void restoreLocks() {
+1 Set<Long> restored = new HashSet<>();
+1 Deque<Procedure<Env>> stack = new ArrayDeque<>();
+1 procedures
+1 .values()
+1 .forEach(
+1 procedure -> {
+1 while (procedure != null) {
+1 if (restored.contains(procedure.getProcId())) {
+1 restoreLocks(stack, restored);
+1 return;
+1 }
+1 if (!procedure.hasParent()) {
+1 restoreLock(procedure, restored);
+1 restoreLocks(stack, restored);
+1 return;
+1 }
+1 stack.push(procedure);
+1 procedure = procedures.get(procedure.getParentProcId());
+1 }
+1 });
+1 }
+1
+1 public void startWorkers() {
+1 if (!running.compareAndSet(false, true)) {
+1 LOG.warn("Already running");
+1 return;
+1 }
+1 timeoutExecutor.start();
+1 workerMonitorExecutor.start();
+1 for (WorkerThread workerThread : workerThreads) {
+1 workerThread.start();
+1 }
+1 LOG.info("{} procedure workers are started.", workerThreads.size());
+1 }
+1
+1 public void startCompletedCleaner(long cleanTimeInterval, long cleanEvictTTL) {
+1 addInternalProcedure(
+1 new CompletedProcedureRecycler(store, completed, cleanTimeInterval, cleanEvictTTL));
+1 }
+1
+1 public void addInternalProcedure(InternalProcedure interalProcedure) {
+1 if (interalProcedure == null) {
+1 return;
+1 }
+1 interalProcedure.setState(ProcedureState.WAITING_TIMEOUT);
+1 timeoutExecutor.add(interalProcedure);
+1 }
+1
+1 public boolean removeInternalProcedure(InternalProcedure internalProcedure) {
+1 if (internalProcedure == null) {
+1 return true;
+1 }
+1 internalProcedure.setState(ProcedureState.SUCCESS);
+1 return timeoutExecutor.remove(internalProcedure);
+1 }
+1
+1 /**
+1 * Executes procedure
+1 *
+1 * <p>Calls doExecute() if success and return subprocedures submit sub procs set the state to
+1 * WAITING, wait for all sub procs completed. else if no sub procs procedure completed
+1 * successfully set procedure's parent to RUNNABLE in case of failure start rollback of the
+1 * procedure.
+1 *
+1 * @param proc procedure
+1 */
+1 private void executeProcedure(Procedure<Env> proc) {
+1 if (proc.isFinished()) {
+1 LOG.debug("{} is already finished.", proc);
+1 return;
+1 }
+1 final Long rootProcId = getRootProcedureId(proc);
+1 if (rootProcId == null) {
+1 LOG.warn("Rollback because parent is done/rolledback, proc is {}", proc);
+1 executeRollback(proc);
+1 return;
+1 }
+1 RootProcedureStack<Env> rootProcStack = rollbackStack.get(rootProcId);
+1 if (rootProcStack == null) {
+1 LOG.warn("Rollback stack is null for {}", proc.getProcId());
+1 return;
+1 }
+1 ProcedureLockState lockState = null;
+1 try {
+1 do {
+1 if (!rootProcStack.acquire()) {
+1 if (rootProcStack.setRollback()) {
+1 lockState = executeRootStackRollback(rootProcId, rootProcStack);
+1 switch (lockState) {
+1 case LOCK_ACQUIRED:
+1 break;
+1 case LOCK_EVENT_WAIT:
+1 LOG.info("LOCK_EVENT_WAIT rollback {}", proc);
+1 rootProcStack.unsetRollback();
+1 break;
+1 case LOCK_YIELD_WAIT:
+1 rootProcStack.unsetRollback();
+1 scheduler.yield(proc);
+1 break;
+1 default:
+1 throw new UnsupportedOperationException();
+1 }
+1 } else {
+1 if (!proc.wasExecuted()) {
+1 switch (executeRollback(proc)) {
+1 case LOCK_ACQUIRED:
+1 break;
+1 case LOCK_EVENT_WAIT:
+1 LOG.info("LOCK_EVENT_WAIT can't rollback child running for {}", proc);
+1 break;
+1 case LOCK_YIELD_WAIT:
+1 scheduler.yield(proc);
+1 break;
+1 default:
+1 throw new UnsupportedOperationException();
+1 }
+1 }
+1 }
+1 break;
+1 }
+1 lockState = acquireLock(proc);
+1 switch (lockState) {
+1 case LOCK_ACQUIRED:
+1 executeProcedure(rootProcStack, proc);
+1 break;
+1 case LOCK_YIELD_WAIT:
+1 case LOCK_EVENT_WAIT:
+1 LOG.info("{} lockstate is {}", proc, lockState);
+1 break;
+1 default:
+1 throw new UnsupportedOperationException();
+1 }
+1 rootProcStack.release();
+1
+1 if (proc.isSuccess()) {
+1 // update metrics on finishing the procedure
+1 proc.updateMetricsOnFinish(getEnvironment(), proc.elapsedTime(), true);
+1 LOG.debug("{} finished in {}ms successfully.", proc, proc.elapsedTime());
+1 if (proc.getProcId() == rootProcId) {
+1 rootProcedureCleanup(proc);
+1 } else {
+1 executeCompletionCleanup(proc);
+1 }
+1 return;
+1 }
+1
+1 } while (rootProcStack.isFailed());
+1 } finally {
+1 // Only after procedure has completed execution can it be allowed to be rescheduled to prevent
+1 // data races
+1 if (Objects.equals(lockState, ProcedureLockState.LOCK_EVENT_WAIT)) {
+1 LOG.info("procedureId {} wait for lock.", proc.getProcId());
+1 ((ConfigNodeProcedureEnv) this.environment).getNodeLock().waitProcedure(proc);
+1 }
+1 }
+1 }
+1
+1 /**
+1 * execute procedure and submit its children
+1 *
+1 * @param rootProcStack procedure's root proc stack
+1 * @param proc procedure
+1 */
+1 private void executeProcedure(RootProcedureStack rootProcStack, Procedure<Env> proc) {
+1 if (proc.getState() != ProcedureState.RUNNABLE) {
+1 LOG.error(
+1 "The executing procedure should in RUNNABLE state, but it's not. Procedure is {}", proc);
+1 return;
+1 }
+1 boolean reExecute;
+1
+1 Procedure<Env>[] subprocs = null;
+1 do {
+1 reExecute = false;
+1 try {
+1 subprocs = proc.doExecute(this.environment);
+1 if (subprocs != null && subprocs.length == 0) {
+1 subprocs = null;
+1 }
+1 } catch (InterruptedException e) {
+1 LOG.warn("Interrupt during execution, suspend or retry it later.", e);
+1 yieldProcedure(proc);
+1 } catch (Throwable e) {
+1 LOG.error("CODE-BUG:{}", proc, e);
+1 proc.setFailure(new ProcedureException(e.getMessage(), e));
+1 }
+1
+1 if (!proc.isFailed()) {
+1 if (subprocs != null) {
+1 if (subprocs.length == 1 && subprocs[0] == proc) {
+1 subprocs = null;
+1 reExecute = true;
+1 } else {
+1 subprocs = initializeChildren(rootProcStack, proc, subprocs);
+1 LOG.info("Initialized sub procs:{}", Arrays.toString(subprocs));
+1 }
+1 } else if (proc.getState() == ProcedureState.WAITING_TIMEOUT) {
+1 LOG.info("Added into timeoutExecutor {}", proc);
+1 } else {
+1 proc.setState(ProcedureState.SUCCESS);
+1 }
+1 }
+1 // add procedure into rollback stack.
+1 rootProcStack.addRollbackStep(proc);
+1
+1 updateStoreOnExecution(rootProcStack, proc, subprocs);
+1
+1 if (!store.isRunning()) {
+1 return;
+1 }
+1
+1 if (proc.isRunnable() && proc.isYieldAfterExecution(this.environment)) {
+1 yieldProcedure(proc);
+1 return;
+1 }
+1 } while (reExecute);
+1
+1 if (subprocs != null && !proc.isFailed()) {
+1 submitChildrenProcedures(subprocs);
+1 }
+1
+1 releaseLock(proc, false);
+1 if (proc.isFinished() && proc.hasParent()) {
+1 countDownChildren(rootProcStack, proc);
+1 }
+1 }
+1
+1 /**
+1 * Serve as a countdown latch to check whether all children have already completed.
+1 *
+1 * @param rootProcStack root procedure stack
+1 * @param proc proc
+1 */
+1 private void countDownChildren(RootProcedureStack rootProcStack, Procedure<Env> proc) {
+1 Procedure<Env> parent = procedures.get(proc.getParentProcId());
+1 if (parent == null && rootProcStack.isRollingback()) {
+1 return;
+1 }
+1 if (parent != null && parent.tryRunnable()) {
+1 // If success, means all its children have completed, move parent to front of the queue.
+1 store.update(parent);
+1 scheduler.addFront(parent);
+1 LOG.info(
+1 "Finished subprocedure pid={}, resume processing ppid={}",
+1 proc.getProcId(),
+1 parent.getProcId());
+1 }
+1 }
+1
+1 /**
+1 * Submit children procedures.
+1 *
+1 * @param subprocs children procedures
+1 */
+1 private void submitChildrenProcedures(Procedure<Env>[] subprocs) {
+1 for (Procedure<Env> subproc : subprocs) {
+1 subproc.updateMetricsOnSubmit(getEnvironment());
+1 procedures.put(subproc.getProcId(), subproc);
+1 scheduler.addFront(subproc);
+1 LOG.info("Sub-Procedure pid={} has been submitted", subproc.getProcId());
+1 }
+1 }
+1
+1 private void updateStoreOnExecution(
+1 RootProcedureStack rootProcStack, Procedure<Env> proc, Procedure<Env>[] subprocs) {
+1 if (subprocs != null && !proc.isFailed()) {
+1 if (LOG.isDebugEnabled()) {
+1 LOG.debug("Stored {}, children {}", proc, Arrays.toString(subprocs));
+1 }
+1 store.update(subprocs);
+1 } else {
+1 LOG.debug("Store update {}", proc);
+1 if (proc.isFinished() && !proc.hasParent()) {
+1 final long[] childProcIds = rootProcStack.getSubprocedureIds();
+1 if (childProcIds != null) {
+1 store.delete(childProcIds);
+1 for (long childProcId : childProcIds) {
+1 procedures.remove(childProcId);
+1 }
+1 } else {
+1 store.update(proc);
+1 }
+1 } else {
+1 store.update(proc);
+1 }
+1 }
+1 }
+1
+1 private Procedure<Env>[] initializeChildren(
+1 RootProcedureStack rootProcStack, Procedure<Env> proc, Procedure<Env>[] subprocs) {
+1 final long rootProcedureId = getRootProcedureId(proc);
+1 for (int i = 0; i < subprocs.length; i++) {
+1 Procedure<Env> subproc = subprocs[i];
+1 if (subproc == null) {
+1 String errMsg = "subproc[" + i + "] is null, aborting procedure";
+1 proc.setFailure(new ProcedureException((errMsg), new IllegalArgumentException(errMsg)));
+1 return null;
+1 }
+1 subproc.setParentProcId(proc.getProcId());
+1 subproc.setRootProcId(rootProcedureId);
+1 subproc.setProcId(store.getNextProcId());
+1 subproc.setProcRunnable();
+1 rootProcStack.addSubProcedure(subproc);
+1 }
+1
+1 if (!proc.isFailed()) {
+1 proc.setChildrenLatch(subprocs.length);
+1 switch (proc.getState()) {
+1 case RUNNABLE:
+1 proc.setState(ProcedureState.WAITING);
+1 break;
+1 case WAITING_TIMEOUT:
+1 timeoutExecutor.add(proc);
+1 break;
+1 default:
+1 break;
+1 }
+1 }
+1 return subprocs;
+1 }
+1
+1 private void yieldProcedure(Procedure<Env> proc) {
+1 releaseLock(proc, false);
+1 scheduler.yield(proc);
+1 }
+1
+1 /**
+1 * Rollback full root procedure stack.
+1 *
+1 * @param rootProcId root procedure id
+1 * @param procedureStack root procedure stack
+1 * @return lock state
+1 */
+1 private ProcedureLockState executeRootStackRollback(
+1 Long rootProcId, RootProcedureStack procedureStack) {
+1 Procedure<Env> rootProcedure = procedures.get(rootProcId);
+1 ProcedureException exception = rootProcedure.getException();
+1 if (exception == null) {
+1 exception = procedureStack.getException();
+1 rootProcedure.setFailure(exception);
+1 store.update(rootProcedure);
+1 }
+1 List<Procedure<Env>> subprocStack = procedureStack.getSubproceduresStack();
+1 int stackTail = subprocStack.size();
+1 while (stackTail-- > 0) {
+1 Procedure<Env> procedure = subprocStack.get(stackTail);
+1 if (procedure.isSuccess()) {
+1 subprocStack.remove(stackTail);
+1 cleanupAfterRollback(procedure);
+1 continue;
+1 }
+1 ProcedureLockState lockState = acquireLock(procedure);
+1 if (lockState != ProcedureLockState.LOCK_ACQUIRED) {
+1 return lockState;
+1 }
+1 lockState = executeRollback(procedure);
+1 releaseLock(procedure, false);
+1
+1 boolean abortRollback = lockState != ProcedureLockState.LOCK_ACQUIRED;
+1 abortRollback |= !isRunning() || !store.isRunning();
+1 if (abortRollback) {
+1 return lockState;
+1 }
+1
+1 if (!procedure.isFinished() && procedure.isYieldAfterExecution(this.environment)) {
+1 return ProcedureLockState.LOCK_YIELD_WAIT;
+1 }
+1
+1 if (procedure != rootProcedure) {
+1 executeCompletionCleanup(procedure);
+1 }
+1 }
+1
+1 LOG.info("Rolled back {}, time duration is {}", rootProcedure, rootProcedure.elapsedTime());
+1 rootProcedureCleanup(rootProcedure);
+1 return ProcedureLockState.LOCK_ACQUIRED;
+1 }
+1
+1 private ProcedureLockState acquireLock(Procedure<Env> proc) {
+1 if (proc.hasLock()) {
+1 return ProcedureLockState.LOCK_ACQUIRED;
+1 }
+1 return proc.doAcquireLock(this.environment, store);
+1 }
+1
+1 /**
+1 * do execute defined in procedure and then update store or remove completely in case it is a
+1 * child.
+1 *
+1 * @param procedure procedure
+1 * @return procedure lock state
+1 */
+1 private ProcedureLockState executeRollback(Procedure<Env> procedure) {
+1 try {
+1 procedure.doRollback(this.environment);
+1 } catch (IOException e) {
+1 LOG.error("Roll back failed for {}", procedure, e);
+1 } catch (InterruptedException e) {
+1 LOG.warn("Interrupted exception occured for {}", procedure, e);
+1 } catch (Throwable t) {
+1 LOG.error("CODE-BUG: runtime exception for {}", procedure, t);
+1 }
+1 cleanupAfterRollback(procedure);
+1 return ProcedureLockState.LOCK_ACQUIRED;
+1 }
+1
+1 private void cleanupAfterRollback(Procedure<Env> procedure) {
+1 if (procedure.removeStackIndex()) {
+1 if (!procedure.isSuccess()) {
+1 procedure.setState(ProcedureState.ROLLEDBACK);
+1 }
+1
+1 // update metrics on finishing the procedure (fail)
+1 procedure.updateMetricsOnFinish(getEnvironment(), procedure.elapsedTime(), false);
+1
+1 if (procedure.hasParent()) {
+1 store.delete(procedure.getProcId());
+1 procedures.remove(procedure.getProcId());
+1 } else {
+1 final long[] childProcIds = rollbackStack.get(procedure.getProcId()).getSubprocedureIds();
+1 if (childProcIds != null) {
+1 store.delete(childProcIds);
+1 } else {
+1 store.update(procedure);
+1 }
+1 }
+1 } else {
+1 store.update(procedure);
+1 }
+1 }
+1
+1 private void executeCompletionCleanup(Procedure<Env> proc) {
+1 if (proc.hasLock()) {
+1 releaseLock(proc, true);
+1 }
+1 }
+1
+1 private void rootProcedureCleanup(Procedure<Env> proc) {
+1 executeCompletionCleanup(proc);
+1 CompletedProcedureContainer<Env> retainer = new CompletedProcedureContainer<>(proc);
+1 completed.put(proc.getProcId(), retainer);
+1 rollbackStack.remove(proc.getProcId());
+1 procedures.remove(proc.getProcId());
+1 }
+1
+1 /**
+1 * Add a Procedure to executor.
+1 *
+1 * @param procedure procedure
+1 * @return procedure id
+1 */
+1 private long pushProcedure(Procedure<Env> procedure) {
+1 final long currentProcId = procedure.getProcId();
+1 // Update metrics on start of a procedure
+1 procedure.updateMetricsOnSubmit(getEnvironment());
+1 RootProcedureStack<Env> stack = new RootProcedureStack<>();
+1 rollbackStack.put(currentProcId, stack);
+1 procedures.put(currentProcId, procedure);
+1 scheduler.addBack(procedure);
+1 return procedure.getProcId();
+1 }
+1
+1 private class WorkerThread extends StoppableThread {
+1 private final AtomicLong startTime = new AtomicLong(Long.MAX_VALUE);
+1 private final AtomicReference<Procedure<Env>> activeProcedure = new AtomicReference<>();
+1 protected long keepAliveTime = -1;
+1
+1 public WorkerThread(ThreadGroup threadGroup) {
+1 this(threadGroup, "ProcedureCoreWorker-");
+1 }
+1
+1 public WorkerThread(ThreadGroup threadGroup, String prefix) {
+1 super(threadGroup, prefix + workId.incrementAndGet());
+1 setDaemon(true);
+1 }
+1
+1 @Override
+1 public void sendStopSignal() {
+1 scheduler.signalAll();
+1 }
+1
+1 @Override
+1 public void run() {
+1 long lastUpdated = System.currentTimeMillis();
+1 try {
+1 while (isRunning() && keepAlive(lastUpdated)) {
+1 Procedure<Env> procedure = scheduler.poll(keepAliveTime, TimeUnit.MILLISECONDS);
+1 if (procedure == null) {
+1 Thread.sleep(1000);
+1 continue;
+1 }
+1 this.activeProcedure.set(procedure);
+1 activeExecutorCount.incrementAndGet();
+1 startTime.set(System.currentTimeMillis());
+1 executeProcedure(procedure);
+1 activeExecutorCount.decrementAndGet();
+1 LOG.trace(
+1 "Halt pid={}, activeCount={}", procedure.getProcId(), activeExecutorCount.get());
+1 this.activeProcedure.set(null);
+1 lastUpdated = System.currentTimeMillis();
+1 startTime.set(lastUpdated);
+1 }
+1
+1 } catch (Exception e) {
+1 if (this.activeProcedure.get() != null) {
+1 LOG.warn(
+1 "Exception happened when worker {} execute procedure {}",
+1 getName(),
+1 this.activeProcedure.get(),
+1 e);
+1 }
+1 } finally {
+1 LOG.info("Procedure worker {} terminated.", getName());
+1 }
+1 workerThreads.remove(this);
+1 }
+1
+1 protected boolean keepAlive(long lastUpdated) {
+1 return true;
+1 }
+1
+1 @Override
+1 public String toString() {
+1 Procedure<?> p = this.activeProcedure.get();
+1 return getName() + "(pid=" + (p == null ? NO_PROC_ID : p.getProcId() + ")");
+1 }
+1
+1 /**
+1 * @return the time since the current procedure is running
+1 */
+1 public long getCurrentRunTime() {
+1 return System.currentTimeMillis() - startTime.get();
+1 }
+1 }
+1
+1 // A temporary worker thread will be launched when too many core workers are stuck.
+1 // They will timeout after keepAliveTime if there is no procedure to run.
+1 private final class TemporaryWorkerThread extends WorkerThread {
+1
+1 public TemporaryWorkerThread(ThreadGroup group) {
+1 super(group, "ProcedureTemporaryWorker-");
+1 this.keepAliveTime = TimeUnit.SECONDS.toMillis(10);
+1 }
+1
+1 @Override
+1 protected boolean keepAlive(long lastUpdate) {
+1 return System.currentTimeMillis() - lastUpdate < keepAliveTime;
+1 }
+1 }
+1
+1 private final class WorkerMonitor extends InternalProcedure<Env> {
+1 private static final int DEFAULT_WORKER_MONITOR_INTERVAL = 30000; // 30sec
+1
+1 private static final int DEFAULT_WORKER_STUCK_THRESHOLD = 60000; // 60sec
+1
+1 private static final float DEFAULT_WORKER_ADD_STUCK_PERCENTAGE = 0.5f; // 50% stuck
+1
+1 public WorkerMonitor() {
+1 super(DEFAULT_WORKER_MONITOR_INTERVAL);
+1 updateTimestamp();
+1 }
+1
+1 private int calculateRunningAndStuckWorkers() {
+1 // Check if any of the worker is stuck
+1 int runningCount = 0, stuckCount = 0;
+1 for (WorkerThread worker : workerThreads) {
+1 final Procedure<?> proc = worker.activeProcedure.get();
+1 if (proc == null) {
+1 continue;
+1 }
+1 runningCount++;
+1 // WARN the worker is stuck
+1 if (worker.getCurrentRunTime() > DEFAULT_WORKER_STUCK_THRESHOLD) {
+1 stuckCount++;
+1 LOG.warn(
+1 "Worker stuck {}({}), run time {} ms",
+1 worker,
+1 proc.getProcType(),
+1 worker.getCurrentRunTime());
+1 }
+1 LOG.info(
+1 "Procedure workers: {} is running, {} is running and stuck", runningCount, stuckCount);
+1 }
+1 return stuckCount;
+1 }
+1
+1 private void checkThreadCount(final int stuckCount) {
+1 // Nothing to do if there are no runnable tasks
+1 if (stuckCount < 1 || !scheduler.hasRunnables()) {
+1 return;
+1 }
+1 // Add a new thread if the worker stuck percentage exceed the threshold limit
+1 // and every handler is active.
+1 final float stuckPerc = ((float) stuckCount) / workerThreads.size();
+1 // Let's add new worker thread more aggressively, as they will timeout finally if there is no
+1 // work to do.
+1 if (stuckPerc >= DEFAULT_WORKER_ADD_STUCK_PERCENTAGE && workerThreads.size() < maxPoolSize) {
+1 final TemporaryWorkerThread worker = new TemporaryWorkerThread(threadGroup);
+1 workerThreads.add(worker);
+1 worker.start();
+1 LOG.debug("Added new worker thread {}", worker);
+1 }
+1 }
+1
+1 @Override
+1 protected void periodicExecute(Env env) {
+1 final int stuckCount = calculateRunningAndStuckWorkers();
+1 checkThreadCount(stuckCount);
+1 updateTimestamp();
+1 }
+1 }
+1
+1 public int getWorkerThreadCount() {
+1 return workerThreads.size();
+1 }
+1
+1 public long getActiveWorkerThreadCount() {
+1 return workerThreads.stream().filter(worker -> worker.activeProcedure.get() != null).count();
+1 }
+1
+1 public boolean isRunning() {
+1 return running.get();
+1 }
+1
+1 public void stop() {
+1 if (!running.getAndSet(false)) {
+1 return;
+1 }
+1 LOG.info("Stopping");
+1 scheduler.stop();
+1 timeoutExecutor.sendStopSignal();
+1 }
+1
+1 public void join() {
+1 timeoutExecutor.awaitTermination();
+1 workerMonitorExecutor.awaitTermination();
+1 for (WorkerThread workerThread : workerThreads) {
+1 workerThread.awaitTermination();
+1 }
+1 try {
+1 threadGroup.destroy();
+1 } catch (IllegalThreadStateException e) {
+1 LOG.warn(
+1 "ProcedureExecutor threadGroup {} contains running threads which are used by non-procedure module.",
+1 this.threadGroup);
+1 this.threadGroup.list();
+1 }
+1 }
+1
+1 public boolean isStarted(long procId) {
+1 Procedure<Env> procedure = procedures.get(procId);
+1 if (procedure == null) {
+1 return completed.get(procId) != null;
+1 }
+1 return procedure.wasExecuted();
+1 }
+1
+1 public boolean isFinished(final long procId) {
+1 return !procedures.containsKey(procId);
+1 }
+1
+1 public ConcurrentHashMap<Long, Procedure<Env>> getProcedures() {
+1 return procedures;
+1 }
+1
+1 // -----------------------------CLIENT IMPLEMENTATION-----------------------------------
+1 /**
+1 * Submit a new root-procedure to the executor, called by client.
+1 *
+1 * @param procedure root procedure
+1 * @return procedure id
+1 */
+1 public long submitProcedure(Procedure<Env> procedure) {
+1 Preconditions.checkArgument(procedure.getState() == ProcedureState.INITIALIZING);
+1 Preconditions.checkArgument(!procedure.hasParent(), "Unexpected parent", procedure);
+1 // Initialize the procedure
+1 procedure.setProcId(store.getNextProcId());
+1 procedure.setProcRunnable();
+1 // Commit the transaction
+1 store.update(procedure);
+1 LOG.debug("{} is stored.", procedure);
+1 // Add the procedure to the executor
+1 return pushProcedure(procedure);
+1 }
+1
+1 public ProcedureScheduler getScheduler() {
+1 return scheduler;
+1 }
+1
+1 public Env getEnvironment() {
+1 return environment;
+1 }
+1
+1 public IProcedureStore getStore() {
+1 return store;
+1 }
+1
+1 public RootProcedureStack<Env> getRollbackStack(long rootProcId) {
+1 return rollbackStack.get(rootProcId);
+1 }
+1}
+1
\ No newline at end of file
diff --git a/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/procedure/ProcedureMetrics.java b/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/procedure/ProcedureMetrics.java
index 25533667431..8ac6ff8f99b 100644
--- a/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/procedure/ProcedureMetrics.java
+++ b/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/procedure/ProcedureMetrics.java
@@ -1,184 +1,185 @@
-/*
- * 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.confignode.procedure;
-
-import org.apache.iotdb.commons.service.metric.enums.Metric;
-import org.apache.iotdb.commons.service.metric.enums.Tag;
-import org.apache.iotdb.confignode.manager.ProcedureManager;
-import org.apache.iotdb.confignode.procedure.env.ConfigNodeProcedureEnv;
-import org.apache.iotdb.confignode.procedure.scheduler.ProcedureScheduler;
-import org.apache.iotdb.metrics.AbstractMetricService;
-import org.apache.iotdb.metrics.impl.DoNothingMetricManager;
-import org.apache.iotdb.metrics.metricsets.IMetricSet;
-import org.apache.iotdb.metrics.type.Counter;
-import org.apache.iotdb.metrics.type.Timer;
-import org.apache.iotdb.metrics.utils.MetricLevel;
-import org.apache.iotdb.metrics.utils.MetricType;
-
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-import java.util.Map;
-import java.util.Optional;
-import java.util.concurrent.ConcurrentHashMap;
-
-public class ProcedureMetrics implements IMetricSet {
- private static final Logger LOGGER = LoggerFactory.getLogger(ProcedureMetrics.class);
-
- private final ProcedureManager procedureManager;
-
- private final ProcedureExecutor<ConfigNodeProcedureEnv> executor;
-
- private final ProcedureScheduler scheduler;
-
- private final Map<String, ProcedureMetricItems> metricItemsMap = new ConcurrentHashMap<>();
-
- private AbstractMetricService metricService;
-
- public ProcedureMetrics(ProcedureManager procedureManager) {
- this.procedureManager = procedureManager;
- this.executor = procedureManager.getExecutor();
- this.scheduler = procedureManager.getScheduler();
- }
-
- @Override
- public void bindTo(AbstractMetricService metricService) {
- this.metricService = metricService;
- bindThreadMetrics(metricService);
- }
-
- @Override
- public void unbindFrom(AbstractMetricService metricService) {
- unbindThreadMetrics(metricService);
-
- for (ProcedureMetricItems items : metricItemsMap.values()) {
- items.unbindMetricItems(metricService);
- }
- metricItemsMap.clear();
- }
-
- private void bindThreadMetrics(AbstractMetricService metricService) {
- metricService.createAutoGauge(
- Metric.PROCEDURE_WORKER_THREAD_COUNT.toString(),
- MetricLevel.CORE,
- executor,
- ProcedureExecutor::getWorkerThreadCount);
- metricService.createAutoGauge(
- Metric.PROCEDURE_ACTIVE_WORKER_THREAD_COUNT.toString(),
- MetricLevel.CORE,
- executor,
- ProcedureExecutor::getActiveWorkerThreadCount);
- metricService.createAutoGauge(
- Metric.PROCEDURE_QUEUE_LENGTH.toString(),
- MetricLevel.CORE,
- scheduler,
- ProcedureScheduler::size);
- }
-
- private void unbindThreadMetrics(AbstractMetricService metricService) {
- metricService.remove(MetricType.AUTO_GAUGE, Metric.PROCEDURE_WORKER_THREAD_COUNT.toString());
- metricService.remove(
- MetricType.AUTO_GAUGE, Metric.PROCEDURE_ACTIVE_WORKER_THREAD_COUNT.toString());
- metricService.remove(MetricType.AUTO_GAUGE, Metric.PROCEDURE_QUEUE_LENGTH.toString());
- }
-
- public void updateMetricsOnSubmit(String procType) {
- Optional.ofNullable(metricService)
- .ifPresent(
- service -> {
- metricItemsMap
- .computeIfAbsent(procType, k -> new ProcedureMetricItems(k, service))
- .updateMetricsOnSubmit();
- });
- }
-
- public void updateMetricsOnFinish(String procType, long runtime, boolean success) {
- Optional.ofNullable(metricService)
- .ifPresent(
- service -> {
- metricItemsMap
- .computeIfAbsent(procType, k -> new ProcedureMetricItems(k, service))
- .updateMetricsOnFinish(runtime, success);
- });
- }
-
- class ProcedureMetricItems {
- private final String procType;
- private Counter submittedCounter = DoNothingMetricManager.DO_NOTHING_COUNTER;
- private Counter failedCounter = DoNothingMetricManager.DO_NOTHING_COUNTER;
- private Timer executionTimer = DoNothingMetricManager.DO_NOTHING_TIMER;
-
- public ProcedureMetricItems(String procType, AbstractMetricService metricService) {
- this.procType = procType;
- bindMetricItems(metricService);
- }
-
- public void bindMetricItems(AbstractMetricService metricService) {
- this.submittedCounter =
- metricService.getOrCreateCounter(
- Metric.PROCEDURE_SUBMITTED_COUNT.toString(),
- MetricLevel.CORE,
- Tag.TYPE.toString(),
- procType);
- this.failedCounter =
- metricService.getOrCreateCounter(
- Metric.PROCEDURE_FAILED_COUNT.toString(),
- MetricLevel.CORE,
- Tag.TYPE.toString(),
- procType);
- this.executionTimer =
- metricService.getOrCreateTimer(
- Metric.PROCEDURE_EXECUTION_TIME.toString(),
- MetricLevel.CORE,
- Tag.TYPE.toString(),
- procType);
- }
-
- public void unbindMetricItems(AbstractMetricService metricService) {
- metricService.remove(
- MetricType.COUNTER,
- Metric.PROCEDURE_SUBMITTED_COUNT.toString(),
- Tag.TYPE.toString(),
- procType);
- metricService.remove(
- MetricType.COUNTER,
- Metric.PROCEDURE_FAILED_COUNT.toString(),
- Tag.TYPE.toString(),
- procType);
- metricService.remove(
- MetricType.TIMER,
- Metric.PROCEDURE_EXECUTION_TIME.toString(),
- Tag.TYPE.toString(),
- procType);
- }
-
- public void updateMetricsOnSubmit() {
- submittedCounter.inc();
- }
-
- public void updateMetricsOnFinish(long runtime, boolean success) {
- if (success) {
- executionTimer.updateMillis(runtime);
- } else {
- failedCounter.inc();
- }
- }
- }
-}
+1/*
+1 * Licensed to the Apache Software Foundation (ASF) under one
+1 * or more contributor license agreements. See the NOTICE file
+1 * distributed with this work for additional information
+1 * regarding copyright ownership. The ASF licenses this file
+1 * to you under the Apache License, Version 2.0 (the
+1 * "License"); you may not use this file except in compliance
+1 * with the License. You may obtain a copy of the License at
+1 *
+1 * http://www.apache.org/licenses/LICENSE-2.0
+1 *
+1 * Unless required by applicable law or agreed to in writing,
+1 * software distributed under the License is distributed on an
+1 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+1 * KIND, either express or implied. See the License for the
+1 * specific language governing permissions and limitations
+1 * under the License.
+1 */
+1
+1package org.apache.iotdb.confignode.procedure;
+1
+1import org.apache.iotdb.commons.service.metric.enums.Metric;
+1import org.apache.iotdb.commons.service.metric.enums.Tag;
+1import org.apache.iotdb.confignode.manager.ProcedureManager;
+1import org.apache.iotdb.confignode.procedure.env.ConfigNodeProcedureEnv;
+1import org.apache.iotdb.confignode.procedure.scheduler.ProcedureScheduler;
+1import org.apache.iotdb.metrics.AbstractMetricService;
+1import org.apache.iotdb.metrics.impl.DoNothingMetricManager;
+1import org.apache.iotdb.metrics.metricsets.IMetricSet;
+1import org.apache.iotdb.metrics.type.Counter;
+1import org.apache.iotdb.metrics.type.Timer;
+1import org.apache.iotdb.metrics.utils.MetricLevel;
+1import org.apache.iotdb.metrics.utils.MetricType;
+1
+1import org.slf4j.Logger;
+1import org.slf4j.LoggerFactory;
+1
+1import java.util.Map;
+1import java.util.Optional;
+1import java.util.concurrent.ConcurrentHashMap;
+1
+1public class ProcedureMetrics implements IMetricSet {
+1 private static final Logger LOGGER = LoggerFactory.getLogger(ProcedureMetrics.class);
+1
+1 private final ProcedureManager procedureManager;
+1
+1 private final ProcedureExecutor<ConfigNodeProcedureEnv> executor;
+1
+1 private final ProcedureScheduler scheduler;
+1
+1 private final Map<String, ProcedureMetricItems> metricItemsMap = new ConcurrentHashMap<>();
+1
+1 private AbstractMetricService metricService;
+1
+1 public ProcedureMetrics(ProcedureManager procedureManager) {
+1 this.procedureManager = procedureManager;
+1 this.executor = procedureManager.getExecutor();
+1 this.scheduler = procedureManager.getScheduler();
+1 }
+1
+1 @Override
+1 public void bindTo(AbstractMetricService metricService) {
+1 this.metricService = metricService;
+1 bindThreadMetrics(metricService);
+1 }
+1
+1 @Override
+1 public void unbindFrom(AbstractMetricService metricService) {
+1 unbindThreadMetrics(metricService);
+1
+1 for (ProcedureMetricItems items : metricItemsMap.values()) {
+1 items.unbindMetricItems(metricService);
+1 }
+1 metricItemsMap.clear();
+1 }
+1
+1 private void bindThreadMetrics(AbstractMetricService metricService) {
+1 metricService.createAutoGauge(
+1 Metric.PROCEDURE_WORKER_THREAD_COUNT.toString(),
+1 MetricLevel.CORE,
+1 executor,
+1 ProcedureExecutor::getWorkerThreadCount);
+1 metricService.createAutoGauge(
+1 Metric.PROCEDURE_ACTIVE_WORKER_THREAD_COUNT.toString(),
+1 MetricLevel.CORE,
+1 executor,
+1 ProcedureExecutor::getActiveWorkerThreadCount);
+1 metricService.createAutoGauge(
+1 Metric.PROCEDURE_QUEUE_LENGTH.toString(),
+1 MetricLevel.CORE,
+1 scheduler,
+1 ProcedureScheduler::size);
+1 }
+1
+1 private void unbindThreadMetrics(AbstractMetricService metricService) {
+1 metricService.remove(MetricType.AUTO_GAUGE, Metric.PROCEDURE_WORKER_THREAD_COUNT.toString());
+1 metricService.remove(
+1 MetricType.AUTO_GAUGE, Metric.PROCEDURE_ACTIVE_WORKER_THREAD_COUNT.toString());
+1 metricService.remove(MetricType.AUTO_GAUGE, Metric.PROCEDURE_QUEUE_LENGTH.toString());
+1 }
+1
+1 public void updateMetricsOnSubmit(String procType) {
+1 Optional.ofNullable(metricService)
+1 .ifPresent(
+1 service -> {
+1 metricItemsMap
+1 .computeIfAbsent(procType, k -> new ProcedureMetricItems(k, service))
+1 .updateMetricsOnSubmit();
+1 });
+1 }
+1
+1 public void updateMetricsOnFinish(String procType, long runtime, boolean success) {
+1 Optional.ofNullable(metricService)
+1 .ifPresent(
+1 service -> {
+1 metricItemsMap
+1 .computeIfAbsent(procType, k -> new ProcedureMetricItems(k, service))
+1 .updateMetricsOnFinish(runtime, success);
+1 });
+1 }
+1
+1 class ProcedureMetricItems {
+1 private final String procType;
+1 private Counter submittedCounter = DoNothingMetricManager.DO_NOTHING_COUNTER;
+1 private Counter failedCounter = DoNothingMetricManager.DO_NOTHING_COUNTER;
+1 private Timer executionTimer = DoNothingMetricManager.DO_NOTHING_TIMER;
+1
+1 public ProcedureMetricItems(String procType, AbstractMetricService metricService) {
+1 this.procType = procType;
+1 bindMetricItems(metricService);
+1 }
+1
+1 public void bindMetricItems(AbstractMetricService metricService) {
+1 this.submittedCounter =
+1 metricService.getOrCreateCounter(
+1 Metric.PROCEDURE_SUBMITTED_COUNT.toString(),
+1 MetricLevel.CORE,
+1 Tag.TYPE.toString(),
+1 procType);
+1 this.failedCounter =
+1 metricService.getOrCreateCounter(
+1 Metric.PROCEDURE_FAILED_COUNT.toString(),
+1 MetricLevel.CORE,
+1 Tag.TYPE.toString(),
+1 procType);
+1 this.executionTimer =
+1 metricService.getOrCreateTimer(
+1 Metric.PROCEDURE_EXECUTION_TIME.toString(),
+1 MetricLevel.CORE,
+1 Tag.TYPE.toString(),
+1 procType);
+1 }
+1
+1 public void unbindMetricItems(AbstractMetricService metricService) {
+1 metricService.remove(
+1 MetricType.COUNTER,
+1 Metric.PROCEDURE_SUBMITTED_COUNT.toString(),
+1 Tag.TYPE.toString(),
+1 procType);
+1 metricService.remove(
+1 MetricType.COUNTER,
+1 Metric.PROCEDURE_FAILED_COUNT.toString(),
+1 Tag.TYPE.toString(),
+1 procType);
+1 metricService.remove(
+1 MetricType.TIMER,
+1 Metric.PROCEDURE_EXECUTION_TIME.toString(),
+1 Tag.TYPE.toString(),
+1 procType);
+1 }
+1
+1 public void updateMetricsOnSubmit() {
+1 submittedCounter.inc();
+1 }
+1
+1 public void updateMetricsOnFinish(long runtime, boolean success) {
+1 if (success) {
+1 executionTimer.updateMillis(runtime);
+1 } else {
+1 failedCounter.inc();
+1 }
+1 }
+1 }
+1}
+1
\ No newline at end of file
diff --git a/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/procedure/RootProcedureStack.java b/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/procedure/RootProcedureStack.java
index ff19872a550..88caab664cd 100644
--- a/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/procedure/RootProcedureStack.java
+++ b/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/procedure/RootProcedureStack.java
@@ -1,177 +1,178 @@
-/*
- * 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.confignode.procedure;
-
-import org.apache.iotdb.confignode.procedure.exception.ProcedureException;
-import org.apache.iotdb.confignode.procedure.state.ProcedureState;
-
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-import java.util.ArrayList;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Set;
-
-public class RootProcedureStack<Env> {
- private static final Logger LOG = LoggerFactory.getLogger(RootProcedureStack.class);
-
- private enum State {
- RUNNING, // The Procedure is running or ready to run
- FAILED, // The Procedure failed, waiting for the rollback executing
- ROLLINGBACK, // The Procedure failed and the execution was rolledback
- }
-
- private Set<Procedure<Env>> subprocs = null;
- private ArrayList<Procedure<Env>> subprocStack = null;
- private State state = State.RUNNING;
- private int running = 0;
-
- public synchronized boolean isFailed() {
- switch (state) {
- case ROLLINGBACK:
- case FAILED:
- return true;
- default:
- break;
- }
- return false;
- }
-
- public synchronized boolean isRollingback() {
- return state == State.ROLLINGBACK;
- }
-
- /** Called by the {@link ProcedureExecutor} to mark rollback execution. */
- protected synchronized boolean setRollback() {
- if (running == 0 && state == State.FAILED) {
- state = State.ROLLINGBACK;
- return true;
- }
- return false;
- }
-
- /** Called by the {@link ProcedureExecutor} to mark rollback execution. */
- protected synchronized void unsetRollback() {
- assert state == State.ROLLINGBACK;
- state = State.FAILED;
- }
-
- protected synchronized long[] getSubprocedureIds() {
- if (subprocs == null) {
- return new long[0];
- }
- return subprocs.stream().mapToLong(Procedure::getProcId).toArray();
- }
-
- protected synchronized List<Procedure<Env>> getSubproceduresStack() {
- return subprocStack;
- }
-
- protected synchronized ProcedureException getException() {
- if (subprocStack != null) {
- for (Procedure<Env> proc : subprocStack) {
- if (proc.hasException()) {
- return proc.getException();
- }
- }
- }
- return null;
- }
-
- /** Called by the {@link ProcedureExecutor} to mark the procedure step as running. */
- protected synchronized boolean acquire() {
- if (state != State.RUNNING) {
- return false;
- }
-
- running++;
- return true;
- }
-
- /** Called by the {@link ProcedureExecutor} to mark the procedure step as finished. */
- protected synchronized void release() {
- running--;
- }
-
- protected synchronized void abort() {
- if (state == State.RUNNING) {
- state = State.FAILED;
- }
- }
-
- /**
- * Called by the {@link ProcedureExecutor} after the procedure step is completed, to add the step
- * to the rollback list (or procedure stack).
- */
- protected synchronized void addRollbackStep(Procedure<Env> proc) {
- if (proc.isFailed()) {
- state = State.FAILED;
- }
- if (subprocStack == null) {
- subprocStack = new ArrayList<>();
- }
- proc.addStackIndex(subprocStack.size());
- LOG.trace("Add procedure {} as the {}th rollback step", proc, subprocStack.size());
- subprocStack.add(proc);
- }
-
- protected synchronized void addSubProcedure(Procedure<Env> proc) {
- if (!proc.hasParent()) {
- return;
- }
- if (subprocs == null) {
- subprocs = new HashSet<>();
- }
- subprocs.add(proc);
- }
-
- /**
- * Called on store load by the {@link ProcedureExecutor} to load part of the stack.
- *
- * <p>Each procedure has its own stack-positions. Which means we have to write to the store only
- * the Procedure we executed, and nothing else. On load we recreate the full stack by aggregating
- * each procedure stack-positions.
- */
- protected synchronized void loadStack(Procedure<Env> proc) {
- addSubProcedure(proc);
- int[] stackIndexes = proc.getStackIndexes();
- if (stackIndexes != null) {
- if (subprocStack == null) {
- subprocStack = new ArrayList<>();
- }
- int diff = (1 + stackIndexes[stackIndexes.length - 1]) - subprocStack.size();
- if (diff > 0) {
- subprocStack.ensureCapacity(1 + stackIndexes[stackIndexes.length - 1]);
- while (diff-- > 0) {
- subprocStack.add(null);
- }
- }
- for (int stackIndex : stackIndexes) {
- subprocStack.set(stackIndex, proc);
- }
- }
- if (proc.getState() == ProcedureState.ROLLEDBACK) {
- state = State.ROLLINGBACK;
- } else if (proc.isFailed()) {
- state = State.FAILED;
- }
- }
-}
+1/*
+1 * Licensed to the Apache Software Foundation (ASF) under one
+1 * or more contributor license agreements. See the NOTICE file
+1 * distributed with this work for additional information
+1 * regarding copyright ownership. The ASF licenses this file
+1 * to you under the Apache License, Version 2.0 (the
+1 * "License"); you may not use this file except in compliance
+1 * with the License. You may obtain a copy of the License at
+1 *
+1 * http://www.apache.org/licenses/LICENSE-2.0
+1 *
+1 * Unless required by applicable law or agreed to in writing,
+1 * software distributed under the License is distributed on an
+1 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+1 * KIND, either express or implied. See the License for the
+1 * specific language governing permissions and limitations
+1 * under the License.
+1 */
+1
+1package org.apache.iotdb.confignode.procedure;
+1
+1import org.apache.iotdb.confignode.procedure.exception.ProcedureException;
+1import org.apache.iotdb.confignode.procedure.state.ProcedureState;
+1
+1import org.slf4j.Logger;
+1import org.slf4j.LoggerFactory;
+1
+1import java.util.ArrayList;
+1import java.util.HashSet;
+1import java.util.List;
+1import java.util.Set;
+1
+1public class RootProcedureStack<Env> {
+1 private static final Logger LOG = LoggerFactory.getLogger(RootProcedureStack.class);
+1
+1 private enum State {
+1 RUNNING, // The Procedure is running or ready to run
+1 FAILED, // The Procedure failed, waiting for the rollback executing
+1 ROLLINGBACK, // The Procedure failed and the execution was rolledback
+1 }
+1
+1 private Set<Procedure<Env>> subprocs = null;
+1 private ArrayList<Procedure<Env>> subprocStack = null;
+1 private State state = State.RUNNING;
+1 private int running = 0;
+1
+1 public synchronized boolean isFailed() {
+1 switch (state) {
+1 case ROLLINGBACK:
+1 case FAILED:
+1 return true;
+1 default:
+1 break;
+1 }
+1 return false;
+1 }
+1
+1 public synchronized boolean isRollingback() {
+1 return state == State.ROLLINGBACK;
+1 }
+1
+1 /** Called by the {@link ProcedureExecutor} to mark rollback execution. */
+1 protected synchronized boolean setRollback() {
+1 if (running == 0 && state == State.FAILED) {
+1 state = State.ROLLINGBACK;
+1 return true;
+1 }
+1 return false;
+1 }
+1
+1 /** Called by the {@link ProcedureExecutor} to mark rollback execution. */
+1 protected synchronized void unsetRollback() {
+1 assert state == State.ROLLINGBACK;
+1 state = State.FAILED;
+1 }
+1
+1 protected synchronized long[] getSubprocedureIds() {
+1 if (subprocs == null) {
+1 return new long[0];
+1 }
+1 return subprocs.stream().mapToLong(Procedure::getProcId).toArray();
+1 }
+1
+1 protected synchronized List<Procedure<Env>> getSubproceduresStack() {
+1 return subprocStack;
+1 }
+1
+1 protected synchronized ProcedureException getException() {
+1 if (subprocStack != null) {
+1 for (Procedure<Env> proc : subprocStack) {
+1 if (proc.hasException()) {
+1 return proc.getException();
+1 }
+1 }
+1 }
+1 return null;
+1 }
+1
+1 /** Called by the {@link ProcedureExecutor} to mark the procedure step as running. */
+1 protected synchronized boolean acquire() {
+1 if (state != State.RUNNING) {
+1 return false;
+1 }
+1
+1 running++;
+1 return true;
+1 }
+1
+1 /** Called by the {@link ProcedureExecutor} to mark the procedure step as finished. */
+1 protected synchronized void release() {
+1 running--;
+1 }
+1
+1 protected synchronized void abort() {
+1 if (state == State.RUNNING) {
+1 state = State.FAILED;
+1 }
+1 }
+1
+1 /**
+1 * Called by the {@link ProcedureExecutor} after the procedure step is completed, to add the step
+1 * to the rollback list (or procedure stack).
+1 */
+1 protected synchronized void addRollbackStep(Procedure<Env> proc) {
+1 if (proc.isFailed()) {
+1 state = State.FAILED;
+1 }
+1 if (subprocStack == null) {
+1 subprocStack = new ArrayList<>();
+1 }
+1 proc.addStackIndex(subprocStack.size());
+1 LOG.trace("Add procedure {} as the {}th rollback step", proc, subprocStack.size());
+1 subprocStack.add(proc);
+1 }
+1
+1 protected synchronized void addSubProcedure(Procedure<Env> proc) {
+1 if (!proc.hasParent()) {
+1 return;
+1 }
+1 if (subprocs == null) {
+1 subprocs = new HashSet<>();
+1 }
+1 subprocs.add(proc);
+1 }
+1
+1 /**
+1 * Called on store load by the {@link ProcedureExecutor} to load part of the stack.
+1 *
+1 * <p>Each procedure has its own stack-positions. Which means we have to write to the store only
+1 * the Procedure we executed, and nothing else. On load we recreate the full stack by aggregating
+1 * each procedure stack-positions.
+1 */
+1 protected synchronized void loadStack(Procedure<Env> proc) {
+1 addSubProcedure(proc);
+1 int[] stackIndexes = proc.getStackIndexes();
+1 if (stackIndexes != null) {
+1 if (subprocStack == null) {
+1 subprocStack = new ArrayList<>();
+1 }
+1 int diff = (1 + stackIndexes[stackIndexes.length - 1]) - subprocStack.size();
+1 if (diff > 0) {
+1 subprocStack.ensureCapacity(1 + stackIndexes[stackIndexes.length - 1]);
+1 while (diff-- > 0) {
+1 subprocStack.add(null);
+1 }
+1 }
+1 for (int stackIndex : stackIndexes) {
+1 subprocStack.set(stackIndex, proc);
+1 }
+1 }
+1 if (proc.getState() == ProcedureState.ROLLEDBACK) {
+1 state = State.ROLLINGBACK;
+1 } else if (proc.isFailed()) {
+1 state = State.FAILED;
+1 }
+1 }
+1}
+1
\ No newline at end of file
diff --git a/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/procedure/StoppableThread.java b/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/procedure/StoppableThread.java
index 2e5bf8dd203..5e6297d3d97 100644
--- a/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/procedure/StoppableThread.java
+++ b/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/procedure/StoppableThread.java
@@ -1,55 +1,56 @@
-/*
- * 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.confignode.procedure;
-
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-public abstract class StoppableThread extends Thread {
-
- private static final int JOIN_TIMEOUT = 250;
- private static final Logger LOG = LoggerFactory.getLogger(StoppableThread.class);
-
- protected StoppableThread(ThreadGroup threadGroup, String name) {
- super(threadGroup, name);
- }
-
- public abstract void sendStopSignal();
-
- public void awaitTermination() {
- try {
- for (int i = 0; isAlive(); i++) {
- sendStopSignal();
- join(JOIN_TIMEOUT);
- if (i > 0 && (i % 8) == 0) {
- interrupt();
- }
- }
- } catch (InterruptedException e) {
- LOG.warn("{} join wait got interrupted", getName(), e);
- Thread.currentThread().interrupt();
- }
- }
-
- @Override
- public void run() {
- // empty method
- }
-}
+1/*
+1 * Licensed to the Apache Software Foundation (ASF) under one
+1 * or more contributor license agreements. See the NOTICE file
+1 * distributed with this work for additional information
+1 * regarding copyright ownership. The ASF licenses this file
+1 * to you under the Apache License, Version 2.0 (the
+1 * "License"); you may not use this file except in compliance
+1 * with the License. You may obtain a copy of the License at
+1 *
+1 * http://www.apache.org/licenses/LICENSE-2.0
+1 *
+1 * Unless required by applicable law or agreed to in writing,
+1 * software distributed under the License is distributed on an
+1 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+1 * KIND, either express or implied. See the License for the
+1 * specific language governing permissions and limitations
+1 * under the License.
+1 */
+1
+1package org.apache.iotdb.confignode.procedure;
+1
+1import org.slf4j.Logger;
+1import org.slf4j.LoggerFactory;
+1
+1public abstract class StoppableThread extends Thread {
+1
+1 private static final int JOIN_TIMEOUT = 250;
+1 private static final Logger LOG = LoggerFactory.getLogger(StoppableThread.class);
+1
+1 protected StoppableThread(ThreadGroup threadGroup, String name) {
+1 super(threadGroup, name);
+1 }
+1
+1 public abstract void sendStopSignal();
+1
+1 public void awaitTermination() {
+1 try {
+1 for (int i = 0; isAlive(); i++) {
+1 sendStopSignal();
+1 join(JOIN_TIMEOUT);
+1 if (i > 0 && (i % 8) == 0) {
+1 interrupt();
+1 }
+1 }
+1 } catch (InterruptedException e) {
+1 LOG.warn("{} join wait got interrupted", getName(), e);
+1 Thread.currentThread().interrupt();
+1 }
+1 }
+1
+1 @Override
+1 public void run() {
+1 // empty method
+1 }
+1}
+1
\ No newline at end of file
diff --git a/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/procedure/TimeoutExecutorThread.java b/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/procedure/TimeoutExecutorThread.java
index 5aaf9a623f5..1b028fa1288 100644
--- a/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/procedure/TimeoutExecutorThread.java
+++ b/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/procedure/TimeoutExecutorThread.java
@@ -1,107 +1,108 @@
-/*
- * 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.confignode.procedure;
-
-import java.util.concurrent.DelayQueue;
-import java.util.concurrent.Delayed;
-import java.util.concurrent.TimeUnit;
-
-public class TimeoutExecutorThread<Env> extends StoppableThread {
-
- private static final int DELAY_QUEUE_TIMEOUT = 20;
- private final ProcedureExecutor<Env> executor;
- private final DelayQueue<ProcedureDelayContainer<Env>> queue = new DelayQueue<>();
-
- public TimeoutExecutorThread(
- ProcedureExecutor<Env> envProcedureExecutor, ThreadGroup threadGroup, String name) {
- super(threadGroup, name);
- setDaemon(true);
- this.executor = envProcedureExecutor;
- }
-
- public void add(Procedure<Env> procedure) {
- queue.add(new ProcedureDelayContainer<>(procedure));
- }
-
- public boolean remove(Procedure<Env> procedure) {
- return queue.remove(new ProcedureDelayContainer<>(procedure));
- }
-
- private ProcedureDelayContainer<Env> takeQuietly() {
- try {
- return queue.poll(DELAY_QUEUE_TIMEOUT, TimeUnit.SECONDS);
- } catch (InterruptedException e) {
- currentThread().interrupt();
- return null;
- }
- }
-
- @Override
- public void run() {
- while (executor.isRunning()) {
- ProcedureDelayContainer<Env> delayTask = takeQuietly();
- if (delayTask == null) {
- continue;
- }
- Procedure<Env> procedure = delayTask.getProcedure();
- if (procedure instanceof InternalProcedure) {
- InternalProcedure internal = (InternalProcedure) procedure;
- internal.periodicExecute(executor.getEnvironment());
- procedure.updateTimestamp();
- queue.add(delayTask);
- } else {
- if (procedure.setTimeoutFailure(executor.getEnvironment())) {
- long rootProcId = executor.getRootProcedureId(procedure);
- RootProcedureStack<Env> rollbackStack = executor.getRollbackStack(rootProcId);
- rollbackStack.abort();
- executor.getStore().update(procedure);
- executor.getScheduler().addFront(procedure);
- }
- }
- }
- }
-
- public void sendStopSignal() {}
-
- private static class ProcedureDelayContainer<Env> implements Delayed {
-
- private final Procedure<Env> procedure;
-
- public ProcedureDelayContainer(Procedure<Env> procedure) {
- this.procedure = procedure;
- }
-
- public Procedure<Env> getProcedure() {
- return procedure;
- }
-
- @Override
- public long getDelay(TimeUnit unit) {
- long delay = procedure.getTimeoutTimestamp() - System.currentTimeMillis();
- return unit.convert(delay, TimeUnit.MILLISECONDS);
- }
-
- @Override
- public int compareTo(Delayed other) {
- return Long.compare(
- this.getDelay(TimeUnit.MILLISECONDS), other.getDelay(TimeUnit.MILLISECONDS));
- }
- }
-}
+1/*
+1 * Licensed to the Apache Software Foundation (ASF) under one
+1 * or more contributor license agreements. See the NOTICE file
+1 * distributed with this work for additional information
+1 * regarding copyright ownership. The ASF licenses this file
+1 * to you under the Apache License, Version 2.0 (the
+1 * "License"); you may not use this file except in compliance
+1 * with the License. You may obtain a copy of the License at
+1 *
+1 * http://www.apache.org/licenses/LICENSE-2.0
+1 *
+1 * Unless required by applicable law or agreed to in writing,
+1 * software distributed under the License is distributed on an
+1 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+1 * KIND, either express or implied. See the License for the
+1 * specific language governing permissions and limitations
+1 * under the License.
+1 */
+1
+1package org.apache.iotdb.confignode.procedure;
+1
+1import java.util.concurrent.DelayQueue;
+1import java.util.concurrent.Delayed;
+1import java.util.concurrent.TimeUnit;
+1
+1public class TimeoutExecutorThread<Env> extends StoppableThread {
+1
+1 private static final int DELAY_QUEUE_TIMEOUT = 20;
+1 private final ProcedureExecutor<Env> executor;
+1 private final DelayQueue<ProcedureDelayContainer<Env>> queue = new DelayQueue<>();
+1
+1 public TimeoutExecutorThread(
+1 ProcedureExecutor<Env> envProcedureExecutor, ThreadGroup threadGroup, String name) {
+1 super(threadGroup, name);
+1 setDaemon(true);
+1 this.executor = envProcedureExecutor;
+1 }
+1
+1 public void add(Procedure<Env> procedure) {
+1 queue.add(new ProcedureDelayContainer<>(procedure));
+1 }
+1
+1 public boolean remove(Procedure<Env> procedure) {
+1 return queue.remove(new ProcedureDelayContainer<>(procedure));
+1 }
+1
+1 private ProcedureDelayContainer<Env> takeQuietly() {
+1 try {
+1 return queue.poll(DELAY_QUEUE_TIMEOUT, TimeUnit.SECONDS);
+1 } catch (InterruptedException e) {
+1 currentThread().interrupt();
+1 return null;
+1 }
+1 }
+1
+1 @Override
+1 public void run() {
+1 while (executor.isRunning()) {
+1 ProcedureDelayContainer<Env> delayTask = takeQuietly();
+1 if (delayTask == null) {
+1 continue;
+1 }
+1 Procedure<Env> procedure = delayTask.getProcedure();
+1 if (procedure instanceof InternalProcedure) {
+1 InternalProcedure internal = (InternalProcedure) procedure;
+1 internal.periodicExecute(executor.getEnvironment());
+1 procedure.updateTimestamp();
+1 queue.add(delayTask);
+1 } else {
+1 if (procedure.setTimeoutFailure(executor.getEnvironment())) {
+1 long rootProcId = executor.getRootProcedureId(procedure);
+1 RootProcedureStack<Env> rollbackStack = executor.getRollbackStack(rootProcId);
+1 rollbackStack.abort();
+1 executor.getStore().update(procedure);
+1 executor.getScheduler().addFront(procedure);
+1 }
+1 }
+1 }
+1 }
+1
+1 public void sendStopSignal() {}
+1
+1 private static class ProcedureDelayContainer<Env> implements Delayed {
+1
+1 private final Procedure<Env> procedure;
+1
+1 public ProcedureDelayContainer(Procedure<Env> procedure) {
+1 this.procedure = procedure;
+1 }
+1
+1 public Procedure<Env> getProcedure() {
+1 return procedure;
+1 }
+1
+1 @Override
+1 public long getDelay(TimeUnit unit) {
+1 long delay = procedure.getTimeoutTimestamp() - System.currentTimeMillis();
+1 return unit.convert(delay, TimeUnit.MILLISECONDS);
+1 }
+1
+1 @Override
+1 public int compareTo(Delayed other) {
+1 return Long.compare(
+1 this.getDelay(TimeUnit.MILLISECONDS), other.getDelay(TimeUnit.MILLISECONDS));
+1 }
+1 }
+1}
+1
\ No newline at end of file
diff --git a/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/procedure/env/ConfigNodeProcedureEnv.java b/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/procedure/env/ConfigNodeProcedureEnv.java
index 960d0a7977f..f234032107b 100644
--- a/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/procedure/env/ConfigNodeProcedureEnv.java
+++ b/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/procedure/env/ConfigNodeProcedureEnv.java
@@ -1,894 +1,895 @@
-/*
- * 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.confignode.procedure.env;
-
-import org.apache.iotdb.common.rpc.thrift.TConfigNodeLocation;
-import org.apache.iotdb.common.rpc.thrift.TConsensusGroupId;
-import org.apache.iotdb.common.rpc.thrift.TConsensusGroupType;
-import org.apache.iotdb.common.rpc.thrift.TDataNodeConfiguration;
-import org.apache.iotdb.common.rpc.thrift.TDataNodeLocation;
-import org.apache.iotdb.common.rpc.thrift.TRegionReplicaSet;
-import org.apache.iotdb.common.rpc.thrift.TSStatus;
-import org.apache.iotdb.commons.cluster.NodeStatus;
-import org.apache.iotdb.commons.cluster.NodeType;
-import org.apache.iotdb.commons.pipe.agent.plugin.meta.PipePluginMeta;
-import org.apache.iotdb.commons.pipe.config.PipeConfig;
-import org.apache.iotdb.commons.trigger.TriggerInformation;
-import org.apache.iotdb.confignode.client.CnToCnNodeRequestType;
-import org.apache.iotdb.confignode.client.async.CnToDnAsyncRequestType;
-import org.apache.iotdb.confignode.client.async.CnToDnInternalServiceAsyncRequestManager;
-import org.apache.iotdb.confignode.client.async.handlers.DataNodeAsyncRequestContext;
-import org.apache.iotdb.confignode.client.sync.CnToDnSyncRequestType;
-import org.apache.iotdb.confignode.client.sync.SyncConfigNodeClientPool;
-import org.apache.iotdb.confignode.client.sync.SyncDataNodeClientPool;
-import org.apache.iotdb.confignode.consensus.request.write.confignode.RemoveConfigNodePlan;
-import org.apache.iotdb.confignode.consensus.request.write.database.DeleteDatabasePlan;
-import org.apache.iotdb.confignode.consensus.request.write.database.PreDeleteDatabasePlan;
-import org.apache.iotdb.confignode.consensus.request.write.region.CreateRegionGroupsPlan;
-import org.apache.iotdb.confignode.exception.AddConsensusGroupException;
-import org.apache.iotdb.confignode.exception.AddPeerException;
-import org.apache.iotdb.confignode.manager.ConfigManager;
-import org.apache.iotdb.confignode.manager.consensus.ConsensusManager;
-import org.apache.iotdb.confignode.manager.load.LoadManager;
-import org.apache.iotdb.confignode.manager.load.cache.region.RegionHeartbeatSample;
-import org.apache.iotdb.confignode.manager.node.NodeManager;
-import org.apache.iotdb.confignode.manager.partition.PartitionManager;
-import org.apache.iotdb.confignode.manager.schema.ClusterSchemaManager;
-import org.apache.iotdb.confignode.persistence.partition.PartitionInfo;
-import org.apache.iotdb.confignode.persistence.schema.ClusterSchemaInfo;
-import org.apache.iotdb.confignode.procedure.exception.ProcedureException;
-import org.apache.iotdb.confignode.procedure.scheduler.LockQueue;
-import org.apache.iotdb.confignode.procedure.scheduler.ProcedureScheduler;
-import org.apache.iotdb.confignode.rpc.thrift.TAddConsensusGroupReq;
-import org.apache.iotdb.confignode.rpc.thrift.TNodeVersionInfo;
-import org.apache.iotdb.consensus.exception.ConsensusException;
-import org.apache.iotdb.db.protocol.client.ConfigNodeInfo;
-import org.apache.iotdb.mpp.rpc.thrift.TActiveTriggerInstanceReq;
-import org.apache.iotdb.mpp.rpc.thrift.TCreateDataRegionReq;
-import org.apache.iotdb.mpp.rpc.thrift.TCreatePipePluginInstanceReq;
-import org.apache.iotdb.mpp.rpc.thrift.TCreateSchemaRegionReq;
-import org.apache.iotdb.mpp.rpc.thrift.TCreateTriggerInstanceReq;
-import org.apache.iotdb.mpp.rpc.thrift.TDropPipePluginInstanceReq;
-import org.apache.iotdb.mpp.rpc.thrift.TDropTriggerInstanceReq;
-import org.apache.iotdb.mpp.rpc.thrift.TInactiveTriggerInstanceReq;
-import org.apache.iotdb.mpp.rpc.thrift.TInvalidateCacheReq;
-import org.apache.iotdb.mpp.rpc.thrift.TNotifyRegionMigrationReq;
-import org.apache.iotdb.mpp.rpc.thrift.TPushConsumerGroupMetaReq;
-import org.apache.iotdb.mpp.rpc.thrift.TPushConsumerGroupMetaResp;
-import org.apache.iotdb.mpp.rpc.thrift.TPushMultiPipeMetaReq;
-import org.apache.iotdb.mpp.rpc.thrift.TPushMultiTopicMetaReq;
-import org.apache.iotdb.mpp.rpc.thrift.TPushPipeMetaReq;
-import org.apache.iotdb.mpp.rpc.thrift.TPushPipeMetaResp;
-import org.apache.iotdb.mpp.rpc.thrift.TPushSingleConsumerGroupMetaReq;
-import org.apache.iotdb.mpp.rpc.thrift.TPushSinglePipeMetaReq;
-import org.apache.iotdb.mpp.rpc.thrift.TPushSingleTopicMetaReq;
-import org.apache.iotdb.mpp.rpc.thrift.TPushTopicMetaReq;
-import org.apache.iotdb.mpp.rpc.thrift.TPushTopicMetaResp;
-import org.apache.iotdb.rpc.TSStatusCode;
-
-import org.apache.thrift.TException;
-import org.apache.tsfile.utils.Binary;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-import java.io.IOException;
-import java.nio.ByteBuffer;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-import java.util.concurrent.TimeUnit;
-import java.util.concurrent.locks.ReentrantLock;
-import java.util.stream.Collectors;
-
-public class ConfigNodeProcedureEnv {
-
- private static final Logger LOG = LoggerFactory.getLogger(ConfigNodeProcedureEnv.class);
-
- /** Add or remove node lock. */
- private final LockQueue nodeLock = new LockQueue();
-
- private final ReentrantLock schedulerLock = new ReentrantLock(true);
-
- private final ReentrantLock submitRegionMigrateLock = new ReentrantLock(true);
-
- private final ConfigManager configManager;
-
- private final ProcedureScheduler scheduler;
-
- private final RegionMaintainHandler regionMaintainHandler;
-
- private final RemoveDataNodeHandler removeDataNodeHandler;
-
- private final ReentrantLock removeConfigNodeLock;
-
- public ConfigNodeProcedureEnv(ConfigManager configManager, ProcedureScheduler scheduler) {
- this.configManager = configManager;
- this.scheduler = scheduler;
- this.regionMaintainHandler = new RegionMaintainHandler(configManager);
- this.removeDataNodeHandler = new RemoveDataNodeHandler(configManager);
- this.removeConfigNodeLock = new ReentrantLock();
- }
-
- public ConfigManager getConfigManager() {
- return configManager;
- }
-
- /**
- * Delete ConfigNode cache, includes {@link ClusterSchemaInfo} and {@link PartitionInfo}.
- *
- * @param name database name
- * @param isGeneratedByPipe whether the deletion is triggered by pipe request
- * @return tsStatus
- */
- public TSStatus deleteDatabaseConfig(final String name, final boolean isGeneratedByPipe) {
- return getClusterSchemaManager()
- .deleteDatabase(new DeleteDatabasePlan(name), isGeneratedByPipe);
- }
-
- /**
- * Pre delete a database.
- *
- * @param preDeleteType execute/rollback
- * @param deleteSgName database name
- */
- public void preDeleteDatabase(
- final PreDeleteDatabasePlan.PreDeleteType preDeleteType, final String deleteSgName) {
- getPartitionManager().preDeleteDatabase(deleteSgName, preDeleteType);
- }
-
- /**
- * @param databaseName database name
- * @return ALL SUCCESS OR NOT
- * @throws IOException IOE
- * @throws TException Thrift IOE
- */
- public boolean invalidateCache(final String databaseName) throws IOException, TException {
- final List<TDataNodeConfiguration> allDataNodes = getNodeManager().getRegisteredDataNodes();
- final TInvalidateCacheReq invalidateCacheReq = new TInvalidateCacheReq();
- invalidateCacheReq.setStorageGroup(true);
- invalidateCacheReq.setFullPath(databaseName);
- for (final TDataNodeConfiguration dataNodeConfiguration : allDataNodes) {
- final int dataNodeId = dataNodeConfiguration.getLocation().getDataNodeId();
-
- // If the node is not alive, retry for up to 10 times
- NodeStatus nodeStatus = getLoadManager().getNodeStatus(dataNodeId);
- int retryNum = 10;
- if (nodeStatus == NodeStatus.Unknown) {
- for (int i = 0; i < retryNum && nodeStatus == NodeStatus.Unknown; i++) {
- try {
- TimeUnit.MILLISECONDS.sleep(500);
- } catch (final InterruptedException e) {
- LOG.error("Sleep failed in ConfigNodeProcedureEnv: ", e);
- Thread.currentThread().interrupt();
- break;
- }
- nodeStatus = getLoadManager().getNodeStatus(dataNodeId);
- }
- }
-
- if (nodeStatus == NodeStatus.Unknown) {
- LOG.warn(
- "Invalidate cache failed, because DataNode {} is Unknown",
- dataNodeConfiguration.getLocation().getInternalEndPoint());
- return false;
- }
-
- // Always invalidate PartitionCache first
- final TSStatus invalidatePartitionStatus =
- (TSStatus)
- SyncDataNodeClientPool.getInstance()
- .sendSyncRequestToDataNodeWithRetry(
- dataNodeConfiguration.getLocation().getInternalEndPoint(),
- invalidateCacheReq,
- CnToDnSyncRequestType.INVALIDATE_PARTITION_CACHE);
-
- final TSStatus invalidateSchemaStatus =
- (TSStatus)
- SyncDataNodeClientPool.getInstance()
- .sendSyncRequestToDataNodeWithRetry(
- dataNodeConfiguration.getLocation().getInternalEndPoint(),
- invalidateCacheReq,
- CnToDnSyncRequestType.INVALIDATE_SCHEMA_CACHE);
-
- if (!verifySucceed(invalidatePartitionStatus, invalidateSchemaStatus)) {
- LOG.error(
- "Invalidate cache failed, invalidate partition cache status is {}, invalidate schemaengine cache status is {}",
- invalidatePartitionStatus,
- invalidateSchemaStatus);
- return false;
- }
- }
- return true;
- }
-
- public boolean verifySucceed(TSStatus... status) {
- return Arrays.stream(status)
- .allMatch(tsStatus -> tsStatus.getCode() == TSStatusCode.SUCCESS_STATUS.getStatusCode());
- }
-
- /**
- * Let the remotely new ConfigNode build the ConsensusGroup.
- *
- * <p>Actually, the parameter of this method can be empty, adding new raft peer to exist group
- * should invoke createPeer(groupId, emptyList).
- *
- * @param tConfigNodeLocation New ConfigNode's location
- */
- public void addConsensusGroup(TConfigNodeLocation tConfigNodeLocation)
- throws AddConsensusGroupException {
- List<TConfigNodeLocation> configNodeLocations =
- new ArrayList<>(configManager.getNodeManager().getRegisteredConfigNodes());
- configNodeLocations.add(tConfigNodeLocation);
- TSStatus status =
- (TSStatus)
- SyncConfigNodeClientPool.getInstance()
- .sendSyncRequestToConfigNodeWithRetry(
- tConfigNodeLocation.getInternalEndPoint(),
- new TAddConsensusGroupReq(configNodeLocations),
- CnToCnNodeRequestType.ADD_CONSENSUS_GROUP);
- if (status.getCode() != TSStatusCode.SUCCESS_STATUS.getStatusCode()) {
- throw new AddConsensusGroupException(tConfigNodeLocation);
- }
- }
-
- /**
- * Leader will add the new ConfigNode Peer into ConfigRegion.
- *
- * @param configNodeLocation The new ConfigNode
- * @throws AddPeerException When addPeer doesn't success
- */
- public void addConfigNodePeer(TConfigNodeLocation configNodeLocation) throws AddPeerException {
- configManager.getConsensusManager().addConfigNodePeer(configNodeLocation);
- }
-
- /**
- * Remove peer in Leader node.
- *
- * @param tConfigNodeLocation node is removed
- * @throws ProcedureException if failed status
- */
- public void removeConfigNodePeer(TConfigNodeLocation tConfigNodeLocation)
- throws ProcedureException {
- removeConfigNodeLock.lock();
- TSStatus tsStatus;
- try {
- // Execute removePeer
- if (getConsensusManager().removeConfigNodePeer(tConfigNodeLocation)) {
- tsStatus = getConsensusManager().write(new RemoveConfigNodePlan(tConfigNodeLocation));
- } else {
- tsStatus =
- new TSStatus(TSStatusCode.REMOVE_CONFIGNODE_ERROR.getStatusCode())
- .setMessage(
- "Remove ConfigNode failed because update ConsensusGroup peer information failed.");
- }
- } catch (ConsensusException e) {
- LOG.warn("Failed in the write API executing the consensus layer due to: ", e);
- tsStatus = new TSStatus(TSStatusCode.EXECUTE_STATEMENT_ERROR.getStatusCode());
- tsStatus.setMessage(e.getMessage());
- }
- try {
- if (tsStatus.getCode() != TSStatusCode.SUCCESS_STATUS.getStatusCode()) {
- throw new ProcedureException(tsStatus.getMessage());
- }
- } finally {
- removeConfigNodeLock.unlock();
- }
- }
-
- /**
- * Remove Consensus Group in removed node.
- *
- * @param removedConfigNode config node location
- * @throws ProcedureException if failed status
- */
- public void deleteConfigNodePeer(TConfigNodeLocation removedConfigNode)
- throws ProcedureException {
- TSStatus tsStatus =
- (TSStatus)
- SyncConfigNodeClientPool.getInstance()
- .sendSyncRequestToConfigNodeWithRetry(
- removedConfigNode.getInternalEndPoint(),
- removedConfigNode,
- CnToCnNodeRequestType.DELETE_CONFIG_NODE_PEER);
- if (tsStatus.getCode() != TSStatusCode.SUCCESS_STATUS.getStatusCode()) {
- throw new ProcedureException(tsStatus.getMessage());
- }
- }
-
- /**
- * Stop ConfigNode and remove heartbeatCache.
- *
- * @param tConfigNodeLocation config node location
- * @throws ProcedureException if failed status
- */
- public void stopAndClearConfigNode(TConfigNodeLocation tConfigNodeLocation)
- throws ProcedureException {
- TSStatus tsStatus =
- (TSStatus)
- SyncConfigNodeClientPool.getInstance()
- .sendSyncRequestToConfigNodeWithRetry(
- tConfigNodeLocation.getInternalEndPoint(),
- tConfigNodeLocation,
- CnToCnNodeRequestType.STOP_AND_CLEAR_CONFIG_NODE);
-
- if (tsStatus.getCode() != TSStatusCode.SUCCESS_STATUS.getStatusCode()) {
- throw new ProcedureException(tsStatus.getMessage());
- }
-
- getLoadManager().removeNodeCache(tConfigNodeLocation.getConfigNodeId());
- }
-
- /**
- * Leader will record the new ConfigNode's information.
- *
- * @param configNodeLocation The new ConfigNode
- * @param versionInfo The new ConfigNode's versionInfo
- */
- public void applyConfigNode(
- TConfigNodeLocation configNodeLocation, TNodeVersionInfo versionInfo) {
- configManager.getNodeManager().applyConfigNode(configNodeLocation, versionInfo);
- }
-
- /**
- * Leader will notify the new ConfigNode that registration success.
- *
- * @param configNodeLocation The new ConfigNode
- */
- public void notifyRegisterSuccess(TConfigNodeLocation configNodeLocation) {
- SyncConfigNodeClientPool.getInstance()
- .sendSyncRequestToConfigNodeWithRetry(
- configNodeLocation.getInternalEndPoint(),
- null,
- CnToCnNodeRequestType.NOTIFY_REGISTER_SUCCESS);
- }
-
- /**
- * Create a new ConfigNodeHeartbeatCache
- *
- * @param nodeId the index of the new ConfigNode
- */
- public void createConfigNodeHeartbeatCache(int nodeId) {
- getLoadManager().getLoadCache().createNodeHeartbeatCache(NodeType.ConfigNode, nodeId);
- // TODO: invoke a force heartbeat to update new ConfigNode's status immediately
- }
-
- /**
- * Do region creations and broadcast the {@link CreateRegionGroupsPlan}.
- *
- * @return Those RegionReplicas that failed to create
- */
- public Map<TConsensusGroupId, TRegionReplicaSet> doRegionCreation(
- final TConsensusGroupType consensusGroupType,
- final CreateRegionGroupsPlan createRegionGroupsPlan) {
-
- // Prepare clientHandler
- DataNodeAsyncRequestContext<?, TSStatus> clientHandler;
- switch (consensusGroupType) {
- case SchemaRegion:
- clientHandler = getCreateSchemaRegionClientHandler(createRegionGroupsPlan);
- break;
- case DataRegion:
- default:
- clientHandler = getCreateDataRegionClientHandler(createRegionGroupsPlan);
- break;
- }
- if (clientHandler.getRequestIndices().isEmpty()) {
- return new HashMap<>();
- }
-
- // Send CreateRegion requests to DataNodes
- CnToDnInternalServiceAsyncRequestManager.getInstance().sendAsyncRequestWithRetry(clientHandler);
-
- // Filter RegionGroups that weren't created successfully
- int requestId = 0;
- Map<Integer, TSStatus> responseMap = clientHandler.getResponseMap();
- Map<TConsensusGroupId, TRegionReplicaSet> failedRegions = new HashMap<>();
- for (List<TRegionReplicaSet> regionReplicaSets :
- createRegionGroupsPlan.getRegionGroupMap().values()) {
- for (final TRegionReplicaSet regionReplicaSet : regionReplicaSets) {
- for (final TDataNodeLocation dataNodeLocation : regionReplicaSet.getDataNodeLocations()) {
- if (responseMap.get(requestId).getCode() != TSStatusCode.SUCCESS_STATUS.getStatusCode()) {
- failedRegions
- .computeIfAbsent(
- regionReplicaSet.getRegionId(),
- empty -> new TRegionReplicaSet().setRegionId(regionReplicaSet.getRegionId()))
- .addToDataNodeLocations(dataNodeLocation);
- }
- requestId += 1;
- }
- }
- }
- return failedRegions;
- }
-
- private DataNodeAsyncRequestContext<TCreateSchemaRegionReq, TSStatus>
- getCreateSchemaRegionClientHandler(CreateRegionGroupsPlan createRegionGroupsPlan) {
- DataNodeAsyncRequestContext<TCreateSchemaRegionReq, TSStatus> clientHandler =
- new DataNodeAsyncRequestContext<>(CnToDnAsyncRequestType.CREATE_SCHEMA_REGION);
-
- int requestId = 0;
- for (Map.Entry<String, List<TRegionReplicaSet>> sgRegionsEntry :
- createRegionGroupsPlan.getRegionGroupMap().entrySet()) {
- String storageGroup = sgRegionsEntry.getKey();
- List<TRegionReplicaSet> regionReplicaSets = sgRegionsEntry.getValue();
- for (TRegionReplicaSet regionReplicaSet : regionReplicaSets) {
- for (TDataNodeLocation dataNodeLocation : regionReplicaSet.getDataNodeLocations()) {
- clientHandler.putRequest(
- requestId, genCreateSchemaRegionReq(storageGroup, regionReplicaSet));
- clientHandler.putNodeLocation(requestId, dataNodeLocation);
- requestId += 1;
- }
- }
- }
-
- return clientHandler;
- }
-
- private DataNodeAsyncRequestContext<TCreateDataRegionReq, TSStatus>
- getCreateDataRegionClientHandler(CreateRegionGroupsPlan createRegionGroupsPlan) {
- DataNodeAsyncRequestContext<TCreateDataRegionReq, TSStatus> clientHandler =
- new DataNodeAsyncRequestContext<>(CnToDnAsyncRequestType.CREATE_DATA_REGION);
-
- int requestId = 0;
- for (Map.Entry<String, List<TRegionReplicaSet>> sgRegionsEntry :
- createRegionGroupsPlan.getRegionGroupMap().entrySet()) {
- String storageGroup = sgRegionsEntry.getKey();
- List<TRegionReplicaSet> regionReplicaSets = sgRegionsEntry.getValue();
- for (TRegionReplicaSet regionReplicaSet : regionReplicaSets) {
- for (TDataNodeLocation dataNodeLocation : regionReplicaSet.getDataNodeLocations()) {
- clientHandler.putRequest(
- requestId, genCreateDataRegionReq(storageGroup, regionReplicaSet));
- clientHandler.putNodeLocation(requestId, dataNodeLocation);
- requestId += 1;
- }
- }
- }
-
- return clientHandler;
- }
-
- private TCreateSchemaRegionReq genCreateSchemaRegionReq(
- String storageGroup, TRegionReplicaSet regionReplicaSet) {
- TCreateSchemaRegionReq req = new TCreateSchemaRegionReq();
- req.setStorageGroup(storageGroup);
- req.setRegionReplicaSet(regionReplicaSet);
- return req;
- }
-
- private TCreateDataRegionReq genCreateDataRegionReq(
- String storageGroup, TRegionReplicaSet regionReplicaSet) {
- TCreateDataRegionReq req = new TCreateDataRegionReq();
- req.setStorageGroup(storageGroup);
- req.setRegionReplicaSet(regionReplicaSet);
- return req;
- }
-
- public List<TSStatus> notifyRegionMigrationToAllDataNodes(
- TConsensusGroupId consensusGroupId, boolean isStart) {
- final Map<Integer, TDataNodeLocation> dataNodeLocationMap =
- configManager.getNodeManager().getRegisteredDataNodeLocations();
- final TNotifyRegionMigrationReq request =
- new TNotifyRegionMigrationReq(
- configManager
- .getConsensusManager()
- .getConsensusImpl()
- .getLogicalClock(ConfigNodeInfo.CONFIG_REGION_ID),
- System.nanoTime(),
- configManager.getProcedureManager().getRegionOperationConsensusIds());
- request.setRegionId(consensusGroupId);
- request.setIsStart(isStart);
-
- final DataNodeAsyncRequestContext<TNotifyRegionMigrationReq, TSStatus> clientHandler =
- new DataNodeAsyncRequestContext<>(
- CnToDnAsyncRequestType.NOTIFY_REGION_MIGRATION, request, dataNodeLocationMap);
- CnToDnInternalServiceAsyncRequestManager.getInstance().sendAsyncRequestWithRetry(clientHandler);
- return clientHandler.getResponseList();
- }
-
- public void persistRegionGroup(CreateRegionGroupsPlan createRegionGroupsPlan) {
- // Persist the allocation result
- try {
- getConsensusManager().write(createRegionGroupsPlan);
- } catch (ConsensusException e) {
- LOG.warn("Failed in the write API executing the consensus layer due to: ", e);
- }
- }
-
- /**
- * Force activating RegionGroup by setting status to Running, therefore the ConfigNode-leader can
- * select leader for it and use it to allocate new Partitions
- *
- * @param activateRegionGroupMap Map<Database, Map<RegionGroupId, Map<DataNodeId, activate
- * heartbeat sample>>>
- */
- public void activateRegionGroup(
- Map<String, Map<TConsensusGroupId, Map<Integer, RegionHeartbeatSample>>>
- activateRegionGroupMap) {
- // Create RegionGroup heartbeat Caches
- activateRegionGroupMap.forEach(
- (database, regionGroupSampleMap) ->
- regionGroupSampleMap.forEach(
- (regionGroupId, regionSampleMap) ->
- getLoadManager()
- .getLoadCache()
- .createRegionGroupHeartbeatCache(
- database, regionGroupId, regionSampleMap.keySet())));
- // Force update first heartbeat samples
- getLoadManager()
- .forceUpdateRegionGroupCache(
- activateRegionGroupMap.values().stream()
- .flatMap(innerMap -> innerMap.entrySet().stream())
- .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue, (a, b) -> b)));
- // Wait for leader and priority redistribution
- getLoadManager()
- .waitForRegionGroupReady(
- activateRegionGroupMap.values().stream()
- .flatMap(innterMap -> innterMap.keySet().stream())
- .collect(Collectors.toList()));
- }
-
- public List<TRegionReplicaSet> getAllReplicaSets(String storageGroup) {
- return getPartitionManager().getAllReplicaSets(storageGroup);
- }
-
- public List<TSStatus> createTriggerOnDataNodes(
- TriggerInformation triggerInformation, Binary jarFile) throws IOException {
- NodeManager nodeManager = configManager.getNodeManager();
- final Map<Integer, TDataNodeLocation> dataNodeLocationMap =
- nodeManager.getRegisteredDataNodeLocations();
- final TCreateTriggerInstanceReq request =
- new TCreateTriggerInstanceReq(triggerInformation.serialize());
- if (jarFile != null) {
- request.setJarFile(ByteBuffer.wrap(jarFile.getValues()));
- }
-
- DataNodeAsyncRequestContext<TCreateTriggerInstanceReq, TSStatus> clientHandler =
- new DataNodeAsyncRequestContext<>(
- CnToDnAsyncRequestType.CREATE_TRIGGER_INSTANCE, request, dataNodeLocationMap);
- CnToDnInternalServiceAsyncRequestManager.getInstance().sendAsyncRequestWithRetry(clientHandler);
- return clientHandler.getResponseList();
- }
-
- public List<TSStatus> dropTriggerOnDataNodes(String triggerName, boolean needToDeleteJarFile) {
- NodeManager nodeManager = configManager.getNodeManager();
- final Map<Integer, TDataNodeLocation> dataNodeLocationMap =
- nodeManager.getRegisteredDataNodeLocations();
- final TDropTriggerInstanceReq request =
- new TDropTriggerInstanceReq(triggerName, needToDeleteJarFile);
-
- DataNodeAsyncRequestContext<TDropTriggerInstanceReq, TSStatus> clientHandler =
- new DataNodeAsyncRequestContext<>(
- CnToDnAsyncRequestType.DROP_TRIGGER_INSTANCE, request, dataNodeLocationMap);
- CnToDnInternalServiceAsyncRequestManager.getInstance().sendAsyncRequestWithRetry(clientHandler);
- return clientHandler.getResponseList();
- }
-
- public List<TSStatus> activeTriggerOnDataNodes(String triggerName) {
- NodeManager nodeManager = configManager.getNodeManager();
- final Map<Integer, TDataNodeLocation> dataNodeLocationMap =
- nodeManager.getRegisteredDataNodeLocations();
- final TActiveTriggerInstanceReq request = new TActiveTriggerInstanceReq(triggerName);
-
- DataNodeAsyncRequestContext<TActiveTriggerInstanceReq, TSStatus> clientHandler =
- new DataNodeAsyncRequestContext<>(
- CnToDnAsyncRequestType.ACTIVE_TRIGGER_INSTANCE, request, dataNodeLocationMap);
- CnToDnInternalServiceAsyncRequestManager.getInstance().sendAsyncRequestWithRetry(clientHandler);
- return clientHandler.getResponseList();
- }
-
- public List<TSStatus> inactiveTriggerOnDataNodes(String triggerName) {
- NodeManager nodeManager = configManager.getNodeManager();
- final Map<Integer, TDataNodeLocation> dataNodeLocationMap =
- nodeManager.getRegisteredDataNodeLocations();
- final TInactiveTriggerInstanceReq request = new TInactiveTriggerInstanceReq(triggerName);
-
- DataNodeAsyncRequestContext<TInactiveTriggerInstanceReq, TSStatus> clientHandler =
- new DataNodeAsyncRequestContext<>(
- CnToDnAsyncRequestType.INACTIVE_TRIGGER_INSTANCE, request, dataNodeLocationMap);
- CnToDnInternalServiceAsyncRequestManager.getInstance().sendAsyncRequestWithRetry(clientHandler);
- return clientHandler.getResponseList();
- }
-
- public List<TSStatus> createPipePluginOnDataNodes(PipePluginMeta pipePluginMeta, byte[] jarFile)
- throws IOException {
- final Map<Integer, TDataNodeLocation> dataNodeLocationMap =
- configManager.getNodeManager().getRegisteredDataNodeLocations();
- final TCreatePipePluginInstanceReq request =
- new TCreatePipePluginInstanceReq(pipePluginMeta.serialize(), ByteBuffer.wrap(jarFile));
-
- final DataNodeAsyncRequestContext<TCreatePipePluginInstanceReq, TSStatus> clientHandler =
- new DataNodeAsyncRequestContext<>(
- CnToDnAsyncRequestType.CREATE_PIPE_PLUGIN, request, dataNodeLocationMap);
- CnToDnInternalServiceAsyncRequestManager.getInstance().sendAsyncRequestWithRetry(clientHandler);
- return clientHandler.getResponseList();
- }
-
- public List<TSStatus> dropPipePluginOnDataNodes(
- String pipePluginName, boolean needToDeleteJarFile) {
- final Map<Integer, TDataNodeLocation> dataNodeLocationMap =
- configManager.getNodeManager().getRegisteredDataNodeLocations();
- final TDropPipePluginInstanceReq request =
- new TDropPipePluginInstanceReq(pipePluginName, needToDeleteJarFile);
-
- DataNodeAsyncRequestContext<TDropPipePluginInstanceReq, TSStatus> clientHandler =
- new DataNodeAsyncRequestContext<>(
- CnToDnAsyncRequestType.DROP_PIPE_PLUGIN, request, dataNodeLocationMap);
- CnToDnInternalServiceAsyncRequestManager.getInstance().sendAsyncRequestWithRetry(clientHandler);
- return clientHandler.getResponseList();
- }
-
- public Map<Integer, TPushPipeMetaResp> pushAllPipeMetaToDataNodes(
- List<ByteBuffer> pipeMetaBinaryList) {
- final Map<Integer, TDataNodeLocation> dataNodeLocationMap =
- configManager.getNodeManager().getRegisteredDataNodeLocations();
- final TPushPipeMetaReq request = new TPushPipeMetaReq().setPipeMetas(pipeMetaBinaryList);
-
- final DataNodeAsyncRequestContext<TPushPipeMetaReq, TPushPipeMetaResp> clientHandler =
- new DataNodeAsyncRequestContext<>(
- CnToDnAsyncRequestType.PIPE_PUSH_ALL_META, request, dataNodeLocationMap);
- CnToDnInternalServiceAsyncRequestManager.getInstance()
- .sendAsyncRequestToNodeWithRetryAndTimeoutInMs(
- clientHandler,
- PipeConfig.getInstance().getPipeMetaSyncerSyncIntervalMinutes() * 60 * 1000 * 2 / 3);
- return clientHandler.getResponseMap();
- }
-
- public Map<Integer, TPushPipeMetaResp> pushSinglePipeMetaToDataNodes(ByteBuffer pipeMetaBinary) {
- final Map<Integer, TDataNodeLocation> dataNodeLocationMap =
- configManager.getNodeManager().getRegisteredDataNodeLocations();
- final TPushSinglePipeMetaReq request = new TPushSinglePipeMetaReq().setPipeMeta(pipeMetaBinary);
-
- final DataNodeAsyncRequestContext<TPushSinglePipeMetaReq, TPushPipeMetaResp> clientHandler =
- new DataNodeAsyncRequestContext<>(
- CnToDnAsyncRequestType.PIPE_PUSH_SINGLE_META, request, dataNodeLocationMap);
- CnToDnInternalServiceAsyncRequestManager.getInstance()
- .sendAsyncRequestToNodeWithRetryAndTimeoutInMs(
- clientHandler,
- PipeConfig.getInstance().getPipeMetaSyncerSyncIntervalMinutes() * 60 * 1000 * 2 / 3);
- return clientHandler.getResponseMap();
- }
-
- public Map<Integer, TPushPipeMetaResp> dropSinglePipeOnDataNodes(String pipeNameToDrop) {
- final Map<Integer, TDataNodeLocation> dataNodeLocationMap =
- configManager.getNodeManager().getRegisteredDataNodeLocations();
- final TPushSinglePipeMetaReq request =
- new TPushSinglePipeMetaReq().setPipeNameToDrop(pipeNameToDrop);
-
- final DataNodeAsyncRequestContext<TPushSinglePipeMetaReq, TPushPipeMetaResp> clientHandler =
- new DataNodeAsyncRequestContext<>(
- CnToDnAsyncRequestType.PIPE_PUSH_SINGLE_META, request, dataNodeLocationMap);
- CnToDnInternalServiceAsyncRequestManager.getInstance()
- .sendAsyncRequestToNodeWithRetryAndTimeoutInMs(
- clientHandler,
- PipeConfig.getInstance().getPipeMetaSyncerSyncIntervalMinutes() * 60 * 1000 * 2 / 3);
- return clientHandler.getResponseMap();
- }
-
- public Map<Integer, TPushPipeMetaResp> pushMultiPipeMetaToDataNodes(
- List<ByteBuffer> pipeMetaBinaryList) {
- final Map<Integer, TDataNodeLocation> dataNodeLocationMap =
- configManager.getNodeManager().getRegisteredDataNodeLocations();
- final TPushMultiPipeMetaReq request =
- new TPushMultiPipeMetaReq().setPipeMetas(pipeMetaBinaryList);
-
- final DataNodeAsyncRequestContext<TPushMultiPipeMetaReq, TPushPipeMetaResp> clientHandler =
- new DataNodeAsyncRequestContext<>(
- CnToDnAsyncRequestType.PIPE_PUSH_MULTI_META, request, dataNodeLocationMap);
- CnToDnInternalServiceAsyncRequestManager.getInstance()
- .sendAsyncRequestToNodeWithRetryAndTimeoutInMs(
- clientHandler,
- PipeConfig.getInstance().getPipeMetaSyncerSyncIntervalMinutes() * 60 * 1000 * 2 / 3);
- return clientHandler.getResponseMap();
- }
-
- public Map<Integer, TPushPipeMetaResp> dropMultiPipeOnDataNodes(List<String> pipeNamesToDrop) {
- final Map<Integer, TDataNodeLocation> dataNodeLocationMap =
- configManager.getNodeManager().getRegisteredDataNodeLocations();
- final TPushMultiPipeMetaReq request =
- new TPushMultiPipeMetaReq().setPipeNamesToDrop(pipeNamesToDrop);
-
- final DataNodeAsyncRequestContext<TPushMultiPipeMetaReq, TPushPipeMetaResp> clientHandler =
- new DataNodeAsyncRequestContext<>(
- CnToDnAsyncRequestType.PIPE_PUSH_MULTI_META, request, dataNodeLocationMap);
- CnToDnInternalServiceAsyncRequestManager.getInstance()
- .sendAsyncRequestToNodeWithRetryAndTimeoutInMs(
- clientHandler,
- PipeConfig.getInstance().getPipeMetaSyncerSyncIntervalMinutes() * 60 * 1000 * 2 / 3);
- return clientHandler.getResponseMap();
- }
-
- public Map<Integer, TPushTopicMetaResp> pushAllTopicMetaToDataNodes(
- List<ByteBuffer> topicMetaBinaryList) {
- final Map<Integer, TDataNodeLocation> dataNodeLocationMap =
- configManager.getNodeManager().getRegisteredDataNodeLocations();
- final TPushTopicMetaReq request = new TPushTopicMetaReq().setTopicMetas(topicMetaBinaryList);
-
- final DataNodeAsyncRequestContext<TPushTopicMetaReq, TPushTopicMetaResp> clientHandler =
- new DataNodeAsyncRequestContext<>(
- CnToDnAsyncRequestType.TOPIC_PUSH_ALL_META, request, dataNodeLocationMap);
- CnToDnInternalServiceAsyncRequestManager.getInstance()
- .sendAsyncRequestToNodeWithRetryAndTimeoutInMs(
- clientHandler,
- PipeConfig.getInstance().getPipeMetaSyncerSyncIntervalMinutes() * 60 * 1000 * 2 / 3);
- return clientHandler.getResponseMap();
- }
-
- public List<TSStatus> pushSingleTopicOnDataNode(ByteBuffer topicMeta) {
- final Map<Integer, TDataNodeLocation> dataNodeLocationMap =
- configManager.getNodeManager().getRegisteredDataNodeLocations();
- final TPushSingleTopicMetaReq request = new TPushSingleTopicMetaReq().setTopicMeta(topicMeta);
-
- final DataNodeAsyncRequestContext<TPushSingleTopicMetaReq, TPushTopicMetaResp> clientHandler =
- new DataNodeAsyncRequestContext<>(
- CnToDnAsyncRequestType.TOPIC_PUSH_SINGLE_META, request, dataNodeLocationMap);
- CnToDnInternalServiceAsyncRequestManager.getInstance().sendAsyncRequestWithRetry(clientHandler);
- return clientHandler.getResponseList().stream()
- .map(TPushTopicMetaResp::getStatus)
- .collect(Collectors.toList());
- }
-
- public List<TSStatus> dropSingleTopicOnDataNode(String topicNameToDrop) {
- final Map<Integer, TDataNodeLocation> dataNodeLocationMap =
- configManager.getNodeManager().getRegisteredDataNodeLocations();
- final TPushSingleTopicMetaReq request =
- new TPushSingleTopicMetaReq().setTopicNameToDrop(topicNameToDrop);
-
- final DataNodeAsyncRequestContext<TPushSingleTopicMetaReq, TPushTopicMetaResp> clientHandler =
- new DataNodeAsyncRequestContext<>(
- CnToDnAsyncRequestType.TOPIC_PUSH_SINGLE_META, request, dataNodeLocationMap);
- CnToDnInternalServiceAsyncRequestManager.getInstance().sendAsyncRequestWithRetry(clientHandler);
- return clientHandler.getResponseList().stream()
- .map(TPushTopicMetaResp::getStatus)
- .collect(Collectors.toList());
- }
-
- public Map<Integer, TPushTopicMetaResp> pushMultiTopicMetaToDataNodes(
- List<ByteBuffer> topicMetaBinaryList) {
- final Map<Integer, TDataNodeLocation> dataNodeLocationMap =
- configManager.getNodeManager().getRegisteredDataNodeLocations();
- final TPushMultiTopicMetaReq request =
- new TPushMultiTopicMetaReq().setTopicMetas(topicMetaBinaryList);
-
- final DataNodeAsyncRequestContext<TPushMultiTopicMetaReq, TPushTopicMetaResp> clientHandler =
- new DataNodeAsyncRequestContext<>(
- CnToDnAsyncRequestType.TOPIC_PUSH_MULTI_META, request, dataNodeLocationMap);
- CnToDnInternalServiceAsyncRequestManager.getInstance()
- .sendAsyncRequestToNodeWithRetryAndTimeoutInMs(
- clientHandler,
- PipeConfig.getInstance().getPipeMetaSyncerSyncIntervalMinutes() * 60 * 1000 * 2 / 3);
- return clientHandler.getResponseMap();
- }
-
- public Map<Integer, TPushTopicMetaResp> dropMultiTopicOnDataNodes(List<String> topicNamesToDrop) {
- final Map<Integer, TDataNodeLocation> dataNodeLocationMap =
- configManager.getNodeManager().getRegisteredDataNodeLocations();
- final TPushMultiTopicMetaReq request =
- new TPushMultiTopicMetaReq().setTopicNamesToDrop(topicNamesToDrop);
-
- final DataNodeAsyncRequestContext<TPushMultiTopicMetaReq, TPushTopicMetaResp> clientHandler =
- new DataNodeAsyncRequestContext<>(
- CnToDnAsyncRequestType.TOPIC_PUSH_MULTI_META, request, dataNodeLocationMap);
- CnToDnInternalServiceAsyncRequestManager.getInstance()
- .sendAsyncRequestToNodeWithRetryAndTimeoutInMs(
- clientHandler,
- PipeConfig.getInstance().getPipeMetaSyncerSyncIntervalMinutes() * 60 * 1000 * 2 / 3);
- return clientHandler.getResponseMap();
- }
-
- public Map<Integer, TPushConsumerGroupMetaResp> pushAllConsumerGroupMetaToDataNodes(
- List<ByteBuffer> consumerGroupMetaBinaryList) {
- final Map<Integer, TDataNodeLocation> dataNodeLocationMap =
- configManager.getNodeManager().getRegisteredDataNodeLocations();
- final TPushConsumerGroupMetaReq request =
- new TPushConsumerGroupMetaReq().setConsumerGroupMetas(consumerGroupMetaBinaryList);
-
- final DataNodeAsyncRequestContext<TPushConsumerGroupMetaReq, TPushConsumerGroupMetaResp>
- clientHandler =
- new DataNodeAsyncRequestContext<>(
- CnToDnAsyncRequestType.CONSUMER_GROUP_PUSH_ALL_META, request, dataNodeLocationMap);
- CnToDnInternalServiceAsyncRequestManager.getInstance()
- .sendAsyncRequestToNodeWithRetryAndTimeoutInMs(
- clientHandler,
- PipeConfig.getInstance().getPipeMetaSyncerSyncIntervalMinutes() * 60 * 1000 * 2 / 3);
- return clientHandler.getResponseMap();
- }
-
- public List<TSStatus> pushSingleConsumerGroupOnDataNode(ByteBuffer consumerGroupMeta) {
- final Map<Integer, TDataNodeLocation> dataNodeLocationMap =
- configManager.getNodeManager().getRegisteredDataNodeLocations();
- final TPushSingleConsumerGroupMetaReq request =
- new TPushSingleConsumerGroupMetaReq().setConsumerGroupMeta(consumerGroupMeta);
-
- final DataNodeAsyncRequestContext<TPushSingleConsumerGroupMetaReq, TPushConsumerGroupMetaResp>
- clientHandler =
- new DataNodeAsyncRequestContext<>(
- CnToDnAsyncRequestType.CONSUMER_GROUP_PUSH_SINGLE_META,
- request,
- dataNodeLocationMap);
- CnToDnInternalServiceAsyncRequestManager.getInstance().sendAsyncRequestWithRetry(clientHandler);
- return clientHandler.getResponseList().stream()
- .map(TPushConsumerGroupMetaResp::getStatus)
- .collect(Collectors.toList());
- }
-
- public List<TSStatus> dropSingleConsumerGroupOnDataNode(String consumerGroupNameToDrop) {
- final Map<Integer, TDataNodeLocation> dataNodeLocationMap =
- configManager.getNodeManager().getRegisteredDataNodeLocations();
- final TPushSingleConsumerGroupMetaReq request =
- new TPushSingleConsumerGroupMetaReq().setConsumerGroupNameToDrop(consumerGroupNameToDrop);
-
- final DataNodeAsyncRequestContext<TPushSingleConsumerGroupMetaReq, TPushConsumerGroupMetaResp>
- clientHandler =
- new DataNodeAsyncRequestContext<>(
- CnToDnAsyncRequestType.CONSUMER_GROUP_PUSH_SINGLE_META,
- request,
- dataNodeLocationMap);
- CnToDnInternalServiceAsyncRequestManager.getInstance().sendAsyncRequestWithRetry(clientHandler);
- return clientHandler.getResponseList().stream()
- .map(TPushConsumerGroupMetaResp::getStatus)
- .collect(Collectors.toList());
- }
-
- public LockQueue getNodeLock() {
- return nodeLock;
- }
-
- public ProcedureScheduler getScheduler() {
- return scheduler;
- }
-
- public ReentrantLock getSchedulerLock() {
- return schedulerLock;
- }
-
- public ReentrantLock getSubmitRegionMigrateLock() {
- return submitRegionMigrateLock;
- }
-
- public RegionMaintainHandler getRegionMaintainHandler() {
- return regionMaintainHandler;
- }
-
- public RemoveDataNodeHandler getRemoveDataNodeHandler() {
- return removeDataNodeHandler;
- }
-
- private ConsensusManager getConsensusManager() {
- return configManager.getConsensusManager();
- }
-
- private NodeManager getNodeManager() {
- return configManager.getNodeManager();
- }
-
- private ClusterSchemaManager getClusterSchemaManager() {
- return configManager.getClusterSchemaManager();
- }
-
- private PartitionManager getPartitionManager() {
- return configManager.getPartitionManager();
- }
-
- private LoadManager getLoadManager() {
- return configManager.getLoadManager();
- }
-}
+1/*
+1 * Licensed to the Apache Software Foundation (ASF) under one
+1 * or more contributor license agreements. See the NOTICE file
+1 * distributed with this work for additional information
+1 * regarding copyright ownership. The ASF licenses this file
+1 * to you under the Apache License, Version 2.0 (the
+1 * "License"); you may not use this file except in compliance
+1 * with the License. You may obtain a copy of the License at
+1 *
+1 * http://www.apache.org/licenses/LICENSE-2.0
+1 *
+1 * Unless required by applicable law or agreed to in writing,
+1 * software distributed under the License is distributed on an
+1 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+1 * KIND, either express or implied. See the License for the
+1 * specific language governing permissions and limitations
+1 * under the License.
+1 */
+1
+1package org.apache.iotdb.confignode.procedure.env;
+1
+1import org.apache.iotdb.common.rpc.thrift.TConfigNodeLocation;
+1import org.apache.iotdb.common.rpc.thrift.TConsensusGroupId;
+1import org.apache.iotdb.common.rpc.thrift.TConsensusGroupType;
+1import org.apache.iotdb.common.rpc.thrift.TDataNodeConfiguration;
+1import org.apache.iotdb.common.rpc.thrift.TDataNodeLocation;
+1import org.apache.iotdb.common.rpc.thrift.TRegionReplicaSet;
+1import org.apache.iotdb.common.rpc.thrift.TSStatus;
+1import org.apache.iotdb.commons.cluster.NodeStatus;
+1import org.apache.iotdb.commons.cluster.NodeType;
+1import org.apache.iotdb.commons.pipe.agent.plugin.meta.PipePluginMeta;
+1import org.apache.iotdb.commons.pipe.config.PipeConfig;
+1import org.apache.iotdb.commons.trigger.TriggerInformation;
+1import org.apache.iotdb.confignode.client.CnToCnNodeRequestType;
+1import org.apache.iotdb.confignode.client.async.CnToDnAsyncRequestType;
+1import org.apache.iotdb.confignode.client.async.CnToDnInternalServiceAsyncRequestManager;
+1import org.apache.iotdb.confignode.client.async.handlers.DataNodeAsyncRequestContext;
+1import org.apache.iotdb.confignode.client.sync.CnToDnSyncRequestType;
+1import org.apache.iotdb.confignode.client.sync.SyncConfigNodeClientPool;
+1import org.apache.iotdb.confignode.client.sync.SyncDataNodeClientPool;
+1import org.apache.iotdb.confignode.consensus.request.write.confignode.RemoveConfigNodePlan;
+1import org.apache.iotdb.confignode.consensus.request.write.database.DeleteDatabasePlan;
+1import org.apache.iotdb.confignode.consensus.request.write.database.PreDeleteDatabasePlan;
+1import org.apache.iotdb.confignode.consensus.request.write.region.CreateRegionGroupsPlan;
+1import org.apache.iotdb.confignode.exception.AddConsensusGroupException;
+1import org.apache.iotdb.confignode.exception.AddPeerException;
+1import org.apache.iotdb.confignode.manager.ConfigManager;
+1import org.apache.iotdb.confignode.manager.consensus.ConsensusManager;
+1import org.apache.iotdb.confignode.manager.load.LoadManager;
+1import org.apache.iotdb.confignode.manager.load.cache.region.RegionHeartbeatSample;
+1import org.apache.iotdb.confignode.manager.node.NodeManager;
+1import org.apache.iotdb.confignode.manager.partition.PartitionManager;
+1import org.apache.iotdb.confignode.manager.schema.ClusterSchemaManager;
+1import org.apache.iotdb.confignode.persistence.partition.PartitionInfo;
+1import org.apache.iotdb.confignode.persistence.schema.ClusterSchemaInfo;
+1import org.apache.iotdb.confignode.procedure.exception.ProcedureException;
+1import org.apache.iotdb.confignode.procedure.scheduler.LockQueue;
+1import org.apache.iotdb.confignode.procedure.scheduler.ProcedureScheduler;
+1import org.apache.iotdb.confignode.rpc.thrift.TAddConsensusGroupReq;
+1import org.apache.iotdb.confignode.rpc.thrift.TNodeVersionInfo;
+1import org.apache.iotdb.consensus.exception.ConsensusException;
+1import org.apache.iotdb.db.protocol.client.ConfigNodeInfo;
+1import org.apache.iotdb.mpp.rpc.thrift.TActiveTriggerInstanceReq;
+1import org.apache.iotdb.mpp.rpc.thrift.TCreateDataRegionReq;
+1import org.apache.iotdb.mpp.rpc.thrift.TCreatePipePluginInstanceReq;
+1import org.apache.iotdb.mpp.rpc.thrift.TCreateSchemaRegionReq;
+1import org.apache.iotdb.mpp.rpc.thrift.TCreateTriggerInstanceReq;
+1import org.apache.iotdb.mpp.rpc.thrift.TDropPipePluginInstanceReq;
+1import org.apache.iotdb.mpp.rpc.thrift.TDropTriggerInstanceReq;
+1import org.apache.iotdb.mpp.rpc.thrift.TInactiveTriggerInstanceReq;
+1import org.apache.iotdb.mpp.rpc.thrift.TInvalidateCacheReq;
+1import org.apache.iotdb.mpp.rpc.thrift.TNotifyRegionMigrationReq;
+1import org.apache.iotdb.mpp.rpc.thrift.TPushConsumerGroupMetaReq;
+1import org.apache.iotdb.mpp.rpc.thrift.TPushConsumerGroupMetaResp;
+1import org.apache.iotdb.mpp.rpc.thrift.TPushMultiPipeMetaReq;
+1import org.apache.iotdb.mpp.rpc.thrift.TPushMultiTopicMetaReq;
+1import org.apache.iotdb.mpp.rpc.thrift.TPushPipeMetaReq;
+1import org.apache.iotdb.mpp.rpc.thrift.TPushPipeMetaResp;
+1import org.apache.iotdb.mpp.rpc.thrift.TPushSingleConsumerGroupMetaReq;
+1import org.apache.iotdb.mpp.rpc.thrift.TPushSinglePipeMetaReq;
+1import org.apache.iotdb.mpp.rpc.thrift.TPushSingleTopicMetaReq;
+1import org.apache.iotdb.mpp.rpc.thrift.TPushTopicMetaReq;
+1import org.apache.iotdb.mpp.rpc.thrift.TPushTopicMetaResp;
+1import org.apache.iotdb.rpc.TSStatusCode;
+1
+1import org.apache.thrift.TException;
+1import org.apache.tsfile.utils.Binary;
+1import org.slf4j.Logger;
+1import org.slf4j.LoggerFactory;
+1
+1import java.io.IOException;
+1import java.nio.ByteBuffer;
+1import java.util.ArrayList;
+1import java.util.Arrays;
+1import java.util.HashMap;
+1import java.util.List;
+1import java.util.Map;
+1import java.util.concurrent.TimeUnit;
+1import java.util.concurrent.locks.ReentrantLock;
+1import java.util.stream.Collectors;
+1
+1public class ConfigNodeProcedureEnv {
+1
+1 private static final Logger LOG = LoggerFactory.getLogger(ConfigNodeProcedureEnv.class);
+1
+1 /** Add or remove node lock. */
+1 private final LockQueue nodeLock = new LockQueue();
+1
+1 private final ReentrantLock schedulerLock = new ReentrantLock(true);
+1
+1 private final ReentrantLock submitRegionMigrateLock = new ReentrantLock(true);
+1
+1 private final ConfigManager configManager;
+1
+1 private final ProcedureScheduler scheduler;
+1
+1 private final RegionMaintainHandler regionMaintainHandler;
+1
+1 private final RemoveDataNodeHandler removeDataNodeHandler;
+1
+1 private final ReentrantLock removeConfigNodeLock;
+1
+1 public ConfigNodeProcedureEnv(ConfigManager configManager, ProcedureScheduler scheduler) {
+1 this.configManager = configManager;
+1 this.scheduler = scheduler;
+1 this.regionMaintainHandler = new RegionMaintainHandler(configManager);
+1 this.removeDataNodeHandler = new RemoveDataNodeHandler(configManager);
+1 this.removeConfigNodeLock = new ReentrantLock();
+1 }
+1
+1 public ConfigManager getConfigManager() {
+1 return configManager;
+1 }
+1
+1 /**
+1 * Delete ConfigNode cache, includes {@link ClusterSchemaInfo} and {@link PartitionInfo}.
+1 *
+1 * @param name database name
+1 * @param isGeneratedByPipe whether the deletion is triggered by pipe request
+1 * @return tsStatus
+1 */
+1 public TSStatus deleteDatabaseConfig(final String name, final boolean isGeneratedByPipe) {
+1 return getClusterSchemaManager()
+1 .deleteDatabase(new DeleteDatabasePlan(name), isGeneratedByPipe);
+1 }
+1
+1 /**
+1 * Pre delete a database.
+1 *
+1 * @param preDeleteType execute/rollback
+1 * @param deleteSgName database name
+1 */
+1 public void preDeleteDatabase(
+1 final PreDeleteDatabasePlan.PreDeleteType preDeleteType, final String deleteSgName) {
+1 getPartitionManager().preDeleteDatabase(deleteSgName, preDeleteType);
+1 }
+1
+1 /**
+1 * @param databaseName database name
+1 * @return ALL SUCCESS OR NOT
+1 * @throws IOException IOE
+1 * @throws TException Thrift IOE
+1 */
+1 public boolean invalidateCache(final String databaseName) throws IOException, TException {
+1 final List<TDataNodeConfiguration> allDataNodes = getNodeManager().getRegisteredDataNodes();
+1 final TInvalidateCacheReq invalidateCacheReq = new TInvalidateCacheReq();
+1 invalidateCacheReq.setStorageGroup(true);
+1 invalidateCacheReq.setFullPath(databaseName);
+1 for (final TDataNodeConfiguration dataNodeConfiguration : allDataNodes) {
+1 final int dataNodeId = dataNodeConfiguration.getLocation().getDataNodeId();
+1
+1 // If the node is not alive, retry for up to 10 times
+1 NodeStatus nodeStatus = getLoadManager().getNodeStatus(dataNodeId);
+1 int retryNum = 10;
+1 if (nodeStatus == NodeStatus.Unknown) {
+1 for (int i = 0; i < retryNum && nodeStatus == NodeStatus.Unknown; i++) {
+1 try {
+1 TimeUnit.MILLISECONDS.sleep(500);
+1 } catch (final InterruptedException e) {
+1 LOG.error("Sleep failed in ConfigNodeProcedureEnv: ", e);
+1 Thread.currentThread().interrupt();
+1 break;
+1 }
+1 nodeStatus = getLoadManager().getNodeStatus(dataNodeId);
+1 }
+1 }
+1
+1 if (nodeStatus == NodeStatus.Unknown) {
+1 LOG.warn(
+1 "Invalidate cache failed, because DataNode {} is Unknown",
+1 dataNodeConfiguration.getLocation().getInternalEndPoint());
+1 return false;
+1 }
+1
+1 // Always invalidate PartitionCache first
+1 final TSStatus invalidatePartitionStatus =
+1 (TSStatus)
+1 SyncDataNodeClientPool.getInstance()
+1 .sendSyncRequestToDataNodeWithRetry(
+1 dataNodeConfiguration.getLocation().getInternalEndPoint(),
+1 invalidateCacheReq,
+1 CnToDnSyncRequestType.INVALIDATE_PARTITION_CACHE);
+1
+1 final TSStatus invalidateSchemaStatus =
+1 (TSStatus)
+1 SyncDataNodeClientPool.getInstance()
+1 .sendSyncRequestToDataNodeWithRetry(
+1 dataNodeConfiguration.getLocation().getInternalEndPoint(),
+1 invalidateCacheReq,
+1 CnToDnSyncRequestType.INVALIDATE_SCHEMA_CACHE);
+1
+1 if (!verifySucceed(invalidatePartitionStatus, invalidateSchemaStatus)) {
+1 LOG.error(
+1 "Invalidate cache failed, invalidate partition cache status is {}, invalidate schemaengine cache status is {}",
+1 invalidatePartitionStatus,
+1 invalidateSchemaStatus);
+1 return false;
+1 }
+1 }
+1 return true;
+1 }
+1
+1 public boolean verifySucceed(TSStatus... status) {
+1 return Arrays.stream(status)
+1 .allMatch(tsStatus -> tsStatus.getCode() == TSStatusCode.SUCCESS_STATUS.getStatusCode());
+1 }
+1
+1 /**
+1 * Let the remotely new ConfigNode build the ConsensusGroup.
+1 *
+1 * <p>Actually, the parameter of this method can be empty, adding new raft peer to exist group
+1 * should invoke createPeer(groupId, emptyList).
+1 *
+1 * @param tConfigNodeLocation New ConfigNode's location
+1 */
+1 public void addConsensusGroup(TConfigNodeLocation tConfigNodeLocation)
+1 throws AddConsensusGroupException {
+1 List<TConfigNodeLocation> configNodeLocations =
+1 new ArrayList<>(configManager.getNodeManager().getRegisteredConfigNodes());
+1 configNodeLocations.add(tConfigNodeLocation);
+1 TSStatus status =
+1 (TSStatus)
+1 SyncConfigNodeClientPool.getInstance()
+1 .sendSyncRequestToConfigNodeWithRetry(
+1 tConfigNodeLocation.getInternalEndPoint(),
+1 new TAddConsensusGroupReq(configNodeLocations),
+1 CnToCnNodeRequestType.ADD_CONSENSUS_GROUP);
+1 if (status.getCode() != TSStatusCode.SUCCESS_STATUS.getStatusCode()) {
+1 throw new AddConsensusGroupException(tConfigNodeLocation);
+1 }
+1 }
+1
+1 /**
+1 * Leader will add the new ConfigNode Peer into ConfigRegion.
+1 *
+1 * @param configNodeLocation The new ConfigNode
+1 * @throws AddPeerException When addPeer doesn't success
+1 */
+1 public void addConfigNodePeer(TConfigNodeLocation configNodeLocation) throws AddPeerException {
+1 configManager.getConsensusManager().addConfigNodePeer(configNodeLocation);
+1 }
+1
+1 /**
+1 * Remove peer in Leader node.
+1 *
+1 * @param tConfigNodeLocation node is removed
+1 * @throws ProcedureException if failed status
+1 */
+1 public void removeConfigNodePeer(TConfigNodeLocation tConfigNodeLocation)
+1 throws ProcedureException {
+1 removeConfigNodeLock.lock();
+1 TSStatus tsStatus;
+1 try {
+1 // Execute removePeer
+1 if (getConsensusManager().removeConfigNodePeer(tConfigNodeLocation)) {
+1 tsStatus = getConsensusManager().write(new RemoveConfigNodePlan(tConfigNodeLocation));
+1 } else {
+1 tsStatus =
+1 new TSStatus(TSStatusCode.REMOVE_CONFIGNODE_ERROR.getStatusCode())
+1 .setMessage(
+1 "Remove ConfigNode failed because update ConsensusGroup peer information failed.");
+1 }
+1 } catch (ConsensusException e) {
+1 LOG.warn("Failed in the write API executing the consensus layer due to: ", e);
+1 tsStatus = new TSStatus(TSStatusCode.EXECUTE_STATEMENT_ERROR.getStatusCode());
+1 tsStatus.setMessage(e.getMessage());
+1 }
+1 try {
+1 if (tsStatus.getCode() != TSStatusCode.SUCCESS_STATUS.getStatusCode()) {
+1 throw new ProcedureException(tsStatus.getMessage());
+1 }
+1 } finally {
+1 removeConfigNodeLock.unlock();
+1 }
+1 }
+1
+1 /**
+1 * Remove Consensus Group in removed node.
+1 *
+1 * @param removedConfigNode config node location
+1 * @throws ProcedureException if failed status
+1 */
+1 public void deleteConfigNodePeer(TConfigNodeLocation removedConfigNode)
+1 throws ProcedureException {
+1 TSStatus tsStatus =
+1 (TSStatus)
+1 SyncConfigNodeClientPool.getInstance()
+1 .sendSyncRequestToConfigNodeWithRetry(
+1 removedConfigNode.getInternalEndPoint(),
+1 removedConfigNode,
+1 CnToCnNodeRequestType.DELETE_CONFIG_NODE_PEER);
+1 if (tsStatus.getCode() != TSStatusCode.SUCCESS_STATUS.getStatusCode()) {
+1 throw new ProcedureException(tsStatus.getMessage());
+1 }
+1 }
+1
+1 /**
+1 * Stop ConfigNode and remove heartbeatCache.
+1 *
+1 * @param tConfigNodeLocation config node location
+1 * @throws ProcedureException if failed status
+1 */
+1 public void stopAndClearConfigNode(TConfigNodeLocation tConfigNodeLocation)
+1 throws ProcedureException {
+1 TSStatus tsStatus =
+1 (TSStatus)
+1 SyncConfigNodeClientPool.getInstance()
+1 .sendSyncRequestToConfigNodeWithRetry(
+1 tConfigNodeLocation.getInternalEndPoint(),
+1 tConfigNodeLocation,
+1 CnToCnNodeRequestType.STOP_AND_CLEAR_CONFIG_NODE);
+1
+1 if (tsStatus.getCode() != TSStatusCode.SUCCESS_STATUS.getStatusCode()) {
+1 throw new ProcedureException(tsStatus.getMessage());
+1 }
+1
+1 getLoadManager().removeNodeCache(tConfigNodeLocation.getConfigNodeId());
+1 }
+1
+1 /**
+1 * Leader will record the new ConfigNode's information.
+1 *
+1 * @param configNodeLocation The new ConfigNode
+1 * @param versionInfo The new ConfigNode's versionInfo
+1 */
+1 public void applyConfigNode(
+1 TConfigNodeLocation configNodeLocation, TNodeVersionInfo versionInfo) {
+1 configManager.getNodeManager().applyConfigNode(configNodeLocation, versionInfo);
+1 }
+1
+1 /**
+1 * Leader will notify the new ConfigNode that registration success.
+1 *
+1 * @param configNodeLocation The new ConfigNode
+1 */
+1 public void notifyRegisterSuccess(TConfigNodeLocation configNodeLocation) {
+1 SyncConfigNodeClientPool.getInstance()
+1 .sendSyncRequestToConfigNodeWithRetry(
+1 configNodeLocation.getInternalEndPoint(),
+1 null,
+1 CnToCnNodeRequestType.NOTIFY_REGISTER_SUCCESS);
+1 }
+1
+1 /**
+1 * Create a new ConfigNodeHeartbeatCache
+1 *
+1 * @param nodeId the index of the new ConfigNode
+1 */
+1 public void createConfigNodeHeartbeatCache(int nodeId) {
+1 getLoadManager().getLoadCache().createNodeHeartbeatCache(NodeType.ConfigNode, nodeId);
+1 // TODO: invoke a force heartbeat to update new ConfigNode's status immediately
+1 }
+1
+1 /**
+1 * Do region creations and broadcast the {@link CreateRegionGroupsPlan}.
+1 *
+1 * @return Those RegionReplicas that failed to create
+1 */
+1 public Map<TConsensusGroupId, TRegionReplicaSet> doRegionCreation(
+1 final TConsensusGroupType consensusGroupType,
+1 final CreateRegionGroupsPlan createRegionGroupsPlan) {
+1
+1 // Prepare clientHandler
+1 DataNodeAsyncRequestContext<?, TSStatus> clientHandler;
+1 switch (consensusGroupType) {
+1 case SchemaRegion:
+1 clientHandler = getCreateSchemaRegionClientHandler(createRegionGroupsPlan);
+1 break;
+1 case DataRegion:
+1 default:
+1 clientHandler = getCreateDataRegionClientHandler(createRegionGroupsPlan);
+1 break;
+1 }
+1 if (clientHandler.getRequestIndices().isEmpty()) {
+1 return new HashMap<>();
+1 }
+1
+1 // Send CreateRegion requests to DataNodes
+1 CnToDnInternalServiceAsyncRequestManager.getInstance().sendAsyncRequestWithRetry(clientHandler);
+1
+1 // Filter RegionGroups that weren't created successfully
+1 int requestId = 0;
+1 Map<Integer, TSStatus> responseMap = clientHandler.getResponseMap();
+1 Map<TConsensusGroupId, TRegionReplicaSet> failedRegions = new HashMap<>();
+1 for (List<TRegionReplicaSet> regionReplicaSets :
+1 createRegionGroupsPlan.getRegionGroupMap().values()) {
+1 for (final TRegionReplicaSet regionReplicaSet : regionReplicaSets) {
+1 for (final TDataNodeLocation dataNodeLocation : regionReplicaSet.getDataNodeLocations()) {
+1 if (responseMap.get(requestId).getCode() != TSStatusCode.SUCCESS_STATUS.getStatusCode()) {
+1 failedRegions
+1 .computeIfAbsent(
+1 regionReplicaSet.getRegionId(),
+1 empty -> new TRegionReplicaSet().setRegionId(regionReplicaSet.getRegionId()))
+1 .addToDataNodeLocations(dataNodeLocation);
+1 }
+1 requestId += 1;
+1 }
+1 }
+1 }
+1 return failedRegions;
+1 }
+1
+1 private DataNodeAsyncRequestContext<TCreateSchemaRegionReq, TSStatus>
+1 getCreateSchemaRegionClientHandler(CreateRegionGroupsPlan createRegionGroupsPlan) {
+1 DataNodeAsyncRequestContext<TCreateSchemaRegionReq, TSStatus> clientHandler =
+1 new DataNodeAsyncRequestContext<>(CnToDnAsyncRequestType.CREATE_SCHEMA_REGION);
+1
+1 int requestId = 0;
+1 for (Map.Entry<String, List<TRegionReplicaSet>> sgRegionsEntry :
+1 createRegionGroupsPlan.getRegionGroupMap().entrySet()) {
+1 String storageGroup = sgRegionsEntry.getKey();
+1 List<TRegionReplicaSet> regionReplicaSets = sgRegionsEntry.getValue();
+1 for (TRegionReplicaSet regionReplicaSet : regionReplicaSets) {
+1 for (TDataNodeLocation dataNodeLocation : regionReplicaSet.getDataNodeLocations()) {
+1 clientHandler.putRequest(
+1 requestId, genCreateSchemaRegionReq(storageGroup, regionReplicaSet));
+1 clientHandler.putNodeLocation(requestId, dataNodeLocation);
+1 requestId += 1;
+1 }
+1 }
+1 }
+1
+1 return clientHandler;
+1 }
+1
+1 private DataNodeAsyncRequestContext<TCreateDataRegionReq, TSStatus>
+1 getCreateDataRegionClientHandler(CreateRegionGroupsPlan createRegionGroupsPlan) {
+1 DataNodeAsyncRequestContext<TCreateDataRegionReq, TSStatus> clientHandler =
+1 new DataNodeAsyncRequestContext<>(CnToDnAsyncRequestType.CREATE_DATA_REGION);
+1
+1 int requestId = 0;
+1 for (Map.Entry<String, List<TRegionReplicaSet>> sgRegionsEntry :
+1 createRegionGroupsPlan.getRegionGroupMap().entrySet()) {
+1 String storageGroup = sgRegionsEntry.getKey();
+1 List<TRegionReplicaSet> regionReplicaSets = sgRegionsEntry.getValue();
+1 for (TRegionReplicaSet regionReplicaSet : regionReplicaSets) {
+1 for (TDataNodeLocation dataNodeLocation : regionReplicaSet.getDataNodeLocations()) {
+1 clientHandler.putRequest(
+1 requestId, genCreateDataRegionReq(storageGroup, regionReplicaSet));
+1 clientHandler.putNodeLocation(requestId, dataNodeLocation);
+1 requestId += 1;
+1 }
+1 }
+1 }
+1
+1 return clientHandler;
+1 }
+1
+1 private TCreateSchemaRegionReq genCreateSchemaRegionReq(
+1 String storageGroup, TRegionReplicaSet regionReplicaSet) {
+1 TCreateSchemaRegionReq req = new TCreateSchemaRegionReq();
+1 req.setStorageGroup(storageGroup);
+1 req.setRegionReplicaSet(regionReplicaSet);
+1 return req;
+1 }
+1
+1 private TCreateDataRegionReq genCreateDataRegionReq(
+1 String storageGroup, TRegionReplicaSet regionReplicaSet) {
+1 TCreateDataRegionReq req = new TCreateDataRegionReq();
+1 req.setStorageGroup(storageGroup);
+1 req.setRegionReplicaSet(regionReplicaSet);
+1 return req;
+1 }
+1
+1 public List<TSStatus> notifyRegionMigrationToAllDataNodes(
+1 TConsensusGroupId consensusGroupId, boolean isStart) {
+1 final Map<Integer, TDataNodeLocation> dataNodeLocationMap =
+1 configManager.getNodeManager().getRegisteredDataNodeLocations();
+1 final TNotifyRegionMigrationReq request =
+1 new TNotifyRegionMigrationReq(
+1 configManager
+1 .getConsensusManager()
+1 .getConsensusImpl()
+1 .getLogicalClock(ConfigNodeInfo.CONFIG_REGION_ID),
+1 System.nanoTime(),
+1 configManager.getProcedureManager().getRegionOperationConsensusIds());
+1 request.setRegionId(consensusGroupId);
+1 request.setIsStart(isStart);
+1
+1 final DataNodeAsyncRequestContext<TNotifyRegionMigrationReq, TSStatus> clientHandler =
+1 new DataNodeAsyncRequestContext<>(
+1 CnToDnAsyncRequestType.NOTIFY_REGION_MIGRATION, request, dataNodeLocationMap);
+1 CnToDnInternalServiceAsyncRequestManager.getInstance().sendAsyncRequestWithRetry(clientHandler);
+1 return clientHandler.getResponseList();
+1 }
+1
+1 public void persistRegionGroup(CreateRegionGroupsPlan createRegionGroupsPlan) {
+1 // Persist the allocation result
+1 try {
+1 getConsensusManager().write(createRegionGroupsPlan);
+1 } catch (ConsensusException e) {
+1 LOG.warn("Failed in the write API executing the consensus layer due to: ", e);
+1 }
+1 }
+1
+1 /**
+1 * Force activating RegionGroup by setting status to Running, therefore the ConfigNode-leader can
+1 * select leader for it and use it to allocate new Partitions
+1 *
+1 * @param activateRegionGroupMap Map<Database, Map<RegionGroupId, Map<DataNodeId, activate
+1 * heartbeat sample>>>
+1 */
+1 public void activateRegionGroup(
+1 Map<String, Map<TConsensusGroupId, Map<Integer, RegionHeartbeatSample>>>
+1 activateRegionGroupMap) {
+1 // Create RegionGroup heartbeat Caches
+1 activateRegionGroupMap.forEach(
+1 (database, regionGroupSampleMap) ->
+1 regionGroupSampleMap.forEach(
+1 (regionGroupId, regionSampleMap) ->
+1 getLoadManager()
+1 .getLoadCache()
+1 .createRegionGroupHeartbeatCache(
+1 database, regionGroupId, regionSampleMap.keySet())));
+1 // Force update first heartbeat samples
+1 getLoadManager()
+1 .forceUpdateRegionGroupCache(
+1 activateRegionGroupMap.values().stream()
+1 .flatMap(innerMap -> innerMap.entrySet().stream())
+1 .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue, (a, b) -> b)));
+1 // Wait for leader and priority redistribution
+1 getLoadManager()
+1 .waitForRegionGroupReady(
+1 activateRegionGroupMap.values().stream()
+1 .flatMap(innterMap -> innterMap.keySet().stream())
+1 .collect(Collectors.toList()));
+1 }
+1
+1 public List<TRegionReplicaSet> getAllReplicaSets(String storageGroup) {
+1 return getPartitionManager().getAllReplicaSets(storageGroup);
+1 }
+1
+1 public List<TSStatus> createTriggerOnDataNodes(
+1 TriggerInformation triggerInformation, Binary jarFile) throws IOException {
+1 NodeManager nodeManager = configManager.getNodeManager();
+1 final Map<Integer, TDataNodeLocation> dataNodeLocationMap =
+1 nodeManager.getRegisteredDataNodeLocations();
+1 final TCreateTriggerInstanceReq request =
+1 new TCreateTriggerInstanceReq(triggerInformation.serialize());
+1 if (jarFile != null) {
+1 request.setJarFile(ByteBuffer.wrap(jarFile.getValues()));
+1 }
+1
+1 DataNodeAsyncRequestContext<TCreateTriggerInstanceReq, TSStatus> clientHandler =
+1 new DataNodeAsyncRequestContext<>(
+1 CnToDnAsyncRequestType.CREATE_TRIGGER_INSTANCE, request, dataNodeLocationMap);
+1 CnToDnInternalServiceAsyncRequestManager.getInstance().sendAsyncRequestWithRetry(clientHandler);
+1 return clientHandler.getResponseList();
+1 }
+1
+1 public List<TSStatus> dropTriggerOnDataNodes(String triggerName, boolean needToDeleteJarFile) {
+1 NodeManager nodeManager = configManager.getNodeManager();
+1 final Map<Integer, TDataNodeLocation> dataNodeLocationMap =
+1 nodeManager.getRegisteredDataNodeLocations();
+1 final TDropTriggerInstanceReq request =
+1 new TDropTriggerInstanceReq(triggerName, needToDeleteJarFile);
+1
+1 DataNodeAsyncRequestContext<TDropTriggerInstanceReq, TSStatus> clientHandler =
+1 new DataNodeAsyncRequestContext<>(
+1 CnToDnAsyncRequestType.DROP_TRIGGER_INSTANCE, request, dataNodeLocationMap);
+1 CnToDnInternalServiceAsyncRequestManager.getInstance().sendAsyncRequestWithRetry(clientHandler);
+1 return clientHandler.getResponseList();
+1 }
+1
+1 public List<TSStatus> activeTriggerOnDataNodes(String triggerName) {
+1 NodeManager nodeManager = configManager.getNodeManager();
+1 final Map<Integer, TDataNodeLocation> dataNodeLocationMap =
+1 nodeManager.getRegisteredDataNodeLocations();
+1 final TActiveTriggerInstanceReq request = new TActiveTriggerInstanceReq(triggerName);
+1
+1 DataNodeAsyncRequestContext<TActiveTriggerInstanceReq, TSStatus> clientHandler =
+1 new DataNodeAsyncRequestContext<>(
+1 CnToDnAsyncRequestType.ACTIVE_TRIGGER_INSTANCE, request, dataNodeLocationMap);
+1 CnToDnInternalServiceAsyncRequestManager.getInstance().sendAsyncRequestWithRetry(clientHandler);
+1 return clientHandler.getResponseList();
+1 }
+1
+1 public List<TSStatus> inactiveTriggerOnDataNodes(String triggerName) {
+1 NodeManager nodeManager = configManager.getNodeManager();
+1 final Map<Integer, TDataNodeLocation> dataNodeLocationMap =
+1 nodeManager.getRegisteredDataNodeLocations();
+1 final TInactiveTriggerInstanceReq request = new TInactiveTriggerInstanceReq(triggerName);
+1
+1 DataNodeAsyncRequestContext<TInactiveTriggerInstanceReq, TSStatus> clientHandler =
+1 new DataNodeAsyncRequestContext<>(
+1 CnToDnAsyncRequestType.INACTIVE_TRIGGER_INSTANCE, request, dataNodeLocationMap);
+1 CnToDnInternalServiceAsyncRequestManager.getInstance().sendAsyncRequestWithRetry(clientHandler);
+1 return clientHandler.getResponseList();
+1 }
+1
+1 public List<TSStatus> createPipePluginOnDataNodes(PipePluginMeta pipePluginMeta, byte[] jarFile)
+1 throws IOException {
+1 final Map<Integer, TDataNodeLocation> dataNodeLocationMap =
+1 configManager.getNodeManager().getRegisteredDataNodeLocations();
+1 final TCreatePipePluginInstanceReq request =
+1 new TCreatePipePluginInstanceReq(pipePluginMeta.serialize(), ByteBuffer.wrap(jarFile));
+1
+1 final DataNodeAsyncRequestContext<TCreatePipePluginInstanceReq, TSStatus> clientHandler =
+1 new DataNodeAsyncRequestContext<>(
+1 CnToDnAsyncRequestType.CREATE_PIPE_PLUGIN, request, dataNodeLocationMap);
+1 CnToDnInternalServiceAsyncRequestManager.getInstance().sendAsyncRequestWithRetry(clientHandler);
+1 return clientHandler.getResponseList();
+1 }
+1
+1 public List<TSStatus> dropPipePluginOnDataNodes(
+1 String pipePluginName, boolean needToDeleteJarFile) {
+1 final Map<Integer, TDataNodeLocation> dataNodeLocationMap =
+1 configManager.getNodeManager().getRegisteredDataNodeLocations();
+1 final TDropPipePluginInstanceReq request =
+1 new TDropPipePluginInstanceReq(pipePluginName, needToDeleteJarFile);
+1
+1 DataNodeAsyncRequestContext<TDropPipePluginInstanceReq, TSStatus> clientHandler =
+1 new DataNodeAsyncRequestContext<>(
+1 CnToDnAsyncRequestType.DROP_PIPE_PLUGIN, request, dataNodeLocationMap);
+1 CnToDnInternalServiceAsyncRequestManager.getInstance().sendAsyncRequestWithRetry(clientHandler);
+1 return clientHandler.getResponseList();
+1 }
+1
+1 public Map<Integer, TPushPipeMetaResp> pushAllPipeMetaToDataNodes(
+1 List<ByteBuffer> pipeMetaBinaryList) {
+1 final Map<Integer, TDataNodeLocation> dataNodeLocationMap =
+1 configManager.getNodeManager().getRegisteredDataNodeLocations();
+1 final TPushPipeMetaReq request = new TPushPipeMetaReq().setPipeMetas(pipeMetaBinaryList);
+1
+1 final DataNodeAsyncRequestContext<TPushPipeMetaReq, TPushPipeMetaResp> clientHandler =
+1 new DataNodeAsyncRequestContext<>(
+1 CnToDnAsyncRequestType.PIPE_PUSH_ALL_META, request, dataNodeLocationMap);
+1 CnToDnInternalServiceAsyncRequestManager.getInstance()
+1 .sendAsyncRequestToNodeWithRetryAndTimeoutInMs(
+1 clientHandler,
+1 PipeConfig.getInstance().getPipeMetaSyncerSyncIntervalMinutes() * 60 * 1000 * 2 / 3);
+1 return clientHandler.getResponseMap();
+1 }
+1
+1 public Map<Integer, TPushPipeMetaResp> pushSinglePipeMetaToDataNodes(ByteBuffer pipeMetaBinary) {
+1 final Map<Integer, TDataNodeLocation> dataNodeLocationMap =
+1 configManager.getNodeManager().getRegisteredDataNodeLocations();
+1 final TPushSinglePipeMetaReq request = new TPushSinglePipeMetaReq().setPipeMeta(pipeMetaBinary);
+1
+1 final DataNodeAsyncRequestContext<TPushSinglePipeMetaReq, TPushPipeMetaResp> clientHandler =
+1 new DataNodeAsyncRequestContext<>(
+1 CnToDnAsyncRequestType.PIPE_PUSH_SINGLE_META, request, dataNodeLocationMap);
+1 CnToDnInternalServiceAsyncRequestManager.getInstance()
+1 .sendAsyncRequestToNodeWithRetryAndTimeoutInMs(
+1 clientHandler,
+1 PipeConfig.getInstance().getPipeMetaSyncerSyncIntervalMinutes() * 60 * 1000 * 2 / 3);
+1 return clientHandler.getResponseMap();
+1 }
+1
+1 public Map<Integer, TPushPipeMetaResp> dropSinglePipeOnDataNodes(String pipeNameToDrop) {
+1 final Map<Integer, TDataNodeLocation> dataNodeLocationMap =
+1 configManager.getNodeManager().getRegisteredDataNodeLocations();
+1 final TPushSinglePipeMetaReq request =
+1 new TPushSinglePipeMetaReq().setPipeNameToDrop(pipeNameToDrop);
+1
+1 final DataNodeAsyncRequestContext<TPushSinglePipeMetaReq, TPushPipeMetaResp> clientHandler =
+1 new DataNodeAsyncRequestContext<>(
+1 CnToDnAsyncRequestType.PIPE_PUSH_SINGLE_META, request, dataNodeLocationMap);
+1 CnToDnInternalServiceAsyncRequestManager.getInstance()
+1 .sendAsyncRequestToNodeWithRetryAndTimeoutInMs(
+1 clientHandler,
+1 PipeConfig.getInstance().getPipeMetaSyncerSyncIntervalMinutes() * 60 * 1000 * 2 / 3);
+1 return clientHandler.getResponseMap();
+1 }
+1
+1 public Map<Integer, TPushPipeMetaResp> pushMultiPipeMetaToDataNodes(
+1 List<ByteBuffer> pipeMetaBinaryList) {
+1 final Map<Integer, TDataNodeLocation> dataNodeLocationMap =
+1 configManager.getNodeManager().getRegisteredDataNodeLocations();
+1 final TPushMultiPipeMetaReq request =
+1 new TPushMultiPipeMetaReq().setPipeMetas(pipeMetaBinaryList);
+1
+1 final DataNodeAsyncRequestContext<TPushMultiPipeMetaReq, TPushPipeMetaResp> clientHandler =
+1 new DataNodeAsyncRequestContext<>(
+1 CnToDnAsyncRequestType.PIPE_PUSH_MULTI_META, request, dataNodeLocationMap);
+1 CnToDnInternalServiceAsyncRequestManager.getInstance()
+1 .sendAsyncRequestToNodeWithRetryAndTimeoutInMs(
+1 clientHandler,
+1 PipeConfig.getInstance().getPipeMetaSyncerSyncIntervalMinutes() * 60 * 1000 * 2 / 3);
+1 return clientHandler.getResponseMap();
+1 }
+1
+1 public Map<Integer, TPushPipeMetaResp> dropMultiPipeOnDataNodes(List<String> pipeNamesToDrop) {
+1 final Map<Integer, TDataNodeLocation> dataNodeLocationMap =
+1 configManager.getNodeManager().getRegisteredDataNodeLocations();
+1 final TPushMultiPipeMetaReq request =
+1 new TPushMultiPipeMetaReq().setPipeNamesToDrop(pipeNamesToDrop);
+1
+1 final DataNodeAsyncRequestContext<TPushMultiPipeMetaReq, TPushPipeMetaResp> clientHandler =
+1 new DataNodeAsyncRequestContext<>(
+1 CnToDnAsyncRequestType.PIPE_PUSH_MULTI_META, request, dataNodeLocationMap);
+1 CnToDnInternalServiceAsyncRequestManager.getInstance()
+1 .sendAsyncRequestToNodeWithRetryAndTimeoutInMs(
+1 clientHandler,
+1 PipeConfig.getInstance().getPipeMetaSyncerSyncIntervalMinutes() * 60 * 1000 * 2 / 3);
+1 return clientHandler.getResponseMap();
+1 }
+1
+1 public Map<Integer, TPushTopicMetaResp> pushAllTopicMetaToDataNodes(
+1 List<ByteBuffer> topicMetaBinaryList) {
+1 final Map<Integer, TDataNodeLocation> dataNodeLocationMap =
+1 configManager.getNodeManager().getRegisteredDataNodeLocations();
+1 final TPushTopicMetaReq request = new TPushTopicMetaReq().setTopicMetas(topicMetaBinaryList);
+1
+1 final DataNodeAsyncRequestContext<TPushTopicMetaReq, TPushTopicMetaResp> clientHandler =
+1 new DataNodeAsyncRequestContext<>(
+1 CnToDnAsyncRequestType.TOPIC_PUSH_ALL_META, request, dataNodeLocationMap);
+1 CnToDnInternalServiceAsyncRequestManager.getInstance()
+1 .sendAsyncRequestToNodeWithRetryAndTimeoutInMs(
+1 clientHandler,
+1 PipeConfig.getInstance().getPipeMetaSyncerSyncIntervalMinutes() * 60 * 1000 * 2 / 3);
+1 return clientHandler.getResponseMap();
+1 }
+1
+1 public List<TSStatus> pushSingleTopicOnDataNode(ByteBuffer topicMeta) {
+1 final Map<Integer, TDataNodeLocation> dataNodeLocationMap =
+1 configManager.getNodeManager().getRegisteredDataNodeLocations();
+1 final TPushSingleTopicMetaReq request = new TPushSingleTopicMetaReq().setTopicMeta(topicMeta);
+1
+1 final DataNodeAsyncRequestContext<TPushSingleTopicMetaReq, TPushTopicMetaResp> clientHandler =
+1 new DataNodeAsyncRequestContext<>(
+1 CnToDnAsyncRequestType.TOPIC_PUSH_SINGLE_META, request, dataNodeLocationMap);
+1 CnToDnInternalServiceAsyncRequestManager.getInstance().sendAsyncRequestWithRetry(clientHandler);
+1 return clientHandler.getResponseList().stream()
+1 .map(TPushTopicMetaResp::getStatus)
+1 .collect(Collectors.toList());
+1 }
+1
+1 public List<TSStatus> dropSingleTopicOnDataNode(String topicNameToDrop) {
+1 final Map<Integer, TDataNodeLocation> dataNodeLocationMap =
+1 configManager.getNodeManager().getRegisteredDataNodeLocations();
+1 final TPushSingleTopicMetaReq request =
+1 new TPushSingleTopicMetaReq().setTopicNameToDrop(topicNameToDrop);
+1
+1 final DataNodeAsyncRequestContext<TPushSingleTopicMetaReq, TPushTopicMetaResp> clientHandler =
+1 new DataNodeAsyncRequestContext<>(
+1 CnToDnAsyncRequestType.TOPIC_PUSH_SINGLE_META, request, dataNodeLocationMap);
+1 CnToDnInternalServiceAsyncRequestManager.getInstance().sendAsyncRequestWithRetry(clientHandler);
+1 return clientHandler.getResponseList().stream()
+1 .map(TPushTopicMetaResp::getStatus)
+1 .collect(Collectors.toList());
+1 }
+1
+1 public Map<Integer, TPushTopicMetaResp> pushMultiTopicMetaToDataNodes(
+1 List<ByteBuffer> topicMetaBinaryList) {
+1 final Map<Integer, TDataNodeLocation> dataNodeLocationMap =
+1 configManager.getNodeManager().getRegisteredDataNodeLocations();
+1 final TPushMultiTopicMetaReq request =
+1 new TPushMultiTopicMetaReq().setTopicMetas(topicMetaBinaryList);
+1
+1 final DataNodeAsyncRequestContext<TPushMultiTopicMetaReq, TPushTopicMetaResp> clientHandler =
+1 new DataNodeAsyncRequestContext<>(
+1 CnToDnAsyncRequestType.TOPIC_PUSH_MULTI_META, request, dataNodeLocationMap);
+1 CnToDnInternalServiceAsyncRequestManager.getInstance()
+1 .sendAsyncRequestToNodeWithRetryAndTimeoutInMs(
+1 clientHandler,
+1 PipeConfig.getInstance().getPipeMetaSyncerSyncIntervalMinutes() * 60 * 1000 * 2 / 3);
+1 return clientHandler.getResponseMap();
+1 }
+1
+1 public Map<Integer, TPushTopicMetaResp> dropMultiTopicOnDataNodes(List<String> topicNamesToDrop) {
+1 final Map<Integer, TDataNodeLocation> dataNodeLocationMap =
+1 configManager.getNodeManager().getRegisteredDataNodeLocations();
+1 final TPushMultiTopicMetaReq request =
+1 new TPushMultiTopicMetaReq().setTopicNamesToDrop(topicNamesToDrop);
+1
+1 final DataNodeAsyncRequestContext<TPushMultiTopicMetaReq, TPushTopicMetaResp> clientHandler =
+1 new DataNodeAsyncRequestContext<>(
+1 CnToDnAsyncRequestType.TOPIC_PUSH_MULTI_META, request, dataNodeLocationMap);
+1 CnToDnInternalServiceAsyncRequestManager.getInstance()
+1 .sendAsyncRequestToNodeWithRetryAndTimeoutInMs(
+1 clientHandler,
+1 PipeConfig.getInstance().getPipeMetaSyncerSyncIntervalMinutes() * 60 * 1000 * 2 / 3);
+1 return clientHandler.getResponseMap();
+1 }
+1
+1 public Map<Integer, TPushConsumerGroupMetaResp> pushAllConsumerGroupMetaToDataNodes(
+1 List<ByteBuffer> consumerGroupMetaBinaryList) {
+1 final Map<Integer, TDataNodeLocation> dataNodeLocationMap =
+1 configManager.getNodeManager().getRegisteredDataNodeLocations();
+1 final TPushConsumerGroupMetaReq request =
+1 new TPushConsumerGroupMetaReq().setConsumerGroupMetas(consumerGroupMetaBinaryList);
+1
+1 final DataNodeAsyncRequestContext<TPushConsumerGroupMetaReq, TPushConsumerGroupMetaResp>
+1 clientHandler =
+1 new DataNodeAsyncRequestContext<>(
+1 CnToDnAsyncRequestType.CONSUMER_GROUP_PUSH_ALL_META, request, dataNodeLocationMap);
+1 CnToDnInternalServiceAsyncRequestManager.getInstance()
+1 .sendAsyncRequestToNodeWithRetryAndTimeoutInMs(
+1 clientHandler,
+1 PipeConfig.getInstance().getPipeMetaSyncerSyncIntervalMinutes() * 60 * 1000 * 2 / 3);
+1 return clientHandler.getResponseMap();
+1 }
+1
+1 public List<TSStatus> pushSingleConsumerGroupOnDataNode(ByteBuffer consumerGroupMeta) {
+1 final Map<Integer, TDataNodeLocation> dataNodeLocationMap =
+1 configManager.getNodeManager().getRegisteredDataNodeLocations();
+1 final TPushSingleConsumerGroupMetaReq request =
+1 new TPushSingleConsumerGroupMetaReq().setConsumerGroupMeta(consumerGroupMeta);
+1
+1 final DataNodeAsyncRequestContext<TPushSingleConsumerGroupMetaReq, TPushConsumerGroupMetaResp>
+1 clientHandler =
+1 new DataNodeAsyncRequestContext<>(
+1 CnToDnAsyncRequestType.CONSUMER_GROUP_PUSH_SINGLE_META,
+1 request,
+1 dataNodeLocationMap);
+1 CnToDnInternalServiceAsyncRequestManager.getInstance().sendAsyncRequestWithRetry(clientHandler);
+1 return clientHandler.getResponseList().stream()
+1 .map(TPushConsumerGroupMetaResp::getStatus)
+1 .collect(Collectors.toList());
+1 }
+1
+1 public List<TSStatus> dropSingleConsumerGroupOnDataNode(String consumerGroupNameToDrop) {
+1 final Map<Integer, TDataNodeLocation> dataNodeLocationMap =
+1 configManager.getNodeManager().getRegisteredDataNodeLocations();
+1 final TPushSingleConsumerGroupMetaReq request =
+1 new TPushSingleConsumerGroupMetaReq().setConsumerGroupNameToDrop(consumerGroupNameToDrop);
+1
+1 final DataNodeAsyncRequestContext<TPushSingleConsumerGroupMetaReq, TPushConsumerGroupMetaResp>
+1 clientHandler =
+1 new DataNodeAsyncRequestContext<>(
+1 CnToDnAsyncRequestType.CONSUMER_GROUP_PUSH_SINGLE_META,
+1 request,
+1 dataNodeLocationMap);
+1 CnToDnInternalServiceAsyncRequestManager.getInstance().sendAsyncRequestWithRetry(clientHandler);
+1 return clientHandler.getResponseList().stream()
+1 .map(TPushConsumerGroupMetaResp::getStatus)
+1 .collect(Collectors.toList());
+1 }
+1
+1 public LockQueue getNodeLock() {
+1 return nodeLock;
+1 }
+1
+1 public ProcedureScheduler getScheduler() {
+1 return scheduler;
+1 }
+1
+1 public ReentrantLock getSchedulerLock() {
+1 return schedulerLock;
+1 }
+1
+1 public ReentrantLock getSubmitRegionMigrateLock() {
+1 return submitRegionMigrateLock;
+1 }
+1
+1 public RegionMaintainHandler getRegionMaintainHandler() {
+1 return regionMaintainHandler;
+1 }
+1
+1 public RemoveDataNodeHandler getRemoveDataNodeHandler() {
+1 return removeDataNodeHandler;
+1 }
+1
+1 private ConsensusManager getConsensusManager() {
+1 return configManager.getConsensusManager();
+1 }
+1
+1 private NodeManager getNodeManager() {
+1 return configManager.getNodeManager();
+1 }
+1
+1 private ClusterSchemaManager getClusterSchemaManager() {
+1 return configManager.getClusterSchemaManager();
+1 }
+1
+1 private PartitionManager getPartitionManager() {
+1 return configManager.getPartitionManager();
+1 }
+1
+1 private LoadManager getLoadManager() {
+1 return configManager.getLoadManager();
+1 }
+1}
+1
\ No newline at end of file
diff --git a/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/procedure/env/RegionMaintainHandler.java b/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/procedure/env/RegionMaintainHandler.java
index 928f3cfcd37..ebea1555bc1 100644
--- a/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/procedure/env/RegionMaintainHandler.java
+++ b/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/procedure/env/RegionMaintainHandler.java
@@ -1,587 +1,588 @@
-/*
- * 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.confignode.procedure.env;
-
-import org.apache.iotdb.common.rpc.thrift.TConsensusGroupId;
-import org.apache.iotdb.common.rpc.thrift.TConsensusGroupType;
-import org.apache.iotdb.common.rpc.thrift.TDataNodeConfiguration;
-import org.apache.iotdb.common.rpc.thrift.TDataNodeLocation;
-import org.apache.iotdb.common.rpc.thrift.TEndPoint;
-import org.apache.iotdb.common.rpc.thrift.TRegionMaintainTaskStatus;
-import org.apache.iotdb.common.rpc.thrift.TRegionMigrateFailedType;
-import org.apache.iotdb.common.rpc.thrift.TRegionReplicaSet;
-import org.apache.iotdb.common.rpc.thrift.TSStatus;
-import org.apache.iotdb.commons.client.ClientPoolFactory;
-import org.apache.iotdb.commons.client.IClientManager;
-import org.apache.iotdb.commons.client.sync.SyncDataNodeInternalServiceClient;
-import org.apache.iotdb.commons.cluster.NodeStatus;
-import org.apache.iotdb.commons.cluster.RegionStatus;
-import org.apache.iotdb.commons.utils.CommonDateTimeUtils;
-import org.apache.iotdb.confignode.client.async.CnToDnAsyncRequestType;
-import org.apache.iotdb.confignode.client.async.CnToDnInternalServiceAsyncRequestManager;
-import org.apache.iotdb.confignode.client.async.handlers.DataNodeAsyncRequestContext;
-import org.apache.iotdb.confignode.client.sync.CnToDnSyncRequestType;
-import org.apache.iotdb.confignode.client.sync.SyncDataNodeClientPool;
-import org.apache.iotdb.confignode.conf.ConfigNodeConfig;
-import org.apache.iotdb.confignode.conf.ConfigNodeDescriptor;
-import org.apache.iotdb.confignode.consensus.request.write.partition.AddRegionLocationPlan;
-import org.apache.iotdb.confignode.consensus.request.write.partition.RemoveRegionLocationPlan;
-import org.apache.iotdb.confignode.manager.ConfigManager;
-import org.apache.iotdb.confignode.manager.load.cache.consensus.ConsensusGroupHeartbeatSample;
-import org.apache.iotdb.confignode.procedure.exception.ProcedureException;
-import org.apache.iotdb.mpp.rpc.thrift.TCreatePeerReq;
-import org.apache.iotdb.mpp.rpc.thrift.TMaintainPeerReq;
-import org.apache.iotdb.mpp.rpc.thrift.TRegionLeaderChangeResp;
-import org.apache.iotdb.mpp.rpc.thrift.TRegionMigrateResult;
-import org.apache.iotdb.mpp.rpc.thrift.TResetPeerListReq;
-import org.apache.iotdb.rpc.TSStatusCode;
-
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-import java.util.Optional;
-import java.util.concurrent.TimeUnit;
-import java.util.stream.Collectors;
-
-import static org.apache.iotdb.confignode.conf.ConfigNodeConstant.REGION_MIGRATE_PROCESS;
-import static org.apache.iotdb.consensus.ConsensusFactory.IOT_CONSENSUS;
-import static org.apache.iotdb.consensus.ConsensusFactory.IOT_CONSENSUS_V2;
-import static org.apache.iotdb.consensus.ConsensusFactory.RATIS_CONSENSUS;
-
-public class RegionMaintainHandler {
-
- private static final Logger LOGGER = LoggerFactory.getLogger(RegionMaintainHandler.class);
-
- private static final ConfigNodeConfig CONF = ConfigNodeDescriptor.getInstance().getConf();
-
- private final ConfigManager configManager;
-
- private final IClientManager<TEndPoint, SyncDataNodeInternalServiceClient> dataNodeClientManager;
-
- public RegionMaintainHandler(ConfigManager configManager) {
- this.configManager = configManager;
- dataNodeClientManager =
- new IClientManager.Factory<TEndPoint, SyncDataNodeInternalServiceClient>()
- .createClientManager(
- new ClientPoolFactory.SyncDataNodeInternalServiceClientPoolFactory());
- }
-
- public static String getIdWithRpcEndpoint(TDataNodeLocation location) {
- return String.format(
- "[dataNodeId: %s, clientRpcEndPoint: %s]",
- location.getDataNodeId(), location.getClientRpcEndPoint());
- }
-
- public static String simplifiedLocation(TDataNodeLocation dataNodeLocation) {
- return dataNodeLocation.getDataNodeId() + "@" + dataNodeLocation.getInternalEndPoint().getIp();
- }
-
- /**
- * Find dest data node.
- *
- * @param regionId region id
- * @return dest data node location
- */
- public TDataNodeLocation findDestDataNode(TConsensusGroupId regionId) {
- TSStatus status;
- List<TDataNodeLocation> regionReplicaNodes = findRegionLocations(regionId);
- if (regionReplicaNodes.isEmpty()) {
- LOGGER.warn("Cannot find region replica nodes, region: {}", regionId);
- status = new TSStatus(TSStatusCode.MIGRATE_REGION_ERROR.getStatusCode());
- status.setMessage("Cannot find region replica nodes, region: " + regionId);
- return null;
- }
-
- Optional<TDataNodeLocation> newNode = pickNewReplicaNodeForRegion(regionReplicaNodes);
- if (!newNode.isPresent()) {
- LOGGER.warn("No enough Data node to migrate region: {}", regionId);
- return null;
- }
- return newNode.get();
- }
-
- /**
- * Create a new RegionReplica and build the ConsensusGroup on the destined DataNode
- *
- * <p>createNewRegionPeer should be invoked on a DataNode that doesn't contain any peer of the
- * specific ConsensusGroup, in order to avoid there exists one DataNode who has more than one
- * RegionReplica.
- *
- * @param regionId The given ConsensusGroup
- * @param destDataNode The destined DataNode where the new peer will be created
- * @return status
- */
- public TSStatus createNewRegionPeer(TConsensusGroupId regionId, TDataNodeLocation destDataNode) {
- TSStatus status;
- List<TDataNodeLocation> regionReplicaNodes = findRegionLocations(regionId);
- if (regionReplicaNodes.isEmpty()) {
- LOGGER.warn(
- "{}, Cannot find region replica nodes in createPeer, regionId: {}",
- REGION_MIGRATE_PROCESS,
- regionId);
- status = new TSStatus(TSStatusCode.MIGRATE_REGION_ERROR.getStatusCode());
- status.setMessage("Not find region replica nodes in createPeer, regionId: " + regionId);
- return status;
- }
-
- List<TDataNodeLocation> currentPeerNodes;
- if (TConsensusGroupType.DataRegion.equals(regionId.getType())
- && (IOT_CONSENSUS.equals(CONF.getDataRegionConsensusProtocolClass())
- || IOT_CONSENSUS_V2.equals(CONF.getDataRegionConsensusProtocolClass()))) {
- // parameter of createPeer for MultiLeader should be all peers
- currentPeerNodes = new ArrayList<>(regionReplicaNodes);
- currentPeerNodes.add(destDataNode);
- } else {
- // parameter of createPeer for Ratis can be empty
- currentPeerNodes = Collections.emptyList();
- }
-
- String database = configManager.getPartitionManager().getRegionDatabase(regionId);
- TCreatePeerReq req = new TCreatePeerReq(regionId, currentPeerNodes, database);
-
- status =
- (TSStatus)
- SyncDataNodeClientPool.getInstance()
- .sendSyncRequestToDataNodeWithRetry(
- destDataNode.getInternalEndPoint(),
- req,
- CnToDnSyncRequestType.CREATE_NEW_REGION_PEER);
-
- if (isSucceed(status)) {
- LOGGER.info(
- "{}, Send action createNewRegionPeer finished, regionId: {}, newPeerDataNodeId: {}",
- REGION_MIGRATE_PROCESS,
- regionId,
- getIdWithRpcEndpoint(destDataNode));
- } else {
- LOGGER.error(
- "{}, Send action createNewRegionPeer error, regionId: {}, newPeerDataNodeId: {}, result: {}",
- REGION_MIGRATE_PROCESS,
- regionId,
- getIdWithRpcEndpoint(destDataNode),
- status);
- }
-
- return status;
- }
-
- /**
- * Order the specific ConsensusGroup to add peer for the new RegionReplica.
- *
- * <p>The add peer interface could be invoked at any DataNode who contains one of the
- * RegionReplica of the specified ConsensusGroup except the new one
- *
- * @param destDataNode The DataNodeLocation where the new RegionReplica is created
- * @param regionId region id
- * @return TSStatus
- */
- public TSStatus submitAddRegionPeerTask(
- long procedureId,
- TDataNodeLocation destDataNode,
- TConsensusGroupId regionId,
- TDataNodeLocation coordinator) {
- TSStatus status;
-
- // Send addRegionPeer request to the selected DataNode,
- // destDataNode is where the new RegionReplica is created
- TMaintainPeerReq maintainPeerReq = new TMaintainPeerReq(regionId, destDataNode, procedureId);
- status =
- (TSStatus)
- SyncDataNodeClientPool.getInstance()
- .sendSyncRequestToDataNodeWithRetry(
- coordinator.getInternalEndPoint(),
- maintainPeerReq,
- CnToDnSyncRequestType.ADD_REGION_PEER);
- LOGGER.info(
- "{}, Send action addRegionPeer finished, regionId: {}, rpcDataNode: {}, destDataNode: {}, status: {}",
- REGION_MIGRATE_PROCESS,
- regionId,
- getIdWithRpcEndpoint(coordinator),
- getIdWithRpcEndpoint(destDataNode),
- status);
- return status;
- }
-
- /**
- * Order the specific ConsensusGroup to remove peer for the old RegionReplica.
- *
- * <p>The remove peer interface could be invoked at any DataNode who contains one of the
- * RegionReplica of the specified ConsensusGroup except the origin one
- *
- * @param originalDataNode The DataNodeLocation who contains the original RegionReplica
- * @param regionId region id
- * @return TSStatus
- */
- public TSStatus submitRemoveRegionPeerTask(
- long procedureId,
- TDataNodeLocation originalDataNode,
- TConsensusGroupId regionId,
- TDataNodeLocation coordinator) {
- TSStatus status;
-
- // Send removeRegionPeer request to the rpcClientDataNode
- TMaintainPeerReq maintainPeerReq =
- new TMaintainPeerReq(regionId, originalDataNode, procedureId);
- status =
- (TSStatus)
- SyncDataNodeClientPool.getInstance()
- .sendSyncRequestToDataNodeWithRetry(
- coordinator.getInternalEndPoint(),
- maintainPeerReq,
- CnToDnSyncRequestType.REMOVE_REGION_PEER);
- LOGGER.info(
- "{}, Send action removeRegionPeer finished, regionId: {}, rpcDataNode: {}",
- REGION_MIGRATE_PROCESS,
- regionId,
- getIdWithRpcEndpoint(coordinator));
- return status;
- }
-
- /**
- * Delete a Region peer in the given ConsensusGroup and all of its data on the specified DataNode
- *
- * <p>If the originalDataNode is down, we should delete local data and do other cleanup works
- * manually.
- *
- * @param originalDataNode The DataNodeLocation who contains the original RegionReplica
- * @param regionId region id
- * @return TSStatus
- */
- public TSStatus submitDeleteOldRegionPeerTask(
- long procedureId, TDataNodeLocation originalDataNode, TConsensusGroupId regionId) {
-
- TSStatus status;
- TMaintainPeerReq maintainPeerReq =
- new TMaintainPeerReq(regionId, originalDataNode, procedureId);
-
- status =
- configManager.getLoadManager().getNodeStatus(originalDataNode.getDataNodeId())
- == NodeStatus.Unknown
- ? (TSStatus)
- SyncDataNodeClientPool.getInstance()
- .sendSyncRequestToDataNodeWithGivenRetry(
- originalDataNode.getInternalEndPoint(),
- maintainPeerReq,
- CnToDnSyncRequestType.DELETE_OLD_REGION_PEER,
- 1)
- : (TSStatus)
- SyncDataNodeClientPool.getInstance()
- .sendSyncRequestToDataNodeWithRetry(
- originalDataNode.getInternalEndPoint(),
- maintainPeerReq,
- CnToDnSyncRequestType.DELETE_OLD_REGION_PEER);
- LOGGER.info(
- "{}, Send action deleteOldRegionPeer finished, regionId: {}, dataNodeId: {}",
- REGION_MIGRATE_PROCESS,
- regionId,
- originalDataNode.getInternalEndPoint());
- return status;
- }
-
- public Map<Integer, TSStatus> resetPeerList(
- TConsensusGroupId regionId,
- List<TDataNodeLocation> correctDataNodeLocations,
- Map<Integer, TDataNodeLocation> dataNodeLocationMap) {
- DataNodeAsyncRequestContext<TResetPeerListReq, TSStatus> clientHandler =
- new DataNodeAsyncRequestContext<>(
- CnToDnAsyncRequestType.RESET_PEER_LIST,
- new TResetPeerListReq(regionId, correctDataNodeLocations),
- dataNodeLocationMap);
- CnToDnInternalServiceAsyncRequestManager.getInstance().sendAsyncRequestWithRetry(clientHandler);
- return clientHandler.getResponseMap();
- }
-
- // TODO: will use 'procedure yield' to refactor later
- public TRegionMigrateResult waitTaskFinish(long taskId, TDataNodeLocation dataNodeLocation) {
- final long MAX_DISCONNECTION_TOLERATE_MS = 600_000;
- final long INITIAL_DISCONNECTION_TOLERATE_MS = 60_000;
- long startTime = System.nanoTime();
- long lastReportTime = System.nanoTime();
- while (true) {
- try (SyncDataNodeInternalServiceClient dataNodeClient =
- dataNodeClientManager.borrowClient(dataNodeLocation.getInternalEndPoint())) {
- TRegionMigrateResult report = dataNodeClient.getRegionMaintainResult(taskId);
- lastReportTime = System.nanoTime();
- if (report.getTaskStatus() != TRegionMaintainTaskStatus.PROCESSING) {
- return report;
- }
- } catch (Exception ignore) {
-
- }
- long waitTime =
- Math.min(
- INITIAL_DISCONNECTION_TOLERATE_MS
- + TimeUnit.NANOSECONDS.toMillis(lastReportTime - startTime) / 60,
- MAX_DISCONNECTION_TOLERATE_MS);
- long disconnectionTime = TimeUnit.NANOSECONDS.toMillis(System.nanoTime() - lastReportTime);
- if (disconnectionTime > waitTime) {
- LOGGER.warn(
- "{} task {} cannot get task report from DataNode {}, last report time is {} ago",
- REGION_MIGRATE_PROCESS,
- taskId,
- dataNodeLocation,
- CommonDateTimeUtils.convertMillisecondToDurationStr(
- TimeUnit.NANOSECONDS.toMillis(System.nanoTime() - lastReportTime)));
- TRegionMigrateResult report = new TRegionMigrateResult();
- report.setTaskStatus(TRegionMaintainTaskStatus.FAIL);
- report.setFailedNodeAndReason(new HashMap<>());
- report.getFailedNodeAndReason().put(dataNodeLocation, TRegionMigrateFailedType.Disconnect);
- return report;
- }
- try {
- TimeUnit.SECONDS.sleep(1);
- } catch (InterruptedException ignore) {
- Thread.currentThread().interrupt();
- return new TRegionMigrateResult(TRegionMaintainTaskStatus.PROCESSING);
- }
- }
- }
-
- public void addRegionLocation(TConsensusGroupId regionId, TDataNodeLocation newLocation) {
- AddRegionLocationPlan req = new AddRegionLocationPlan(regionId, newLocation);
- TSStatus status = configManager.getPartitionManager().addRegionLocation(req);
- LOGGER.info(
- "AddRegionLocation finished, add region {} to {}, result is {}",
- regionId,
- getIdWithRpcEndpoint(newLocation),
- status);
- configManager
- .getLoadManager()
- .getLoadCache()
- .createRegionCache(regionId, newLocation.getDataNodeId());
- }
-
- public void forceUpdateRegionCache(
- TConsensusGroupId regionId, TDataNodeLocation newLocation, RegionStatus regionStatus) {
- configManager
- .getLoadManager()
- .forceUpdateRegionCache(regionId, newLocation.getDataNodeId(), regionStatus);
- }
-
- public void removeRegionLocation(
- TConsensusGroupId regionId, TDataNodeLocation deprecatedLocation) {
- RemoveRegionLocationPlan req = new RemoveRegionLocationPlan(regionId, deprecatedLocation);
- TSStatus status = configManager.getPartitionManager().removeRegionLocation(req);
- LOGGER.info(
- "RemoveRegionLocation remove region {} from DataNode {}, result is {}",
- regionId,
- getIdWithRpcEndpoint(deprecatedLocation),
- status);
- configManager.getLoadManager().removeRegionCache(regionId, deprecatedLocation.getDataNodeId());
- configManager.getLoadManager().getRouteBalancer().balanceRegionLeaderAndPriority();
- }
-
- /**
- * Find all DataNodes which contains the given regionId
- *
- * @param regionId region id
- * @return DataNode locations
- */
- public List<TDataNodeLocation> findRegionLocations(TConsensusGroupId regionId) {
- Optional<TRegionReplicaSet> regionReplicaSet = getRegionReplicaSet(regionId);
- if (regionReplicaSet.isPresent()) {
- return regionReplicaSet.get().getDataNodeLocations();
- }
- return Collections.emptyList();
- }
-
- public Optional<TRegionReplicaSet> getRegionReplicaSet(TConsensusGroupId regionId) {
- return configManager.getPartitionManager().getAllReplicaSets().stream()
- .filter(rg -> rg.regionId.equals(regionId))
- .findAny();
- }
-
- public String getRegionReplicaSetString(TConsensusGroupId regionId) {
- Optional<TRegionReplicaSet> regionReplicaSet = getRegionReplicaSet(regionId);
- if (!regionReplicaSet.isPresent()) {
- return "UNKNOWN!";
- }
- StringBuilder result = new StringBuilder(regionReplicaSet.get().getRegionId() + ": {");
- for (TDataNodeLocation dataNodeLocation : regionReplicaSet.get().getDataNodeLocations()) {
- result.append(simplifiedLocation(dataNodeLocation)).append(", ");
- }
- result.append("}");
- return result.toString();
- }
-
- private Optional<TDataNodeLocation> pickNewReplicaNodeForRegion(
- List<TDataNodeLocation> regionReplicaNodes) {
- List<TDataNodeConfiguration> dataNodeConfigurations =
- configManager.getNodeManager().filterDataNodeThroughStatus(NodeStatus.Running);
- // Randomly selected to ensure a basic load balancing
- Collections.shuffle(dataNodeConfigurations);
- return dataNodeConfigurations.stream()
- .map(TDataNodeConfiguration::getLocation)
- .filter(e -> !regionReplicaNodes.contains(e))
- .findAny();
- }
-
- public boolean isSucceed(TSStatus status) {
- return status.getCode() == TSStatusCode.SUCCESS_STATUS.getStatusCode();
- }
-
- public boolean isFailed(TSStatus status) {
- return !isSucceed(status);
- }
-
- /**
- * Change the leader of given Region.
- *
- * <p>For IOT_CONSENSUS, using `changeLeaderForIoTConsensus` method to change the regionLeaderMap
- * maintained in ConfigNode.
- *
- * <p>For RATIS_CONSENSUS, invoking `changeRegionLeader` DataNode RPC method to change the leader.
- *
- * @param regionId The region to be migrated
- * @param originalDataNode The DataNode where the region locates
- */
- public void transferRegionLeader(
- TConsensusGroupId regionId, TDataNodeLocation originalDataNode, TDataNodeLocation coodinator)
- throws ProcedureException, InterruptedException {
- // find new leader
- Optional<TDataNodeLocation> newLeaderNode = Optional.empty();
- List<TDataNodeLocation> excludeDataNode = new ArrayList<>();
- excludeDataNode.add(originalDataNode);
- excludeDataNode.add(coodinator);
- newLeaderNode = filterDataNodeWithOtherRegionReplica(regionId, excludeDataNode);
- if (!newLeaderNode.isPresent()) {
- // If we have no choice, we use it
- newLeaderNode = Optional.of(coodinator);
- }
- // ratis needs DataNode to do election by itself
- long timestamp = System.nanoTime();
- if (TConsensusGroupType.SchemaRegion.equals(regionId.getType())
- || TConsensusGroupType.DataRegion.equals(regionId.getType())
- && RATIS_CONSENSUS.equals(CONF.getDataRegionConsensusProtocolClass())) {
- final int MAX_RETRY_TIME = 10;
- int retryTime = 0;
- long sleepTime =
- (CONF.getSchemaRegionRatisRpcLeaderElectionTimeoutMaxMs()
- + CONF.getSchemaRegionRatisRpcLeaderElectionTimeoutMinMs())
- / 2;
- Integer leaderId = configManager.getLoadManager().getRegionLeaderMap().get(regionId);
-
- if (leaderId != -1) {
- // The migrated node is not leader, so we don't need to transfer temporarily
- if (originalDataNode.getDataNodeId() != leaderId) {
- return;
- }
- }
- while (true) {
- TRegionLeaderChangeResp resp =
- SyncDataNodeClientPool.getInstance()
- .changeRegionLeader(
- regionId, originalDataNode.getInternalEndPoint(), newLeaderNode.get());
- if (resp.getStatus().getCode() == TSStatusCode.SUCCESS_STATUS.getStatusCode()) {
- timestamp = resp.getConsensusLogicalTimestamp();
- break;
- }
- if (retryTime++ > MAX_RETRY_TIME) {
- LOGGER.warn("[RemoveRegion] Ratis transfer leader fail, but procedure will continue.");
- return;
- }
- LOGGER.warn(
- "Call changeRegionLeader fail for the {} time, will sleep {} ms", retryTime, sleepTime);
- Thread.sleep(sleepTime);
- }
- }
-
- configManager
- .getLoadManager()
- .forceUpdateConsensusGroupCache(
- Collections.singletonMap(
- regionId,
- new ConsensusGroupHeartbeatSample(timestamp, newLeaderNode.get().getDataNodeId())));
- configManager.getLoadManager().getRouteBalancer().balanceRegionLeaderAndPriority();
-
- LOGGER.info(
- "{}, Change region leader finished, regionId: {}, newLeaderNode: {}",
- REGION_MIGRATE_PROCESS,
- regionId,
- newLeaderNode);
- }
-
- /**
- * Filter a DataNode who contains other RegionReplica excepts the given one.
- *
- * <p>Choose the RUNNING status datanode firstly, if no RUNNING status datanode match the
- * condition, then we choose the REMOVING status datanode.
- *
- * <p>`addRegionPeer`, `removeRegionPeer` and `changeRegionLeader` invoke this method.
- *
- * @param regionId The specific RegionId
- * @param filterLocation The DataNodeLocation that should be filtered
- * @return A DataNodeLocation that contains other RegionReplica and different from the
- * filterLocation
- */
- public Optional<TDataNodeLocation> filterDataNodeWithOtherRegionReplica(
- TConsensusGroupId regionId, TDataNodeLocation filterLocation) {
- List<TDataNodeLocation> filterLocations = Collections.singletonList(filterLocation);
- return filterDataNodeWithOtherRegionReplica(regionId, filterLocations);
- }
-
- public Optional<TDataNodeLocation> filterDataNodeWithOtherRegionReplica(
- TConsensusGroupId regionId, List<TDataNodeLocation> filterLocations) {
- return filterDataNodeWithOtherRegionReplica(
- regionId, filterLocations, NodeStatus.Running, NodeStatus.ReadOnly);
- }
-
- public Optional<TDataNodeLocation> filterDataNodeWithOtherRegionReplica(
- TConsensusGroupId regionId, TDataNodeLocation filterLocation, NodeStatus... allowingStatus) {
- List<TDataNodeLocation> excludeLocations = Collections.singletonList(filterLocation);
- return filterDataNodeWithOtherRegionReplica(regionId, excludeLocations, allowingStatus);
- }
-
- public Optional<TDataNodeLocation> filterDataNodeWithOtherRegionReplica(
- TConsensusGroupId regionId,
- List<TDataNodeLocation> excludeLocations,
- NodeStatus... allowingStatus) {
- List<TDataNodeLocation> regionLocations = findRegionLocations(regionId);
- if (regionLocations.isEmpty()) {
- LOGGER.warn("Cannot find DataNodes contain the given region: {}", regionId);
- return Optional.empty();
- }
-
- // Choosing the RUNNING DataNodes to execute firstly
- // If all DataNodes are not RUNNING, then choose the REMOVING DataNodes secondly
- List<TDataNodeLocation> aliveDataNodes =
- configManager.getNodeManager().filterDataNodeThroughStatus(allowingStatus).stream()
- .map(TDataNodeConfiguration::getLocation)
- .collect(Collectors.toList());
- final int leaderId = configManager.getLoadManager().getRegionLeaderMap().get(regionId);
- Collections.shuffle(aliveDataNodes);
- Optional<TDataNodeLocation> bestChoice = Optional.empty();
- for (TDataNodeLocation aliveDataNode : aliveDataNodes) {
- if (regionLocations.contains(aliveDataNode) && !excludeLocations.contains(aliveDataNode)) {
- if (leaderId == aliveDataNode.getDataNodeId()) {
- bestChoice = Optional.of(aliveDataNode);
- break;
- } else if (!bestChoice.isPresent()) {
- bestChoice = Optional.of(aliveDataNode);
- }
- }
- }
- return bestChoice;
- }
-}
+1/*
+1 * Licensed to the Apache Software Foundation (ASF) under one
+1 * or more contributor license agreements. See the NOTICE file
+1 * distributed with this work for additional information
+1 * regarding copyright ownership. The ASF licenses this file
+1 * to you under the Apache License, Version 2.0 (the
+1 * "License"); you may not use this file except in compliance
+1 * with the License. You may obtain a copy of the License at
+1 *
+1 * http://www.apache.org/licenses/LICENSE-2.0
+1 *
+1 * Unless required by applicable law or agreed to in writing,
+1 * software distributed under the License is distributed on an
+1 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+1 * KIND, either express or implied. See the License for the
+1 * specific language governing permissions and limitations
+1 * under the License.
+1 */
+1package org.apache.iotdb.confignode.procedure.env;
+1
+1import org.apache.iotdb.common.rpc.thrift.TConsensusGroupId;
+1import org.apache.iotdb.common.rpc.thrift.TConsensusGroupType;
+1import org.apache.iotdb.common.rpc.thrift.TDataNodeConfiguration;
+1import org.apache.iotdb.common.rpc.thrift.TDataNodeLocation;
+1import org.apache.iotdb.common.rpc.thrift.TEndPoint;
+1import org.apache.iotdb.common.rpc.thrift.TRegionMaintainTaskStatus;
+1import org.apache.iotdb.common.rpc.thrift.TRegionMigrateFailedType;
+1import org.apache.iotdb.common.rpc.thrift.TRegionReplicaSet;
+1import org.apache.iotdb.common.rpc.thrift.TSStatus;
+1import org.apache.iotdb.commons.client.ClientPoolFactory;
+1import org.apache.iotdb.commons.client.IClientManager;
+1import org.apache.iotdb.commons.client.sync.SyncDataNodeInternalServiceClient;
+1import org.apache.iotdb.commons.cluster.NodeStatus;
+1import org.apache.iotdb.commons.cluster.RegionStatus;
+1import org.apache.iotdb.commons.utils.CommonDateTimeUtils;
+1import org.apache.iotdb.confignode.client.async.CnToDnAsyncRequestType;
+1import org.apache.iotdb.confignode.client.async.CnToDnInternalServiceAsyncRequestManager;
+1import org.apache.iotdb.confignode.client.async.handlers.DataNodeAsyncRequestContext;
+1import org.apache.iotdb.confignode.client.sync.CnToDnSyncRequestType;
+1import org.apache.iotdb.confignode.client.sync.SyncDataNodeClientPool;
+1import org.apache.iotdb.confignode.conf.ConfigNodeConfig;
+1import org.apache.iotdb.confignode.conf.ConfigNodeDescriptor;
+1import org.apache.iotdb.confignode.consensus.request.write.partition.AddRegionLocationPlan;
+1import org.apache.iotdb.confignode.consensus.request.write.partition.RemoveRegionLocationPlan;
+1import org.apache.iotdb.confignode.manager.ConfigManager;
+1import org.apache.iotdb.confignode.manager.load.cache.consensus.ConsensusGroupHeartbeatSample;
+1import org.apache.iotdb.confignode.procedure.exception.ProcedureException;
+1import org.apache.iotdb.mpp.rpc.thrift.TCreatePeerReq;
+1import org.apache.iotdb.mpp.rpc.thrift.TMaintainPeerReq;
+1import org.apache.iotdb.mpp.rpc.thrift.TRegionLeaderChangeResp;
+1import org.apache.iotdb.mpp.rpc.thrift.TRegionMigrateResult;
+1import org.apache.iotdb.mpp.rpc.thrift.TResetPeerListReq;
+1import org.apache.iotdb.rpc.TSStatusCode;
+1
+1import org.slf4j.Logger;
+1import org.slf4j.LoggerFactory;
+1
+1import java.util.ArrayList;
+1import java.util.Collections;
+1import java.util.HashMap;
+1import java.util.List;
+1import java.util.Map;
+1import java.util.Optional;
+1import java.util.concurrent.TimeUnit;
+1import java.util.stream.Collectors;
+1
+1import static org.apache.iotdb.confignode.conf.ConfigNodeConstant.REGION_MIGRATE_PROCESS;
+1import static org.apache.iotdb.consensus.ConsensusFactory.IOT_CONSENSUS;
+1import static org.apache.iotdb.consensus.ConsensusFactory.IOT_CONSENSUS_V2;
+1import static org.apache.iotdb.consensus.ConsensusFactory.RATIS_CONSENSUS;
+1
+1public class RegionMaintainHandler {
+1
+1 private static final Logger LOGGER = LoggerFactory.getLogger(RegionMaintainHandler.class);
+1
+1 private static final ConfigNodeConfig CONF = ConfigNodeDescriptor.getInstance().getConf();
+1
+1 private final ConfigManager configManager;
+1
+1 private final IClientManager<TEndPoint, SyncDataNodeInternalServiceClient> dataNodeClientManager;
+1
+1 public RegionMaintainHandler(ConfigManager configManager) {
+1 this.configManager = configManager;
+1 dataNodeClientManager =
+1 new IClientManager.Factory<TEndPoint, SyncDataNodeInternalServiceClient>()
+1 .createClientManager(
+1 new ClientPoolFactory.SyncDataNodeInternalServiceClientPoolFactory());
+1 }
+1
+1 public static String getIdWithRpcEndpoint(TDataNodeLocation location) {
+1 return String.format(
+1 "[dataNodeId: %s, clientRpcEndPoint: %s]",
+1 location.getDataNodeId(), location.getClientRpcEndPoint());
+1 }
+1
+1 public static String simplifiedLocation(TDataNodeLocation dataNodeLocation) {
+1 return dataNodeLocation.getDataNodeId() + "@" + dataNodeLocation.getInternalEndPoint().getIp();
+1 }
+1
+1 /**
+1 * Find dest data node.
+1 *
+1 * @param regionId region id
+1 * @return dest data node location
+1 */
+1 public TDataNodeLocation findDestDataNode(TConsensusGroupId regionId) {
+1 TSStatus status;
+1 List<TDataNodeLocation> regionReplicaNodes = findRegionLocations(regionId);
+1 if (regionReplicaNodes.isEmpty()) {
+1 LOGGER.warn("Cannot find region replica nodes, region: {}", regionId);
+1 status = new TSStatus(TSStatusCode.MIGRATE_REGION_ERROR.getStatusCode());
+1 status.setMessage("Cannot find region replica nodes, region: " + regionId);
+1 return null;
+1 }
+1
+1 Optional<TDataNodeLocation> newNode = pickNewReplicaNodeForRegion(regionReplicaNodes);
+1 if (!newNode.isPresent()) {
+1 LOGGER.warn("No enough Data node to migrate region: {}", regionId);
+1 return null;
+1 }
+1 return newNode.get();
+1 }
+1
+1 /**
+1 * Create a new RegionReplica and build the ConsensusGroup on the destined DataNode
+1 *
+1 * <p>createNewRegionPeer should be invoked on a DataNode that doesn't contain any peer of the
+1 * specific ConsensusGroup, in order to avoid there exists one DataNode who has more than one
+1 * RegionReplica.
+1 *
+1 * @param regionId The given ConsensusGroup
+1 * @param destDataNode The destined DataNode where the new peer will be created
+1 * @return status
+1 */
+1 public TSStatus createNewRegionPeer(TConsensusGroupId regionId, TDataNodeLocation destDataNode) {
+1 TSStatus status;
+1 List<TDataNodeLocation> regionReplicaNodes = findRegionLocations(regionId);
+1 if (regionReplicaNodes.isEmpty()) {
+1 LOGGER.warn(
+1 "{}, Cannot find region replica nodes in createPeer, regionId: {}",
+1 REGION_MIGRATE_PROCESS,
+1 regionId);
+1 status = new TSStatus(TSStatusCode.MIGRATE_REGION_ERROR.getStatusCode());
+1 status.setMessage("Not find region replica nodes in createPeer, regionId: " + regionId);
+1 return status;
+1 }
+1
+1 List<TDataNodeLocation> currentPeerNodes;
+1 if (TConsensusGroupType.DataRegion.equals(regionId.getType())
+1 && (IOT_CONSENSUS.equals(CONF.getDataRegionConsensusProtocolClass())
+1 || IOT_CONSENSUS_V2.equals(CONF.getDataRegionConsensusProtocolClass()))) {
+1 // parameter of createPeer for MultiLeader should be all peers
+1 currentPeerNodes = new ArrayList<>(regionReplicaNodes);
+1 currentPeerNodes.add(destDataNode);
+1 } else {
+1 // parameter of createPeer for Ratis can be empty
+1 currentPeerNodes = Collections.emptyList();
+1 }
+1
+1 String database = configManager.getPartitionManager().getRegionDatabase(regionId);
+1 TCreatePeerReq req = new TCreatePeerReq(regionId, currentPeerNodes, database);
+1
+1 status =
+1 (TSStatus)
+1 SyncDataNodeClientPool.getInstance()
+1 .sendSyncRequestToDataNodeWithRetry(
+1 destDataNode.getInternalEndPoint(),
+1 req,
+1 CnToDnSyncRequestType.CREATE_NEW_REGION_PEER);
+1
+1 if (isSucceed(status)) {
+1 LOGGER.info(
+1 "{}, Send action createNewRegionPeer finished, regionId: {}, newPeerDataNodeId: {}",
+1 REGION_MIGRATE_PROCESS,
+1 regionId,
+1 getIdWithRpcEndpoint(destDataNode));
+1 } else {
+1 LOGGER.error(
+1 "{}, Send action createNewRegionPeer error, regionId: {}, newPeerDataNodeId: {}, result: {}",
+1 REGION_MIGRATE_PROCESS,
+1 regionId,
+1 getIdWithRpcEndpoint(destDataNode),
+1 status);
+1 }
+1
+1 return status;
+1 }
+1
+1 /**
+1 * Order the specific ConsensusGroup to add peer for the new RegionReplica.
+1 *
+1 * <p>The add peer interface could be invoked at any DataNode who contains one of the
+1 * RegionReplica of the specified ConsensusGroup except the new one
+1 *
+1 * @param destDataNode The DataNodeLocation where the new RegionReplica is created
+1 * @param regionId region id
+1 * @return TSStatus
+1 */
+1 public TSStatus submitAddRegionPeerTask(
+1 long procedureId,
+1 TDataNodeLocation destDataNode,
+1 TConsensusGroupId regionId,
+1 TDataNodeLocation coordinator) {
+1 TSStatus status;
+1
+1 // Send addRegionPeer request to the selected DataNode,
+1 // destDataNode is where the new RegionReplica is created
+1 TMaintainPeerReq maintainPeerReq = new TMaintainPeerReq(regionId, destDataNode, procedureId);
+1 status =
+1 (TSStatus)
+1 SyncDataNodeClientPool.getInstance()
+1 .sendSyncRequestToDataNodeWithRetry(
+1 coordinator.getInternalEndPoint(),
+1 maintainPeerReq,
+1 CnToDnSyncRequestType.ADD_REGION_PEER);
+1 LOGGER.info(
+1 "{}, Send action addRegionPeer finished, regionId: {}, rpcDataNode: {}, destDataNode: {}, status: {}",
+1 REGION_MIGRATE_PROCESS,
+1 regionId,
+1 getIdWithRpcEndpoint(coordinator),
+1 getIdWithRpcEndpoint(destDataNode),
+1 status);
+1 return status;
+1 }
+1
+1 /**
+1 * Order the specific ConsensusGroup to remove peer for the old RegionReplica.
+1 *
+1 * <p>The remove peer interface could be invoked at any DataNode who contains one of the
+1 * RegionReplica of the specified ConsensusGroup except the origin one
+1 *
+1 * @param originalDataNode The DataNodeLocation who contains the original RegionReplica
+1 * @param regionId region id
+1 * @return TSStatus
+1 */
+1 public TSStatus submitRemoveRegionPeerTask(
+1 long procedureId,
+1 TDataNodeLocation originalDataNode,
+1 TConsensusGroupId regionId,
+1 TDataNodeLocation coordinator) {
+1 TSStatus status;
+1
+1 // Send removeRegionPeer request to the rpcClientDataNode
+1 TMaintainPeerReq maintainPeerReq =
+1 new TMaintainPeerReq(regionId, originalDataNode, procedureId);
+1 status =
+1 (TSStatus)
+1 SyncDataNodeClientPool.getInstance()
+1 .sendSyncRequestToDataNodeWithRetry(
+1 coordinator.getInternalEndPoint(),
+1 maintainPeerReq,
+1 CnToDnSyncRequestType.REMOVE_REGION_PEER);
+1 LOGGER.info(
+1 "{}, Send action removeRegionPeer finished, regionId: {}, rpcDataNode: {}",
+1 REGION_MIGRATE_PROCESS,
+1 regionId,
+1 getIdWithRpcEndpoint(coordinator));
+1 return status;
+1 }
+1
+1 /**
+1 * Delete a Region peer in the given ConsensusGroup and all of its data on the specified DataNode
+1 *
+1 * <p>If the originalDataNode is down, we should delete local data and do other cleanup works
+1 * manually.
+1 *
+1 * @param originalDataNode The DataNodeLocation who contains the original RegionReplica
+1 * @param regionId region id
+1 * @return TSStatus
+1 */
+1 public TSStatus submitDeleteOldRegionPeerTask(
+1 long procedureId, TDataNodeLocation originalDataNode, TConsensusGroupId regionId) {
+1
+1 TSStatus status;
+1 TMaintainPeerReq maintainPeerReq =
+1 new TMaintainPeerReq(regionId, originalDataNode, procedureId);
+1
+1 status =
+1 configManager.getLoadManager().getNodeStatus(originalDataNode.getDataNodeId())
+1 == NodeStatus.Unknown
+1 ? (TSStatus)
+1 SyncDataNodeClientPool.getInstance()
+1 .sendSyncRequestToDataNodeWithGivenRetry(
+1 originalDataNode.getInternalEndPoint(),
+1 maintainPeerReq,
+1 CnToDnSyncRequestType.DELETE_OLD_REGION_PEER,
+1 1)
+1 : (TSStatus)
+1 SyncDataNodeClientPool.getInstance()
+1 .sendSyncRequestToDataNodeWithRetry(
+1 originalDataNode.getInternalEndPoint(),
+1 maintainPeerReq,
+1 CnToDnSyncRequestType.DELETE_OLD_REGION_PEER);
+1 LOGGER.info(
+1 "{}, Send action deleteOldRegionPeer finished, regionId: {}, dataNodeId: {}",
+1 REGION_MIGRATE_PROCESS,
+1 regionId,
+1 originalDataNode.getInternalEndPoint());
+1 return status;
+1 }
+1
+1 public Map<Integer, TSStatus> resetPeerList(
+1 TConsensusGroupId regionId,
+1 List<TDataNodeLocation> correctDataNodeLocations,
+1 Map<Integer, TDataNodeLocation> dataNodeLocationMap) {
+1 DataNodeAsyncRequestContext<TResetPeerListReq, TSStatus> clientHandler =
+1 new DataNodeAsyncRequestContext<>(
+1 CnToDnAsyncRequestType.RESET_PEER_LIST,
+1 new TResetPeerListReq(regionId, correctDataNodeLocations),
+1 dataNodeLocationMap);
+1 CnToDnInternalServiceAsyncRequestManager.getInstance().sendAsyncRequestWithRetry(clientHandler);
+1 return clientHandler.getResponseMap();
+1 }
+1
+1 // TODO: will use 'procedure yield' to refactor later
+1 public TRegionMigrateResult waitTaskFinish(long taskId, TDataNodeLocation dataNodeLocation) {
+1 final long MAX_DISCONNECTION_TOLERATE_MS = 600_000;
+1 final long INITIAL_DISCONNECTION_TOLERATE_MS = 60_000;
+1 long startTime = System.nanoTime();
+1 long lastReportTime = System.nanoTime();
+1 while (true) {
+1 try (SyncDataNodeInternalServiceClient dataNodeClient =
+1 dataNodeClientManager.borrowClient(dataNodeLocation.getInternalEndPoint())) {
+1 TRegionMigrateResult report = dataNodeClient.getRegionMaintainResult(taskId);
+1 lastReportTime = System.nanoTime();
+1 if (report.getTaskStatus() != TRegionMaintainTaskStatus.PROCESSING) {
+1 return report;
+1 }
+1 } catch (Exception ignore) {
+1
+1 }
+1 long waitTime =
+1 Math.min(
+1 INITIAL_DISCONNECTION_TOLERATE_MS
+1 + TimeUnit.NANOSECONDS.toMillis(lastReportTime - startTime) / 60,
+1 MAX_DISCONNECTION_TOLERATE_MS);
+1 long disconnectionTime = TimeUnit.NANOSECONDS.toMillis(System.nanoTime() - lastReportTime);
+1 if (disconnectionTime > waitTime) {
+1 LOGGER.warn(
+1 "{} task {} cannot get task report from DataNode {}, last report time is {} ago",
+1 REGION_MIGRATE_PROCESS,
+1 taskId,
+1 dataNodeLocation,
+1 CommonDateTimeUtils.convertMillisecondToDurationStr(
+1 TimeUnit.NANOSECONDS.toMillis(System.nanoTime() - lastReportTime)));
+1 TRegionMigrateResult report = new TRegionMigrateResult();
+1 report.setTaskStatus(TRegionMaintainTaskStatus.FAIL);
+1 report.setFailedNodeAndReason(new HashMap<>());
+1 report.getFailedNodeAndReason().put(dataNodeLocation, TRegionMigrateFailedType.Disconnect);
+1 return report;
+1 }
+1 try {
+1 TimeUnit.SECONDS.sleep(1);
+1 } catch (InterruptedException ignore) {
+1 Thread.currentThread().interrupt();
+1 return new TRegionMigrateResult(TRegionMaintainTaskStatus.PROCESSING);
+1 }
+1 }
+1 }
+1
+1 public void addRegionLocation(TConsensusGroupId regionId, TDataNodeLocation newLocation) {
+1 AddRegionLocationPlan req = new AddRegionLocationPlan(regionId, newLocation);
+1 TSStatus status = configManager.getPartitionManager().addRegionLocation(req);
+1 LOGGER.info(
+1 "AddRegionLocation finished, add region {} to {}, result is {}",
+1 regionId,
+1 getIdWithRpcEndpoint(newLocation),
+1 status);
... 4877 lines suppressed ...