You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@iotdb.apache.org by ja...@apache.org on 2026/08/28 08:33:32 UTC
(iotdb) branch edge-version created (now 1e5de50df56)
This is an automated email from the ASF dual-hosted git repository.
JackieTien97 pushed a change to branch edge-version
in repository https://gitbox.apache.org/repos/asf/iotdb.git
at 1e5de50df56 Add IoTDB Edge distribution: ConfigNode + DataNode merged in one JVM
This branch includes the following new commits:
new 1e5de50df56 Add IoTDB Edge distribution: ConfigNode + DataNode merged in one JVM
The 1 revisions listed above as "new" are entirely new to this
repository and will be described in separate emails. The revisions
listed as "add" were already present in the repository and have only
been added to this reference.
(iotdb) 01/01: Add IoTDB Edge distribution: ConfigNode + DataNode merged in one JVM
Posted by ja...@apache.org.
This is an automated email from the ASF dual-hosted git repository.
JackieTien97 pushed a commit to branch edge-version
in repository https://gitbox.apache.org/repos/asf/iotdb.git
commit 1e5de50df562385d0a72a5ed4e3eea6352677242
Author: JackieTien97 <ja...@gmail.com>
AuthorDate: Fri Aug 28 15:59:01 2026 +0800
Add IoTDB Edge distribution: ConfigNode + DataNode merged in one JVM
Produce apache-iotdb-edge-${version}-all-bin from the default distribution
build, targeting resource-constrained edge machines that share the host
with other processes (about 512 MB total process RSS by default).
- EdgeNode (iotdb-core/confignode, org.apache.iotdb.edge): bootstraps the
ConfigNode on a background thread, waits for its internal RPC port, then
starts the DataNode in the same JVM, saving a whole JVM's fixed overhead
(~140 MB validated on x86 and Raspberry Pi 4B).
- One pair of lifecycle scripts only: sbin/start-edge.sh / stop-edge.sh
(plus Windows bats); no separate ConfigNode/DataNode scripts in this
package. start-edge.sh sets CONFIGNODE_HOME explicitly so the ConfigNode
resolves its directories against the installation dir.
- conf/edge-env.sh: fixed small JVM budget (Xmx 224M, direct 96M,
metaspace 160m, SerialGC, Xss320k, ActiveProcessorCount=2).
- Edge-tuned iotdb-system.properties defaults: small thread pools, 1M WAL
buffer, 4M tsfile write buffer, 128M compaction target, CUSTOM region
policy with 1 region group per database, series_slot_num=1, metrics off.
- Validated at 1 Hz mixed read/write on x86 (>=20k series) and
Raspberry Pi 4B (>=10k series, 30-min soak per shape, zero failed points).
---
distribution/pom.xml | 16 ++
distribution/src/assembly/all.xml | 4 +
distribution/src/assembly/{all.xml => edge.xml} | 40 ++--
.../resources/conf-edge/iotdb-system.properties | 126 +++++++++++
.../assembly/resources/conf-edge/logback-edge.xml | 252 +++++++++++++++++++++
.../iotdb/confignode/i18n/ConfigNodeMessages.java | 9 +
.../iotdb/confignode/i18n/ConfigNodeMessages.java | 9 +
.../main/java/org/apache/iotdb/edge/EdgeNode.java | 114 ++++++++++
scripts/conf/edge-env.sh | 69 ++++++
scripts/conf/windows/edge-env.bat | 44 ++++
scripts/sbin/start-edge.sh | 92 ++++++++
scripts/sbin/stop-edge.sh | 45 ++++
scripts/sbin/windows/start-edge.bat | 105 +++++++++
scripts/sbin/windows/stop-edge.bat | 23 ++
14 files changed, 935 insertions(+), 13 deletions(-)
diff --git a/distribution/pom.xml b/distribution/pom.xml
index 3e618451f58..f14b9deeda4 100644
--- a/distribution/pom.xml
+++ b/distribution/pom.xml
@@ -93,6 +93,21 @@
<finalName>apache-iotdb-${project.version}</finalName>
</configuration>
</execution>
+ <!-- Edge distribution: ConfigNode + DataNode merged in one JVM,
+ with resource-constrained default configuration. -->
+ <execution>
+ <id>edge-bin</id>
+ <goals>
+ <goal>single</goal>
+ </goals>
+ <phase>package</phase>
+ <configuration>
+ <descriptors>
+ <descriptor>src/assembly/edge.xml</descriptor>
+ </descriptors>
+ <finalName>apache-iotdb-edge-${project.version}</finalName>
+ </configuration>
+ </execution>
</executions>
</plugin>
<!--
@@ -123,6 +138,7 @@
<include>apache-iotdb-${project.version}-confignode-bin.zip</include>
<include>apache-iotdb-${project.version}-library-udf-bin.zip</include>
<include>apache-iotdb-${project.version}-external-service-impl-bin.zip</include>
+ <include>apache-iotdb-edge-${project.version}-all-bin.zip</include>
</includes>
</fileSet>
</fileSets>
diff --git a/distribution/src/assembly/all.xml b/distribution/src/assembly/all.xml
index c8b583de664..10b6f09589a 100644
--- a/distribution/src/assembly/all.xml
+++ b/distribution/src/assembly/all.xml
@@ -64,6 +64,8 @@
<excludes>
<exclude>ainode-env.*</exclude>
<exclude>**/ainode-env.*</exclude>
+ <exclude>edge-env.*</exclude>
+ <exclude>**/edge-env.*</exclude>
<exclude>iotdb-common.sh</exclude>
<exclude>**/iotdb-common.bat</exclude>
</excludes>
@@ -85,6 +87,8 @@
<excludes>
<exclude>*ainode.*</exclude>
<exclude>**/*ainode.*</exclude>
+ <exclude>*edge.*</exclude>
+ <exclude>**/*edge.*</exclude>
</excludes>
<fileMode>0755</fileMode>
</fileSet>
diff --git a/distribution/src/assembly/all.xml b/distribution/src/assembly/edge.xml
similarity index 77%
copy from distribution/src/assembly/all.xml
copy to distribution/src/assembly/edge.xml
index c8b583de664..16055237409 100644
--- a/distribution/src/assembly/all.xml
+++ b/distribution/src/assembly/edge.xml
@@ -25,7 +25,7 @@
<format>dir</format>
<format>zip</format>
</formats>
- <baseDirectory>apache-iotdb-${project.version}-all-bin</baseDirectory>
+ <baseDirectory>apache-iotdb-edge-${project.version}-all-bin</baseDirectory>
<dependencySets>
<dependencySet>
<includes>
@@ -46,27 +46,35 @@
</dependencySet>
</dependencySets>
<fileSets>
+ <!-- jmx credentials and the tool logback come from the datanode resources;
+ logback-datanode.xml is replaced by the merged logback-edge.xml below. -->
<fileSet>
<outputDirectory>conf</outputDirectory>
<directory>${project.basedir}/../iotdb-core/datanode/src/assembly/resources/conf</directory>
+ <excludes>
+ <exclude>logback-datanode.xml</exclude>
+ </excludes>
</fileSet>
+ <!-- iotdb-system.properties is replaced by the edge-tuned one below. -->
<fileSet>
<outputDirectory>conf</outputDirectory>
- <directory>${project.basedir}/../iotdb-core/confignode/src/assembly/resources/conf</directory>
+ <directory>${project.basedir}/../iotdb-core/node-commons/src/assembly/resources/conf</directory>
+ <excludes>
+ <exclude>iotdb-system.properties</exclude>
+ </excludes>
</fileSet>
+ <!-- edge-specific configuration: tuned iotdb-system.properties + merged logback. -->
<fileSet>
<outputDirectory>conf</outputDirectory>
- <directory>${project.basedir}/../iotdb-core/node-commons/src/assembly/resources/conf</directory>
+ <directory>${project.basedir}/src/assembly/resources/conf-edge</directory>
</fileSet>
<fileSet>
<outputDirectory>conf</outputDirectory>
<directory>${project.basedir}/../scripts/conf</directory>
- <excludes>
- <exclude>ainode-env.*</exclude>
- <exclude>**/ainode-env.*</exclude>
- <exclude>iotdb-common.sh</exclude>
- <exclude>**/iotdb-common.bat</exclude>
- </excludes>
+ <includes>
+ <include>edge-env.sh</include>
+ <include>windows/edge-env.bat</include>
+ </includes>
<fileMode>0755</fileMode>
</fileSet>
<fileSet>
@@ -79,13 +87,19 @@
<filtered>true</filtered>
<fileMode>0755</fileMode>
</fileSet>
+ <!-- one pair of start/stop scripts for the merged process, plus the cli. -->
<fileSet>
<outputDirectory>sbin</outputDirectory>
<directory>${project.basedir}/../scripts/sbin</directory>
- <excludes>
- <exclude>*ainode.*</exclude>
- <exclude>**/*ainode.*</exclude>
- </excludes>
+ <includes>
+ <include>start-edge.sh</include>
+ <include>stop-edge.sh</include>
+ <include>start-cli.sh</include>
+ <include>windows/start-edge.bat</include>
+ <include>windows/stop-edge.bat</include>
+ <include>windows/start-cli.bat</include>
+ <include>windows/start-cli-table.bat</include>
+ </includes>
<fileMode>0755</fileMode>
</fileSet>
<fileSet>
diff --git a/distribution/src/assembly/resources/conf-edge/iotdb-system.properties b/distribution/src/assembly/resources/conf-edge/iotdb-system.properties
new file mode 100644
index 00000000000..e76a17a231c
--- /dev/null
+++ b/distribution/src/assembly/resources/conf-edge/iotdb-system.properties
@@ -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.
+#
+
+####################
+### Cluster Configuration
+####################
+
+cluster_name=defaultCluster
+
+####################
+### Seed ConfigNode
+####################
+
+cn_seed_config_node=127.0.0.1:10710
+
+dn_seed_config_node=127.0.0.1:10710
+
+####################
+### Node RPC Configuration
+####################
+
+cn_internal_address=127.0.0.1
+cn_internal_port=10710
+cn_consensus_port=10720
+
+dn_rpc_address=127.0.0.1
+dn_rpc_port=6667
+dn_internal_address=127.0.0.1
+dn_internal_port=10730
+dn_mpp_data_exchange_port=10740
+dn_schema_region_consensus_port=10750
+dn_data_region_consensus_port=10760
+
+####################
+### Replication configuration
+####################
+
+schema_replication_factor=1
+data_replication_factor=1
+
+####################
+### Directory Configuration
+####################
+
+# dn_data_dirs=data/datanode/data
+# dn_wal_dirs=data/datanode/wal
+
+####################
+### Metric Configuration
+####################
+
+# cn_metric_reporter_list=
+cn_metric_prometheus_reporter_port=9091
+
+# dn_metric_reporter_list=
+dn_metric_prometheus_reporter_port=9092
+
+####################
+### IoTDB Edge Tuning
+####################
+# The following defaults are tuned for the edge distribution: ConfigNode and
+# DataNode run in ONE JVM with a small fixed memory budget (see conf/edge-env.sh),
+# sharing the machine with other processes. Validated on x86 and Raspberry Pi 4B.
+
+# ---- thread pools (small fixed sizes instead of CPU-core-based defaults) ----
+query_thread_count=2
+degree_of_query_parallelism=1
+mpp_data_exchange_core_pool_size=2
+mpp_data_exchange_max_pool_size=2
+flush_thread_count=2
+compaction_thread_count=2
+sub_compaction_thread_count=1
+compaction_schedule_thread_num=1
+pipe_subtask_executor_max_thread_num=2
+pipe_sink_selector_number=1
+pipe_sink_max_client_number=8
+model_inference_execution_thread_count=1
+continuous_query_submit_thread_count=1
+into_operation_execution_thread_count=1
+procedure_core_worker_thread_count=2
+partition_table_recover_worker_num=2
+max_sub_task_num_for_information_table_scan=1
+load_active_listening_max_thread_num=1
+dn_selector_thread_nums_of_client_manager=1
+cn_selector_thread_nums_of_client_manager=1
+max_allowed_concurrent_queries=100
+
+# ---- memory / file buffers ----
+wal_buffer_size_in_byte=1048576
+group_size_in_byte=4194304
+target_compaction_file_size=134217728
+into_operation_buffer_size_in_byte=8388608
+batch_size=10000
+schema_region_ratis_log_appender_buffer_size_max=4194304
+config_node_ratis_log_appender_buffer_size_max=4194304
+
+# ---- background io politeness (share disks with other processes) ----
+compaction_write_throughput_mb_per_sec=8
+partition_table_recover_max_read_mb_per_sec=5
+
+# ---- single-node region / partition layout ----
+schema_region_group_extension_policy=CUSTOM
+default_schema_region_group_num_per_database=1
+data_region_group_extension_policy=CUSTOM
+default_data_region_group_num_per_database=1
+series_slot_num=1
+
+# ---- metrics off (the internal MetricService is shared by both nodes) ----
+cn_metric_level=OFF
+dn_metric_level=OFF
diff --git a/distribution/src/assembly/resources/conf-edge/logback-edge.xml b/distribution/src/assembly/resources/conf-edge/logback-edge.xml
new file mode 100644
index 00000000000..58d31b8e84d
--- /dev/null
+++ b/distribution/src/assembly/resources/conf-edge/logback-edge.xml
@@ -0,0 +1,252 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+ 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.
+
+-->
+<configuration scan="true" scanPeriod="60 seconds">
+ <jmxConfigurator/>
+ <!-- prevent logback from outputting its own status at the start of every log -->
+ <statusListener class="ch.qos.logback.core.status.NopStatusListener"/>
+ <appender class="ch.qos.logback.core.rolling.RollingFileAppender" name="FILEERROR">
+ <file>${IOTDB_HOME}/logs/log_edge_error.log</file>
+ <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
+ <fileNamePattern>${IOTDB_HOME}/logs/log-edge-error-%d{yyyyMMdd}.log.gz</fileNamePattern>
+ <maxHistory>30</maxHistory>
+ </rollingPolicy>
+ <append>true</append>
+ <encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
+ <pattern>%d [%t] %-5p %C{25}:%L - %m %n</pattern>
+ <charset>utf-8</charset>
+ </encoder>
+ <filter class="ch.qos.logback.classic.filter.LevelFilter">
+ <level>error</level>
+ <onMatch>ACCEPT</onMatch>
+ <onMismatch>DENY</onMismatch>
+ </filter>
+ </appender>
+ <appender class="ch.qos.logback.core.rolling.RollingFileAppender" name="FILEWARN">
+ <file>${IOTDB_HOME}/logs/log_edge_warn.log</file>
+ <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
+ <fileNamePattern>${IOTDB_HOME}/logs/log-edge-warn-%d{yyyyMMdd}.log.gz</fileNamePattern>
+ <maxHistory>30</maxHistory>
+ </rollingPolicy>
+ <append>true</append>
+ <encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
+ <pattern>%d [%t] %-5p %C{25}:%L - %m %n</pattern>
+ <charset>utf-8</charset>
+ </encoder>
+ <filter class="ch.qos.logback.classic.filter.LevelFilter">
+ <level>WARN</level>
+ <onMatch>ACCEPT</onMatch>
+ <onMismatch>DENY</onMismatch>
+ </filter>
+ </appender>
+ <appender class="ch.qos.logback.core.rolling.RollingFileAppender" name="FILEDEBUG">
+ <file>${IOTDB_HOME}/logs/log_edge_debug.log</file>
+ <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
+ <fileNamePattern>${IOTDB_HOME}/logs/log-edge-debug-%d{yyyyMMdd}.log.gz</fileNamePattern>
+ <maxHistory>30</maxHistory>
+ </rollingPolicy>
+ <append>true</append>
+ <encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
+ <pattern>%d [%t] %-5p %C{25}:%L - %m %n</pattern>
+ <charset>utf-8</charset>
+ </encoder>
+ <filter class="ch.qos.logback.classic.filter.LevelFilter">
+ <level>DEBUG</level>
+ <onMatch>ACCEPT</onMatch>
+ <onMismatch>DENY</onMismatch>
+ </filter>
+ </appender>
+ <appender class="ch.qos.logback.core.rolling.RollingFileAppender" name="FILETRACE">
+ <file>${IOTDB_HOME}/logs/log_edge_trace.log</file>
+ <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
+ <fileNamePattern>${IOTDB_HOME}/logs/log-edge-trace-%d{yyyyMMdd}.log.gz</fileNamePattern>
+ <maxHistory>30</maxHistory>
+ </rollingPolicy>
+ <append>true</append>
+ <encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
+ <pattern>%d [%t] %-5p %C{25}:%L - %m %n</pattern>
+ <charset>utf-8</charset>
+ </encoder>
+ <filter class="ch.qos.logback.classic.filter.LevelFilter">
+ <level>TRACE</level>
+ <onMatch>ACCEPT</onMatch>
+ <onMismatch>DENY</onMismatch>
+ </filter>
+ </appender>
+ <appender class="ch.qos.logback.core.ConsoleAppender" name="stdout">
+ <Target>System.out</Target>
+ <encoder>
+ <pattern>%d [%t] %-5p %C{25}:%L - %m %n</pattern>
+ <charset>utf-8</charset>
+ </encoder>
+ <filter class="ch.qos.logback.classic.filter.ThresholdFilter">
+ <level>${CONSOLE_LOG_LEVEL:-DEBUG}</level>
+ </filter>
+ </appender>
+ <!-- a log appender that collect all log records whose level is greater than debug-->
+ <appender class="ch.qos.logback.core.rolling.RollingFileAppender" name="FILEALL">
+ <file>${IOTDB_HOME}/logs/log_edge_all.log</file>
+ <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
+ <fileNamePattern>${IOTDB_HOME}/logs/log-edge-all-%d{yyyyMMdd}.log.gz</fileNamePattern>
+ <maxHistory>30</maxHistory>
+ </rollingPolicy>
+ <append>true</append>
+ <encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
+ <pattern>%d [%t] %-5p %C{25}:%L - %m %n</pattern>
+ <charset>utf-8</charset>
+ </encoder>
+ <filter class="ch.qos.logback.classic.filter.ThresholdFilter">
+ <level>INFO</level>
+ </filter>
+ </appender>
+ <appender class="ch.qos.logback.core.rolling.RollingFileAppender" name="FILE_COST_MEASURE">
+ <file>${IOTDB_HOME}/logs/log_edge_measure.log</file>
+ <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
+ <fileNamePattern>${IOTDB_HOME}/logs/log-edge-measure-%d{yyyyMMdd}.log.gz</fileNamePattern>
+ <maxHistory>30</maxHistory>
+ </rollingPolicy>
+ <append>true</append>
+ <encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
+ <pattern>%d [%t] %-5p %C{25}:%L - %m %n</pattern>
+ <charset>utf-8</charset>
+ </encoder>
+ <filter class="ch.qos.logback.classic.filter.ThresholdFilter">
+ <level>INFO</level>
+ </filter>
+ </appender>
+ <appender class="ch.qos.logback.core.rolling.RollingFileAppender" name="QUERY_DEBUG">
+ <file>${IOTDB_HOME}/logs/log_edge_query_debug.log</file>
+ <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
+ <fileNamePattern>${IOTDB_HOME}/logs/log-edge-query-debug-%d{yyyyMMdd}.log.gz</fileNamePattern>
+ <maxHistory>30</maxHistory>
+ </rollingPolicy>
+ <append>true</append>
+ <encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
+ <pattern>%d [%t] %C{25}:%L - %m %n</pattern>
+ <charset>utf-8</charset>
+ </encoder>
+ <filter class="ch.qos.logback.classic.filter.ThresholdFilter">
+ <level>INFO</level>
+ </filter>
+ </appender>
+ <appender class="ch.qos.logback.core.rolling.RollingFileAppender" name="SLOW_SQL">
+ <file>${IOTDB_HOME}/logs/log_edge_slow_sql.log</file>
+ <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
+ <fileNamePattern>${IOTDB_HOME}/logs/log-edge-slow-sql-%d{yyyyMMdd}.log.gz</fileNamePattern>
+ <maxHistory>30</maxHistory>
+ </rollingPolicy>
+ <append>true</append>
+ <encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
+ <pattern>%d [%t] %-5p %C{25}:%L - %m %n</pattern>
+ <charset>utf-8</charset>
+ </encoder>
+ <filter class="ch.qos.logback.classic.filter.ThresholdFilter">
+ <level>INFO</level>
+ </filter>
+ </appender>
+ <appender class="ch.qos.logback.core.rolling.RollingFileAppender" name="SAMPLED_QUERIES">
+ <file>${IOTDB_HOME}/logs/log_edge_sampled_queries.log</file>
+ <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
+ <fileNamePattern>${IOTDB_HOME}/logs/log-edge-sampled-queries-%d{yyyyMMdd}.log.gz</fileNamePattern>
+ <maxHistory>30</maxHistory>
+ </rollingPolicy>
+ <append>true</append>
+ <encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
+ <pattern>%d %m %n</pattern>
+ <charset>utf-8</charset>
+ </encoder>
+ <filter class="ch.qos.logback.classic.filter.ThresholdFilter">
+ <level>INFO</level>
+ </filter>
+ </appender>
+ <appender class="ch.qos.logback.core.rolling.RollingFileAppender" name="COMPACTION">
+ <file>${IOTDB_HOME}/logs/log_edge_compaction.log</file>
+ <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
+ <fileNamePattern>${IOTDB_HOME}/logs/log-edge-compaction-%d{yyyyMMdd}.log.gz</fileNamePattern>
+ <maxHistory>30</maxHistory>
+ </rollingPolicy>
+ <append>true</append>
+ <encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
+ <pattern>%d [%t] %-5p %C{25}:%L - %m %n</pattern>
+ <charset>utf-8</charset>
+ </encoder>
+ <filter class="ch.qos.logback.classic.filter.ThresholdFilter">
+ <level>INFO</level>
+ </filter>
+ </appender>
+ <appender class="ch.qos.logback.core.rolling.RollingFileAppender" name="EXPLAIN_ANALYZE">
+ <file>${IOTDB_HOME}/logs/log_explain_analyze.log</file>
+ <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
+ <fileNamePattern>${IOTDB_HOME}/logs/log-edge-explain-%d{yyyyMMdd}.log.gz</fileNamePattern>
+ <maxHistory>30</maxHistory>
+ </rollingPolicy>
+ <append>true</append>
+ <encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
+ <pattern>%d [%t] %-5p %C{25}:%L - %m %n</pattern>
+ <charset>utf-8</charset>
+ </encoder>
+ <filter class="ch.qos.logback.classic.filter.ThresholdFilter">
+ <level>INFO</level>
+ </filter>
+ </appender>
+ <root level="info">
+ <appender-ref ref="FILETRACE"/>
+ <appender-ref ref="FILEDEBUG"/>
+ <appender-ref ref="FILEWARN"/>
+ <appender-ref ref="FILEERROR"/>
+ <appender-ref ref="FILEALL"/>
+ <appender-ref ref="stdout"/>
+ </root>
+ <logger level="OFF" name="io.moquette.broker.metrics.MQTTMessageLogger"/>
+ <logger level="info" name="org.apache.iotdb.db.service"/>
+ <logger level="info" name="org.apache.iotdb.db.conf"/>
+ <logger level="info" name="org.apache.iotdb.db.cost.statistic">
+ <appender-ref ref="FILE_COST_MEASURE"/>
+ </logger>
+ <logger level="info" name="QUERY_DEBUG">
+ <appender-ref ref="QUERY_DEBUG"/>
+ </logger>
+ <logger level="info" name="SLOW_SQL" additivity="false">
+ <appender-ref ref="SLOW_SQL"/>
+ </logger>
+ <logger level="info" name="SAMPLED_QUERIES" additivity="false">
+ <appender-ref ref="SAMPLED_QUERIES"/>
+ </logger>
+ <logger level="info" name="QUERY_FREQUENCY">
+ <appender-ref ref="QUERY_FREQUENCY"/>
+ </logger>
+ <logger level="info" name="DETAILED_FAILURE_QUERY_TRACE"/>
+ <logger level="info" name="COMPACTION">
+ <appender-ref ref="COMPACTION"/>
+ </logger>
+ <logger level="info" name="org.apache.iotdb.pipe.api">
+ <appender-ref ref="PIPE"/>
+ </logger>
+ <logger level="info" name="org.apache.iotdb.db.pipe">
+ <appender-ref ref="PIPE"/>
+ </logger>
+ <logger level="info" name="org.apache.iotdb.commons.pipe">
+ <appender-ref ref="PIPE"/>
+ </logger>
+ <logger level="info" name="EXPLAIN_ANALYZE" additivity="false">
+ <appender-ref ref="EXPLAIN_ANALYZE"/>
+ </logger>
+</configuration>
diff --git a/iotdb-core/confignode/src/main/i18n/en/org/apache/iotdb/confignode/i18n/ConfigNodeMessages.java b/iotdb-core/confignode/src/main/i18n/en/org/apache/iotdb/confignode/i18n/ConfigNodeMessages.java
index c4832cd886c..800d59bf727 100644
--- a/iotdb-core/confignode/src/main/i18n/en/org/apache/iotdb/confignode/i18n/ConfigNodeMessages.java
+++ b/iotdb-core/confignode/src/main/i18n/en/org/apache/iotdb/confignode/i18n/ConfigNodeMessages.java
@@ -678,4 +678,13 @@ public final class ConfigNodeMessages {
public static final String
EXCEPTION_FAILED_TO_CREATE_OR_ALTER_TOPIC_SUBSCRIBING_ONLY_TO_THE_AUDIT_DATABASE_OR_PATHS_UNDER_IT_IS_NOT_ALLOWED_3E96A6BA =
"Failed to create or alter topic, subscribing only to the __audit database or paths under it is not allowed";
+ public static final String LOG_STARTING_IOTDB_EDGE_CONFIGNODE_AND_DATANODE_IN_ONE_77F32605 =
+ "Starting IoTDB Edge: ConfigNode and DataNode in one process";
+ public static final String LOG_IOTDB_EDGE_CONFIGNODE_IS_READY_STARTING_DATANODE_6729159E =
+ "IoTDB Edge: ConfigNode is ready, starting DataNode";
+ public static final String EXCEPTION_IOTDB_EDGE_CONFIGNODE_BOOTSTRAP_FAILED_02EEE59A =
+ "IoTDB Edge: ConfigNode bootstrap failed";
+ public static final String
+ EXCEPTION_IOTDB_EDGE_CONFIGNODE_INTERNAL_PORT_ARG_IS_NOT_READY_WITHIN_03697FF5 =
+ "IoTDB Edge: ConfigNode internal port %s is not ready within %s ms";
}
diff --git a/iotdb-core/confignode/src/main/i18n/zh/org/apache/iotdb/confignode/i18n/ConfigNodeMessages.java b/iotdb-core/confignode/src/main/i18n/zh/org/apache/iotdb/confignode/i18n/ConfigNodeMessages.java
index 3029154ab3e..14713cbb801 100644
--- a/iotdb-core/confignode/src/main/i18n/zh/org/apache/iotdb/confignode/i18n/ConfigNodeMessages.java
+++ b/iotdb-core/confignode/src/main/i18n/zh/org/apache/iotdb/confignode/i18n/ConfigNodeMessages.java
@@ -721,4 +721,13 @@ public final class ConfigNodeMessages {
public static final String
EXCEPTION_FAILED_TO_CREATE_OR_ALTER_TOPIC_SUBSCRIBING_ONLY_TO_THE_AUDIT_DATABASE_OR_PATHS_UNDER_IT_IS_NOT_ALLOWED_3E96A6BA =
"创建或修改 topic 失败,不允许仅订阅 __audit 数据库或其下的路径";
+ public static final String LOG_STARTING_IOTDB_EDGE_CONFIGNODE_AND_DATANODE_IN_ONE_77F32605 =
+ "正在启动 IoTDB Edge:ConfigNode 与 DataNode 运行于同一进程";
+ public static final String LOG_IOTDB_EDGE_CONFIGNODE_IS_READY_STARTING_DATANODE_6729159E =
+ "IoTDB Edge:ConfigNode 已就绪,开始启动 DataNode";
+ public static final String EXCEPTION_IOTDB_EDGE_CONFIGNODE_BOOTSTRAP_FAILED_02EEE59A =
+ "IoTDB Edge:ConfigNode 启动失败";
+ public static final String
+ EXCEPTION_IOTDB_EDGE_CONFIGNODE_INTERNAL_PORT_ARG_IS_NOT_READY_WITHIN_03697FF5 =
+ "IoTDB Edge:ConfigNode 内部端口 %s 在 %s ms 内未就绪";
}
diff --git a/iotdb-core/confignode/src/main/java/org/apache/iotdb/edge/EdgeNode.java b/iotdb-core/confignode/src/main/java/org/apache/iotdb/edge/EdgeNode.java
new file mode 100644
index 00000000000..061e12602d6
--- /dev/null
+++ b/iotdb-core/confignode/src/main/java/org/apache/iotdb/edge/EdgeNode.java
@@ -0,0 +1,114 @@
+/*
+ * 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.edge;
+
+import org.apache.iotdb.confignode.conf.ConfigNodeDescriptor;
+import org.apache.iotdb.confignode.i18n.ConfigNodeMessages;
+import org.apache.iotdb.confignode.service.ConfigNode;
+import org.apache.iotdb.db.service.DataNode;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.net.InetSocketAddress;
+import java.net.Socket;
+
+/**
+ * Entry point of the IoTDB Edge distribution: starts the ConfigNode and the DataNode services
+ * inside ONE JVM process, so that a resource-constrained edge machine only pays a single JVM's
+ * fixed overhead (metaspace, code cache, GC structures, thread stacks).
+ *
+ * <p>The ConfigNode is bootstrapped on a background thread first; once its internal RPC port
+ * accepts connections (i.e. the seed ConfigNode finished its consensus initialization), the
+ * DataNode is started on the main thread. Both services then keep the JVM alive with their own
+ * non-daemon threads. If either node fails fatally, its own error handling terminates the whole
+ * process, which is the intended single-process semantics of the edge deployment.
+ *
+ * <p>Note for launchers: both {@code CONFIGNODE_HOME} and {@code IOTDB_HOME} system properties must
+ * point to the installation directory (see {@code sbin/start-edge.sh}), otherwise the ConfigNode
+ * resolves its data directories against the process working directory.
+ */
+public final class EdgeNode {
+
+ private static final Logger LOGGER = LoggerFactory.getLogger(EdgeNode.class);
+
+ /** Max duration to wait for the ConfigNode internal RPC port to accept connections. */
+ private static final long CONFIG_NODE_READY_TIMEOUT_MS = 300_000L;
+
+ private static final long PORT_PROBE_INTERVAL_MS = 500L;
+
+ /** Extra delay after the port opens, leaving time for the leader election to settle. */
+ private static final long LEADER_ELECTION_GRACE_MS = 5_000L;
+
+ private EdgeNode() {}
+
+ public static void main(String[] args) throws Exception {
+ LOGGER.info(ConfigNodeMessages.LOG_STARTING_IOTDB_EDGE_CONFIGNODE_AND_DATANODE_IN_ONE_77F32605);
+
+ final Throwable[] configNodeError = new Throwable[1];
+ Thread configNodeThread =
+ new Thread(
+ () -> {
+ try {
+ ConfigNode.main(new String[] {"-s"});
+ } catch (Throwable t) {
+ configNodeError[0] = t;
+ }
+ },
+ "EdgeNode-ConfigNode-Bootstrap");
+ configNodeThread.start();
+
+ int internalPort = ConfigNodeDescriptor.getInstance().getConf().getInternalPort();
+ waitPortOpen(internalPort, configNodeError);
+ if (configNodeError[0] != null) {
+ throw new IllegalStateException(
+ ConfigNodeMessages.EXCEPTION_IOTDB_EDGE_CONFIGNODE_BOOTSTRAP_FAILED_02EEE59A,
+ configNodeError[0]);
+ }
+ Thread.sleep(LEADER_ELECTION_GRACE_MS);
+ LOGGER.info(ConfigNodeMessages.LOG_IOTDB_EDGE_CONFIGNODE_IS_READY_STARTING_DATANODE_6729159E);
+
+ // DataNode.main returns after a successful start; the services of both nodes keep the JVM
+ // alive with non-daemon threads afterwards.
+ DataNode.main(new String[] {"-s"});
+ }
+
+ private static void waitPortOpen(int port, Throwable[] configNodeError)
+ throws InterruptedException {
+ long deadline = System.currentTimeMillis() + CONFIG_NODE_READY_TIMEOUT_MS;
+ while (System.currentTimeMillis() < deadline) {
+ if (configNodeError[0] != null) {
+ return;
+ }
+ try (Socket socket = new Socket()) {
+ socket.connect(new InetSocketAddress("127.0.0.1", port), 1000);
+ return;
+ } catch (Exception e) {
+ Thread.sleep(PORT_PROBE_INTERVAL_MS);
+ }
+ }
+ throw new IllegalStateException(
+ String.format(
+ ConfigNodeMessages
+ .EXCEPTION_IOTDB_EDGE_CONFIGNODE_INTERNAL_PORT_ARG_IS_NOT_READY_WITHIN_03697FF5,
+ port,
+ CONFIG_NODE_READY_TIMEOUT_MS));
+ }
+}
diff --git a/scripts/conf/edge-env.sh b/scripts/conf/edge-env.sh
new file mode 100644
index 00000000000..627623047d4
--- /dev/null
+++ b/scripts/conf/edge-env.sh
@@ -0,0 +1,69 @@
+#!/bin/bash
+#
+# 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.
+#
+
+# IoTDB Edge runs ConfigNode and DataNode inside ONE JVM with a fixed, small
+# memory budget so that it can share a machine with other processes.
+# The defaults below target a total process RSS of about 512 MB and were
+# validated on x86 servers and Raspberry Pi 4B class devices.
+
+# On-heap memory of the merged process. Example values: '224M', '512M'.
+ON_HEAP_MEMORY="${ON_HEAP_MEMORY:-224M}"
+# Initial heap. Kept small so an idle edge instance stays light.
+INIT_HEAP_MEMORY="${INIT_HEAP_MEMORY:-64M}"
+# Off-heap (direct buffer) memory.
+OFF_HEAP_MEMORY="${OFF_HEAP_MEMORY:-96M}"
+
+if [ "${OFF_HEAP_MEMORY%"G"}" != "$OFF_HEAP_MEMORY" ]; then
+ off_heap_memory_size_in_mb=$(expr ${OFF_HEAP_MEMORY%"G"} \* 1024)
+else
+ off_heap_memory_size_in_mb=$(expr ${OFF_HEAP_MEMORY%"M"})
+fi
+# Max cached buffer size, which equals OFF_HEAP_MEMORY / io threads number (200)
+MAX_CACHED_BUFFER_SIZE=$(expr $off_heap_memory_size_in_mb \* 1024 \* 1024 / 200)
+
+IOTDB_JMX_OPTS="$IOTDB_JMX_OPTS -Diotdb.jmx.local=true"
+IOTDB_JMX_OPTS="$IOTDB_JMX_OPTS -Xms${INIT_HEAP_MEMORY}"
+IOTDB_JMX_OPTS="$IOTDB_JMX_OPTS -Xmx${ON_HEAP_MEMORY}"
+IOTDB_JMX_OPTS="$IOTDB_JMX_OPTS -XX:MaxDirectMemorySize=${OFF_HEAP_MEMORY}"
+IOTDB_JMX_OPTS="$IOTDB_JMX_OPTS -Djdk.nio.maxCachedBufferSize=${MAX_CACHED_BUFFER_SIZE}"
+IOTDB_JMX_OPTS="$IOTDB_JMX_OPTS -XX:+CrashOnOutOfMemoryError"
+# Serial GC has the lowest fixed memory overhead; typical edge write rates leave
+# plenty of latency headroom for its pauses.
+IOTDB_JMX_OPTS="$IOTDB_JMX_OPTS -XX:+UseSerialGC"
+IOTDB_JMX_OPTS="$IOTDB_JMX_OPTS -Xss320k"
+IOTDB_JMX_OPTS="$IOTDB_JMX_OPTS -XX:MaxMetaspaceSize=160m"
+IOTDB_JMX_OPTS="$IOTDB_JMX_OPTS -XX:CompressedClassSpaceSize=40m"
+IOTDB_JMX_OPTS="$IOTDB_JMX_OPTS -XX:ReservedCodeCacheSize=64m"
+# Cap the processors the JVM sees, shrinking internal thread pools on big hosts.
+IOTDB_JMX_OPTS="$IOTDB_JMX_OPTS -XX:ActiveProcessorCount=2"
+IOTDB_JMX_OPTS="$IOTDB_JMX_OPTS -XX:+UnlockDiagnosticVMOptions"
+IOTDB_JMX_OPTS="$IOTDB_JMX_OPTS -XX:+UseCRC32Intrinsics"
+IOTDB_JMX_OPTS="$IOTDB_JMX_OPTS -XX:SafepointTimeoutDelay=1000"
+IOTDB_JMX_OPTS="$IOTDB_JMX_OPTS -XX:+SafepointTimeout"
+IOTDB_JMX_OPTS="$IOTDB_JMX_OPTS -Dsun.jnu.encoding=UTF-8 -Dfile.encoding=UTF-8"
+
+# Append tsfile locale option populated by Maven at package time
+# (see conf/iotdb-common.sh; empty in default build, "-Dtsfile.locale=zh" under with-zh-locale).
+if [ -n "$TSFILE_LOCALE_JVM_OPT" ]; then
+ IOTDB_JMX_OPTS="$IOTDB_JMX_OPTS $TSFILE_LOCALE_JVM_OPT"
+fi
+
+echo "IoTDB Edge on heap memory size = ${ON_HEAP_MEMORY}B, off heap memory size = ${OFF_HEAP_MEMORY}B"
+echo "If you want to change this configuration, please check conf/edge-env.sh."
diff --git a/scripts/conf/windows/edge-env.bat b/scripts/conf/windows/edge-env.bat
new file mode 100644
index 00000000000..0554d765e97
--- /dev/null
+++ b/scripts/conf/windows/edge-env.bat
@@ -0,0 +1,44 @@
+@echo off
+@REM
+@REM Licensed to the Apache Software Foundation (ASF) under one
+@REM or more contributor license agreements. See the NOTICE file
+@REM distributed with this work for additional information
+@REM regarding copyright ownership. The ASF licenses this file
+@REM to you under the Apache License, Version 2.0 (the
+@REM "License"); you may not use this file except in compliance
+@REM with the License. You may obtain a copy of the License at
+@REM
+@REM http://www.apache.org/licenses/LICENSE-2.0
+@REM
+@REM Unless required by applicable law or agreed to in writing,
+@REM software distributed under the License is distributed on an
+@REM "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+@REM KIND, either express or implied. See the License for the
+@REM specific language governing permissions and limitations
+@REM under the License.
+@REM
+
+@REM IoTDB Edge runs ConfigNode and DataNode inside ONE JVM with a fixed, small
+@REM memory budget (about 512 MB total process RSS by default).
+
+if "%ON_HEAP_MEMORY%"=="" set ON_HEAP_MEMORY=224M
+if "%INIT_HEAP_MEMORY%"=="" set INIT_HEAP_MEMORY=64M
+if "%OFF_HEAP_MEMORY%"=="" set OFF_HEAP_MEMORY=96M
+
+set IOTDB_JMX_OPTS=%IOTDB_JMX_OPTS% -Diotdb.jmx.local=true
+set IOTDB_JMX_OPTS=%IOTDB_JMX_OPTS% -Xms%INIT_HEAP_MEMORY%
+set IOTDB_JMX_OPTS=%IOTDB_JMX_OPTS% -Xmx%ON_HEAP_MEMORY%
+set IOTDB_JMX_OPTS=%IOTDB_JMX_OPTS% -XX:MaxDirectMemorySize=%OFF_HEAP_MEMORY%
+set IOTDB_JMX_OPTS=%IOTDB_JMX_OPTS% -XX:+CrashOnOutOfMemoryError
+set IOTDB_JMX_OPTS=%IOTDB_JMX_OPTS% -XX:+UseSerialGC
+set IOTDB_JMX_OPTS=%IOTDB_JMX_OPTS% -Xss320k
+set IOTDB_JMX_OPTS=%IOTDB_JMX_OPTS% -XX:MaxMetaspaceSize=160m
+set IOTDB_JMX_OPTS=%IOTDB_JMX_OPTS% -XX:CompressedClassSpaceSize=40m
+set IOTDB_JMX_OPTS=%IOTDB_JMX_OPTS% -XX:ReservedCodeCacheSize=64m
+set IOTDB_JMX_OPTS=%IOTDB_JMX_OPTS% -XX:ActiveProcessorCount=2
+set IOTDB_JMX_OPTS=%IOTDB_JMX_OPTS% -XX:+UnlockDiagnosticVMOptions
+set IOTDB_JMX_OPTS=%IOTDB_JMX_OPTS% -XX:+UseCRC32Intrinsics
+set IOTDB_JMX_OPTS=%IOTDB_JMX_OPTS% -Dsun.jnu.encoding=UTF-8 -Dfile.encoding=UTF-8
+
+echo IoTDB Edge on heap memory size = %ON_HEAP_MEMORY%B, off heap memory size = %OFF_HEAP_MEMORY%B
+echo If you want to change this configuration, please check conf\windows\edge-env.bat.
diff --git a/scripts/sbin/start-edge.sh b/scripts/sbin/start-edge.sh
new file mode 100644
index 00000000000..6b5b9477b6b
--- /dev/null
+++ b/scripts/sbin/start-edge.sh
@@ -0,0 +1,92 @@
+#!/bin/bash
+#
+# 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.
+#
+
+# Start IoTDB Edge: ConfigNode + DataNode in one JVM process.
+
+if [ -z "${IOTDB_HOME}" ]; then
+ export IOTDB_HOME="$(cd "$(dirname "$0")"/.. && pwd)"
+fi
+if [ -z "${IOTDB_CONF}" ]; then
+ export IOTDB_CONF=${IOTDB_HOME}/conf
+fi
+export IOTDB_DATA_HOME=${IOTDB_DATA_HOME:-${IOTDB_HOME}}
+export IOTDB_LOG_DIR=${IOTDB_LOG_DIR:-${IOTDB_HOME}/logs}
+mkdir -p "${IOTDB_LOG_DIR}"
+
+# Refuse to start when any node port is already occupied.
+for p in 10710 10720 6667 10730 10740 10750 10760; do
+ (echo > /dev/tcp/127.0.0.1/$p) 2>/dev/null && { echo "ERROR: port $p is already in use, IoTDB Edge cannot start."; exit 1; }
+done
+
+if [ -f "${IOTDB_CONF}/iotdb-common.sh" ]; then
+ . "${IOTDB_CONF}/iotdb-common.sh"
+fi
+. "${IOTDB_CONF}/edge-env.sh"
+
+# find java in JAVA_HOME
+if [ -n "$JAVA_HOME" ]; then
+ for java in "$JAVA_HOME"/bin/amd64/java "$JAVA_HOME"/bin/java; do
+ if [ -x "$java" ]; then
+ JAVA="$java"
+ break
+ fi
+ done
+else
+ JAVA=java
+fi
+if [ -z "$JAVA" ]; then
+ echo "Unable to find java executable. Check JAVA_HOME and PATH environment variables." > /dev/stderr
+ exit 1
+fi
+
+illegal_access_params=""
+illegal_access_params="$illegal_access_params --add-opens=java.base/java.util.concurrent=ALL-UNNAMED"
+illegal_access_params="$illegal_access_params --add-opens=java.base/java.lang=ALL-UNNAMED"
+illegal_access_params="$illegal_access_params --add-opens=java.base/java.util=ALL-UNNAMED"
+illegal_access_params="$illegal_access_params --add-opens=java.base/java.nio=ALL-UNNAMED"
+illegal_access_params="$illegal_access_params --add-opens=java.base/java.io=ALL-UNNAMED"
+illegal_access_params="$illegal_access_params --add-opens=java.base/java.net=ALL-UNNAMED"
+
+CLASSPATH=""
+for f in "${IOTDB_HOME}"/lib/*.jar; do
+ CLASSPATH=${CLASSPATH}":"$f
+done
+
+iotdb_parms="-Dlogback.configurationFile=${IOTDB_CONF}/logback-edge.xml"
+iotdb_parms="$iotdb_parms -DIOTDB_HOME=${IOTDB_HOME}"
+# CONFIGNODE_HOME must also point to the installation directory, otherwise the
+# ConfigNode part resolves its data directories against the working directory.
+iotdb_parms="$iotdb_parms -DCONFIGNODE_HOME=${IOTDB_HOME}"
+iotdb_parms="$iotdb_parms -DIOTDB_DATA_HOME=${IOTDB_DATA_HOME}"
+iotdb_parms="$iotdb_parms -DTSFILE_HOME=${IOTDB_HOME}"
+iotdb_parms="$iotdb_parms -DIOTDB_CONF=${IOTDB_CONF}"
+iotdb_parms="$iotdb_parms -DCONFIGNODE_CONF=${IOTDB_CONF}"
+iotdb_parms="$iotdb_parms -DTSFILE_CONF=${IOTDB_CONF}"
+iotdb_parms="$iotdb_parms -Dname=iotdb.EdgeNode"
+iotdb_parms="$iotdb_parms -DIOTDB_LOG_DIR=${IOTDB_LOG_DIR}"
+iotdb_parms="$iotdb_parms -DCONFIGNODE_LOG_DIR=${IOTDB_LOG_DIR}"
+iotdb_parms="$iotdb_parms -DOFF_HEAP_MEMORY=${OFF_HEAP_MEMORY}"
+
+classname=org.apache.iotdb.edge.EdgeNode
+
+echo "Starting IoTDB Edge (ConfigNode + DataNode in one process)"
+nohup "$JAVA" $illegal_access_params $iotdb_parms $IOTDB_JMX_OPTS -cp "$CLASSPATH" "$classname" -s > "${IOTDB_LOG_DIR}/log_edge_console.log" 2>&1 &
+echo $! > "${IOTDB_HOME}/edge.pid"
+echo "IoTDB Edge started, pid $(cat "${IOTDB_HOME}/edge.pid"), console log: ${IOTDB_LOG_DIR}/log_edge_console.log"
diff --git a/scripts/sbin/stop-edge.sh b/scripts/sbin/stop-edge.sh
new file mode 100644
index 00000000000..b3944ca9db2
--- /dev/null
+++ b/scripts/sbin/stop-edge.sh
@@ -0,0 +1,45 @@
+#!/bin/bash
+#
+# 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.
+#
+
+# Stop IoTDB Edge (the merged ConfigNode + DataNode process).
+
+IOTDB_HOME="$(cd "$(dirname "$0")"/.. && pwd)"
+
+if [ -f "${IOTDB_HOME}/edge.pid" ]; then
+ PID=$(cat "${IOTDB_HOME}/edge.pid")
+ if kill -0 "$PID" 2>/dev/null; then
+ kill "$PID"
+ for i in $(seq 1 30); do
+ kill -0 "$PID" 2>/dev/null || break
+ sleep 1
+ done
+ if kill -0 "$PID" 2>/dev/null; then
+ kill -9 "$PID"
+ fi
+ echo "IoTDB Edge process $PID stopped."
+ else
+ echo "IoTDB Edge process $PID is not running."
+ fi
+ rm -f "${IOTDB_HOME}/edge.pid"
+else
+ echo "No pid file found, trying to stop by process name."
+ pkill -f "org.apache.iotdb.edge.EdgeNode" 2>/dev/null
+fi
+exit 0
diff --git a/scripts/sbin/windows/start-edge.bat b/scripts/sbin/windows/start-edge.bat
new file mode 100644
index 00000000000..42d452ce24c
--- /dev/null
+++ b/scripts/sbin/windows/start-edge.bat
@@ -0,0 +1,105 @@
+@echo off
+@REM
+@REM Licensed to the Apache Software Foundation (ASF) under one
+@REM or more contributor license agreements. See the NOTICE file
+@REM distributed with this work for additional information
+@REM regarding copyright ownership. The ASF licenses this file
+@REM to you under the Apache License, Version 2.0 (the
+@REM "License"); you may not use this file except in compliance
+@REM with the License. You may obtain a copy of the License at
+@REM
+@REM http://www.apache.org/licenses/LICENSE-2.0
+@REM
+@REM Unless required by applicable law or agreed to in writing,
+@REM software distributed under the License is distributed on an
+@REM "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+@REM KIND, either express or implied. See the License for the
+@REM specific language governing permissions and limitations
+@REM under the License.
+@REM
+
+@REM set cmd format
+powershell -NoProfile -Command "$v=(Get-ItemProperty 'HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion').CurrentMajorVersionNumber; if($v -gt 6) { cmd /c 'chcp 65001' }"
+
+title IoTDB Edge
+
+echo ````````````````````````
+echo Starting IoTDB Edge (ConfigNode + DataNode in one process)
+echo ````````````````````````
+
+@REM -----------------------------------------------------------------------------
+@REM SET JAVA
+set PATH="%JAVA_HOME%\bin\";%PATH%
+set "FULL_VERSION="
+set "MAJOR_VERSION="
+set "MINOR_VERSION="
+
+for /f tokens^=2-5^ delims^=.-_+^" %%j in ('java -fullversion 2^>^&1') do (
+ set "FULL_VERSION=%%j-%%k-%%l-%%m"
+ IF "%%j" == "1" (
+ set "MAJOR_VERSION=%%k"
+ set "MINOR_VERSION=%%l"
+ ) else (
+ set "MAJOR_VERSION=%%j"
+ set "MINOR_VERSION=%%k"
+ )
+)
+
+set JAVA_VERSION=%MAJOR_VERSION%
+
+@REM IoTDB requires JDK 17 or later.
+IF "%JAVA_VERSION%" == "" (
+ echo Failed to determine Java version. IoTDB only supports jdk ^>= 17, please check your java installation.
+ goto finally
+)
+IF %JAVA_VERSION% LSS 17 (
+ echo IoTDB only supports jdk ^>= 17, please check your java version.
+ goto finally
+)
+
+@REM -----------------------------------------------------------------------------
+@REM SET DIRS
+pushd %~dp0..\..
+if NOT DEFINED IOTDB_HOME set IOTDB_HOME=%cd%
+popd
+if NOT DEFINED IOTDB_CONF set IOTDB_CONF=%IOTDB_HOME%\conf
+set IOTDB_LOG_DIR=%IOTDB_HOME%\logs
+if NOT EXIST %IOTDB_LOG_DIR% mkdir %IOTDB_LOG_DIR%
+
+@REM -----------------------------------------------------------------------------
+@REM SET JVM OPTIONS
+if EXIST %IOTDB_CONF%\windows\edge-env.bat (
+ call %IOTDB_CONF%\windows\edge-env.bat
+) else (
+ echo Can't find %IOTDB_CONF%\windows\edge-env.bat
+ goto finally
+)
+
+set illegal_access_params=--add-opens=java.base/java.util.concurrent=ALL-UNNAMED --add-opens=java.base/java.lang=ALL-UNNAMED --add-opens=java.base/java.util=ALL-UNNAMED --add-opens=java.base/java.nio=ALL-UNNAMED --add-opens=java.base/java.io=ALL-UNNAMED --add-opens=java.base/java.net=ALL-UNNAMED
+
+set CLASSPATH=%IOTDB_HOME%\lib\*
+set MAIN_CLASS=org.apache.iotdb.edge.EdgeNode
+
+@REM CONFIGNODE_HOME must also point to the installation directory, otherwise the
+@REM ConfigNode part resolves its data directories against the working directory.
+set iotdb_parms=-Dlogback.configurationFile="%IOTDB_CONF%\logback-edge.xml"
+set iotdb_parms=%iotdb_parms% -DIOTDB_HOME="%IOTDB_HOME%"
+set iotdb_parms=%iotdb_parms% -DCONFIGNODE_HOME="%IOTDB_HOME%"
+set iotdb_parms=%iotdb_parms% -DIOTDB_DATA_HOME="%IOTDB_HOME%"
+set iotdb_parms=%iotdb_parms% -DTSFILE_HOME="%IOTDB_HOME%"
+set iotdb_parms=%iotdb_parms% -DIOTDB_CONF="%IOTDB_CONF%"
+set iotdb_parms=%iotdb_parms% -DCONFIGNODE_CONF="%IOTDB_CONF%"
+set iotdb_parms=%iotdb_parms% -DTSFILE_CONF="%IOTDB_CONF%"
+set iotdb_parms=%iotdb_parms% -Dname=iotdb.EdgeNode
+set iotdb_parms=%iotdb_parms% -DIOTDB_LOG_DIR="%IOTDB_LOG_DIR%"
+set iotdb_parms=%iotdb_parms% -DCONFIGNODE_LOG_DIR="%IOTDB_LOG_DIR%"
+set iotdb_parms=%iotdb_parms% -DOFF_HEAP_MEMORY=%OFF_HEAP_MEMORY%
+
+@REM -----------------------------------------------------------------------------
+@REM START
+java %illegal_access_params% %iotdb_parms% %IOTDB_JMX_OPTS% -cp "%CLASSPATH%" %MAIN_CLASS% -s
+
+goto finally
+
+:finally
+pause
diff --git a/scripts/sbin/windows/stop-edge.bat b/scripts/sbin/windows/stop-edge.bat
new file mode 100644
index 00000000000..c07917fbba4
--- /dev/null
+++ b/scripts/sbin/windows/stop-edge.bat
@@ -0,0 +1,23 @@
+@echo off
+@REM
+@REM Licensed to the Apache Software Foundation (ASF) under one
+@REM or more contributor license agreements. See the NOTICE file
+@REM distributed with this work for additional information
+@REM regarding copyright ownership. The ASF licenses this file
+@REM to you under the Apache License, Version 2.0 (the
+@REM "License"); you may not use this file except in compliance
+@REM with the License. You may obtain a copy of the License at
+@REM
+@REM http://www.apache.org/licenses/LICENSE-2.0
+@REM
+@REM Unless required by applicable law or agreed to in writing,
+@REM software distributed under the License is distributed on an
+@REM "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+@REM KIND, either express or implied. See the License for the
+@REM specific language governing permissions and limitations
+@REM under the License.
+@REM
+
+echo Stopping IoTDB Edge (the merged ConfigNode + DataNode process)
+powershell -NoProfile -Command "Get-CimInstance Win32_Process -Filter \"name='java.exe'\" | Where-Object { $_.CommandLine -like '*org.apache.iotdb.edge.EdgeNode*' } | ForEach-Object { Stop-Process -Id $_.ProcessId -Force; Write-Host ('IoTDB Edge process ' + $_.ProcessId + ' stopped.') }"
+pause