diff --git a/all-in-one/pom.xml b/all-in-one/pom.xml
new file mode 100644
index 000000000..061724ce4
--- /dev/null
+++ b/all-in-one/pom.xml
@@ -0,0 +1,213 @@
+
+
+
+ 4.0.0
+
+ cn.edu.tsinghua
+ iot-benchmark
+ 1.0.0
+ ../pom.xml
+
+ all-in-one
+ Benchmark All-in-One
+
+
+
+ cn.edu.tsinghua
+ core
+ ${project.version}
+
+
+ cn.edu.tsinghua
+ influxdb
+ ${project.version}
+
+
+ cn.edu.tsinghua
+ influxdb-2.0
+ ${project.version}
+
+
+ cn.edu.tsinghua
+ cnosdb
+ ${project.version}
+
+
+ cn.edu.tsinghua
+ kairosdb
+ ${project.version}
+
+
+ cn.edu.tsinghua
+ opentsdb
+ ${project.version}
+
+
+ cn.edu.tsinghua
+ tdengine
+ ${project.version}
+
+
+ cn.edu.tsinghua
+ tdengine-3.0
+ ${project.version}
+
+
+ cn.edu.tsinghua
+ questdb
+ ${project.version}
+
+
+ cn.edu.tsinghua
+ dolphindb-3.0
+ ${project.version}
+
+
+ cn.edu.tsinghua
+ dolphindb-2.0
+ ${project.version}
+
+
+ cn.edu.tsinghua
+ timescaledb
+ ${project.version}
+
+
+ cn.edu.tsinghua
+ timescaledb-cluster
+ ${project.version}
+
+
+ cn.edu.tsinghua
+ victoriametrics
+ ${project.version}
+
+
+ cn.edu.tsinghua
+ iotdb-2.0
+ ${project.version}
+
+
+ cn.edu.tsinghua
+ iotdb-1.3
+ ${project.version}
+
+
+ cn.edu.tsinghua
+ mssqlserver
+ ${project.version}
+
+
+ cn.edu.tsinghua
+ sqlite
+ ${project.version}
+
+
+
+ iot-benchmark-all-in-one
+
+
+ org.apache.maven.plugins
+ maven-assembly-plugin
+ 3.1.0
+
+
+
+ server-assembly
+ package
+
+ single
+
+
+
+ src/assembly/assembly.xml
+
+ false
+
+
+ true
+ true
+
+
+
+
+
+
+
+ org.codehaus.mojo
+ exec-maven-plugin
+ 3.1.0
+
+
+
+ dedup-lib
+ package
+
+ java
+
+
+ cn.edu.tsinghua.iot.benchmark.allinone.DedupTool
+
+ ${project.build.directory}/iot-benchmark-all-in-one/iot-benchmark-all-in-one/lib
+
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-antrun-plugin
+ 3.1.0
+
+
+
+ repackage-zip
+ package
+
+ run
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/all-in-one/src/assembly/assembly.xml b/all-in-one/src/assembly/assembly.xml
new file mode 100644
index 000000000..0f3e6d272
--- /dev/null
+++ b/all-in-one/src/assembly/assembly.xml
@@ -0,0 +1,154 @@
+
+
+
+
+ all-in-one
+
+
+ dir
+
+ true
+
+
+ ${maven.multiModuleProjectDirectory}/core/target/iot-benchmark-core/iot-benchmark-core/lib
+ lib/core
+
+
+ ${maven.multiModuleProjectDirectory}/influxdb/target/iot-benchmark-influxdb/iot-benchmark-influxdb/lib
+ lib/influxdb
+
+
+ ${maven.multiModuleProjectDirectory}/influxdb-2.0/target/iot-benchmark-influxdb-2.0/iot-benchmark-influxdb-2.0/lib
+ lib/influxdb-2.0
+
+
+ ${maven.multiModuleProjectDirectory}/cnosdb/target/iot-benchmark-cnosdb/iot-benchmark-cnosdb/lib
+ lib/cnosdb
+
+
+ ${maven.multiModuleProjectDirectory}/kairosdb/target/iot-benchmark-kairosdb/iot-benchmark-kairosdb/lib
+ lib/kairosdb
+
+
+ ${maven.multiModuleProjectDirectory}/opentsdb/target/iot-benchmark-opentsdb/iot-benchmark-opentsdb/lib
+ lib/opentsdb
+
+
+ ${maven.multiModuleProjectDirectory}/tdengine/target/iot-benchmark-tdengine/iot-benchmark-tdengine/lib
+ lib/tdengine
+
+
+ ${maven.multiModuleProjectDirectory}/tdengine-3.0/target/iot-benchmark-tdengine-3.0/iot-benchmark-tdengine-3.0/lib
+ lib/tdengine-3.0
+
+
+ ${maven.multiModuleProjectDirectory}/questdb/target/iot-benchmark-questdb/iot-benchmark-questdb/lib
+ lib/questdb
+
+
+ ${maven.multiModuleProjectDirectory}/dolphindb-3.0/target/iot-benchmark-dolphindb-3.0/iot-benchmark-dolphindb-3.0/lib
+ lib/dolphindb-3.0
+
+
+ ${maven.multiModuleProjectDirectory}/dolphindb-2.0/target/iot-benchmark-dolphindb-2.0/iot-benchmark-dolphindb-2.0/lib
+ lib/dolphindb-2.0
+
+
+ ${maven.multiModuleProjectDirectory}/timescaledb/target/iot-benchmark-timescaledb/iot-benchmark-timescaledb/lib
+ lib/timescaledb
+
+
+ ${maven.multiModuleProjectDirectory}/timescaledb-cluster/target/iot-benchmark-timescaledb-cluster/iot-benchmark-timescaledb-cluster/lib
+ lib/timescaledb-cluster
+
+
+ ${maven.multiModuleProjectDirectory}/victoriametrics/target/iot-benchmark-victoriametrics/iot-benchmark-victoriametrics/lib
+ lib/victoriametrics
+
+
+ ${maven.multiModuleProjectDirectory}/iotdb-2.0/target/iot-benchmark-iotdb-2.0/iot-benchmark-iotdb-2.0/lib
+ lib/iotdb-2.0
+
+
+ ${maven.multiModuleProjectDirectory}/iotdb-1.3/target/iot-benchmark-iotdb-1.3/iot-benchmark-iotdb-1.3/lib
+ lib/iotdb-1.3
+
+
+ ${maven.multiModuleProjectDirectory}/mssqlserver/target/iot-benchmark-mssqlserver/iot-benchmark-mssqlserver/lib
+ lib/mssqlserver
+
+
+ ${maven.multiModuleProjectDirectory}/sqlite/target/iot-benchmark-sqlite/iot-benchmark-sqlite/lib
+ lib/sqlite
+
+
+ ${maven.multiModuleProjectDirectory}/configuration/bin/
+ bin
+ 0755
+
+
+ ${maven.multiModuleProjectDirectory}/configuration/conf/
+ conf
+
+
+
+
+ ${maven.multiModuleProjectDirectory}/configuration/benchmark.bat
+
+
+ ${maven.multiModuleProjectDirectory}/configuration/benchmark.sh
+ 0755
+
+
+ ${maven.multiModuleProjectDirectory}/configuration/rep-benchmark.sh
+ 0755
+
+
+
+ ${maven.multiModuleProjectDirectory}/configuration/cli-benchmark.sh
+ 0755
+
+
+ ${maven.multiModuleProjectDirectory}/configuration/routine
+
+
+ ${maven.multiModuleProjectDirectory}/LICENSE
+
+
+ ${project.build.outputDirectory}/git.properties
+
+
+
diff --git a/all-in-one/src/main/java/cn/edu/tsinghua/iot/benchmark/allinone/DedupTool.java b/all-in-one/src/main/java/cn/edu/tsinghua/iot/benchmark/allinone/DedupTool.java
new file mode 100644
index 000000000..feca5b221
--- /dev/null
+++ b/all-in-one/src/main/java/cn/edu/tsinghua/iot/benchmark/allinone/DedupTool.java
@@ -0,0 +1,190 @@
+/*
+ * 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 cn.edu.tsinghua.iot.benchmark.allinone;
+
+import java.io.BufferedInputStream;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.nio.file.DirectoryStream;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * All-in-one 发行包体积优化(构建期后处理)。
+ *
+ *
各数据库模块的发行目录会各自带上 core 的全部依赖(fastutil 16MB 等在每个模块目录 重复一份,18 份总计约 490MB)。本工具对 lib/<db>
+ * 做两步收敛(判定均为文件名 + 逐字节比较,同名不同版本的 jar 保留):
+ *
+ *
+ * - 去重:删除与 lib/core 同名且内容完全一致的 jar(core 与模块目录各保留一份)。
+ *
- 提升:lib/core 没有、但出现在 >=2 个模块目录、且各副本逐字节一致的 jar, 提升一份到 lib/core 并删除各模块目录副本。
+ *
+ *
+ * 运行时 classpath 顺序是「专属目录在前、lib/core 在后」(见 bin/startup.sh),收敛后 运行时从 lib/core 加载同一份内容,行为等价。
+ *
+ *
日志生态 jar(slf4j-*、logback-*、log4j-*、reload4j-* 前缀)不参与提升:绑定由 classpath 目录顺序决定「哪个目录在前哪个绑定生效」,提升进
+ * core 会与 core 的唯一绑定 (slf4j-reload4j)同目录并存,JVM 通配符展开顺序未指定 → 绑定随机化。
+ *
+ *
zip 由 maven-antrun-plugin 在两步之后重建(见 all-in-one/pom.xml)。
+ *
+ *
用法(maven exec:java):{@code DedupTool }
+ */
+public final class DedupTool {
+
+ private static final int BUFFER_SIZE = 64 * 1024;
+
+ private DedupTool() {}
+
+ public static void main(String[] args) throws IOException {
+ if (args.length < 1) {
+ System.err.println("用法: DedupTool ");
+ System.exit(1);
+ }
+ Path libDir = Paths.get(args[0]);
+ Path coreDir = libDir.resolve("core");
+
+ Map coreJars = new HashMap<>();
+ try (DirectoryStream jars = Files.newDirectoryStream(coreDir, "*.jar")) {
+ for (Path jar : jars) {
+ coreJars.put(jar.getFileName().toString(), jar);
+ }
+ }
+ System.out.println("[all-in-one dedup] lib/core: " + coreJars.size() + " jars");
+
+ // name -> 各模块目录中的副本(已存在的,随删除实时移除)
+ Map> moduleJars = new HashMap<>();
+ try (DirectoryStream dirs = Files.newDirectoryStream(libDir)) {
+ for (Path dir : dirs) {
+ if (!Files.isDirectory(dir) || dir.equals(coreDir)) {
+ continue;
+ }
+ try (DirectoryStream jars = Files.newDirectoryStream(dir, "*.jar")) {
+ for (Path jar : jars) {
+ moduleJars
+ .computeIfAbsent(jar.getFileName().toString(), k -> new ArrayList<>())
+ .add(jar);
+ }
+ }
+ }
+ }
+
+ // 第一步:去重——删除与 lib/core 同名且逐字节一致的模块目录副本
+ int removed = 0;
+ long removedBytes = 0;
+ for (Map.Entry> entry : moduleJars.entrySet()) {
+ Path coreJar = coreJars.get(entry.getKey());
+ if (coreJar == null) {
+ continue;
+ }
+ Iterator it = entry.getValue().iterator();
+ while (it.hasNext()) {
+ Path jar = it.next();
+ if (!sameContent(jar, coreJar)) {
+ continue;
+ }
+ long size = Files.size(jar);
+ Files.delete(jar);
+ it.remove();
+ removed++;
+ removedBytes += size;
+ }
+ }
+ System.out.printf(
+ "[all-in-one dedup] step1 removed %d jars (%d MB)%n", removed, removedBytes / 1024 / 1024);
+
+ // 第二步:提升——core 没有、>=2 个模块目录同名且各副本逐字节一致、且非日志生态
+ // 的 jar,复制一份到 lib/core 并删除各模块目录副本
+ int promoted = 0;
+ long promotedBytes = 0;
+ for (Map.Entry> entry : moduleJars.entrySet()) {
+ String name = entry.getKey();
+ List copies = entry.getValue();
+ if (coreJars.containsKey(name) || copies.size() < 2 || isLoggingJar(name)) {
+ continue;
+ }
+ boolean allSame = true;
+ for (int i = 1; i < copies.size(); i++) {
+ if (!sameContent(copies.get(0), copies.get(i))) {
+ allSame = false;
+ break;
+ }
+ }
+ if (!allSame) {
+ System.out.println("[all-in-one dedup] step2 skip 同名不同版本: " + name);
+ continue;
+ }
+ long size = Files.size(copies.get(0));
+ Files.copy(copies.get(0), coreDir.resolve(name));
+ for (Path jar : copies) {
+ Files.delete(jar);
+ }
+ promoted++;
+ promotedBytes += size * (copies.size() - 1);
+ }
+ System.out.printf(
+ "[all-in-one dedup] step2 promoted %d jars to lib/core (saved %d MB)%n",
+ promoted, promotedBytes / 1024 / 1024);
+ }
+
+ /** 日志生态 jar 不参与提升:绑定随 classpath 目录顺序生效,提升会破坏绑定确定性。 */
+ private static boolean isLoggingJar(String name) {
+ return name.startsWith("slf4j-")
+ || name.startsWith("logback-")
+ || name.startsWith("log4j-")
+ || name.startsWith("reload4j-");
+ }
+
+ /** 先比大小、再流式逐块比较,避免一次读入 16MB 级 jar。 */
+ private static boolean sameContent(Path a, Path b) throws IOException {
+ if (Files.size(a) != Files.size(b)) {
+ return false;
+ }
+ byte[] bufA = new byte[BUFFER_SIZE];
+ byte[] bufB = new byte[BUFFER_SIZE];
+ try (InputStream inA = new BufferedInputStream(new FileInputStream(a.toFile()));
+ InputStream inB = new BufferedInputStream(new FileInputStream(b.toFile()))) {
+ while (true) {
+ int n = inA.read(bufA);
+ if (n < 0) {
+ return true;
+ }
+ int off = 0;
+ while (off < n) {
+ int m = inB.read(bufB, off, n - off);
+ if (m < 0) {
+ return false;
+ }
+ off += m;
+ }
+ for (int i = 0; i < n; i++) {
+ if (bufA[i] != bufB[i]) {
+ return false;
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/configuration/benchmark.bat b/configuration/benchmark.bat
index 1cbcb650c..6edf5e619 100644
--- a/configuration/benchmark.bat
+++ b/configuration/benchmark.bat
@@ -70,12 +70,50 @@ set JAVA_OPTS=-ea^
@REM ***** CLASSPATH library setting *****
@REM Ensure that any user defined CLASSPATH variables are not used on startup
-set CLASSPATH="%BENCHMARK_HOME%\lib\*"
-@REM set CLASSPATH=%CLASSPATH%;iotdb.IoTDB
+@REM all-in-one 布局(存在 lib\core 目录)时:先加载 DB_SWITCH 对应的专属目录
+@REM lib\,再加载 lib\core;旧布局(lib 直接放 jar)保持原行为。
+set CLASSPATH=
+set DB_LIB_DIR=
+if exist "%BENCHMARK_HOME%\lib\core\" (
+ for /f "tokens=1,* delims==" %%a in ('findstr /b /c:"DB_SWITCH=" "%BENCHMARK_CONF%\config.properties" 2^>nul') do set "DB_SWITCH=%%b"
+ if not defined DB_SWITCH for /f "tokens=1,* delims==" %%a in ('findstr /b /c:"# DB_SWITCH=" "%BENCHMARK_CONF%\config.properties" 2^>nul') do set "DB_SWITCH=%%b"
+ if not defined DB_SWITCH for /f "tokens=1,* delims==" %%a in ('findstr /b /c:"#DB_SWITCH=" "%BENCHMARK_CONF%\config.properties" 2^>nul') do set "DB_SWITCH=%%b"
+ if defined DB_SWITCH set "DB_SWITCH=%DB_SWITCH: =%"
+ if defined DB_SWITCH call :select_db_lib
+ if defined DB_LIB_DIR if exist "%BENCHMARK_HOME%\lib\%DB_LIB_DIR%\" (
+ for %%f in ("%BENCHMARK_HOME%\lib\%DB_LIB_DIR%\*.jar") do call :append "%%~f"
+ )
+ for %%f in ("%BENCHMARK_HOME%\lib\core\*.jar") do call :append "%%~f"
+ @REM conf 也放入 classpath:SLF4J 选中 reload4j(log4j 1.2)binding 的模块
+ @REM (如 iotdb-2.0/1.3)依赖 classpath 上的 log4j.properties,否则 LOGGER 输出被吞。
+ call :append "%BENCHMARK_HOME%\conf"
+) else (
+ set CLASSPATH="%BENCHMARK_HOME%\lib\*"
+)
goto okClasspath
+:select_db_lib
+echo.%DB_SWITCH%|findstr /b /c:"IoTDB-200-" >nul && set "DB_LIB_DIR=iotdb-2.0" && goto :eof
+echo.%DB_SWITCH%|findstr /b /c:"IoTDB-130-" >nul && set "DB_LIB_DIR=iotdb-1.3" && goto :eof
+echo.%DB_SWITCH%|findstr /b /c:"InfluxDB-2" >nul && set "DB_LIB_DIR=influxdb-2.0" && goto :eof
+echo.%DB_SWITCH%|findstr /b /c:"InfluxDB" >nul && set "DB_LIB_DIR=influxdb" && goto :eof
+echo.%DB_SWITCH%|findstr /b /c:"OpenTSDB" >nul && set "DB_LIB_DIR=opentsdb" && goto :eof
+echo.%DB_SWITCH%|findstr /b /c:"CnosDB" >nul && set "DB_LIB_DIR=cnosdb" && goto :eof
+echo.%DB_SWITCH%|findstr /b /c:"KairosDB" >nul && set "DB_LIB_DIR=kairosdb" && goto :eof
+echo.%DB_SWITCH%|findstr /b /c:"TimescaleDB-cluster" >nul && set "DB_LIB_DIR=timescaledb-cluster" && goto :eof
+echo.%DB_SWITCH%|findstr /b /c:"TimescaleDB" >nul && set "DB_LIB_DIR=timescaledb" && goto :eof
+echo.%DB_SWITCH%|findstr /b /c:"TDengine-3" >nul && set "DB_LIB_DIR=tdengine-3.0" && goto :eof
+echo.%DB_SWITCH%|findstr /b /c:"TDengine" >nul && set "DB_LIB_DIR=tdengine" && goto :eof
+echo.%DB_SWITCH%|findstr /b /c:"QuestDB" >nul && set "DB_LIB_DIR=questdb" && goto :eof
+echo.%DB_SWITCH%|findstr /b /c:"MsSqlServer" >nul && set "DB_LIB_DIR=mssqlserver" && goto :eof
+echo.%DB_SWITCH%|findstr /b /c:"VictoriaMetrics" >nul && set "DB_LIB_DIR=victoriametrics" && goto :eof
+echo.%DB_SWITCH%|findstr /b /c:"DolphinDB-3" >nul && set "DB_LIB_DIR=dolphindb-3.0" && goto :eof
+echo.%DB_SWITCH%|findstr /b /c:"DolphinDB-2" >nul && set "DB_LIB_DIR=dolphindb-2.0" && goto :eof
+echo.%DB_SWITCH%|findstr /b /c:"SQLite" >nul && set "DB_LIB_DIR=sqlite" && goto :eof
+goto :eof
+
:append
-set CLASSPATH=%CLASSPATH%;%1
+set CLASSPATH=%CLASSPATH%;%~1
goto :eof
@@ -84,7 +122,7 @@ goto :eof
rem echo CLASSPATH: %CLASSPATH%
-"%JAVA_HOME%\bin\java" %JAVA_OPTS% -cp .;./lib/* %MAIN_CLASS% -cf %BENCHMARK_HOME%/conf
+"%JAVA_HOME%\bin\java" %JAVA_OPTS% -cp "%CLASSPATH%" %MAIN_CLASS% -cf %BENCHMARK_HOME%/conf
goto finally
:err
diff --git a/configuration/bin/startup.sh b/configuration/bin/startup.sh
index aaa3b9caa..483b8fccb 100755
--- a/configuration/bin/startup.sh
+++ b/configuration/bin/startup.sh
@@ -98,10 +98,78 @@ echo Using configuration file: "${benchmark_conf}"
# set MAIN_CLASS
MAIN_CLASS=cn.edu.tsinghua.iot.benchmark.App
# set CLASSPATH
+# all-in-one 布局(存在 lib/core 目录)时:
+# 先加载 DB_SWITCH 对应的专属目录 lib/,再加载 lib/core,
+# 保证各模块优先使用自己版本的同名依赖;FakeDB/SelfCheck 仅用 lib/core。
+# 用 classpath 通配符(lib//*)由 java 自行展开 jar;
+# Windows(Git Bash/MSYS)下路径转 C:/ 形式、分隔符用分号,Linux/macOS 用冒号。
+# 旧布局(lib 直接放 jar)保持原行为。
CLASSPATH=""
-for f in ${BENCHMARK_HOME}/lib/*.jar; do
- CLASSPATH=${CLASSPATH}":"$f
-done
+case "$(uname -s 2>/dev/null)" in
+ MINGW*|MSYS*)
+ CP_SEP=";"
+ ;;
+ *)
+ CP_SEP=":"
+ ;;
+esac
+if [ -d "${BENCHMARK_HOME}/lib/core" ]; then
+ CONF_FILE="${benchmark_conf}"
+ if [ -d "${benchmark_conf}" ]; then
+ CONF_FILE="${benchmark_conf}/config.properties"
+ fi
+ DB_SWITCH=$(grep -E '^DB_SWITCH=' "${CONF_FILE}" 2>/dev/null | head -n1 | cut -d'=' -f2- | tr -d ' ')
+ if [ -z "${DB_SWITCH}" ]; then
+ DB_SWITCH=$(grep -E '^#[[:space:]]*DB_SWITCH=' "${CONF_FILE}" 2>/dev/null | head -n1 | sed 's/^#[[:space:]]*//' | cut -d'=' -f2- | tr -d ' ')
+ fi
+ DB_LIB_DIR=""
+ case "${DB_SWITCH}" in
+ IoTDB-200-*) DB_LIB_DIR="iotdb-2.0" ;;
+ IoTDB-130-*) DB_LIB_DIR="iotdb-1.3" ;;
+ InfluxDB-2*) DB_LIB_DIR="influxdb-2.0" ;;
+ InfluxDB*) DB_LIB_DIR="influxdb" ;;
+ OpenTSDB*) DB_LIB_DIR="opentsdb" ;;
+ CnosDB*) DB_LIB_DIR="cnosdb" ;;
+ KairosDB*) DB_LIB_DIR="kairosdb" ;;
+ TimescaleDB-cluster*) DB_LIB_DIR="timescaledb-cluster" ;;
+ TimescaleDB*) DB_LIB_DIR="timescaledb" ;;
+ TDengine-3*) DB_LIB_DIR="tdengine-3.0" ;;
+ TDengine*) DB_LIB_DIR="tdengine" ;;
+ QuestDB*) DB_LIB_DIR="questdb" ;;
+ MsSqlServer*) DB_LIB_DIR="mssqlserver" ;;
+ VictoriaMetrics*) DB_LIB_DIR="victoriametrics" ;;
+ DolphinDB-3*) DB_LIB_DIR="dolphindb-3.0" ;;
+ DolphinDB-2*) DB_LIB_DIR="dolphindb-2.0" ;;
+ SQLite*) DB_LIB_DIR="sqlite" ;;
+ *) DB_LIB_DIR="" ;;
+ esac
+ CP_HOME="${BENCHMARK_HOME}"
+ if [ "${CP_SEP}" = ";" ]; then
+ CP_HOME="$(cd "${BENCHMARK_HOME}" && pwd -W)"
+ fi
+ CP_DB_LIBS=""
+ if [ -n "${DB_LIB_DIR}" ] && [ -d "${BENCHMARK_HOME}/lib/${DB_LIB_DIR}" ]; then
+ CP_DB_LIBS="${CP_HOME}/lib/${DB_LIB_DIR}/*"
+ fi
+ # conf 目录也放入 classpath:SLF4J 全项目统一 2.x 生态后,默认绑定为
+ # lib/core 的 slf4j-reload4j(配置走 conf/log4j.properties);iotdb 模块
+ # 自带 logback 1.3.16、dolphindb 模块自带 slf4j-log4j12,经「模块目录
+ # 优先」规则确定性生效。core 目录刻意只携带一个绑定——多个绑定并存时
+ # SLF4J 2.x 按 classpath 顺序选取,而 JVM 对通配符展开顺序不作保证。
+ if [ -n "${CP_DB_LIBS}" ]; then
+ CLASSPATH="${CP_DB_LIBS}${CP_SEP}${CP_HOME}/lib/core/*${CP_SEP}${CP_HOME}/conf"
+ else
+ CLASSPATH="${CP_HOME}/lib/core/*${CP_SEP}${CP_HOME}/conf"
+ fi
+else
+ CP_HOME="${BENCHMARK_HOME}"
+ if [ "${CP_SEP}" = ";" ]; then
+ CP_HOME="$(cd "${BENCHMARK_HOME}" && pwd -W)"
+ fi
+ for f in ${CP_HOME}/lib/*.jar; do
+ CLASSPATH=${CLASSPATH}${CP_SEP}$f
+ done
+fi
# set benchmark_parms
benchmark_parms="$benchmark_parms -Duser.timezone=GMT+8"
diff --git a/configuration/conf/log4j.properties b/configuration/conf/log4j.properties
new file mode 100644
index 000000000..c334e9dd4
--- /dev/null
+++ b/configuration/conf/log4j.properties
@@ -0,0 +1,14 @@
+# log4j 1.2 configuration (compatible with reload4j).
+# Used when SLF4J selects the reload4j binding on the classpath, e.g. the
+# iotdb modules whose lib ships logback-classic 1.3.x (invisible to
+# slf4j-api 1.7) so SLF4J falls back to slf4j-reload4j. Without this file
+# log4j warns "No appenders could be found" and every LOGGER output
+# (including the latency matrix) is silently dropped.
+# The conf directory is on the classpath (see bin/startup.sh), so log4j 1.2
+# picks this file up automatically.
+log4j.rootLogger=INFO, CONSOLE
+
+log4j.appender.CONSOLE=org.apache.log4j.ConsoleAppender
+log4j.appender.CONSOLE.target=System.out
+log4j.appender.CONSOLE.layout=org.apache.log4j.PatternLayout
+log4j.appender.CONSOLE.layout.ConversionPattern=%d{yyyy-MM-dd HH:mm:ss,SSS} %-5p %c{1}:%L - %m%n
diff --git a/core/pom.xml b/core/pom.xml
index eeaed16d1..1851f245e 100644
--- a/core/pom.xml
+++ b/core/pom.xml
@@ -92,6 +92,66 @@
opencsv
5.5.2
+
+
+ org.slf4j
+ jcl-over-slf4j
+ ${org.slf4j.version}
+
+
+ org.slf4j
+ jul-to-slf4j
+ ${org.slf4j.version}
+
+
+ org.slf4j
+ slf4j-api
+ ${org.slf4j.version}
+
+
+ org.slf4j
+ slf4j-reload4j
+ ${org.slf4j.version}
+
+
+ iot-benchmark-core
+
+
+ org.apache.maven.plugins
+ maven-assembly-plugin
+ 3.1.0
+
+
+
+ server-assembly
+ package
+
+ single
+
+
+
+ src/assembly/assembly.xml
+
+ false
+
+
+ true
+ true
+
+
+
+
+
+
+
+
+
diff --git a/core/src/assembly/assembly.xml b/core/src/assembly/assembly.xml
new file mode 100644
index 000000000..d6d623635
--- /dev/null
+++ b/core/src/assembly/assembly.xml
@@ -0,0 +1,71 @@
+
+
+
+ core
+
+ dir
+ zip
+
+ true
+
+
+ lib
+
+
+
+
+ ${maven.multiModuleProjectDirectory}/configuration/bin/
+ bin
+ 0755
+
+
+ ${maven.multiModuleProjectDirectory}/configuration/conf/
+ conf
+
+
+
+
+ ${maven.multiModuleProjectDirectory}/configuration/benchmark.bat
+
+
+ ${maven.multiModuleProjectDirectory}/configuration/benchmark.sh
+ 0755
+
+
+ ${maven.multiModuleProjectDirectory}/configuration/rep-benchmark.sh
+ 0755
+
+
+
+ ${maven.multiModuleProjectDirectory}/configuration/cli-benchmark.sh
+ 0755
+
+
+ ${maven.multiModuleProjectDirectory}/configuration/routine
+
+
+ ${maven.multiModuleProjectDirectory}/LICENSE
+
+
+ ${project.build.outputDirectory}/git.properties
+
+
+
diff --git a/dolphindb-2.0/pom.xml b/dolphindb-2.0/pom.xml
index 62cba54e7..bdb3d6c9f 100644
--- a/dolphindb-2.0/pom.xml
+++ b/dolphindb-2.0/pom.xml
@@ -23,6 +23,14 @@
com.dolphindb
dolphindb-javaapi
2.00.11.1
+
+
+
+ org.slf4j
+ slf4j-simple
+
+
org.slf4j
diff --git a/dolphindb-3.0/pom.xml b/dolphindb-3.0/pom.xml
index 93575ed47..5a09bad39 100644
--- a/dolphindb-3.0/pom.xml
+++ b/dolphindb-3.0/pom.xml
@@ -23,6 +23,14 @@
com.dolphindb
dolphindb-javaapi
3.00.0.2
+
+
+
+ org.slf4j
+ slf4j-simple
+
+
org.slf4j
diff --git a/iotdb-1.3/pom.xml b/iotdb-1.3/pom.xml
index 0c3ed804d..f6e37c618 100644
--- a/iotdb-1.3/pom.xml
+++ b/iotdb-1.3/pom.xml
@@ -69,6 +69,14 @@
iotdb-session
${iotdb.version}
+
+
+ org.slf4j
+ slf4j-api
+ ${org.slf4j.version}
+
ch.qos.logback
logback-classic
diff --git a/iotdb-2.0/pom.xml b/iotdb-2.0/pom.xml
index 49eaba286..fd6163723 100644
--- a/iotdb-2.0/pom.xml
+++ b/iotdb-2.0/pom.xml
@@ -69,6 +69,14 @@
iotdb-session
${iotdb.version}
+
+
+ org.slf4j
+ slf4j-api
+ ${org.slf4j.version}
+
ch.qos.logback
logback-classic
diff --git a/mssqlserver/src/main/java/cn.edu.tsinghua.iotdb.benchmark.mssqlserver/MsSQLServerDB.java b/mssqlserver/src/main/java/cn.edu.tsinghua.iotdb.benchmark.mssqlserver/MsSQLServerDB.java
index 10d10c17d..ca9f65709 100644
--- a/mssqlserver/src/main/java/cn.edu.tsinghua.iotdb.benchmark.mssqlserver/MsSQLServerDB.java
+++ b/mssqlserver/src/main/java/cn.edu.tsinghua.iotdb.benchmark.mssqlserver/MsSQLServerDB.java
@@ -32,7 +32,9 @@
import java.sql.Statement;
import java.sql.Timestamp;
import java.util.ArrayList;
+import java.util.HashSet;
import java.util.List;
+import java.util.Set;
public class MsSQLServerDB implements IDatabase {
private static final Logger LOGGER = LoggerFactory.getLogger(MsSQLServerDB.class);
@@ -68,9 +70,11 @@ public class MsSQLServerDB implements IDatabase {
"SELECT * from %s where pk_fk_Id in (?) and pk_TimeStamp >= ? and pk_TimeStamp <= ? and value > ? order by pk_TimeStamp desc",
};
- private PreparedStatement[] insertStatements = new PreparedStatement[6];
+ // 槽位数 = SensorType 枚举值个数;此前写死 6,枚举扩展后 ordinal 越界(ArrayIndexOutOfBounds)
+ private PreparedStatement[] insertStatements = new PreparedStatement[SensorType.values().length];
// first: sensorType second: query index
- private PreparedStatement[][] queryStatements = new PreparedStatement[6][10];
+ private PreparedStatement[][] queryStatements =
+ new PreparedStatement[SensorType.values().length][10];
private static final String DELETE_TABLE = "drop table if exists %s_%s";
private DBConfig dbConfig;
@@ -94,7 +98,10 @@ public void init() throws TsdbException {
+ ":"
+ dbConfig.getPORT().get(0)
+ ";DataBaseName="
- + dbConfig.getDB_NAME(),
+ + dbConfig.getDB_NAME()
+ // mssql-jdbc 12.2+ 默认 encrypt=true,容器/自签证书环境会因 PKIX 校验失败
+ // 连不上;压测场景显式关闭加密。
+ + ";encrypt=false",
dbConfig.getUSERNAME(),
dbConfig.getPASSWORD());
@@ -135,8 +142,13 @@ public void init() throws TsdbException {
public void cleanup() throws TsdbException {
try {
Statement statement = connection.createStatement();
+ // 类型映射存在多对一(如 TEXT 与 default 分支都映射 text),按唯一表名 drop 一次
+ Set dropped = new HashSet<>();
for (SensorType sensorType : SensorType.values()) {
- statement.execute(String.format(DELETE_TABLE, dbConfig.getDB_NAME(), typeMap(sensorType)));
+ if (dropped.add(typeMap(sensorType))) {
+ statement.execute(
+ String.format(DELETE_TABLE, dbConfig.getDB_NAME(), typeMap(sensorType)));
+ }
}
statement.close();
} catch (SQLException sqlException) {
@@ -170,11 +182,17 @@ public Double registerSchema(List schemaList) throws TsdbException
try {
start = System.nanoTime();
Statement statement = connection.createStatement();
+ // 类型映射存在多对一(TEXT 与 default 分支都映射 text、FLOAT 与 DOUBLE 都映射 float),
+ // 按唯一表名建表,避免 "already an object named" 撞表
+ Set created = new HashSet<>();
for (SensorType sensorType : SensorType.values()) {
if (sensorType == SensorType.DOUBLE) {
continue;
}
String sysType = typeMap(sensorType);
+ if (!created.add(sysType)) {
+ continue;
+ }
String createSQL =
String.format(
CREATE_TABLE,
diff --git a/opentsdb/src/main/java/cn/edu/tsinghua/iot/benchmark/opentsdb/HttpRequest.java b/opentsdb/src/main/java/cn/edu/tsinghua/iot/benchmark/opentsdb/HttpRequest.java
index bf54b5662..fdc3b0b83 100644
--- a/opentsdb/src/main/java/cn/edu/tsinghua/iot/benchmark/opentsdb/HttpRequest.java
+++ b/opentsdb/src/main/java/cn/edu/tsinghua/iot/benchmark/opentsdb/HttpRequest.java
@@ -21,11 +21,13 @@
import java.io.BufferedReader;
import java.io.IOException;
+import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.HttpURLConnection;
import java.net.URL;
import java.net.URLConnection;
+import java.nio.charset.StandardCharsets;
/** From https://www.cnblogs.com/zhuawang/archive/2012/12/08/2809380.html */
public class HttpRequest {
@@ -94,7 +96,7 @@ public static String sendPost(String url, String param) throws IOException {
try {
URL realUrl = new URL(url);
// 打开和URL之间的连接
- URLConnection conn = realUrl.openConnection();
+ HttpURLConnection conn = (HttpURLConnection) realUrl.openConnection();
// 设置通用的请求属性
conn.setRequestProperty("accept", "*/*");
conn.setRequestProperty("connection", "Keep-Alive");
@@ -103,12 +105,13 @@ public static String sendPost(String url, String param) throws IOException {
// 发送POST请求必须设置如下两行
conn.setDoOutput(true);
conn.setDoInput(true);
- // 获取URLConnection对象对应的输出流
- out = new PrintWriter(conn.getOutputStream());
- // 发送请求参数
- if (param != null) out.print(param);
- // flush输出流的缓冲
- out.flush();
+ // OpenTSDB 2.x 的 Netty HTTP 层不支持 chunked 传输编码:批量写入的 JSON 体
+ // (约 90KB)超过 HttpURLConnection 内部缓冲后会隐式走 chunked,被服务端
+ // 400 拒绝(Chunked request not supported)。显式固定长度强制 Content-Length,
+ // 并统一按 UTF-8 编码输出
+ byte[] body = (param == null ? "" : param).getBytes(StandardCharsets.UTF_8);
+ conn.setFixedLengthStreamingMode(body.length);
+ conn.getOutputStream().write(body);
// 定义BufferedReader输入流来读取URL的响应
in = new BufferedReader(new InputStreamReader(conn.getInputStream()));
String line;
@@ -153,7 +156,27 @@ public static String sendDelete(String url, String param) throws IOException {
while ((line = in.readLine()) != null) {
result += line;
}
- } catch (Exception e) {
+ } catch (IOException e) {
+ // 非 2xx 时 getInputStream 抛 IOException;把错误响应体拼进异常消息,
+ // 便于调用方按错误内容(如空库时的 No such name)做容错处理
+ try {
+ HttpURLConnection errorConn = (HttpURLConnection) new URL(url).openConnection();
+ InputStream errorStream = errorConn.getErrorStream();
+ if (errorStream != null) {
+ StringBuilder body = new StringBuilder();
+ BufferedReader errorReader = new BufferedReader(new InputStreamReader(errorStream));
+ String line;
+ while ((line = errorReader.readLine()) != null) {
+ body.append(line);
+ }
+ errorReader.close();
+ throw new IOException(e.getMessage() + " | body: " + body);
+ }
+ } catch (IOException inner) {
+ if (inner.getMessage() != null && inner.getMessage().contains(" | body: ")) {
+ throw inner;
+ }
+ }
throw e;
}
// 使用finally块来关闭输出流、输入流
diff --git a/opentsdb/src/main/java/cn/edu/tsinghua/iot/benchmark/opentsdb/OpenTSDB.java b/opentsdb/src/main/java/cn/edu/tsinghua/iot/benchmark/opentsdb/OpenTSDB.java
index fd67356d5..45597402a 100644
--- a/opentsdb/src/main/java/cn/edu/tsinghua/iot/benchmark/opentsdb/OpenTSDB.java
+++ b/opentsdb/src/main/java/cn/edu/tsinghua/iot/benchmark/opentsdb/OpenTSDB.java
@@ -61,7 +61,9 @@ public OpenTSDB(DBConfig dbConfig) {
}
sensorRandom = new Random(1 + config.getQUERY_SEED());
String openUrl = dbConfig.getHOST().get(0) + ":" + dbConfig.getPORT().get(0);
- writeUrl = openUrl + "/api/put?summary ";
+ // ?summary 模式要求每个数据点带 summary 字段,而 OpenTSDBDataModel 没有该字段,
+ // 在 OpenTSDB 2.4.1 上会 400;普通 put 模式配合 auto_create_metrics 即可写入
+ writeUrl = openUrl + "/api/put";
queryUrl = openUrl + "/api/query";
}
@@ -84,6 +86,12 @@ public void cleanup() throws TsdbException {
LOGGER.info("Delete old data of {} ...", metricName);
LOGGER.debug("Delete request response: {}", response);
} catch (IOException e) {
+ // 空库首跑时 metric 不存在,OpenTSDB 返回 400(No such name),
+ // 此时没有旧数据可删,跳过即可,不应中断压测
+ if (e.getMessage() != null && e.getMessage().contains("No such name")) {
+ LOGGER.warn("Metric {} does not exist yet, skip delete.", metricName);
+ continue;
+ }
LOGGER.error("Delete old OpenTSDB metric {} failed. Error: {}", metricName, e.getMessage());
throw new TsdbException(e);
}
diff --git a/pom.xml b/pom.xml
index 4f19476c3..0619fb4ef 100644
--- a/pom.xml
+++ b/pom.xml
@@ -29,6 +29,7 @@
mssqlserver
sqlite
verification
+ all-in-one
@@ -40,14 +41,18 @@
${targetJavaVersion}
UTF-8
4.13.1
- 1.1.3
+
+ 1.3.16
1.3.1
false
false
false
2.43.0
1.22.0
- 1.7.36
+
+ 2.0.9
diff --git a/timescaledb-cluster/pom.xml b/timescaledb-cluster/pom.xml
index d58f14c61..b0284c966 100644
--- a/timescaledb-cluster/pom.xml
+++ b/timescaledb-cluster/pom.xml
@@ -39,6 +39,11 @@
slf4j-reload4j
${org.slf4j.version}
+
+ org.postgresql
+ postgresql
+ 42.7.11
+
diff --git a/timescaledb/pom.xml b/timescaledb/pom.xml
index 981ce4c4a..75aeeb290 100644
--- a/timescaledb/pom.xml
+++ b/timescaledb/pom.xml
@@ -39,6 +39,11 @@
slf4j-reload4j
${org.slf4j.version}
+
+ org.postgresql
+ postgresql
+ 42.7.11
+
diff --git a/verification/pom.xml b/verification/pom.xml
index 08070c853..2dc1da8d4 100644
--- a/verification/pom.xml
+++ b/verification/pom.xml
@@ -27,9 +27,16 @@
+
ch.qos.logback
logback-classic
- 1.2.13
+ ${logback.version}
+
+
+
+ org.slf4j
+ slf4j-api
+ ${org.slf4j.version}