yugaaank commented on code in PR #3104:
URL: https://github.com/apache/hugegraph/pull/3104#discussion_r3635705460


##########
hugegraph-server/hugegraph-dist/src/assembly/travis/run-riscv64-smoke.sh:
##########
@@ -0,0 +1,474 @@
+#!/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.
+#
+# run-riscv64-smoke.sh — Disposable RISC-V (linux/riscv64) end-to-end smoke 
test.
+#
+# Builds the HugeGraph Server distribution on the contributor's native host
+# (x86-64 / arm64), then runs it inside an emulated linux/riscv64 container to
+# verify the RocksDB backend + Gremlin stack actually work on RISC-V via QEMU.
+#
+# This is a CORRECTNESS gate, not a performance benchmark. QEMU user-mode
+# emulation is several times slower than native; expect the full run (build +
+# init + REST + Gremlin + restart) to take many minutes the first time.
+#
+# It requires Docker with QEMU/binfmt_misc multi-platform support (Docker
+# Desktop ships this; on Linux install it with:
+#   docker run --privileged --rm tonistiigi/binfmt --install all
+# No host RISC-V packages are installed by this script.
+#
+# It removes ONLY what it creates: a uniquely-named container, volume, and
+# locally-built image. The QEMU emulator registration is left untouched
+# (remove it with: docker run --privileged --rm tonistiigi/binfmt --uninstall 
all).
+#
+# Usage:
+#   ./run-riscv64-smoke.sh [path-to-server-dist.tar.gz]
+#   ./run-riscv64-smoke.sh            # builds the dist under hugegraph-server/
+#
+set -euo pipefail
+
+ARCH=riscv64
+PLATFORM="linux/${ARCH}"
+# Ubuntu 24.04 publishes openjdk-11-jre-headless for linux/riscv64 (glibc).
+BASE_IMAGE="ubuntu:24.04"
+
+REPO_ROOT=$(cd "$(dirname "$0")/../../../../.." && pwd)
+
+# Use disk-backed temp: the extracted server (~1GB) blows out a small RAM tmpfs
+# /tmp. Default to a work dir on the same (large) filesystem as the repo.
+: "${TMPDIR:=$REPO_ROOT/.riscv64-smoke-tmp}"
+mkdir -p "$TMPDIR"
+export TMPDIR
+
+# Unique, traceable names so cleanup removes only our own resources.
+STAMP=$(date +%Y%m%d%H%M%S)
+TAG="hugegraph-riscv64-smoke:${STAMP}"
+# KEEP: don't auto-cleanup so a failed/finished run stays pokeable.
+VOL="hugegraph-riscv64-data-${STAMP}"
+CONTAINER="hugegraph-riscv64-smoke-${STAMP}"
+
+DIST_ARG="${1:-}"
+DIST_TAR=""
+SERVER_DIR=""
+BUILD_CTX=""
+ROCKS_BUILD=""
+
+GREEN=$'\033[0;32m'; RED=$'\033[0;31m'; NC=$'\033[0m'
+pass() { echo "${GREEN}PASS${NC} $*"; }
+fail() { echo "${RED}FAIL${NC} $*" >&2; }
+
+# Check if the container is still alive (returns 0 if running).
+container_alive() {
+    $DOCKER ps --format '{{.Names}}' 2>/dev/null | grep -qx "$CONTAINER"
+}
+
+# Run docker exec with container-liveness guard + exit-code logging.
+# Usage: safe_exec $DOCKER exec <container> <cmd> [args...]
+# Handles multi-word DOCKER values (e.g. "sudo docker") via eval.
+# Returns the command's output on stdout; logs failures to stderr.
+safe_exec() {
+    local rc
+    if ! container_alive; then
+        fail "container '$CONTAINER' is no longer running (before: $*)"
+        dump_health_diagnostics
+        return 1
+    fi
+    set +e
+    eval "$@"
+    rc=$?
+    set -e
+    if [[ $rc -ne 0 ]]; then
+        fail "command failed (exit $rc): $*"
+        dump_health_diagnostics
+        return 1
+    fi
+}
+
+# DOCKER: allow callers to override (e.g. "sudo docker"). Defaults to plain 
docker.
+DOCKER=${DOCKER:-docker}
+
+# Dump container diagnostics to stderr. Defined early so the EXIT trap can
+# call it even if the script fails before the main body.
+dump_health_diagnostics() {
+    echo "----- health-check diagnostics ($CONTAINER) -----" >&2
+    $DOCKER inspect --format \
+        'state={{.State.Status}} exit={{.State.ExitCode}} 
error={{.State.Error}}' \
+        "$CONTAINER" >&2 || true
+
+    if $DOCKER ps --format '{{.Names}}' 2>/dev/null | grep -qx "$CONTAINER"; 
then
+        echo "----- listening processes -----" >&2
+        $DOCKER exec "$CONTAINER" sh -c \
+            'lsof -nP -iTCP:8080 -sTCP:LISTEN || true' >&2 || true
+        for endpoint in versions graphs; do
+            echo "----- GET /$endpoint -----" >&2
+            $DOCKER exec "$CONTAINER" curl -i -sS --connect-timeout 5 
--max-time 15 \
+                "http://127.0.0.1:8080/$endpoint"; >&2 || true
+        done
+    fi
+
+    echo "----- recent container logs -----" >&2
+    $DOCKER logs --tail 100 "$CONTAINER" >&2 || true
+    echo "----- end health-check diagnostics -----" >&2
+}
+
+_EXIT_CODE=0
+_exit_handler() {
+    local rc=$?
+    # If the script failed, log the exit code so the root cause isn't lost.
+    if [[ $rc -ne 0 ]]; then
+        echo "" >&2
+        echo "==> Script failed with exit code $rc" >&2
+        if container_alive; then
+            dump_health_diagnostics
+        else
+            echo "==> container '$CONTAINER' is not running (may have been 
OOM-killed or crashed)" >&2
+            # Best-effort: check docker inspect for OOMKilled + dump any 
surviving logs
+            $DOCKER inspect --format \
+                'OOMKilled={{.State.OOMKilled}} ExitCode={{.State.ExitCode}} 
Status={{.State.Status}}' \
+                "$CONTAINER" >&2 2>/dev/null || true
+            $DOCKER logs --tail 80 "$CONTAINER" >&2 2>/dev/null || true
+        fi
+    fi
+    _cleanup
+}
+
+_cleanup() {
+    # KEEP=1: leave everything standing for live poking. Still dump logs so a
+    # failure's root cause is visible; skip all teardown.
+    if [[ -n "${KEEP:-}" ]]; then
+        echo "==> KEEP=1 set — skipping cleanup. Resources left standing:" >&2
+        echo "    container: $CONTAINER" >&2
+        echo "    volume:    $VOL" >&2
+        echo "    image:     $TAG" >&2
+        return 0
+    fi
+    # Dump container logs first so a failure's root cause isn't destroyed by 
cleanup.
+    if $DOCKER ps -a --format '{{.Names}}' 2>/dev/null | grep -qx 
"$CONTAINER"; then
+        echo "----- container logs ($CONTAINER) -----" >&2
+        $DOCKER logs --tail 60 "$CONTAINER" >&2 || true
+        echo "----- end logs -----" >&2
+        # The startup wrapper hides the real error in the server log file; 
dump it
+        # from the stopped container (docker cp works on exited containers).
+        echo "----- hugegraph-server.log -----" >&2
+        $DOCKER cp "$CONTAINER:/server/logs/hugegraph-server.log" - 
2>/dev/null \
+            | tar -xO 2>/dev/null | tail -n 80 >&2 || true
+        echo "----- end hugegraph-server.log -----" >&2
+    fi
+    $DOCKER rm -f "$CONTAINER" >/dev/null 2>&1 || true
+    # Only remove the volume we created.
+    $DOCKER volume rm "$VOL" >/dev/null 2>&1 || true
+    # Only remove the image we tagged; never prune the host's image store.
+    $DOCKER rmi "$TAG" >/dev/null 2>&1 || true
+    # Remove disk-backed temp work dirs (build context holds the ~1GB extract).
+    [[ -n "$BUILD_CTX"   && -d "$BUILD_CTX"   ]] && rm -rf "$BUILD_CTX"
+    [[ -n "$ROCKS_BUILD" && -d "$ROCKS_BUILD" ]] && rm -rf "$ROCKS_BUILD"
+}
+trap _exit_handler EXIT
+
+need() { command -v "$1" >/dev/null 2>&1 || { fail "missing required command: 
$1"; exit 1; }; }
+need docker
+# buildx is required: the legacy builder ignores --platform.
+$DOCKER buildx version >/dev/null 2>&1 || { fail "docker buildx is required 
(pacman -S docker-buildx)"; exit 1; }
+
+# Register QEMU user-mode emulation for riscv64 if the kernel can't already run
+# it (otherwise the build/run fails with 'exec format error'). binfmt handlers
+# do NOT survive reboots, so a fresh checkout needs this — makes the smoke test
+# self-contained on any x86 host. Pinned tag: :latest 403s on Docker Hub.
+if ! ls /proc/sys/fs/binfmt_misc/qemu-riscv64 >/dev/null 2>&1; then
+    echo "==> Registering QEMU riscv64 emulation (binfmt)"
+    $DOCKER run --privileged --rm tonistiigi/binfmt:qemu-v8.1.5 --install 
riscv64 >/dev/null
+fi
+
+# 1. Resolve the Server distribution tarball (build on host if not supplied).
+#    Build from the ROOT pom (not hugegraph-server/): the root parent pom
+#    `hugegraph` and the root sibling `hugegraph-struct` must be installed
+#    into ~/.m2 first, or module resolution fails on the ${revision} parent.
+if [[ -n "$DIST_ARG" ]]; then
+    DIST_TAR="$DIST_ARG"
+else
+    echo "==> Building HugeGraph Server distribution on host (native platform)"
+    mvn -f "$REPO_ROOT/pom.xml" package -e -B -ntp \
+        -Dmaven.test.skip=true -Dmaven.javadoc.skip=true -Drat.skip=true \
+        -pl hugegraph-server/hugegraph-dist -am
+    DIST_TAR=$(ls -t 
"$REPO_ROOT"/hugegraph-server/apache-hugegraph-server-*.tar.gz 2>/dev/null | 
head -n1 || true)
+fi
+[[ -f "$DIST_TAR" ]] || { fail "server distribution tarball not found"; exit 
1; }
+echo "==> Using distribution: $DIST_TAR"
+
+# Extract straight into the build context's server/ dir — no second copy
+# (the extracted tree is ~1GB; copying it twice doubled the temp footprint).
+BUILD_CTX=$(mktemp -d "hugegraph-riscv64-ctx.XXXXXX")
+SERVER_DIR="$BUILD_CTX/server"
+mkdir -p "$SERVER_DIR"
+tar -xzf "$DIST_TAR" -C "$SERVER_DIR" --strip-components=1
+
+# Force the RocksDB backend for the smoke run.
+CONF="$SERVER_DIR/conf/graphs/hugegraph.properties"
+grep -qE '^[[:space:]]*backend[[:space:]]*=' "$CONF" \
+    && sed -i 's/^[[:space:]]*backend[[:space:]]*=.*/backend=rocksdb/' "$CONF" 
\
+    || echo "backend=rocksdb" >> "$CONF"
+grep -qE '^[[:space:]]*serializer[[:space:]]*=' "$CONF" \
+    && sed -i 's/^[[:space:]]*serializer[[:space:]]*=.*/serializer=binary/' 
"$CONF" \
+    || echo "serializer=binary" >> "$CONF"
+
+# 2. Build a tiny RISC-V runtime image on top of the glibc base.
+#    libatomic1 is installed and exposed via LD_LIBRARY_PATH (+ a /usr/lib
+#    symlink) so RocksDB's __atomic_compare_exchange_1 symbol resolves
+#    automatically — no manual step for the user. If the JNI still lacks the
+#    symbol, the RocksDB step below fails loudly instead of being treated as
+#    the goal.
+# Reuse the repo's own container entrypoint: it runs init-store.sh (create the
+# RocksDB store) BEFORE start-hugegraph.sh. Hand-rolling the start command
+# skips init and the server aborts with an empty log (the failure we hit).
+cp "$REPO_ROOT/hugegraph-server/hugegraph-dist/docker/docker-entrypoint.sh" \
+    "$BUILD_CTX/server/docker-entrypoint.sh"
+# The entrypoint hardcodes 'start-hugegraph.sh ... -t 120'. Under QEMU 
emulation
+# the analyzer dictionary loads twice (~3.5 min each) plus Gremlin/Groovy 
warmup,
+# so the server needs well over 600s to bind 8080. Raise the startup timeout.
+sed -i 's/-t 120/-t 1800/' "$BUILD_CTX/server/docker-entrypoint.sh"
+cat > "$BUILD_CTX/Dockerfile" <<EOF
+FROM ${BASE_IMAGE}
+ENV DEBIAN_FRONTEND=noninteractive
+RUN apt-get -q update && apt-get -q install -y --no-install-recommends 
--no-install-suggests openjdk-11-jre-headless libatomic1 curl jq dumb-init lsof 
procps && apt-get clean && rm -rf /var/lib/apt/lists/*
+# Force libatomic to load before the JNI: rocksdbjni:8.10.2's RISC-V
+# .so has an undefined __atomic_compare_exchange_1 that only libatomic
+# satisfies. LD_LIBRARY_PATH alone is NOT enough (verified: symbol stays
+# unresolved); LD_PRELOAD guarantees libatomic loads first into every
+# Java process (the RocksDB smoke test AND the server).
+# Path is the constant riscv64 multiarch dir confirmed inside the image;
+# dpkg-architecture is unreliable under QEMU emulation, so hardcode it.
+RUN ln -sf /usr/lib/riscv64-linux-gnu/libatomic.so.1 /usr/lib/libatomic.so.1
+WORKDIR /server
+COPY server/ /server/
+RUN chmod 755 /server/docker-entrypoint.sh && sed -i 
"s#^restserver.url.*#restserver.url=http://127.0.0.1:8080#"; 
/server/conf/rest-server.properties
+# The Ubuntu openjdk-11 RISC-V build lacks -XX:G1RSetUpdatingPauseTimePercent,
+# which start-hugegraph.sh always appends for the default G1GC. It is a tuning
+# hint, not required for correctness, so strip it to let the JVM launch.
+RUN sed -i "s/-XX:G1RSetUpdatingPauseTimePercent=5//" 
/server/bin/hugegraph-server.sh
+# JAVA_OPTS mirrors the official image (auth proxy needs --add-exports on JDK 
11).
+# STDOUT_MODE=true routes the server log to stdout so 'docker logs' shows 
failures.
+ENV JAVA_OPTS="-XX:+UnlockExperimentalVMOptions -XX:+UseContainerSupport 
-XX:MaxRAMPercentage=50 
--add-exports=java.base/jdk.internal.reflect=ALL-UNNAMED" 
LD_PRELOAD=/usr/lib/libatomic.so.1 HUGEGRAPH_HOME=server STDOUT_MODE=true
+EXPOSE 8080
+ENTRYPOINT ["/usr/bin/dumb-init", "--"]
+CMD ["/server/docker-entrypoint.sh"]
+EOF
+
+echo "==> Building RISC-V runtime image $TAG"
+$DOCKER buildx build --platform "$PLATFORM" -t "$TAG" "$BUILD_CTX" --load
+
+# 3. Start the server in the background; trap handles cleanup.
+echo "==> Starting server container on ${PLATFORM}"
+$DOCKER run -d --name "$CONTAINER" --platform "$PLATFORM" \
+    -p 8080 --memory=4g -v "$VOL:/server" "$TAG"
+
+# Wait until /graphs answers 200 (server auto-inits + starts via entrypoint).
+# This is the same readiness endpoint used by start-hugegraph.sh. /versions is
+# asserted separately after the server has completed its startup sequence.
+wait_health() {
+    # This probe starts while the entrypoint is still running init-store.sh.
+    # Under QEMU the analyzer dictionary loads once during initialization and
+    # again during server startup, so the full end-to-end wait needs to exceed
+    # the entrypoint's 1800s startup timeout as well as initialization time.
+    local elapsed=0 limit=3600
+    while (( elapsed < limit )); do
+        # Bail fast if the container died instead of polling a corpse for 10 
min.
+        if ! $DOCKER ps --format '{{.Names}}' 2>/dev/null | grep -qx 
"$CONTAINER"; then
+            echo "==> container '$CONTAINER' is no longer running" >&2
+            dump_health_diagnostics
+            return 1
+        fi
+        local code
+        code=$($DOCKER exec "$CONTAINER" \

Review Comment:
   Fixed. Added ` --connect-timeout 10 --max-time {60|300} ` to all 5 container 
curl calls. ` wait_health ` now uses ` $SECONDS ` for wall-clock tracking 
instead of a manual counter that didn't advance during curl execution.



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to