Copilot commented on code in PR #10046:
URL: https://github.com/apache/gravitino/pull/10046#discussion_r2875894966


##########
integration-test-common/doris-docker-script/start-be.sh:
##########
@@ -0,0 +1,87 @@
+#!/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.
+#
+
+# BE-only startup script for docker-compose multi-container deployment.
+# This overrides the image's default start.sh which starts both FE and BE.
+
+DORIS_HOME="$(dirname "${BASH_SOURCE-$0}")"
+DORIS_HOME="$(cd "${DORIS_HOME}" >/dev/null; pwd)"
+DORIS_BE_HOME="${DORIS_HOME}/be/"
+
+# Patch BE startup script: skip vm.max_map_count and ulimit checks, remove 
slow chmod
+DORIS_BE_SCRIPT="${DORIS_BE_HOME}/bin/start_be.sh"
+sed -i '/Please set vm.max_map_count/,/exit 1/{s/exit 1/#exit 1\n        echo 
"skip this"/}' ${DORIS_BE_SCRIPT}
+sed -i '/Please set the maximum number of open file descriptors/,/exit 
1/{s/exit 1/#exit 1\n        echo "skip this"/}' ${DORIS_BE_SCRIPT}
+sed -i 's/chmod 755 "${DORIS_HOME}\/lib\/doris_be"/#&/' ${DORIS_BE_SCRIPT}
+
+# Configure priority_networks and report interval in be.conf
+CONTAINER_IP=$(hostname -i)
+PRIORITY_NETWORKS=$(echo "${CONTAINER_IP}" | awk -F '.' 
'{print$1"."$2"."$3".0/24"}')
+echo "add priority_networks = ${PRIORITY_NETWORKS} to be.conf"
+echo "priority_networks = ${PRIORITY_NETWORKS}" >> 
${DORIS_BE_HOME}/conf/be.conf
+echo "report_disk_state_interval_seconds = 10" >> ${DORIS_BE_HOME}/conf/be.conf
+
+# Start only BE in daemon mode
+${DORIS_BE_HOME}/bin/start_be.sh --daemon
+
+# Wait for BE to be ready
+be_started=false
+for i in {1..20}; do
+  sleep 5
+  url="localhost:8040/api/health"
+  echo "check be for the $i times"
+  response=$(curl --silent --request GET $url)
+
+  if echo "$response" | grep -q '"status": "OK"'; then
+    be_started=true
+    echo "Doris BE started successfully, response: $response"
+    break
+  else
+    echo "check failed, response: $response"
+  fi
+done
+
+if [ "$be_started" = false ]; then
+  echo "ERROR: Doris BE failed to start"

Review Comment:
   If BE fails the readiness check (`be_started` is false), the script only 
prints an error and then proceeds, keeping the container alive. Please `exit 1` 
(non-zero) on this failure so the compose stack fails fast instead of leaving a 
broken BE container running.
   ```suggestion
     echo "ERROR: Doris BE failed to start"
     exit 1
   ```



##########
integration-test-common/src/test/java/org/apache/gravitino/integration/test/container/DorisContainer.java:
##########
@@ -63,78 +84,130 @@ protected DorisContainer(
       Map<String, String> envVars,
       Optional<Network> network) {
     super(image, hostName, ports, extraHosts, filesToMount, envVars, network);
-  }
 
-  @Override
-  protected void setupContainer() {
-    super.setupContainer();
-    withLogConsumer(new PrintingContainerLog(format("%-14s| ", 
"DorisContainer")));
-    withStartupTimeout(Duration.ofMinutes(5));
+    String dir = System.getenv("GRAVITINO_ROOT_DIR");
+    if (dir == null || dir.isEmpty()) {
+      throw new RuntimeException("GRAVITINO_ROOT_DIR is not set");
+    }
+    String composeFile =
+        ITUtils.joinPath(
+            dir, "integration-test-common", "doris-docker-script", 
"docker-compose.yaml");
+
+    composeContainer =
+        new ComposeContainer(new File(composeFile))
+            .withEnv("GRAVITINO_CI_DORIS_DOCKER_IMAGE", DEFAULT_IMAGE)
+            .withExposedService(
+                FE_SERVICE,
+                FE_MYSQL_PORT,
+                
Wait.forListeningPort().withStartupTimeout(Duration.ofMinutes(3)))
+            .withExposedService(FE_SERVICE, FE_HTTP_PORT)
+            .withScaledService(BE_SERVICE, BE_SCALE)
+            .withStartupTimeout(Duration.ofMinutes(5))
+            .withTailChildContainers(true)
+            .withLocalCompose(true);
+
+    // Expose ports for each BE instance (1-indexed)
+    for (int i = 1; i <= BE_SCALE; i++) {
+      composeContainer
+          .withExposedService(
+              BE_SERVICE,
+              i,
+              BE_HEARTBEAT_PORT,
+              
Wait.forListeningPort().withStartupTimeout(Duration.ofMinutes(3)))
+          .withExposedService(BE_SERVICE, i, BE_WEBSERVER_PORT);
+    }
   }
 
   @Override
   public void start() {
-    super.start();
+    composeContainer.start();
+    resolveFeIpAddress();
     Preconditions.check("Doris container startup failed!", 
checkContainerStatus(5));
     Preconditions.check("Doris container password change failed!", 
changePassword());
   }
 
   @Override
   public void close() {
     copyDorisLog();
-    super.close();
+    if (composeContainer != null) {
+      composeContainer.stop();
+    }
   }
 
   private void copyDorisLog() {
     try {
-      // stop Doris container
       String destPath = System.getenv("IT_PROJECT_DIR");
-      LOG.info("Copy doris log file to {}", destPath);
-
-      String feTarPath = "/doris-be.tar";
-      String beTarPath = "/doris-fe.tar";
-
-      // Pack the jar files
-      container.execInContainer("tar", "cf", feTarPath, DORIS_BE_PATH);
-      container.execInContainer("tar", "cf", beTarPath, DORIS_FE_PATH);
-
-      container.copyFileFromContainer(feTarPath, destPath + File.separator + 
"doris-be.tar");
-      container.copyFileFromContainer(beTarPath, destPath + File.separator + 
"doris-fe.tar");
+      if (destPath == null || destPath.isEmpty()) {
+        LOG.warn("IT_PROJECT_DIR is not set, skipping Doris log copy");
+        return;
+      }
+      LOG.info("Copy doris log files to {}", destPath);
+      copyContainerLog(FE_SERVICE, DORIS_FE_PATH, destPath, "doris-fe");
+      for (int i = 1; i <= BE_SCALE; i++) {
+        copyContainerLog(BE_SERVICE + "-" + i, DORIS_BE_PATH, destPath, 
"doris-be-" + i);
+      }
     } catch (Exception e) {
-      LOG.error("Failed to copy container log to local", e);
+      LOG.error("Failed to copy Doris container logs to local", e);
+    }
+  }
+
+  private void copyContainerLog(String serviceName, String logPath, String 
destPath, String tarName)
+      throws Exception {
+    Optional<ContainerState> container = 
composeContainer.getContainerByServiceName(serviceName);
+    if (container.isPresent()) {
+      String tarPath = "/" + tarName + ".tar";
+      container.get().execInContainer("tar", "cf", tarPath, logPath);
+      container.get().copyFileFromContainer(tarPath, destPath + File.separator 
+ tarName + ".tar");
+    } else {
+      LOG.warn("Doris {} container not found, skipping log copy", serviceName);
     }
   }
 
+  @Override
+  public String getContainerIpAddress() {
+    return feIpAddress;
+  }
+
   @Override
   protected boolean checkContainerStatus(int retryLimit) {
-    String dorisJdbcUrl = format("jdbc:mysql://%s:%d/", 
getContainerIpAddress(), FE_MYSQL_PORT);
-    LOG.info("Doris url is " + dorisJdbcUrl);
+    String dorisJdbcUrl = format("jdbc:mysql://%s:%d/", feIpAddress, 
FE_MYSQL_PORT);
+    LOG.info("Doris JDBC url is {}", dorisJdbcUrl);
 
     await()
-        .atMost(30, TimeUnit.SECONDS)
-        .pollInterval(30 / retryLimit, TimeUnit.SECONDS)
+        .atMost(120, TimeUnit.SECONDS)
+        .pollInterval(120 / retryLimit, TimeUnit.SECONDS)
         .until(
             () -> {
               try (Connection connection =
                       DriverManager.getConnection(dorisJdbcUrl, USER_NAME, "");
                   Statement statement = connection.createStatement()) {
 
-                // execute `SHOW PROC '/backends';` to check if backends is 
ready
+                // execute `SHOW PROC '/backends';` to check if all backends 
are ready
                 String query = "SHOW PROC '/backends';";
+                int aliveCount = 0;
                 try (ResultSet resultSet = statement.executeQuery(query)) {
                   while (resultSet.next()) {
+                    LOG.info("Test " + resultSet.toString());

Review Comment:
   `LOG.info("Test " + resultSet.toString())` inside the backend polling loop 
will spam logs and `ResultSet#toString()` is not meaningful for diagnosing 
readiness. Please remove this debug log or replace it with specific columns 
(and use parameterized logging) if detailed readiness diagnostics are needed.
   ```suggestion
   
   ```



##########
integration-test-common/src/test/java/org/apache/gravitino/integration/test/container/DorisContainer.java:
##########
@@ -63,78 +84,130 @@ protected DorisContainer(
       Map<String, String> envVars,
       Optional<Network> network) {
     super(image, hostName, ports, extraHosts, filesToMount, envVars, network);
-  }
 
-  @Override
-  protected void setupContainer() {
-    super.setupContainer();
-    withLogConsumer(new PrintingContainerLog(format("%-14s| ", 
"DorisContainer")));
-    withStartupTimeout(Duration.ofMinutes(5));
+    String dir = System.getenv("GRAVITINO_ROOT_DIR");
+    if (dir == null || dir.isEmpty()) {
+      throw new RuntimeException("GRAVITINO_ROOT_DIR is not set");
+    }
+    String composeFile =
+        ITUtils.joinPath(
+            dir, "integration-test-common", "doris-docker-script", 
"docker-compose.yaml");
+
+    composeContainer =
+        new ComposeContainer(new File(composeFile))
+            .withEnv("GRAVITINO_CI_DORIS_DOCKER_IMAGE", DEFAULT_IMAGE)

Review Comment:
   `ComposeContainer.withEnv("GRAVITINO_CI_DORIS_DOCKER_IMAGE", DEFAULT_IMAGE)` 
will receive `null` if `GRAVITINO_CI_DORIS_DOCKER_IMAGE` is unset, which can 
lead to an NPE or to docker-compose using an empty image value. Please fail 
fast with a clear validation message or provide a default image (e.g., the 
Doris version referenced in the PR description).
   ```suggestion
       String effectiveImage =
           (DEFAULT_IMAGE != null && !DEFAULT_IMAGE.isEmpty()) ? DEFAULT_IMAGE 
: image;
       Preconditions.checkArgument(
           effectiveImage != null && !effectiveImage.isEmpty(),
           "Doris Docker image must be configured via 
GRAVITINO_CI_DORIS_DOCKER_IMAGE or "
               + "DorisContainer builder image");
   
       composeContainer =
           new ComposeContainer(new File(composeFile))
               .withEnv("GRAVITINO_CI_DORIS_DOCKER_IMAGE", effectiveImage)
   ```



##########
integration-test-common/src/test/java/org/apache/gravitino/integration/test/container/DorisContainer.java:
##########
@@ -63,78 +84,130 @@ protected DorisContainer(
       Map<String, String> envVars,
       Optional<Network> network) {
     super(image, hostName, ports, extraHosts, filesToMount, envVars, network);
-  }
 

Review Comment:
   Even though this class delegates to `ComposeContainer`, it still calls 
`super(...)`, which eagerly constructs and configures a `GenericContainer` (and 
requires a non-null `image`) that is never started or used. This adds 
unnecessary overhead and makes the compose-based container indirectly depend on 
BaseContainer’s image semantics; consider refactoring to avoid creating the 
unused `GenericContainer` (e.g., not extending `BaseContainer`, or adding a 
compose-specific base/contract).



##########
integration-test-common/doris-docker-script/start-be.sh:
##########
@@ -0,0 +1,87 @@
+#!/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.
+#
+
+# BE-only startup script for docker-compose multi-container deployment.
+# This overrides the image's default start.sh which starts both FE and BE.
+
+DORIS_HOME="$(dirname "${BASH_SOURCE-$0}")"
+DORIS_HOME="$(cd "${DORIS_HOME}" >/dev/null; pwd)"
+DORIS_BE_HOME="${DORIS_HOME}/be/"
+
+# Patch BE startup script: skip vm.max_map_count and ulimit checks, remove 
slow chmod
+DORIS_BE_SCRIPT="${DORIS_BE_HOME}/bin/start_be.sh"
+sed -i '/Please set vm.max_map_count/,/exit 1/{s/exit 1/#exit 1\n        echo 
"skip this"/}' ${DORIS_BE_SCRIPT}
+sed -i '/Please set the maximum number of open file descriptors/,/exit 
1/{s/exit 1/#exit 1\n        echo "skip this"/}' ${DORIS_BE_SCRIPT}
+sed -i 's/chmod 755 "${DORIS_HOME}\/lib\/doris_be"/#&/' ${DORIS_BE_SCRIPT}
+
+# Configure priority_networks and report interval in be.conf
+CONTAINER_IP=$(hostname -i)
+PRIORITY_NETWORKS=$(echo "${CONTAINER_IP}" | awk -F '.' 
'{print$1"."$2"."$3".0/24"}')
+echo "add priority_networks = ${PRIORITY_NETWORKS} to be.conf"
+echo "priority_networks = ${PRIORITY_NETWORKS}" >> 
${DORIS_BE_HOME}/conf/be.conf
+echo "report_disk_state_interval_seconds = 10" >> ${DORIS_BE_HOME}/conf/be.conf
+
+# Start only BE in daemon mode
+${DORIS_BE_HOME}/bin/start_be.sh --daemon
+
+# Wait for BE to be ready
+be_started=false
+for i in {1..20}; do
+  sleep 5
+  url="localhost:8040/api/health"
+  echo "check be for the $i times"
+  response=$(curl --silent --request GET $url)
+
+  if echo "$response" | grep -q '"status": "OK"'; then
+    be_started=true
+    echo "Doris BE started successfully, response: $response"
+    break
+  else
+    echo "check failed, response: $response"
+  fi
+done
+
+if [ "$be_started" = false ]; then
+  echo "ERROR: Doris BE failed to start"
+fi
+
+# Register this BE to the central FE
+be_added=false
+for i in {1..10}; do
+  echo "add Doris BE to FE for the $i times"
+
+  mysql -h doris-fe -P9030 -uroot -e "ALTER SYSTEM ADD BACKEND 
'${CONTAINER_IP}:9050'"
+  if [ $? -ne 0 ]; then
+    echo "Failed to add the BE to the FE"
+  else
+    be_added=true
+    echo "Doris BE added to FE successfully"
+    break
+  fi
+
+  sleep 1
+done
+
+if [ "$be_added" = false ]; then
+  echo "ERROR: Doris BE failed to add to FE"
+fi
+

Review Comment:
   If registering the BE with the FE fails (`be_added` is false), the script 
only prints an error and then continues to keep the container alive. Please 
`exit 1` here as well; otherwise the compose environment may look "up" while 
the cluster can never become ready.



##########
integration-test-common/doris-docker-script/docker-compose.yaml:
##########
@@ -0,0 +1,62 @@
+#
+# 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.
+
+services:
+
+  doris-fe:
+    image: ${GRAVITINO_CI_DORIS_DOCKER_IMAGE} 
+    hostname: doris-fe

Review Comment:
   The compose file uses `image: ${GRAVITINO_CI_DORIS_DOCKER_IMAGE}` without a 
default. If the env var is missing/empty, `docker-compose` will fail with an 
invalid image reference. Consider adding a default value in the YAML (e.g., 
`${GRAVITINO_CI_DORIS_DOCKER_IMAGE:-apache/doris:2.1.7}`) to match the PR 
description and make local runs more reliable.



##########
integration-test-common/doris-docker-script/docker-compose.yaml:
##########
@@ -0,0 +1,62 @@
+#
+# 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.
+
+services:
+
+  doris-fe:
+    image: ${GRAVITINO_CI_DORIS_DOCKER_IMAGE} 
+    hostname: doris-fe
+    networks:
+      - doris-net
+    volumes:
+      - ./start-fe.sh:/opt/apache-doris/start-fe.sh
+    command: ["bash", "start-fe.sh"]
+    ports:
+      - "8030:8030"
+      - "9030:9030"

Review Comment:
   Binding FE ports to fixed host ports (`8030:8030`, `9030:9030`) makes the IT 
environment fragile (fails if those ports are already in use, and prevents 
parallel test execution on the same host). Consider removing the `ports:` 
section and relying on Testcontainers' `withExposedService(...)` port mapping, 
then use `composeContainer.getServicePort(...)` when constructing JDBC/HTTP 
URLs.
   ```suggestion
   
   ```



##########
integration-test-common/doris-docker-script/start-fe.sh:
##########
@@ -0,0 +1,59 @@
+#!/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.
+#
+
+# FE-only startup script for docker-compose multi-container deployment.
+# This overrides the image's default start.sh which starts both FE and BE.
+
+DORIS_HOME="$(dirname "${BASH_SOURCE-$0}")"
+DORIS_HOME="$(cd "${DORIS_HOME}" >/dev/null; pwd)"
+DORIS_FE_HOME="${DORIS_HOME}/fe/"
+
+# Configure priority_networks based on container IP
+CONTAINER_IP=$(hostname -i)
+PRIORITY_NETWORKS=$(echo "${CONTAINER_IP}" | awk -F '.' 
'{print$1"."$2"."$3".0/24"}')
+echo "add priority_networks = ${PRIORITY_NETWORKS} to fe.conf"
+echo "priority_networks = ${PRIORITY_NETWORKS}" >> 
${DORIS_FE_HOME}/conf/fe.conf
+
+# Start only FE in daemon mode
+${DORIS_FE_HOME}/bin/start_fe.sh --daemon
+
+# Wait for FE to be ready
+fe_started=false
+for i in {1..20}; do
+  sleep 5
+  url="http://localhost:8030/api/bootstrap";
+  echo "check fe for the $i times"
+  response=$(curl --silent --request GET $url)
+
+  if echo "$response" | grep -q '"msg":"success"'; then
+    fe_started=true
+    echo "Doris FE started successfully, response: $response"
+    break
+  else
+    echo "check failed, response: $response"
+  fi
+done
+
+if [ "$fe_started" = false ]; then
+  echo "ERROR: Doris FE failed to start"

Review Comment:
   If FE fails to start, the script only prints an error and then continues to 
keep the container alive, which can hide failures and slow down debugging. 
Please `exit 1` (non-zero) when `fe_started` is false so 
docker-compose/Testcontainers fail fast.
   ```suggestion
     echo "ERROR: Doris FE failed to start"
     exit 1
   ```



-- 
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]

Reply via email to