This is an automated email from the ASF dual-hosted git repository.

wusheng pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/skywalking.git


The following commit(s) were added to refs/heads/master by this push:
     new 37b4896d47 Fix and optimize AI endpoint grouping related 
functionalities (#11003)
37b4896d47 is described below

commit 37b4896d47d90de6c6021949929465380ffa3444
Author: Superskyyy <[email protected]>
AuthorDate: Tue Jun 27 18:02:38 2023 -0400

    Fix and optimize AI endpoint grouping related functionalities (#11003)
---
 docs/en/changes/changes.md                         |   1 +
 .../services/HttpUriRecognitionService.java        |  32 ++++--
 .../group/uri/RegexVSQuickMatchBenchmark.java      | 119 +++++++++++----------
 .../core/config/group/EndpointNameGrouping.java    |  13 +--
 .../openapi/EndpointGroupingRule4Openapi.java      |   2 +-
 .../config/group/uri/quickmatch/PatternTree.java   |  75 ++++++++++---
 .../config/group/uri/quickmatch/StringToken.java   |   7 +-
 .../core/config/group/uri/quickmatch/VarToken.java |   7 +-
 .../group/EndpointGroupingRuleReaderTest.java      |   6 ++
 .../group/uri/quickmatch/PatternTreeTest.java      |  64 +++++++++--
 .../oap/server/library/util/StringFormatGroup.java |   2 +-
 11 files changed, 223 insertions(+), 105 deletions(-)

diff --git a/docs/en/changes/changes.md b/docs/en/changes/changes.md
index 06824dc7b2..0bc4a5fad2 100644
--- a/docs/en/changes/changes.md
+++ b/docs/en/changes/changes.md
@@ -26,6 +26,7 @@
 * Add component ID for Aerospike(ID=149).
 * Packages with name `recevier` are renamed to `receiver`.
 * `BanyanDBMetricsDAO` handles `storeIDTag` in `multiGet` for 
`BanyanDBModelExtension`.
+* Fix endpoint grouping-related logic and enhance the performance of 
PatternTree retrieval. 
 
 #### UI
 
diff --git 
a/oap-server/ai-pipeline/src/main/java/org/apache/skywalking/oap/server/ai/pipeline/services/HttpUriRecognitionService.java
 
b/oap-server/ai-pipeline/src/main/java/org/apache/skywalking/oap/server/ai/pipeline/services/HttpUriRecognitionService.java
index 51a59821d7..e4e37289e2 100644
--- 
a/oap-server/ai-pipeline/src/main/java/org/apache/skywalking/oap/server/ai/pipeline/services/HttpUriRecognitionService.java
+++ 
b/oap-server/ai-pipeline/src/main/java/org/apache/skywalking/oap/server/ai/pipeline/services/HttpUriRecognitionService.java
@@ -68,14 +68,19 @@ public class HttpUriRecognitionService implements 
HttpUriRecognition {
                                                        .build()
                       );
             final String newVersion = httpUriRecognitionResponse.getVersion();
+            if (log.isDebugEnabled()) {
+                log.debug("Remote response version: {}, local version {}", 
newVersion, version);
+            }
             if (version.equals(newVersion)) {
-                // Same version, nothing changed.
                 return null;
+            } else {
+                version = newVersion;
             }
+
             return httpUriRecognitionResponse.getPatternsList()
-                                             .stream()
-                                             .map(pattern -> new 
HttpUriPattern(pattern.getPattern()))
-                                             .collect(Collectors.toList());
+                    .stream()
+                    .map(pattern -> new HttpUriPattern(pattern.getPattern()))
+                    .collect(Collectors.toList());
 
         } catch (Exception e) {
             log.error("fetch all patterns failed from remote server.", e);
@@ -91,15 +96,20 @@ public class HttpUriRecognitionService implements 
HttpUriRecognition {
             }
             final HttpUriRecognitionRequest.Builder builder = 
HttpUriRecognitionRequest.newBuilder();
             builder.setService(service);
-            unrecognizedURIs.forEach(httpUri -> {
-                builder.getUnrecognizedUrisBuilderList().add(
-                    HttpRawUri.newBuilder().setName(httpUri.getName())
-                );
-            });
+            if (log.isDebugEnabled()) {
+                log.debug("Feeding to remote, service: {}, uri size: {}", 
service, unrecognizedURIs.size());
+            }
+            List<HttpRawUri> rawUriList = unrecognizedURIs.stream()
+                    .map(httpUri -> HttpRawUri.newBuilder()
+                            .setName(httpUri.getName())
+                            .build())
+                    .collect(Collectors.toList());
+
+            builder.addAllUnrecognizedUris(rawUriList);
             stub.withDeadlineAfter(30, TimeUnit.SECONDS)
-                .feedRawData(builder.build());
+                    .feedRawData(builder.build());
         } catch (Exception e) {
-            log.error("feed matched and unmatched URIs to the remote server.", 
e);
+            log.error("Failed to feed matched and unmatched URIs to the remote 
server.", e);
         }
     }
 }
diff --git 
a/oap-server/microbench/src/main/java/org/apache/skywalking/oap/server/microbench/core/config/group/uri/RegexVSQuickMatchBenchmark.java
 
