diff --git a/.github/workflows/riscv64-ci.yml b/.github/workflows/riscv64-ci.yml
new file mode 100644
index 0000000000..9f8ca12057
--- /dev/null
+++ b/.github/workflows/riscv64-ci.yml
@@ -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.
+#
+
+name: "RISC-V Server CI"
+
+on:
+ workflow_call:
+
+permissions:
+ contents: read
+
+env:
+ BINFMT_IMAGE: >-
+ tonistiigi/binfmt@sha256:400a4873b838d1b89194d982c45e5fb3cda4593fbfd7e08a02e76b03b21166f0
+ RISCV64_BASE_IMAGE: >-
+ ubuntu@sha256:4edded5722eb644868b7b976033d241d2ab3fff0a170924df69b200a59a2b994
+ DRAGONWELL_RISCV64_ARCHIVE: >-
+ Alibaba_Dragonwell_Extended_11.0.31.28.11_riscv64_linux.tar.gz
+ DRAGONWELL_RISCV64_SHA256: >-
+ 7df2d308f0dca7a779d2854e6da19214f99cd77aa6d4982c3bf981a77266a79b
+ DRAGONWELL_RISCV64_URL: >-
+ https://github.com/dragonwell-project/dragonwell11/releases/download/dragonwell-extended-11.0.31.28_jdk-11.0.31-ga/Alibaba_Dragonwell_Extended_11.0.31.28.11_riscv64_linux.tar.gz
+ RISCV64_CONTAINER: >-
+ hugegraph-riscv64-ci-${{ github.run_id }}-${{ github.run_attempt }}
+
+jobs:
+ build-server-riscv64:
+ runs-on: ubuntu-24.04
+ timeout-minutes: 90
+
+ steps:
+ - name: Checkout
+ uses: actions/checkout@v4
+ with:
+ persist-credentials: false
+
+ - name: Start isolated RISC-V environment
+ run: |
+ docker run --privileged --rm "$BINFMT_IMAGE" --install riscv64
+ docker run --detach --init --platform linux/riscv64 \
+ --name "$RISCV64_CONTAINER" "$RISCV64_BASE_IMAGE" sleep infinity
+ test "$(docker exec "$RISCV64_CONTAINER" uname -m)" = riscv64
+
+ - name: Prepare source and Java 11
+ run: |
+ ARCHIVE_PATH="$RUNNER_TEMP/$DRAGONWELL_RISCV64_ARCHIVE"
+ curl --fail --location --retry 3 --show-error \
+ "$DRAGONWELL_RISCV64_URL" --output "$ARCHIVE_PATH"
+ echo "$DRAGONWELL_RISCV64_SHA256 $ARCHIVE_PATH" | sha256sum -c -
+
+ docker exec "$RISCV64_CONTAINER" mkdir -p /workspace /opt/dragonwell
+ git ls-files -z | tar --null --files-from=- -cf - | \
+ docker exec -i "$RISCV64_CONTAINER" tar -xf - -C /workspace
+ docker cp "$ARCHIVE_PATH" \
+ "$RISCV64_CONTAINER:/tmp/$DRAGONWELL_RISCV64_ARCHIVE"
+
+ - name: Build and smoke test on RISC-V
+ run: |
+ docker exec \
+ --env DRAGONWELL_RISCV64_ARCHIVE="$DRAGONWELL_RISCV64_ARCHIVE" \
+ --env DRAGONWELL_RISCV64_SHA256="$DRAGONWELL_RISCV64_SHA256" \
+ "$RISCV64_CONTAINER" bash -euo pipefail -c '
+ test "$(uname -m)" = riscv64
+ apt-get -q update
+ apt-get -q install -y --no-install-recommends \
+ ca-certificates curl jq libatomic1 libgcc-s1 libstdc++6 \
+ lsof maven procps \
+ protobuf-compiler protobuf-compiler-grpc-java-plugin
+
+ ARCHIVE_PATH="/tmp/$DRAGONWELL_RISCV64_ARCHIVE"
+ echo "$DRAGONWELL_RISCV64_SHA256 $ARCHIVE_PATH" | sha256sum -c -
+ tar -xzf "$ARCHIVE_PATH" --strip-components=1 -C /opt/dragonwell
+ rm "$ARCHIVE_PATH"
+ export JAVA_HOME=/opt/dragonwell
+ export PATH="$JAVA_HOME/bin:$PATH"
+
+ java -XshowSettings:vm -version
+ test -x /usr/bin/protoc
+ test -x /usr/bin/grpc_java_plugin
+
+ cd /workspace
+ MAVEN_OPTS="-XX:TieredStopAtLevel=1 -XX:ActiveProcessorCount=2" \
+ mvn clean package -B -ntp -Drocksdb-only \
+ -P riscv64-protobuf-tools \
+ -pl hugegraph-server/hugegraph-dist -am \
+ -Dmaven.test.skip=true -Dmaven.javadoc.skip=true
+ hugegraph-server/hugegraph-dist/src/assembly/travis/check-rocksdb-only-dist.sh \
+ hugegraph-server/apache-hugegraph-server-*/
+ hugegraph-server/hugegraph-dist/src/assembly/travis/run-native-runtime-smoke-test.sh \
+ hugegraph-server/apache-hugegraph-server-*/
+ '
+
+ - name: Show server log on failure
+ if: failure()
+ run: |
+ if docker container inspect "$RISCV64_CONTAINER" >/dev/null 2>&1; then
+ docker exec "$RISCV64_CONTAINER" bash -c '
+ find /workspace -path "*/logs/hugegraph-server.log" \
+ -exec tail -n 200 {} \;
+ ' || true
+ fi
+
+ - name: Clean up RISC-V environment
+ if: always()
+ run: |
+ if docker container inspect "$RISCV64_CONTAINER" >/dev/null 2>&1; then
+ docker rm --force --volumes "$RISCV64_CONTAINER"
+ fi
+ if docker container inspect "$RISCV64_CONTAINER" >/dev/null 2>&1; then
+ echo "RISC-V CI container still exists: $RISCV64_CONTAINER" >&2
+ exit 1
+ fi
diff --git a/.github/workflows/server-ci.yml b/.github/workflows/server-ci.yml
index 586e36da49..0d6c54b51c 100644
--- a/.github/workflows/server-ci.yml
+++ b/.github/workflows/server-ci.yml
@@ -203,3 +203,8 @@ jobs:
if [ -f "$SERVER_DIR/bin/pid" ]; then
$TRAVIS_DIR/stop-server.sh $SERVER_DIR || true
fi
+
+ build-server-riscv64:
+ uses: ./.github/workflows/riscv64-ci.yml
+ permissions:
+ contents: read
diff --git a/hugegraph-pd/hg-pd-grpc/pom.xml b/hugegraph-pd/hg-pd-grpc/pom.xml
index 7df8622e19..be8ccc44fd 100644
--- a/hugegraph-pd/hg-pd-grpc/pom.xml
+++ b/hugegraph-pd/hg-pd-grpc/pom.xml
@@ -32,7 +32,7 @@
- 1.6.0
+ 1.7.1
1.39.0
3.17.2
0.6.1
@@ -127,4 +127,39 @@
+
+
+
+ riscv64-protobuf-tools
+
+
+ riscv64
+
+
+
+
+ 1.47.0
+ 3.21.7
+
+
+
+ com.google.protobuf
+ protobuf-java
+ ${riscv64.protobuf.java.version}
+
+
+
+
+
+ org.xolstice.maven.plugins
+ protobuf-maven-plugin
+
+ /usr/bin/protoc
+ /usr/bin/grpc_java_plugin
+
+
+
+
+
+
diff --git a/hugegraph-server/README.md b/hugegraph-server/README.md
index c145190bf0..ef6c9f111d 100644
--- a/hugegraph-server/README.md
+++ b/hugegraph-server/README.md
@@ -30,3 +30,46 @@ HUGEGRAPH_VERSION=1.7.0 docker compose -f docker-compose-3pd-3store-3server.yml
```
See [docker/README.md](../docker/README.md) for the full setup guide.
+
+## RISC-V Development and Testing
+
+The end-to-end RISC-V CI validation currently runs on 64-bit `linux/riscv64` LP64D with
+Ubuntu 24.04 and glibc 2.39. It covers HugeGraph Server and the embedded RocksDB backend.
+PD, Store, HStore, other backends, musl/Alpine, and 32-bit RISC-V are out of scope.
+
+The dedicated [RISC-V Server CI](../.github/workflows/riscv64-ci.yml) runs a RocksDB-only
+native build and runtime smoke test in an isolated QEMU environment. It uses the
+checksum-pinned Alibaba Dragonwell 11 Extended Server VM and installs `libatomic1` for the
+packaged RocksDB JNI library. QEMU is for correctness testing and is not a performance
+benchmark.
+
+The packaged `rocksdbjni:8.10.2` RISC-V ELF references glibc symbols through
+`GLIBC_2.30`. This is an inferred minimum for that JNI artifact only, not a validated
+compatibility floor for the complete HugeGraph, Dragonwell, and system-library runtime.
+
+The repository Dockerfile and published HugeGraph image do not include RISC-V support.
+Image publication requires separate multi-architecture design and validation.
+
+For the same validation on a native Debian-derived RISC-V system, use Dragonwell 11
+Extended `11.0.31.28.11` with the archive checksum declared in the CI workflow, then
+install the build and runtime dependencies:
+
+```bash
+sudo apt-get update
+sudo apt-get install -y ca-certificates curl jq libatomic1 libgcc-s1 libstdc++6 \
+ lsof maven procps protobuf-compiler protobuf-compiler-grpc-java-plugin
+```
+
+After selecting Dragonwell as `JAVA_HOME`, build and verify the RocksDB-only distribution:
+
+```bash
+test "$(uname -m)" = riscv64
+"$JAVA_HOME/bin/java" -XshowSettings:vm -version
+mvn clean package -Drocksdb-only -pl hugegraph-server/hugegraph-dist -am \
+ -P riscv64-protobuf-tools \
+ -Dmaven.test.skip=true -Dmaven.javadoc.skip=true
+hugegraph-server/hugegraph-dist/src/assembly/travis/check-rocksdb-only-dist.sh \
+ hugegraph-server/apache-hugegraph-server-*/
+hugegraph-server/hugegraph-dist/src/assembly/travis/run-native-runtime-smoke-test.sh \
+ hugegraph-server/apache-hugegraph-server-*/
+```
diff --git a/hugegraph-server/hugegraph-core/pom.xml b/hugegraph-server/hugegraph-core/pom.xml
index 2334496329..14f5274cb4 100644
--- a/hugegraph-server/hugegraph-core/pom.xml
+++ b/hugegraph-server/hugegraph-core/pom.xml
@@ -335,7 +335,7 @@
kr.motd.maven
os-maven-plugin
- 1.5.0.Final
+ 1.7.1
@@ -408,4 +408,26 @@
+
+
+
+ riscv64-protobuf-tools
+
+
+ riscv64
+
+
+
+
+
+ org.xolstice.maven.plugins
+ protobuf-maven-plugin
+
+ /usr/bin/protoc
+
+
+
+
+
+
diff --git a/hugegraph-server/hugegraph-dist/pom.xml b/hugegraph-server/hugegraph-dist/pom.xml
index 5914b038be..430a213900 100644
--- a/hugegraph-server/hugegraph-dist/pom.xml
+++ b/hugegraph-server/hugegraph-dist/pom.xml
@@ -34,6 +34,8 @@
${assembly.dir}/descriptor
${assembly.dir}/static
2.4.0
+ 0.3.2
+ rocksdb, hbase, hstore
@@ -59,53 +61,11 @@
-
- org.apache.hugegraph
- hugegraph-cassandra
- ${revision}
-
-
- metrics-core
- io.dropwizard.metrics
-
-
-
-
- org.apache.hugegraph
- hugegraph-scylladb
- ${revision}
-
org.apache.hugegraph
hugegraph-rocksdb
${revision}
-
- org.apache.hugegraph
- hugegraph-mysql
- ${revision}
-
-
- org.apache.hugegraph
- hugegraph-palo
- ${revision}
-
-
- org.apache.hugegraph
- hugegraph-hbase
- ${revision}
-
-
- org.apache.hugegraph
- hugegraph-postgresql
- ${revision}
-
-
- org.apache.hugegraph
- hugegraph-hstore
- ${revision}
-
-
org.apache.tinkerpop
gremlin-console
@@ -133,6 +93,13 @@
${jansi.version}
runtime
+
+
+ com.github.jbellis
+ jamm
+ ${jamm.version}
+ runtime
+
@@ -144,6 +111,16 @@
${basedir}/src/main/resources
false
+
+ backend.properties
+
+
+
+ ${basedir}/src/main/resources
+ true
+
+ backend.properties
+
@@ -289,6 +266,68 @@
+
+ rocksdb-only
+
+
+ rocksdb-only
+
+
+
+ rocksdb
+
+
+
+ all-backends
+
+
+ !rocksdb-only
+
+
+
+
+ org.apache.hugegraph
+ hugegraph-cassandra
+ ${revision}
+
+
+ metrics-core
+ io.dropwizard.metrics
+
+
+
+
+ org.apache.hugegraph
+ hugegraph-scylladb
+ ${revision}
+
+
+ org.apache.hugegraph
+ hugegraph-mysql
+ ${revision}
+
+
+ org.apache.hugegraph
+ hugegraph-palo
+ ${revision}
+
+
+ org.apache.hugegraph
+ hugegraph-hbase
+ ${revision}
+
+
+ org.apache.hugegraph
+ hugegraph-postgresql
+ ${revision}
+
+
+ org.apache.hugegraph
+ hugegraph-hstore
+ ${revision}
+
+
+
assembly-hugegraph
diff --git a/hugegraph-server/hugegraph-dist/src/assembly/static/bin/dump-store.sh b/hugegraph-server/hugegraph-dist/src/assembly/static/bin/dump-store.sh
index 425ad9f157..14b6ac1f79 100755
--- a/hugegraph-server/hugegraph-dist/src/assembly/static/bin/dump-store.sh
+++ b/hugegraph-server/hugegraph-dist/src/assembly/static/bin/dump-store.sh
@@ -30,16 +30,20 @@ TOP="$(cd $BIN/../ && pwd)"
CONF=$TOP/conf
LIB=$TOP/lib
+. "${BIN}"/util.sh
+
+configure_riscv64_libatomic || exit 1
+
if [ -n "$JAVA_HOME" ]; then
JAVA="$JAVA_HOME"/bin/java
else
JAVA=java
fi
-conf=$1
if [ $# -eq 0 ]; then
- conf=$CONF/hugegraph.properties
+ set -- "$CONF/graphs/hugegraph.properties"
fi
+conf=$1
cd $TOP
@@ -47,5 +51,5 @@ echo "Dumping HugeGraph Store($conf)..."
dump_store_ext_jar_path=$LIB/hugegraph-dist-*.jar
for i in $LIB/*.jar; do dump_store_ext_jar_path=$dump_store_ext_jar_path:$i; export dump_store_ext_jar_path; done
-exec $JAVA -cp dump_store_ext_jar_path \
-org.apache.hugegraph.cmd.StoreDumper $conf $2 $3 $4
+exec "$JAVA" -cp "$dump_store_ext_jar_path" \
+org.apache.hugegraph.cmd.StoreDumper "$@"
diff --git a/hugegraph-server/hugegraph-dist/src/assembly/static/bin/hugegraph-server.sh b/hugegraph-server/hugegraph-dist/src/assembly/static/bin/hugegraph-server.sh
index c5f84d4933..3915a5f63e 100644
--- a/hugegraph-server/hugegraph-dist/src/assembly/static/bin/hugegraph-server.sh
+++ b/hugegraph-server/hugegraph-dist/src/assembly/static/bin/hugegraph-server.sh
@@ -45,6 +45,8 @@ GITHUB="https://github.com"
export HUGEGRAPH_HOME="$TOP"
. "${BIN}"/util.sh
+configure_riscv64_libatomic || exit 1
+
# Parse the server arguments in array way
SERVER_ARGS=("$@")
GREMLIN_SERVER_CONF="${SERVER_ARGS[0]:-}"
diff --git a/hugegraph-server/hugegraph-dist/src/assembly/static/bin/init-store.sh b/hugegraph-server/hugegraph-dist/src/assembly/static/bin/init-store.sh
index 9aefd1160b..b9da84166b 100755
--- a/hugegraph-server/hugegraph-dist/src/assembly/static/bin/init-store.sh
+++ b/hugegraph-server/hugegraph-dist/src/assembly/static/bin/init-store.sh
@@ -33,6 +33,7 @@ PLUGINS="$TOP/plugins"
. "${BIN}"/util.sh
+configure_riscv64_libatomic || exit 1
ensure_path_writable "${PLUGINS}"
if [[ -n "$JAVA_HOME" ]]; then
diff --git a/hugegraph-server/hugegraph-dist/src/assembly/static/bin/stop-hugegraph.sh b/hugegraph-server/hugegraph-dist/src/assembly/static/bin/stop-hugegraph.sh
index 7407746348..c63035e072 100755
--- a/hugegraph-server/hugegraph-dist/src/assembly/static/bin/stop-hugegraph.sh
+++ b/hugegraph-server/hugegraph-dist/src/assembly/static/bin/stop-hugegraph.sh
@@ -63,6 +63,8 @@ fi
PID=$(cat $PID_FILE)
-if kill_process_and_wait "HugeGraphServer" "$PID" "$SERVER_SHUTDOWN_TIMEOUT_S"; then
- rm "$PID_FILE"
+if ! kill_process_and_wait "HugeGraphServer" "$PID" "$SERVER_SHUTDOWN_TIMEOUT_S"; then
+ exit 1
fi
+
+rm "$PID_FILE"
diff --git a/hugegraph-server/hugegraph-dist/src/assembly/static/bin/util.sh b/hugegraph-server/hugegraph-dist/src/assembly/static/bin/util.sh
index e2655c69d3..91c0c3efbf 100755
--- a/hugegraph-server/hugegraph-dist/src/assembly/static/bin/util.sh
+++ b/hugegraph-server/hugegraph-dist/src/assembly/static/bin/util.sh
@@ -25,6 +25,45 @@ function command_available() {
fi
}
+function configure_riscv64_libatomic() {
+ if [[ "$(uname -s)" != "Linux" || "$(uname -m)" != "riscv64" ]]; then
+ return 0
+ fi
+
+ if [[ "${LD_PRELOAD:-}" == *"libatomic.so.1"* ]]; then
+ return 0
+ fi
+
+ local libatomic=""
+ if command_available "ldconfig"; then
+ libatomic=$(ldconfig -p 2>/dev/null | \
+ awk '/libatomic\.so\.1 .*=>/ && !path {path=$NF}
+ END {if (path) print path}')
+ fi
+
+ if [[ -z "$libatomic" ]]; then
+ local candidate
+ for candidate in /lib/riscv64-linux-gnu/libatomic.so.1 \
+ /usr/lib/riscv64-linux-gnu/libatomic.so.1 \
+ /lib64/lp64d/libatomic.so.1 \
+ /usr/lib64/lp64d/libatomic.so.1 \
+ /lib64/libatomic.so.1 \
+ /usr/lib64/libatomic.so.1; do
+ if [[ -r "$candidate" ]]; then
+ libatomic="$candidate"
+ break
+ fi
+ done
+ fi
+
+ if [[ -z "$libatomic" ]]; then
+ echo "RISC-V RocksDB requires libatomic.so.1; install libatomic1" >&2
+ return 1
+ fi
+
+ export LD_PRELOAD="${LD_PRELOAD:+${LD_PRELOAD}:}${libatomic}"
+}
+
# read a property from .properties file
function read_property() {
# file path
diff --git a/hugegraph-server/hugegraph-dist/src/assembly/travis/check-rocksdb-only-dist.sh b/hugegraph-server/hugegraph-dist/src/assembly/travis/check-rocksdb-only-dist.sh
new file mode 100755
index 0000000000..133739422d
--- /dev/null
+++ b/hugegraph-server/hugegraph-dist/src/assembly/travis/check-rocksdb-only-dist.sh
@@ -0,0 +1,97 @@
+#!/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.
+#
+
+set -euo pipefail
+
+if [[ $# -ne 1 ]]; then
+ echo "USAGE: $0 SERVER_DIR" >&2
+ exit 1
+fi
+
+SERVER_DIR=$1
+LIB_DIR="$SERVER_DIR/lib"
+DIST_JAR=""
+WORK_DIR=$(mktemp -d "${TMPDIR:-/tmp}/hugegraph-dist-check.XXXXXX")
+
+cleanup() {
+ rm -rf "$WORK_DIR"
+}
+trap cleanup EXIT
+
+if [[ ! -d "$LIB_DIR" ]]; then
+ echo "Distribution lib directory does not exist: $LIB_DIR" >&2
+ exit 1
+fi
+
+function require_jar() {
+ local pattern=$1
+ if ! find "$LIB_DIR" -maxdepth 1 -type f -name "$pattern" -print -quit | grep -q .; then
+ echo "Missing required RocksDB distribution artifact: $pattern" >&2
+ exit 1
+ fi
+}
+
+function forbid_jar() {
+ local pattern=$1
+ local match
+ match=$(find "$LIB_DIR" -maxdepth 1 -type f -name "$pattern" -print -quit)
+ if [[ -n "$match" ]]; then
+ echo "Unrelated backend artifact found in RocksDB-only distribution: $match" >&2
+ exit 1
+ fi
+}
+
+for pattern in hugegraph-core-*.jar \
+ hugegraph-api-*.jar \
+ hugegraph-rocksdb-*.jar \
+ hugegraph-dist-*.jar \
+ jamm-*.jar \
+ rocksdbjni-*.jar; do
+ require_jar "$pattern"
+done
+
+DIST_JAR=$(find "$LIB_DIR" -maxdepth 1 -type f \
+ -name 'hugegraph-dist-*.jar' -print -quit)
+DIST_JAR=$(cd "$(dirname "$DIST_JAR")" && pwd)/$(basename "$DIST_JAR")
+if command -v unzip >/dev/null 2>&1; then
+ BACKEND_PROPERTIES=$(unzip -p "$DIST_JAR" backend.properties)
+elif command -v jar >/dev/null 2>&1; then
+ (cd "$WORK_DIR" && jar xf "$DIST_JAR" backend.properties)
+ BACKEND_PROPERTIES=$(cat "$WORK_DIR/backend.properties")
+else
+ echo "The distribution check requires unzip or a JDK jar command" >&2
+ exit 1
+fi
+
+if ! grep -qx 'backends=\[rocksdb\]' <<< "$BACKEND_PROPERTIES"; then
+ echo "RocksDB-only distribution has an invalid backend registry" >&2
+ printf '%s\n' "$BACKEND_PROPERTIES" >&2
+ exit 1
+fi
+
+for pattern in hugegraph-cassandra-*.jar \
+ hugegraph-scylladb-*.jar \
+ hugegraph-mysql-*.jar \
+ hugegraph-palo-*.jar \
+ hugegraph-hbase-*.jar \
+ hugegraph-postgresql-*.jar \
+ hugegraph-hstore-*.jar; do
+ forbid_jar "$pattern"
+done
+
+echo "RocksDB-only distribution contains the required backend and no unrelated backend JARs"
diff --git a/hugegraph-server/hugegraph-dist/src/assembly/travis/rocksdb-jni-smoke.groovy b/hugegraph-server/hugegraph-dist/src/assembly/travis/rocksdb-jni-smoke.groovy
new file mode 100644
index 0000000000..1baa856bf9
--- /dev/null
+++ b/hugegraph-server/hugegraph-dist/src/assembly/travis/rocksdb-jni-smoke.groovy
@@ -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.
+//
+
+import java.nio.charset.StandardCharsets
+import java.util.Arrays
+
+import org.rocksdb.Options
+import org.rocksdb.RocksDB
+
+rocksdbSmokePath = System.getenv('ROCKSDB_SMOKE_DIR')
+if (!rocksdbSmokePath) {
+ throw new IllegalArgumentException('ROCKSDB_SMOKE_DIR is required')
+}
+
+rocksdbSmokeKey = 'hugegraph-riscv64-key'.getBytes(StandardCharsets.UTF_8)
+rocksdbSmokeExpected = 'hugegraph-riscv64-value'.getBytes(StandardCharsets.UTF_8)
+
+RocksDB.loadLibrary()
+
+rocksdbSmokeOptions = new Options().setCreateIfMissing(true)
+rocksdbSmokeDb = null
+try {
+ rocksdbSmokeDb = RocksDB.open(rocksdbSmokeOptions, rocksdbSmokePath)
+ rocksdbSmokeDb.put(rocksdbSmokeKey, rocksdbSmokeExpected)
+ def actual = rocksdbSmokeDb.get(rocksdbSmokeKey)
+ if (!Arrays.equals(rocksdbSmokeExpected, actual)) {
+ throw new IllegalStateException('RocksDB value differs after put/get')
+ }
+ rocksdbSmokeDb.close()
+ rocksdbSmokeDb = null
+
+ rocksdbSmokeDb = RocksDB.open(rocksdbSmokeOptions, rocksdbSmokePath)
+ actual = rocksdbSmokeDb.get(rocksdbSmokeKey)
+ if (!Arrays.equals(rocksdbSmokeExpected, actual)) {
+ throw new IllegalStateException('RocksDB value differs after reopen')
+ }
+ println('rocksdb-jni-smoke-ok')
+} finally {
+ rocksdbSmokeDb?.close()
+ rocksdbSmokeOptions.close()
+}
diff --git a/hugegraph-server/hugegraph-dist/src/assembly/travis/run-native-runtime-smoke-test.sh b/hugegraph-server/hugegraph-dist/src/assembly/travis/run-native-runtime-smoke-test.sh
new file mode 100755
index 0000000000..7cf09e30ee
--- /dev/null
+++ b/hugegraph-server/hugegraph-dist/src/assembly/travis/run-native-runtime-smoke-test.sh
@@ -0,0 +1,91 @@
+#!/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.
+#
+set -euo pipefail
+
+if [[ $# -ne 1 ]]; then
+ echo "USAGE: $0 SERVER_DIR" >&2
+ exit 1
+fi
+
+TRAVIS_DIR=$(cd "$(dirname "$0")" && pwd)
+SERVER_DIR=$(cd "$1" && pwd)
+SERVER_URL=${SERVER_URL:-http://127.0.0.1:8080}
+EXPECTED_ARCH=${EXPECTED_ARCH:-riscv64}
+EXPECTED_JAVA_MAJOR=${EXPECTED_JAVA_MAJOR:-11}
+SERVER_START_ATTEMPTED=false
+SERVER_STARTUP_TIMEOUT=${SERVER_STARTUP_TIMEOUT:-300}
+SERVER_START_COMMAND_TIMEOUT=$((SERVER_STARTUP_TIMEOUT + 30))
+STORE_DUMP_TIMEOUT=${STORE_DUMP_TIMEOUT:-120}
+WORK_DIR=$(mktemp -d "${TMPDIR:-/tmp}/hugegraph-native-runtime-smoke.XXXXXX")
+RUN_ID="$(date +%s)_$$"
+
+export EXPECTED_ARCH EXPECTED_JAVA_MAJOR
+
+cleanup() {
+ local status=$?
+ trap - EXIT
+ if [[ "$SERVER_START_ATTEMPTED" == "true" ]]; then
+ "$SERVER_DIR/bin/stop-hugegraph.sh" -m false >/dev/null 2>&1 || status=1
+ fi
+ rm -rf "$WORK_DIR"
+ exit "$status"
+}
+trap cleanup EXIT
+
+start_server() {
+ if ! command -v timeout >/dev/null 2>&1; then
+ echo "Required command is unavailable: timeout" >&2
+ return 1
+ fi
+ timeout --foreground --kill-after=15s \
+ "${SERVER_START_COMMAND_TIMEOUT}s" \
+ "$SERVER_DIR/bin/start-hugegraph.sh" -t "$SERVER_STARTUP_TIMEOUT"
+}
+
+"$TRAVIS_DIR/run-rocksdb-jni-smoke-test.sh" "$SERVER_DIR"
+
+"$SERVER_DIR/bin/init-store.sh"
+SERVER_START_ATTEMPTED=true
+start_server
+"$TRAVIS_DIR/run-server-e2e-smoke-test.sh" "$SERVER_URL" create "$RUN_ID" | \
+ tee "$WORK_DIR/create.log"
+grep -q '^server-e2e-smoke-create-ok$' "$WORK_DIR/create.log"
+
+"$SERVER_DIR/bin/stop-hugegraph.sh" -m false
+timeout --foreground --kill-after=15s "${STORE_DUMP_TIMEOUT}s" \
+ "$SERVER_DIR/bin/dump-store.sh" | tee "$WORK_DIR/store-dump.log"
+grep -q 'Dump table VERTEX (offset 0 limit 20):' "$WORK_DIR/store-dump.log"
+grep -Fq "riscv-smoke-v1-$RUN_ID" "$WORK_DIR/store-dump.log"
+grep -Fq "riscv-smoke-v2-$RUN_ID" "$WORK_DIR/store-dump.log"
+echo "store-dump-smoke-ok"
+
+start_server
+"$TRAVIS_DIR/run-server-e2e-smoke-test.sh" "$SERVER_URL" verify "$RUN_ID" | \
+ tee "$WORK_DIR/verify.log"
+grep -q '^server-e2e-smoke-verify-ok$' "$WORK_DIR/verify.log"
+
+if [[ -d "$SERVER_DIR/logs" ]] && \
+ grep -Eirq 'undefined symbol|UnsatisfiedLinkError|UnsupportedClassVersionError|NoClassDefFoundError' \
+ "$SERVER_DIR/logs"; then
+ echo "Native linkage or Java compatibility error found in $SERVER_DIR/logs" >&2
+ grep -Eirn 'undefined symbol|UnsatisfiedLinkError|UnsupportedClassVersionError|NoClassDefFoundError' \
+ "$SERVER_DIR/logs" >&2
+ exit 1
+fi
+
+echo "native-runtime-smoke-ok"
diff --git a/hugegraph-server/hugegraph-dist/src/assembly/travis/run-rocksdb-jni-smoke-test.sh b/hugegraph-server/hugegraph-dist/src/assembly/travis/run-rocksdb-jni-smoke-test.sh
new file mode 100755
index 0000000000..5603431d5d
--- /dev/null
+++ b/hugegraph-server/hugegraph-dist/src/assembly/travis/run-rocksdb-jni-smoke-test.sh
@@ -0,0 +1,130 @@
+#!/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.
+#
+set -euo pipefail
+
+if [[ $# -ne 1 ]]; then
+ echo "USAGE: $0 SERVER_DIR" >&2
+ exit 1
+fi
+
+TRAVIS_DIR=$(cd "$(dirname "$0")" && pwd)
+SERVER_DIR=$(cd "$1" && pwd)
+EXPECTED_ARCH=${EXPECTED_ARCH:-}
+EXPECTED_JAVA_MAJOR=${EXPECTED_JAVA_MAJOR:-11}
+ACTUAL_ARCH=$(uname -m)
+
+if [[ -n "${JAVA_HOME:-}" ]]; then
+ JAVA_CMD="$JAVA_HOME/bin/java"
+else
+ JAVA_CMD=$(command -v java || true)
+fi
+if [[ -z "$JAVA_CMD" || ! -x "$JAVA_CMD" ]]; then
+ echo "Java executable is unavailable: ${JAVA_CMD:-not found}" >&2
+ exit 1
+fi
+
+if [[ -n "$EXPECTED_ARCH" && "$ACTUAL_ARCH" != "$EXPECTED_ARCH" ]]; then
+ echo "Expected architecture $EXPECTED_ARCH, got $ACTUAL_ARCH" >&2
+ exit 1
+fi
+
+JAVA_VERSION=$("$JAVA_CMD" -version 2>&1 | \
+ awk -F '"' '/version/ {print $2; exit}')
+JAVA_MAJOR=${JAVA_VERSION%%.*}
+if [[ "$JAVA_MAJOR" == "1" ]]; then
+ JAVA_MAJOR=$(echo "$JAVA_VERSION" | cut -d. -f2)
+fi
+if [[ "$JAVA_MAJOR" != "$EXPECTED_JAVA_MAJOR" ]]; then
+ echo "Expected Java $EXPECTED_JAVA_MAJOR, got $JAVA_VERSION" >&2
+ exit 1
+fi
+
+JAVA_PROPERTIES=$("$JAVA_CMD" -XshowSettings:properties -version 2>&1)
+java_property() {
+ local key=$1
+ awk -F ' = ' -v key="$key" '{
+ name = $1
+ sub(/^[[:space:]]*/, "", name)
+ if (name == key) {
+ print $2
+ exit
+ }
+ }' <<< "$JAVA_PROPERTIES"
+}
+
+JAVA_VM_NAME=$(java_property java.vm.name)
+JAVA_VM_VENDOR=$(java_property java.vm.vendor)
+JAVA_VM_VERSION=$(java_property java.vm.version)
+JAVA_VM_INFO=$(java_property java.vm.info)
+
+if [[ -z "$JAVA_VM_NAME" || -z "$JAVA_VM_VENDOR" || -z "$JAVA_VM_INFO" ]]; then
+ echo "Failed to read Java VM properties" >&2
+ exit 1
+fi
+
+echo "Architecture: $ACTUAL_ARCH"
+echo "Java: $JAVA_VERSION"
+echo "Java VM: $JAVA_VM_NAME ($JAVA_VM_VENDOR, $JAVA_VM_VERSION, $JAVA_VM_INFO)"
+if command -v getconf >/dev/null 2>&1; then
+ echo "C library: $(getconf GNU_LIBC_VERSION 2>/dev/null || echo unknown)"
+fi
+
+. "$SERVER_DIR/bin/util.sh"
+configure_riscv64_libatomic
+
+if [[ "$ACTUAL_ARCH" == "riscv64" ]]; then
+ EXPECTED_RISCV64_JAVA_VERSION=${EXPECTED_RISCV64_JAVA_VERSION:-11.0.31.28}
+ if [[ "$JAVA_VERSION" != "$EXPECTED_RISCV64_JAVA_VERSION" ]]; then
+ echo "Expected RISC-V Java $EXPECTED_RISCV64_JAVA_VERSION, got $JAVA_VERSION" >&2
+ exit 1
+ fi
+ if [[ "$JAVA_VM_NAME" != "OpenJDK 64-Bit Server VM" ]]; then
+ echo "Expected RISC-V Server VM, got $JAVA_VM_NAME" >&2
+ exit 1
+ fi
+ if [[ "$JAVA_VM_VENDOR" != "Alibaba" ]]; then
+ echo "Expected RISC-V Java vendor Alibaba, got $JAVA_VM_VENDOR" >&2
+ exit 1
+ fi
+ if [[ "$JAVA_VM_INFO" != *"mixed mode"* ]]; then
+ echo "Expected RISC-V Java mixed mode, got $JAVA_VM_INFO" >&2
+ exit 1
+ fi
+ if [[ "${LD_PRELOAD:-}" != *"libatomic.so.1"* ]]; then
+ echo "libatomic.so.1 was not added to LD_PRELOAD on riscv64" >&2
+ exit 1
+ fi
+ echo "LD_PRELOAD: $LD_PRELOAD"
+fi
+
+WORK_DIR=$(mktemp -d "${TMPDIR:-/tmp}/hugegraph-rocksdb-jni-smoke.XXXXXX")
+SMOKE_LOG="$WORK_DIR/rocksdb-jni-smoke.log"
+export ROCKSDB_SMOKE_DIR="$WORK_DIR/db"
+
+cleanup() {
+ rm -rf "$WORK_DIR"
+}
+trap cleanup EXIT
+
+"$JAVA_CMD" -cp "$SERVER_DIR/lib/*" groovy.ui.GroovyMain \
+ "$TRAVIS_DIR/rocksdb-jni-smoke.groovy" | tee "$SMOKE_LOG"
+
+if ! grep -q '^rocksdb-jni-smoke-ok$' "$SMOKE_LOG"; then
+ echo "RocksDB JNI smoke marker not found" >&2
+ exit 1
+fi
diff --git a/hugegraph-server/hugegraph-dist/src/assembly/travis/run-server-e2e-smoke-test.sh b/hugegraph-server/hugegraph-dist/src/assembly/travis/run-server-e2e-smoke-test.sh
new file mode 100755
index 0000000000..9762e4fa26
--- /dev/null
+++ b/hugegraph-server/hugegraph-dist/src/assembly/travis/run-server-e2e-smoke-test.sh
@@ -0,0 +1,191 @@
+#!/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.
+#
+set -euo pipefail
+
+if [[ $# -ne 3 || ( "$2" != "create" && "$2" != "verify" ) ]]; then
+ echo "USAGE: $0 SERVER_URL create|verify RUN_ID" >&2
+ exit 1
+fi
+
+SERVER_URL=${1%/}
+MODE=$2
+RUN_ID=$3
+
+if [[ ! "$RUN_ID" =~ ^[a-zA-Z0-9_]+$ ]]; then
+ echo "RUN_ID must contain only letters, numbers, and underscores" >&2
+ exit 1
+fi
+
+GRAPH_PATH=/graphspaces/DEFAULT/graphs/hugegraph
+WORK_DIR=$(mktemp -d "${TMPDIR:-/tmp}/hugegraph-server-smoke.XXXXXX")
+RESPONSE_FILE="$WORK_DIR/response.json"
+PROPERTY_KEY="riscv_smoke_name_$RUN_ID"
+VERTEX_LABEL="riscv_smoke_node_$RUN_ID"
+EDGE_LABEL="riscv_smoke_link_$RUN_ID"
+VERTEX_ONE="riscv-smoke-v1-$RUN_ID"
+VERTEX_TWO="riscv-smoke-v2-$RUN_ID"
+CURL_CONNECT_TIMEOUT=3
+CURL_PROBE_TIMEOUT=5
+CURL_REQUEST_TIMEOUT=60
+SERVER_READY_TIMEOUT=240
+
+cleanup() {
+ rm -rf "$WORK_DIR"
+}
+trap cleanup EXIT
+
+for command in curl jq; do
+ if ! command -v "$command" >/dev/null 2>&1; then
+ echo "Required command is unavailable: $command" >&2
+ exit 1
+ fi
+done
+
+if [[ -n "${HUGEGRAPH_USERNAME:-}" || -n "${HUGEGRAPH_PASSWORD:-}" ]]; then
+ if [[ -z "${HUGEGRAPH_USERNAME:-}" || -z "${HUGEGRAPH_PASSWORD:-}" ]]; then
+ echo "Set both HUGEGRAPH_USERNAME and HUGEGRAPH_PASSWORD" >&2
+ exit 1
+ fi
+fi
+
+curl_request() {
+ if [[ -n "${HUGEGRAPH_USERNAME:-}" ]]; then
+ curl --compressed --user "$HUGEGRAPH_USERNAME:$HUGEGRAPH_PASSWORD" "$@"
+ else
+ curl --compressed "$@"
+ fi
+}
+
+wait_for_server() {
+ local deadline=$((SECONDS + SERVER_READY_TIMEOUT))
+ while ((SECONDS < deadline)); do
+ if curl_request --silent --show-error --fail \
+ --connect-timeout "$CURL_CONNECT_TIMEOUT" \
+ --max-time "$CURL_PROBE_TIMEOUT" \
+ "$SERVER_URL/versions" >/dev/null 2>&1; then
+ return 0
+ fi
+ sleep 1
+ done
+ echo "HugeGraph did not become ready at $SERVER_URL" >&2
+ return 1
+}
+
+request() {
+ local method=$1
+ local path=$2
+ local expected_status=$3
+ local body=${4:-}
+ local status
+ local curl_args=(--silent --show-error --output "$RESPONSE_FILE" \
+ --write-out '%{http_code}' --request "$method" \
+ --connect-timeout "$CURL_CONNECT_TIMEOUT" \
+ --max-time "$CURL_REQUEST_TIMEOUT" \
+ --header 'Content-Type: application/json')
+
+ if [[ -n "$body" ]]; then
+ curl_args+=(--data "$body")
+ fi
+ status=$(curl_request "${curl_args[@]}" "$SERVER_URL$path")
+ if [[ "$status" != "$expected_status" ]]; then
+ echo "$method $path returned HTTP $status, expected $expected_status" >&2
+ cat "$RESPONSE_FILE" >&2
+ return 1
+ fi
+}
+
+assert_json() {
+ local expression=$1
+ shift
+ if ! jq --exit-status "$@" "$expression" "$RESPONSE_FILE" >/dev/null; then
+ echo "JSON assertion failed: $expression" >&2
+ cat "$RESPONSE_FILE" >&2
+ return 1
+ fi
+}
+
+verify_graph() {
+ request GET "$GRAPH_PATH/graph/vertices/%22$VERTEX_ONE%22" 200
+ assert_json '.id == $id and .label == $label and .properties[$key] == "first"' \
+ --arg id "$VERTEX_ONE" --arg label "$VERTEX_LABEL" \
+ --arg key "$PROPERTY_KEY"
+
+ request GET "$GRAPH_PATH/graph/vertices/%22$VERTEX_TWO%22" 200
+ assert_json '.id == $id and .label == $label and .properties[$key] == "second"' \
+ --arg id "$VERTEX_TWO" --arg label "$VERTEX_LABEL" \
+ --arg key "$PROPERTY_KEY"
+
+ request GET "$GRAPH_PATH/graph/edges" 200
+ assert_json '.edges | any(.label == $label and .outV == $out and .inV == $in)' \
+ --arg label "$EDGE_LABEL" --arg out "$VERTEX_ONE" \
+ --arg in "$VERTEX_TWO"
+
+ request POST /gremlin 200 \
+ "$(jq -cn --arg query "g.V().hasLabel('$VERTEX_LABEL').count()" \
+ '{gremlin:$query, bindings:{}, language:"gremlin-groovy",
+ aliases:{g:"__g_DEFAULT-hugegraph"}}')"
+ assert_json '.result.data == [2]'
+}
+
+wait_for_server
+request GET /versions 200
+assert_json 'type == "object" and length > 0'
+
+if [[ "$MODE" == "create" ]]; then
+ request POST "$GRAPH_PATH/schema/propertykeys" 202 \
+ "$(jq -cn --arg name "$PROPERTY_KEY" \
+ '{name:$name, data_type:"TEXT", cardinality:"SINGLE", properties:[]}')"
+ assert_json '.property_key.name == $name and .task_id == 0' \
+ --arg name "$PROPERTY_KEY"
+
+ request POST "$GRAPH_PATH/schema/vertexlabels" 201 \
+ "$(jq -cn --arg name "$VERTEX_LABEL" --arg key "$PROPERTY_KEY" \
+ '{name:$name, id_strategy:"CUSTOMIZE_STRING", properties:[$key],
+ primary_keys:[], nullable_keys:[]}')"
+ assert_json '.name == $name' --arg name "$VERTEX_LABEL"
+
+ request POST "$GRAPH_PATH/schema/edgelabels" 201 \
+ "$(jq -cn --arg name "$EDGE_LABEL" --arg label "$VERTEX_LABEL" \
+ '{name:$name, source_label:$label, target_label:$label,
+ frequency:"SINGLE", properties:[], sort_keys:[], nullable_keys:[]}')"
+ assert_json '.name == $name' --arg name "$EDGE_LABEL"
+
+ request POST "$GRAPH_PATH/graph/vertices" 201 \
+ "$(jq -cn --arg id "$VERTEX_ONE" --arg label "$VERTEX_LABEL" \
+ --arg key "$PROPERTY_KEY" \
+ '{id:$id, label:$label, properties:{($key):"first"}}')"
+ assert_json '.id == $id' --arg id "$VERTEX_ONE"
+
+ request POST "$GRAPH_PATH/graph/vertices" 201 \
+ "$(jq -cn --arg id "$VERTEX_TWO" --arg label "$VERTEX_LABEL" \
+ --arg key "$PROPERTY_KEY" \
+ '{id:$id, label:$label, properties:{($key):"second"}}')"
+ assert_json '.id == $id' --arg id "$VERTEX_TWO"
+
+ request POST "$GRAPH_PATH/graph/edges" 201 \
+ "$(jq -cn --arg label "$EDGE_LABEL" --arg vertexLabel "$VERTEX_LABEL" \
+ --arg out "$VERTEX_ONE" --arg in "$VERTEX_TWO" \
+ '{label:$label, outVLabel:$vertexLabel, inVLabel:$vertexLabel,
+ outV:$out, inV:$in, properties:{}}')"
+ assert_json '.label == $label and .outV == $out and .inV == $in' \
+ --arg label "$EDGE_LABEL" --arg out "$VERTEX_ONE" \
+ --arg in "$VERTEX_TWO"
+fi
+
+verify_graph
+echo "server-e2e-smoke-$MODE-ok"
diff --git a/hugegraph-server/hugegraph-dist/src/main/resources/backend.properties b/hugegraph-server/hugegraph-dist/src/main/resources/backend.properties
index 45060f90c2..119ee02441 100644
--- a/hugegraph-server/hugegraph-dist/src/main/resources/backend.properties
+++ b/hugegraph-server/hugegraph-dist/src/main/resources/backend.properties
@@ -15,4 +15,4 @@
# limitations under the License.
#
-backends=[rocksdb, hbase, hstore]
+backends=[${hugegraph.backends}]