From 4d61221eb60441bdce9601130f1b65fd8aa8365c Mon Sep 17 00:00:00 2001 From: Weihao Li <18110526956@163.com> Date: Tue, 23 Dec 2025 17:27:05 +0800 Subject: [PATCH 1/6] add api Signed-off-by: Weihao Li <18110526956@163.com> --- iotdb-api/external-service-api/pom.xml | 60 +++++++++++++++++++ .../externalservice/api/IExternalService.java | 33 ++++++++++ iotdb-api/pom.xml | 1 + 3 files changed, 94 insertions(+) create mode 100644 iotdb-api/external-service-api/pom.xml create mode 100644 iotdb-api/external-service-api/src/main/java/org/apache/iotdb/externalservice/api/IExternalService.java diff --git a/iotdb-api/external-service-api/pom.xml b/iotdb-api/external-service-api/pom.xml new file mode 100644 index 000000000000..adbb6e6ffb00 --- /dev/null +++ b/iotdb-api/external-service-api/pom.xml @@ -0,0 +1,60 @@ + + + + 4.0.0 + + org.apache.iotdb + iotdb-api + 2.0.7-SNAPSHOT + + external-service-api + IoTDB: API: External Service API + + + get-jar-with-dependencies + + + + org.apache.maven.plugins + maven-assembly-plugin + + + jar-with-dependencies + + + + + make-assembly + + + single + + + package + + + + + + + + diff --git a/iotdb-api/external-service-api/src/main/java/org/apache/iotdb/externalservice/api/IExternalService.java b/iotdb-api/external-service-api/src/main/java/org/apache/iotdb/externalservice/api/IExternalService.java new file mode 100644 index 000000000000..1ae5f30e7a9d --- /dev/null +++ b/iotdb-api/external-service-api/src/main/java/org/apache/iotdb/externalservice/api/IExternalService.java @@ -0,0 +1,33 @@ +/* + * 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.externalservice.api; + +/** An interface to support user-defined service. */ +public interface IExternalService { + + /** Start current service. */ + void start(); + + /** + * Stop current service. If current service uses thread or thread pool, current service should + * guarantee to putBack thread or thread pool. + */ + void stop(); +} diff --git a/iotdb-api/pom.xml b/iotdb-api/pom.xml index 56c3efcd6f08..d724f9d11a97 100644 --- a/iotdb-api/pom.xml +++ b/iotdb-api/pom.xml @@ -31,6 +31,7 @@ IoTDB: API external-api + external-service-api pipe-api trigger-api udf-api From 8f939840ab37fbd0bdbf3acb23f66d462219ce04 Mon Sep 17 00:00:00 2001 From: Weihao Li <18110526956@163.com> Date: Tue, 6 Jan 2026 17:16:26 +0800 Subject: [PATCH 2/6] add process in CN: ExternalServiceInfo Signed-off-by: Weihao Li <18110526956@163.com> --- .../org/apache/iotdb/rpc/TSStatusCode.java | 5 + .../consensus/request/ConfigPhysicalPlan.java | 14 + .../request/ConfigPhysicalPlanType.java | 6 + .../ShowExternalServicePlan.java | 60 ++++ .../CreateExternalServicePlan.java | 89 ++++++ .../DropExternalServicePlan.java | 87 ++++++ .../StartExternalServicePlan.java | 87 ++++++ .../StopExternalServicePlan.java | 87 ++++++ .../ShowExternalServiceResp.java | 56 ++++ .../confignode/manager/ConfigManager.java | 47 +++ .../iotdb/confignode/manager/IManager.java | 12 + .../externalservice/ExternalServiceInfo.java | 126 ++++++++ .../ExternalServiceManager.java | 143 +++++++++ .../confignode1conf/iotdb-system.properties | 4 +- iotdb-core/datanode/pom.xml | 5 + .../org/apache/iotdb/db/conf/IoTDBConfig.java | 9 + .../org/apache/iotdb/db/service/DataNode.java | 1 + .../BuiltinExternalServices.java | 58 ++++ .../ExternalServiceClassLoader.java | 57 ++++ .../ExternalServiceManagementException.java | 27 ++ .../ExternalServiceManagementService.java | 274 ++++++++++++++++++ iotdb-core/node-commons/pom.xml | 5 + .../iotdb/commons/conf/IoTDBConstant.java | 1 + .../commons/externalservice/ServiceInfo.java | 143 +++++++++ .../iotdb/commons/service/ServiceType.java | 1 + .../src/main/thrift/confignode.thrift | 32 ++ 26 files changed, 1434 insertions(+), 2 deletions(-) create mode 100644 iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/consensus/request/read/exernalservice/ShowExternalServicePlan.java create mode 100644 iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/consensus/request/write/externalservice/CreateExternalServicePlan.java create mode 100644 iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/consensus/request/write/externalservice/DropExternalServicePlan.java create mode 100644 iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/consensus/request/write/externalservice/StartExternalServicePlan.java create mode 100644 iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/consensus/request/write/externalservice/StopExternalServicePlan.java create mode 100644 iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/consensus/response/externalservice/ShowExternalServiceResp.java create mode 100644 iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/manager/externalservice/ExternalServiceInfo.java create mode 100644 iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/manager/externalservice/ExternalServiceManager.java create mode 100644 iotdb-core/datanode/src/main/java/org/apache/iotdb/db/service/exernalservice/BuiltinExternalServices.java create mode 100644 iotdb-core/datanode/src/main/java/org/apache/iotdb/db/service/exernalservice/ExternalServiceClassLoader.java create mode 100644 iotdb-core/datanode/src/main/java/org/apache/iotdb/db/service/exernalservice/ExternalServiceManagementException.java create mode 100644 iotdb-core/datanode/src/main/java/org/apache/iotdb/db/service/exernalservice/ExternalServiceManagementService.java create mode 100644 iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/externalservice/ServiceInfo.java diff --git a/iotdb-client/service-rpc/src/main/java/org/apache/iotdb/rpc/TSStatusCode.java b/iotdb-client/service-rpc/src/main/java/org/apache/iotdb/rpc/TSStatusCode.java index bb533ddbbd57..3f64722f32f1 100644 --- a/iotdb-client/service-rpc/src/main/java/org/apache/iotdb/rpc/TSStatusCode.java +++ b/iotdb-client/service-rpc/src/main/java/org/apache/iotdb/rpc/TSStatusCode.java @@ -338,6 +338,11 @@ public enum TSStatusCode { RATIS_READ_UNAVAILABLE(2207), PIPE_CONSENSUS_CLOSE_ERROR(2208), PIPE_CONSENSUS_WAIT_ORDER_TIMEOUT(2209), + + // ExternalService + NO_SUCH_EXTERNAL_SERVICE(2300), + EXTERNAL_SERVICE_ALREADY_ACTIVE(2301), + EXTERNAL_SERVICE_ALREADY_EXIST(2302), ; private final int statusCode; diff --git a/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/consensus/request/ConfigPhysicalPlan.java b/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/consensus/request/ConfigPhysicalPlan.java index d1bf4c45ced5..4a822578dabb 100644 --- a/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/consensus/request/ConfigPhysicalPlan.java +++ b/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/consensus/request/ConfigPhysicalPlan.java @@ -46,6 +46,8 @@ import org.apache.iotdb.confignode.consensus.request.write.datanode.RegisterDataNodePlan; import org.apache.iotdb.confignode.consensus.request.write.datanode.RemoveDataNodePlan; import org.apache.iotdb.confignode.consensus.request.write.datanode.UpdateDataNodePlan; +import org.apache.iotdb.confignode.consensus.request.write.externalservice.CreateExternalServicePlan; +import org.apache.iotdb.confignode.consensus.request.write.externalservice.DropExternalServicePlan; import org.apache.iotdb.confignode.consensus.request.write.function.CreateFunctionPlan; import org.apache.iotdb.confignode.consensus.request.write.function.DropTableModelFunctionPlan; import org.apache.iotdb.confignode.consensus.request.write.function.DropTreeModelFunctionPlan; @@ -588,6 +590,18 @@ public static ConfigPhysicalPlan create(final ByteBuffer buffer) throws IOExcept case setThrottleQuota: plan = new SetThrottleQuotaPlan(); break; + case CreateExternalService: + plan = new CreateExternalServicePlan(); + break; + case StartExternalService: + plan = new StartExternalServicePlan(); + break; + case StopExternalService: + plan = new StopExternalServicePlan(); + break; + case DropExternalService: + plan = new DropExternalServicePlan(); + break; default: throw new IOException("unknown PhysicalPlan configPhysicalPlanType: " + planType); } diff --git a/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/consensus/request/ConfigPhysicalPlanType.java b/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/consensus/request/ConfigPhysicalPlanType.java index 360dd83dd70c..ecabc2331b2e 100644 --- a/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/consensus/request/ConfigPhysicalPlanType.java +++ b/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/consensus/request/ConfigPhysicalPlanType.java @@ -335,6 +335,12 @@ public enum ConfigPhysicalPlanType { EnableSeparationOfAdminPowers((short) 2200), + CreateExternalService((short) 2301), + StartExternalService((short) 2302), + StopExternalService((short) 2303), + DropExternalService((short) 2304), + ShowExternalService((short) 2305), + /** Test Only. */ TestOnly((short) 30000), ; diff --git a/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/consensus/request/read/exernalservice/ShowExternalServicePlan.java b/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/consensus/request/read/exernalservice/ShowExternalServicePlan.java new file mode 100644 index 000000000000..72986c1eb81d --- /dev/null +++ b/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/consensus/request/read/exernalservice/ShowExternalServicePlan.java @@ -0,0 +1,60 @@ +/* + * 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.consensus.request.read.exernalservice; + +import org.apache.iotdb.confignode.consensus.request.ConfigPhysicalPlanType; +import org.apache.iotdb.confignode.consensus.request.read.ConfigPhysicalReadPlan; + +import java.util.Objects; + +/** + * Get infos of ExternalService by the specific DataNode's id. And return all when dataNodeID is set + * to -1. + */ +public class ShowExternalServicePlan extends ConfigPhysicalReadPlan { + + private final int dataNodeId; + + public ShowExternalServicePlan(final int dataNodeId) { + super(ConfigPhysicalPlanType.GetDataNodeConfiguration); + this.dataNodeId = dataNodeId; + } + + public Integer getDataNodeId() { + return dataNodeId; + } + + @Override + public boolean equals(final Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + final ShowExternalServicePlan that = (ShowExternalServicePlan) o; + return dataNodeId == that.dataNodeId; + } + + @Override + public int hashCode() { + return Objects.hash(dataNodeId); + } +} diff --git a/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/consensus/request/write/externalservice/CreateExternalServicePlan.java b/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/consensus/request/write/externalservice/CreateExternalServicePlan.java new file mode 100644 index 000000000000..ae65fc05b941 --- /dev/null +++ b/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/consensus/request/write/externalservice/CreateExternalServicePlan.java @@ -0,0 +1,89 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.apache.iotdb.confignode.consensus.request.write.externalservice; + +import org.apache.iotdb.commons.externalservice.ServiceInfo; +import org.apache.iotdb.confignode.consensus.request.ConfigPhysicalPlan; +import org.apache.iotdb.confignode.consensus.request.ConfigPhysicalPlanType; + +import org.apache.tsfile.utils.ReadWriteIOUtils; + +import java.io.DataOutputStream; +import java.io.IOException; +import java.nio.ByteBuffer; +import java.util.Objects; + +public class CreateExternalServicePlan extends ConfigPhysicalPlan { + + private int datanodeId; + private ServiceInfo serviceInfo; + + public CreateExternalServicePlan() { + super(ConfigPhysicalPlanType.CreateExternalService); + } + + public CreateExternalServicePlan(int datanodeId, ServiceInfo serviceInfo) { + super(ConfigPhysicalPlanType.CreateExternalService); + this.datanodeId = datanodeId; + this.serviceInfo = serviceInfo; + } + + public int getDatanodeId() { + return datanodeId; + } + + public ServiceInfo getServiceInfo() { + return serviceInfo; + } + + @Override + protected void serializeImpl(DataOutputStream stream) throws IOException { + stream.writeShort(getType().getPlanType()); + + ReadWriteIOUtils.write(datanodeId, stream); + serviceInfo.serialize(stream); + } + + @Override + protected void deserializeImpl(ByteBuffer buffer) throws IOException { + datanodeId = ReadWriteIOUtils.readInt(buffer); + serviceInfo = ServiceInfo.deserialize(buffer); + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + if (!super.equals(o)) { + return false; + } + CreateExternalServicePlan that = (CreateExternalServicePlan) o; + return datanodeId == that.datanodeId && Objects.equals(serviceInfo, that.serviceInfo); + } + + @Override + public int hashCode() { + return Objects.hash(super.hashCode(), datanodeId, serviceInfo); + } +} diff --git a/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/consensus/request/write/externalservice/DropExternalServicePlan.java b/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/consensus/request/write/externalservice/DropExternalServicePlan.java new file mode 100644 index 000000000000..a25619a9fa08 --- /dev/null +++ b/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/consensus/request/write/externalservice/DropExternalServicePlan.java @@ -0,0 +1,87 @@ +/* + * 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.consensus.request.write.externalservice; + +import org.apache.iotdb.confignode.consensus.request.ConfigPhysicalPlan; +import org.apache.iotdb.confignode.consensus.request.ConfigPhysicalPlanType; + +import org.apache.tsfile.utils.ReadWriteIOUtils; + +import java.io.DataOutputStream; +import java.io.IOException; +import java.nio.ByteBuffer; +import java.util.Objects; + +public class DropExternalServicePlan extends ConfigPhysicalPlan { + + private int dataNodeId; + private String serviceName; + + public DropExternalServicePlan() { + super(ConfigPhysicalPlanType.DropExternalService); + } + + public DropExternalServicePlan(int dataNodeId, String serviceName) { + super(ConfigPhysicalPlanType.DropExternalService); + this.dataNodeId = dataNodeId; + this.serviceName = serviceName; + } + + public int getDataNodeId() { + return dataNodeId; + } + + public String getServiceName() { + return serviceName; + } + + @Override + protected void serializeImpl(DataOutputStream stream) throws IOException { + stream.writeShort(getType().getPlanType()); + ReadWriteIOUtils.write(dataNodeId, stream); + ReadWriteIOUtils.write(serviceName, stream); + } + + @Override + protected void deserializeImpl(ByteBuffer buffer) throws IOException { + dataNodeId = ReadWriteIOUtils.readInt(buffer); + serviceName = ReadWriteIOUtils.readString(buffer); + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + if (!super.equals(o)) { + return false; + } + DropExternalServicePlan that = (DropExternalServicePlan) o; + return dataNodeId == that.dataNodeId && Objects.equals(serviceName, that.serviceName); + } + + @Override + public int hashCode() { + return Objects.hash(super.hashCode(), dataNodeId, serviceName); + } +} diff --git a/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/consensus/request/write/externalservice/StartExternalServicePlan.java b/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/consensus/request/write/externalservice/StartExternalServicePlan.java new file mode 100644 index 000000000000..67fd0aa3d450 --- /dev/null +++ b/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/consensus/request/write/externalservice/StartExternalServicePlan.java @@ -0,0 +1,87 @@ +/* + * 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.consensus.request.write.externalservice; + +import org.apache.iotdb.confignode.consensus.request.ConfigPhysicalPlan; +import org.apache.iotdb.confignode.consensus.request.ConfigPhysicalPlanType; + +import org.apache.tsfile.utils.ReadWriteIOUtils; + +import java.io.DataOutputStream; +import java.io.IOException; +import java.nio.ByteBuffer; +import java.util.Objects; + +public class StartExternalServicePlan extends ConfigPhysicalPlan { + + private int dataNodeId; + private String serviceName; + + public StartExternalServicePlan() { + super(ConfigPhysicalPlanType.StartExternalService); + } + + public StartExternalServicePlan(int dataNodeId, String serviceName) { + super(ConfigPhysicalPlanType.StartExternalService); + this.dataNodeId = dataNodeId; + this.serviceName = serviceName; + } + + public int getDataNodeId() { + return dataNodeId; + } + + public String getServiceName() { + return serviceName; + } + + @Override + protected void serializeImpl(DataOutputStream stream) throws IOException { + stream.writeShort(getType().getPlanType()); + ReadWriteIOUtils.write(dataNodeId, stream); + ReadWriteIOUtils.write(serviceName, stream); + } + + @Override + protected void deserializeImpl(ByteBuffer buffer) throws IOException { + dataNodeId = ReadWriteIOUtils.readInt(buffer); + serviceName = ReadWriteIOUtils.readString(buffer); + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + if (!super.equals(o)) { + return false; + } + StartExternalServicePlan that = (StartExternalServicePlan) o; + return dataNodeId == that.dataNodeId && Objects.equals(serviceName, that.serviceName); + } + + @Override + public int hashCode() { + return Objects.hash(super.hashCode(), dataNodeId, serviceName); + } +} diff --git a/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/consensus/request/write/externalservice/StopExternalServicePlan.java b/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/consensus/request/write/externalservice/StopExternalServicePlan.java new file mode 100644 index 000000000000..83fcbe5c67d6 --- /dev/null +++ b/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/consensus/request/write/externalservice/StopExternalServicePlan.java @@ -0,0 +1,87 @@ +/* + * 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.consensus.request.write.externalservice; + +import org.apache.iotdb.confignode.consensus.request.ConfigPhysicalPlan; +import org.apache.iotdb.confignode.consensus.request.ConfigPhysicalPlanType; + +import org.apache.tsfile.utils.ReadWriteIOUtils; + +import java.io.DataOutputStream; +import java.io.IOException; +import java.nio.ByteBuffer; +import java.util.Objects; + +public class StopExternalServicePlan extends ConfigPhysicalPlan { + + private int dataNodeId; + private String serviceName; + + public StopExternalServicePlan() { + super(ConfigPhysicalPlanType.StopExternalService); + } + + public StopExternalServicePlan(int dataNodeId, String serviceName) { + super(ConfigPhysicalPlanType.StopExternalService); + this.dataNodeId = dataNodeId; + this.serviceName = serviceName; + } + + public int getDataNodeId() { + return dataNodeId; + } + + public String getServiceName() { + return serviceName; + } + + @Override + protected void serializeImpl(DataOutputStream stream) throws IOException { + stream.writeShort(getType().getPlanType()); + ReadWriteIOUtils.write(dataNodeId, stream); + ReadWriteIOUtils.write(serviceName, stream); + } + + @Override + protected void deserializeImpl(ByteBuffer buffer) throws IOException { + dataNodeId = ReadWriteIOUtils.readInt(buffer); + serviceName = ReadWriteIOUtils.readString(buffer); + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + if (!super.equals(o)) { + return false; + } + StopExternalServicePlan that = (StopExternalServicePlan) o; + return dataNodeId == that.dataNodeId && Objects.equals(serviceName, that.serviceName); + } + + @Override + public int hashCode() { + return Objects.hash(super.hashCode(), dataNodeId, serviceName); + } +} diff --git a/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/consensus/response/externalservice/ShowExternalServiceResp.java b/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/consensus/response/externalservice/ShowExternalServiceResp.java new file mode 100644 index 000000000000..d6020ac72ba8 --- /dev/null +++ b/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/consensus/response/externalservice/ShowExternalServiceResp.java @@ -0,0 +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.consensus.response.externalservice; + +import org.apache.iotdb.common.rpc.thrift.TSStatus; +import org.apache.iotdb.commons.externalservice.ServiceInfo; +import org.apache.iotdb.confignode.rpc.thrift.TExternalServiceEntry; +import org.apache.iotdb.confignode.rpc.thrift.TShowExternalServiceResp; +import org.apache.iotdb.consensus.common.DataSet; + +import javax.validation.constraints.NotNull; + +import java.util.Comparator; +import java.util.List; +import java.util.stream.Collectors; + +public class ShowExternalServiceResp implements DataSet { + + private final TSStatus status; + private final List serviceInfos; + + public ShowExternalServiceResp( + @NotNull TSStatus status, @NotNull List serviceInfos) { + this.status = status; + this.serviceInfos = serviceInfos; + } + + public TShowExternalServiceResp convertToRpcShowExternalServiceResp() { + return new TShowExternalServiceResp( + status, + serviceInfos.stream() + .map( + entry -> + new TExternalServiceEntry( + entry.getServiceName(), entry.getClassName(), entry.getState().getValue())) + .sorted(Comparator.comparing(entry -> entry.serviceName)) + .collect(Collectors.toList())); + } +} diff --git a/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/manager/ConfigManager.java b/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/manager/ConfigManager.java index 3c3d23041950..f3740ec81a1f 100644 --- a/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/manager/ConfigManager.java +++ b/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/manager/ConfigManager.java @@ -109,6 +109,7 @@ import org.apache.iotdb.confignode.consensus.statemachine.ConfigRegionStateMachine; import org.apache.iotdb.confignode.manager.consensus.ConsensusManager; import org.apache.iotdb.confignode.manager.cq.CQManager; +import org.apache.iotdb.confignode.manager.externalservice.ExternalServiceManager; import org.apache.iotdb.confignode.manager.load.LoadManager; import org.apache.iotdb.confignode.manager.load.cache.node.NodeHeartbeatSample; import org.apache.iotdb.confignode.manager.node.ClusterNodeStartUtils; @@ -154,6 +155,7 @@ import org.apache.iotdb.confignode.rpc.thrift.TCountTimeSlotListResp; import org.apache.iotdb.confignode.rpc.thrift.TCreateCQReq; import org.apache.iotdb.confignode.rpc.thrift.TCreateConsumerReq; +import org.apache.iotdb.confignode.rpc.thrift.TCreateExternalServiceReq; import org.apache.iotdb.confignode.rpc.thrift.TCreateFunctionReq; import org.apache.iotdb.confignode.rpc.thrift.TCreatePipePluginReq; import org.apache.iotdb.confignode.rpc.thrift.TCreatePipeReq; @@ -225,6 +227,7 @@ import org.apache.iotdb.confignode.rpc.thrift.TShowDataNodes4InformationSchemaResp; import org.apache.iotdb.confignode.rpc.thrift.TShowDataNodesResp; import org.apache.iotdb.confignode.rpc.thrift.TShowDatabaseResp; +import org.apache.iotdb.confignode.rpc.thrift.TShowExternalServiceResp; import org.apache.iotdb.confignode.rpc.thrift.TShowPipePluginReq; import org.apache.iotdb.confignode.rpc.thrift.TShowPipeReq; import org.apache.iotdb.confignode.rpc.thrift.TShowPipeResp; @@ -315,6 +318,9 @@ public class ConfigManager implements IManager { /** Manage procedure. */ private final ProcedureManager procedureManager; + /** ExternalService. */ + private final ExternalServiceManager externalServiceManager; + /** UDF. */ private final UDFManager udfManager; @@ -389,6 +395,7 @@ public ConfigManager() throws IOException { this.partitionManager = new PartitionManager(this, partitionInfo); this.permissionManager = createPermissionManager(authorInfo); this.procedureManager = createProcedureManager(procedureInfo); + this.externalServiceManager = new ExternalServiceManager(this); this.udfManager = new UDFManager(this, udfInfo); this.triggerManager = new TriggerManager(this, triggerInfo); this.cqManager = new CQManager(this); @@ -2600,6 +2607,46 @@ public TShowCQResp showCQ() { : new TShowCQResp(status, Collections.emptyList()); } + @Override + public TSStatus createExternalService(TCreateExternalServiceReq req) { + TSStatus status = confirmLeader(); + return status.getCode() == TSStatusCode.SUCCESS_STATUS.getStatusCode() + ? externalServiceManager.createService(req) + : status; + } + + @Override + public TSStatus startExternalService(int dataNodeId, String serviceName) { + TSStatus status = confirmLeader(); + return status.getCode() == TSStatusCode.SUCCESS_STATUS.getStatusCode() + ? externalServiceManager.startService(dataNodeId, serviceName) + : status; + } + + @Override + public TSStatus stopExternalService(int dataNodeId, String serviceName) { + TSStatus status = confirmLeader(); + return status.getCode() == TSStatusCode.SUCCESS_STATUS.getStatusCode() + ? externalServiceManager.stopService(dataNodeId, serviceName) + : status; + } + + @Override + public TSStatus dropExternalService(int dataNodeId, String serviceName) { + TSStatus status = confirmLeader(); + return status.getCode() == TSStatusCode.SUCCESS_STATUS.getStatusCode() + ? externalServiceManager.dropService(dataNodeId, serviceName) + : status; + } + + @Override + public TShowExternalServiceResp showExternalService(int dataNodeId) { + TSStatus status = confirmLeader(); + return status.getCode() == TSStatusCode.SUCCESS_STATUS.getStatusCode() + ? externalServiceManager.showService(dataNodeId) + : new TShowExternalServiceResp(status, Collections.emptyList()); + } + /** * Get all related schemaRegion which may contains the timeseries matched by given patternTree. */ diff --git a/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/manager/IManager.java b/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/manager/IManager.java index caa189be8157..74d0fb509372 100644 --- a/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/manager/IManager.java +++ b/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/manager/IManager.java @@ -75,6 +75,7 @@ import org.apache.iotdb.confignode.rpc.thrift.TCountTimeSlotListResp; import org.apache.iotdb.confignode.rpc.thrift.TCreateCQReq; import org.apache.iotdb.confignode.rpc.thrift.TCreateConsumerReq; +import org.apache.iotdb.confignode.rpc.thrift.TCreateExternalServiceReq; import org.apache.iotdb.confignode.rpc.thrift.TCreateFunctionReq; import org.apache.iotdb.confignode.rpc.thrift.TCreatePipePluginReq; import org.apache.iotdb.confignode.rpc.thrift.TCreatePipeReq; @@ -144,6 +145,7 @@ import org.apache.iotdb.confignode.rpc.thrift.TShowDataNodes4InformationSchemaResp; import org.apache.iotdb.confignode.rpc.thrift.TShowDataNodesResp; import org.apache.iotdb.confignode.rpc.thrift.TShowDatabaseResp; +import org.apache.iotdb.confignode.rpc.thrift.TShowExternalServiceResp; import org.apache.iotdb.confignode.rpc.thrift.TShowPipePluginReq; import org.apache.iotdb.confignode.rpc.thrift.TShowPipeReq; import org.apache.iotdb.confignode.rpc.thrift.TShowPipeResp; @@ -862,6 +864,16 @@ TDataPartitionTableResp getOrCreateDataPartition( TShowCQResp showCQ(); + TSStatus createExternalService(TCreateExternalServiceReq req); + + TSStatus startExternalService(int dataNodeId, String serviceName); + + TSStatus stopExternalService(int dataNodeId, String serviceName); + + TSStatus dropExternalService(int dataNodeId, String serviceName); + + TShowExternalServiceResp showExternalService(int dataNodeId); + TSStatus checkConfigNodeGlobalConfig(TConfigNodeRegisterReq req); TSStatus transfer(List newUnknownDataList); diff --git a/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/manager/externalservice/ExternalServiceInfo.java b/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/manager/externalservice/ExternalServiceInfo.java new file mode 100644 index 000000000000..104ec6eb88db --- /dev/null +++ b/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/manager/externalservice/ExternalServiceInfo.java @@ -0,0 +1,126 @@ +/* + * 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.externalservice; + +import org.apache.iotdb.common.rpc.thrift.TSStatus; +import org.apache.iotdb.commons.externalservice.ServiceInfo; +import org.apache.iotdb.commons.snapshot.SnapshotProcessor; +import org.apache.iotdb.confignode.consensus.request.write.externalservice.CreateExternalServicePlan; +import org.apache.iotdb.rpc.TSStatusCode; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.io.File; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.IOException; +import java.util.HashMap; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; + +public class ExternalServiceInfo implements SnapshotProcessor { + + private static final Logger LOGGER = LoggerFactory.getLogger(ExternalServiceInfo.class); + + private final Map> datanodeToServiceInfos; + + private static final String SNAPSHOT_FILENAME = "service_info.bin"; + + public ExternalServiceInfo() { + datanodeToServiceInfos = new ConcurrentHashMap<>(); + } + + /** + * Add a new ExternalService only if there was no mapping for this service on target + * DataNode, otherwise ignore this operation. + * + * @return SUCCESS_STATUS if there was no mapping for this service on target DataNode, + * otherwise EXTERNAL_SERVICE_AlREADY_EXIST + */ + public TSStatus addService(CreateExternalServicePlan plan) { + TSStatus res = new TSStatus(); + Map serviceInfos = + datanodeToServiceInfos.computeIfAbsent(plan.getDatanodeId(), k -> new HashMap<>()); + String serviceName = plan.getServiceInfo().getServiceName(); + if (serviceInfos.containsKey(serviceName)) { + res.code = TSStatusCode.EXTERNAL_SERVICE_ALREADY_EXIST.getStatusCode(); + res.message = + String.format( + "ExternalService %s has already been created on DataNode .", + serviceName, plan.getDatanodeId()); + } else { + serviceInfos.put(serviceName, plan.getServiceInfo()); + res.code = TSStatusCode.SUCCESS_STATUS.getStatusCode(); + } + return res; + } + + @Override + public boolean processTakeSnapshot(File snapshotDir) throws IOException { + File snapshotFile = new File(snapshotDir, SNAPSHOT_FILENAME); + if (snapshotFile.exists() && snapshotFile.isFile()) { + LOGGER.error( + "Failed to take snapshot, because snapshot file [{}] is already exist.", + snapshotFile.getAbsolutePath()); + return false; + } + + try (FileOutputStream fileOutputStream = new FileOutputStream(snapshotFile)) { + + // serializeExistedJarToMD5(fileOutputStream); + + // udfTable.serializeUDFTable(fileOutputStream); + + // fsync + fileOutputStream.getFD().sync(); + + return true; + } + } + + @Override + public void processLoadSnapshot(File snapshotDir) throws IOException { + File snapshotFile = new File(snapshotDir, SNAPSHOT_FILENAME); + if (!snapshotFile.exists() || !snapshotFile.isFile()) { + LOGGER.error( + "Failed to load snapshot,snapshot file [{}] is not exist.", + snapshotFile.getAbsolutePath()); + return; + } + + // acquireUDFTableLock(); + try (FileInputStream fileInputStream = new FileInputStream(snapshotFile)) { + + clear(); + + // deserializeExistedJarToMD5(fileInputStream); + + // udfTable.deserializeUDFTable(fileInputStream); + } finally { + // releaseUDFTableLock(); + } + } + + public void clear() { + // existedJarToMD5.clear(); + // udfTable.clear(); + } +} diff --git a/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/manager/externalservice/ExternalServiceManager.java b/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/manager/externalservice/ExternalServiceManager.java new file mode 100644 index 000000000000..1e6d2c16e9b4 --- /dev/null +++ b/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/manager/externalservice/ExternalServiceManager.java @@ -0,0 +1,143 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.apache.iotdb.confignode.manager.externalservice; + +import org.apache.iotdb.common.rpc.thrift.TSStatus; +import org.apache.iotdb.commons.externalservice.ServiceInfo; +import org.apache.iotdb.confignode.consensus.request.read.exernalservice.ShowExternalServicePlan; +import org.apache.iotdb.confignode.consensus.request.write.externalservice.CreateExternalServicePlan; +import org.apache.iotdb.confignode.consensus.request.write.externalservice.DropExternalServicePlan; +import org.apache.iotdb.confignode.consensus.request.write.externalservice.StartExternalServicePlan; +import org.apache.iotdb.confignode.consensus.request.write.externalservice.StopExternalServicePlan; +import org.apache.iotdb.confignode.consensus.response.externalservice.ShowExternalServiceResp; +import org.apache.iotdb.confignode.manager.ConfigManager; +import org.apache.iotdb.confignode.rpc.thrift.TCreateExternalServiceReq; +import org.apache.iotdb.confignode.rpc.thrift.TShowExternalServiceResp; +import org.apache.iotdb.consensus.common.DataSet; +import org.apache.iotdb.consensus.exception.ConsensusException; +import org.apache.iotdb.rpc.TSStatusCode; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.Collections; + +public class ExternalServiceManager { + + private static final Logger LOGGER = LoggerFactory.getLogger(ExternalServiceManager.class); + + private final ConfigManager configManager; + + public ExternalServiceManager(ConfigManager configManager) { + this.configManager = configManager; + } + + public TSStatus createService(TCreateExternalServiceReq req) { + try { + return configManager + .getConsensusManager() + .write( + new CreateExternalServicePlan( + req.getDataNodeId(), + new ServiceInfo( + req.getServiceName(), + req.getClassName(), + ServiceInfo.ServiceType.USER_DEFINED))); + } catch (ConsensusException e) { + LOGGER.warn( + "Unexpected error happened while creating Service {} on DataNode {}: ", + req.getServiceName(), + req.getDataNodeId(), + e); + // consensus layer related errors + TSStatus res = new TSStatus(TSStatusCode.EXECUTE_STATEMENT_ERROR.getStatusCode()); + res.setMessage(e.getMessage()); + return res; + } + } + + public TSStatus startService(int dataNodeId, String serviceName) { + try { + return configManager + .getConsensusManager() + .write(new StartExternalServicePlan(dataNodeId, serviceName)); + } catch (ConsensusException e) { + LOGGER.warn( + "Unexpected error happened while starting Service {} on DataNode {}: ", + serviceName, + dataNodeId, + e); + // consensus layer related errors + TSStatus res = new TSStatus(TSStatusCode.EXECUTE_STATEMENT_ERROR.getStatusCode()); + res.setMessage(e.getMessage()); + return res; + } + } + + public TSStatus stopService(int dataNodeId, String serviceName) { + try { + return configManager + .getConsensusManager() + .write(new StopExternalServicePlan(dataNodeId, serviceName)); + } catch (ConsensusException e) { + LOGGER.warn( + "Unexpected error happened while stopping Service {} on DataNode {}: ", + serviceName, + dataNodeId, + e); + // consensus layer related errors + TSStatus res = new TSStatus(TSStatusCode.EXECUTE_STATEMENT_ERROR.getStatusCode()); + res.setMessage(e.getMessage()); + return res; + } + } + + public TSStatus dropService(int dataNodeId, String serviceName) { + try { + return configManager + .getConsensusManager() + .write(new DropExternalServicePlan(dataNodeId, serviceName)); + } catch (ConsensusException e) { + LOGGER.warn( + "Unexpected error happened while dropping Service {} on DataNode {}: ", + serviceName, + dataNodeId, + e); + // consensus layer related errors + TSStatus res = new TSStatus(TSStatusCode.EXECUTE_STATEMENT_ERROR.getStatusCode()); + res.setMessage(e.getMessage()); + return res; + } + } + + public TShowExternalServiceResp showService(int dataNodeId) { + try { + DataSet response = + configManager.getConsensusManager().read(new ShowExternalServicePlan(dataNodeId)); + return ((ShowExternalServiceResp) response).convertToRpcShowExternalServiceResp(); + } catch (ConsensusException e) { + LOGGER.warn("Unexpected error happened while showing Service: ", e); + // consensus layer related errors + TSStatus res = new TSStatus(TSStatusCode.EXECUTE_STATEMENT_ERROR.getStatusCode()); + res.setMessage(e.getMessage()); + return new TShowExternalServiceResp(res, Collections.emptyList()); + } + } +} diff --git a/iotdb-core/confignode/src/test/resources/confignode1conf/iotdb-system.properties b/iotdb-core/confignode/src/test/resources/confignode1conf/iotdb-system.properties index b396e373f869..508ab394aa34 100644 --- a/iotdb-core/confignode/src/test/resources/confignode1conf/iotdb-system.properties +++ b/iotdb-core/confignode/src/test/resources/confignode1conf/iotdb-system.properties @@ -33,8 +33,8 @@ cn_metric_prometheus_reporter_port=9091 timestamp_precision=ms data_region_consensus_protocol_class=org.apache.iotdb.consensus.iot.IoTConsensus schema_region_consensus_protocol_class=org.apache.iotdb.consensus.ratis.RatisConsensus -schema_replication_factor=3 -data_replication_factor=3 +schema_replication_factor=1 +data_replication_factor=1 udf_lib_dir=target/confignode1/ext/udf trigger_lib_dir=target/confignode1/ext/trigger pipe_lib_dir=target/confignode1/ext/pipe diff --git a/iotdb-core/datanode/pom.xml b/iotdb-core/datanode/pom.xml index 85c96595381e..72c4acba5861 100644 --- a/iotdb-core/datanode/pom.xml +++ b/iotdb-core/datanode/pom.xml @@ -94,6 +94,11 @@ iotdb-thrift-consensus 2.0.7-SNAPSHOT + + org.apache.iotdb + external-service-api + 2.0.7-SNAPSHOT + org.apache.iotdb udf-api diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/conf/IoTDBConfig.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/conf/IoTDBConfig.java index b2c808654677..abc340b7af48 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/conf/IoTDBConfig.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/conf/IoTDBConfig.java @@ -270,6 +270,10 @@ public class IoTDBConfig { private String triggerTemporaryLibDir = triggerDir + File.separator + IoTDBConstant.TMP_FOLDER_NAME; + /** External lib directory for UDF, stores user-uploaded JAR files */ + private String externalServiceDir = + IoTDBConstant.EXT_FOLDER_NAME + File.separator + IoTDBConstant.EXTERNAL_SERVICE_FOLDER_NAME; + /** External lib directory for Pipe Plugin, stores user-defined JAR files */ private String pipeDir = IoTDBConstant.EXT_FOLDER_NAME + File.separator + IoTDBConstant.PIPE_FOLDER_NAME; @@ -1358,6 +1362,7 @@ private void formulateFolders() { udfTemporaryLibDir = addDataHomeDir(udfTemporaryLibDir); triggerDir = addDataHomeDir(triggerDir); triggerTemporaryLibDir = addDataHomeDir(triggerTemporaryLibDir); + externalServiceDir = addDataHomeDir(externalServiceDir); pipeDir = addDataHomeDir(pipeDir); pipeTemporaryLibDir = addDataHomeDir(pipeTemporaryLibDir); for (int i = 0; i < pipeReceiverFileDirs.length; i++) { @@ -1679,6 +1684,10 @@ public void updateTriggerTemporaryLibDir() { this.triggerTemporaryLibDir = triggerDir + File.separator + IoTDBConstant.TMP_FOLDER_NAME; } + public String getExternalServiceDir() { + return externalServiceDir; + } + public String getPipeLibDir() { return pipeDir; } diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/service/DataNode.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/service/DataNode.java index 1fac05012fe8..01b39023d54e 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/service/DataNode.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/service/DataNode.java @@ -1287,6 +1287,7 @@ private void initProtocols() throws StartupException { if (IoTDBRestServiceDescriptor.getInstance().getConfig().isEnableRestService()) { registerManager.register(RestService.getInstance()); } + // registerManager.register(ExternalServiceManagementService.getInstance()); if (PipeConfig.getInstance().getPipeAirGapReceiverEnabled()) { registerManager.register(PipeDataNodeAgent.receiver().airGap()); } diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/service/exernalservice/BuiltinExternalServices.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/service/exernalservice/BuiltinExternalServices.java new file mode 100644 index 000000000000..9b9fa403e7fc --- /dev/null +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/service/exernalservice/BuiltinExternalServices.java @@ -0,0 +1,58 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.apache.iotdb.db.service.exernalservice; + +import org.apache.iotdb.db.conf.IoTDBDescriptor; +import org.apache.iotdb.db.conf.rest.IoTDBRestServiceDescriptor; + +import java.util.function.Supplier; + +public enum BuiltinExternalServices { + MQTT( + "MQTT", + "org.apache.iotdb.externalservice.Mqtt", + IoTDBDescriptor.getInstance().getConfig()::isEnableMQTTService), + REST( + "REST", + "org.apache.iotdb.externalservice.Rest", + IoTDBRestServiceDescriptor.getInstance().getConfig()::isEnableRestService); + + private final String serviceName; + private final String className; + private final Supplier enabledFunction; + + BuiltinExternalServices(String serviceName, String className, Supplier enabledFunction) { + this.serviceName = serviceName; + this.className = className; + this.enabledFunction = enabledFunction; + } + + public String getServiceName() { + return serviceName; + } + + public String getClassName() { + return className; + } + + public boolean isEnabled() { + return enabledFunction.get(); + } +} diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/service/exernalservice/ExternalServiceClassLoader.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/service/exernalservice/ExternalServiceClassLoader.java new file mode 100644 index 000000000000..85166f1a1df6 --- /dev/null +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/service/exernalservice/ExternalServiceClassLoader.java @@ -0,0 +1,57 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.apache.iotdb.db.service.exernalservice; + +import org.apache.iotdb.commons.file.SystemFileFactory; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.io.IOException; +import java.net.URL; +import java.net.URLClassLoader; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +public class ExternalServiceClassLoader extends URLClassLoader { + + private static final Logger LOGGER = LoggerFactory.getLogger(ExternalServiceClassLoader.class); + + private final String libRoot; + + public ExternalServiceClassLoader(String libRoot) throws IOException { + super(new URL[0]); + this.libRoot = libRoot; + LOGGER.info("External Service lib root: {}", libRoot); + addURLs(); + } + + private void addURLs() throws IOException { + try (Stream pathStream = + Files.walk(SystemFileFactory.INSTANCE.getFile(libRoot).toPath())) { + for (Path path : + pathStream.filter(path -> !path.toFile().isDirectory()).collect(Collectors.toList())) { + super.addURL(path.toUri().toURL()); + } + } + } +} diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/service/exernalservice/ExternalServiceManagementException.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/service/exernalservice/ExternalServiceManagementException.java new file mode 100644 index 000000000000..0ccc917ec697 --- /dev/null +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/service/exernalservice/ExternalServiceManagementException.java @@ -0,0 +1,27 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.apache.iotdb.db.service.exernalservice; + +public class ExternalServiceManagementException extends RuntimeException { + + public ExternalServiceManagementException(String message) { + super(message); + } +} diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/service/exernalservice/ExternalServiceManagementService.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/service/exernalservice/ExternalServiceManagementService.java new file mode 100644 index 000000000000..39ec79cbd823 --- /dev/null +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/service/exernalservice/ExternalServiceManagementService.java @@ -0,0 +1,274 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.apache.iotdb.db.service.exernalservice; + +import org.apache.iotdb.commons.exception.StartupException; +import org.apache.iotdb.commons.externalservice.ServiceInfo; +import org.apache.iotdb.db.conf.IoTDBDescriptor; +import org.apache.iotdb.externalservice.api.IExternalService; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import javax.annotation.concurrent.GuardedBy; + +import java.io.IOException; +import java.lang.reflect.InvocationTargetException; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.concurrent.locks.ReadWriteLock; +import java.util.concurrent.locks.ReentrantReadWriteLock; +import java.util.stream.Collectors; + +import static com.google.common.base.Preconditions.checkState; +import static org.apache.iotdb.commons.externalservice.ServiceInfo.State.RUNNING; +import static org.apache.iotdb.commons.externalservice.ServiceInfo.State.STOPPED; +import static org.apache.iotdb.db.service.exernalservice.ServiceInfo.State.RUNNING; +import static org.apache.iotdb.db.service.exernalservice.ServiceInfo.State.STOPPED; + +public class ExternalServiceManagementService { + @GuardedBy("lock") + private final Map serviceInfos; + + private final String libRoot; + + private final ReadWriteLock lock = new ReentrantReadWriteLock(); + + private static final Logger LOGGER = + LoggerFactory.getLogger(ExternalServiceManagementService.class); + + private ExternalServiceManagementService(String libRoot) { + this.serviceInfos = new HashMap<>(); + this.libRoot = libRoot; + } + + public List getAllServices() { + try { + lock.writeLock().lock(); + return serviceInfos.values().stream().collect(Collectors.toList()); + } finally { + lock.writeLock().unlock(); + } + } + + public void createService(String serviceName, String className) { + try { + lock.writeLock().lock(); + + if (serviceInfos.containsKey(serviceName)) { + throw new ExternalServiceManagementException( + String.format("Failed to create External Service %s, it already exists!", serviceName)); + } + + logAccessor.writeWal( + new ServiceLogAccessor.ServiceWalEntry( + ServiceLogAccessor.OperationType.CREATE, serviceName, className)); + serviceInfos.put( + serviceName, + new ServiceInfo(serviceName, className, ServiceInfo.ServiceType.USER_DEFINED)); + } finally { + lock.writeLock().unlock(); + } + } + + public void startService(String serviceName) { + try { + lock.writeLock().lock(); + + ServiceInfo serviceInfo = serviceInfos.get(serviceName); + if (serviceInfo == null) { + throw new ExternalServiceManagementException( + String.format( + "Failed to stop External Service %s, because it is not existed!", serviceName)); + } + + if (serviceInfo.getState() == RUNNING) { + return; + } else { + // The state is STOPPED + if (serviceInfo.getServiceInstance() != null) { + serviceInfo.getServiceInstance().start(); + } else { + // lazy create Instance + serviceInfo.setServiceInstance(createExternalServiceInstance(serviceName)); + } + } + + logAccessor.writeWal( + new ServiceLogAccessor.ServiceWalEntry( + ServiceLogAccessor.OperationType.START, serviceName, null)); + serviceInfo.setState(RUNNING); + } finally { + lock.writeLock().unlock(); + } + } + + private IExternalService createExternalServiceInstance(String serviceName) { + // close ClassLoader automatically to release the file handle + try (ExternalServiceClassLoader classLoader = new ExternalServiceClassLoader(libRoot); ) { + return (IExternalService) + Class.forName(serviceName, true, classLoader).getDeclaredConstructor().newInstance(); + } catch (IOException + | InstantiationException + | InvocationTargetException + | NoSuchMethodException + | IllegalAccessException + | ClassNotFoundException + | ClassCastException e) { + String errorMessage = + String.format( + "Failed to start External Service %s, because its instance can not be constructed successfully. Exception: %s", + serviceName, e); + LOGGER.warn(errorMessage, e); + throw new ExternalServiceManagementException(errorMessage); + } + } + + public void stopService(String serviceName) { + try { + lock.writeLock().lock(); + + ServiceInfo serviceInfo = serviceInfos.get(serviceName); + if (serviceInfo == null) { + throw new ExternalServiceManagementException( + String.format( + "Failed to start External Service %s, because it is not existed!", serviceName)); + } + + if (serviceInfo.getState() == STOPPED) { + return; + } else { + // The state is RUNNING + stopService(serviceInfo); + } + + logAccessor.writeWal( + new ServiceLogAccessor.ServiceWalEntry( + ServiceLogAccessor.OperationType.STOP, serviceName, null)); + serviceInfo.setState(STOPPED); + } finally { + lock.writeLock().unlock(); + } + } + + private void stopService(ServiceInfo serviceInfo) { + checkState( + serviceInfo.getServiceInstance() != null, + "External Service instance is null when state is RUNNING!", + serviceInfo.getServiceName()); + serviceInfo.getServiceInstance().stop(); + } + + public void dropService(String serviceName, boolean forcedly) { + try { + lock.writeLock().lock(); + + ServiceInfo serviceInfo = serviceInfos.get(serviceName); + if (serviceInfo == null) { + throw new ExternalServiceManagementException( + String.format( + "Failed to drop External Service %s, because it is not existed!", serviceName)); + } + if (serviceInfo.getServiceType() == ServiceInfo.ServiceType.BUILTIN) { + throw new ExternalServiceManagementException( + String.format( + "Failed to drop External Service %s, because it is BUILT-IN!", serviceName)); + } + + if (serviceInfo.getState() == STOPPED) { + // do nothing + } else { + // The state is RUNNING + if (forcedly) { + try { + stopService(serviceInfo); + } catch (Exception e) { + // record errMsg if exception occurs during the stop of service + LOGGER.warn( + "Failed to stop External Service %s because %s. It will be drop forcedly", + serviceName, e.getMessage()); + } + } else { + throw new ExternalServiceManagementException( + String.format( + "Failed to drop External Service %s, because it is RUNNING!", serviceName)); + } + } + + logAccessor.writeWal( + new ServiceLogAccessor.ServiceWalEntry( + ServiceLogAccessor.OperationType.DROP, serviceName, null)); + serviceInfos.remove(serviceName); + } finally { + lock.writeLock().unlock(); + } + } + + public void start() throws StartupException { + lock.writeLock().lock(); + try { + restoreBuiltInServices(); + restoreUserDefinedServices(); + + // start services with RUNNING state + serviceInfos + .values() + .forEach( + serviceInfo -> { + if (serviceInfo.getState() == RUNNING) { + serviceInfo.setServiceInstance( + createExternalServiceInstance(serviceInfo.getServiceName())); + } + }); + } finally { + lock.writeLock().unlock(); + } + } + + private void restoreBuiltInServices() { + for (BuiltinExternalServices builtinExternalService : BuiltinExternalServices.values()) { + serviceInfos.put( + builtinExternalService.getServiceName(), + new ServiceInfo( + builtinExternalService.getServiceName(), + builtinExternalService.getClassName(), + ServiceInfo.ServiceType.BUILTIN, + builtinExternalService.isEnabled() ? RUNNING : STOPPED)); + } + } + + private void restoreUserDefinedServices() { + // TODO + } + + public static ExternalServiceManagementService getInstance() { + return ExternalServiceManagementServiceHolder.INSTANCE; + } + + private static class ExternalServiceManagementServiceHolder { + + private static final ExternalServiceManagementService INSTANCE = + new ExternalServiceManagementService( + IoTDBDescriptor.getInstance().getConfig().getExternalServiceDir()); + + private ExternalServiceManagementServiceHolder() {} + } +} diff --git a/iotdb-core/node-commons/pom.xml b/iotdb-core/node-commons/pom.xml index 0bf2c01bfb56..6e54c6b0b4cf 100644 --- a/iotdb-core/node-commons/pom.xml +++ b/iotdb-core/node-commons/pom.xml @@ -45,6 +45,11 @@ common ${tsfile.version} + + org.apache.iotdb + external-service-api + 2.0.7-SNAPSHOT + org.apache.iotdb udf-api diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/conf/IoTDBConstant.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/conf/IoTDBConstant.java index adf72842797e..741f65c3141e 100644 --- a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/conf/IoTDBConstant.java +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/conf/IoTDBConstant.java @@ -267,6 +267,7 @@ private IoTDBConstant() {} public static final String EXT_FOLDER_NAME = "ext"; public static final String UDF_FOLDER_NAME = "udf"; public static final String TRIGGER_FOLDER_NAME = "trigger"; + public static final String EXTERNAL_SERVICE_FOLDER_NAME = "external_service"; public static final String PIPE_FOLDER_NAME = "pipe"; public static final String TMP_FOLDER_NAME = "tmp"; public static final String DELETION_FOLDER_NAME = "deletion"; diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/externalservice/ServiceInfo.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/externalservice/ServiceInfo.java new file mode 100644 index 000000000000..c469d3835f96 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/externalservice/ServiceInfo.java @@ -0,0 +1,143 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.apache.iotdb.commons.externalservice; + +import org.apache.iotdb.externalservice.api.IExternalService; + +import com.google.common.base.Objects; +import org.apache.tsfile.utils.ReadWriteIOUtils; + +import java.io.DataOutputStream; +import java.io.IOException; +import java.nio.ByteBuffer; + +public class ServiceInfo { + private final String serviceName; + private final String className; + // This field needn't to serde, only USER_DEFINED service will be persisted on CN + private final transient ServiceType serviceType; + private State state; + + private transient IExternalService serviceInstance; + + public ServiceInfo(String serviceName, String className, ServiceType serviceType) { + this.serviceName = serviceName; + this.className = className; + this.serviceType = serviceType; + this.state = State.STOPPED; + } + + public ServiceInfo(String serviceName, String className, ServiceType serviceType, State state) { + this.serviceName = serviceName; + this.className = className; + this.serviceType = serviceType; + this.state = state; + } + + public String getClassName() { + return className; + } + + public State getState() { + return state; + } + + public void setState(State state) { + this.state = state; + } + + public ServiceType getServiceType() { + return serviceType; + } + + public String getServiceName() { + return serviceName; + } + + public IExternalService getServiceInstance() { + return serviceInstance; + } + + public void setServiceInstance(IExternalService serviceInstance) { + this.serviceInstance = serviceInstance; + } + + public void serialize(DataOutputStream stream) throws IOException { + ReadWriteIOUtils.write(serviceName, stream); + ReadWriteIOUtils.write(className, stream); + ReadWriteIOUtils.write(state.getValue(), stream); + } + + public static ServiceInfo deserialize(ByteBuffer buffer) { + return new ServiceInfo( + ReadWriteIOUtils.readString(buffer), + ReadWriteIOUtils.readString(buffer), + ServiceType.USER_DEFINED, + State.deserialize(ReadWriteIOUtils.readByte(buffer))); + } + + @Override + public boolean equals(Object o) { + if (o == null || getClass() != o.getClass()) { + return false; + } + ServiceInfo that = (ServiceInfo) o; + return Objects.equal(serviceName, that.serviceName) + && Objects.equal(className, that.className) + && serviceType == that.serviceType + && state == that.state; + } + + @Override + public int hashCode() { + return Objects.hashCode(serviceName, className, serviceType, state); + } + + public enum ServiceType { + BUILTIN, + USER_DEFINED + } + + public enum State { + RUNNING((byte) 0), + STOPPED((byte) 1); + + private final byte value; + + State(byte value) { + this.value = value; + } + + public byte getValue() { + return value; + } + + public static State deserialize(byte t) { + switch (t) { + case 0: + return RUNNING; + case 1: + return STOPPED; + default: + throw new IllegalArgumentException("Unknown State: " + t); + } + } + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/service/ServiceType.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/service/ServiceType.java index 7267c79a6655..492184d0df0b 100644 --- a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/service/ServiceType.java +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/service/ServiceType.java @@ -26,6 +26,7 @@ public enum ServiceType { RPC_SERVICE("RPC ServerService", "RPCService"), INFLUX_SERVICE("InfluxDB Protocol Service", "InfluxDB Protocol"), MQTT_SERVICE("MQTTService", "MqttService"), + EXTERNAL_SERVICE("ExternalService Service", "ExternalService"), AIR_GAP_SERVICE("AirGapService", "AirGapService"), MONITOR_SERVICE("Monitor ServerService", "Monitor"), STAT_MONITOR_SERVICE("Statistics ServerService", "StatMonitorService"), diff --git a/iotdb-protocol/thrift-confignode/src/main/thrift/confignode.thrift b/iotdb-protocol/thrift-confignode/src/main/thrift/confignode.thrift index 9680f11f138c..1866e2ea66a9 100644 --- a/iotdb-protocol/thrift-confignode/src/main/thrift/confignode.thrift +++ b/iotdb-protocol/thrift-confignode/src/main/thrift/confignode.thrift @@ -1089,6 +1089,25 @@ struct TShowCQResp { 2: required list cqList } +// ==================================================== +// ExternalService +// ==================================================== +struct TCreateExternalServiceReq { + 1: required i32 dataNodeId + 2: required string serviceName + 3: required string className +} + +struct TExternalServiceEntry { + 1: required string serviceName + 2: required string className + 3: required byte state +} + +struct TShowExternalServiceResp { + 1: required common.TSStatus status + 2: required list externalServiceInfos +} struct TDeactivateSchemaTemplateReq { 1: required string queryId @@ -1983,6 +2002,19 @@ service IConfigNodeRPCService { */ TShowCQResp showCQ() + // ==================================================== + // ExternalService + // ==================================================== + common.TSStatus createExternalService(TCreateExternalServiceReq req) + + common.TSStatus startExternalService(i32 dataNodeId, string serviceName) + + common.TSStatus stopExternalService(i32 dataNodeId, string serviceName) + + common.TSStatus dropExternalService(i32 dataNodeId, string serviceName) + + TShowCQResp showExternalService(i32 dataNodeId) + // ====================================================== // Quota // ====================================================== From 720c7c5b764912daf925f3ded4a6e1813907d49c Mon Sep 17 00:00:00 2001 From: Weihao Li <18110526956@163.com> Date: Fri, 9 Jan 2026 14:58:34 +0800 Subject: [PATCH 3/6] draft Signed-off-by: Weihao Li <18110526956@163.com> --- .../org/apache/iotdb/rpc/TSStatusCode.java | 4 +- .../iotdb/db/qp/sql/IdentifierParser.g4 | 2 + .../apache/iotdb/db/qp/sql/IoTDBSqlParser.g4 | 23 ++++ .../org/apache/iotdb/db/qp/sql/SqlLexer.g4 | 8 ++ .../client/async/CnToDnAsyncRequestType.java | 3 + ...oDnInternalServiceAsyncRequestManager.java | 5 + .../rpc/DataNodeAsyncRequestRPCHandler.java | 9 ++ .../GetBuiltInExternalServiceRPCHandler.java | 82 +++++++++++++ .../consensus/request/ConfigPhysicalPlan.java | 2 + .../ShowExternalServicePlan.java | 20 ++-- .../ShowExternalServiceResp.java | 10 +- .../confignode/manager/ConfigManager.java | 15 ++- .../iotdb/confignode/manager/IManager.java | 4 +- .../externalservice/ExternalServiceInfo.java | 109 ++++++++++++++++-- .../ExternalServiceManager.java | 65 ++++++++++- .../thrift/ConfigNodeRPCServiceProcessor.java | 27 +++++ .../db/protocol/client/ConfigNodeClient.java | 27 +++++ .../impl/DataNodeInternalRPCServiceImpl.java | 17 +++ .../db/queryengine/plan/Coordinator.java | 10 ++ .../executor/ClusterConfigTaskExecutor.java | 26 +++++ .../config/executor/IConfigTaskExecutor.java | 11 ++ .../metadata/CreateExternalServiceTask.java | 55 +++++++++ .../queryengine/plan/parser/ASTVisitor.java | 37 ++++++ .../security/TreeAccessCheckVisitor.java | 37 ++++++ .../plan/relational/sql/ast/AstVisitor.java | 20 ++++ .../sql/ast/CreateExternalService.java | 97 ++++++++++++++++ .../sql/ast/DropExternalService.java | 85 ++++++++++++++ .../sql/ast/ShowExternalService.java | 81 +++++++++++++ .../sql/ast/StartExternalService.java | 85 ++++++++++++++ .../sql/ast/StopExternalService.java | 85 ++++++++++++++ .../relational/sql/parser/AstBuilder.java | 40 +++++++ .../plan/statement/StatementType.java | 6 + .../plan/statement/StatementVisitor.java | 31 +++++ .../CreateExternalServiceStatement.java | 66 +++++++++++ .../DropExternalServiceStatement.java | 60 ++++++++++ .../ShowExternalServiceStatement.java | 60 ++++++++++ .../StartExternalServiceStatement.java | 60 ++++++++++ .../StopExternalServiceStatement.java | 60 ++++++++++ .../ExternalServiceManagementService.java | 26 ++--- .../relational/grammar/sql/RelationalSql.g4 | 30 +++++ .../src/main/thrift/common.thrift | 12 ++ .../src/main/thrift/confignode.thrift | 13 +-- .../src/main/thrift/datanode.thrift | 5 + 43 files changed, 1468 insertions(+), 62 deletions(-) create mode 100644 iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/client/async/handlers/rpc/GetBuiltInExternalServiceRPCHandler.java create mode 100644 iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/metadata/CreateExternalServiceTask.java create mode 100644 iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/sql/ast/CreateExternalService.java create mode 100644 iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/sql/ast/DropExternalService.java create mode 100644 iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/sql/ast/ShowExternalService.java create mode 100644 iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/sql/ast/StartExternalService.java create mode 100644 iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/sql/ast/StopExternalService.java create mode 100644 iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/statement/metadata/externalservice/CreateExternalServiceStatement.java create mode 100644 iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/statement/metadata/externalservice/DropExternalServiceStatement.java create mode 100644 iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/statement/metadata/externalservice/ShowExternalServiceStatement.java create mode 100644 iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/statement/metadata/externalservice/StartExternalServiceStatement.java create mode 100644 iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/statement/metadata/externalservice/StopExternalServiceStatement.java diff --git a/iotdb-client/service-rpc/src/main/java/org/apache/iotdb/rpc/TSStatusCode.java b/iotdb-client/service-rpc/src/main/java/org/apache/iotdb/rpc/TSStatusCode.java index 3f64722f32f1..f16e1526ece3 100644 --- a/iotdb-client/service-rpc/src/main/java/org/apache/iotdb/rpc/TSStatusCode.java +++ b/iotdb-client/service-rpc/src/main/java/org/apache/iotdb/rpc/TSStatusCode.java @@ -341,8 +341,8 @@ public enum TSStatusCode { // ExternalService NO_SUCH_EXTERNAL_SERVICE(2300), - EXTERNAL_SERVICE_ALREADY_ACTIVE(2301), - EXTERNAL_SERVICE_ALREADY_EXIST(2302), + EXTERNAL_SERVICE_ALREADY_EXIST(2301), + GET_BUILTIN_EXTERNAL_SERVICE_ERROR(2302), ; private final int statusCode; diff --git a/iotdb-core/antlr/src/main/antlr4/org/apache/iotdb/db/qp/sql/IdentifierParser.g4 b/iotdb-core/antlr/src/main/antlr4/org/apache/iotdb/db/qp/sql/IdentifierParser.g4 index 4a01b352384c..aa1cc62a97a7 100644 --- a/iotdb-core/antlr/src/main/antlr4/org/apache/iotdb/db/qp/sql/IdentifierParser.g4 +++ b/iotdb-core/antlr/src/main/antlr4/org/apache/iotdb/db/qp/sql/IdentifierParser.g4 @@ -214,6 +214,8 @@ keyWords | SECURITY | SELECT | SERIESSLOTID + | SERVICE + | SERVICES | SESSION | SET | SETTLE diff --git a/iotdb-core/antlr/src/main/antlr4/org/apache/iotdb/db/qp/sql/IoTDBSqlParser.g4 b/iotdb-core/antlr/src/main/antlr4/org/apache/iotdb/db/qp/sql/IoTDBSqlParser.g4 index 0d86a02a1cfc..6ac16cac74dc 100644 --- a/iotdb-core/antlr/src/main/antlr4/org/apache/iotdb/db/qp/sql/IoTDBSqlParser.g4 +++ b/iotdb-core/antlr/src/main/antlr4/org/apache/iotdb/db/qp/sql/IoTDBSqlParser.g4 @@ -52,6 +52,8 @@ ddlStatement | createFunction | dropFunction | showFunctions // Trigger | createTrigger | dropTrigger | showTriggers | startTrigger | stopTrigger + // ExternalService + | createService | startService | stopService | dropService | showService // Pipe Task | createPipe | alterPipe | dropPipe | startPipe | stopPipe | showPipes // Pipe Plugin @@ -437,6 +439,27 @@ stopTrigger : STOP TRIGGER triggerName=identifier ; +// ExternalService ========================================================================================= +createService + : CREATE SERVICE serviceName=identifier + AS className=STRING_LITERAL + ; + +startService + : START SERVICE serviceName=identifier + ; + +stopService + : STOP SERVICE serviceName=identifier + ; + +dropService + : DROP SERVICE serviceName=identifier + ; + +showService + : SHOW SERVICES (ON targetDataNodeId=INTEGER_LITERAL)? + ; // CQ ============================================================================================== // ---- Create Continuous Query diff --git a/iotdb-core/antlr/src/main/antlr4/org/apache/iotdb/db/qp/sql/SqlLexer.g4 b/iotdb-core/antlr/src/main/antlr4/org/apache/iotdb/db/qp/sql/SqlLexer.g4 index 85e039b1e5d8..fca8ffc1b420 100644 --- a/iotdb-core/antlr/src/main/antlr4/org/apache/iotdb/db/qp/sql/SqlLexer.g4 +++ b/iotdb-core/antlr/src/main/antlr4/org/apache/iotdb/db/qp/sql/SqlLexer.g4 @@ -786,6 +786,14 @@ SERIESSLOTID : S E R I E S S L O T I D ; +SERVICE + : S E R V I C E + ; + +SERVICES + : S E R V I C E S + ; + SESSION : S E S S I O N ; diff --git a/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/client/async/CnToDnAsyncRequestType.java b/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/client/async/CnToDnAsyncRequestType.java index c2f8b1e9d13c..e5753bf1bd18 100644 --- a/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/client/async/CnToDnAsyncRequestType.java +++ b/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/client/async/CnToDnAsyncRequestType.java @@ -60,6 +60,9 @@ public enum CnToDnAsyncRequestType { INACTIVE_TRIGGER_INSTANCE, UPDATE_TRIGGER_LOCATION, + // ExternalService + GET_BUILTIN_SERVICE, + // Pipe Plugin CREATE_PIPE_PLUGIN, DROP_PIPE_PLUGIN, diff --git a/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/client/async/CnToDnInternalServiceAsyncRequestManager.java b/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/client/async/CnToDnInternalServiceAsyncRequestManager.java index 9227325596d6..cd69f8b2c846 100644 --- a/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/client/async/CnToDnInternalServiceAsyncRequestManager.java +++ b/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/client/async/CnToDnInternalServiceAsyncRequestManager.java @@ -38,6 +38,7 @@ import org.apache.iotdb.confignode.client.async.handlers.rpc.DataNodeAsyncRequestRPCHandler; import org.apache.iotdb.confignode.client.async.handlers.rpc.DataNodeTSStatusRPCHandler; import org.apache.iotdb.confignode.client.async.handlers.rpc.FetchSchemaBlackListRPCHandler; +import org.apache.iotdb.confignode.client.async.handlers.rpc.GetBuiltInExternalServiceRPCHandler; import org.apache.iotdb.confignode.client.async.handlers.rpc.PipeHeartbeatRPCHandler; import org.apache.iotdb.confignode.client.async.handlers.rpc.PipePushMetaRPCHandler; import org.apache.iotdb.confignode.client.async.handlers.rpc.SchemaUpdateRPCHandler; @@ -486,6 +487,10 @@ protected void initActionMapBuilder() { CnToDnAsyncRequestType.ENABLE_SEPARATION_OF_ADMIN_POWERS, (req, client, handler) -> client.enableSeparationOfAdminPower((DataNodeTSStatusRPCHandler) handler)); + actionMapBuilder.put( + CnToDnAsyncRequestType.GET_BUILTIN_SERVICE, + (req, client, handler) -> + client.getBuiltInService((GetBuiltInExternalServiceRPCHandler) handler)); } @Override diff --git a/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/client/async/handlers/rpc/DataNodeAsyncRequestRPCHandler.java b/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/client/async/handlers/rpc/DataNodeAsyncRequestRPCHandler.java index 4e3fdb09f7ff..b2e2ec323278 100644 --- a/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/client/async/handlers/rpc/DataNodeAsyncRequestRPCHandler.java +++ b/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/client/async/handlers/rpc/DataNodeAsyncRequestRPCHandler.java @@ -20,6 +20,7 @@ package org.apache.iotdb.confignode.client.async.handlers.rpc; import org.apache.iotdb.common.rpc.thrift.TDataNodeLocation; +import org.apache.iotdb.common.rpc.thrift.TExternalServiceListResp; import org.apache.iotdb.common.rpc.thrift.TPipeHeartbeatResp; import org.apache.iotdb.common.rpc.thrift.TSStatus; import org.apache.iotdb.common.rpc.thrift.TTestConnectionResp; @@ -193,6 +194,14 @@ public static DataNodeAsyncRequestRPCHandler buildHandler( dataNodeLocationMap, (Map) responseMap, countDownLatch); + case GET_BUILTIN_SERVICE: + return new GetBuiltInExternalServiceRPCHandler( + requestType, + requestId, + targetDataNode, + dataNodeLocationMap, + (Map) responseMap, + countDownLatch); case SET_TTL: case CREATE_DATA_REGION: case CREATE_SCHEMA_REGION: diff --git a/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/client/async/handlers/rpc/GetBuiltInExternalServiceRPCHandler.java b/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/client/async/handlers/rpc/GetBuiltInExternalServiceRPCHandler.java new file mode 100644 index 000000000000..38b34fe6d493 --- /dev/null +++ b/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/client/async/handlers/rpc/GetBuiltInExternalServiceRPCHandler.java @@ -0,0 +1,82 @@ +/* + * 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.client.async.handlers.rpc; + +import org.apache.iotdb.common.rpc.thrift.TDataNodeLocation; +import org.apache.iotdb.common.rpc.thrift.TExternalServiceListResp; +import org.apache.iotdb.confignode.client.async.CnToDnAsyncRequestType; +import org.apache.iotdb.rpc.TSStatusCode; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.Map; +import java.util.concurrent.CountDownLatch; + +public class GetBuiltInExternalServiceRPCHandler + extends DataNodeAsyncRequestRPCHandler { + private static final Logger LOGGER = + LoggerFactory.getLogger(GetBuiltInExternalServiceRPCHandler.class); + + public GetBuiltInExternalServiceRPCHandler( + CnToDnAsyncRequestType requestType, + int requestId, + TDataNodeLocation targetDataNode, + Map dataNodeLocationMap, + Map responseMap, + CountDownLatch countDownLatch) { + super(requestType, requestId, targetDataNode, dataNodeLocationMap, responseMap, countDownLatch); + } + + @Override + public void onComplete(TExternalServiceListResp response) { + // Put response only when success + if (response.getStatus().getCode() == TSStatusCode.SUCCESS_STATUS.getStatusCode()) { + responseMap.put(requestId, response); + } else { + LOGGER.error( + "Failed to {} on DataNode: {}, response: {}", + requestType, + formattedTargetLocation, + response); + } + + // Always remove to avoid retrying + nodeLocationMap.remove(requestId); + + // Always CountDown + countDownLatch.countDown(); + } + + @Override + public void onError(Exception e) { + String errorMsg = + "Failed to " + + requestType + + " on DataNode: " + + formattedTargetLocation + + ", exception: " + + e.getMessage(); + LOGGER.error(errorMsg, e); + + // Always CountDown + countDownLatch.countDown(); + } +} diff --git a/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/consensus/request/ConfigPhysicalPlan.java b/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/consensus/request/ConfigPhysicalPlan.java index 4a822578dabb..7fd7cd029119 100644 --- a/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/consensus/request/ConfigPhysicalPlan.java +++ b/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/consensus/request/ConfigPhysicalPlan.java @@ -48,6 +48,8 @@ import org.apache.iotdb.confignode.consensus.request.write.datanode.UpdateDataNodePlan; import org.apache.iotdb.confignode.consensus.request.write.externalservice.CreateExternalServicePlan; import org.apache.iotdb.confignode.consensus.request.write.externalservice.DropExternalServicePlan; +import org.apache.iotdb.confignode.consensus.request.write.externalservice.StartExternalServicePlan; +import org.apache.iotdb.confignode.consensus.request.write.externalservice.StopExternalServicePlan; import org.apache.iotdb.confignode.consensus.request.write.function.CreateFunctionPlan; import org.apache.iotdb.confignode.consensus.request.write.function.DropTableModelFunctionPlan; import org.apache.iotdb.confignode.consensus.request.write.function.DropTreeModelFunctionPlan; diff --git a/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/consensus/request/read/exernalservice/ShowExternalServicePlan.java b/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/consensus/request/read/exernalservice/ShowExternalServicePlan.java index 72986c1eb81d..b9910c27202f 100644 --- a/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/consensus/request/read/exernalservice/ShowExternalServicePlan.java +++ b/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/consensus/request/read/exernalservice/ShowExternalServicePlan.java @@ -22,23 +22,21 @@ import org.apache.iotdb.confignode.consensus.request.ConfigPhysicalPlanType; import org.apache.iotdb.confignode.consensus.request.read.ConfigPhysicalReadPlan; +import java.util.List; import java.util.Objects; -/** - * Get infos of ExternalService by the specific DataNode's id. And return all when dataNodeID is set - * to -1. - */ +/** Get infos of ExternalService by the DataNode's id. */ public class ShowExternalServicePlan extends ConfigPhysicalReadPlan { - private final int dataNodeId; + private final List dataNodeIds; - public ShowExternalServicePlan(final int dataNodeId) { + public ShowExternalServicePlan(List dataNodeIds) { super(ConfigPhysicalPlanType.GetDataNodeConfiguration); - this.dataNodeId = dataNodeId; + this.dataNodeIds = dataNodeIds; } - public Integer getDataNodeId() { - return dataNodeId; + public List getDataNodeIds() { + return dataNodeIds; } @Override @@ -50,11 +48,11 @@ public boolean equals(final Object o) { return false; } final ShowExternalServicePlan that = (ShowExternalServicePlan) o; - return dataNodeId == that.dataNodeId; + return dataNodeIds.equals(that.dataNodeIds); } @Override public int hashCode() { - return Objects.hash(dataNodeId); + return Objects.hash(dataNodeIds); } } diff --git a/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/consensus/response/externalservice/ShowExternalServiceResp.java b/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/consensus/response/externalservice/ShowExternalServiceResp.java index d6020ac72ba8..ce06adf3b7f7 100644 --- a/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/consensus/response/externalservice/ShowExternalServiceResp.java +++ b/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/consensus/response/externalservice/ShowExternalServiceResp.java @@ -19,15 +19,14 @@ package org.apache.iotdb.confignode.consensus.response.externalservice; +import org.apache.iotdb.common.rpc.thrift.TExternalServiceEntry; +import org.apache.iotdb.common.rpc.thrift.TExternalServiceListResp; import org.apache.iotdb.common.rpc.thrift.TSStatus; import org.apache.iotdb.commons.externalservice.ServiceInfo; -import org.apache.iotdb.confignode.rpc.thrift.TExternalServiceEntry; -import org.apache.iotdb.confignode.rpc.thrift.TShowExternalServiceResp; import org.apache.iotdb.consensus.common.DataSet; import javax.validation.constraints.NotNull; -import java.util.Comparator; import java.util.List; import java.util.stream.Collectors; @@ -42,15 +41,14 @@ public ShowExternalServiceResp( this.serviceInfos = serviceInfos; } - public TShowExternalServiceResp convertToRpcShowExternalServiceResp() { - return new TShowExternalServiceResp( + public TExternalServiceListResp convertToRpcShowExternalServiceResp() { + return new TExternalServiceListResp( status, serviceInfos.stream() .map( entry -> new TExternalServiceEntry( entry.getServiceName(), entry.getClassName(), entry.getState().getValue())) - .sorted(Comparator.comparing(entry -> entry.serviceName)) .collect(Collectors.toList())); } } diff --git a/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/manager/ConfigManager.java b/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/manager/ConfigManager.java index f3740ec81a1f..17a4eedb7f8a 100644 --- a/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/manager/ConfigManager.java +++ b/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/manager/ConfigManager.java @@ -25,6 +25,7 @@ import org.apache.iotdb.common.rpc.thrift.TConsensusGroupId; import org.apache.iotdb.common.rpc.thrift.TDataNodeConfiguration; import org.apache.iotdb.common.rpc.thrift.TDataNodeLocation; +import org.apache.iotdb.common.rpc.thrift.TExternalServiceListResp; import org.apache.iotdb.common.rpc.thrift.TFlushReq; import org.apache.iotdb.common.rpc.thrift.TPipeHeartbeatResp; import org.apache.iotdb.common.rpc.thrift.TRegionReplicaSet; @@ -227,7 +228,6 @@ import org.apache.iotdb.confignode.rpc.thrift.TShowDataNodes4InformationSchemaResp; import org.apache.iotdb.confignode.rpc.thrift.TShowDataNodesResp; import org.apache.iotdb.confignode.rpc.thrift.TShowDatabaseResp; -import org.apache.iotdb.confignode.rpc.thrift.TShowExternalServiceResp; import org.apache.iotdb.confignode.rpc.thrift.TShowPipePluginReq; import org.apache.iotdb.confignode.rpc.thrift.TShowPipeReq; import org.apache.iotdb.confignode.rpc.thrift.TShowPipeResp; @@ -1858,6 +1858,15 @@ public TGetDataNodeLocationsResp getReadableDataNodeLocations() { : new TGetDataNodeLocationsResp(status, Collections.emptyList()); } + public Map getReadableDataNodeLocationMap() { + TSStatus status = confirmLeader(); + return status.getCode() == TSStatusCode.SUCCESS_STATUS.getStatusCode() + ? nodeManager.filterDataNodeThroughStatus(NodeStatus::isReadable).stream() + .map(TDataNodeConfiguration::getLocation) + .collect(Collectors.toMap(TDataNodeLocation::getDataNodeId, location -> location)) + : Collections.emptyMap(); + } + @Override public TRegionRouteMapResp getLatestRegionRouteMap() { final long retryIntervalInMS = 100; @@ -2640,11 +2649,11 @@ public TSStatus dropExternalService(int dataNodeId, String serviceName) { } @Override - public TShowExternalServiceResp showExternalService(int dataNodeId) { + public TExternalServiceListResp showExternalService(int dataNodeId) { TSStatus status = confirmLeader(); return status.getCode() == TSStatusCode.SUCCESS_STATUS.getStatusCode() ? externalServiceManager.showService(dataNodeId) - : new TShowExternalServiceResp(status, Collections.emptyList()); + : new TExternalServiceListResp(status, Collections.emptyList()); } /** diff --git a/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/manager/IManager.java b/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/manager/IManager.java index 74d0fb509372..f083fd9b966c 100644 --- a/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/manager/IManager.java +++ b/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/manager/IManager.java @@ -22,6 +22,7 @@ import org.apache.iotdb.common.rpc.thrift.TConfigNodeLocation; import org.apache.iotdb.common.rpc.thrift.TConsensusGroupId; import org.apache.iotdb.common.rpc.thrift.TDataNodeLocation; +import org.apache.iotdb.common.rpc.thrift.TExternalServiceListResp; import org.apache.iotdb.common.rpc.thrift.TFlushReq; import org.apache.iotdb.common.rpc.thrift.TPipeHeartbeatResp; import org.apache.iotdb.common.rpc.thrift.TSStatus; @@ -145,7 +146,6 @@ import org.apache.iotdb.confignode.rpc.thrift.TShowDataNodes4InformationSchemaResp; import org.apache.iotdb.confignode.rpc.thrift.TShowDataNodesResp; import org.apache.iotdb.confignode.rpc.thrift.TShowDatabaseResp; -import org.apache.iotdb.confignode.rpc.thrift.TShowExternalServiceResp; import org.apache.iotdb.confignode.rpc.thrift.TShowPipePluginReq; import org.apache.iotdb.confignode.rpc.thrift.TShowPipeReq; import org.apache.iotdb.confignode.rpc.thrift.TShowPipeResp; @@ -872,7 +872,7 @@ TDataPartitionTableResp getOrCreateDataPartition( TSStatus dropExternalService(int dataNodeId, String serviceName); - TShowExternalServiceResp showExternalService(int dataNodeId); + TExternalServiceListResp showExternalService(int dataNodeId); TSStatus checkConfigNodeGlobalConfig(TConfigNodeRegisterReq req); diff --git a/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/manager/externalservice/ExternalServiceInfo.java b/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/manager/externalservice/ExternalServiceInfo.java index 104ec6eb88db..122156f41fd0 100644 --- a/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/manager/externalservice/ExternalServiceInfo.java +++ b/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/manager/externalservice/ExternalServiceInfo.java @@ -23,6 +23,10 @@ import org.apache.iotdb.commons.externalservice.ServiceInfo; import org.apache.iotdb.commons.snapshot.SnapshotProcessor; import org.apache.iotdb.confignode.consensus.request.write.externalservice.CreateExternalServicePlan; +import org.apache.iotdb.confignode.consensus.request.write.externalservice.DropExternalServicePlan; +import org.apache.iotdb.confignode.consensus.request.write.externalservice.StartExternalServicePlan; +import org.apache.iotdb.confignode.consensus.request.write.externalservice.StopExternalServicePlan; +import org.apache.iotdb.confignode.consensus.response.externalservice.ShowExternalServiceResp; import org.apache.iotdb.rpc.TSStatusCode; import org.slf4j.Logger; @@ -32,9 +36,11 @@ import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; -import java.util.HashMap; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; +import java.util.stream.Collectors; + +import static com.google.common.base.Preconditions.checkState; public class ExternalServiceInfo implements SnapshotProcessor { @@ -49,22 +55,22 @@ public ExternalServiceInfo() { } /** - * Add a new ExternalService only if there was no mapping for this service on target - * DataNode, otherwise ignore this operation. + * Add a new ExternalService on target DataNode. * - * @return SUCCESS_STATUS if there was no mapping for this service on target DataNode, - * otherwise EXTERNAL_SERVICE_AlREADY_EXIST + * @return SUCCESS_STATUS if this service was not existed on target DataNode, otherwise + * EXTERNAL_SERVICE_AlREADY_EXIST */ public TSStatus addService(CreateExternalServicePlan plan) { TSStatus res = new TSStatus(); Map serviceInfos = - datanodeToServiceInfos.computeIfAbsent(plan.getDatanodeId(), k -> new HashMap<>()); + datanodeToServiceInfos.computeIfAbsent( + plan.getDatanodeId(), k -> new ConcurrentHashMap<>()); String serviceName = plan.getServiceInfo().getServiceName(); if (serviceInfos.containsKey(serviceName)) { res.code = TSStatusCode.EXTERNAL_SERVICE_ALREADY_EXIST.getStatusCode(); res.message = String.format( - "ExternalService %s has already been created on DataNode .", + "ExternalService %s has already been created on DataNode %s.", serviceName, plan.getDatanodeId()); } else { serviceInfos.put(serviceName, plan.getServiceInfo()); @@ -73,6 +79,95 @@ public TSStatus addService(CreateExternalServicePlan plan) { return res; } + /** + * Drop the ExternalService whose name is same as serviceName in plan. + * + * @return SUCCESS_STATUS if this service was existed on target DataNode, otherwise + * NO_SUCH_EXTERNAL_SERVICE + */ + public TSStatus dropService(DropExternalServicePlan plan) { + TSStatus res = new TSStatus(); + Map serviceInfos = + datanodeToServiceInfos.computeIfAbsent( + plan.getDataNodeId(), k -> new ConcurrentHashMap<>()); + String serviceName = plan.getServiceName(); + if (!serviceInfos.containsKey(serviceName)) { + res.code = TSStatusCode.NO_SUCH_EXTERNAL_SERVICE.getStatusCode(); + res.message = + String.format( + "ExternalService %s is not existed on DataNode %s.", + serviceName, plan.getDataNodeId()); + } else { + serviceInfos.remove(serviceName); + res.code = TSStatusCode.SUCCESS_STATUS.getStatusCode(); + } + return res; + } + + /** + * Start the ExternalService whose name is same as serviceName in plan. + * + * @return SUCCESS_STATUS if this service was existed on target DataNode, otherwise + * NO_SUCH_EXTERNAL_SERVICE + */ + public TSStatus startService(StartExternalServicePlan plan) { + TSStatus res = new TSStatus(); + Map serviceInfos = + datanodeToServiceInfos.computeIfAbsent( + plan.getDataNodeId(), k -> new ConcurrentHashMap<>()); + String serviceName = plan.getServiceName(); + if (!serviceInfos.containsKey(serviceName)) { + res.code = TSStatusCode.NO_SUCH_EXTERNAL_SERVICE.getStatusCode(); + res.message = + String.format( + "ExternalService %s is not existed on DataNode %s.", + serviceName, plan.getDataNodeId()); + } else { + ServiceInfo serviceInfo = serviceInfos.get(serviceName); + // The WRITE operations of StateMachine are not concurrent + checkState(serviceInfo != null, "Target serviceInfo should not be null."); + serviceInfo.setState(ServiceInfo.State.RUNNING); + res.code = TSStatusCode.SUCCESS_STATUS.getStatusCode(); + } + return res; + } + + /** + * Stop the ExternalService whose name is same as serviceName in plan. + * + * @return SUCCESS_STATUS if this service was existed on target DataNode, otherwise + * NO_SUCH_EXTERNAL_SERVICE + */ + public TSStatus stopService(StopExternalServicePlan plan) { + TSStatus res = new TSStatus(); + Map serviceInfos = + datanodeToServiceInfos.computeIfAbsent( + plan.getDataNodeId(), k -> new ConcurrentHashMap<>()); + String serviceName = plan.getServiceName(); + if (!serviceInfos.containsKey(serviceName)) { + res.code = TSStatusCode.NO_SUCH_EXTERNAL_SERVICE.getStatusCode(); + res.message = + String.format( + "ExternalService %s is not existed on DataNode %s.", + serviceName, plan.getDataNodeId()); + } else { + ServiceInfo serviceInfo = serviceInfos.get(serviceName); + // The WRITE operations of StateMachine are not concurrent + checkState(serviceInfo != null, "Target serviceInfo should not be null."); + serviceInfo.setState(ServiceInfo.State.STOPPED); + res.code = TSStatusCode.SUCCESS_STATUS.getStatusCode(); + } + return res; + } + + public ShowExternalServiceResp showService() { + return new ShowExternalServiceResp( + new TSStatus(TSStatusCode.SUCCESS_STATUS.getStatusCode()), + datanodeToServiceInfos.values().stream() + .flatMap(stringServiceInfoMap -> stringServiceInfoMap.values().stream()) + .collect(Collectors.toList())); + } + @Override public boolean processTakeSnapshot(File snapshotDir) throws IOException { File snapshotFile = new File(snapshotDir, SNAPSHOT_FILENAME); diff --git a/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/manager/externalservice/ExternalServiceManager.java b/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/manager/externalservice/ExternalServiceManager.java index 1e6d2c16e9b4..63e0a088f085 100644 --- a/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/manager/externalservice/ExternalServiceManager.java +++ b/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/manager/externalservice/ExternalServiceManager.java @@ -19,8 +19,13 @@ package org.apache.iotdb.confignode.manager.externalservice; +import org.apache.iotdb.common.rpc.thrift.TDataNodeLocation; +import org.apache.iotdb.common.rpc.thrift.TExternalServiceListResp; import org.apache.iotdb.common.rpc.thrift.TSStatus; import org.apache.iotdb.commons.externalservice.ServiceInfo; +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.consensus.request.read.exernalservice.ShowExternalServicePlan; import org.apache.iotdb.confignode.consensus.request.write.externalservice.CreateExternalServicePlan; import org.apache.iotdb.confignode.consensus.request.write.externalservice.DropExternalServicePlan; @@ -29,15 +34,17 @@ import org.apache.iotdb.confignode.consensus.response.externalservice.ShowExternalServiceResp; import org.apache.iotdb.confignode.manager.ConfigManager; import org.apache.iotdb.confignode.rpc.thrift.TCreateExternalServiceReq; -import org.apache.iotdb.confignode.rpc.thrift.TShowExternalServiceResp; import org.apache.iotdb.consensus.common.DataSet; import org.apache.iotdb.consensus.exception.ConsensusException; +import org.apache.iotdb.mpp.rpc.thrift.TCreateFunctionInstanceReq; 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.Map; public class ExternalServiceManager { @@ -127,17 +134,65 @@ public TSStatus dropService(int dataNodeId, String serviceName) { } } - public TShowExternalServiceResp showService(int dataNodeId) { + public TExternalServiceListResp showService(int dataNodeId) { + Map targetDataNodes = + configManager.getReadableDataNodeLocationMap(); + + if (targetDataNodes.isEmpty()) { + // no readable DN, return directly + return new TExternalServiceListResp( + new TSStatus(TSStatusCode.SUCCESS_STATUS.getStatusCode()), Collections.emptyList()); + } + + if (dataNodeId != -1) { + if (!targetDataNodes.containsKey(dataNodeId)) { + // target DN is not readable, return directly + return new TExternalServiceListResp( + new TSStatus(TSStatusCode.SUCCESS_STATUS.getStatusCode()), Collections.emptyList()); + } else { + targetDataNodes = Collections.singletonMap(dataNodeId, targetDataNodes.get(dataNodeId)); + } + } + + // 1. get built-in services info from DN + Map builtInServiceInfos = + getBuiltInServiceInfosFromDataNodes(targetDataNodes); + if (builtInServiceInfos.isEmpty()) { + return new TExternalServiceListResp( + new TSStatus(TSStatusCode.SUCCESS_STATUS.getStatusCode()), Collections.emptyList()); + } + try { + // 2. get user-defined services info from CN consensus DataSet response = - configManager.getConsensusManager().read(new ShowExternalServicePlan(dataNodeId)); - return ((ShowExternalServiceResp) response).convertToRpcShowExternalServiceResp(); + configManager + .getConsensusManager() + .read(new ShowExternalServicePlan(new ArrayList<>(builtInServiceInfos.keySet()))); + TExternalServiceListResp resp = + ((ShowExternalServiceResp) response).convertToRpcShowExternalServiceResp(); + + // 3. combined built-in services info and user-defined services info + builtInServiceInfos + .values() + .forEach( + builtInResp -> + resp.externalServiceInfos.addAll(builtInResp.getExternalServiceInfos())); + return resp; } catch (ConsensusException e) { LOGGER.warn("Unexpected error happened while showing Service: ", e); // consensus layer related errors TSStatus res = new TSStatus(TSStatusCode.EXECUTE_STATEMENT_ERROR.getStatusCode()); res.setMessage(e.getMessage()); - return new TShowExternalServiceResp(res, Collections.emptyList()); + return new TExternalServiceListResp(res, Collections.emptyList()); } } + + private Map getBuiltInServiceInfosFromDataNodes( + Map dataNodeLocationMap) { + DataNodeAsyncRequestContext context = + new DataNodeAsyncRequestContext<>( + CnToDnAsyncRequestType.GET_BUILTIN_SERVICE, dataNodeLocationMap); + CnToDnInternalServiceAsyncRequestManager.getInstance().sendAsyncRequestWithRetry(context); + return context.getResponseMap(); + } } diff --git a/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/service/thrift/ConfigNodeRPCServiceProcessor.java b/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/service/thrift/ConfigNodeRPCServiceProcessor.java index afd399951da8..26fed0e1c4a3 100644 --- a/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/service/thrift/ConfigNodeRPCServiceProcessor.java +++ b/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/service/thrift/ConfigNodeRPCServiceProcessor.java @@ -23,6 +23,7 @@ import org.apache.iotdb.common.rpc.thrift.TAINodeLocation; import org.apache.iotdb.common.rpc.thrift.TConfigNodeLocation; import org.apache.iotdb.common.rpc.thrift.TDataNodeLocation; +import org.apache.iotdb.common.rpc.thrift.TExternalServiceListResp; import org.apache.iotdb.common.rpc.thrift.TFlushReq; import org.apache.iotdb.common.rpc.thrift.TNodeLocations; import org.apache.iotdb.common.rpc.thrift.TPipeHeartbeatResp; @@ -115,6 +116,7 @@ import org.apache.iotdb.confignode.rpc.thrift.TCountTimeSlotListResp; import org.apache.iotdb.confignode.rpc.thrift.TCreateCQReq; import org.apache.iotdb.confignode.rpc.thrift.TCreateConsumerReq; +import org.apache.iotdb.confignode.rpc.thrift.TCreateExternalServiceReq; import org.apache.iotdb.confignode.rpc.thrift.TCreateFunctionReq; import org.apache.iotdb.confignode.rpc.thrift.TCreatePipePluginReq; import org.apache.iotdb.confignode.rpc.thrift.TCreatePipeReq; @@ -1363,6 +1365,31 @@ public TShowCQResp showCQ() { return configManager.showCQ(); } + @Override + public TSStatus createExternalService(TCreateExternalServiceReq req) { + return configManager.createExternalService(req); + } + + @Override + public TSStatus startExternalService(int dataNodeId, String serviceName) { + return configManager.startExternalService(dataNodeId, serviceName); + } + + @Override + public TSStatus stopExternalService(int dataNodeId, String serviceName) { + return configManager.stopExternalService(dataNodeId, serviceName); + } + + @Override + public TSStatus dropExternalService(int dataNodeId, String serviceName) { + return configManager.dropExternalService(dataNodeId, serviceName); + } + + @Override + public TExternalServiceListResp showExternalService(int dataNodeId) { + return configManager.showExternalService(dataNodeId); + } + @Override public TSStatus setSpaceQuota(final TSetSpaceQuotaReq req) throws TException { return configManager.setSpaceQuota(req); diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/protocol/client/ConfigNodeClient.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/protocol/client/ConfigNodeClient.java index 114629c0ed70..f2fb42ca97fb 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/protocol/client/ConfigNodeClient.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/protocol/client/ConfigNodeClient.java @@ -22,6 +22,7 @@ import org.apache.iotdb.common.rpc.thrift.TConfigNodeLocation; import org.apache.iotdb.common.rpc.thrift.TDataNodeLocation; import org.apache.iotdb.common.rpc.thrift.TEndPoint; +import org.apache.iotdb.common.rpc.thrift.TExternalServiceListResp; import org.apache.iotdb.common.rpc.thrift.TFlushReq; import org.apache.iotdb.common.rpc.thrift.TNodeLocations; import org.apache.iotdb.common.rpc.thrift.TPipeHeartbeatResp; @@ -73,6 +74,7 @@ import org.apache.iotdb.confignode.rpc.thrift.TCountTimeSlotListResp; import org.apache.iotdb.confignode.rpc.thrift.TCreateCQReq; import org.apache.iotdb.confignode.rpc.thrift.TCreateConsumerReq; +import org.apache.iotdb.confignode.rpc.thrift.TCreateExternalServiceReq; import org.apache.iotdb.confignode.rpc.thrift.TCreateFunctionReq; import org.apache.iotdb.confignode.rpc.thrift.TCreatePipePluginReq; import org.apache.iotdb.confignode.rpc.thrift.TCreatePipeReq; @@ -1342,6 +1344,31 @@ public TShowCQResp showCQ() throws TException { () -> client.showCQ(), resp -> !updateConfigNodeLeader(resp.status)); } + @Override + public TSStatus createExternalService(TCreateExternalServiceReq req) throws TException { + return null; + } + + @Override + public TSStatus startExternalService(int dataNodeId, String serviceName) throws TException { + return null; + } + + @Override + public TSStatus stopExternalService(int dataNodeId, String serviceName) throws TException { + return null; + } + + @Override + public TSStatus dropExternalService(int dataNodeId, String serviceName) throws TException { + return null; + } + + @Override + public TExternalServiceListResp showExternalService(int dataNodeId) throws TException { + return null; + } + @Override public TSStatus setSpaceQuota(TSetSpaceQuotaReq req) throws TException { return executeRemoteCallWithRetry( diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/protocol/thrift/impl/DataNodeInternalRPCServiceImpl.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/protocol/thrift/impl/DataNodeInternalRPCServiceImpl.java index acdef794fa39..903167cffe96 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/protocol/thrift/impl/DataNodeInternalRPCServiceImpl.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/protocol/thrift/impl/DataNodeInternalRPCServiceImpl.java @@ -23,6 +23,7 @@ import org.apache.iotdb.common.rpc.thrift.TConsensusGroupId; import org.apache.iotdb.common.rpc.thrift.TDataNodeLocation; import org.apache.iotdb.common.rpc.thrift.TEndPoint; +import org.apache.iotdb.common.rpc.thrift.TExternalServiceListResp; import org.apache.iotdb.common.rpc.thrift.TFlushReq; import org.apache.iotdb.common.rpc.thrift.TLoadSample; import org.apache.iotdb.common.rpc.thrift.TNodeLocations; @@ -2935,6 +2936,22 @@ public TSStatus dropPipePlugin(TDropPipePluginInstanceReq req) { } } + @Override + public TExternalServiceListResp getBuiltInService() { + try { + + // PipeDataNodeAgent.plugin().deregister(req.getPipePluginName(), req.isNeedToDeleteJar()); + // TODO + return new TExternalServiceListResp( + new TSStatus(TSStatusCode.SUCCESS_STATUS.getStatusCode()), null); + } catch (Exception e) { + return new TExternalServiceListResp( + new TSStatus(TSStatusCode.GET_BUILTIN_EXTERNAL_SERVICE_ERROR.getStatusCode()) + .setMessage(e.getMessage()), + Collections.emptyList()); + } + } + private boolean isSucceed(TSStatus status) { return status.getCode() == TSStatusCode.SUCCESS_STATUS.getStatusCode(); } diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/Coordinator.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/Coordinator.java index 29ae1ed87e7e..cf1a342e27a0 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/Coordinator.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/Coordinator.java @@ -69,6 +69,7 @@ import org.apache.iotdb.db.queryengine.plan.relational.sql.ast.AlterDB; import org.apache.iotdb.db.queryengine.plan.relational.sql.ast.ClearCache; import org.apache.iotdb.db.queryengine.plan.relational.sql.ast.CreateDB; +import org.apache.iotdb.db.queryengine.plan.relational.sql.ast.CreateExternalService; import org.apache.iotdb.db.queryengine.plan.relational.sql.ast.CreateFunction; import org.apache.iotdb.db.queryengine.plan.relational.sql.ast.CreateModel; import org.apache.iotdb.db.queryengine.plan.relational.sql.ast.CreateTable; @@ -78,6 +79,7 @@ import org.apache.iotdb.db.queryengine.plan.relational.sql.ast.DescribeTable; import org.apache.iotdb.db.queryengine.plan.relational.sql.ast.DropColumn; import org.apache.iotdb.db.queryengine.plan.relational.sql.ast.DropDB; +import org.apache.iotdb.db.queryengine.plan.relational.sql.ast.DropExternalService; import org.apache.iotdb.db.queryengine.plan.relational.sql.ast.DropFunction; import org.apache.iotdb.db.queryengine.plan.relational.sql.ast.DropModel; import org.apache.iotdb.db.queryengine.plan.relational.sql.ast.DropTable; @@ -121,6 +123,7 @@ import org.apache.iotdb.db.queryengine.plan.relational.sql.ast.ShowCurrentUser; import org.apache.iotdb.db.queryengine.plan.relational.sql.ast.ShowDB; import org.apache.iotdb.db.queryengine.plan.relational.sql.ast.ShowDataNodes; +import org.apache.iotdb.db.queryengine.plan.relational.sql.ast.ShowExternalService; import org.apache.iotdb.db.queryengine.plan.relational.sql.ast.ShowFunctions; import org.apache.iotdb.db.queryengine.plan.relational.sql.ast.ShowLoadedModels; import org.apache.iotdb.db.queryengine.plan.relational.sql.ast.ShowModels; @@ -128,7 +131,9 @@ import org.apache.iotdb.db.queryengine.plan.relational.sql.ast.ShowTables; import org.apache.iotdb.db.queryengine.plan.relational.sql.ast.ShowVariables; import org.apache.iotdb.db.queryengine.plan.relational.sql.ast.ShowVersion; +import org.apache.iotdb.db.queryengine.plan.relational.sql.ast.StartExternalService; import org.apache.iotdb.db.queryengine.plan.relational.sql.ast.StartRepairData; +import org.apache.iotdb.db.queryengine.plan.relational.sql.ast.StopExternalService; import org.apache.iotdb.db.queryengine.plan.relational.sql.ast.StopRepairData; import org.apache.iotdb.db.queryengine.plan.relational.sql.ast.SubscriptionStatement; import org.apache.iotdb.db.queryengine.plan.relational.sql.ast.UnloadModel; @@ -534,6 +539,11 @@ private IQueryExecution createQueryExecutionForTableModel( || statement instanceof CreateFunction || statement instanceof DropFunction || statement instanceof ShowFunctions + || statement instanceof CreateExternalService + || statement instanceof StartExternalService + || statement instanceof StopExternalService + || statement instanceof DropExternalService + || statement instanceof ShowExternalService || statement instanceof RelationalAuthorStatement || statement instanceof MigrateRegion || statement instanceof ReconstructRegion diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/executor/ClusterConfigTaskExecutor.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/executor/ClusterConfigTaskExecutor.java index 4675c9b9e009..10b84690a7a2 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/executor/ClusterConfigTaskExecutor.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/executor/ClusterConfigTaskExecutor.java @@ -4839,4 +4839,30 @@ public void handlePipeConfigClientExit(final String clientId) { LOGGER.warn("Failed to handlePipeConfigClientExit.", e); } } + + @Override + public SettableFuture createExternalService( + int dataNodeId, String serviceName, String className) { + return null; + } + + @Override + public SettableFuture startExternalService(String serviceName) { + return null; + } + + @Override + public SettableFuture stopExternalService(String serviceName) { + return null; + } + + @Override + public SettableFuture dropExternalService(String serviceName) { + return null; + } + + @Override + public SettableFuture showExternalService(int dataNodeId) { + return null; + } } diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/executor/IConfigTaskExecutor.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/executor/IConfigTaskExecutor.java index 4cb28ee6ef69..c5720d448b13 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/executor/IConfigTaskExecutor.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/executor/IConfigTaskExecutor.java @@ -315,6 +315,17 @@ SettableFuture showThrottleQuota( void handlePipeConfigClientExit(String clientId); + SettableFuture createExternalService( + int dataNodeId, String serviceName, String className); + + SettableFuture startExternalService(String serviceName); + + SettableFuture stopExternalService(String serviceName); + + SettableFuture dropExternalService(String serviceName); + + SettableFuture showExternalService(int dataNodeId); + // =============================== table syntax ========================================= SettableFuture showDatabases( diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/metadata/CreateExternalServiceTask.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/metadata/CreateExternalServiceTask.java new file mode 100644 index 000000000000..2a0ee442f716 --- /dev/null +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/metadata/CreateExternalServiceTask.java @@ -0,0 +1,55 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.apache.iotdb.db.queryengine.plan.execution.config.metadata; + +import org.apache.iotdb.db.queryengine.common.QueryId; +import org.apache.iotdb.db.queryengine.plan.execution.config.ConfigTaskResult; +import org.apache.iotdb.db.queryengine.plan.execution.config.IConfigTask; +import org.apache.iotdb.db.queryengine.plan.execution.config.executor.IConfigTaskExecutor; +import org.apache.iotdb.db.queryengine.plan.relational.sql.ast.CreateFunction; +import org.apache.iotdb.db.queryengine.plan.statement.metadata.externalservice.CreateExternalServiceStatement; + +import com.google.common.util.concurrent.ListenableFuture; + +public class CreateExternalServiceTask implements IConfigTask { + + private final String serviceName; + private final String className; + + private final int dataNodeId; + + public CreateExternalServiceTask(CreateExternalServiceStatement statement) { + this.serviceName = statement.getServiceName(); + this.className = statement.getClassName(); + this.dataNodeId = QueryId.getDataNodeId(); + } + + public CreateExternalServiceTask(CreateFunction createFunctionStatement) { + this.serviceName = createFunctionStatement.getUdfName(); + this.className = createFunctionStatement.getClassName(); + this.dataNodeId = QueryId.getDataNodeId(); + } + + @Override + public ListenableFuture execute(IConfigTaskExecutor configTaskExecutor) + throws InterruptedException { + return configTaskExecutor.createExternalService(dataNodeId, serviceName, className); + } +} diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/parser/ASTVisitor.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/parser/ASTVisitor.java index 0a95f1afe209..93fc90339db3 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/parser/ASTVisitor.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/parser/ASTVisitor.java @@ -187,6 +187,11 @@ import org.apache.iotdb.db.queryengine.plan.statement.metadata.ShowTriggersStatement; import org.apache.iotdb.db.queryengine.plan.statement.metadata.ShowVariablesStatement; import org.apache.iotdb.db.queryengine.plan.statement.metadata.UnSetTTLStatement; +import org.apache.iotdb.db.queryengine.plan.statement.metadata.externalservice.CreateExternalServiceStatement; +import org.apache.iotdb.db.queryengine.plan.statement.metadata.externalservice.DropExternalServiceStatement; +import org.apache.iotdb.db.queryengine.plan.statement.metadata.externalservice.ShowExternalServiceStatement; +import org.apache.iotdb.db.queryengine.plan.statement.metadata.externalservice.StartExternalServiceStatement; +import org.apache.iotdb.db.queryengine.plan.statement.metadata.externalservice.StopExternalServiceStatement; import org.apache.iotdb.db.queryengine.plan.statement.metadata.model.CreateModelStatement; import org.apache.iotdb.db.queryengine.plan.statement.metadata.model.CreateTrainingStatement; import org.apache.iotdb.db.queryengine.plan.statement.metadata.model.DropModelStatement; @@ -1093,6 +1098,38 @@ public Statement visitShowTriggers(IoTDBSqlParser.ShowTriggersContext ctx) { return new ShowTriggersStatement(); } + @Override + public Statement visitCreateService(IoTDBSqlParser.CreateServiceContext ctx) { + String serviceName = parseIdentifier(ctx.serviceName.getText()); + String className = parseStringLiteral(ctx.className.getText()); + return new CreateExternalServiceStatement(serviceName, className); + } + + @Override + public Statement visitStartService(IoTDBSqlParser.StartServiceContext ctx) { + return new StartExternalServiceStatement(parseIdentifier(ctx.serviceName.getText())); + } + + @Override + public Statement visitStopService(IoTDBSqlParser.StopServiceContext ctx) { + return new StopExternalServiceStatement(parseIdentifier(ctx.serviceName.getText())); + } + + @Override + public Statement visitDropService(IoTDBSqlParser.DropServiceContext ctx) { + return new DropExternalServiceStatement(parseIdentifier(ctx.serviceName.getText())); + } + + @Override + public Statement visitShowService(IoTDBSqlParser.ShowServiceContext ctx) { + // show services on all DNs + int dataNodeId = -1; + if (ctx.ON() != null) { + dataNodeId = Integer.parseInt(ctx.targetDataNodeId.getText()); + } + return new ShowExternalServiceStatement(dataNodeId); + } + // Create PipePlugin ===================================================================== @Override public Statement visitCreatePipePlugin(IoTDBSqlParser.CreatePipePluginContext ctx) { diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/security/TreeAccessCheckVisitor.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/security/TreeAccessCheckVisitor.java index 7f9cf30e93c2..e310ddb53d03 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/security/TreeAccessCheckVisitor.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/security/TreeAccessCheckVisitor.java @@ -91,6 +91,11 @@ import org.apache.iotdb.db.queryengine.plan.statement.metadata.ShowTriggersStatement; import org.apache.iotdb.db.queryengine.plan.statement.metadata.ShowVariablesStatement; import org.apache.iotdb.db.queryengine.plan.statement.metadata.UnSetTTLStatement; +import org.apache.iotdb.db.queryengine.plan.statement.metadata.externalservice.CreateExternalServiceStatement; +import org.apache.iotdb.db.queryengine.plan.statement.metadata.externalservice.DropExternalServiceStatement; +import org.apache.iotdb.db.queryengine.plan.statement.metadata.externalservice.ShowExternalServiceStatement; +import org.apache.iotdb.db.queryengine.plan.statement.metadata.externalservice.StartExternalServiceStatement; +import org.apache.iotdb.db.queryengine.plan.statement.metadata.externalservice.StopExternalServiceStatement; import org.apache.iotdb.db.queryengine.plan.statement.metadata.model.CreateModelStatement; import org.apache.iotdb.db.queryengine.plan.statement.metadata.model.CreateTrainingStatement; import org.apache.iotdb.db.queryengine.plan.statement.metadata.model.DropModelStatement; @@ -936,6 +941,38 @@ private TSStatus checkTriggerManagement(IAuditEntity auditEntity, Supplier ""); + } + + @Override + public TSStatus visitStartExternalService( + StartExternalServiceStatement startExternalServiceStatement, TreeAccessCheckContext context) { + return checkGlobalAuth(context, PrivilegeType.MAINTAIN, () -> ""); + } + + @Override + public TSStatus visitStopExternalService( + StopExternalServiceStatement stopExternalServiceStatement, TreeAccessCheckContext context) { + return checkGlobalAuth(context, PrivilegeType.MAINTAIN, () -> ""); + } + + @Override + public TSStatus visitDropExternalService( + DropExternalServiceStatement dropExternalServiceStatement, TreeAccessCheckContext context) { + return checkGlobalAuth(context, PrivilegeType.MAINTAIN, () -> ""); + } + + @Override + public TSStatus visitShowExternalService( + ShowExternalServiceStatement showExternalServiceStatement, TreeAccessCheckContext context) { + return checkGlobalAuth(context, PrivilegeType.MAINTAIN, () -> ""); + } + // ============================== database related =========================== @Override public TSStatus visitSetDatabase( diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/sql/ast/AstVisitor.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/sql/ast/AstVisitor.java index 4b32201aa8e3..e8e5cce22ca9 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/sql/ast/AstVisitor.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/sql/ast/AstVisitor.java @@ -601,6 +601,26 @@ protected R visitDropFunction(DropFunction node, C context) { return visitStatement(node, context); } + protected R visitCreateExternalService(CreateExternalService node, C context) { + return visitStatement(node, context); + } + + protected R visitStartExternalService(StartExternalService node, C context) { + return visitStatement(node, context); + } + + protected R visitStopExternalService(StopExternalService node, C context) { + return visitStatement(node, context); + } + + protected R visitDropExternalService(DropExternalService node, C context) { + return visitStatement(node, context); + } + + protected R visitShowExternalService(ShowExternalService node, C context) { + return visitStatement(node, context); + } + protected R visitCreateOrUpdateDevice(CreateOrUpdateDevice node, C context) { return visitStatement(node, context); } diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/sql/ast/CreateExternalService.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/sql/ast/CreateExternalService.java new file mode 100644 index 000000000000..36674368e9fd --- /dev/null +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/sql/ast/CreateExternalService.java @@ -0,0 +1,97 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.apache.iotdb.db.queryengine.plan.relational.sql.ast; + +import com.google.common.collect.ImmutableList; +import org.apache.tsfile.utils.RamUsageEstimator; + +import java.util.List; +import java.util.Objects; + +import static com.google.common.base.MoreObjects.toStringHelper; +import static java.util.Objects.requireNonNull; + +public class CreateExternalService extends Statement { + private static final long INSTANCE_SIZE = + RamUsageEstimator.shallowSizeOfInstance(CreateExternalService.class); + + private final String serviceName; + private final String className; + + public CreateExternalService(NodeLocation location, String serviceName, String className) { + super(requireNonNull(location, "location is null")); + + this.serviceName = requireNonNull(serviceName, "serviceName is null"); + this.className = requireNonNull(className, "className is null"); + } + + public String getServiceName() { + return serviceName; + } + + public String getClassName() { + return className; + } + + @Override + public R accept(AstVisitor visitor, C context) { + return visitor.visitCreateExternalService(this, context); + } + + @Override + public List getChildren() { + return ImmutableList.of(); + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + CreateExternalService that = (CreateExternalService) o; + return Objects.equals(serviceName, that.serviceName) + && Objects.equals(className, that.className); + } + + @Override + public int hashCode() { + return Objects.hash(serviceName, className); + } + + @Override + public String toString() { + return toStringHelper(this) + .add("serviceName", serviceName) + .add("className", className) + .toString(); + } + + @Override + public long ramBytesUsed() { + long size = INSTANCE_SIZE; + size += AstMemoryEstimationHelper.getEstimatedSizeOfNodeLocation(getLocationInternal()); + size += RamUsageEstimator.sizeOf(serviceName); + size += RamUsageEstimator.sizeOf(className); + return size; + } +} diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/sql/ast/DropExternalService.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/sql/ast/DropExternalService.java new file mode 100644 index 000000000000..c4a53faea69a --- /dev/null +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/sql/ast/DropExternalService.java @@ -0,0 +1,85 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.apache.iotdb.db.queryengine.plan.relational.sql.ast; + +import com.google.common.collect.ImmutableList; +import org.apache.tsfile.utils.RamUsageEstimator; + +import java.util.List; +import java.util.Objects; + +import static com.google.common.base.MoreObjects.toStringHelper; +import static java.util.Objects.requireNonNull; + +public class DropExternalService extends Statement { + private static final long INSTANCE_SIZE = + RamUsageEstimator.shallowSizeOfInstance(DropExternalService.class); + + private final String serviceName; + + public DropExternalService(NodeLocation location, String serviceName) { + super(requireNonNull(location, "location is null")); + this.serviceName = requireNonNull(serviceName, "serviceName is null"); + } + + public String getServiceName() { + return serviceName; + } + + @Override + public R accept(AstVisitor visitor, C context) { + return visitor.visitDropExternalService(this, context); + } + + @Override + public List getChildren() { + return ImmutableList.of(); + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + DropExternalService that = (DropExternalService) o; + return Objects.equals(serviceName, that.serviceName); + } + + @Override + public int hashCode() { + return Objects.hash(serviceName); + } + + @Override + public String toString() { + return toStringHelper(this).add("serviceName", serviceName).toString(); + } + + @Override + public long ramBytesUsed() { + long size = INSTANCE_SIZE; + size += AstMemoryEstimationHelper.getEstimatedSizeOfNodeLocation(getLocationInternal()); + size += RamUsageEstimator.sizeOf(serviceName); + return size; + } +} diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/sql/ast/ShowExternalService.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/sql/ast/ShowExternalService.java new file mode 100644 index 000000000000..b03848f99782 --- /dev/null +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/sql/ast/ShowExternalService.java @@ -0,0 +1,81 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.apache.iotdb.db.queryengine.plan.relational.sql.ast; + +import com.google.common.collect.ImmutableList; +import org.apache.tsfile.utils.RamUsageEstimator; + +import java.util.List; + +import static com.google.common.base.MoreObjects.toStringHelper; + +public class ShowExternalService extends Statement { + + private static final long INSTANCE_SIZE = + RamUsageEstimator.shallowSizeOfInstance(ShowExternalService.class); + + // -1 means show services on all DNs + private int dataNodeId; + + public ShowExternalService(NodeLocation location, int dataNodeId) { + super(location); + this.dataNodeId = dataNodeId; + } + + public int getDataNodeId() { + return dataNodeId; + } + + @Override + public List getChildren() { + return ImmutableList.of(); + } + + @Override + public R accept(AstVisitor visitor, C context) { + return visitor.visitShowExternalService(this, context); + } + + @Override + public int hashCode() { + return 0; + } + + @Override + public boolean equals(Object obj) { + if (!(obj instanceof ShowExternalService)) { + return false; + } + return dataNodeId == ((ShowExternalService) obj).getDataNodeId(); + } + + @Override + public String toString() { + return toStringHelper(this).toString(); + } + + @Override + public long ramBytesUsed() { + long size = INSTANCE_SIZE; + size += AstMemoryEstimationHelper.getEstimatedSizeOfNodeLocation(getLocationInternal()); + size += Integer.SIZE; + return size; + } +} diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/sql/ast/StartExternalService.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/sql/ast/StartExternalService.java new file mode 100644 index 000000000000..3c72ecce395e --- /dev/null +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/sql/ast/StartExternalService.java @@ -0,0 +1,85 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.apache.iotdb.db.queryengine.plan.relational.sql.ast; + +import com.google.common.collect.ImmutableList; +import org.apache.tsfile.utils.RamUsageEstimator; + +import java.util.List; +import java.util.Objects; + +import static com.google.common.base.MoreObjects.toStringHelper; +import static java.util.Objects.requireNonNull; + +public class StartExternalService extends Statement { + private static final long INSTANCE_SIZE = + RamUsageEstimator.shallowSizeOfInstance(StartExternalService.class); + + private final String serviceName; + + public StartExternalService(NodeLocation location, String serviceName) { + super(requireNonNull(location, "location is null")); + this.serviceName = requireNonNull(serviceName, "serviceName is null"); + } + + public String getServiceName() { + return serviceName; + } + + @Override + public R accept(AstVisitor visitor, C context) { + return visitor.visitStartExternalService(this, context); + } + + @Override + public List getChildren() { + return ImmutableList.of(); + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + StartExternalService that = (StartExternalService) o; + return Objects.equals(serviceName, that.serviceName); + } + + @Override + public int hashCode() { + return Objects.hash(serviceName); + } + + @Override + public String toString() { + return toStringHelper(this).add("serviceName", serviceName).toString(); + } + + @Override + public long ramBytesUsed() { + long size = INSTANCE_SIZE; + size += AstMemoryEstimationHelper.getEstimatedSizeOfNodeLocation(getLocationInternal()); + size += RamUsageEstimator.sizeOf(serviceName); + return size; + } +} diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/sql/ast/StopExternalService.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/sql/ast/StopExternalService.java new file mode 100644 index 000000000000..039c2c9b5590 --- /dev/null +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/sql/ast/StopExternalService.java @@ -0,0 +1,85 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.apache.iotdb.db.queryengine.plan.relational.sql.ast; + +import com.google.common.collect.ImmutableList; +import org.apache.tsfile.utils.RamUsageEstimator; + +import java.util.List; +import java.util.Objects; + +import static com.google.common.base.MoreObjects.toStringHelper; +import static java.util.Objects.requireNonNull; + +public class StopExternalService extends Statement { + private static final long INSTANCE_SIZE = + RamUsageEstimator.shallowSizeOfInstance(StopExternalService.class); + + private final String serviceName; + + public StopExternalService(NodeLocation location, String serviceName) { + super(requireNonNull(location, "location is null")); + this.serviceName = requireNonNull(serviceName, "serviceName is null"); + } + + public String getServiceName() { + return serviceName; + } + + @Override + public R accept(AstVisitor visitor, C context) { + return visitor.visitStopExternalService(this, context); + } + + @Override + public List getChildren() { + return ImmutableList.of(); + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + StopExternalService that = (StopExternalService) o; + return Objects.equals(serviceName, that.serviceName); + } + + @Override + public int hashCode() { + return Objects.hash(serviceName); + } + + @Override + public String toString() { + return toStringHelper(this).add("serviceName", serviceName).toString(); + } + + @Override + public long ramBytesUsed() { + long size = INSTANCE_SIZE; + size += AstMemoryEstimationHelper.getEstimatedSizeOfNodeLocation(getLocationInternal()); + size += RamUsageEstimator.sizeOf(serviceName); + return size; + } +} diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/sql/parser/AstBuilder.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/sql/parser/AstBuilder.java index 5ad2b579cb66..a8001b045492 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/sql/parser/AstBuilder.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/sql/parser/AstBuilder.java @@ -58,6 +58,7 @@ import org.apache.iotdb.db.queryengine.plan.relational.sql.ast.CountDevice; import org.apache.iotdb.db.queryengine.plan.relational.sql.ast.CountStatement; import org.apache.iotdb.db.queryengine.plan.relational.sql.ast.CreateDB; +import org.apache.iotdb.db.queryengine.plan.relational.sql.ast.CreateExternalService; import org.apache.iotdb.db.queryengine.plan.relational.sql.ast.CreateFunction; import org.apache.iotdb.db.queryengine.plan.relational.sql.ast.CreateIndex; import org.apache.iotdb.db.queryengine.plan.relational.sql.ast.CreateModel; @@ -80,6 +81,7 @@ import org.apache.iotdb.db.queryengine.plan.relational.sql.ast.DoubleLiteral; import org.apache.iotdb.db.queryengine.plan.relational.sql.ast.DropColumn; import org.apache.iotdb.db.queryengine.plan.relational.sql.ast.DropDB; +import org.apache.iotdb.db.queryengine.plan.relational.sql.ast.DropExternalService; import org.apache.iotdb.db.queryengine.plan.relational.sql.ast.DropFunction; import org.apache.iotdb.db.queryengine.plan.relational.sql.ast.DropIndex; import org.apache.iotdb.db.queryengine.plan.relational.sql.ast.DropModel; @@ -193,6 +195,7 @@ import org.apache.iotdb.db.queryengine.plan.relational.sql.ast.ShowDB; import org.apache.iotdb.db.queryengine.plan.relational.sql.ast.ShowDataNodes; import org.apache.iotdb.db.queryengine.plan.relational.sql.ast.ShowDevice; +import org.apache.iotdb.db.queryengine.plan.relational.sql.ast.ShowExternalService; import org.apache.iotdb.db.queryengine.plan.relational.sql.ast.ShowFunctions; import org.apache.iotdb.db.queryengine.plan.relational.sql.ast.ShowIndex; import org.apache.iotdb.db.queryengine.plan.relational.sql.ast.ShowLoadedModels; @@ -212,9 +215,11 @@ import org.apache.iotdb.db.queryengine.plan.relational.sql.ast.SingleColumn; import org.apache.iotdb.db.queryengine.plan.relational.sql.ast.SkipTo; import org.apache.iotdb.db.queryengine.plan.relational.sql.ast.SortItem; +import org.apache.iotdb.db.queryengine.plan.relational.sql.ast.StartExternalService; import org.apache.iotdb.db.queryengine.plan.relational.sql.ast.StartPipe; import org.apache.iotdb.db.queryengine.plan.relational.sql.ast.StartRepairData; import org.apache.iotdb.db.queryengine.plan.relational.sql.ast.Statement; +import org.apache.iotdb.db.queryengine.plan.relational.sql.ast.StopExternalService; import org.apache.iotdb.db.queryengine.plan.relational.sql.ast.StopPipe; import org.apache.iotdb.db.queryengine.plan.relational.sql.ast.StopRepairData; import org.apache.iotdb.db.queryengine.plan.relational.sql.ast.StringLiteral; @@ -1087,6 +1092,41 @@ public Node visitShowFunctionsStatement(RelationalSqlParser.ShowFunctionsStateme return new ShowFunctions(); } + @Override + public Node visitCreateServiceStatement(RelationalSqlParser.CreateServiceStatementContext ctx) { + String serviceName = ((Identifier) visit(ctx.serviceName)).getValue(); + String className = ((StringLiteral) visit(ctx.className)).getValue(); + return new CreateExternalService(getLocation(ctx), serviceName, className); + } + + @Override + public Node visitStartServiceStatement(RelationalSqlParser.StartServiceStatementContext ctx) { + return new StartExternalService( + getLocation(ctx), ((Identifier) visit(ctx.serviceName)).getValue()); + } + + @Override + public Node visitStopServiceStatement(RelationalSqlParser.StopServiceStatementContext ctx) { + return new StopExternalService( + getLocation(ctx), ((Identifier) visit(ctx.serviceName)).getValue()); + } + + @Override + public Node visitDropServiceStatement(RelationalSqlParser.DropServiceStatementContext ctx) { + return new DropExternalService( + getLocation(ctx), ((Identifier) visit(ctx.serviceName)).getValue()); + } + + @Override + public Node visitShowServiceStatement(RelationalSqlParser.ShowServiceStatementContext ctx) { + // show services on all DNs + int dataNodeId = -1; + if (ctx.ON() != null) { + dataNodeId = Integer.parseInt(ctx.targetDataNodeId.getText()); + } + return new ShowExternalService(getLocation(ctx), dataNodeId); + } + @Override public Node visitLoadTsFileStatement(RelationalSqlParser.LoadTsFileStatementContext ctx) { final Map withAttributes = diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/statement/StatementType.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/statement/StatementType.java index d75e2e09b7c2..721459ce84d3 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/statement/StatementType.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/statement/StatementType.java @@ -192,4 +192,10 @@ public enum StatementType { FAST_LAST_QUERY, SHOW_CONFIGURATION, + + CREATE_EXTERNAL_SERVICE, + START_EXTERNAL_SERVICE, + STOP_EXTERNAL_SERVICE, + DROP_EXTERNAL_SERVICE, + SHOW_EXTERNAL_SERVICE, } diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/statement/StatementVisitor.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/statement/StatementVisitor.java index 3266ba072651..29d49453f444 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/statement/StatementVisitor.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/statement/StatementVisitor.java @@ -80,6 +80,11 @@ import org.apache.iotdb.db.queryengine.plan.statement.metadata.ShowTriggersStatement; import org.apache.iotdb.db.queryengine.plan.statement.metadata.ShowVariablesStatement; import org.apache.iotdb.db.queryengine.plan.statement.metadata.UnSetTTLStatement; +import org.apache.iotdb.db.queryengine.plan.statement.metadata.externalservice.CreateExternalServiceStatement; +import org.apache.iotdb.db.queryengine.plan.statement.metadata.externalservice.DropExternalServiceStatement; +import org.apache.iotdb.db.queryengine.plan.statement.metadata.externalservice.ShowExternalServiceStatement; +import org.apache.iotdb.db.queryengine.plan.statement.metadata.externalservice.StartExternalServiceStatement; +import org.apache.iotdb.db.queryengine.plan.statement.metadata.externalservice.StopExternalServiceStatement; import org.apache.iotdb.db.queryengine.plan.statement.metadata.model.CreateModelStatement; import org.apache.iotdb.db.queryengine.plan.statement.metadata.model.CreateTrainingStatement; import org.apache.iotdb.db.queryengine.plan.statement.metadata.model.DropModelStatement; @@ -280,6 +285,32 @@ public R visitShowTriggers(ShowTriggersStatement showTriggersStatement, C contex return visitStatement(showTriggersStatement, context); } + // ExternalService + public R visitCreateExternalService( + CreateExternalServiceStatement createExternalServiceStatement, C context) { + return visitStatement(createExternalServiceStatement, context); + } + + public R visitStartExternalService( + StartExternalServiceStatement startExternalServiceStatement, C context) { + return visitStatement(startExternalServiceStatement, context); + } + + public R visitStopExternalService( + StopExternalServiceStatement stopExternalServiceStatement, C context) { + return visitStatement(stopExternalServiceStatement, context); + } + + public R visitDropExternalService( + DropExternalServiceStatement dropExternalServiceStatement, C context) { + return visitStatement(dropExternalServiceStatement, context); + } + + public R visitShowExternalService( + ShowExternalServiceStatement showExternalServiceStatement, C context) { + return visitStatement(showExternalServiceStatement, context); + } + // Pipe Plugin public R visitCreatePipePlugin(CreatePipePluginStatement createPipePluginStatement, C context) { return visitStatement(createPipePluginStatement, context); diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/statement/metadata/externalservice/CreateExternalServiceStatement.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/statement/metadata/externalservice/CreateExternalServiceStatement.java new file mode 100644 index 000000000000..83cd86a1128d --- /dev/null +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/statement/metadata/externalservice/CreateExternalServiceStatement.java @@ -0,0 +1,66 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.apache.iotdb.db.queryengine.plan.statement.metadata.externalservice; + +import org.apache.iotdb.commons.path.PartialPath; +import org.apache.iotdb.db.queryengine.plan.analyze.QueryType; +import org.apache.iotdb.db.queryengine.plan.statement.IConfigStatement; +import org.apache.iotdb.db.queryengine.plan.statement.Statement; +import org.apache.iotdb.db.queryengine.plan.statement.StatementType; +import org.apache.iotdb.db.queryengine.plan.statement.StatementVisitor; + +import java.util.Collections; +import java.util.List; + +public class CreateExternalServiceStatement extends Statement implements IConfigStatement { + + private final String serviceName; + private final String className; + + public CreateExternalServiceStatement(String serviceName, String className) { + super(); + statementType = StatementType.CREATE_EXTERNAL_SERVICE; + this.serviceName = serviceName; + this.className = className; + } + + public String getServiceName() { + return serviceName; + } + + public String getClassName() { + return className; + } + + @Override + public R accept(StatementVisitor visitor, C context) { + return visitor.visitCreateExternalService(this, context); + } + + @Override + public QueryType getQueryType() { + return QueryType.WRITE; + } + + @Override + public List getPaths() { + return Collections.emptyList(); + } +} diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/statement/metadata/externalservice/DropExternalServiceStatement.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/statement/metadata/externalservice/DropExternalServiceStatement.java new file mode 100644 index 000000000000..9db372caa267 --- /dev/null +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/statement/metadata/externalservice/DropExternalServiceStatement.java @@ -0,0 +1,60 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.apache.iotdb.db.queryengine.plan.statement.metadata.externalservice; + +import org.apache.iotdb.commons.path.PartialPath; +import org.apache.iotdb.db.queryengine.plan.analyze.QueryType; +import org.apache.iotdb.db.queryengine.plan.statement.IConfigStatement; +import org.apache.iotdb.db.queryengine.plan.statement.Statement; +import org.apache.iotdb.db.queryengine.plan.statement.StatementType; +import org.apache.iotdb.db.queryengine.plan.statement.StatementVisitor; + +import java.util.Collections; +import java.util.List; + +public class DropExternalServiceStatement extends Statement implements IConfigStatement { + + private final String serviceName; + + public DropExternalServiceStatement(String serviceName) { + super(); + statementType = StatementType.DROP_EXTERNAL_SERVICE; + this.serviceName = serviceName; + } + + public String getServiceName() { + return serviceName; + } + + @Override + public R accept(StatementVisitor visitor, C context) { + return visitor.visitDropExternalService(this, context); + } + + @Override + public QueryType getQueryType() { + return QueryType.WRITE; + } + + @Override + public List getPaths() { + return Collections.emptyList(); + } +} diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/statement/metadata/externalservice/ShowExternalServiceStatement.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/statement/metadata/externalservice/ShowExternalServiceStatement.java new file mode 100644 index 000000000000..3f5d3b657077 --- /dev/null +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/statement/metadata/externalservice/ShowExternalServiceStatement.java @@ -0,0 +1,60 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.apache.iotdb.db.queryengine.plan.statement.metadata.externalservice; + +import org.apache.iotdb.commons.path.PartialPath; +import org.apache.iotdb.db.queryengine.plan.analyze.QueryType; +import org.apache.iotdb.db.queryengine.plan.statement.IConfigStatement; +import org.apache.iotdb.db.queryengine.plan.statement.Statement; +import org.apache.iotdb.db.queryengine.plan.statement.StatementType; +import org.apache.iotdb.db.queryengine.plan.statement.StatementVisitor; + +import java.util.Collections; +import java.util.List; + +public class ShowExternalServiceStatement extends Statement implements IConfigStatement { + + private final int dataNodeId; + + public ShowExternalServiceStatement(int dataNodeId) { + super(); + statementType = StatementType.SHOW_EXTERNAL_SERVICE; + this.dataNodeId = dataNodeId; + } + + public int getDataNodeId() { + return dataNodeId; + } + + @Override + public R accept(StatementVisitor visitor, C context) { + return visitor.visitShowExternalService(this, context); + } + + @Override + public QueryType getQueryType() { + return QueryType.WRITE; + } + + @Override + public List getPaths() { + return Collections.emptyList(); + } +} diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/statement/metadata/externalservice/StartExternalServiceStatement.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/statement/metadata/externalservice/StartExternalServiceStatement.java new file mode 100644 index 000000000000..f7575a508853 --- /dev/null +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/statement/metadata/externalservice/StartExternalServiceStatement.java @@ -0,0 +1,60 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.apache.iotdb.db.queryengine.plan.statement.metadata.externalservice; + +import org.apache.iotdb.commons.path.PartialPath; +import org.apache.iotdb.db.queryengine.plan.analyze.QueryType; +import org.apache.iotdb.db.queryengine.plan.statement.IConfigStatement; +import org.apache.iotdb.db.queryengine.plan.statement.Statement; +import org.apache.iotdb.db.queryengine.plan.statement.StatementType; +import org.apache.iotdb.db.queryengine.plan.statement.StatementVisitor; + +import java.util.Collections; +import java.util.List; + +public class StartExternalServiceStatement extends Statement implements IConfigStatement { + + private final String serviceName; + + public StartExternalServiceStatement(String serviceName) { + super(); + statementType = StatementType.START_EXTERNAL_SERVICE; + this.serviceName = serviceName; + } + + public String getServiceName() { + return serviceName; + } + + @Override + public R accept(StatementVisitor visitor, C context) { + return visitor.visitStartExternalService(this, context); + } + + @Override + public QueryType getQueryType() { + return QueryType.WRITE; + } + + @Override + public List getPaths() { + return Collections.emptyList(); + } +} diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/statement/metadata/externalservice/StopExternalServiceStatement.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/statement/metadata/externalservice/StopExternalServiceStatement.java new file mode 100644 index 000000000000..8de43a8546d4 --- /dev/null +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/statement/metadata/externalservice/StopExternalServiceStatement.java @@ -0,0 +1,60 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.apache.iotdb.db.queryengine.plan.statement.metadata.externalservice; + +import org.apache.iotdb.commons.path.PartialPath; +import org.apache.iotdb.db.queryengine.plan.analyze.QueryType; +import org.apache.iotdb.db.queryengine.plan.statement.IConfigStatement; +import org.apache.iotdb.db.queryengine.plan.statement.Statement; +import org.apache.iotdb.db.queryengine.plan.statement.StatementType; +import org.apache.iotdb.db.queryengine.plan.statement.StatementVisitor; + +import java.util.Collections; +import java.util.List; + +public class StopExternalServiceStatement extends Statement implements IConfigStatement { + + private final String serviceName; + + public StopExternalServiceStatement(String serviceName) { + super(); + statementType = StatementType.STOP_EXTERNAL_SERVICE; + this.serviceName = serviceName; + } + + public String getServiceName() { + return serviceName; + } + + @Override + public R accept(StatementVisitor visitor, C context) { + return visitor.visitStopExternalService(this, context); + } + + @Override + public QueryType getQueryType() { + return QueryType.WRITE; + } + + @Override + public List getPaths() { + return Collections.emptyList(); + } +} diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/service/exernalservice/ExternalServiceManagementService.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/service/exernalservice/ExternalServiceManagementService.java index 39ec79cbd823..46f131a40dec 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/service/exernalservice/ExternalServiceManagementService.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/service/exernalservice/ExternalServiceManagementService.java @@ -41,8 +41,6 @@ import static com.google.common.base.Preconditions.checkState; import static org.apache.iotdb.commons.externalservice.ServiceInfo.State.RUNNING; import static org.apache.iotdb.commons.externalservice.ServiceInfo.State.STOPPED; -import static org.apache.iotdb.db.service.exernalservice.ServiceInfo.State.RUNNING; -import static org.apache.iotdb.db.service.exernalservice.ServiceInfo.State.STOPPED; public class ExternalServiceManagementService { @GuardedBy("lock") @@ -78,9 +76,9 @@ public void createService(String serviceName, String className) { String.format("Failed to create External Service %s, it already exists!", serviceName)); } - logAccessor.writeWal( - new ServiceLogAccessor.ServiceWalEntry( - ServiceLogAccessor.OperationType.CREATE, serviceName, className)); + /*logAccessor.writeWal( + new ServiceLogAccessor.ServiceWalEntry( + ServiceLogAccessor.OperationType.CREATE, serviceName, className));*/ serviceInfos.put( serviceName, new ServiceInfo(serviceName, className, ServiceInfo.ServiceType.USER_DEFINED)); @@ -112,9 +110,9 @@ public void startService(String serviceName) { } } - logAccessor.writeWal( - new ServiceLogAccessor.ServiceWalEntry( - ServiceLogAccessor.OperationType.START, serviceName, null)); + /*logAccessor.writeWal( + new ServiceLogAccessor.ServiceWalEntry( + ServiceLogAccessor.OperationType.START, serviceName, null));*/ serviceInfo.setState(RUNNING); } finally { lock.writeLock().unlock(); @@ -160,9 +158,9 @@ public void stopService(String serviceName) { stopService(serviceInfo); } - logAccessor.writeWal( - new ServiceLogAccessor.ServiceWalEntry( - ServiceLogAccessor.OperationType.STOP, serviceName, null)); + /*logAccessor.writeWal( + new ServiceLogAccessor.ServiceWalEntry( + ServiceLogAccessor.OperationType.STOP, serviceName, null));*/ serviceInfo.setState(STOPPED); } finally { lock.writeLock().unlock(); @@ -213,9 +211,9 @@ public void dropService(String serviceName, boolean forcedly) { } } - logAccessor.writeWal( - new ServiceLogAccessor.ServiceWalEntry( - ServiceLogAccessor.OperationType.DROP, serviceName, null)); + /*logAccessor.writeWal( + new ServiceLogAccessor.ServiceWalEntry( + ServiceLogAccessor.OperationType.DROP, serviceName, null));*/ serviceInfos.remove(serviceName); } finally { lock.writeLock().unlock(); diff --git a/iotdb-core/relational-grammar/src/main/antlr4/org/apache/iotdb/db/relational/grammar/sql/RelationalSql.g4 b/iotdb-core/relational-grammar/src/main/antlr4/org/apache/iotdb/db/relational/grammar/sql/RelationalSql.g4 index 183a878a4869..98ded23c4145 100644 --- a/iotdb-core/relational-grammar/src/main/antlr4/org/apache/iotdb/db/relational/grammar/sql/RelationalSql.g4 +++ b/iotdb-core/relational-grammar/src/main/antlr4/org/apache/iotdb/db/relational/grammar/sql/RelationalSql.g4 @@ -84,6 +84,13 @@ statement | dropFunctionStatement | createFunctionStatement + // ExternalService Statement + | createServiceStatement + | startServiceStatement + | stopServiceStatement + | dropServiceStatement + | showServiceStatement + // Load Statement | loadTsFileStatement @@ -374,6 +381,27 @@ showFunctionsStatement ; +// -------------------------------------------- ExternalService Statement ---------------------------------------------------------- +createServiceStatement + : CREATE SERVICE serviceName=identifier + AS className=string + ; + +startServiceStatement + : START SERVICE serviceName=identifier + ; + +stopServiceStatement + : STOP SERVICE serviceName=identifier + ; + +dropServiceStatement + : DROP SERVICE serviceName=identifier + ; + +showServiceStatement + : SHOW SERVICES (ON targetDataNodeId=INTEGER_VALUE)? + ; // -------------------------------------------- Load Statement --------------------------------------------------------- loadTsFileStatement @@ -1764,6 +1792,8 @@ SECURITY: 'SECURITY'; SEEK: 'SEEK'; SELECT: 'SELECT'; SERIALIZABLE: 'SERIALIZABLE'; +SERVICE: 'SERVICE'; +SERVICES: 'SERVICES'; SESSION: 'SESSION'; SET: 'SET'; SETS: 'SETS'; diff --git a/iotdb-protocol/thrift-commons/src/main/thrift/common.thrift b/iotdb-protocol/thrift-commons/src/main/thrift/common.thrift index 58c4484a4ae8..ae12a4a5bbe9 100644 --- a/iotdb-protocol/thrift-commons/src/main/thrift/common.thrift +++ b/iotdb-protocol/thrift-commons/src/main/thrift/common.thrift @@ -261,6 +261,18 @@ struct TNodeLocations { 2: optional list dataNodeLocations } +struct TExternalServiceListResp { + 1: required TSStatus status + 2: required list externalServiceInfos +} + + +struct TExternalServiceEntry { + 1: required string serviceName + 2: required string className + 3: required byte state +} + enum TAggregationType { COUNT, AVG, diff --git a/iotdb-protocol/thrift-confignode/src/main/thrift/confignode.thrift b/iotdb-protocol/thrift-confignode/src/main/thrift/confignode.thrift index 1866e2ea66a9..2beef6f2570e 100644 --- a/iotdb-protocol/thrift-confignode/src/main/thrift/confignode.thrift +++ b/iotdb-protocol/thrift-confignode/src/main/thrift/confignode.thrift @@ -1098,17 +1098,6 @@ struct TCreateExternalServiceReq { 3: required string className } -struct TExternalServiceEntry { - 1: required string serviceName - 2: required string className - 3: required byte state -} - -struct TShowExternalServiceResp { - 1: required common.TSStatus status - 2: required list externalServiceInfos -} - struct TDeactivateSchemaTemplateReq { 1: required string queryId 2: required binary pathPatternTree @@ -2013,7 +2002,7 @@ service IConfigNodeRPCService { common.TSStatus dropExternalService(i32 dataNodeId, string serviceName) - TShowCQResp showExternalService(i32 dataNodeId) + common.TExternalServiceListResp showExternalService(i32 dataNodeId) // ====================================================== // Quota diff --git a/iotdb-protocol/thrift-datanode/src/main/thrift/datanode.thrift b/iotdb-protocol/thrift-datanode/src/main/thrift/datanode.thrift index 942a04f5f2a7..93bf9744d261 100644 --- a/iotdb-protocol/thrift-datanode/src/main/thrift/datanode.thrift +++ b/iotdb-protocol/thrift-datanode/src/main/thrift/datanode.thrift @@ -1025,6 +1025,11 @@ service IDataNodeRPCService { **/ common.TSStatus dropPipePlugin(TDropPipePluginInstanceReq req) + /** + * Config node will get built-in services info from data nodes. + **/ + common.TExternalServiceListResp getBuiltInService() + /* Maintenance Tools */ common.TSStatus merge() From 3e4cf34731d9574d8c26f639e1bbe4b81c49f6d4 Mon Sep 17 00:00:00 2001 From: Weihao Li <18110526956@163.com> Date: Mon, 12 Jan 2026 01:18:11 +0800 Subject: [PATCH 4/6] done Signed-off-by: Weihao Li <18110526956@163.com> --- .../iotdb/db/qp/sql/IdentifierParser.g4 | 1 + .../apache/iotdb/db/qp/sql/IoTDBSqlParser.g4 | 3 +- .../org/apache/iotdb/db/qp/sql/SqlLexer.g4 | 5 + .../ShowExternalServicePlan.java | 10 +- .../ShowExternalServiceResp.java | 29 ++-- .../confignode/manager/ConfigManager.java | 3 + .../externalservice/ExternalServiceInfo.java | 21 ++- .../ExternalServiceManager.java | 17 +-- .../executor/ConfigPlanExecutor.java | 22 +++ .../db/protocol/client/ConfigNodeClient.java | 18 ++- .../impl/DataNodeInternalRPCServiceImpl.java | 8 +- .../common/header/DatasetHeaderFactory.java | 4 + ...formationSchemaContentSupplierFactory.java | 2 + .../config/TableConfigTaskVisitor.java | 35 +++++ .../config/TreeConfigTaskVisitor.java | 41 ++++++ .../executor/ClusterConfigTaskExecutor.java | 76 ++++++++++- .../config/executor/IConfigTaskExecutor.java | 5 +- .../CreateExternalServiceTask.java | 13 +- .../DropExternalServiceTask.java | 46 +++++++ .../ShowExternalServiceTask.java | 63 +++++++++ .../StartExternalServiceTask.java | 44 ++++++ .../StopExternalServiceTask.java | 43 ++++++ .../queryengine/plan/parser/ASTVisitor.java | 2 +- .../DropExternalServiceStatement.java | 8 +- .../ShowExternalServiceStatement.java | 12 +- .../ExternalServiceManagementService.java | 126 ++++++++++++++---- .../commons/externalservice/ServiceInfo.java | 32 ++++- .../schema/column/ColumnHeaderConstant.java | 12 ++ .../relational/grammar/sql/RelationalSql.g4 | 3 +- .../src/main/thrift/common.thrift | 2 + 30 files changed, 606 insertions(+), 100 deletions(-) rename iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/metadata/{ => externalservice}/CreateExternalServiceTask.java (85%) create mode 100644 iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/metadata/externalservice/DropExternalServiceTask.java create mode 100644 iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/metadata/externalservice/ShowExternalServiceTask.java create mode 100644 iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/metadata/externalservice/StartExternalServiceTask.java create mode 100644 iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/metadata/externalservice/StopExternalServiceTask.java diff --git a/iotdb-core/antlr/src/main/antlr4/org/apache/iotdb/db/qp/sql/IdentifierParser.g4 b/iotdb-core/antlr/src/main/antlr4/org/apache/iotdb/db/qp/sql/IdentifierParser.g4 index aa1cc62a97a7..70f9fced2b36 100644 --- a/iotdb-core/antlr/src/main/antlr4/org/apache/iotdb/db/qp/sql/IdentifierParser.g4 +++ b/iotdb-core/antlr/src/main/antlr4/org/apache/iotdb/db/qp/sql/IdentifierParser.g4 @@ -115,6 +115,7 @@ keyWords | FIRST | FLUSH | FOR + | FORCEDLY | FROM | FULL | FUNCTION diff --git a/iotdb-core/antlr/src/main/antlr4/org/apache/iotdb/db/qp/sql/IoTDBSqlParser.g4 b/iotdb-core/antlr/src/main/antlr4/org/apache/iotdb/db/qp/sql/IoTDBSqlParser.g4 index 6ac16cac74dc..efe661e05430 100644 --- a/iotdb-core/antlr/src/main/antlr4/org/apache/iotdb/db/qp/sql/IoTDBSqlParser.g4 +++ b/iotdb-core/antlr/src/main/antlr4/org/apache/iotdb/db/qp/sql/IoTDBSqlParser.g4 @@ -454,7 +454,8 @@ stopService ; dropService - : DROP SERVICE serviceName=identifier + : DROP SERVICE serviceName=identifier FORCEDLY? + ; showService diff --git a/iotdb-core/antlr/src/main/antlr4/org/apache/iotdb/db/qp/sql/SqlLexer.g4 b/iotdb-core/antlr/src/main/antlr4/org/apache/iotdb/db/qp/sql/SqlLexer.g4 index fca8ffc1b420..4ff6f0dc1290 100644 --- a/iotdb-core/antlr/src/main/antlr4/org/apache/iotdb/db/qp/sql/SqlLexer.g4 +++ b/iotdb-core/antlr/src/main/antlr4/org/apache/iotdb/db/qp/sql/SqlLexer.g4 @@ -382,6 +382,11 @@ FOR : F O R ; +FORCEDLY + : F O R C E D L Y + ; + + FROM : F R O M ; diff --git a/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/consensus/request/read/exernalservice/ShowExternalServicePlan.java b/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/consensus/request/read/exernalservice/ShowExternalServicePlan.java index b9910c27202f..4e013e3ce7bc 100644 --- a/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/consensus/request/read/exernalservice/ShowExternalServicePlan.java +++ b/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/consensus/request/read/exernalservice/ShowExternalServicePlan.java @@ -22,20 +22,20 @@ import org.apache.iotdb.confignode.consensus.request.ConfigPhysicalPlanType; import org.apache.iotdb.confignode.consensus.request.read.ConfigPhysicalReadPlan; -import java.util.List; import java.util.Objects; +import java.util.Set; /** Get infos of ExternalService by the DataNode's id. */ public class ShowExternalServicePlan extends ConfigPhysicalReadPlan { - private final List dataNodeIds; + private final Set dataNodeIds; - public ShowExternalServicePlan(List dataNodeIds) { - super(ConfigPhysicalPlanType.GetDataNodeConfiguration); + public ShowExternalServicePlan(Set dataNodeIds) { + super(ConfigPhysicalPlanType.ShowExternalService); this.dataNodeIds = dataNodeIds; } - public List getDataNodeIds() { + public Set getDataNodeIds() { return dataNodeIds; } diff --git a/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/consensus/response/externalservice/ShowExternalServiceResp.java b/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/consensus/response/externalservice/ShowExternalServiceResp.java index ce06adf3b7f7..217b1e890a2c 100644 --- a/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/consensus/response/externalservice/ShowExternalServiceResp.java +++ b/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/consensus/response/externalservice/ShowExternalServiceResp.java @@ -22,33 +22,32 @@ import org.apache.iotdb.common.rpc.thrift.TExternalServiceEntry; import org.apache.iotdb.common.rpc.thrift.TExternalServiceListResp; import org.apache.iotdb.common.rpc.thrift.TSStatus; -import org.apache.iotdb.commons.externalservice.ServiceInfo; import org.apache.iotdb.consensus.common.DataSet; +import org.apache.iotdb.rpc.TSStatusCode; import javax.validation.constraints.NotNull; +import java.util.Comparator; import java.util.List; -import java.util.stream.Collectors; public class ShowExternalServiceResp implements DataSet { - private final TSStatus status; - private final List serviceInfos; + private final List serviceInfoEntryList; - public ShowExternalServiceResp( - @NotNull TSStatus status, @NotNull List serviceInfos) { - this.status = status; - this.serviceInfos = serviceInfos; + public ShowExternalServiceResp(@NotNull List serviceInfoEntryList) { + this.serviceInfoEntryList = serviceInfoEntryList; + } + + public List getServiceInfoEntryList() { + return serviceInfoEntryList; } public TExternalServiceListResp convertToRpcShowExternalServiceResp() { + serviceInfoEntryList.sort( + Comparator.comparingInt(TExternalServiceEntry::getDataNodId) + .thenComparing(TExternalServiceEntry::getServiceType) + .thenComparing(TExternalServiceEntry::getServiceName)); return new TExternalServiceListResp( - status, - serviceInfos.stream() - .map( - entry -> - new TExternalServiceEntry( - entry.getServiceName(), entry.getClassName(), entry.getState().getValue())) - .collect(Collectors.toList())); + new TSStatus(TSStatusCode.SUCCESS_STATUS.getStatusCode()), serviceInfoEntryList); } } diff --git a/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/manager/ConfigManager.java b/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/manager/ConfigManager.java index 17a4eedb7f8a..0188715094f7 100644 --- a/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/manager/ConfigManager.java +++ b/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/manager/ConfigManager.java @@ -110,6 +110,7 @@ import org.apache.iotdb.confignode.consensus.statemachine.ConfigRegionStateMachine; import org.apache.iotdb.confignode.manager.consensus.ConsensusManager; import org.apache.iotdb.confignode.manager.cq.CQManager; +import org.apache.iotdb.confignode.manager.externalservice.ExternalServiceInfo; import org.apache.iotdb.confignode.manager.externalservice.ExternalServiceManager; import org.apache.iotdb.confignode.manager.load.LoadManager; import org.apache.iotdb.confignode.manager.load.cache.node.NodeHeartbeatSample; @@ -360,6 +361,7 @@ public ConfigManager() throws IOException { UDFInfo udfInfo = new UDFInfo(); TriggerInfo triggerInfo = new TriggerInfo(); CQInfo cqInfo = new CQInfo(); + ExternalServiceInfo externalServiceInfo = new ExternalServiceInfo(); PipeInfo pipeInfo = new PipeInfo(); QuotaInfo quotaInfo = new QuotaInfo(); TTLInfo ttlInfo = new TTLInfo(); @@ -377,6 +379,7 @@ public ConfigManager() throws IOException { udfInfo, triggerInfo, cqInfo, + externalServiceInfo, pipeInfo, subscriptionInfo, quotaInfo, diff --git a/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/manager/externalservice/ExternalServiceInfo.java b/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/manager/externalservice/ExternalServiceInfo.java index 122156f41fd0..4cad78108cc3 100644 --- a/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/manager/externalservice/ExternalServiceInfo.java +++ b/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/manager/externalservice/ExternalServiceInfo.java @@ -19,6 +19,7 @@ package org.apache.iotdb.confignode.manager.externalservice; +import org.apache.iotdb.common.rpc.thrift.TExternalServiceEntry; import org.apache.iotdb.common.rpc.thrift.TSStatus; import org.apache.iotdb.commons.externalservice.ServiceInfo; import org.apache.iotdb.commons.snapshot.SnapshotProcessor; @@ -37,6 +38,7 @@ import java.io.FileOutputStream; import java.io.IOException; import java.util.Map; +import java.util.Set; import java.util.concurrent.ConcurrentHashMap; import java.util.stream.Collectors; @@ -160,11 +162,21 @@ public TSStatus stopService(StopExternalServicePlan plan) { return res; } - public ShowExternalServiceResp showService() { + public ShowExternalServiceResp showService(Set dataNodes) { return new ShowExternalServiceResp( - new TSStatus(TSStatusCode.SUCCESS_STATUS.getStatusCode()), - datanodeToServiceInfos.values().stream() - .flatMap(stringServiceInfoMap -> stringServiceInfoMap.values().stream()) + datanodeToServiceInfos.entrySet().stream() + .filter(entry -> dataNodes.contains(entry.getKey())) + .flatMap( + entry -> + entry.getValue().values().stream() + .map( + serviceInfo -> + new TExternalServiceEntry( + serviceInfo.getServiceName(), + serviceInfo.getClassName(), + serviceInfo.getState().getValue(), + entry.getKey(), + ServiceInfo.ServiceType.USER_DEFINED.getValue()))) .collect(Collectors.toList())); } @@ -180,6 +192,7 @@ public boolean processTakeSnapshot(File snapshotDir) throws IOException { try (FileOutputStream fileOutputStream = new FileOutputStream(snapshotFile)) { + // TODO // serializeExistedJarToMD5(fileOutputStream); // udfTable.serializeUDFTable(fileOutputStream); diff --git a/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/manager/externalservice/ExternalServiceManager.java b/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/manager/externalservice/ExternalServiceManager.java index 63e0a088f085..344479608af2 100644 --- a/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/manager/externalservice/ExternalServiceManager.java +++ b/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/manager/externalservice/ExternalServiceManager.java @@ -34,7 +34,6 @@ import org.apache.iotdb.confignode.consensus.response.externalservice.ShowExternalServiceResp; import org.apache.iotdb.confignode.manager.ConfigManager; import org.apache.iotdb.confignode.rpc.thrift.TCreateExternalServiceReq; -import org.apache.iotdb.consensus.common.DataSet; import org.apache.iotdb.consensus.exception.ConsensusException; import org.apache.iotdb.mpp.rpc.thrift.TCreateFunctionInstanceReq; import org.apache.iotdb.rpc.TSStatusCode; @@ -42,7 +41,6 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import java.util.ArrayList; import java.util.Collections; import java.util.Map; @@ -164,20 +162,19 @@ public TExternalServiceListResp showService(int dataNodeId) { try { // 2. get user-defined services info from CN consensus - DataSet response = - configManager - .getConsensusManager() - .read(new ShowExternalServicePlan(new ArrayList<>(builtInServiceInfos.keySet()))); - TExternalServiceListResp resp = - ((ShowExternalServiceResp) response).convertToRpcShowExternalServiceResp(); + ShowExternalServiceResp response = + (ShowExternalServiceResp) + configManager + .getConsensusManager() + .read(new ShowExternalServicePlan(builtInServiceInfos.keySet())); // 3. combined built-in services info and user-defined services info builtInServiceInfos .values() .forEach( builtInResp -> - resp.externalServiceInfos.addAll(builtInResp.getExternalServiceInfos())); - return resp; + response.getServiceInfoEntryList().addAll(builtInResp.getExternalServiceInfos())); + return response.convertToRpcShowExternalServiceResp(); } catch (ConsensusException e) { LOGGER.warn("Unexpected error happened while showing Service: ", e); // consensus layer related errors diff --git a/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/persistence/executor/ConfigPlanExecutor.java b/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/persistence/executor/ConfigPlanExecutor.java index 5cbcfb85881d..12ba1d8840b4 100644 --- a/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/persistence/executor/ConfigPlanExecutor.java +++ b/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/persistence/executor/ConfigPlanExecutor.java @@ -33,6 +33,7 @@ import org.apache.iotdb.confignode.consensus.request.read.database.CountDatabasePlan; import org.apache.iotdb.confignode.consensus.request.read.database.GetDatabasePlan; import org.apache.iotdb.confignode.consensus.request.read.datanode.GetDataNodeConfigurationPlan; +import org.apache.iotdb.confignode.consensus.request.read.exernalservice.ShowExternalServicePlan; import org.apache.iotdb.confignode.consensus.request.read.function.GetFunctionTablePlan; import org.apache.iotdb.confignode.consensus.request.read.function.GetUDFJarPlan; import org.apache.iotdb.confignode.consensus.request.read.partition.CountTimeSlotListPlan; @@ -78,6 +79,10 @@ import org.apache.iotdb.confignode.consensus.request.write.datanode.RegisterDataNodePlan; import org.apache.iotdb.confignode.consensus.request.write.datanode.RemoveDataNodePlan; import org.apache.iotdb.confignode.consensus.request.write.datanode.UpdateDataNodePlan; +import org.apache.iotdb.confignode.consensus.request.write.externalservice.CreateExternalServicePlan; +import org.apache.iotdb.confignode.consensus.request.write.externalservice.DropExternalServicePlan; +import org.apache.iotdb.confignode.consensus.request.write.externalservice.StartExternalServicePlan; +import org.apache.iotdb.confignode.consensus.request.write.externalservice.StopExternalServicePlan; import org.apache.iotdb.confignode.consensus.request.write.function.CreateFunctionPlan; import org.apache.iotdb.confignode.consensus.request.write.function.DropTableModelFunctionPlan; import org.apache.iotdb.confignode.consensus.request.write.function.DropTreeModelFunctionPlan; @@ -143,6 +148,7 @@ import org.apache.iotdb.confignode.consensus.request.write.trigger.UpdateTriggersOnTransferNodesPlan; import org.apache.iotdb.confignode.consensus.response.partition.SchemaNodeManagementResp; import org.apache.iotdb.confignode.exception.physical.UnknownPhysicalPlanTypeException; +import org.apache.iotdb.confignode.manager.externalservice.ExternalServiceInfo; import org.apache.iotdb.confignode.manager.pipe.agent.PipeConfigNodeAgent; import org.apache.iotdb.confignode.persistence.ClusterInfo; import org.apache.iotdb.confignode.persistence.ProcedureInfo; @@ -204,6 +210,8 @@ public class ConfigPlanExecutor { private final CQInfo cqInfo; + private final ExternalServiceInfo externalServiceInfo; + private final PipeInfo pipeInfo; private final SubscriptionInfo subscriptionInfo; @@ -222,6 +230,7 @@ public ConfigPlanExecutor( UDFInfo udfInfo, TriggerInfo triggerInfo, CQInfo cqInfo, + ExternalServiceInfo externalServiceInfo, PipeInfo pipeInfo, SubscriptionInfo subscriptionInfo, QuotaInfo quotaInfo, @@ -253,6 +262,9 @@ public ConfigPlanExecutor( this.cqInfo = cqInfo; this.snapshotProcessorList.add(cqInfo); + this.externalServiceInfo = externalServiceInfo; + this.snapshotProcessorList.add(externalServiceInfo); + this.pipeInfo = pipeInfo; this.snapshotProcessorList.add(pipeInfo); @@ -344,6 +356,8 @@ public DataSet executeQueryPlan(final ConfigPhysicalReadPlan req) return partitionInfo.getSeriesSlotList((GetSeriesSlotListPlan) req); case SHOW_CQ: return cqInfo.showCQ(); + case ShowExternalService: + return externalServiceInfo.showService(((ShowExternalServicePlan) req).getDataNodeIds()); case GetFunctionTable: return udfInfo.getUDFTable((GetFunctionTablePlan) req); case GetFunctionJar: @@ -635,6 +649,14 @@ public TSStatus executeNonQueryPlan(ConfigPhysicalPlan physicalPlan) return cqInfo.activeCQ((ActiveCQPlan) physicalPlan); case UPDATE_CQ_LAST_EXEC_TIME: return cqInfo.updateCQLastExecutionTime((UpdateCQLastExecTimePlan) physicalPlan); + case CreateExternalService: + return externalServiceInfo.addService((CreateExternalServicePlan) physicalPlan); + case StartExternalService: + return externalServiceInfo.startService((StartExternalServicePlan) physicalPlan); + case StopExternalService: + return externalServiceInfo.stopService((StopExternalServicePlan) physicalPlan); + case DropExternalService: + return externalServiceInfo.dropService((DropExternalServicePlan) physicalPlan); case CreatePipePlugin: return pipeInfo.getPipePluginInfo().createPipePlugin((CreatePipePluginPlan) physicalPlan); case DropPipePlugin: diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/protocol/client/ConfigNodeClient.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/protocol/client/ConfigNodeClient.java index f2fb42ca97fb..e2c04caedfb2 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/protocol/client/ConfigNodeClient.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/protocol/client/ConfigNodeClient.java @@ -1346,27 +1346,35 @@ public TShowCQResp showCQ() throws TException { @Override public TSStatus createExternalService(TCreateExternalServiceReq req) throws TException { - return null; + return executeRemoteCallWithRetry( + () -> client.createExternalService(req), resp -> !updateConfigNodeLeader(resp)); } @Override public TSStatus startExternalService(int dataNodeId, String serviceName) throws TException { - return null; + return executeRemoteCallWithRetry( + () -> client.startExternalService(dataNodeId, serviceName), + resp -> !updateConfigNodeLeader(resp)); } @Override public TSStatus stopExternalService(int dataNodeId, String serviceName) throws TException { - return null; + return executeRemoteCallWithRetry( + () -> client.stopExternalService(dataNodeId, serviceName), + resp -> !updateConfigNodeLeader(resp)); } @Override public TSStatus dropExternalService(int dataNodeId, String serviceName) throws TException { - return null; + return executeRemoteCallWithRetry( + () -> client.dropExternalService(dataNodeId, serviceName), + resp -> !updateConfigNodeLeader(resp)); } @Override public TExternalServiceListResp showExternalService(int dataNodeId) throws TException { - return null; + return executeRemoteCallWithRetry( + () -> client.showExternalService(dataNodeId), resp -> !updateConfigNodeLeader(resp.status)); } @Override diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/protocol/thrift/impl/DataNodeInternalRPCServiceImpl.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/protocol/thrift/impl/DataNodeInternalRPCServiceImpl.java index 903167cffe96..845a52538140 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/protocol/thrift/impl/DataNodeInternalRPCServiceImpl.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/protocol/thrift/impl/DataNodeInternalRPCServiceImpl.java @@ -23,6 +23,7 @@ import org.apache.iotdb.common.rpc.thrift.TConsensusGroupId; import org.apache.iotdb.common.rpc.thrift.TDataNodeLocation; import org.apache.iotdb.common.rpc.thrift.TEndPoint; +import org.apache.iotdb.common.rpc.thrift.TExternalServiceEntry; import org.apache.iotdb.common.rpc.thrift.TExternalServiceListResp; import org.apache.iotdb.common.rpc.thrift.TFlushReq; import org.apache.iotdb.common.rpc.thrift.TLoadSample; @@ -187,6 +188,7 @@ import org.apache.iotdb.db.schemaengine.template.TemplateInternalRPCUpdateType; import org.apache.iotdb.db.service.DataNode; import org.apache.iotdb.db.service.RegionMigrateService; +import org.apache.iotdb.db.service.exernalservice.ExternalServiceManagementService; import org.apache.iotdb.db.service.metrics.FileMetrics; import org.apache.iotdb.db.storageengine.StorageEngine; import org.apache.iotdb.db.storageengine.dataregion.DataRegion; @@ -2940,10 +2942,10 @@ public TSStatus dropPipePlugin(TDropPipePluginInstanceReq req) { public TExternalServiceListResp getBuiltInService() { try { - // PipeDataNodeAgent.plugin().deregister(req.getPipePluginName(), req.isNeedToDeleteJar()); - // TODO + List serviceEntries = + ExternalServiceManagementService.getInstance().getBuiltInServices(); return new TExternalServiceListResp( - new TSStatus(TSStatusCode.SUCCESS_STATUS.getStatusCode()), null); + new TSStatus(TSStatusCode.SUCCESS_STATUS.getStatusCode()), serviceEntries); } catch (Exception e) { return new TExternalServiceListResp( new TSStatus(TSStatusCode.GET_BUILTIN_EXTERNAL_SERVICE_ERROR.getStatusCode()) diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/common/header/DatasetHeaderFactory.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/common/header/DatasetHeaderFactory.java index d7fd3e071c64..4d19daca70d5 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/common/header/DatasetHeaderFactory.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/common/header/DatasetHeaderFactory.java @@ -113,6 +113,10 @@ public static DatasetHeader getShowTriggersHeader() { return new DatasetHeader(ColumnHeaderConstant.showTriggersColumnHeaders, true); } + public static DatasetHeader getShowExternalServiceHeader() { + return new DatasetHeader(ColumnHeaderConstant.showExternalServiceColumnHeaders, true); + } + public static DatasetHeader getShowPipePluginsHeader() { return new DatasetHeader(ColumnHeaderConstant.showPipePluginsColumnHeaders, true); } diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/execution/operator/source/relational/InformationSchemaContentSupplierFactory.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/execution/operator/source/relational/InformationSchemaContentSupplierFactory.java index b8fd3a094db1..c7d590b30fb9 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/execution/operator/source/relational/InformationSchemaContentSupplierFactory.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/execution/operator/source/relational/InformationSchemaContentSupplierFactory.java @@ -861,6 +861,8 @@ public boolean hasNext() { } } + // TODO add ExternalService + private static class ConfigurationsSupplier extends TsBlockSupplier { private final Iterator> resultIterator; diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/TableConfigTaskVisitor.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/TableConfigTaskVisitor.java index 4b609aa87eb7..28fc6e8efe39 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/TableConfigTaskVisitor.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/TableConfigTaskVisitor.java @@ -144,6 +144,7 @@ import org.apache.iotdb.db.queryengine.plan.relational.sql.ast.ClearCache; import org.apache.iotdb.db.queryengine.plan.relational.sql.ast.ColumnDefinition; import org.apache.iotdb.db.queryengine.plan.relational.sql.ast.CreateDB; +import org.apache.iotdb.db.queryengine.plan.relational.sql.ast.CreateExternalService; import org.apache.iotdb.db.queryengine.plan.relational.sql.ast.CreateFunction; import org.apache.iotdb.db.queryengine.plan.relational.sql.ast.CreateModel; import org.apache.iotdb.db.queryengine.plan.relational.sql.ast.CreatePipe; @@ -159,6 +160,7 @@ import org.apache.iotdb.db.queryengine.plan.relational.sql.ast.DescribeTable; import org.apache.iotdb.db.queryengine.plan.relational.sql.ast.DropColumn; import org.apache.iotdb.db.queryengine.plan.relational.sql.ast.DropDB; +import org.apache.iotdb.db.queryengine.plan.relational.sql.ast.DropExternalService; import org.apache.iotdb.db.queryengine.plan.relational.sql.ast.DropFunction; import org.apache.iotdb.db.queryengine.plan.relational.sql.ast.DropModel; import org.apache.iotdb.db.queryengine.plan.relational.sql.ast.DropPipe; @@ -206,6 +208,7 @@ import org.apache.iotdb.db.queryengine.plan.relational.sql.ast.ShowCurrentUser; import org.apache.iotdb.db.queryengine.plan.relational.sql.ast.ShowDB; import org.apache.iotdb.db.queryengine.plan.relational.sql.ast.ShowDataNodes; +import org.apache.iotdb.db.queryengine.plan.relational.sql.ast.ShowExternalService; import org.apache.iotdb.db.queryengine.plan.relational.sql.ast.ShowFunctions; import org.apache.iotdb.db.queryengine.plan.relational.sql.ast.ShowLoadedModels; import org.apache.iotdb.db.queryengine.plan.relational.sql.ast.ShowModels; @@ -217,8 +220,10 @@ import org.apache.iotdb.db.queryengine.plan.relational.sql.ast.ShowTopics; import org.apache.iotdb.db.queryengine.plan.relational.sql.ast.ShowVariables; import org.apache.iotdb.db.queryengine.plan.relational.sql.ast.ShowVersion; +import org.apache.iotdb.db.queryengine.plan.relational.sql.ast.StartExternalService; import org.apache.iotdb.db.queryengine.plan.relational.sql.ast.StartPipe; import org.apache.iotdb.db.queryengine.plan.relational.sql.ast.StartRepairData; +import org.apache.iotdb.db.queryengine.plan.relational.sql.ast.StopExternalService; import org.apache.iotdb.db.queryengine.plan.relational.sql.ast.StopPipe; import org.apache.iotdb.db.queryengine.plan.relational.sql.ast.StopRepairData; import org.apache.iotdb.db.queryengine.plan.relational.sql.ast.UnloadModel; @@ -1511,6 +1516,36 @@ protected IConfigTask visitDropFunction(DropFunction node, MPPQueryContext conte return new DropFunctionTask(Model.TABLE, node.getUdfName()); } + @Override + protected IConfigTask visitCreateExternalService( + CreateExternalService node, MPPQueryContext context) { + return super.visitCreateExternalService(node, context); + } + + @Override + protected IConfigTask visitStartExternalService( + StartExternalService node, MPPQueryContext context) { + return super.visitStartExternalService(node, context); + } + + @Override + protected IConfigTask visitStopExternalService( + StopExternalService node, MPPQueryContext context) { + return super.visitStopExternalService(node, context); + } + + @Override + protected IConfigTask visitDropExternalService( + DropExternalService node, MPPQueryContext context) { + return super.visitDropExternalService(node, context); + } + + @Override + protected IConfigTask visitShowExternalService( + ShowExternalService node, MPPQueryContext context) { + return super.visitShowExternalService(node, context); + } + @Override protected IConfigTask visitMigrateRegion(MigrateRegion migrateRegion, MPPQueryContext context) { context.setQueryType(QueryType.WRITE); diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/TreeConfigTaskVisitor.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/TreeConfigTaskVisitor.java index 3e2d408b5a75..60deee795093 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/TreeConfigTaskVisitor.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/TreeConfigTaskVisitor.java @@ -76,6 +76,11 @@ import org.apache.iotdb.db.queryengine.plan.execution.config.metadata.ai.ShowLoadedModelsTask; import org.apache.iotdb.db.queryengine.plan.execution.config.metadata.ai.ShowModelsTask; import org.apache.iotdb.db.queryengine.plan.execution.config.metadata.ai.UnloadModelTask; +import org.apache.iotdb.db.queryengine.plan.execution.config.metadata.externalservice.CreateExternalServiceTask; +import org.apache.iotdb.db.queryengine.plan.execution.config.metadata.externalservice.DropExternalServiceTask; +import org.apache.iotdb.db.queryengine.plan.execution.config.metadata.externalservice.ShowExternalServiceTask; +import org.apache.iotdb.db.queryengine.plan.execution.config.metadata.externalservice.StartExternalServiceTask; +import org.apache.iotdb.db.queryengine.plan.execution.config.metadata.externalservice.StopExternalServiceTask; import org.apache.iotdb.db.queryengine.plan.execution.config.metadata.region.ExtendRegionTask; import org.apache.iotdb.db.queryengine.plan.execution.config.metadata.region.MigrateRegionTask; import org.apache.iotdb.db.queryengine.plan.execution.config.metadata.region.ReconstructRegionTask; @@ -159,6 +164,11 @@ import org.apache.iotdb.db.queryengine.plan.statement.metadata.ShowTriggersStatement; import org.apache.iotdb.db.queryengine.plan.statement.metadata.ShowVariablesStatement; import org.apache.iotdb.db.queryengine.plan.statement.metadata.UnSetTTLStatement; +import org.apache.iotdb.db.queryengine.plan.statement.metadata.externalservice.CreateExternalServiceStatement; +import org.apache.iotdb.db.queryengine.plan.statement.metadata.externalservice.DropExternalServiceStatement; +import org.apache.iotdb.db.queryengine.plan.statement.metadata.externalservice.ShowExternalServiceStatement; +import org.apache.iotdb.db.queryengine.plan.statement.metadata.externalservice.StartExternalServiceStatement; +import org.apache.iotdb.db.queryengine.plan.statement.metadata.externalservice.StopExternalServiceStatement; import org.apache.iotdb.db.queryengine.plan.statement.metadata.model.CreateModelStatement; import org.apache.iotdb.db.queryengine.plan.statement.metadata.model.CreateTrainingStatement; import org.apache.iotdb.db.queryengine.plan.statement.metadata.model.DropModelStatement; @@ -459,6 +469,37 @@ public IConfigTask visitShowTriggers( return new ShowTriggersTask(); } + @Override + public IConfigTask visitCreateExternalService( + CreateExternalServiceStatement createExternalServiceStatement, MPPQueryContext context) { + return new CreateExternalServiceTask(createExternalServiceStatement); + } + + @Override + public IConfigTask visitStartExternalService( + StartExternalServiceStatement startExternalServiceStatement, MPPQueryContext context) { + return new StartExternalServiceTask(startExternalServiceStatement.getServiceName()); + } + + @Override + public IConfigTask visitStopExternalService( + StopExternalServiceStatement stopExternalServiceStatement, MPPQueryContext context) { + return new StopExternalServiceTask(stopExternalServiceStatement.getServiceName()); + } + + @Override + public IConfigTask visitDropExternalService( + DropExternalServiceStatement dropExternalServiceStatement, MPPQueryContext context) { + return new DropExternalServiceTask( + dropExternalServiceStatement.getServiceName(), dropExternalServiceStatement.isForcedly()); + } + + @Override + public IConfigTask visitShowExternalService( + ShowExternalServiceStatement showExternalServiceStatement, MPPQueryContext context) { + return new ShowExternalServiceTask(showExternalServiceStatement.getDataNodeId()); + } + @Override public IConfigTask visitCreatePipePlugin( CreatePipePluginStatement createPipePluginStatement, MPPQueryContext context) { diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/executor/ClusterConfigTaskExecutor.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/executor/ClusterConfigTaskExecutor.java index 10b84690a7a2..5d59c645fb23 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/executor/ClusterConfigTaskExecutor.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/executor/ClusterConfigTaskExecutor.java @@ -35,6 +35,7 @@ import org.apache.iotdb.common.rpc.thrift.TConfigNodeLocation; import org.apache.iotdb.common.rpc.thrift.TDataNodeConfiguration; import org.apache.iotdb.common.rpc.thrift.TDataNodeLocation; +import org.apache.iotdb.common.rpc.thrift.TExternalServiceEntry; import org.apache.iotdb.common.rpc.thrift.TFlushReq; import org.apache.iotdb.common.rpc.thrift.TSStatus; import org.apache.iotdb.common.rpc.thrift.TSetConfigurationReq; @@ -72,6 +73,8 @@ import org.apache.iotdb.commons.pipe.datastructure.visibility.VisibilityUtils; import org.apache.iotdb.commons.pipe.sink.payload.airgap.AirGapPseudoTPipeTransferRequest; import org.apache.iotdb.commons.schema.cache.CacheClearOptions; +import org.apache.iotdb.commons.schema.column.ColumnHeader; +import org.apache.iotdb.commons.schema.column.ColumnHeaderConstant; import org.apache.iotdb.commons.schema.table.AlterOrDropTableOperationType; import org.apache.iotdb.commons.schema.table.TsTable; import org.apache.iotdb.commons.schema.table.TsTableInternalRPCUtil; @@ -313,6 +316,8 @@ import org.apache.iotdb.db.schemaengine.template.alter.TemplateAlterOperationUtil; import org.apache.iotdb.db.schemaengine.template.alter.TemplateExtendInfo; import org.apache.iotdb.db.service.DataNodeInternalRPCService; +import org.apache.iotdb.db.service.exernalservice.ExternalServiceManagementException; +import org.apache.iotdb.db.service.exernalservice.ExternalServiceManagementService; import org.apache.iotdb.db.storageengine.StorageEngine; import org.apache.iotdb.db.storageengine.dataregion.compaction.repair.RepairTaskStatus; import org.apache.iotdb.db.storageengine.dataregion.compaction.schedule.CompactionScheduleTaskManager; @@ -342,6 +347,8 @@ import org.apache.tsfile.common.constant.TsFileConstant; import org.apache.tsfile.enums.TSDataType; import org.apache.tsfile.external.commons.codec.digest.DigestUtils; +import org.apache.tsfile.read.common.block.TsBlockBuilder; +import org.apache.tsfile.read.common.block.column.TimeColumnBuilder; import org.apache.tsfile.utils.ReadWriteIOUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -363,6 +370,7 @@ import java.util.Arrays; import java.util.Collections; import java.util.HashSet; +import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Objects; @@ -377,6 +385,8 @@ import static org.apache.iotdb.commons.schema.SchemaConstant.ALL_MATCH_SCOPE; import static org.apache.iotdb.commons.schema.SchemaConstant.ALL_RESULT_NODES; import static org.apache.iotdb.db.protocol.client.ConfigNodeClient.MSG_RECONNECTION_FAIL; +import static org.apache.iotdb.db.queryengine.common.header.DatasetHeaderFactory.getShowExternalServiceHeader; +import static org.apache.iotdb.db.queryengine.plan.execution.config.metadata.externalservice.ShowExternalServiceTask.appendServiceEntry; import static org.apache.iotdb.db.utils.constant.SqlConstant.ROOT; import static org.apache.iotdb.udf.api.type.Type.OBJECT; @@ -4842,27 +4852,79 @@ public void handlePipeConfigClientExit(final String clientId) { @Override public SettableFuture createExternalService( - int dataNodeId, String serviceName, String className) { - return null; + String serviceName, String className) { + SettableFuture future = SettableFuture.create(); + try { + ExternalServiceManagementService.getInstance().createService(serviceName, className); + future.set(new ConfigTaskResult(new TSStatus(TSStatusCode.SUCCESS_STATUS.getStatusCode()))); + } catch (Exception e) { + future.setException(new ExternalServiceManagementException(e.getMessage())); + } + return future; } @Override public SettableFuture startExternalService(String serviceName) { - return null; + SettableFuture future = SettableFuture.create(); + try { + ExternalServiceManagementService.getInstance().startService(serviceName); + future.set(new ConfigTaskResult(new TSStatus(TSStatusCode.SUCCESS_STATUS.getStatusCode()))); + } catch (Exception e) { + future.setException(new ExternalServiceManagementException(e.getMessage())); + } + return future; } @Override public SettableFuture stopExternalService(String serviceName) { - return null; + SettableFuture future = SettableFuture.create(); + try { + ExternalServiceManagementService.getInstance().stopService(serviceName); + future.set(new ConfigTaskResult(new TSStatus(TSStatusCode.SUCCESS_STATUS.getStatusCode()))); + } catch (Exception e) { + future.setException(new ExternalServiceManagementException(e.getMessage())); + } + return future; } @Override - public SettableFuture dropExternalService(String serviceName) { - return null; + public SettableFuture dropExternalService( + String serviceName, boolean forcedly) { + SettableFuture future = SettableFuture.create(); + try { + ExternalServiceManagementService.getInstance().dropService(serviceName, forcedly); + future.set(new ConfigTaskResult(new TSStatus(TSStatusCode.SUCCESS_STATUS.getStatusCode()))); + } catch (Exception e) { + future.setException(new ExternalServiceManagementException(e.getMessage())); + } + return future; } @Override public SettableFuture showExternalService(int dataNodeId) { - return null; + SettableFuture future = SettableFuture.create(); + try { + Iterator iterator = + ExternalServiceManagementService.getInstance().showService(dataNodeId); + + List outputDataTypes = + ColumnHeaderConstant.showExternalServiceColumnHeaders.stream() + .map(ColumnHeader::getColumnType) + .collect(Collectors.toList()); + TsBlockBuilder builder = new TsBlockBuilder(outputDataTypes); + TimeColumnBuilder timeColumnBuilder = builder.getTimeColumnBuilder(); + while (iterator.hasNext()) { + timeColumnBuilder.writeLong(0L); + appendServiceEntry(iterator.next(), builder.getValueColumnBuilders()); + builder.declarePosition(); + } + + future.set( + new ConfigTaskResult( + TSStatusCode.SUCCESS_STATUS, builder.build(), getShowExternalServiceHeader())); + } catch (Exception e) { + future.setException(e); + } + return future; } } diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/executor/IConfigTaskExecutor.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/executor/IConfigTaskExecutor.java index c5720d448b13..9e02ba6cff7f 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/executor/IConfigTaskExecutor.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/executor/IConfigTaskExecutor.java @@ -315,14 +315,13 @@ SettableFuture showThrottleQuota( void handlePipeConfigClientExit(String clientId); - SettableFuture createExternalService( - int dataNodeId, String serviceName, String className); + SettableFuture createExternalService(String serviceName, String className); SettableFuture startExternalService(String serviceName); SettableFuture stopExternalService(String serviceName); - SettableFuture dropExternalService(String serviceName); + SettableFuture dropExternalService(String serviceName, boolean forcedly); SettableFuture showExternalService(int dataNodeId); diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/metadata/CreateExternalServiceTask.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/metadata/externalservice/CreateExternalServiceTask.java similarity index 85% rename from iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/metadata/CreateExternalServiceTask.java rename to iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/metadata/externalservice/CreateExternalServiceTask.java index 2a0ee442f716..767347c115aa 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/metadata/CreateExternalServiceTask.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/metadata/externalservice/CreateExternalServiceTask.java @@ -17,9 +17,8 @@ * under the License. */ -package org.apache.iotdb.db.queryengine.plan.execution.config.metadata; +package org.apache.iotdb.db.queryengine.plan.execution.config.metadata.externalservice; -import org.apache.iotdb.db.queryengine.common.QueryId; import org.apache.iotdb.db.queryengine.plan.execution.config.ConfigTaskResult; import org.apache.iotdb.db.queryengine.plan.execution.config.IConfigTask; import org.apache.iotdb.db.queryengine.plan.execution.config.executor.IConfigTaskExecutor; @@ -28,28 +27,26 @@ import com.google.common.util.concurrent.ListenableFuture; +import java.util.Locale; + public class CreateExternalServiceTask implements IConfigTask { private final String serviceName; private final String className; - private final int dataNodeId; - public CreateExternalServiceTask(CreateExternalServiceStatement statement) { - this.serviceName = statement.getServiceName(); + this.serviceName = statement.getServiceName().toUpperCase(Locale.ENGLISH); this.className = statement.getClassName(); - this.dataNodeId = QueryId.getDataNodeId(); } public CreateExternalServiceTask(CreateFunction createFunctionStatement) { this.serviceName = createFunctionStatement.getUdfName(); this.className = createFunctionStatement.getClassName(); - this.dataNodeId = QueryId.getDataNodeId(); } @Override public ListenableFuture execute(IConfigTaskExecutor configTaskExecutor) throws InterruptedException { - return configTaskExecutor.createExternalService(dataNodeId, serviceName, className); + return configTaskExecutor.createExternalService(serviceName, className); } } diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/metadata/externalservice/DropExternalServiceTask.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/metadata/externalservice/DropExternalServiceTask.java new file mode 100644 index 000000000000..5cfca89da2bb --- /dev/null +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/metadata/externalservice/DropExternalServiceTask.java @@ -0,0 +1,46 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.apache.iotdb.db.queryengine.plan.execution.config.metadata.externalservice; + +import org.apache.iotdb.db.queryengine.plan.execution.config.ConfigTaskResult; +import org.apache.iotdb.db.queryengine.plan.execution.config.IConfigTask; +import org.apache.iotdb.db.queryengine.plan.execution.config.executor.IConfigTaskExecutor; + +import com.google.common.util.concurrent.ListenableFuture; + +import java.util.Locale; + +public class DropExternalServiceTask implements IConfigTask { + + private final String serviceName; + private final boolean forcedly; + + public DropExternalServiceTask(String serviceName, boolean forcedly) { + this.serviceName = serviceName.toUpperCase(Locale.ENGLISH); + ; + this.forcedly = forcedly; + } + + @Override + public ListenableFuture execute(IConfigTaskExecutor configTaskExecutor) + throws InterruptedException { + return configTaskExecutor.dropExternalService(serviceName, forcedly); + } +} diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/metadata/externalservice/ShowExternalServiceTask.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/metadata/externalservice/ShowExternalServiceTask.java new file mode 100644 index 000000000000..66d0e2e6073b --- /dev/null +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/metadata/externalservice/ShowExternalServiceTask.java @@ -0,0 +1,63 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.apache.iotdb.db.queryengine.plan.execution.config.metadata.externalservice; + +import org.apache.iotdb.common.rpc.thrift.TExternalServiceEntry; +import org.apache.iotdb.commons.externalservice.ServiceInfo; +import org.apache.iotdb.db.queryengine.plan.execution.config.ConfigTaskResult; +import org.apache.iotdb.db.queryengine.plan.execution.config.IConfigTask; +import org.apache.iotdb.db.queryengine.plan.execution.config.executor.IConfigTaskExecutor; + +import com.google.common.util.concurrent.ListenableFuture; +import org.apache.tsfile.block.column.ColumnBuilder; +import org.apache.tsfile.common.conf.TSFileConfig; +import org.apache.tsfile.utils.Binary; + +public class ShowExternalServiceTask implements IConfigTask { + private final int dataNodeId; + + public ShowExternalServiceTask(int dataNodeId) { + this.dataNodeId = dataNodeId; + } + + @Override + public ListenableFuture execute(IConfigTaskExecutor configTaskExecutor) + throws InterruptedException { + return configTaskExecutor.showExternalService(dataNodeId); + } + + public static void appendServiceEntry( + TExternalServiceEntry externalServiceEntry, ColumnBuilder[] columnBuilders) { + columnBuilders[0].writeBinary( + new Binary(externalServiceEntry.getServiceName(), TSFileConfig.STRING_CHARSET)); + columnBuilders[1].writeInt(externalServiceEntry.getDataNodId()); + columnBuilders[2].writeBinary( + new Binary( + ServiceInfo.State.deserialize(externalServiceEntry.getState()).toString(), + TSFileConfig.STRING_CHARSET)); + columnBuilders[3].writeBinary( + new Binary(externalServiceEntry.getClassName(), TSFileConfig.STRING_CHARSET)); + columnBuilders[4].writeBinary( + new Binary( + ServiceInfo.ServiceType.deserialize(externalServiceEntry.getServiceType()) + .getDisplayName(), + TSFileConfig.STRING_CHARSET)); + } +} diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/metadata/externalservice/StartExternalServiceTask.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/metadata/externalservice/StartExternalServiceTask.java new file mode 100644 index 000000000000..5b0d18df8c79 --- /dev/null +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/metadata/externalservice/StartExternalServiceTask.java @@ -0,0 +1,44 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.apache.iotdb.db.queryengine.plan.execution.config.metadata.externalservice; + +import org.apache.iotdb.db.queryengine.plan.execution.config.ConfigTaskResult; +import org.apache.iotdb.db.queryengine.plan.execution.config.IConfigTask; +import org.apache.iotdb.db.queryengine.plan.execution.config.executor.IConfigTaskExecutor; + +import com.google.common.util.concurrent.ListenableFuture; + +import java.util.Locale; + +public class StartExternalServiceTask implements IConfigTask { + + private final String serviceName; + + public StartExternalServiceTask(String serviceName) { + this.serviceName = serviceName.toUpperCase(Locale.ENGLISH); + ; + } + + @Override + public ListenableFuture execute(IConfigTaskExecutor configTaskExecutor) + throws InterruptedException { + return configTaskExecutor.startExternalService(serviceName); + } +} diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/metadata/externalservice/StopExternalServiceTask.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/metadata/externalservice/StopExternalServiceTask.java new file mode 100644 index 000000000000..671cad38965d --- /dev/null +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/metadata/externalservice/StopExternalServiceTask.java @@ -0,0 +1,43 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.apache.iotdb.db.queryengine.plan.execution.config.metadata.externalservice; + +import org.apache.iotdb.db.queryengine.plan.execution.config.ConfigTaskResult; +import org.apache.iotdb.db.queryengine.plan.execution.config.IConfigTask; +import org.apache.iotdb.db.queryengine.plan.execution.config.executor.IConfigTaskExecutor; + +import com.google.common.util.concurrent.ListenableFuture; + +import java.util.Locale; + +public class StopExternalServiceTask implements IConfigTask { + + private final String serviceName; + + public StopExternalServiceTask(String serviceName) { + this.serviceName = serviceName.toUpperCase(Locale.ENGLISH); + } + + @Override + public ListenableFuture execute(IConfigTaskExecutor configTaskExecutor) + throws InterruptedException { + return configTaskExecutor.stopExternalService(serviceName); + } +} diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/parser/ASTVisitor.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/parser/ASTVisitor.java index 93fc90339db3..25d31e7c3382 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/parser/ASTVisitor.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/parser/ASTVisitor.java @@ -1117,7 +1117,7 @@ public Statement visitStopService(IoTDBSqlParser.StopServiceContext ctx) { @Override public Statement visitDropService(IoTDBSqlParser.DropServiceContext ctx) { - return new DropExternalServiceStatement(parseIdentifier(ctx.serviceName.getText())); + return new DropExternalServiceStatement(parseIdentifier(ctx.serviceName.getText()), false); } @Override diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/statement/metadata/externalservice/DropExternalServiceStatement.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/statement/metadata/externalservice/DropExternalServiceStatement.java index 9db372caa267..b480112d7695 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/statement/metadata/externalservice/DropExternalServiceStatement.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/statement/metadata/externalservice/DropExternalServiceStatement.java @@ -32,17 +32,23 @@ public class DropExternalServiceStatement extends Statement implements IConfigStatement { private final String serviceName; + private final boolean forcedly; - public DropExternalServiceStatement(String serviceName) { + public DropExternalServiceStatement(String serviceName, boolean forcedly) { super(); statementType = StatementType.DROP_EXTERNAL_SERVICE; this.serviceName = serviceName; + this.forcedly = forcedly; } public String getServiceName() { return serviceName; } + public boolean isForcedly() { + return forcedly; + } + @Override public R accept(StatementVisitor visitor, C context) { return visitor.visitDropExternalService(this, context); diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/statement/metadata/externalservice/ShowExternalServiceStatement.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/statement/metadata/externalservice/ShowExternalServiceStatement.java index 3f5d3b657077..dfcfbffd3039 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/statement/metadata/externalservice/ShowExternalServiceStatement.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/statement/metadata/externalservice/ShowExternalServiceStatement.java @@ -22,14 +22,14 @@ import org.apache.iotdb.commons.path.PartialPath; import org.apache.iotdb.db.queryengine.plan.analyze.QueryType; import org.apache.iotdb.db.queryengine.plan.statement.IConfigStatement; -import org.apache.iotdb.db.queryengine.plan.statement.Statement; import org.apache.iotdb.db.queryengine.plan.statement.StatementType; import org.apache.iotdb.db.queryengine.plan.statement.StatementVisitor; +import org.apache.iotdb.db.queryengine.plan.statement.metadata.ShowStatement; import java.util.Collections; import java.util.List; -public class ShowExternalServiceStatement extends Statement implements IConfigStatement { +public class ShowExternalServiceStatement extends ShowStatement implements IConfigStatement { private final int dataNodeId; @@ -49,12 +49,12 @@ public R accept(StatementVisitor visitor, C context) { } @Override - public QueryType getQueryType() { - return QueryType.WRITE; + public List getPaths() { + return Collections.emptyList(); } @Override - public List getPaths() { - return Collections.emptyList(); + public QueryType getQueryType() { + return QueryType.READ; } } diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/service/exernalservice/ExternalServiceManagementService.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/service/exernalservice/ExternalServiceManagementService.java index 46f131a40dec..49d0ce22e8b8 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/service/exernalservice/ExternalServiceManagementService.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/service/exernalservice/ExternalServiceManagementService.java @@ -19,11 +19,22 @@ package org.apache.iotdb.db.service.exernalservice; -import org.apache.iotdb.commons.exception.StartupException; +import org.apache.iotdb.common.rpc.thrift.TExternalServiceEntry; +import org.apache.iotdb.common.rpc.thrift.TExternalServiceListResp; +import org.apache.iotdb.common.rpc.thrift.TSStatus; +import org.apache.iotdb.commons.client.exception.ClientManagerException; +import org.apache.iotdb.commons.exception.IoTDBRuntimeException; import org.apache.iotdb.commons.externalservice.ServiceInfo; +import org.apache.iotdb.confignode.rpc.thrift.TCreateExternalServiceReq; import org.apache.iotdb.db.conf.IoTDBDescriptor; +import org.apache.iotdb.db.protocol.client.ConfigNodeClient; +import org.apache.iotdb.db.protocol.client.ConfigNodeClientManager; +import org.apache.iotdb.db.protocol.client.ConfigNodeInfo; +import org.apache.iotdb.db.queryengine.common.QueryId; import org.apache.iotdb.externalservice.api.IExternalService; +import org.apache.iotdb.rpc.TSStatusCode; +import org.apache.thrift.TException; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -32,6 +43,7 @@ import java.io.IOException; import java.lang.reflect.InvocationTargetException; import java.util.HashMap; +import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.concurrent.locks.ReadWriteLock; @@ -55,30 +67,49 @@ public class ExternalServiceManagementService { private ExternalServiceManagementService(String libRoot) { this.serviceInfos = new HashMap<>(); + restoreBuiltInServices(); this.libRoot = libRoot; } - public List getAllServices() { + public Iterator showService(int dataNodeId) + throws ClientManagerException, TException { try { - lock.writeLock().lock(); - return serviceInfos.values().stream().collect(Collectors.toList()); + lock.readLock().lock(); + + ConfigNodeClient client = + ConfigNodeClientManager.getInstance().borrowClient(ConfigNodeInfo.CONFIG_REGION_ID); + TExternalServiceListResp resp = client.showExternalService(dataNodeId); + if (resp.getStatus().getCode() != TSStatusCode.SUCCESS_STATUS.getStatusCode()) { + throw new IoTDBRuntimeException(resp.getStatus().message, resp.getStatus().code); + } + return resp.getExternalServiceInfos().iterator(); } finally { - lock.writeLock().unlock(); + lock.readLock().unlock(); } } - public void createService(String serviceName, String className) { + public void createService(String serviceName, String className) + throws ClientManagerException, TException { try { lock.writeLock().lock(); + // 1. validate if (serviceInfos.containsKey(serviceName)) { throw new ExternalServiceManagementException( String.format("Failed to create External Service %s, it already exists!", serviceName)); } - /*logAccessor.writeWal( - new ServiceLogAccessor.ServiceWalEntry( - ServiceLogAccessor.OperationType.CREATE, serviceName, className));*/ + // 2. persist on CN + ConfigNodeClient client = + ConfigNodeClientManager.getInstance().borrowClient(ConfigNodeInfo.CONFIG_REGION_ID); + TSStatus status = + client.createExternalService( + new TCreateExternalServiceReq(QueryId.getDataNodeId(), serviceName, className)); + if (status.getCode() != TSStatusCode.SUCCESS_STATUS.getStatusCode()) { + throw new IoTDBRuntimeException(status.message, status.code); + } + + // 3. modify memory info serviceInfos.put( serviceName, new ServiceInfo(serviceName, className, ServiceInfo.ServiceType.USER_DEFINED)); @@ -87,17 +118,19 @@ public void createService(String serviceName, String className) { } } - public void startService(String serviceName) { + public void startService(String serviceName) throws ClientManagerException, TException { try { lock.writeLock().lock(); + // 1. validate ServiceInfo serviceInfo = serviceInfos.get(serviceName); if (serviceInfo == null) { throw new ExternalServiceManagementException( String.format( - "Failed to stop External Service %s, because it is not existed!", serviceName)); + "Failed to start External Service %s, because it is not existed!", serviceName)); } + // 2. call start method of ServiceInstance, create if Instance was not created if (serviceInfo.getState() == RUNNING) { return; } else { @@ -110,9 +143,16 @@ public void startService(String serviceName) { } } - /*logAccessor.writeWal( - new ServiceLogAccessor.ServiceWalEntry( - ServiceLogAccessor.OperationType.START, serviceName, null));*/ + // 3. persist on CN, rollback if failed + ConfigNodeClient client = + ConfigNodeClientManager.getInstance().borrowClient(ConfigNodeInfo.CONFIG_REGION_ID); + TSStatus status = client.startExternalService(QueryId.getDataNodeId(), serviceName); + if (status.getCode() != TSStatusCode.SUCCESS_STATUS.getStatusCode()) { + serviceInfo.getServiceInstance().stop(); + throw new IoTDBRuntimeException(status.message, status.code); + } + + // 4. modify memory info serviceInfo.setState(RUNNING); } finally { lock.writeLock().unlock(); @@ -140,10 +180,11 @@ private IExternalService createExternalServiceInstance(String serviceName) { } } - public void stopService(String serviceName) { + public void stopService(String serviceName) throws ClientManagerException, TException { try { lock.writeLock().lock(); + // 1. validate ServiceInfo serviceInfo = serviceInfos.get(serviceName); if (serviceInfo == null) { throw new ExternalServiceManagementException( @@ -151,6 +192,7 @@ public void stopService(String serviceName) { "Failed to start External Service %s, because it is not existed!", serviceName)); } + // 2. call stop method of ServiceInstance if (serviceInfo.getState() == STOPPED) { return; } else { @@ -158,9 +200,16 @@ public void stopService(String serviceName) { stopService(serviceInfo); } - /*logAccessor.writeWal( - new ServiceLogAccessor.ServiceWalEntry( - ServiceLogAccessor.OperationType.STOP, serviceName, null));*/ + // 3. persist on CN, rollback if failed + ConfigNodeClient client = + ConfigNodeClientManager.getInstance().borrowClient(ConfigNodeInfo.CONFIG_REGION_ID); + TSStatus status = client.stopExternalService(QueryId.getDataNodeId(), serviceName); + if (status.getCode() != TSStatusCode.SUCCESS_STATUS.getStatusCode()) { + serviceInfo.getServiceInstance().start(); + throw new IoTDBRuntimeException(status.message, status.code); + } + + // 4. modify memory info serviceInfo.setState(STOPPED); } finally { lock.writeLock().unlock(); @@ -175,10 +224,12 @@ private void stopService(ServiceInfo serviceInfo) { serviceInfo.getServiceInstance().stop(); } - public void dropService(String serviceName, boolean forcedly) { + public void dropService(String serviceName, boolean forcedly) + throws ClientManagerException, TException { try { lock.writeLock().lock(); + // 1. validate ServiceInfo serviceInfo = serviceInfos.get(serviceName); if (serviceInfo == null) { throw new ExternalServiceManagementException( @@ -191,6 +242,7 @@ public void dropService(String serviceName, boolean forcedly) { "Failed to drop External Service %s, because it is BUILT-IN!", serviceName)); } + // 2. stop or fail when service are not stopped if (serviceInfo.getState() == STOPPED) { // do nothing } else { @@ -211,21 +263,24 @@ public void dropService(String serviceName, boolean forcedly) { } } - /*logAccessor.writeWal( - new ServiceLogAccessor.ServiceWalEntry( - ServiceLogAccessor.OperationType.DROP, serviceName, null));*/ + // 3. persist on CN + ConfigNodeClient client = + ConfigNodeClientManager.getInstance().borrowClient(ConfigNodeInfo.CONFIG_REGION_ID); + TSStatus status = client.dropExternalService(QueryId.getDataNodeId(), serviceName); + if (status.getCode() != TSStatusCode.SUCCESS_STATUS.getStatusCode()) { + throw new IoTDBRuntimeException(status.message, status.code); + } + + // 4. modify memory info serviceInfos.remove(serviceName); } finally { lock.writeLock().unlock(); } } - public void start() throws StartupException { + public void restoreRunningServices() { lock.writeLock().lock(); try { - restoreBuiltInServices(); - restoreUserDefinedServices(); - // start services with RUNNING state serviceInfos .values() @@ -253,8 +308,23 @@ private void restoreBuiltInServices() { } } - private void restoreUserDefinedServices() { - // TODO + public List getBuiltInServices() { + try { + lock.readLock().lock(); + return serviceInfos.values().stream() + .filter(serviceInfo -> serviceInfo.getServiceType() == ServiceInfo.ServiceType.BUILTIN) + .map( + serviceInfo -> + new TExternalServiceEntry( + serviceInfo.getServiceName(), + serviceInfo.getClassName(), + serviceInfo.getState().getValue(), + QueryId.getDataNodeId(), + ServiceInfo.ServiceType.BUILTIN.getValue())) + .collect(Collectors.toList()); + } finally { + lock.readLock().unlock(); + } } public static ExternalServiceManagementService getInstance() { diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/externalservice/ServiceInfo.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/externalservice/ServiceInfo.java index c469d3835f96..b164adc1ce58 100644 --- a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/externalservice/ServiceInfo.java +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/externalservice/ServiceInfo.java @@ -111,8 +111,36 @@ public int hashCode() { } public enum ServiceType { - BUILTIN, - USER_DEFINED + BUILTIN((byte) 0, "built-in"), + USER_DEFINED((byte) 1, "user-defined"); + + private final byte value; + + private final String displayName; + + ServiceType(byte value, String displayName) { + this.value = value; + this.displayName = displayName; + } + + public byte getValue() { + return value; + } + + public String getDisplayName() { + return displayName; + } + + public static ServiceType deserialize(byte t) { + switch (t) { + case 0: + return BUILTIN; + case 1: + return USER_DEFINED; + default: + throw new IllegalArgumentException("Unknown ServiceType: " + t); + } + } } public enum State { diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/schema/column/ColumnHeaderConstant.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/schema/column/ColumnHeaderConstant.java index 0459d4d2c86e..4addf29d69b0 100644 --- a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/schema/column/ColumnHeaderConstant.java +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/schema/column/ColumnHeaderConstant.java @@ -119,6 +119,10 @@ private ColumnHeaderConstant() { public static final String PATH_PATTERN = "PathPattern"; public static final String CLASS_NAME = "ClassName"; + // column names for show services statement + public static final String SERVICE_NAME = "ServiceName"; + public static final String SERVICE_TYPE = "ServiceType"; + // column names for show pipe plugins statement public static final String PLUGIN_NAME = "PluginName"; public static final String PLUGIN_TYPE = "PluginType"; @@ -546,6 +550,14 @@ private ColumnHeaderConstant() { new ColumnHeader(CLASS_NAME, TSDataType.TEXT), new ColumnHeader(NODE_ID, TSDataType.TEXT)); + public static final List showExternalServiceColumnHeaders = + ImmutableList.of( + new ColumnHeader(SERVICE_NAME, TSDataType.TEXT), + new ColumnHeader(DATA_NODE_ID, TSDataType.INT32), + new ColumnHeader(STATE, TSDataType.TEXT), + new ColumnHeader(CLASS_NAME, TSDataType.TEXT), + new ColumnHeader(SERVICE_TYPE, TSDataType.TEXT)); + public static final List showPipePluginsColumnHeaders = ImmutableList.of( new ColumnHeader(PLUGIN_NAME, TSDataType.TEXT), diff --git a/iotdb-core/relational-grammar/src/main/antlr4/org/apache/iotdb/db/relational/grammar/sql/RelationalSql.g4 b/iotdb-core/relational-grammar/src/main/antlr4/org/apache/iotdb/db/relational/grammar/sql/RelationalSql.g4 index 98ded23c4145..1c7c8269a22d 100644 --- a/iotdb-core/relational-grammar/src/main/antlr4/org/apache/iotdb/db/relational/grammar/sql/RelationalSql.g4 +++ b/iotdb-core/relational-grammar/src/main/antlr4/org/apache/iotdb/db/relational/grammar/sql/RelationalSql.g4 @@ -396,7 +396,7 @@ stopServiceStatement ; dropServiceStatement - : DROP SERVICE serviceName=identifier + : DROP SERVICE serviceName=identifier FORCEDLY? ; showServiceStatement @@ -1604,6 +1604,7 @@ FIRST: 'FIRST'; FLUSH: 'FLUSH'; FOLLOWING: 'FOLLOWING'; FOR: 'FOR'; +FORCEDLY: 'FORCEDLY'; FORMAT: 'FORMAT'; FROM: 'FROM'; FULL: 'FULL'; diff --git a/iotdb-protocol/thrift-commons/src/main/thrift/common.thrift b/iotdb-protocol/thrift-commons/src/main/thrift/common.thrift index ae12a4a5bbe9..df69806250eb 100644 --- a/iotdb-protocol/thrift-commons/src/main/thrift/common.thrift +++ b/iotdb-protocol/thrift-commons/src/main/thrift/common.thrift @@ -271,6 +271,8 @@ struct TExternalServiceEntry { 1: required string serviceName 2: required string className 3: required byte state + 4: required i32 dataNodId + 5: required byte serviceType } enum TAggregationType { From cd49eee32fe79351eaf049a49bc4fd92a89ded2c Mon Sep 17 00:00:00 2001 From: Weihao Li <18110526956@163.com> Date: Mon, 12 Jan 2026 15:33:47 +0800 Subject: [PATCH 5/6] fix Some Signed-off-by: Weihao Li <18110526956@163.com> --- .../externalservice/ExternalServiceInfo.java | 103 ++++++++++++++++-- .../persistence/ExternalServiceInfoTest.java | 91 ++++++++++++++++ ...formationSchemaContentSupplierFactory.java | 37 ++++++- .../db/queryengine/plan/Coordinator.java | 2 - .../config/TableConfigTaskVisitor.java | 26 +++-- .../CreateExternalServiceTask.java | 8 +- .../DropExternalServiceTask.java | 1 - .../DataNodeLocationSupplierFactory.java | 1 + .../security/TreeAccessCheckVisitor.java | 10 +- .../sql/ast/DropExternalService.java | 8 +- .../sql/ast/ShowExternalService.java | 61 ++--------- .../relational/sql/parser/AstBuilder.java | 19 +++- .../relational/sql/rewrite/ShowRewrite.java | 15 +++ .../commons/externalservice/ServiceInfo.java | 13 ++- .../schema/column/ColumnHeaderConstant.java | 3 + .../schema/table/InformationSchema.java | 16 +++ 16 files changed, 320 insertions(+), 94 deletions(-) create mode 100644 iotdb-core/confignode/src/test/java/org/apache/iotdb/confignode/persistence/ExternalServiceInfoTest.java diff --git a/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/manager/externalservice/ExternalServiceInfo.java b/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/manager/externalservice/ExternalServiceInfo.java index 4cad78108cc3..40420e785d0c 100644 --- a/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/manager/externalservice/ExternalServiceInfo.java +++ b/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/manager/externalservice/ExternalServiceInfo.java @@ -23,6 +23,7 @@ import org.apache.iotdb.common.rpc.thrift.TSStatus; import org.apache.iotdb.commons.externalservice.ServiceInfo; import org.apache.iotdb.commons.snapshot.SnapshotProcessor; +import org.apache.iotdb.commons.utils.TestOnly; import org.apache.iotdb.confignode.consensus.request.write.externalservice.CreateExternalServicePlan; import org.apache.iotdb.confignode.consensus.request.write.externalservice.DropExternalServicePlan; import org.apache.iotdb.confignode.consensus.request.write.externalservice.StartExternalServicePlan; @@ -30,17 +31,23 @@ import org.apache.iotdb.confignode.consensus.response.externalservice.ShowExternalServiceResp; import org.apache.iotdb.rpc.TSStatusCode; +import org.apache.tsfile.utils.ReadWriteIOUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import java.io.ByteArrayOutputStream; +import java.io.DataOutputStream; import java.io.File; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; import java.util.Map; import java.util.Set; import java.util.concurrent.ConcurrentHashMap; import java.util.stream.Collectors; +import java.util.zip.CRC32; import static com.google.common.base.Preconditions.checkState; @@ -51,6 +58,8 @@ public class ExternalServiceInfo implements SnapshotProcessor { private final Map> datanodeToServiceInfos; private static final String SNAPSHOT_FILENAME = "service_info.bin"; + private static final int SERIALIZATION_VERSION = 1; + private final CRC32 crc32 = new CRC32(); public ExternalServiceInfo() { datanodeToServiceInfos = new ConcurrentHashMap<>(); @@ -180,6 +189,79 @@ public ShowExternalServiceResp showService(Set dataNodes) { .collect(Collectors.toList())); } + private void serializeInfos(OutputStream outputStream) throws IOException { + ReadWriteIOUtils.write(SERIALIZATION_VERSION, outputStream); + ReadWriteIOUtils.write(datanodeToServiceInfos.size(), outputStream); + for (Map.Entry> outerEntry : + datanodeToServiceInfos.entrySet()) { + ReadWriteIOUtils.write(outerEntry.getKey(), outputStream); // DataNode ID + + Map innerMap = outerEntry.getValue(); + // inner Map + ReadWriteIOUtils.write(innerMap.size(), outputStream); + for (ServiceInfo innerEntry : innerMap.values()) { + serializeServiceInfoWithCRC(innerEntry, outputStream); + } + } + } + + private void serializeServiceInfoWithCRC(ServiceInfo serviceInfo, OutputStream outputStream) + throws IOException { + ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream(); + DataOutputStream tempDos = new DataOutputStream(byteArrayOutputStream); + serviceInfo.serialize(tempDos); + tempDos.flush(); + byte[] bytes = byteArrayOutputStream.toByteArray(); + + crc32.reset(); + crc32.update(bytes, 0, bytes.length); + + ReadWriteIOUtils.write(bytes.length, outputStream); + outputStream.write(bytes); + ReadWriteIOUtils.write(crc32.getValue(), outputStream); + } + + private void deserializeInfos(InputStream inputStream) throws IOException { + if (ReadWriteIOUtils.readInt(inputStream) != SERIALIZATION_VERSION) { + throw new IOException("Incorrect version of " + SNAPSHOT_FILENAME); + } + + int outerSize = ReadWriteIOUtils.readInt(inputStream); + for (int i = 0; i < outerSize; i++) { + int dataNodeId = ReadWriteIOUtils.readInt(inputStream); + int innerSize = ReadWriteIOUtils.readInt(inputStream); + + Map innerMap = + datanodeToServiceInfos.computeIfAbsent( + dataNodeId, k -> new ConcurrentHashMap<>(innerSize)); + for (int j = 0; j < innerSize; j++) { + ServiceInfo value = deserializeServiceInfoConsiderCRC(inputStream); + if (value != null) { + innerMap.put(value.getServiceName(), value); + } + } + datanodeToServiceInfos.put(dataNodeId, innerMap); + } + } + + private ServiceInfo deserializeServiceInfoConsiderCRC(InputStream inputStream) + throws IOException { + int length = ReadWriteIOUtils.readInt(inputStream); + byte[] bytes = new byte[length]; + inputStream.read(bytes); + + crc32.reset(); + crc32.update(bytes, 0, length); + + long expectedCRC = ReadWriteIOUtils.readLong(inputStream); + if (crc32.getValue() != expectedCRC) { + LOGGER.error("Mismatched CRC32 code when deserializing service info."); + return null; + } + + return ServiceInfo.deserialize(inputStream); + } + @Override public boolean processTakeSnapshot(File snapshotDir) throws IOException { File snapshotFile = new File(snapshotDir, SNAPSHOT_FILENAME); @@ -192,10 +274,7 @@ public boolean processTakeSnapshot(File snapshotDir) throws IOException { try (FileOutputStream fileOutputStream = new FileOutputStream(snapshotFile)) { - // TODO - // serializeExistedJarToMD5(fileOutputStream); - - // udfTable.serializeUDFTable(fileOutputStream); + serializeInfos(fileOutputStream); // fsync fileOutputStream.getFD().sync(); @@ -214,21 +293,21 @@ public void processLoadSnapshot(File snapshotDir) throws IOException { return; } - // acquireUDFTableLock(); try (FileInputStream fileInputStream = new FileInputStream(snapshotFile)) { clear(); - // deserializeExistedJarToMD5(fileInputStream); - - // udfTable.deserializeUDFTable(fileInputStream); - } finally { - // releaseUDFTableLock(); + deserializeInfos(fileInputStream); } } public void clear() { - // existedJarToMD5.clear(); - // udfTable.clear(); + datanodeToServiceInfos.values().forEach(subMap -> subMap.clear()); + datanodeToServiceInfos.clear(); + } + + @TestOnly + public Map> getRawDatanodeToServiceInfos() { + return datanodeToServiceInfos; } } diff --git a/iotdb-core/confignode/src/test/java/org/apache/iotdb/confignode/persistence/ExternalServiceInfoTest.java b/iotdb-core/confignode/src/test/java/org/apache/iotdb/confignode/persistence/ExternalServiceInfoTest.java new file mode 100644 index 000000000000..9e3a284b1f6e --- /dev/null +++ b/iotdb-core/confignode/src/test/java/org/apache/iotdb/confignode/persistence/ExternalServiceInfoTest.java @@ -0,0 +1,91 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.apache.iotdb.confignode.persistence; + +import org.apache.iotdb.commons.exception.IllegalPathException; +import org.apache.iotdb.commons.externalservice.ServiceInfo; +import org.apache.iotdb.confignode.consensus.request.write.externalservice.CreateExternalServicePlan; +import org.apache.iotdb.confignode.consensus.request.write.externalservice.StartExternalServicePlan; +import org.apache.iotdb.confignode.manager.externalservice.ExternalServiceInfo; + +import org.apache.thrift.TException; +import org.apache.tsfile.external.commons.io.FileUtils; +import org.junit.AfterClass; +import org.junit.Assert; +import org.junit.BeforeClass; +import org.junit.Ignore; +import org.junit.Test; + +import java.io.File; +import java.io.IOException; + +import static org.apache.iotdb.db.utils.constant.TestConstant.BASE_OUTPUT_PATH; + +public class ExternalServiceInfoTest { + + private static ExternalServiceInfo serviceInfo; + private static ExternalServiceInfo serviceInfoBefore; + private static final File snapshotDir = new File(BASE_OUTPUT_PATH, "snapshot"); + + @BeforeClass + public static void setup() throws IOException { + serviceInfo = new ExternalServiceInfo(); + serviceInfoBefore = new ExternalServiceInfo(); + if (!snapshotDir.exists()) { + snapshotDir.mkdirs(); + } + } + + @AfterClass + public static void cleanup() throws IOException { + serviceInfo.clear(); + if (snapshotDir.exists()) { + FileUtils.deleteDirectory(snapshotDir); + } + } + + @Ignore + @Test + public void testSnapshot() throws TException, IOException, IllegalPathException { + CreateExternalServicePlan createExternalServicePlan = + new CreateExternalServicePlan( + 1, new ServiceInfo("TEST1", "testClassName", ServiceInfo.ServiceType.USER_DEFINED)); + serviceInfo.addService(createExternalServicePlan); + serviceInfoBefore.addService(createExternalServicePlan); + + createExternalServicePlan = + new CreateExternalServicePlan( + 2, new ServiceInfo("TEST1", "testClassName", ServiceInfo.ServiceType.USER_DEFINED)); + serviceInfo.addService(createExternalServicePlan); + serviceInfoBefore.addService(createExternalServicePlan); + + StartExternalServicePlan startExternalServicePlan = new StartExternalServicePlan(1, "TEST1"); + serviceInfo.startService(startExternalServicePlan); + serviceInfoBefore.startService(startExternalServicePlan); + + serviceInfo.processTakeSnapshot(snapshotDir); + serviceInfo.clear(); + serviceInfo.processLoadSnapshot(snapshotDir); + + Assert.assertEquals( + serviceInfo.getRawDatanodeToServiceInfos(), + serviceInfoBefore.getRawDatanodeToServiceInfos()); + } +} diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/execution/operator/source/relational/InformationSchemaContentSupplierFactory.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/execution/operator/source/relational/InformationSchemaContentSupplierFactory.java index c7d590b30fb9..8774b670e7a3 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/execution/operator/source/relational/InformationSchemaContentSupplierFactory.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/execution/operator/source/relational/InformationSchemaContentSupplierFactory.java @@ -24,6 +24,8 @@ import org.apache.iotdb.common.rpc.thrift.TConfigNodeLocation; import org.apache.iotdb.common.rpc.thrift.TConsensusGroupType; import org.apache.iotdb.common.rpc.thrift.TDataNodeLocation; +import org.apache.iotdb.common.rpc.thrift.TExternalServiceEntry; +import org.apache.iotdb.common.rpc.thrift.TExternalServiceListResp; import org.apache.iotdb.commons.audit.UserEntity; import org.apache.iotdb.commons.client.exception.ClientManagerException; import org.apache.iotdb.commons.conf.IoTDBConstant; @@ -126,6 +128,7 @@ import static org.apache.iotdb.db.queryengine.plan.execution.config.metadata.ShowFunctionsTask.getFunctionType; import static org.apache.iotdb.db.queryengine.plan.execution.config.metadata.ShowPipePluginsTask.PIPE_PLUGIN_TYPE_BUILTIN; import static org.apache.iotdb.db.queryengine.plan.execution.config.metadata.ShowPipePluginsTask.PIPE_PLUGIN_TYPE_EXTERNAL; +import static org.apache.iotdb.db.queryengine.plan.execution.config.metadata.externalservice.ShowExternalServiceTask.appendServiceEntry; public class InformationSchemaContentSupplierFactory { @@ -178,6 +181,8 @@ public static Iterator getSupplier( return new CurrentQueriesSupplier(dataTypes, predicate, userEntity); case InformationSchema.QUERIES_COSTS_HISTOGRAM: return new QueriesCostsHistogramSupplier(dataTypes, userEntity); + case InformationSchema.SERVICES: + return new ServicesSupplier(dataTypes, userEntity); default: throw new UnsupportedOperationException("Unknown table: " + tableName); } @@ -861,7 +866,37 @@ public boolean hasNext() { } } - // TODO add ExternalService + private static class ServicesSupplier extends TsBlockSupplier { + + private final Iterator serviceEntryIterator; + + private ServicesSupplier(final List dataTypes, final UserEntity userEntity) + throws Exception { + super(dataTypes); + accessControl.checkUserGlobalSysPrivilege(userEntity); + try (final ConfigNodeClient client = + ConfigNodeClientManager.getInstance().borrowClient(ConfigNodeInfo.CONFIG_REGION_ID)) { + // -1 means get all services + TExternalServiceListResp resp = client.showExternalService(-1); + if (resp.getStatus().getCode() != TSStatusCode.SUCCESS_STATUS.getStatusCode()) { + throw new IoTDBRuntimeException(resp.getStatus()); + } + + serviceEntryIterator = resp.getExternalServiceInfosIterator(); + } + } + + @Override + protected void constructLine() { + appendServiceEntry(serviceEntryIterator.next(), columnBuilders); + resultBuilder.declarePosition(); + } + + @Override + public boolean hasNext() { + return serviceEntryIterator.hasNext(); + } + } private static class ConfigurationsSupplier extends TsBlockSupplier { private final Iterator> resultIterator; diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/Coordinator.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/Coordinator.java index 497c9357d942..0007f5dc7974 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/Coordinator.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/Coordinator.java @@ -125,7 +125,6 @@ import org.apache.iotdb.db.queryengine.plan.relational.sql.ast.ShowCurrentUser; import org.apache.iotdb.db.queryengine.plan.relational.sql.ast.ShowDB; import org.apache.iotdb.db.queryengine.plan.relational.sql.ast.ShowDataNodes; -import org.apache.iotdb.db.queryengine.plan.relational.sql.ast.ShowExternalService; import org.apache.iotdb.db.queryengine.plan.relational.sql.ast.ShowFunctions; import org.apache.iotdb.db.queryengine.plan.relational.sql.ast.ShowLoadedModels; import org.apache.iotdb.db.queryengine.plan.relational.sql.ast.ShowModels; @@ -590,7 +589,6 @@ private IQueryExecution createQueryExecutionForTableModel( || statement instanceof StartExternalService || statement instanceof StopExternalService || statement instanceof DropExternalService - || statement instanceof ShowExternalService || statement instanceof RelationalAuthorStatement || statement instanceof MigrateRegion || statement instanceof ReconstructRegion diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/TableConfigTaskVisitor.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/TableConfigTaskVisitor.java index 28fc6e8efe39..acbe0dd48651 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/TableConfigTaskVisitor.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/TableConfigTaskVisitor.java @@ -71,6 +71,10 @@ import org.apache.iotdb.db.queryengine.plan.execution.config.metadata.ai.ShowLoadedModelsTask; import org.apache.iotdb.db.queryengine.plan.execution.config.metadata.ai.ShowModelsTask; import org.apache.iotdb.db.queryengine.plan.execution.config.metadata.ai.UnloadModelTask; +import org.apache.iotdb.db.queryengine.plan.execution.config.metadata.externalservice.CreateExternalServiceTask; +import org.apache.iotdb.db.queryengine.plan.execution.config.metadata.externalservice.DropExternalServiceTask; +import org.apache.iotdb.db.queryengine.plan.execution.config.metadata.externalservice.StartExternalServiceTask; +import org.apache.iotdb.db.queryengine.plan.execution.config.metadata.externalservice.StopExternalServiceTask; import org.apache.iotdb.db.queryengine.plan.execution.config.metadata.region.ExtendRegionTask; import org.apache.iotdb.db.queryengine.plan.execution.config.metadata.region.MigrateRegionTask; import org.apache.iotdb.db.queryengine.plan.execution.config.metadata.region.ReconstructRegionTask; @@ -208,7 +212,6 @@ import org.apache.iotdb.db.queryengine.plan.relational.sql.ast.ShowCurrentUser; import org.apache.iotdb.db.queryengine.plan.relational.sql.ast.ShowDB; import org.apache.iotdb.db.queryengine.plan.relational.sql.ast.ShowDataNodes; -import org.apache.iotdb.db.queryengine.plan.relational.sql.ast.ShowExternalService; import org.apache.iotdb.db.queryengine.plan.relational.sql.ast.ShowFunctions; import org.apache.iotdb.db.queryengine.plan.relational.sql.ast.ShowLoadedModels; import org.apache.iotdb.db.queryengine.plan.relational.sql.ast.ShowModels; @@ -1519,31 +1522,32 @@ protected IConfigTask visitDropFunction(DropFunction node, MPPQueryContext conte @Override protected IConfigTask visitCreateExternalService( CreateExternalService node, MPPQueryContext context) { - return super.visitCreateExternalService(node, context); + context.setQueryType(QueryType.WRITE); + return new CreateExternalServiceTask(node); } @Override protected IConfigTask visitStartExternalService( StartExternalService node, MPPQueryContext context) { - return super.visitStartExternalService(node, context); + context.setQueryType(QueryType.WRITE); + accessControl.checkUserGlobalSysPrivilege(context); + return new StartExternalServiceTask(node.getServiceName()); } @Override protected IConfigTask visitStopExternalService( StopExternalService node, MPPQueryContext context) { - return super.visitStopExternalService(node, context); + context.setQueryType(QueryType.WRITE); + accessControl.checkUserGlobalSysPrivilege(context); + return new StopExternalServiceTask(node.getServiceName()); } @Override protected IConfigTask visitDropExternalService( DropExternalService node, MPPQueryContext context) { - return super.visitDropExternalService(node, context); - } - - @Override - protected IConfigTask visitShowExternalService( - ShowExternalService node, MPPQueryContext context) { - return super.visitShowExternalService(node, context); + context.setQueryType(QueryType.WRITE); + accessControl.checkUserGlobalSysPrivilege(context); + return new DropExternalServiceTask(node.getServiceName(), node.isForcedly()); } @Override diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/metadata/externalservice/CreateExternalServiceTask.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/metadata/externalservice/CreateExternalServiceTask.java index 767347c115aa..948609333fa8 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/metadata/externalservice/CreateExternalServiceTask.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/metadata/externalservice/CreateExternalServiceTask.java @@ -22,7 +22,7 @@ import org.apache.iotdb.db.queryengine.plan.execution.config.ConfigTaskResult; import org.apache.iotdb.db.queryengine.plan.execution.config.IConfigTask; import org.apache.iotdb.db.queryengine.plan.execution.config.executor.IConfigTaskExecutor; -import org.apache.iotdb.db.queryengine.plan.relational.sql.ast.CreateFunction; +import org.apache.iotdb.db.queryengine.plan.relational.sql.ast.CreateExternalService; import org.apache.iotdb.db.queryengine.plan.statement.metadata.externalservice.CreateExternalServiceStatement; import com.google.common.util.concurrent.ListenableFuture; @@ -39,9 +39,9 @@ public CreateExternalServiceTask(CreateExternalServiceStatement statement) { this.className = statement.getClassName(); } - public CreateExternalServiceTask(CreateFunction createFunctionStatement) { - this.serviceName = createFunctionStatement.getUdfName(); - this.className = createFunctionStatement.getClassName(); + public CreateExternalServiceTask(CreateExternalService createExternalService) { + this.serviceName = createExternalService.getServiceName().toUpperCase(Locale.ENGLISH); + this.className = createExternalService.getClassName(); } @Override diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/metadata/externalservice/DropExternalServiceTask.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/metadata/externalservice/DropExternalServiceTask.java index 5cfca89da2bb..7772af7b2a3d 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/metadata/externalservice/DropExternalServiceTask.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/metadata/externalservice/DropExternalServiceTask.java @@ -34,7 +34,6 @@ public class DropExternalServiceTask implements IConfigTask { public DropExternalServiceTask(String serviceName, boolean forcedly) { this.serviceName = serviceName.toUpperCase(Locale.ENGLISH); - ; this.forcedly = forcedly; } diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/optimizations/DataNodeLocationSupplierFactory.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/optimizations/DataNodeLocationSupplierFactory.java index d7d755ddc1da..8e73872f5ba8 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/optimizations/DataNodeLocationSupplierFactory.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/optimizations/DataNodeLocationSupplierFactory.java @@ -104,6 +104,7 @@ public List getDataNodeLocations(final String tableName) { case InformationSchema.NODES: case InformationSchema.CONFIG_NODES: case InformationSchema.DATA_NODES: + case InformationSchema.SERVICES: return Collections.singletonList(DataNodeEndPoints.getLocalDataNodeLocation()); default: throw new UnsupportedOperationException("Unknown table: " + tableName); diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/security/TreeAccessCheckVisitor.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/security/TreeAccessCheckVisitor.java index e310ddb53d03..3f127a2a0ae3 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/security/TreeAccessCheckVisitor.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/security/TreeAccessCheckVisitor.java @@ -946,31 +946,31 @@ private TSStatus checkTriggerManagement(IAuditEntity auditEntity, Supplier ""); + return checkGlobalAuth(context, PrivilegeType.SYSTEM, () -> ""); } @Override public TSStatus visitStartExternalService( StartExternalServiceStatement startExternalServiceStatement, TreeAccessCheckContext context) { - return checkGlobalAuth(context, PrivilegeType.MAINTAIN, () -> ""); + return checkGlobalAuth(context, PrivilegeType.SYSTEM, () -> ""); } @Override public TSStatus visitStopExternalService( StopExternalServiceStatement stopExternalServiceStatement, TreeAccessCheckContext context) { - return checkGlobalAuth(context, PrivilegeType.MAINTAIN, () -> ""); + return checkGlobalAuth(context, PrivilegeType.SYSTEM, () -> ""); } @Override public TSStatus visitDropExternalService( DropExternalServiceStatement dropExternalServiceStatement, TreeAccessCheckContext context) { - return checkGlobalAuth(context, PrivilegeType.MAINTAIN, () -> ""); + return checkGlobalAuth(context, PrivilegeType.SYSTEM, () -> ""); } @Override public TSStatus visitShowExternalService( ShowExternalServiceStatement showExternalServiceStatement, TreeAccessCheckContext context) { - return checkGlobalAuth(context, PrivilegeType.MAINTAIN, () -> ""); + return checkGlobalAuth(context, PrivilegeType.SYSTEM, () -> ""); } // ============================== database related =========================== diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/sql/ast/DropExternalService.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/sql/ast/DropExternalService.java index c4a53faea69a..602554fa16bd 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/sql/ast/DropExternalService.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/sql/ast/DropExternalService.java @@ -33,16 +33,22 @@ public class DropExternalService extends Statement { RamUsageEstimator.shallowSizeOfInstance(DropExternalService.class); private final String serviceName; + private final boolean forcedly; - public DropExternalService(NodeLocation location, String serviceName) { + public DropExternalService(NodeLocation location, String serviceName, boolean forcedly) { super(requireNonNull(location, "location is null")); this.serviceName = requireNonNull(serviceName, "serviceName is null"); + this.forcedly = forcedly; } public String getServiceName() { return serviceName; } + public boolean isForcedly() { + return forcedly; + } + @Override public R accept(AstVisitor visitor, C context) { return visitor.visitDropExternalService(this, context); diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/sql/ast/ShowExternalService.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/sql/ast/ShowExternalService.java index b03848f99782..ca713507e13b 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/sql/ast/ShowExternalService.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/sql/ast/ShowExternalService.java @@ -19,63 +19,22 @@ package org.apache.iotdb.db.queryengine.plan.relational.sql.ast; -import com.google.common.collect.ImmutableList; -import org.apache.tsfile.utils.RamUsageEstimator; +import java.util.Optional; -import java.util.List; +public class ShowExternalService extends ShowStatement { -import static com.google.common.base.MoreObjects.toStringHelper; - -public class ShowExternalService extends Statement { - - private static final long INSTANCE_SIZE = - RamUsageEstimator.shallowSizeOfInstance(ShowExternalService.class); - - // -1 means show services on all DNs - private int dataNodeId; - - public ShowExternalService(NodeLocation location, int dataNodeId) { - super(location); - this.dataNodeId = dataNodeId; - } - - public int getDataNodeId() { - return dataNodeId; - } - - @Override - public List getChildren() { - return ImmutableList.of(); + public ShowExternalService( + NodeLocation location, + String tableName, + Optional where, + Optional orderBy, + Optional offset, + Optional limit) { + super(location, tableName, where, orderBy, offset, limit); } @Override public R accept(AstVisitor visitor, C context) { return visitor.visitShowExternalService(this, context); } - - @Override - public int hashCode() { - return 0; - } - - @Override - public boolean equals(Object obj) { - if (!(obj instanceof ShowExternalService)) { - return false; - } - return dataNodeId == ((ShowExternalService) obj).getDataNodeId(); - } - - @Override - public String toString() { - return toStringHelper(this).toString(); - } - - @Override - public long ramBytesUsed() { - long size = INSTANCE_SIZE; - size += AstMemoryEstimationHelper.getEstimatedSizeOfNodeLocation(getLocationInternal()); - size += Integer.SIZE; - return size; - } } diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/sql/parser/AstBuilder.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/sql/parser/AstBuilder.java index 4f5c4deee687..3b8150bd5206 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/sql/parser/AstBuilder.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/sql/parser/AstBuilder.java @@ -1114,17 +1114,28 @@ public Node visitStopServiceStatement(RelationalSqlParser.StopServiceStatementCo @Override public Node visitDropServiceStatement(RelationalSqlParser.DropServiceStatementContext ctx) { return new DropExternalService( - getLocation(ctx), ((Identifier) visit(ctx.serviceName)).getValue()); + getLocation(ctx), ((Identifier) visit(ctx.serviceName)).getValue(), ctx.FORCEDLY() != null); } @Override public Node visitShowServiceStatement(RelationalSqlParser.ShowServiceStatementContext ctx) { // show services on all DNs - int dataNodeId = -1; + Optional where = Optional.empty(); if (ctx.ON() != null) { - dataNodeId = Integer.parseInt(ctx.targetDataNodeId.getText()); + where = + Optional.of( + new ComparisonExpression( + ComparisonExpression.Operator.EQUAL, + new Identifier("datanode_id"), + new LongLiteral(ctx.targetDataNodeId.getText()))); } - return new ShowExternalService(getLocation(ctx), dataNodeId); + return new ShowExternalService( + getLocation(ctx), + InformationSchema.SERVICES, + where, + Optional.empty(), + Optional.empty(), + Optional.empty()); } @Override diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/sql/rewrite/ShowRewrite.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/sql/rewrite/ShowRewrite.java index 12fd67d5017a..d0e386e5926d 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/sql/rewrite/ShowRewrite.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/sql/rewrite/ShowRewrite.java @@ -34,6 +34,7 @@ import org.apache.iotdb.db.queryengine.plan.relational.sql.ast.QualifiedName; import org.apache.iotdb.db.queryengine.plan.relational.sql.ast.Relation; import org.apache.iotdb.db.queryengine.plan.relational.sql.ast.Select; +import org.apache.iotdb.db.queryengine.plan.relational.sql.ast.ShowExternalService; import org.apache.iotdb.db.queryengine.plan.relational.sql.ast.ShowQueriesStatement; import org.apache.iotdb.db.queryengine.plan.relational.sql.ast.ShowStatement; import org.apache.iotdb.db.queryengine.plan.relational.sql.ast.SingleColumn; @@ -84,6 +85,20 @@ protected Node visitShowStatement(final ShowStatement showStatement, final Void showStatement.getLimit()); } + @Override + protected Node visitShowExternalService(ShowExternalService node, Void context) { + return simpleQuery( + selectList(new AllColumns()), + from(INFORMATION_DATABASE, node.getTableName()), + node.getWhere(), + Optional.empty(), + Optional.empty(), + Optional.empty(), + Optional.empty(), + Optional.empty(), + Optional.empty()); + } + @Override protected Node visitCountStatement(final CountStatement countStatement, final Void context) { return simpleQuery( diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/externalservice/ServiceInfo.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/externalservice/ServiceInfo.java index b164adc1ce58..e739ac011432 100644 --- a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/externalservice/ServiceInfo.java +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/externalservice/ServiceInfo.java @@ -24,8 +24,9 @@ import com.google.common.base.Objects; import org.apache.tsfile.utils.ReadWriteIOUtils; -import java.io.DataOutputStream; import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; import java.nio.ByteBuffer; public class ServiceInfo { @@ -79,7 +80,7 @@ public void setServiceInstance(IExternalService serviceInstance) { this.serviceInstance = serviceInstance; } - public void serialize(DataOutputStream stream) throws IOException { + public void serialize(OutputStream stream) throws IOException { ReadWriteIOUtils.write(serviceName, stream); ReadWriteIOUtils.write(className, stream); ReadWriteIOUtils.write(state.getValue(), stream); @@ -93,6 +94,14 @@ public static ServiceInfo deserialize(ByteBuffer buffer) { State.deserialize(ReadWriteIOUtils.readByte(buffer))); } + public static ServiceInfo deserialize(InputStream stream) throws IOException { + return new ServiceInfo( + ReadWriteIOUtils.readString(stream), + ReadWriteIOUtils.readString(stream), + ServiceType.USER_DEFINED, + State.deserialize(ReadWriteIOUtils.readByte(stream))); + } + @Override public boolean equals(Object o) { if (o == null || getClass() != o.getClass()) { diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/schema/column/ColumnHeaderConstant.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/schema/column/ColumnHeaderConstant.java index 4addf29d69b0..6da2d0e0e6eb 100644 --- a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/schema/column/ColumnHeaderConstant.java +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/schema/column/ColumnHeaderConstant.java @@ -123,6 +123,9 @@ private ColumnHeaderConstant() { public static final String SERVICE_NAME = "ServiceName"; public static final String SERVICE_TYPE = "ServiceType"; + public static final String SERVICE_NAME_TABLE_MODEL = "service_name"; + public static final String SERVICE_TYPE_TABLE_MODEL = "service_type"; + // column names for show pipe plugins statement public static final String PLUGIN_NAME = "PluginName"; public static final String PLUGIN_TYPE = "PluginType"; diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/schema/table/InformationSchema.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/schema/table/InformationSchema.java index 4f458d34e05a..c9a994abf656 100644 --- a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/schema/table/InformationSchema.java +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/schema/table/InformationSchema.java @@ -52,6 +52,7 @@ public class InformationSchema { public static final String CONNECTIONS = "connections"; public static final String CURRENT_QUERIES = "current_queries"; public static final String QUERIES_COSTS_HISTOGRAM = "queries_costs_histogram"; + public static final String SERVICES = "services"; static { final TsTable queriesTable = new TsTable(QUERIES); @@ -394,6 +395,21 @@ public class InformationSchema { new AttributeColumnSchema(ColumnHeaderConstant.DATANODE_ID, TSDataType.INT32)); queriesCostsHistogramTable.removeColumnSchema(TsTable.TIME_COLUMN_NAME); schemaTables.put(QUERIES_COSTS_HISTOGRAM, queriesCostsHistogramTable); + + final TsTable servicesTable = new TsTable(SERVICES); + servicesTable.addColumnSchema( + new TagColumnSchema(ColumnHeaderConstant.SERVICE_NAME_TABLE_MODEL, TSDataType.STRING)); + servicesTable.addColumnSchema( + new AttributeColumnSchema(ColumnHeaderConstant.DATA_NODE_ID_TABLE_MODEL, TSDataType.INT32)); + servicesTable.addColumnSchema( + new AttributeColumnSchema(ColumnHeaderConstant.STATE_TABLE_MODEL, TSDataType.STRING)); + servicesTable.addColumnSchema( + new AttributeColumnSchema(ColumnHeaderConstant.CLASS_NAME_TABLE_MODEL, TSDataType.STRING)); + servicesTable.addColumnSchema( + new AttributeColumnSchema( + ColumnHeaderConstant.SERVICE_TYPE_TABLE_MODEL, TSDataType.STRING)); + servicesTable.removeColumnSchema(TsTable.TIME_COLUMN_NAME); + schemaTables.put(SERVICES, servicesTable); } public static Map getSchemaTables() { From 3ff5dd263b17de03a554d6a1807a465fb6a18633 Mon Sep 17 00:00:00 2001 From: Weihao Li <18110526956@163.com> Date: Mon, 12 Jan 2026 15:49:36 +0800 Subject: [PATCH 6/6] add snapshot test Signed-off-by: Weihao Li <18110526956@163.com> --- .../externalservice/ExternalServiceInfo.java | 3 ++- .../persistence/ExternalServiceInfoTest.java | 2 -- .../commons/externalservice/ServiceInfo.java | 18 ++++-------------- 3 files changed, 6 insertions(+), 17 deletions(-) diff --git a/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/manager/externalservice/ExternalServiceInfo.java b/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/manager/externalservice/ExternalServiceInfo.java index 40420e785d0c..28e321abf044 100644 --- a/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/manager/externalservice/ExternalServiceInfo.java +++ b/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/manager/externalservice/ExternalServiceInfo.java @@ -43,6 +43,7 @@ import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; +import java.nio.ByteBuffer; import java.util.Map; import java.util.Set; import java.util.concurrent.ConcurrentHashMap; @@ -259,7 +260,7 @@ private ServiceInfo deserializeServiceInfoConsiderCRC(InputStream inputStream) return null; } - return ServiceInfo.deserialize(inputStream); + return ServiceInfo.deserialize(ByteBuffer.wrap(bytes)); } @Override diff --git a/iotdb-core/confignode/src/test/java/org/apache/iotdb/confignode/persistence/ExternalServiceInfoTest.java b/iotdb-core/confignode/src/test/java/org/apache/iotdb/confignode/persistence/ExternalServiceInfoTest.java index 9e3a284b1f6e..4dcf6e0047f7 100644 --- a/iotdb-core/confignode/src/test/java/org/apache/iotdb/confignode/persistence/ExternalServiceInfoTest.java +++ b/iotdb-core/confignode/src/test/java/org/apache/iotdb/confignode/persistence/ExternalServiceInfoTest.java @@ -30,7 +30,6 @@ import org.junit.AfterClass; import org.junit.Assert; import org.junit.BeforeClass; -import org.junit.Ignore; import org.junit.Test; import java.io.File; @@ -61,7 +60,6 @@ public static void cleanup() throws IOException { } } - @Ignore @Test public void testSnapshot() throws TException, IOException, IllegalPathException { CreateExternalServicePlan createExternalServicePlan = diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/externalservice/ServiceInfo.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/externalservice/ServiceInfo.java index e739ac011432..f9ef9a73c02a 100644 --- a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/externalservice/ServiceInfo.java +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/externalservice/ServiceInfo.java @@ -25,7 +25,6 @@ import org.apache.tsfile.utils.ReadWriteIOUtils; import java.io.IOException; -import java.io.InputStream; import java.io.OutputStream; import java.nio.ByteBuffer; @@ -87,19 +86,10 @@ public void serialize(OutputStream stream) throws IOException { } public static ServiceInfo deserialize(ByteBuffer buffer) { - return new ServiceInfo( - ReadWriteIOUtils.readString(buffer), - ReadWriteIOUtils.readString(buffer), - ServiceType.USER_DEFINED, - State.deserialize(ReadWriteIOUtils.readByte(buffer))); - } - - public static ServiceInfo deserialize(InputStream stream) throws IOException { - return new ServiceInfo( - ReadWriteIOUtils.readString(stream), - ReadWriteIOUtils.readString(stream), - ServiceType.USER_DEFINED, - State.deserialize(ReadWriteIOUtils.readByte(stream))); + String serviceName = ReadWriteIOUtils.readString(buffer); + String className = ReadWriteIOUtils.readString(buffer); + State state = State.deserialize(ReadWriteIOUtils.readByte(buffer)); + return new ServiceInfo(serviceName, className, ServiceType.USER_DEFINED, state); } @Override