From 904a3060946a9a6a64c709115b4c1081ff222f3c Mon Sep 17 00:00:00 2001 From: deardeng Date: Tue, 31 Dec 2024 19:36:46 +0800 Subject: [PATCH] [feature](cloud) Support rename compute group sql --- .../meta-service/meta_service_resource.cpp | 9 ++- .../src/resource-manager/resource_manager.cpp | 28 ++++++- cloud/src/resource-manager/resource_manager.h | 4 +- .../org/apache/doris/nereids/DorisParser.g4 | 1 + .../cloud/catalog/CloudClusterChecker.java | 18 ++++- .../apache/doris/cloud/catalog/CloudEnv.java | 4 + .../cloud/system/CloudSystemInfoService.java | 42 +++++++++- .../nereids/parser/LogicalPlanBuilder.java | 7 ++ .../doris/nereids/trees/plans/PlanType.java | 3 +- .../AlterSystemRenameComputeGroupCommand.java | 76 +++++++++++++++++++ gensrc/proto/cloud.proto | 2 + 11 files changed, 185 insertions(+), 9 deletions(-) create mode 100644 fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/commands/AlterSystemRenameComputeGroupCommand.java diff --git a/cloud/src/meta-service/meta_service_resource.cpp b/cloud/src/meta-service/meta_service_resource.cpp index d873dec7b21070..10067bfa129b55 100644 --- a/cloud/src/meta-service/meta_service_resource.cpp +++ b/cloud/src/meta-service/meta_service_resource.cpp @@ -2203,6 +2203,11 @@ void MetaServiceImpl::alter_cluster(google::protobuf::RpcController* controller, } } break; case AlterClusterRequest::RENAME_CLUSTER: { + // SQL mode, cluster cluster name eq empty cluster name, need drop empty cluster first. + // but in http api, cloud control will drop empty cluster + bool drop_empty_cluster = + request->has_drop_empty_cluster() ? request->drop_empty_cluster() : false; + msg = resource_mgr_->update_cluster( instance_id, cluster, [&](const ClusterPB& i) { return i.cluster_id() == cluster.cluster.cluster_id(); }, @@ -2212,7 +2217,7 @@ void MetaServiceImpl::alter_cluster(google::protobuf::RpcController* controller, LOG(INFO) << "cluster.cluster.cluster_name(): " << cluster.cluster.cluster_name(); for (auto itt : cluster_names) { - LOG(INFO) << "itt : " << itt; + LOG(INFO) << "instance's cluster name : " << itt; } if (it != cluster_names.end()) { code = MetaServiceCode::INVALID_ARGUMENT; @@ -2232,7 +2237,7 @@ void MetaServiceImpl::alter_cluster(google::protobuf::RpcController* controller, } c.set_cluster_name(cluster.cluster.cluster_name()); return msg; - }); + }, drop_empty_cluster); } break; case AlterClusterRequest::UPDATE_CLUSTER_ENDPOINT: { msg = resource_mgr_->update_cluster( diff --git a/cloud/src/resource-manager/resource_manager.cpp b/cloud/src/resource-manager/resource_manager.cpp index 9c37d781765510..da40c16653b26a 100644 --- a/cloud/src/resource-manager/resource_manager.cpp +++ b/cloud/src/resource-manager/resource_manager.cpp @@ -455,7 +455,8 @@ std::pair ResourceManager::drop_cluster( std::string ResourceManager::update_cluster( const std::string& instance_id, const ClusterInfo& cluster, std::function filter, - std::function& cluster_names)> action) { + std::function& cluster_names)> action, + bool drop_empty_cluster) { std::stringstream ss; std::string msg; @@ -521,6 +522,31 @@ std::string ResourceManager::update_cluster( auto& clusters = const_cast&>(instance.clusters()); + // check cluster_name is empty cluster, if empty and drop_empty_cluster == true, drop it + if (drop_empty_cluster) { + auto it = cluster_names.find(cluster_name); + if (it != cluster_names.end()) { + // found it, if it's an empty cluster, drop it from instance + int idx = -1; + for (auto& cluster : instance.clusters()) { + idx++; + if (cluster.cluster_name() == cluster_name) { + // Check if cluster is empty (has no nodes) + if (cluster.nodes_size() == 0) { + // Remove empty cluster from instance + auto& clusters = const_cast&>( + instance.clusters()); + clusters.DeleteSubrange(idx, 1); + // Remove cluster name from set + cluster_names.erase(cluster_name); + LOG(INFO) << "Removed empty cluster, cluster_name=" << cluster_name; + } + break; + } + } + } + } + // do update ClusterPB original = clusters[idx]; msg = action(clusters[idx], cluster_names); diff --git a/cloud/src/resource-manager/resource_manager.h b/cloud/src/resource-manager/resource_manager.h index 9e6f4548d244b7..2b83a85628a5a1 100644 --- a/cloud/src/resource-manager/resource_manager.h +++ b/cloud/src/resource-manager/resource_manager.h @@ -88,13 +88,15 @@ class ResourceManager { * * @param cluster cluster to update, only cluster name and cluster id are concered * @param action update operation code snippet + * @param drop_empty_cluster, find cluster.cluster_name is a empty cluster(no node), drop it * @filter filter condition * @return empty string for success, otherwise failure reason returned */ virtual std::string update_cluster( const std::string& instance_id, const ClusterInfo& cluster, std::function filter, - std::function& cluster_names)> action); + std::function& cluster_names)> action, + bool drop_empty_cluster = false); /** * Get instance from underlying storage with given transaction. diff --git a/fe/fe-core/src/main/antlr4/org/apache/doris/nereids/DorisParser.g4 b/fe/fe-core/src/main/antlr4/org/apache/doris/nereids/DorisParser.g4 index 6eedeaad211987..4920dd44e0d72b 100644 --- a/fe/fe-core/src/main/antlr4/org/apache/doris/nereids/DorisParser.g4 +++ b/fe/fe-core/src/main/antlr4/org/apache/doris/nereids/DorisParser.g4 @@ -221,6 +221,7 @@ supportedAlterStatement dropRollupClause (COMMA dropRollupClause)* #alterTableDropRollup | ALTER TABLE name=multipartIdentifier SET LEFT_PAREN propertyItemList RIGHT_PAREN #alterTableProperties + | ALTER SYSTEM RENAME COMPUTE GROUP name=identifier newName=identifier #alterSystemRenameComputeGroup ; supportedDropStatement diff --git a/fe/fe-core/src/main/java/org/apache/doris/cloud/catalog/CloudClusterChecker.java b/fe/fe-core/src/main/java/org/apache/doris/cloud/catalog/CloudClusterChecker.java index 9468c8acecd032..b6756fb5cdf361 100644 --- a/fe/fe-core/src/main/java/org/apache/doris/cloud/catalog/CloudClusterChecker.java +++ b/fe/fe-core/src/main/java/org/apache/doris/cloud/catalog/CloudClusterChecker.java @@ -52,6 +52,8 @@ public class CloudClusterChecker extends MasterDaemon { private CloudSystemInfoService cloudSystemInfoService; + private final Object checkLock = new Object(); + boolean isUpdateCloudUniqueId = false; public CloudClusterChecker(CloudSystemInfoService cloudSystemInfoService) { @@ -321,9 +323,11 @@ private void checkDiffNode(Map remoteClusterIdToPB, @Override protected void runAfterCatalogReady() { - checkCloudBackends(); - updateCloudMetrics(); - checkCloudFes(); + synchronized (checkLock) { + checkCloudBackends(); + updateCloudMetrics(); + checkCloudFes(); + } } private void checkFeNodesMapValid() { @@ -545,4 +549,12 @@ private void updateCloudMetrics() { MetricRepo.updateClusterBackendAliveTotal(entry.getKey(), entry.getValue(), aliveNum); } } + + public void checkNow() { + if (Env.getCurrentEnv().isMaster()) { + synchronized (checkLock) { + runAfterCatalogReady(); + } + } + } } diff --git a/fe/fe-core/src/main/java/org/apache/doris/cloud/catalog/CloudEnv.java b/fe/fe-core/src/main/java/org/apache/doris/cloud/catalog/CloudEnv.java index 89338c228fc0b6..e5bd175eca9abf 100644 --- a/fe/fe-core/src/main/java/org/apache/doris/cloud/catalog/CloudEnv.java +++ b/fe/fe-core/src/main/java/org/apache/doris/cloud/catalog/CloudEnv.java @@ -94,6 +94,10 @@ public CloudUpgradeMgr getCloudUpgradeMgr() { return this.upgradeMgr; } + public CloudClusterChecker getCloudClusterChecker() { + return this.cloudClusterCheck; + } + public String getCloudInstanceId() { return cloudInstanceId; } diff --git a/fe/fe-core/src/main/java/org/apache/doris/cloud/system/CloudSystemInfoService.java b/fe/fe-core/src/main/java/org/apache/doris/cloud/system/CloudSystemInfoService.java index 36ca260dc17f72..6236166f0f7487 100644 --- a/fe/fe-core/src/main/java/org/apache/doris/cloud/system/CloudSystemInfoService.java +++ b/fe/fe-core/src/main/java/org/apache/doris/cloud/system/CloudSystemInfoService.java @@ -885,7 +885,7 @@ public void dropFrontend(Frontend frontend) throws DdlException { private String tryCreateComputeGroup(String clusterName, String computeGroupId) throws UserException { if (Strings.isNullOrEmpty(((CloudEnv) Env.getCurrentEnv()).getCloudInstanceId())) { - throw new DdlException("unable to create compute group due to empty cluster_id"); + throw new DdlException("unable to create compute group due to empty cloud_instance_id"); } Cloud.ClusterPB clusterPB = Cloud.ClusterPB.newBuilder() @@ -1158,4 +1158,44 @@ public String getInstanceId(String cloudUniqueId) throws IOException { throw new IOException("Failed to get instance info"); } } + + public void renameComputeGroup(String originalName, String newGroupName) throws UserException { + String cloudInstanceId = ((CloudEnv) Env.getCurrentEnv()).getCloudInstanceId(); + if (Strings.isNullOrEmpty(cloudInstanceId)) { + throw new DdlException("unable to rename compute group due to empty cloud_instance_id"); + } + String originalComputeGroupId = ((CloudSystemInfoService) Env.getCurrentSystemInfo()) + .getCloudClusterIdByName(originalName); + if (Strings.isNullOrEmpty(originalComputeGroupId)) { + throw new DdlException("unable to rename compute group, " + + "cant get compute group id by compute name " + originalName); + } + + Cloud.ClusterPB clusterPB = Cloud.ClusterPB.newBuilder() + .setClusterId(originalComputeGroupId) + .setClusterName(newGroupName) + .setType(Cloud.ClusterPB.Type.COMPUTE) + .build(); + + Cloud.AlterClusterRequest request = Cloud.AlterClusterRequest.newBuilder() + .setInstanceId(((CloudEnv) Env.getCurrentEnv()).getCloudInstanceId()) + .setOp(Cloud.AlterClusterRequest.Operation.RENAME_CLUSTER) + .setDropEmptyCluster(true) + .setCluster(clusterPB) + .build(); + + + Cloud.AlterClusterResponse response; + try { + response = MetaServiceProxy.getInstance().alterCluster(request); + LOG.info("alter rename compute group, request: {}, response: {}", request, response); + if (response.getStatus().getCode() != Cloud.MetaServiceCode.OK) { + LOG.warn("alter rename compute group not ok, response: {}", response); + throw new UserException("failed to rename compute group errorCode: " + response.getStatus().getCode() + + " msg: " + response.getStatus().getMsg()); + } + } catch (RpcException e) { + throw new UserException("failed to alter rename compute group", e); + } + } } diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/parser/LogicalPlanBuilder.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/parser/LogicalPlanBuilder.java index 4341aafefafd82..bc68cc7e945505 100644 --- a/fe/fe-core/src/main/java/org/apache/doris/nereids/parser/LogicalPlanBuilder.java +++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/parser/LogicalPlanBuilder.java @@ -75,6 +75,7 @@ import org.apache.doris.nereids.DorisParser.AlterRoleContext; import org.apache.doris.nereids.DorisParser.AlterSqlBlockRuleContext; import org.apache.doris.nereids.DorisParser.AlterStorageVaultContext; +import org.apache.doris.nereids.DorisParser.AlterSystemRenameComputeGroupContext; import org.apache.doris.nereids.DorisParser.AlterTableAddRollupContext; import org.apache.doris.nereids.DorisParser.AlterTableClauseContext; import org.apache.doris.nereids.DorisParser.AlterTableContext; @@ -487,6 +488,7 @@ import org.apache.doris.nereids.trees.plans.commands.AlterRoleCommand; import org.apache.doris.nereids.trees.plans.commands.AlterSqlBlockRuleCommand; import org.apache.doris.nereids.trees.plans.commands.AlterStorageVaultCommand; +import org.apache.doris.nereids.trees.plans.commands.AlterSystemRenameComputeGroupCommand; import org.apache.doris.nereids.trees.plans.commands.AlterTableCommand; import org.apache.doris.nereids.trees.plans.commands.AlterViewCommand; import org.apache.doris.nereids.trees.plans.commands.AlterWorkloadGroupCommand; @@ -1284,6 +1286,11 @@ public LogicalPlan visitAlterStorageVault(AlterStorageVaultContext ctx) { return new AlterStorageVaultCommand(vaultName, properties); } + @Override + public LogicalPlan visitAlterSystemRenameComputeGroup(AlterSystemRenameComputeGroupContext ctx) { + return new AlterSystemRenameComputeGroupCommand(ctx.name.getText(), ctx.newName.getText()); + } + @Override public LogicalPlan visitShowConstraint(ShowConstraintContext ctx) { List parts = visitMultipartIdentifier(ctx.table); diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/PlanType.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/PlanType.java index c71a2c8b442fcf..0eaf943b527830 100644 --- a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/PlanType.java +++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/PlanType.java @@ -272,5 +272,6 @@ public enum PlanType { SHOW_QUERY_PROFILE_COMMAND, SWITCH_COMMAND, HELP_COMMAND, - USE_COMMAND + USE_COMMAND, + ALTER_SYSTEM_RENAME_COMPUTE_GROUP } diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/commands/AlterSystemRenameComputeGroupCommand.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/commands/AlterSystemRenameComputeGroupCommand.java new file mode 100644 index 00000000000000..eff040bb58d62b --- /dev/null +++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/commands/AlterSystemRenameComputeGroupCommand.java @@ -0,0 +1,76 @@ +// 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.doris.nereids.trees.plans.commands; + +import org.apache.doris.catalog.Env; +import org.apache.doris.cloud.catalog.CloudEnv; +import org.apache.doris.cloud.system.CloudSystemInfoService; +import org.apache.doris.common.AnalysisException; +import org.apache.doris.common.DdlException; +import org.apache.doris.nereids.trees.plans.PlanType; +import org.apache.doris.nereids.trees.plans.visitor.PlanVisitor; +import org.apache.doris.qe.ConnectContext; +import org.apache.doris.qe.StmtExecutor; + +import com.google.common.base.Strings; + +/** + * Alter System Rename Compute Group + */ +public class AlterSystemRenameComputeGroupCommand extends Command implements ForwardWithSync { + private final String originalName; + private final String newName; + + public AlterSystemRenameComputeGroupCommand(String originalName, String newName) { + super(PlanType.ALTER_SYSTEM_RENAME_COMPUTE_GROUP); + this.originalName = originalName; + this.newName = newName; + } + + private void validate() throws AnalysisException { + if (Strings.isNullOrEmpty(originalName) || Strings.isNullOrEmpty(newName)) { + throw new AnalysisException("rename group requires non-empty or non-empty name"); + } + if (originalName.equals(newName)) { + throw new AnalysisException("rename compute group original name eq new name"); + } + } + + @Override + public void run(ConnectContext ctx, StmtExecutor executor) throws Exception { + validate(); + doRun(ctx); + } + + @Override + public R accept(PlanVisitor visitor, C context) { + return visitor.visitCommand(this, context); + } + + private void doRun(ConnectContext ctx) throws Exception { + try { + // 1. send rename rpc to ms + ((CloudSystemInfoService) Env.getCurrentSystemInfo()).renameComputeGroup(this.originalName, this.newName); + // 2. if 1 not throw exception, refresh cloud cluster + // if not do 2, will wait 10s to get new name + ((CloudEnv) Env.getCurrentEnv()).getCloudClusterChecker().checkNow(); + } catch (Exception e) { + throw new DdlException(e.getMessage()); + } + } +} diff --git a/gensrc/proto/cloud.proto b/gensrc/proto/cloud.proto index 4e00faa0c6f4eb..68c1a0eb9484bc 100644 --- a/gensrc/proto/cloud.proto +++ b/gensrc/proto/cloud.proto @@ -1095,6 +1095,8 @@ message AlterClusterRequest { optional string cloud_unique_id = 2; // For auth optional ClusterPB cluster = 3; optional Operation op = 4; + // for SQL mode rename cluster, rename to cluster name eq instance empty cluster name, need drop empty cluster + optional bool drop_empty_cluster = 5 [default = false]; } message AlterClusterResponse {