Copilot commented on code in PR #6250:
URL: https://github.com/apache/shenyu/pull/6250#discussion_r2588036690


##########
shenyu-infra/shenyu-infra-etcd/src/main/java/org/apache/shenyu/infra/etcd/client/EtcdClient.java:
##########
@@ -375,22 +363,58 @@ public GetResponse getRange(final String key, final 
GetOption getOption) {
 
     /**
      * put data as ephemeral.
-     * @param key key
+     * 
+     * @param key   key
      * @param value value
      */
     public void putEphemeral(final String key, final String value) {
         try {
             KV kvClient = client.getKVClient();
             kvClient.put(ByteSequence.from(key, UTF_8), 
ByteSequence.from(value, UTF_8),
-                            
PutOption.newBuilder().withLeaseId(globalLeaseId).build())
+                    PutOption.newBuilder().withLeaseId(globalLeaseId).build())
                     .get(timeout, TimeUnit.MILLISECONDS);
         } catch (InterruptedException | ExecutionException | TimeoutException 
e) {
             LOG.error("putEphemeral(key:{},value:{}) error.", key, value, e);
+            throw new ShenyuException(e);
         }
     }
 
+    private void initLease() {
+        try {
+            this.globalLeaseId = 
client.getLeaseClient().grant(ttl).get().getID();
+            keepAlive();
+        } catch (InterruptedException | ExecutionException e) {
+            LOG.error("initLease error.", e);
+            throw new ShenyuException(e);
+        }
+    }
+
+    private void keepAlive() {
+        client.getLeaseClient().keepAlive(globalLeaseId, new 
StreamObserver<>() {
+            @Override
+            public void onNext(final LeaseKeepAliveResponse 
leaseKeepAliveResponse) {
+            }
+
+            @Override
+            public void onError(final Throwable throwable) {
+                LOG.error("keep alive error", throwable);
+                try {
+                    TimeUnit.SECONDS.sleep(1);
+                } catch (InterruptedException e) {
+                    LOG.error("keep alive sleep error", e);
+                }
+                keepAlive();
+            }
+
+            @Override
+            public void onCompleted() {
+            }
+        });
+    }

Review Comment:
   The recursive call to `keepAlive()` in the `onError` handler can lead to a 
stack overflow if errors occur continuously. The 1-second sleep doesn't prevent 
rapid recursive calls from exhausting the stack. Consider using an iterative 
approach with a background thread or implementing a circuit breaker pattern to 
limit retries.