b/oap-server/microbench/src/main/java/org/apache/skywalking/oap/server/microbench/core/config/group/uri/RegexVSQuickMatchBenchmark.java
index bc3761a175..60e676783e 100644
--- 
a/oap-server/microbench/src/main/java/org/apache/skywalking/oap/server/microbench/core/config/group/uri/RegexVSQuickMatchBenchmark.java
+++ 
b/oap-server/microbench/src/main/java/org/apache/skywalking/oap/server/microbench/core/config/group/uri/RegexVSQuickMatchBenchmark.java
@@ -126,69 +126,70 @@ public class RegexVSQuickMatchBenchmark extends 
AbstractMicrobenchmark {
 
 /**
  * # JMH version: 1.25
- * # VM version: JDK 11.0.18, OpenJDK 64-Bit Server VM, 11.0.18+10
- * # VM invoker: 
/Users/wusheng/Library/Java/JavaVirtualMachines/temurin-11.0.18/Contents/Home/bin/java
- * # VM options: -ea --add-opens=java.base/java.lang=ALL-UNNAMED 
--add-opens=java.base/java.lang=ALL-UNNAMED 
-Didea.test.cyclic.buffer.size=1048576 -javaagent:/Applications/IntelliJ 
IDEA.app/Contents/lib/idea_rt.jar=53714:/Applications/IntelliJ 
IDEA.app/Contents/bin -Dfile.encoding=UTF-8 -Xmx512m -Xms512m 
-XX:MaxDirectMemorySize=512m -XX:BiasedLockingStartupDelay=0 
-Djmh.executor=CUSTOM 
-Djmh.executor.class=org.apache.skywalking.oap.server.microbench.base.AbstractMicrobenchmark$JmhThread
 [...]
+ * # VM version: JDK 16.0.1, OpenJDK 64-Bit Server VM, 16.0.1+9-24
+ * # VM invoker: C:\Users\Sky\.jdks\openjdk-16.0.1\bin\java.exe
+ * # VM options: -ea --add-opens=java.base/java.lang=ALL-UNNAMED 
--add-opens=java.base/java.lang=ALL-UNNAMED 
-Didea.test.cyclic.buffer.size=1048576 
-javaagent:Y:\jetbrains\apps\IDEA-U\ch-0\231.8109.175\lib\idea_rt.jar=54938:Y:\jetbrains\apps\IDEA-U\ch-0\231.8109.175\bin
 -Dfile.encoding=UTF-8 -Xmx512m -Xms512m -XX:MaxDirectMemorySize=512m 
-XX:BiasedLockingStartupDelay=0 -Djmh.executor=CUSTOM 
-Djmh.executor.class=org.apache.skywalking.oap.server.microbench.base.AbstractMicrobenchmark$JmhTh
 [...]
  * # Warmup: 1 iterations, 10 s each
  * # Measurement: 1 iterations, 10 s each
  * # Timeout: 10 min per iteration
  * # Threads: 4 threads, will synchronize iterations
  * # Benchmark mode: Throughput, ops/time
- * # Benchmark: 
org.apache.skywalking.oap.server.microbench.core.config.group.uri.RegexVSQuickMatchBenchmark.matchFirstQuickUriGrouping
+ * # Benchmark: 
org.apache.skywalking.oap.server.microbench.core.config.group.uri.RegexVSQuickMatchBenchmark.notMatchRegex
+ * Benchmark                                                                   
              Mode  Cnt         Score   Error   Units
+ * RegexVSQuickMatchBenchmark.matchFirstQuickUriGrouping                       
             thrpt       48317763.786           ops/s
+ * RegexVSQuickMatchBenchmark.matchFirstQuickUriGrouping:·gc.alloc.rate        
             thrpt           8773.225          MB/sec
+ * RegexVSQuickMatchBenchmark.matchFirstQuickUriGrouping:·gc.alloc.rate.norm   
             thrpt            200.014            B/op
+ * 
RegexVSQuickMatchBenchmark.matchFirstQuickUriGrouping:·gc.churn.G1_Eden_Space   
         thrpt           8807.405          MB/sec
+ * 
RegexVSQuickMatchBenchmark.matchFirstQuickUriGrouping:·gc.churn.G1_Eden_Space.norm
       thrpt            200.794            B/op
+ * 
RegexVSQuickMatchBenchmark.matchFirstQuickUriGrouping:·gc.churn.G1_Survivor_Space
        thrpt              0.050          MB/sec
+ * 
RegexVSQuickMatchBenchmark.matchFirstQuickUriGrouping:·gc.churn.G1_Survivor_Space.norm
   thrpt              0.001            B/op
+ * RegexVSQuickMatchBenchmark.matchFirstQuickUriGrouping:·gc.count             
             thrpt            303.000          counts
+ * RegexVSQuickMatchBenchmark.matchFirstQuickUriGrouping:·gc.time              
             thrpt            325.000              ms
+ * RegexVSQuickMatchBenchmark.matchFirstRegex                                  
             thrpt       41040542.288           ops/s
+ * RegexVSQuickMatchBenchmark.matchFirstRegex:·gc.alloc.rate                   
             thrpt           8348.690          MB/sec
+ * RegexVSQuickMatchBenchmark.matchFirstRegex:·gc.alloc.rate.norm              
             thrpt            224.016            B/op
+ * RegexVSQuickMatchBenchmark.matchFirstRegex:·gc.churn.G1_Eden_Space          
             thrpt           8378.454          MB/sec
+ * RegexVSQuickMatchBenchmark.matchFirstRegex:·gc.churn.G1_Eden_Space.norm     
             thrpt            224.815            B/op
+ * RegexVSQuickMatchBenchmark.matchFirstRegex:·gc.churn.G1_Survivor_Space      
             thrpt              0.057          MB/sec
+ * RegexVSQuickMatchBenchmark.matchFirstRegex:·gc.churn.G1_Survivor_Space.norm 
             thrpt              0.002            B/op
+ * RegexVSQuickMatchBenchmark.matchFirstRegex:·gc.count                        
             thrpt            288.000          counts
+ * RegexVSQuickMatchBenchmark.matchFirstRegex:·gc.time                         
             thrpt            282.000              ms
+ * RegexVSQuickMatchBenchmark.matchFourthQuickUriGrouping                      
             thrpt       35658131.267           ops/s
+ * RegexVSQuickMatchBenchmark.matchFourthQuickUriGrouping:·gc.alloc.rate       
             thrpt           8020.546          MB/sec
+ * RegexVSQuickMatchBenchmark.matchFourthQuickUriGrouping:·gc.alloc.rate.norm  
             thrpt            248.018            B/op
+ * 
RegexVSQuickMatchBenchmark.matchFourthQuickUriGrouping:·gc.churn.G1_Eden_Space  
         thrpt           8043.279          MB/sec
+ * 
RegexVSQuickMatchBenchmark.matchFourthQuickUriGrouping:·gc.churn.G1_Eden_Space.norm
      thrpt            248.721            B/op
+ * 
RegexVSQuickMatchBenchmark.matchFourthQuickUriGrouping:·gc.churn.G1_Survivor_Space
       thrpt              0.045          MB/sec
+ * 
RegexVSQuickMatchBenchmark.matchFourthQuickUriGrouping:·gc.churn.G1_Survivor_Space.norm
  thrpt              0.001            B/op
+ * RegexVSQuickMatchBenchmark.matchFourthQuickUriGrouping:·gc.count            
             thrpt            277.000          counts
+ * RegexVSQuickMatchBenchmark.matchFourthQuickUriGrouping:·gc.time             
             thrpt            302.000              ms
+ * RegexVSQuickMatchBenchmark.matchFourthRegex                                 
             thrpt       11066068.208           ops/s
+ * RegexVSQuickMatchBenchmark.matchFourthRegex:·gc.alloc.rate                  
             thrpt           8273.312          MB/sec
+ * RegexVSQuickMatchBenchmark.matchFourthRegex:·gc.alloc.rate.norm             
             thrpt            824.060            B/op
+ * RegexVSQuickMatchBenchmark.matchFourthRegex:·gc.churn.G1_Eden_Space         
             thrpt           8279.984          MB/sec
+ * RegexVSQuickMatchBenchmark.matchFourthRegex:·gc.churn.G1_Eden_Space.norm    
             thrpt            824.724            B/op
+ * RegexVSQuickMatchBenchmark.matchFourthRegex:·gc.churn.G1_Survivor_Space     
             thrpt              0.052          MB/sec
+ * 
RegexVSQuickMatchBenchmark.matchFourthRegex:·gc.churn.G1_Survivor_Space.norm    
         thrpt              0.005            B/op
+ * RegexVSQuickMatchBenchmark.matchFourthRegex:·gc.count                       
             thrpt            285.000          counts
+ * RegexVSQuickMatchBenchmark.matchFourthRegex:·gc.time                        
             thrpt            324.000              ms
+ * RegexVSQuickMatchBenchmark.notMatchQuickUriGrouping                         
             thrpt       45843193.472           ops/s
+ * RegexVSQuickMatchBenchmark.notMatchQuickUriGrouping:·gc.alloc.rate          
             thrpt           8653.215          MB/sec
+ * RegexVSQuickMatchBenchmark.notMatchQuickUriGrouping:·gc.alloc.rate.norm     
             thrpt            208.015            B/op
+ * RegexVSQuickMatchBenchmark.notMatchQuickUriGrouping:·gc.churn.G1_Eden_Space 
             thrpt           8652.365          MB/sec
+ * 
RegexVSQuickMatchBenchmark.notMatchQuickUriGrouping:·gc.churn.G1_Eden_Space.norm
         thrpt            207.995            B/op
+ * 
RegexVSQuickMatchBenchmark.notMatchQuickUriGrouping:·gc.churn.G1_Survivor_Space 
         thrpt              0.048          MB/sec
+ * 
RegexVSQuickMatchBenchmark.notMatchQuickUriGrouping:·gc.churn.G1_Survivor_Space.norm
     thrpt              0.001            B/op
+ * RegexVSQuickMatchBenchmark.notMatchQuickUriGrouping:·gc.count               
             thrpt            298.000          counts
+ * RegexVSQuickMatchBenchmark.notMatchQuickUriGrouping:·gc.time                
             thrpt            358.000              ms
+ * RegexVSQuickMatchBenchmark.notMatchRegex                                    
             thrpt        3434953.426           ops/s
+ * RegexVSQuickMatchBenchmark.notMatchRegex:·gc.alloc.rate                     
             thrpt           8898.075          MB/sec
+ * RegexVSQuickMatchBenchmark.notMatchRegex:·gc.alloc.rate.norm                
             thrpt           2856.206            B/op
+ * RegexVSQuickMatchBenchmark.notMatchRegex:·gc.churn.G1_Eden_Space            
             thrpt           8886.568          MB/sec
+ * RegexVSQuickMatchBenchmark.notMatchRegex:·gc.churn.G1_Eden_Space.norm       
             thrpt           2852.512            B/op
+ * RegexVSQuickMatchBenchmark.notMatchRegex:·gc.churn.G1_Survivor_Space        
             thrpt              0.052          MB/sec
+ * RegexVSQuickMatchBenchmark.notMatchRegex:·gc.churn.G1_Survivor_Space.norm   
             thrpt              0.017            B/op
+ * RegexVSQuickMatchBenchmark.notMatchRegex:·gc.count                          
             thrpt            306.000          counts
+ * RegexVSQuickMatchBenchmark.notMatchRegex:·gc.time                           
             thrpt            377.000              ms
  *
- Benchmark                                                                     
        Mode  Cnt         Score   Error   Units
- RegexVSQuickMatchBenchmark.matchFirstQuickUriGrouping                         
       thrpt       28464926.797           ops/s
- RegexVSQuickMatchBenchmark.matchFirstQuickUriGrouping:·gc.alloc.rate          
       thrpt           6194.492          MB/sec
- RegexVSQuickMatchBenchmark.matchFirstQuickUriGrouping:·gc.alloc.rate.norm     
       thrpt            240.000            B/op
- RegexVSQuickMatchBenchmark.matchFirstQuickUriGrouping:·gc.churn.G1_Eden_Space 
       thrpt           6222.267          MB/sec
- 
RegexVSQuickMatchBenchmark.matchFirstQuickUriGrouping:·gc.churn.G1_Eden_Space.norm
   thrpt            241.076            B/op
- RegexVSQuickMatchBenchmark.matchFirstQuickUriGrouping:·gc.churn.G1_Old_Gen    
       thrpt              0.023          MB/sec
- 
RegexVSQuickMatchBenchmark.matchFirstQuickUriGrouping:·gc.churn.G1_Old_Gen.norm 
     thrpt              0.001            B/op
- RegexVSQuickMatchBenchmark.matchFirstQuickUriGrouping:·gc.count               
       thrpt            214.000          counts
- RegexVSQuickMatchBenchmark.matchFirstQuickUriGrouping:·gc.time                
       thrpt            194.000              ms
- RegexVSQuickMatchBenchmark.matchFirstRegex                                    
       thrpt       51679120.204           ops/s
- RegexVSQuickMatchBenchmark.matchFirstRegex:·gc.alloc.rate                     
       thrpt           7130.116          MB/sec
- RegexVSQuickMatchBenchmark.matchFirstRegex:·gc.alloc.rate.norm                
       thrpt            152.000            B/op
- RegexVSQuickMatchBenchmark.matchFirstRegex:·gc.churn.G1_Eden_Space            
       thrpt           7162.842          MB/sec
- RegexVSQuickMatchBenchmark.matchFirstRegex:·gc.churn.G1_Eden_Space.norm       
       thrpt            152.698            B/op
- RegexVSQuickMatchBenchmark.matchFirstRegex:·gc.churn.G1_Old_Gen               
       thrpt              0.020          MB/sec
- RegexVSQuickMatchBenchmark.matchFirstRegex:·gc.churn.G1_Old_Gen.norm          
       thrpt             ≈ 10⁻³            B/op
- RegexVSQuickMatchBenchmark.matchFirstRegex:·gc.count                          
       thrpt            246.000          counts
- RegexVSQuickMatchBenchmark.matchFirstRegex:·gc.time                           
       thrpt            224.000              ms
- RegexVSQuickMatchBenchmark.matchFourthQuickUriGrouping                        
       thrpt       23359343.934           ops/s
- RegexVSQuickMatchBenchmark.matchFourthQuickUriGrouping:·gc.alloc.rate         
       thrpt           6106.164          MB/sec
- RegexVSQuickMatchBenchmark.matchFourthQuickUriGrouping:·gc.alloc.rate.norm    
       thrpt            288.000            B/op
- 
RegexVSQuickMatchBenchmark.matchFourthQuickUriGrouping:·gc.churn.G1_Eden_Space  
     thrpt           6143.526          MB/sec
- 
RegexVSQuickMatchBenchmark.matchFourthQuickUriGrouping:·gc.churn.G1_Eden_Space.norm
  thrpt            289.762            B/op
- RegexVSQuickMatchBenchmark.matchFourthQuickUriGrouping:·gc.churn.G1_Old_Gen   
       thrpt              0.023          MB/sec
- 
RegexVSQuickMatchBenchmark.matchFourthQuickUriGrouping:·gc.churn.G1_Old_Gen.norm
     thrpt              0.001            B/op
- RegexVSQuickMatchBenchmark.matchFourthQuickUriGrouping:·gc.count              
       thrpt            211.000          counts
- RegexVSQuickMatchBenchmark.matchFourthQuickUriGrouping:·gc.time               
       thrpt            143.000              ms
- RegexVSQuickMatchBenchmark.matchFourthRegex                                   
       thrpt       24074353.094           ops/s
- RegexVSQuickMatchBenchmark.matchFourthRegex:·gc.alloc.rate                    
       thrpt          17999.991          MB/sec
- RegexVSQuickMatchBenchmark.matchFourthRegex:·gc.alloc.rate.norm               
       thrpt            824.000            B/op
- RegexVSQuickMatchBenchmark.matchFourthRegex:·gc.churn.G1_Eden_Space           
       thrpt          18070.905          MB/sec
- RegexVSQuickMatchBenchmark.matchFourthRegex:·gc.churn.G1_Eden_Space.norm      
       thrpt            827.246            B/op
- RegexVSQuickMatchBenchmark.matchFourthRegex:·gc.churn.G1_Old_Gen              
       thrpt              0.095          MB/sec
- RegexVSQuickMatchBenchmark.matchFourthRegex:·gc.churn.G1_Old_Gen.norm         
       thrpt              0.004            B/op
- RegexVSQuickMatchBenchmark.matchFourthRegex:·gc.count                         
       thrpt            621.000          counts
- RegexVSQuickMatchBenchmark.matchFourthRegex:·gc.time                          
       thrpt            934.000              ms
- RegexVSQuickMatchBenchmark.notMatchQuickUriGrouping                           
       thrpt       27031477.704           ops/s
- RegexVSQuickMatchBenchmark.notMatchQuickUriGrouping:·gc.alloc.rate            
       thrpt           6081.482          MB/sec
- RegexVSQuickMatchBenchmark.notMatchQuickUriGrouping:·gc.alloc.rate.norm       
       thrpt            248.000            B/op
- RegexVSQuickMatchBenchmark.notMatchQuickUriGrouping:·gc.churn.G1_Eden_Space   
       thrpt           6109.321          MB/sec
- 
RegexVSQuickMatchBenchmark.notMatchQuickUriGrouping:·gc.churn.G1_Eden_Space.norm
     thrpt            249.135            B/op
- RegexVSQuickMatchBenchmark.notMatchQuickUriGrouping:·gc.churn.G1_Old_Gen      
       thrpt              0.022          MB/sec
- RegexVSQuickMatchBenchmark.notMatchQuickUriGrouping:·gc.churn.G1_Old_Gen.norm 
       thrpt              0.001            B/op
- RegexVSQuickMatchBenchmark.notMatchQuickUriGrouping:·gc.count                 
       thrpt            210.000          counts
- RegexVSQuickMatchBenchmark.notMatchQuickUriGrouping:·gc.time                  
       thrpt            171.000              ms
- RegexVSQuickMatchBenchmark.notMatchRegex                                      
       thrpt        9368757.119           ops/s
- RegexVSQuickMatchBenchmark.notMatchRegex:·gc.alloc.rate                       
       thrpt          23999.619          MB/sec
- RegexVSQuickMatchBenchmark.notMatchRegex:·gc.alloc.rate.norm                  
       thrpt           2824.000            B/op
- RegexVSQuickMatchBenchmark.notMatchRegex:·gc.churn.G1_Eden_Space              
       thrpt          24087.019          MB/sec
- RegexVSQuickMatchBenchmark.notMatchRegex:·gc.churn.G1_Eden_Space.norm         
       thrpt           2834.284            B/op
- RegexVSQuickMatchBenchmark.notMatchRegex:·gc.churn.G1_Old_Gen                 
       thrpt              0.114          MB/sec
- RegexVSQuickMatchBenchmark.notMatchRegex:·gc.churn.G1_Old_Gen.norm            
       thrpt              0.013            B/op
- RegexVSQuickMatchBenchmark.notMatchRegex:·gc.count                            
       thrpt            828.000          counts
- RegexVSQuickMatchBenchmark.notMatchRegex:·gc.time                             
       thrpt            896.000              ms
+ * Process finished with exit code 0
  */
\ No newline at end of file
diff --git 
a/oap-server/server-core/src/main/java/org/apache/skywalking/oap/server/core/config/group/EndpointNameGrouping.java
 
b/oap-server/server-core/src/main/java/org/apache/skywalking/oap/server/core/config/group/EndpointNameGrouping.java
index c122db7fae..6d9a850895 100644
--- 
a/oap-server/server-core/src/main/java/org/apache/skywalking/oap/server/core/config/group/EndpointNameGrouping.java
+++ 
b/oap-server/server-core/src/main/java/org/apache/skywalking/oap/server/core/config/group/EndpointNameGrouping.java
@@ -97,7 +97,7 @@ public class EndpointNameGrouping {
             if (svrHttpUris.size() < maxHttpUrisNumberPerService) {
                 if (formattedName._2()) {
                     // Algorithm side should not return a pattern that has no 
{var} in it else this
-                    // code may accidentally retreive the size 1 queue created 
by unformatted endpoint
+                    // code may accidentally retrieve the size 1 queue created 
by unformatted endpoint
                     // The queue size is 10, which means only cache the first 
10 formatted names.
                     final ArrayBlockingQueue<String> formattedURIs = 
svrHttpUris.computeIfAbsent(
                         formattedName._1(), k -> new ArrayBlockingQueue<>(10));
@@ -140,7 +140,7 @@ public class EndpointNameGrouping {
                 log.trace("Endpoint {} of Service {} keeps unchanged.", 
endpointName, serviceName);
             }
         }
-        return new Tuple2<>(formatResult.getName(), formatResult.isMatch());
+        return new Tuple2<>(formatResult.getReplacedName(), 
formatResult.isMatch());
     }
 
     private Tuple2<String, Boolean> formatByQuickUriPattern(String 
serviceName, String endpointName) {
@@ -155,7 +155,7 @@ public class EndpointNameGrouping {
                 log.trace("Endpoint {} of Service {} keeps unchanged.", 
endpointName, serviceName);
             }
         }
-        return new Tuple2<>(formatResult.getName(), formatResult.isMatch());
+        return new Tuple2<>(formatResult.getReplacedName(), 
formatResult.isMatch());
     }
 
     public void startHttpUriRecognitionSvr(final HttpUriRecognition 
httpUriRecognitionSvr,
@@ -172,7 +172,8 @@ public class EndpointNameGrouping {
                  .scheduleWithFixedDelay(
                      new RunnableWithExceptionProtection(
                          () -> {
-                             if (aiPipelineExecutionCounter.incrementAndGet() 
% trainingPeriodHttpUriRecognitionPattern == 0) {
+                             int currentExecutionCounter = 
aiPipelineExecutionCounter.incrementAndGet();
+                             if (currentExecutionCounter % 
trainingPeriodHttpUriRecognitionPattern == 0) {
                                  // Send the cached URIs to the recognition 
server to build new patterns.
                                  cachedHttpUris.forEach((serviceName, 
httpUris) -> {
                                      final List<HttpUriRecognition.HTTPUri> 
candidates4UriPatterns = new ArrayList<>(
@@ -182,7 +183,7 @@ public class EndpointNameGrouping {
                                              //unrecognized uri
                                              candidates4UriPatterns.add(new 
HttpUriRecognition.HTTPUri(uri));
                                          } else {
-                                             String candidateUri = null;
+                                             String candidateUri;
                                              while ((candidateUri = 
candidates.poll()) != null) {
                                                  candidates4UriPatterns.add(
                                                      new 
HttpUriRecognition.HTTPUri(candidateUri));
@@ -195,7 +196,7 @@ public class EndpointNameGrouping {
                                      
httpUriRecognitionSvr.feedRawData(serviceName, candidates4UriPatterns);
                                  });
                              }
-                             if (aiPipelineExecutionCounter.incrementAndGet() 
% syncPeriodHttpUriRecognitionPattern == 0) {
+                             if (currentExecutionCounter % 
syncPeriodHttpUriRecognitionPattern == 0) {
                                  // Sync with the recognition server per 1 min 
to get the latest patterns.
                                  try {
                                      metadataQueryService.listServices(null, 
null).forEach(
diff --git 
a/oap-server/server-core/src/main/java/org/apache/skywalking/oap/server/core/config/group/openapi/EndpointGroupingRule4Openapi.java
 
b/oap-server/server-core/src/main/java/org/apache/skywalking/oap/server/core/config/group/openapi/EndpointGroupingRule4Openapi.java
index f81cf8ddf9..4fa1b09cba 100644
--- 
a/oap-server/server-core/src/main/java/org/apache/skywalking/oap/server/core/config/group/openapi/EndpointGroupingRule4Openapi.java
+++ 
b/oap-server/server-core/src/main/java/org/apache/skywalking/oap/server/core/config/group/openapi/EndpointGroupingRule4Openapi.java
@@ -44,7 +44,7 @@ public class EndpointGroupingRule4Openapi {
     public StringFormatGroup.FormatResult format(String service, String 
endpointName) {
         Map<String, String> endpointNameLookup = directLookup.get(service);
         if (endpointNameLookup != null && endpointNameLookup.get(endpointName) 
!= null) {
-            return new StringFormatGroup.FormatResult(true, 
endpointNameLookup.get(endpointName), endpointName);
+            return new StringFormatGroup.FormatResult(true, endpointName, 
endpointNameLookup.get(endpointName));
         }
 
         Map<String, StringFormatGroup> rules = groupedRules.get(service);
diff --git 
a/oap-server/server-core/src/main/java/org/apache/skywalking/oap/server/core/config/group/uri/quickmatch/PatternTree.java
 
b/oap-server/server-core/src/main/java/org/apache/skywalking/oap/server/core/config/group/uri/quickmatch/PatternTree.java
index 129e703166..1c3527f6d3 100644
--- 
a/oap-server/server-core/src/main/java/org/apache/skywalking/oap/server/core/config/group/uri/quickmatch/PatternTree.java
+++ 
b/oap-server/server-core/src/main/java/org/apache/skywalking/oap/server/core/config/group/uri/quickmatch/PatternTree.java
@@ -40,28 +40,28 @@ public class PatternTree {
      * @param pattern of URIs
      */
     public void addPattern(String pattern) {
-        final String[] tokens = pattern.split("/");
+        final List<String> tokens = splitByCharacter(pattern);
 
         PatternToken current = null;
         for (final PatternToken patternToken : roots) {
-            if (patternToken.isMatch(tokens[0])) {
+            if (patternToken.isMatch(tokens.get(0))) {
                 current = patternToken;
                 break;
             }
         }
 
         if (current == null) {
-            current = new StringToken(tokens[0]);
+            current = new StringToken(tokens.get(0));
             roots.add(current);
         }
 
-        if (tokens.length == 1) {
+        if (tokens.size() == 1) {
             current.setExpression(pattern);
             return;
         }
 
-        for (int i = 1; i < tokens.length; i++) {
-            final String token = tokens[i];
+        for (int i = 1; i < tokens.size(); i++) {
+            final String token = tokens.get(i);
             PatternToken newToken;
             if (VarToken.VAR_TOKEN.equals(token)) {
                 newToken = new VarToken();
@@ -78,19 +78,24 @@ public class PatternTree {
         current.setExpression(pattern);
     }
 
-    List<String> splitByCharacter(String input, char delimiter) {
+    List<String> splitByCharacter(String input) {
         List<String> parts = new ArrayList<>();
+        int length = input.length();
         int start = 0;
 
-        for (int i = 0; i < input.length(); i++) {
-            if (input.charAt(i) == delimiter) {
+        for (int i = 0; i < length; i++) {
+            if (input.charAt(i) == '/') {
+                if (i == 0) {
+                    start = i + 1;
+                    continue;
+                }
                 parts.add(input.substring(start, i));
                 start = i + 1;
             }
         }
 
         // Add the last part if necessary
-        if (start < input.length()) {
+        if (start < length) {
             parts.add(input.substring(start));
         }
 
@@ -98,7 +103,13 @@ public class PatternTree {
     }
 
     public StringFormatGroup.FormatResult match(String uri) {
-        final List<String> slices = splitByCharacter(uri, '/');
+        final List<String> slices = splitByCharacter(uri);
+        if (slices.size() == 1) {
+            // Special case handling, since if a URI is just length one
+            // itself will never be a variable, so simply return true and 
itself
+            // trailing slashes, if ever encountered will be kept as is
+            return new StringFormatGroup.FormatResult(true, uri, uri);
+        }
         List<PatternToken> current = roots;
         PatternToken matchedToken = null;
         for (final String slice : slices) {
@@ -111,14 +122,52 @@ public class PatternTree {
                 }
             }
             if (!matched) {
-                return new StringFormatGroup.FormatResult(false, uri, null);
+                return new StringFormatGroup.FormatResult(false, uri, uri);
             }
             current = matchedToken.children();
         }
         if (matchedToken.isLeaf()) {
             return new StringFormatGroup.FormatResult(true, uri, 
matchedToken.expression());
         } else {
-            return new StringFormatGroup.FormatResult(false, uri, null);
+            return new StringFormatGroup.FormatResult(false, uri, uri);
+        }
+    }
+
+    @SuppressWarnings("unused")
+    // Utility method to visualize the full tree for debugging purposes
+    public String printTree() {
+        StringBuilder sb = new StringBuilder();
+        for (PatternToken root : roots) {
+            sb.append(printNode(root, 0));
         }
+        return sb.toString();
     }
+
+    private String printNode(PatternToken node, int depth) {
+        StringBuilder sb = new StringBuilder();
+
+        sb.append("  ".repeat(Math.max(0, depth)));
+
+        sb.append(node.toString()).append("\n");
+
+        // Append expression if not null
+        if (node.expression() != null) {
+            sb.append("  ").append(node.expression()).append("\n");
+        }
+
+        if (node instanceof StringToken) {
+            StringToken stringToken = (StringToken) node;
+            for (PatternToken child : stringToken.children()) {
+                sb.append(printNode(child, depth + 1));
+            }
+        } else if (node instanceof VarToken) {
+            VarToken varToken = (VarToken) node;
+            for (PatternToken child : varToken.children()) {
+                sb.append(printNode(child, depth + 1));
+            }
+        }
+
+        return sb.toString();
+    }
+
 }
diff --git 
a/oap-server/server-core/src/main/java/org/apache/skywalking/oap/server/core/config/group/uri/quickmatch/StringToken.java
 
b/oap-server/server-core/src/main/java/org/apache/skywalking/oap/server/core/config/group/uri/quickmatch/StringToken.java
index 350502ae68..bce401b0ec 100644
--- 
a/oap-server/server-core/src/main/java/org/apache/skywalking/oap/server/core/config/group/uri/quickmatch/StringToken.java
+++ 
b/oap-server/server-core/src/main/java/org/apache/skywalking/oap/server/core/config/group/uri/quickmatch/StringToken.java
@@ -22,10 +22,8 @@ import java.util.ArrayList;
 import java.util.List;
 import lombok.EqualsAndHashCode;
 import lombok.Setter;
-import lombok.ToString;
 
 @EqualsAndHashCode(of = "value")
-@ToString
 public class StringToken implements PatternToken {
     private final String value;
     private final List<PatternToken> children;
@@ -56,4 +54,9 @@ public class StringToken implements PatternToken {
     public List<PatternToken> children() {
         return children;
     }
+
+    @Override
+    public String toString() {
+        return "StringToken: \"" + value + "\"";
+    }
 }
diff --git 
a/oap-server/server-core/src/main/java/org/apache/skywalking/oap/server/core/config/group/uri/quickmatch/VarToken.java
 
b/oap-server/server-core/src/main/java/org/apache/skywalking/oap/server/core/config/group/uri/quickmatch/VarToken.java
index c668fd5ae9..4991af930f 100644
--- 
a/oap-server/server-core/src/main/java/org/apache/skywalking/oap/server/core/config/group/uri/quickmatch/VarToken.java
+++ 
b/oap-server/server-core/src/main/java/org/apache/skywalking/oap/server/core/config/group/uri/quickmatch/VarToken.java
@@ -22,10 +22,8 @@ import java.util.ArrayList;
 import java.util.List;
 import lombok.EqualsAndHashCode;
 import lombok.Setter;
-import lombok.ToString;
 
 @EqualsAndHashCode(of = "")
-@ToString
 public class VarToken implements PatternToken {
     public static final String VAR_TOKEN = "{var}";
     private List<PatternToken> children = new ArrayList<>();
@@ -54,4 +52,9 @@ public class VarToken implements PatternToken {
     public List<PatternToken> children() {
         return children;
     }
+
+    @Override
+    public String toString() {
+        return "VarToken: \"" + VAR_TOKEN + "\"";
+    }
 }
diff --git 
a/oap-server/server-core/src/test/java/org/apache/skywalking/oap/server/core/config/group/EndpointGroupingRuleReaderTest.java
 
b/oap-server/server-core/src/test/java/org/apache/skywalking/oap/server/core/config/group/EndpointGroupingRuleReaderTest.java
index d07abfe702..3d89d6180c 100644
--- 
a/oap-server/server-core/src/test/java/org/apache/skywalking/oap/server/core/config/group/EndpointGroupingRuleReaderTest.java
+++ 
b/oap-server/server-core/src/test/java/org/apache/skywalking/oap/server/core/config/group/EndpointGroupingRuleReaderTest.java
@@ -36,8 +36,14 @@ public class EndpointGroupingRuleReaderTest {
         Assertions.assertTrue(formatResult.isMatch());
         Assertions.assertEquals("/prod/{var}", formatResult.getReplacedName());
 
+        // This will always match, since after slicing length is 1, which goes 
into special handling
         formatResult = rule.format("serviceA", "/prod/");
+        Assertions.assertTrue(formatResult.isMatch());
+        Assertions.assertEquals("/prod/", formatResult.getReplacedName());
+
+        formatResult = rule.format("serviceA", "/prod/123/456");
         Assertions.assertFalse(formatResult.isMatch());
+        Assertions.assertEquals("/prod/123/456", 
formatResult.getReplacedName());
 
         formatResult = rule.format("serviceB", "/prod/123");
         Assertions.assertFalse(formatResult.isMatch());
diff --git 
a/oap-server/server-core/src/test/java/org/apache/skywalking/oap/server/core/config/group/uri/quickmatch/PatternTreeTest.java
 
b/oap-server/server-core/src/test/java/org/apache/skywalking/oap/server/core/config/group/uri/quickmatch/PatternTreeTest.java
index 469741b040..c07f30c229 100644
--- 
a/oap-server/server-core/src/test/java/org/apache/skywalking/oap/server/core/config/group/uri/quickmatch/PatternTreeTest.java
+++ 
b/oap-server/server-core/src/test/java/org/apache/skywalking/oap/server/core/config/group/uri/quickmatch/PatternTreeTest.java
@@ -30,31 +30,49 @@ class PatternTreeTest {
         PatternTree tree = new PatternTree();
         tree.addPattern("/products/{var}");
         tree.addPattern("/products/{var}/detail");
+        tree.addPattern("/products/{var}/refund");
+        tree.addPattern("/products/{var}/reorder/extra");
         tree.addPattern("/sales/{var}");
         tree.addPattern("/employees/{var}/profile");
+        // This should map to exact same tree nodes
+        tree.addPattern("produces/{var}/profile");
+        tree.addPattern("GET:/posts/{var}");
+        tree.addPattern("https://abc.com/posts/{var}";);
 
         final Field rootField = PatternTree.class.getDeclaredField("roots");
         rootField.setAccessible(true);
         final List<PatternToken> roots = (List<PatternToken>) 
rootField.get(tree);
-        final PatternToken root = roots.get(0);
 
-        Assertions.assertEquals(new StringToken(""), root);
-        Assertions.assertEquals(3, root.children().size());
-        final PatternToken prodToken = root.children().get(0);
+        final PatternToken prodToken = roots.get(0);
         Assertions.assertEquals(new StringToken("products"), prodToken);
         Assertions.assertEquals(1, prodToken.children().size());
-        final PatternToken prodVarToken = prodToken.children().get(0);
-        Assertions.assertEquals(new VarToken(), prodVarToken);
-        final PatternToken detailToken = prodVarToken.children().get(0);
+        final PatternToken varToken = prodToken.children().get(0);
+        Assertions.assertEquals(new VarToken(), varToken);
+        Assertions.assertEquals(3, varToken.children().size());
+        final PatternToken detailToken = varToken.children().get(0);
         Assertions.assertEquals(new StringToken("detail"), detailToken);
 
-        final PatternToken salesToken = root.children().get(1);
+        final PatternToken salesToken = roots.get(1);
         Assertions.assertEquals(new StringToken("sales"), salesToken);
         Assertions.assertEquals(1, salesToken.children().size());
 
-        final PatternToken employeeToken = root.children().get(2);
+        final PatternToken employeeToken = roots.get(2);
         Assertions.assertEquals(new StringToken("employees"), employeeToken);
         Assertions.assertEquals(1, employeeToken.children().size());
+
+        final PatternToken producesToken = roots.get(3);
+        Assertions.assertEquals(new StringToken("produces"), producesToken);
+        Assertions.assertEquals(1, producesToken.children().size());
+
+        final PatternToken getPostsToken = roots.get(4);
+        Assertions.assertEquals(new StringToken("GET:"), getPostsToken);
+
+        final PatternToken abcToken = roots.get(5);
+        Assertions.assertEquals(new StringToken("https:"), abcToken);
+        final PatternToken abcComToken = abcToken.children().get(0);
+        // For general performance purposes, double / will result in an empty 
string token
+        // This is considered an intentional feature rather than a bug
+        Assertions.assertEquals(new StringToken(""), abcComToken);
     }
 
     @Test
@@ -62,8 +80,13 @@ class PatternTreeTest {
         PatternTree tree = new PatternTree();
         tree.addPattern("/products/{var}");
         tree.addPattern("/products/{var}/detail");
+        tree.addPattern("/products/{var}/refund");
+        tree.addPattern("/products/{var}/reorder/extra");
         tree.addPattern("/sales/{var}");
         tree.addPattern("/employees/{var}/profile");
+        tree.addPattern("produces/{var}/profile");
+        tree.addPattern("GET:/posts/{var}");
+        tree.addPattern("https://abc.com/posts/{var}";);
 
         StringFormatGroup.FormatResult result;
         result = tree.match("/products/123");
@@ -77,13 +100,34 @@ class PatternTreeTest {
         result = tree.match("/employees/skywalking/profile");
         Assertions.assertTrue(result.isMatch());
 
-        // URI doesn't have / as prefix
+        // URI doesn't have / as prefix but should still match
         result = tree.match("products/123/detail");
+        Assertions.assertTrue(result.isMatch());
+
+        // URI has / as suffix but should still match
+        result = tree.match("products/123/detail/");
+        Assertions.assertTrue(result.isMatch());
+
+        // URI shorter than pattern
+        result = tree.match("/products/123/reorder");
         Assertions.assertFalse(result.isMatch());
+        Assertions.assertEquals("/products/123/reorder", 
result.getReplacedName());
 
         // URI has extra suffix
         result = tree.match("/products/123/detail/extra");
         Assertions.assertFalse(result.isMatch());
+        Assertions.assertEquals("/products/123/detail/extra", 
result.getReplacedName());
+
+        // Domain style URI
+        result = tree.match("https://abc.com/posts/123abc";);
+        Assertions.assertTrue(result.isMatch());
+
+        // Special case: When endpoint is like /abc or abc,
+        // it will always match since itself cannot be a variable
+        // regardless if abc is actually in the pattern tree
+        result = tree.match("/abc");
+        Assertions.assertTrue(result.isMatch());
+        Assertions.assertEquals("/abc", result.getReplacedName());
     }
 
     @Test
diff --git 
a/oap-server/server-library/library-util/src/main/java/org/apache/skywalking/oap/server/library/util/StringFormatGroup.java
 
b/oap-server/server-library/library-util/src/main/java/org/apache/skywalking/oap/server/library/util/StringFormatGroup.java
index 285ac872ca..c1efc6c67d 100644
--- 
a/oap-server/server-library/library-util/src/main/java/org/apache/skywalking/oap/server/library/util/StringFormatGroup.java
+++ 
b/oap-server/server-library/library-util/src/main/java/org/apache/skywalking/oap/server/library/util/StringFormatGroup.java
@@ -68,7 +68,7 @@ public class StringFormatGroup {
     public FormatResult format(String string) {
         for (PatternRule rule : rules) {
             if (rule.getPattern().matcher(string).matches()) {
-                return new FormatResult(true, rule.getName(), string);
+                return new FormatResult(true, string, rule.getName());
             }
         }
         return new FormatResult(false, string, string);


Reply via email to