Copilot commented on code in PR #7882:
URL: https://github.com/apache/incubator-seata/pull/7882#discussion_r2642577052


##########
namingserver/src/main/java/org/apache/seata/namingserver/manager/ClusterWatcherManager.java:
##########
@@ -113,6 +128,9 @@ public void registryWatcher(Watcher<?> watcher) {
         if (term == null || watcher.getTerm() >= term) {
             WATCHERS.computeIfAbsent(group, value -> new 
ConcurrentLinkedQueue<>())
                     .add(watcher);
+
+            // Immediately refresh watcher count metrics
+            metricsManager.refreshWatcherCountMetrics();

Review Comment:
   The metrics are being refreshed on every watcher registration, which could 
happen very frequently in a high-traffic scenario. Calling 
refreshWatcherCountMetrics on every single registration could create 
unnecessary overhead. Consider batching these updates or using a scheduled 
refresh instead of immediate refresh on every operation.



##########
namingserver/src/main/java/org/apache/seata/namingserver/metrics/NamingServerTagsContributor.java:
##########
@@ -0,0 +1,64 @@
+/*
+ * 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 org.apache.seata.namingserver.metrics;
+
+import io.micrometer.common.KeyValue;
+import io.micrometer.common.KeyValues;
+import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
+import 
org.springframework.http.server.observation.DefaultServerRequestObservationConvention;
+import 
org.springframework.http.server.observation.ServerRequestObservationContext;
+import org.springframework.stereotype.Component;
+
+/**
+ * Custom tags contributor for HTTP server request metrics.
+ * Adds Seata business dimensions (namespace, cluster, vgroup) to
+ * http.server.requests metrics.
+ */
+@Component
+@ConditionalOnProperty(name = "seata.namingserver.metrics.enabled", 
havingValue = "true")
+public class NamingServerTagsContributor extends 
DefaultServerRequestObservationConvention {
+
+    private static final String TAG_NAMESPACE = "namespace";
+    private static final String TAG_CLUSTER = "cluster";
+    private static final String TAG_VGROUP = "vgroup";
+    private static final String UNKNOWN = "unknown";
+
+    @Override
+    public KeyValues 
getLowCardinalityKeyValues(ServerRequestObservationContext context) {
+        KeyValues keyValues = super.getLowCardinalityKeyValues(context);
+
+        // Add namespace tag
+        String namespace = context.getCarrier().getParameter(TAG_NAMESPACE);
+        keyValues = keyValues.and(KeyValue.of(TAG_NAMESPACE, namespace != null 
? namespace : UNKNOWN));
+
+        // Add cluster tag
+        String cluster = context.getCarrier().getParameter(TAG_CLUSTER);
+        if (cluster == null) {
+            cluster = context.getCarrier().getParameter("clusterName");
+        }
+        keyValues = keyValues.and(KeyValue.of(TAG_CLUSTER, cluster != null ? 
cluster : UNKNOWN));
+
+        // Add vgroup tag
+        String vgroup = context.getCarrier().getParameter(TAG_VGROUP);
+        if (vgroup == null) {
+            vgroup = context.getCarrier().getParameter("group");
+        }
+        keyValues = keyValues.and(KeyValue.of(TAG_VGROUP, vgroup != null ? 
vgroup : UNKNOWN));

Review Comment:
   Adding tags with value "unknown" for every HTTP request will create high 
cardinality metrics. When these parameters are not present in requests, it 
would be better to either not add the tag at all, or use a constant shared 
"unknown" value. This could lead to performance issues and excessive memory 
usage in the metrics registry as each unique combination of tag values creates 
a separate time series. Consider only adding tags when they have meaningful 
values.



##########
namingserver/src/main/java/org/apache/seata/namingserver/manager/NamingManager.java:
##########
@@ -346,6 +355,9 @@ public boolean registerInstance(NamingServerNode node, 
String namespace, String
                             node.getTransaction().getHost(),
                             node.getTransaction().getPort()),
                     System.currentTimeMillis());
+
+            // Immediately refresh cluster node count metrics
+            metricsManager.refreshClusterNodeCountMetrics();

Review Comment:
   The metrics are being refreshed on every instance registration and 
unregistration. In high-traffic scenarios with frequent 
registrations/unregistrations, this could create significant overhead. Consider 
batching these updates or using a scheduled refresh strategy instead of 
immediately refreshing on every operation.



##########
namingserver/src/main/java/org/apache/seata/namingserver/manager/NamingManager.java:
##########
@@ -378,6 +390,9 @@ public boolean unregisterInstance(String namespace, String 
clusterName, String u
                             node.getTransaction().getPort()));
                 }
             }
+
+            // Immediately refresh cluster node count metrics
+            metricsManager.refreshClusterNodeCountMetrics();

Review Comment:
   The metrics are being refreshed on every instance registration and 
unregistration. In high-traffic scenarios with frequent 
registrations/unregistrations, this could create significant overhead. Consider 
batching these updates or using a scheduled refresh strategy instead of 
immediately refreshing on every operation.



##########
namingserver/src/main/resources/application.yml:
##########
@@ -33,9 +33,25 @@ heartbeat:
   threshold: 90000
   period: 60000
 seata:
+  namingserver:
+    metrics:
+      enabled: true
   security:
     secretKey: SeataSecretKey0c382ef121d778043159209298fd40bf3850a017
     tokenValidityInMilliseconds: 1800000
     csrf-ignore-urls: /naming/v1/**,/api/v1/naming/**
     ignore:
       urls: 
/,/**/*.css,/**/*.js,/**/*.html,/**/*.map,/**/*.svg,/**/*.png,/**/*.jpeg,/**/*.ico,/api/v1/auth/login,/version.json,/naming/v1/health,/error
+management:
+  endpoints:
+    web:
+      exposure:
+        include: "prometheus,health,info,metrics"
+  metrics:
+    tags:
+      application: { spring.application.name }

