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 extends Node> 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 extends Node> 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