Repository: apache/skywalking (language: python)

Issue:
[Feature] Support to query relation metrics through PromQL
### Search before asking

- [X] I had searched in the [issues](https://github.com/apache/skywalking/issues?q=is%3Aissue) and found no similar feature requirement.


### Description

[PromQL Service](https://skywalking.apache.org/docs/main/next/en/api/promql-service/#supported-metrics-scopehttpsgithubcomapacheskywalkingtree19a2395a9ebbf7dca1bcf7aefba0c1819aad7214oap-serverserver-coresrcmainjavaorgapacheskywalkingoapservercorequeryenumerationscopejavacatalog) doesn't support metrics of relation between services, instances or endpoints.

### Use case

User could use this to build dashboards to show metrics from the lines on the topology or manually click.

### Related issues

_No response_

### Are you willing to submit a pull request to implement this on your own?

- [ ] Yes I am willing to submit a pull request on my own!

### Code of Conduct

- [X] I agree to follow this project's [Code of Conduct](https://www.apache.org/foundation/policies/conduct)



Discussion / hints:
@CzyerChen @weixiang1862 @yswdqz Any of you has interest in implementing this?
I am new to this repo - would like to pick this up. 
Can you add some more verbose details of what is required as part of the feature?
> I am new to this repo - would like to pick this up. 
> 
> Can you add some more verbose details of what is required as part of the feature?

This is not easy. Do you read most of SkyWalking codebases?
I will have to do that. I am brand-new to this repo. 

I would say, you could consider to implement this. But don't be in urgent.

This requires you to have good understanding of the whole system.
I would like to take a shot at this. I will go through the docs and readme. Is there anything else you suggest me to do? Or a similar MR for a different Query language?
You could do as following
1. Get familiar with project, and how to run showcase. https://skywalking.apache.org/docs/skywalking-showcase/next/readme/
2. Learn MQE about the protocol and native UI for relative metrics.
3. Learn PromQL service, and workflow.
4. Try to implement this.
Okay, let me do as you recommend.
@wu-sheng I only have 16 GB RAM in my mac, I am not able to run showcase:
`Release "demo" does not exist. Installing it now.`
It's stuck on this step with RAM Utilization spiking up.

Is there another alternative?
You need to run necessary components only. Don't run the default. It includes all features.
@yswdqz told me, he will work on this from June 2nd/3th.

Fix patch:
diff --git a/docs/en/api/promql-service.md b/docs/en/api/promql-service.md
index a610a9ddaa42..b9497f5bcc2f 100644
--- a/docs/en/api/promql-service.md
+++ b/docs/en/api/promql-service.md
@@ -389,8 +389,9 @@ Not all scopes are supported for now, please check the following table:
 | Service                 | yes     |
 | ServiceInstance         | yes     |
 | Endpoint                | yes     |
-| ServiceRelation         | no      |
-| ServiceInstanceRelation | no      |
+| ServiceRelation         | yes     |
+| ServiceInstanceRelation | yes     |
+| EndpointRelation        | yes     |
 | Process                 | no      |
 | ProcessRelation         | no      |
 
@@ -398,11 +399,14 @@ Not all scopes are supported for now, please check the following table:
 Each metric contains general labels: [layer](../../../oap-server/server-core/src/main/java/org/apache/skywalking/oap/server/core/analysis/Layer.java).
 Different metrics will have different labels depending on their Scope and metric value type.
 
-| Query Labels                     | Scope           | Expression Example                                                                             |
-|----------------------------------|-----------------|------------------------------------------------------------------------------------------------|
-| layer, service                   | Service         | service_cpm{service='$service', layer='$layer'}                                                |
-| layer, service, service_instance | ServiceInstance | service_instance_cpm{service='$service', service_instance='$service_instance', layer='$layer'} |
-| layer, service, endpoint         | Endpoint        | endpoint_cpm{service='$service', endpoint='$endpoint', layer='$layer'}                         |
+| Query Labels                                                                      | Scope                   | Expression Example                                                                                                                                                                                                 |
+|-----------------------------------------------------------------------------------|-------------------------|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
+| layer, service                                                                    | Service                 | service_cpm{service='$service', layer='$layer'}                                                                                                                                                                    |
+| layer, service, service_instance                                                  | ServiceInstance         | service_instance_cpm{service='$service', service_instance='$service_instance', layer='$layer'}                                                                                                                     |
+| layer, service, endpoint                                                          | Endpoint                | endpoint_cpm{service='$service', endpoint='$endpoint', layer='$layer'}                                                                                                                                             |
+| layer, service, dest_service, dest_layer                                          | ServiceRelation         | service_relation_metric{service='$service', layer='$layer', dest_layer='$dest_layer', dest_service='$dest_service'}                                                                                                |
+| layer, service, dest_service, dest_layer, service_instance, dest_service_instance | ServiceInstanceRelation | service_instance_relation_metric{service='$service', layer='$layer', dest_layer='$dest_layer', dest_service='$dest_service', dest_service_instance='$dest_service_instance', service_instance='$service_instance'} |
+| layer, service, dest_service, dest_layer, endpoint, dest_endpoint                 | EndpointRelation        | endpoint_relation_metric{service='$service', endpoint='$endpoint', layer='$layer', dest_layer='$dest_layer', dest_service='$dest_service', dest_endpoint='$dest_endpoint'}                                         |
 
 
 
diff --git a/docs/en/changes/changes.md b/docs/en/changes/changes.md
index fca04eede65e..694d0f89b518 100644
--- a/docs/en/changes/changes.md
+++ b/docs/en/changes/changes.md
@@ -10,6 +10,7 @@
 * Add Golang as a supported language for Elasticsearch.
 * Remove unnecessary BanyanDB flushing logs(info).
 * Increase `SW_CORE_GRPC_MAX_MESSAGE_SIZE` to 50MB.
+* Support to query relation metrics through PromQL.
 
 #### UI
 
diff --git a/oap-server/server-query-plugin/promql-plugin/src/main/java/org/apache/skywalking/oap/query/promql/entity/LabelName.java b/oap-server/server-query-plugin/promql-plugin/src/main/java/org/apache/skywalking/oap/query/promql/entity/LabelName.java
index 63f8f86d5cc9..a891dec70405 100644
--- a/oap-server/server-query-plugin/promql-plugin/src/main/java/org/apache/skywalking/oap/query/promql/entity/LabelName.java
+++ b/oap-server/server-query-plugin/promql-plugin/src/main/java/org/apache/skywalking/oap/query/promql/entity/LabelName.java
@@ -44,7 +44,14 @@ public enum LabelName {
     RECORD("record"),
     //For endpoint_traffic
     LIMIT("limit"),
-    KEYWORD("keyword");
+    KEYWORD("keyword"),
+    //for service_relation_traffic
+    DEST_SERVICE("dest_service"),
+    DEST_LAYER("dest_layer"),
+    //for service_instance_relation_traffic
+    DEST_SERVICE_INSTANCE("dest_service_instance"),
+    //for endpoint_relation
+    DEST_ENDPOINT("dest_endpoint");
 
     final String label;
 
diff --git a/oap-server/server-query-plugin/promql-plugin/src/main/java/org/apache/skywalking/oap/query/promql/rt/PromQLExprQueryVisitor.java b/oap-server/server-query-plugin/promql-plugin/src/main/java/org/apache/skywalking/oap/query/promql/rt/PromQLExprQueryVisitor.java
index c9fba0cbd09d..e9570a590fdc 100644
--- a/oap-server/server-query-plugin/promql-plugin/src/main/java/org/apache/skywalking/oap/query/promql/rt/PromQLExprQueryVisitor.java
+++ b/oap-server/server-query-plugin/promql-plugin/src/main/java/org/apache/skywalking/oap/query/promql/rt/PromQLExprQueryVisitor.java
@@ -440,6 +440,25 @@ private Entity buildEntity(Layer layer,
                 checkLabels(labelMap, LabelName.ENDPOINT);
                 entity.setEndpointName(labelMap.get(LabelName.ENDPOINT));
                 break;
+            case ServiceRelation:
+                checkLabels(labelMap, LabelName.DEST_SERVICE, LabelName.DEST_LAYER);
+                entity.setDestServiceName(labelMap.get(LabelName.DEST_SERVICE));
+                entity.setDestNormal(Layer.nameOf(labelMap.get(LabelName.DEST_LAYER)).isNormal());
+                break;
+            case ServiceInstanceRelation:
+                checkLabels(labelMap, LabelName.DEST_SERVICE, LabelName.SERVICE_INSTANCE, LabelName.DEST_SERVICE_INSTANCE, LabelName.DEST_LAYER);
+                entity.setDestServiceName(labelMap.get(LabelName.DEST_SERVICE));
+                entity.setServiceInstanceName(labelMap.get(LabelName.SERVICE_INSTANCE));
+                entity.setDestServiceInstanceName(labelMap.get(LabelName.DEST_SERVICE_INSTANCE));
+                entity.setDestNormal(Layer.nameOf(labelMap.get(LabelName.DEST_LAYER)).isNormal());
+                break;
+            case EndpointRelation:
+                checkLabels(labelMap, LabelName.DEST_SERVICE, LabelName.ENDPOINT, LabelName.DEST_ENDPOINT, LabelName.DEST_LAYER);
+                entity.setDestServiceName(labelMap.get(LabelName.DEST_SERVICE));
+                entity.setEndpointName(labelMap.get(LabelName.ENDPOINT));
+                entity.setDestEndpointName(labelMap.get(LabelName.DEST_ENDPOINT));
+                entity.setDestNormal(Layer.nameOf(labelMap.get(LabelName.DEST_LAYER)).isNormal());
+                break;
         }
         return entity;
     }


Test patch:
diff --git a/test/e2e-v2/cases/promql/expected/relation-metric-vector.yml b/test/e2e-v2/cases/promql/expected/relation-metric-vector.yml
new file mode 100644
index 000000000000..4d34926ddb11
--- /dev/null
+++ b/test/e2e-v2/cases/promql/expected/relation-metric-vector.yml
@@ -0,0 +1,27 @@
+# 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.
+
+status: success
+data:
+  resultType: vector
+  result:
+    {{- contains .data.result }}
+    - metric:
+        __name__: {{ .metric.__name__}}
+        layer: GENERAL
+      value:
+        - {{ index .value 0 }}
+        - {{ index .value 1 }}
+    {{- end}}
diff --git a/test/e2e-v2/cases/promql/promql-cases.yaml b/test/e2e-v2/cases/promql/promql-cases.yaml
index efb618fd7f89..51adce32cb98 100644
--- a/test/e2e-v2/cases/promql/promql-cases.yaml
+++ b/test/e2e-v2/cases/promql/promql-cases.yaml
@@ -92,3 +92,12 @@ cases:
     expected: expected/endpoint-metric-labeled-matrix.yml
   - query: curl -X GET http://${oap_host}:${oap_9090}/api/v1/query_range -d 'query=endpoint_cpm{parent_service="e2e-service-consumer", layer="GENERAL",top_n="10", order="DES"}&start='$(($(date +%s)-1800))'&end='$(date +%s)
     expected: expected/endpoint-metric-sort-matrix.yml
+  ## ServiceRelation
+  - query: curl -X GET http://${oap_host}:${oap_9090}/api/v1/query -d 'query=service_relation_client_cpm{service="e2e-service-consumer", dest_service="e2e-service-provider",layer="GENERAL", dest_layer="GENERAL"}'
+    expected: expected/relation-metric-vector.yml
+  ## ServiceInstanceRelation
+  - query: curl -X GET http://${oap_host}:${oap_9090}/api/v1/query -d 'query=service_instance_relation_client_cpm{service="e2e-service-consumer", dest_service="e2e-service-provider",layer="GENERAL", dest_layer="GENERAL",dest_service_instance="provider1",service_instance="consumer1"}'
+    expected: expected/relation-metric-vector.yml
+  ## EndPointRelation
+  - query: curl -X GET http://${oap_host}:${oap_9090}/api/v1/query -d 'query=endpoint_relation_cpm{service="e2e-service-consumer", dest_service="e2e-service-provider",layer="GENERAL", dest_layer="GENERAL",dest_endpoint="POST:/users",endpoint="POST:/users"}'
+    expected: expected/relation-metric-vector.yml