Review Comment:
   The syntax used here is incorrect for YAML. The correct syntax to reference 
another property value in Spring Boot YAML should use `${}` without curly 
braces around the reference, like this: `application: 
${spring.application.name}`
   ```suggestion
         application: ${spring.application.name}
   ```



##########
namingserver/src/test/resources/application.yml:
##########
@@ -29,8 +29,24 @@ heartbeat:
   threshold: 5000
   period: 5000
 seata:
+  namingserver:
+    metrics:
+      enabled: true
   security:
     secretKey: SeataSecretKey0c382ef121d778043159209298fd40bf3850a017
     tokenValidityInMilliseconds: 1800000
     ignore:
       urls: 
/,/**/*.css,/**/*.js,/**/*.html,/**/*.map,/**/*.svg,/**/*.png,/**/*.jpeg,/**/*.ico,/api/v1/auth/login,/version.json,/health,/error,/naming/v1/**
+management:
+  endpoints:
+    web:
+      exposure:
+        include: "prometheus,health,info,metrics"
+  metrics:
+    tags:
+      application: { spring.application.name }

Review Comment:
   The syntax used here is incorrect for YAML. The correct syntax to reference 
another property value in Spring Boot YAML should use `${}` without curly 
braces around the reference, like this: `application: 
${spring.application.name}`
   ```suggestion
         application: ${spring.application.name}
   ```



##########
namingserver/src/main/java/org/apache/seata/namingserver/manager/NamingManager.java:
##########
@@ -77,21 +78,23 @@ public class NamingManager {
     private static final Logger LOGGER = 
LoggerFactory.getLogger(NamingManager.class);
     private final ConcurrentMap<InetSocketAddress, Long> instanceLiveTable;
     private volatile LoadingCache<String /* VGroup */, ConcurrentMap<String /* 
namespace */, NamespaceBO>> vGroupMap;
-    private final ConcurrentMap<String /* namespace */, ConcurrentMap<String 
/* clusterName */, ClusterData>>
-            namespaceClusterDataMap;
+    private final ConcurrentMap<String /* namespace */, ConcurrentMap<String 
/* clusterName */, ClusterData>> namespaceClusterDataMap;
 
     @Value("${heartbeat.threshold:90000}")
     private int heartbeatTimeThreshold;
 
     @Value("${heartbeat.period:60000}")
     private int heartbeatCheckTimePeriod;
 
-    protected final ScheduledExecutorService heartBeatCheckService =
-            new ScheduledThreadPoolExecutor(1, new 
CustomizableThreadFactory("heartBeatCheckExcuter"));
+    protected final ScheduledExecutorService heartBeatCheckService = new 
ScheduledThreadPoolExecutor(1,
+            new CustomizableThreadFactory("heartBeatCheckExcuter"));

Review Comment:
   The thread factory name contains a typo: "heartBeatCheckExcuter" should be 
spelled "heartBeatCheckExecutor".
   ```suggestion
               new CustomizableThreadFactory("heartBeatCheckExecutor"));
   ```



##########
namingserver/src/main/java/org/apache/seata/namingserver/metrics/NamingServerTagsContributor.java:
##########
@@ -0,0 +1,64 @@
+/*
+ * 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 org.apache.seata.namingserver.metrics;
+
+import io.micrometer.common.KeyValue;
+import io.micrometer.common.KeyValues;
+import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
+import 
org.springframework.http.server.observation.DefaultServerRequestObservationConvention;
+import 
org.springframework.http.server.observation.ServerRequestObservationContext;
+import org.springframework.stereotype.Component;
+
+/**
+ * Custom tags contributor for HTTP server request metrics.
+ * Adds Seata business dimensions (namespace, cluster, vgroup) to
+ * http.server.requests metrics.
+ */
+@Component
+@ConditionalOnProperty(name = "seata.namingserver.metrics.enabled", 
havingValue = "true")
+public class NamingServerTagsContributor extends 
DefaultServerRequestObservationConvention {
+
+    private static final String TAG_NAMESPACE = "namespace";
+    private static final String TAG_CLUSTER = "cluster";
+    private static final String TAG_VGROUP = "vgroup";
+    private static final String UNKNOWN = "unknown";
+
+    @Override
+    public KeyValues 
getLowCardinalityKeyValues(ServerRequestObservationContext context) {
+        KeyValues keyValues = super.getLowCardinalityKeyValues(context);
+
+        // Add namespace tag
+        String namespace = context.getCarrier().getParameter(TAG_NAMESPACE);
+        keyValues = keyValues.and(KeyValue.of(TAG_NAMESPACE, namespace != null 
? namespace : UNKNOWN));
+
+        // Add cluster tag
+        String cluster = context.getCarrier().getParameter(TAG_CLUSTER);
+        if (cluster == null) {
+            cluster = context.getCarrier().getParameter("clusterName");
+        }
+        keyValues = keyValues.and(KeyValue.of(TAG_CLUSTER, cluster != null ? 
cluster : UNKNOWN));
+
+        // Add vgroup tag
+        String vgroup = context.getCarrier().getParameter(TAG_VGROUP);
+        if (vgroup == null) {
+            vgroup = context.getCarrier().getParameter("group");
+        }
+        keyValues = keyValues.and(KeyValue.of(TAG_VGROUP, vgroup != null ? 
vgroup : UNKNOWN));
+
+        return keyValues;
+    }
+}

Review Comment:
   The NamingServerTagsContributor class lacks test coverage. This component 
extends DefaultServerRequestObservationConvention and adds custom tag logic 
that should be tested to ensure tags are correctly extracted from request 
parameters and handled when missing.



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