##########
shenyu-infra/shenyu-infra-redis/src/main/java/org/apache/shenyu/infra/redis/RedisConnectionFactory.java:
##########
@@ -126,10 +135,27 @@ private List<RedisNode> createRedisNode(final String url) 
{
         List<RedisNode> redisNodes = new ArrayList<>();
         List<String> nodes = Lists.newArrayList(Splitter.on(";").split(url));
         for (String node : nodes) {
-            String[] parts = StringUtils.split(node, ":");
-            Assert.state(Objects.requireNonNull(parts).length == 2, "Must be 
defined as 'host:port'");
-            redisNodes.add(new RedisNode(parts[0], 
Integer.parseInt(parts[1])));
+            redisNodes.add(parseRedisNode(node));
         }
         return redisNodes;
     }
+
+    private RedisNode parseRedisNode(final String url) {
+        String host = url;
+        int port = 6379;
+        int lastColonIndex = url.lastIndexOf(":");
+        int bracketIndex = url.lastIndexOf("]");
+        if (bracketIndex > -1) {
+            if (lastColonIndex > bracketIndex) {
+                host = url.substring(0, lastColonIndex);
+                port = Integer.parseInt(url.substring(lastColonIndex + 1));
+            }
+        } else {
+            if (lastColonIndex > -1) {
+                host = url.substring(0, lastColonIndex);
+                port = Integer.parseInt(url.substring(lastColonIndex + 1));
+            }
+        }

Review Comment:
   The `parseRedisNode` method lacks error handling for malformed URLs. If 
`Integer.parseInt()` fails (e.g., with a non-numeric port like 
"localhost:abc"), it will throw a `NumberFormatException` which is not handled. 
Additionally, there's no validation that the host is not empty. Consider adding 
proper error handling and validation, or wrapping exceptions in a more 
informative exception type.
   ```suggestion
                   try {
                       port = Integer.parseInt(url.substring(lastColonIndex + 
1));
                   } catch (NumberFormatException e) {
                       throw new IllegalArgumentException("Invalid port in 
Redis node URL: " + url, e);
                   }
               }
           } else {
               if (lastColonIndex > -1) {
                   host = url.substring(0, lastColonIndex);
                   try {
                       port = Integer.parseInt(url.substring(lastColonIndex + 
1));
                   } catch (NumberFormatException e) {
                       throw new IllegalArgumentException("Invalid port in 
Redis node URL: " + url, e);
                   }
               }
           }
           if (host == null || host.trim().isEmpty()) {
               throw new IllegalArgumentException("Host is empty in Redis node 
URL: " + url);
           }
           if (port < 1 || port > 65535) {
               throw new IllegalArgumentException("Port out of range in Redis 
node URL: " + url);
           }
   ```



##########
shenyu-infra/shenyu-infra-redis/src/main/java/org/apache/shenyu/infra/redis/RedisConnectionFactory.java:
##########
@@ -89,12 +92,18 @@ private GenericObjectPoolConfig<?> getPoolConfig(final 
RedisConfigProperties red
      * @param redisConfigProperties the rate limiter config
      * @return the redis standalone configuration
      */
-    protected final RedisStandaloneConfiguration 
redisStandaloneConfiguration(final RedisConfigProperties redisConfigProperties) 
{
+    /**
+     * Redis standalone configuration redis standalone configuration.
+     *
+     * @param redisConfigProperties the rate limiter config
+     * @return the redis standalone configuration
+     */

Review Comment:
   Duplicate JavaDoc comments detected. There are two identical JavaDoc blocks 
for the same method `redisStandaloneConfiguration`. The first block (lines 
89-94) should be removed, keeping only the second one (lines 95-100).
   ```suggestion
   
   ```



##########
shenyu-infra/shenyu-infra-redis/src/main/java/org/apache/shenyu/infra/redis/RedisConnectionFactory.java:
##########
@@ -126,10 +135,27 @@ private List<RedisNode> createRedisNode(final String url) 
{
         List<RedisNode> redisNodes = new ArrayList<>();
         List<String> nodes = Lists.newArrayList(Splitter.on(";").split(url));
         for (String node : nodes) {
-            String[] parts = StringUtils.split(node, ":");
-            Assert.state(Objects.requireNonNull(parts).length == 2, "Must be 
defined as 'host:port'");
-            redisNodes.add(new RedisNode(parts[0], 
Integer.parseInt(parts[1])));
+            redisNodes.add(parseRedisNode(node));
         }
         return redisNodes;
     }
+
+    private RedisNode parseRedisNode(final String url) {
+        String host = url;
+        int port = 6379;
+        int lastColonIndex = url.lastIndexOf(":");
+        int bracketIndex = url.lastIndexOf("]");
+        if (bracketIndex > -1) {
+            if (lastColonIndex > bracketIndex) {
+                host = url.substring(0, lastColonIndex);
+                port = Integer.parseInt(url.substring(lastColonIndex + 1));
+            }
+        } else {
+            if (lastColonIndex > -1) {
+                host = url.substring(0, lastColonIndex);
+                port = Integer.parseInt(url.substring(lastColonIndex + 1));
+            }
+        }
+        return new RedisNode(host, port);
+    }

Review Comment:
   The new `parseRedisNode` method lacks test coverage. Consider adding tests 
for various scenarios including: IPv4 addresses with ports (e.g., 
"localhost:6379"), IPv6 addresses with ports (e.g., "[::1]:6379"), IPv6 
addresses without ports (e.g., "[::1]"), addresses without ports using the 
default 6379, and error cases like malformed URLs or invalid port numbers.



##########
shenyu-infra/shenyu-infra-redis/src/main/java/org/apache/shenyu/infra/redis/RedisConnectionFactory.java:
##########
@@ -126,10 +135,27 @@ private List<RedisNode> createRedisNode(final String url) 
{
         List<RedisNode> redisNodes = new ArrayList<>();
         List<String> nodes = Lists.newArrayList(Splitter.on(";").split(url));
         for (String node : nodes) {
-            String[] parts = StringUtils.split(node, ":");
-            Assert.state(Objects.requireNonNull(parts).length == 2, "Must be 
defined as 'host:port'");
-            redisNodes.add(new RedisNode(parts[0], 
Integer.parseInt(parts[1])));
+            redisNodes.add(parseRedisNode(node));
         }
         return redisNodes;
     }
+
+    private RedisNode parseRedisNode(final String url) {
+        String host = url;
+        int port = 6379;
+        int lastColonIndex = url.lastIndexOf(":");
+        int bracketIndex = url.lastIndexOf("]");
+        if (bracketIndex > -1) {
+            if (lastColonIndex > bracketIndex) {
+                host = url.substring(0, lastColonIndex);
+                port = Integer.parseInt(url.substring(lastColonIndex + 1));
+            }
+        } else {
+            if (lastColonIndex > -1) {
+                host = url.substring(0, lastColonIndex);
+                port = Integer.parseInt(url.substring(lastColonIndex + 1));

Review Comment:
   Potential uncaught 'java.lang.NumberFormatException'.
   ```suggestion
                   try {
                       port = Integer.parseInt(url.substring(lastColonIndex + 
1));
                   } catch (NumberFormatException e) {
                       port = 6379; // fallback to default port
                   }
               }
           } else {
               if (lastColonIndex > -1) {
                   host = url.substring(0, lastColonIndex);
                   try {
                       port = Integer.parseInt(url.substring(lastColonIndex + 
1));
                   } catch (NumberFormatException e) {
                       port = 6379; // fallback to default port
                   }
   ```



##########
shenyu-infra/shenyu-infra-etcd/src/main/java/org/apache/shenyu/infra/etcd/client/EtcdClient.java:
##########
@@ -375,22 +363,58 @@ public GetResponse getRange(final String key, final 
GetOption getOption) {
 
     /**
      * put data as ephemeral.
-     * @param key key
+     * 
+     * @param key   key
      * @param value value
      */
     public void putEphemeral(final String key, final String value) {
         try {
             KV kvClient = client.getKVClient();
             kvClient.put(ByteSequence.from(key, UTF_8), 
ByteSequence.from(value, UTF_8),
-                            
PutOption.newBuilder().withLeaseId(globalLeaseId).build())
+                    PutOption.newBuilder().withLeaseId(globalLeaseId).build())
                     .get(timeout, TimeUnit.MILLISECONDS);
         } catch (InterruptedException | ExecutionException | TimeoutException 
e) {
             LOG.error("putEphemeral(key:{},value:{}) error.", key, value, e);
+            throw new ShenyuException(e);
         }
     }
 
+    private void initLease() {
+        try {
+            this.globalLeaseId = 
client.getLeaseClient().grant(ttl).get().getID();
+            keepAlive();
+        } catch (InterruptedException | ExecutionException e) {
+            LOG.error("initLease error.", e);
+            throw new ShenyuException(e);
+        }
+    }
+
+    private void keepAlive() {
+        client.getLeaseClient().keepAlive(globalLeaseId, new 
StreamObserver<>() {
+            @Override
+            public void onNext(final LeaseKeepAliveResponse 
leaseKeepAliveResponse) {
+            }
+
+            @Override
+            public void onError(final Throwable throwable) {
+                LOG.error("keep alive error", throwable);
+                try {
+                    TimeUnit.SECONDS.sleep(1);
+                } catch (InterruptedException e) {
+                    LOG.error("keep alive sleep error", e);
+                }
+                keepAlive();
+            }
+
+            @Override
+            public void onCompleted() {
+            }
+        });
+    }

Review Comment:
   The refactored `keepAlive` method and its recursive error handling logic 
lack test coverage. The existing test in `EtcdClientTest` only covers the basic 
`onError` callback but doesn't verify the new retry behavior with sleep and 
recursive call. Consider adding tests to verify the error recovery logic.



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