diff --git a/apm-dist/src/main/assembly/binary.xml b/apm-dist/src/main/assembly/binary.xml
index d3569f42d9..4eed1b7efa 100644
--- a/apm-dist/src/main/assembly/binary.xml
+++ b/apm-dist/src/main/assembly/binary.xml
@@ -59,6 +59,7 @@
endpoint-name-grouping.yml
metadata-service-mapping.yaml
trace-sampling-policy-settings.yml
+ hierarchy-definition.yml
oal/*.oal
fetcher-prom-rules/*.yaml
envoy-metrics-rules/**
diff --git a/docs/en/changes/changes.md b/docs/en/changes/changes.md
index 753777f212..437aa9522c 100644
--- a/docs/en/changes/changes.md
+++ b/docs/en/changes/changes.md
@@ -12,6 +12,7 @@
service metadata.
* Set up the length of source and dest IDs in relation entities of service, instance, endpoint, and process to 250(was
200).
+* Support build Service/Instance Hierarchy and query.
#### UI
diff --git a/docs/en/swip/SWIP-1.md b/docs/en/swip/SWIP-1.md
index e635bf3bc4..139cdadf69 100644
--- a/docs/en/swip/SWIP-1.md
+++ b/docs/en/swip/SWIP-1.md
@@ -105,7 +105,7 @@ extend type Query {
# Query the service hierarchy, based on the given service. Will recursively return all related layers services in the hierarchy.
getServiceHierarchy(serviceId: ID!, layer: String!): ServiceHierarchy!
# Query the instance hierarchy, based on the given instance. Will return all direct related layers instances in the hierarchy, no recursive.
- getInstanceHierarchy(instanceId: ID!): InstanceHierarchy!
+ getInstanceHierarchy(instanceId: ID!, layer: String!): InstanceHierarchy!
}
```
New fields are going to be added to the `topology.graphqls`.
diff --git a/oap-server/server-core/src/main/java/org/apache/skywalking/oap/server/core/CoreModule.java b/oap-server/server-core/src/main/java/org/apache/skywalking/oap/server/core/CoreModule.java
index 6df46e5e94..188e8106f6 100755
--- a/oap-server/server-core/src/main/java/org/apache/skywalking/oap/server/core/CoreModule.java
+++ b/oap-server/server-core/src/main/java/org/apache/skywalking/oap/server/core/CoreModule.java
@@ -27,7 +27,9 @@ import org.apache.skywalking.oap.server.core.command.CommandService;
import org.apache.skywalking.oap.server.core.config.ConfigService;
import org.apache.skywalking.oap.server.core.config.DownSamplingConfigService;
import org.apache.skywalking.oap.server.core.config.IComponentLibraryCatalogService;
+import org.apache.skywalking.oap.server.core.config.HierarchyDefinitionService;
import org.apache.skywalking.oap.server.core.config.NamingControl;
+import org.apache.skywalking.oap.server.core.hierarchy.HierarchyService;
import org.apache.skywalking.oap.server.core.management.ui.menu.UIMenuManagementService;
import org.apache.skywalking.oap.server.core.management.ui.template.UITemplateManagementService;
import org.apache.skywalking.oap.server.core.oal.rt.OALEngineLoaderService;
@@ -41,6 +43,7 @@ import org.apache.skywalking.oap.server.core.query.AggregationQueryService;
import org.apache.skywalking.oap.server.core.query.AlarmQueryService;
import org.apache.skywalking.oap.server.core.query.BrowserLogQueryService;
import org.apache.skywalking.oap.server.core.query.EventQueryService;
+import org.apache.skywalking.oap.server.core.query.HierarchyQueryService;
import org.apache.skywalking.oap.server.core.query.LogQueryService;
import org.apache.skywalking.oap.server.core.query.MetadataQueryService;
import org.apache.skywalking.oap.server.core.query.MetricsMetadataQueryService;
@@ -81,6 +84,7 @@ public class CoreModule extends ModuleDefine {
classes.add(DownSamplingConfigService.class);
classes.add(NamingControl.class);
classes.add(IComponentLibraryCatalogService.class);
+ classes.add(HierarchyDefinitionService.class);
classes.add(IWorkerInstanceGetter.class);
classes.add(IWorkerInstanceSetter.class);
@@ -98,7 +102,7 @@ public class CoreModule extends ModuleDefine {
addEBPFProfilingService(classes);
classes.add(CommandService.class);
-
+ classes.add(HierarchyService.class);
return classes.toArray(new Class[]{});
}
@@ -138,6 +142,7 @@ public class CoreModule extends ModuleDefine {
classes.add(EventQueryService.class);
classes.add(TagAutoCompleteQueryService.class);
classes.add(RecordQueryService.class);
+ classes.add(HierarchyQueryService.class);
}
private void addServerInterface(List classes) {
diff --git a/oap-server/server-core/src/main/java/org/apache/skywalking/oap/server/core/CoreModuleConfig.java b/oap-server/server-core/src/main/java/org/apache/skywalking/oap/server/core/CoreModuleConfig.java
index 61f7f57780..03eed9a30d 100644
--- a/oap-server/server-core/src/main/java/org/apache/skywalking/oap/server/core/CoreModuleConfig.java
+++ b/oap-server/server-core/src/main/java/org/apache/skywalking/oap/server/core/CoreModuleConfig.java
@@ -223,6 +223,14 @@ public class CoreModuleConfig extends ModuleConfig {
@Getter
private int serviceCacheRefreshInterval = 10;
+ /**
+ * If disable the hierarchy, the service and instance hierarchy relation will not be built.
+ * And the query of hierarchy will return empty result.
+ */
+ @Setter
+ @Getter
+ private boolean enableHierarchy = true;
+
public CoreModuleConfig() {
this.downsampling = new ArrayList<>();
}
diff --git a/oap-server/server-core/src/main/java/org/apache/skywalking/oap/server/core/CoreModuleProvider.java b/oap-server/server-core/src/main/java/org/apache/skywalking/oap/server/core/CoreModuleProvider.java
index 103572ee42..0162c63264 100755
--- a/oap-server/server-core/src/main/java/org/apache/skywalking/oap/server/core/CoreModuleProvider.java
+++ b/oap-server/server-core/src/main/java/org/apache/skywalking/oap/server/core/CoreModuleProvider.java
@@ -44,11 +44,13 @@ import org.apache.skywalking.oap.server.core.command.CommandService;
import org.apache.skywalking.oap.server.core.config.ComponentLibraryCatalogService;
import org.apache.skywalking.oap.server.core.config.ConfigService;
import org.apache.skywalking.oap.server.core.config.DownSamplingConfigService;
+import org.apache.skywalking.oap.server.core.config.HierarchyDefinitionService;
import org.apache.skywalking.oap.server.core.config.IComponentLibraryCatalogService;
import org.apache.skywalking.oap.server.core.config.NamingControl;
import org.apache.skywalking.oap.server.core.config.group.EndpointNameGrouping;
import org.apache.skywalking.oap.server.core.config.group.EndpointNameGroupingRuleWatcher;
import org.apache.skywalking.oap.server.core.config.group.openapi.EndpointNameGroupingRule4OpenapiWatcher;
+import org.apache.skywalking.oap.server.core.hierarchy.HierarchyService;
import org.apache.skywalking.oap.server.core.logging.LoggingConfigWatcher;
import org.apache.skywalking.oap.server.core.management.ui.menu.UIMenuInitializer;
import org.apache.skywalking.oap.server.core.management.ui.menu.UIMenuManagementService;
@@ -66,6 +68,7 @@ import org.apache.skywalking.oap.server.core.query.AggregationQueryService;
import org.apache.skywalking.oap.server.core.query.AlarmQueryService;
import org.apache.skywalking.oap.server.core.query.BrowserLogQueryService;
import org.apache.skywalking.oap.server.core.query.EventQueryService;
+import org.apache.skywalking.oap.server.core.query.HierarchyQueryService;
import org.apache.skywalking.oap.server.core.query.LogQueryService;
import org.apache.skywalking.oap.server.core.query.MetadataQueryService;
import org.apache.skywalking.oap.server.core.query.MetricsMetadataQueryService;
@@ -253,6 +256,8 @@ public class CoreModuleProvider extends ModuleProvider {
this.registerServiceImplementation(ConfigService.class, new ConfigService(moduleConfig, this));
this.registerServiceImplementation(ServerStatusService.class, new ServerStatusService(getManager(), moduleConfig));
+ this.registerServiceImplementation(HierarchyDefinitionService.class, new HierarchyDefinitionService(moduleConfig));
+ this.registerServiceImplementation(HierarchyService.class, new HierarchyService(getManager(), moduleConfig));
this.registerServiceImplementation(
DownSamplingConfigService.class, new DownSamplingConfigService(moduleConfig.getDownsampling()));
@@ -293,6 +298,7 @@ public class CoreModuleProvider extends ModuleProvider {
this.registerServiceImplementation(
TagAutoCompleteQueryService.class, new TagAutoCompleteQueryService(getManager(), moduleConfig));
this.registerServiceImplementation(RecordQueryService.class, new RecordQueryService(getManager()));
+ this.registerServiceImplementation(HierarchyQueryService.class, new HierarchyQueryService(getManager(), moduleConfig));
// add profile service implementations
this.registerServiceImplementation(
diff --git a/oap-server/server-core/src/main/java/org/apache/skywalking/oap/server/core/analysis/IDManager.java b/oap-server/server-core/src/main/java/org/apache/skywalking/oap/server/core/analysis/IDManager.java
index 969e0a7740..b0898257bf 100644
--- a/oap-server/server-core/src/main/java/org/apache/skywalking/oap/server/core/analysis/IDManager.java
+++ b/oap-server/server-core/src/main/java/org/apache/skywalking/oap/server/core/analysis/IDManager.java
@@ -71,6 +71,15 @@ public class IDManager {
return define.sourceId + Const.RELATION_ID_CONNECTOR + define.destId;
}
+ /**
+ * @return encoded service hierarchy relation id
+ */
+ public static String buildServiceHierarchyRelationId(ServiceHierarchyRelationDefine define) {
+ return define.serviceId + Const.SERVICE_ID_CONNECTOR + define.serviceLayer.value() +
+ Const.RELATION_ID_CONNECTOR +
+ define.relatedServiceId + Const.SERVICE_ID_CONNECTOR + define.relatedServiceLayer.value();
+ }
+
/**
* @return service relation ID object decoded from {@link #buildRelationId(ServiceRelationDefine)} result
*/
@@ -100,6 +109,16 @@ public class IDManager {
private final String sourceId;
private final String destId;
}
+
+ @RequiredArgsConstructor
+ @Getter
+ @EqualsAndHashCode
+ public static class ServiceHierarchyRelationDefine {
+ private final String serviceId;
+ private final Layer serviceLayer;
+ private final String relatedServiceId;
+ private final Layer relatedServiceLayer;
+ }
}
/**
@@ -140,6 +159,15 @@ public class IDManager {
return define.sourceId + Const.RELATION_ID_CONNECTOR + define.destId;
}
+ /**
+ * @return encoded instance hierarchy relation id
+ */
+ public static String buildInstanceHierarchyRelationId(InstanceHierarchyRelationDefine define) {
+ return define.instanceId + Const.SERVICE_ID_CONNECTOR + define.serviceLayer.value() +
+ Const.RELATION_ID_CONNECTOR +
+ define.relatedInstanceId + Const.SERVICE_ID_CONNECTOR + define.relatedServiceLayer.value();
+ }
+
/**
* @return service instance relation ID object decoded from {@link #buildRelationId(ServiceInstanceRelationDefine)}
* result
@@ -175,6 +203,16 @@ public class IDManager {
*/
private final String destId;
}
+
+ @RequiredArgsConstructor
+ @Getter
+ @EqualsAndHashCode
+ public static class InstanceHierarchyRelationDefine {
+ private final String instanceId;
+ private final Layer serviceLayer;
+ private final String relatedInstanceId;
+ private final Layer relatedServiceLayer;
+ }
}
/**
diff --git a/oap-server/server-core/src/main/java/org/apache/skywalking/oap/server/core/config/HierarchyDefinitionService.java b/oap-server/server-core/src/main/java/org/apache/skywalking/oap/server/core/config/HierarchyDefinitionService.java
new file mode 100644
index 0000000000..3323c66d53
--- /dev/null
+++ b/oap-server/server-core/src/main/java/org/apache/skywalking/oap/server/core/config/HierarchyDefinitionService.java
@@ -0,0 +1,79 @@
+/*
+ * 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.skywalking.oap.server.core.config;
+
+import java.io.FileNotFoundException;
+import java.io.Reader;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import lombok.Getter;
+import lombok.extern.slf4j.Slf4j;
+import org.apache.skywalking.oap.server.core.CoreModuleConfig;
+import org.apache.skywalking.oap.server.core.UnexpectedException;
+import org.apache.skywalking.oap.server.library.module.Service;
+import org.apache.skywalking.oap.server.library.util.ResourceUtils;
+import org.yaml.snakeyaml.Yaml;
+
+@Slf4j
+public class HierarchyDefinitionService implements Service {
+ @Getter
+ private Map> hierarchyDefinition;
+
+ public HierarchyDefinitionService(CoreModuleConfig moduleConfig) {
+ this.hierarchyDefinition = new HashMap<>();
+ if (moduleConfig.isEnableHierarchy()) {
+ this.init();
+ this.checkLayers();
+ }
+ }
+
+ private void init() {
+ try {
+ Reader applicationReader = ResourceUtils.read("hierarchy-definition.yml");
+ Yaml yaml = new Yaml();
+ this.hierarchyDefinition = yaml.loadAs(applicationReader, Map.class);
+ } catch (FileNotFoundException e) {
+ throw new UnexpectedException("hierarchy-definition.yml not found.", e);
+ }
+ }
+
+ private void checkLayers() {
+ this.hierarchyDefinition.forEach((layer, lowerLayers) -> {
+ if (lowerLayers.contains(layer)) {
+ throw new IllegalArgumentException(
+ "hierarchy-definition.yml " + layer + " contains recursive hierarchy relation.");
+ }
+ checkRecursive(layer);
+ });
+ }
+
+ private void checkRecursive(String layerName) {
+ try {
+ List lowerLayers = this.hierarchyDefinition.get(layerName);
+ if (lowerLayers == null) {
+ return;
+ }
+ lowerLayers.forEach(this::checkRecursive);
+ } catch (Throwable e) {
+ throw new IllegalArgumentException(
+ "hierarchy-definition.yml " + layerName + " contains recursive hierarchy relation.");
+ }
+ }
+}
diff --git a/oap-server/server-core/src/main/java/org/apache/skywalking/oap/server/core/hierarchy/HierarchyService.java b/oap-server/server-core/src/main/java/org/apache/skywalking/oap/server/core/hierarchy/HierarchyService.java
new file mode 100644
index 0000000000..f3dd382ccc
--- /dev/null
+++ b/oap-server/server-core/src/main/java/org/apache/skywalking/oap/server/core/hierarchy/HierarchyService.java
@@ -0,0 +1,143 @@
+/*
+ * 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.skywalking.oap.server.core.hierarchy;
+
+import java.util.List;
+import java.util.Map;
+import lombok.extern.slf4j.Slf4j;
+import org.apache.skywalking.oap.server.core.CoreModule;
+import org.apache.skywalking.oap.server.core.CoreModuleConfig;
+import org.apache.skywalking.oap.server.core.analysis.Layer;
+import org.apache.skywalking.oap.server.core.analysis.TimeBucket;
+import org.apache.skywalking.oap.server.core.config.HierarchyDefinitionService;
+import org.apache.skywalking.oap.server.core.hierarchy.instance.InstanceHierarchyRelation;
+import org.apache.skywalking.oap.server.core.hierarchy.service.ServiceHierarchyRelation;
+import org.apache.skywalking.oap.server.core.source.SourceReceiver;
+import org.apache.skywalking.oap.server.library.module.ModuleManager;
+import org.apache.skywalking.oap.server.library.module.Service;
+
+@Slf4j
+public class HierarchyService implements Service {
+ private final ModuleManager moduleManager;
+ private final boolean isEnableHierarchy;
+ private SourceReceiver sourceReceiver;
+ private Map> hierarchyDefinition;
+
+ public HierarchyService(ModuleManager moduleManager, CoreModuleConfig moduleConfig) {
+ this.moduleManager = moduleManager;
+ this.isEnableHierarchy = moduleConfig.isEnableHierarchy();
+ }
+
+ private SourceReceiver getSourceReceiver() {
+ if (sourceReceiver == null) {
+ this.sourceReceiver = moduleManager.find(CoreModule.NAME).provider().getService(SourceReceiver.class);
+ }
+ return sourceReceiver;
+ }
+
+ private Map> getHierarchyDefinition() {
+ if (hierarchyDefinition == null) {
+ hierarchyDefinition = moduleManager.find(CoreModule.NAME)
+ .provider()
+ .getService(HierarchyDefinitionService.class).getHierarchyDefinition();
+ }
+ return hierarchyDefinition;
+ }
+
+ /**
+ * Build the hierarchy relation between the 2 services. the `serviceLayer` and `relatedServiceLayer` hierarchy
+ * relations should be defined in `config/hierarchy-definition.yml`. Generally, if service A and service B could detect
+ * each other, then build the hierarchy in one side is enough.
+ *
+ * @param serviceName the name of the source service (self)
+ * @param serviceLayer the layer of the source service
+ * @param relatedServiceName the name of the detected related service
+ * @param relatedServiceLayer the layer of the detected related service
+ */
+ public void toServiceHierarchyRelation(String serviceName,
+ Layer serviceLayer,
+ String relatedServiceName,
+ Layer relatedServiceLayer) {
+ if (!this.isEnableHierarchy) {
+ return;
+ }
+ if (!checkHierarchyDefinition(serviceLayer, relatedServiceLayer)) {
+ return;
+ }
+ ServiceHierarchyRelation serviceHierarchy = new ServiceHierarchyRelation();
+ serviceHierarchy.setServiceName(serviceName);
+ serviceHierarchy.setServiceLayer(serviceLayer);
+ serviceHierarchy.setRelatedServiceName(relatedServiceName);
+ serviceHierarchy.setRelatedServiceLayer(relatedServiceLayer);
+ serviceHierarchy.setTimeBucket(TimeBucket.getMinuteTimeBucket(System.currentTimeMillis()));
+ this.getSourceReceiver().receive(serviceHierarchy);
+ }
+
+ /**
+ * Build the hierarchy relation between the 2 instances. the `serviceLayer` and `relatedServiceLayer` hierarchy
+ * relations should be defined in `config/hierarchy-definition.yml`. Generally, if instance A and instance B could detect
+ * each other, then build the hierarchy in one side is enough.
+ *
+ * @param instanceName the name of the source instance (self)
+ * @param serviceName the name of the source service (self)
+ * @param serviceLayer the layer of the source service
+ * @param relatedInstanceName the name of the detected related instance
+ * @param relatedServiceName the name of the detected related service
+ * @param relatedServiceLayer the layer of the detected related service
+ */
+ public void toInstanceHierarchyRelation(String instanceName,
+ String serviceName,
+ Layer serviceLayer,
+ String relatedInstanceName,
+ String relatedServiceName,
+ Layer relatedServiceLayer) {
+ if (!this.isEnableHierarchy) {
+ return;
+ }
+ if (!checkHierarchyDefinition(serviceLayer, relatedServiceLayer)) {
+ return;
+ }
+ InstanceHierarchyRelation instanceHierarchy = new InstanceHierarchyRelation();
+ instanceHierarchy.setInstanceName(instanceName);
+ instanceHierarchy.setServiceName(serviceName);
+ instanceHierarchy.setServiceLayer(serviceLayer);
+ instanceHierarchy.setRelatedInstanceName(relatedInstanceName);
+ instanceHierarchy.setRelatedServiceName(relatedServiceName);
+ instanceHierarchy.setRelatedServiceLayer(relatedServiceLayer);
+ instanceHierarchy.setTimeBucket(TimeBucket.getMinuteTimeBucket(System.currentTimeMillis()));
+ this.getSourceReceiver().receive(instanceHierarchy);
+ }
+
+ private boolean checkHierarchyDefinition(Layer serviceLayer, Layer relatedServiceLayer) {
+ List lowerLayers = getHierarchyDefinition().get(serviceLayer.name());
+ List relatedLowerLayers = getHierarchyDefinition().get(relatedServiceLayer.name());
+ if (lowerLayers == null || relatedLowerLayers == null) {
+ log.error("serviceLayer " + serviceLayer.name() + " or relatedServiceLayer " + relatedServiceLayer.name()
+ + " is not defined in hierarchy-definition.yml.");
+ return false;
+ }
+
+ if (!lowerLayers.contains(relatedServiceLayer.name()) || !relatedLowerLayers.contains(serviceLayer.name())) {
+ log.error("serviceLayer " + serviceLayer.name() + " and relatedServiceLayer " + relatedServiceLayer.name()
+ + " should have the hierarchy relation in hierarchy-definition.yml.");
+ return false;
+ }
+ return true;
+ }
+}
diff --git a/oap-server/server-core/src/main/java/org/apache/skywalking/oap/server/core/hierarchy/instance/InstanceHierarchyRelation.java b/oap-server/server-core/src/main/java/org/apache/skywalking/oap/server/core/hierarchy/instance/InstanceHierarchyRelation.java
new file mode 100644
index 0000000000..23397ba7c2
--- /dev/null
+++ b/oap-server/server-core/src/main/java/org/apache/skywalking/oap/server/core/hierarchy/instance/InstanceHierarchyRelation.java
@@ -0,0 +1,75 @@
+/*
+ * 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.skywalking.oap.server.core.hierarchy.instance;
+
+import lombok.Getter;
+import lombok.Setter;
+import org.apache.skywalking.oap.server.core.analysis.IDManager;
+import org.apache.skywalking.oap.server.core.analysis.Layer;
+import org.apache.skywalking.oap.server.core.source.DefaultScopeDefine;
+import org.apache.skywalking.oap.server.core.source.ScopeDeclaration;
+import org.apache.skywalking.oap.server.core.source.Source;
+
+@ScopeDeclaration(id = DefaultScopeDefine.INSTANCE_HIERARCHY_RELATION, name = "InstanceHierarchyRelation")
+public class InstanceHierarchyRelation extends Source {
+ @Setter
+ @Getter
+ private String instanceName;
+ @Getter
+ private String instanceId;
+ @Setter
+ @Getter
+ private String serviceName;
+ @Getter
+ private String serviceId;
+ @Setter
+ @Getter
+ private Layer serviceLayer;
+ @Setter
+ @Getter
+ private String relatedInstanceName;
+ @Getter
+ private String relatedInstanceId;
+ @Setter
+ @Getter
+ private String relatedServiceName;
+ @Getter
+ private String relatedServiceId;
+ @Setter
+ @Getter
+ private Layer relatedServiceLayer;
+
+ @Override
+ public int scope() {
+ return DefaultScopeDefine.INSTANCE_HIERARCHY_RELATION;
+ }
+
+ @Override
+ public String getEntityId() {
+ return null;
+ }
+
+ @Override
+ public void prepare() {
+ serviceId = IDManager.ServiceID.buildId(serviceName, serviceLayer.isNormal());
+ relatedServiceId = IDManager.ServiceID.buildId(relatedServiceName, relatedServiceLayer.isNormal());
+ instanceId = IDManager.ServiceInstanceID.buildId(serviceId, instanceName);
+ relatedInstanceId = IDManager.ServiceInstanceID.buildId(relatedServiceId, relatedInstanceName);
+ }
+}
diff --git a/oap-server/server-core/src/main/java/org/apache/skywalking/oap/server/core/hierarchy/instance/InstanceHierarchyRelationTraffic.java b/oap-server/server-core/src/main/java/org/apache/skywalking/oap/server/core/hierarchy/instance/InstanceHierarchyRelationTraffic.java
new file mode 100644
index 0000000000..a6a4c592fa
--- /dev/null
+++ b/oap-server/server-core/src/main/java/org/apache/skywalking/oap/server/core/hierarchy/instance/InstanceHierarchyRelationTraffic.java
@@ -0,0 +1,170 @@
+/*
+ * 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.skywalking.oap.server.core.hierarchy.instance;
+
+import lombok.EqualsAndHashCode;
+import lombok.Getter;
+import lombok.Setter;
+import org.apache.skywalking.oap.server.core.analysis.IDManager;
+import org.apache.skywalking.oap.server.core.analysis.Layer;
+import org.apache.skywalking.oap.server.core.analysis.MetricsExtension;
+import org.apache.skywalking.oap.server.core.analysis.Stream;
+import org.apache.skywalking.oap.server.core.analysis.metrics.Metrics;
+import org.apache.skywalking.oap.server.core.analysis.worker.MetricsStreamProcessor;
+import org.apache.skywalking.oap.server.core.remote.grpc.proto.RemoteData;
+import org.apache.skywalking.oap.server.core.source.DefaultScopeDefine;
+import org.apache.skywalking.oap.server.core.storage.StorageID;
+import org.apache.skywalking.oap.server.core.storage.annotation.BanyanDB;
+import org.apache.skywalking.oap.server.core.storage.annotation.Column;
+import org.apache.skywalking.oap.server.core.storage.type.Convert2Entity;
+import org.apache.skywalking.oap.server.core.storage.type.Convert2Storage;
+import org.apache.skywalking.oap.server.core.storage.type.StorageBuilder;
+
+@Stream(name = InstanceHierarchyRelationTraffic.INDEX_NAME, scopeId = DefaultScopeDefine.INSTANCE_HIERARCHY_RELATION,
+ builder = InstanceHierarchyRelationTraffic.Builder.class, processor = MetricsStreamProcessor.class)
+@MetricsExtension(supportDownSampling = false, supportUpdate = false)
+@EqualsAndHashCode(of = {
+ "instanceId",
+ "serviceLayer",
+ "relatedInstanceId",
+ "relatedServiceLayer"
+}, callSuper = true)
+public class InstanceHierarchyRelationTraffic extends Metrics {
+ public static final String INDEX_NAME = "instance_hierarchy_relation";
+ public static final String SERVICE_LAYER = "service_layer";
+ public static final String INSTANCE_ID = "instance_id";
+ public static final String RELATED_SERVICE_LAYER = "related_service_layer";
+ public static final String RELATED_INSTANCE_ID = "related_instance_id";
+
+ @Setter
+ @Getter
+ @Column(name = INSTANCE_ID, length = 250)
+ @BanyanDB.SeriesID(index = 0)
+ private String instanceId;
+
+ @Setter
+ @Getter
+ @Column(name = SERVICE_LAYER)
+ @BanyanDB.SeriesID(index = 1)
+ private Layer serviceLayer = Layer.UNDEFINED;
+
+ @Setter
+ @Getter
+ @Column(name = RELATED_INSTANCE_ID, length = 250)
+ @BanyanDB.SeriesID(index = 2)
+ private String relatedInstanceId;
+
+ @Setter
+ @Getter
+ @Column(name = RELATED_SERVICE_LAYER)
+ @BanyanDB.SeriesID(index = 3)
+ private Layer relatedServiceLayer = Layer.UNDEFINED;
+
+ @Override
+ protected StorageID id0() {
+ String id = IDManager.ServiceInstanceID.buildInstanceHierarchyRelationId(
+ new IDManager.ServiceInstanceID.InstanceHierarchyRelationDefine(
+ instanceId, serviceLayer, relatedInstanceId, relatedServiceLayer));
+ return new StorageID().appendMutant(new String[] {
+ INSTANCE_ID,
+ RELATED_INSTANCE_ID,
+ SERVICE_LAYER,
+ RELATED_SERVICE_LAYER
+ }, id);
+ }
+
+ @Override
+ public boolean combine(final Metrics metrics) {
+ return true;
+ }
+
+ @Override
+ public void calculate() {
+
+ }
+
+ @Override
+ public Metrics toHour() {
+ return null;
+ }
+
+ @Override
+ public Metrics toDay() {
+ return null;
+ }
+
+ @Override
+ public void deserialize(final RemoteData remoteData) {
+ setInstanceId(remoteData.getDataStrings(0));
+ setServiceLayer(Layer.valueOf(remoteData.getDataIntegers(0)));
+ setRelatedInstanceId(remoteData.getDataStrings(1));
+ setRelatedServiceLayer(Layer.valueOf(remoteData.getDataIntegers(1)));
+ setTimeBucket(remoteData.getDataLongs(0));
+ }
+
+ @Override
+ public RemoteData.Builder serialize() {
+ final RemoteData.Builder builder = RemoteData.newBuilder();
+ builder.addDataStrings(instanceId);
+ builder.addDataIntegers(serviceLayer.value());
+ builder.addDataStrings(relatedInstanceId);
+ builder.addDataIntegers(relatedServiceLayer.value());
+ builder.addDataLongs(getTimeBucket());
+ return builder;
+ }
+
+ @Override
+ public int remoteHashCode() {
+ return this.hashCode();
+ }
+
+ public static class Builder implements StorageBuilder {
+ @Override
+ public InstanceHierarchyRelationTraffic storage2Entity(final Convert2Entity converter) {
+ InstanceHierarchyRelationTraffic traffic = new InstanceHierarchyRelationTraffic();
+ traffic.setInstanceId((String) converter.get(INSTANCE_ID));
+ traffic.setRelatedInstanceId((String) converter.get(RELATED_INSTANCE_ID));
+ if (converter.get(SERVICE_LAYER) != null) {
+ traffic.setServiceLayer(Layer.valueOf(((Number) converter.get(SERVICE_LAYER)).intValue()));
+ } else {
+ traffic.setServiceLayer(Layer.UNDEFINED);
+ }
+ if (converter.get(RELATED_SERVICE_LAYER) != null) {
+ traffic.setRelatedServiceLayer(
+ Layer.valueOf(((Number) converter.get(RELATED_SERVICE_LAYER)).intValue()));
+ } else {
+ traffic.setRelatedServiceLayer(Layer.UNDEFINED);
+ }
+ if (converter.get(TIME_BUCKET) != null) {
+ traffic.setTimeBucket(((Number) converter.get(TIME_BUCKET)).longValue());
+ }
+ return traffic;
+ }
+
+ @Override
+ public void entity2Storage(final InstanceHierarchyRelationTraffic storageData,
+ final Convert2Storage converter) {
+ converter.accept(INSTANCE_ID, storageData.getInstanceId());
+ converter.accept(RELATED_INSTANCE_ID, storageData.getRelatedInstanceId());
+ converter.accept(SERVICE_LAYER, storageData.getServiceLayer().value());
+ converter.accept(RELATED_SERVICE_LAYER, storageData.getRelatedServiceLayer().value());
+ converter.accept(TIME_BUCKET, storageData.getTimeBucket());
+ }
+ }
+}
diff --git a/oap-server/server-core/src/main/java/org/apache/skywalking/oap/server/core/hierarchy/instance/InstanceHierarchyTrafficRelationDispatcher.java b/oap-server/server-core/src/main/java/org/apache/skywalking/oap/server/core/hierarchy/instance/InstanceHierarchyTrafficRelationDispatcher.java
new file mode 100644
index 0000000000..78e471a0cc
--- /dev/null
+++ b/oap-server/server-core/src/main/java/org/apache/skywalking/oap/server/core/hierarchy/instance/InstanceHierarchyTrafficRelationDispatcher.java
@@ -0,0 +1,35 @@
+/*
+ * 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.skywalking.oap.server.core.hierarchy.instance;
+
+import org.apache.skywalking.oap.server.core.analysis.SourceDispatcher;
+import org.apache.skywalking.oap.server.core.analysis.worker.MetricsStreamProcessor;
+
+public class InstanceHierarchyTrafficRelationDispatcher implements SourceDispatcher {
+ @Override
+ public void dispatch(final InstanceHierarchyRelation source) {
+ InstanceHierarchyRelationTraffic traffic = new InstanceHierarchyRelationTraffic();
+ traffic.setInstanceId(source.getInstanceId());
+ traffic.setServiceLayer(source.getServiceLayer());
+ traffic.setRelatedInstanceId(source.getRelatedInstanceId());
+ traffic.setRelatedServiceLayer(source.getRelatedServiceLayer());
+ traffic.setTimeBucket(source.getTimeBucket());
+ MetricsStreamProcessor.getInstance().in(traffic);
+ }
+}
diff --git a/oap-server/server-core/src/main/java/org/apache/skywalking/oap/server/core/hierarchy/service/ServiceHierarchyRelation.java b/oap-server/server-core/src/main/java/org/apache/skywalking/oap/server/core/hierarchy/service/ServiceHierarchyRelation.java
new file mode 100644
index 0000000000..2121c61b89
--- /dev/null
+++ b/oap-server/server-core/src/main/java/org/apache/skywalking/oap/server/core/hierarchy/service/ServiceHierarchyRelation.java
@@ -0,0 +1,63 @@
+/*
+ * 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.skywalking.oap.server.core.hierarchy.service;
+
+import lombok.Getter;
+import lombok.Setter;
+import org.apache.skywalking.oap.server.core.analysis.IDManager;
+import org.apache.skywalking.oap.server.core.analysis.Layer;
+import org.apache.skywalking.oap.server.core.source.DefaultScopeDefine;
+import org.apache.skywalking.oap.server.core.source.ScopeDeclaration;
+import org.apache.skywalking.oap.server.core.source.Source;
+
+@ScopeDeclaration(id = DefaultScopeDefine.SERVICE_HIERARCHY_RELATION, name = "ServiceHierarchyRelation")
+public class ServiceHierarchyRelation extends Source {
+ @Setter
+ @Getter
+ private String serviceName;
+ @Getter
+ private String serviceId;
+ @Setter
+ @Getter
+ private Layer serviceLayer;
+ @Setter
+ @Getter
+ private String relatedServiceName;
+ @Getter
+ private String relatedServiceId;
+ @Setter
+ @Getter
+ private Layer relatedServiceLayer;
+
+ @Override
+ public int scope() {
+ return DefaultScopeDefine.SERVICE_HIERARCHY_RELATION;
+ }
+
+ @Override
+ public String getEntityId() {
+ return null;
+ }
+
+ @Override
+ public void prepare() {
+ serviceId = IDManager.ServiceID.buildId(serviceName, serviceLayer.isNormal());
+ relatedServiceId = IDManager.ServiceID.buildId(relatedServiceName, relatedServiceLayer.isNormal());
+ }
+}
diff --git a/oap-server/server-core/src/main/java/org/apache/skywalking/oap/server/core/hierarchy/service/ServiceHierarchyRelationTraffic.java b/oap-server/server-core/src/main/java/org/apache/skywalking/oap/server/core/hierarchy/service/ServiceHierarchyRelationTraffic.java
new file mode 100644
index 0000000000..f115999bfc
--- /dev/null
+++ b/oap-server/server-core/src/main/java/org/apache/skywalking/oap/server/core/hierarchy/service/ServiceHierarchyRelationTraffic.java
@@ -0,0 +1,170 @@
+/*
+ * 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.skywalking.oap.server.core.hierarchy.service;
+
+import lombok.EqualsAndHashCode;
+import lombok.Getter;
+import lombok.Setter;
+import org.apache.skywalking.oap.server.core.analysis.IDManager;
+import org.apache.skywalking.oap.server.core.analysis.Layer;
+import org.apache.skywalking.oap.server.core.analysis.MetricsExtension;
+import org.apache.skywalking.oap.server.core.analysis.Stream;
+import org.apache.skywalking.oap.server.core.analysis.metrics.Metrics;
+import org.apache.skywalking.oap.server.core.analysis.worker.MetricsStreamProcessor;
+import org.apache.skywalking.oap.server.core.remote.grpc.proto.RemoteData;
+import org.apache.skywalking.oap.server.core.source.DefaultScopeDefine;
+import org.apache.skywalking.oap.server.core.storage.StorageID;
+import org.apache.skywalking.oap.server.core.storage.annotation.BanyanDB;
+import org.apache.skywalking.oap.server.core.storage.annotation.Column;
+import org.apache.skywalking.oap.server.core.storage.type.Convert2Entity;
+import org.apache.skywalking.oap.server.core.storage.type.Convert2Storage;
+import org.apache.skywalking.oap.server.core.storage.type.StorageBuilder;
+
+@Stream(name = ServiceHierarchyRelationTraffic.INDEX_NAME, scopeId = DefaultScopeDefine.SERVICE_HIERARCHY_RELATION,
+ builder = ServiceHierarchyRelationTraffic.Builder.class, processor = MetricsStreamProcessor.class)
+@MetricsExtension(supportDownSampling = false, supportUpdate = false)
+@EqualsAndHashCode(of = {
+ "serviceId",
+ "serviceLayer",
+ "relatedServiceId",
+ "relatedServiceLayer"
+}, callSuper = true)
+public class ServiceHierarchyRelationTraffic extends Metrics {
+ public static final String INDEX_NAME = "service_hierarchy_relation";
+ public static final String SERVICE_ID = "service_id";
+ public static final String SERVICE_LAYER = "service_layer";
+ public static final String RELATED_SERVICE_ID = "related_service_id";
+ public static final String RELATED_SERVICE_LAYER = "related_service_layer";
+
+ @Setter
+ @Getter
+ @Column(name = SERVICE_ID, length = 250)
+ @BanyanDB.SeriesID(index = 0)
+ private String serviceId;
+
+ @Setter
+ @Getter
+ @Column(name = SERVICE_LAYER)
+ @BanyanDB.SeriesID(index = 1)
+ private Layer serviceLayer = Layer.UNDEFINED;
+
+ @Setter
+ @Getter
+ @Column(name = RELATED_SERVICE_ID, length = 250)
+ @BanyanDB.SeriesID(index = 2)
+ private String relatedServiceId;
+
+ @Setter
+ @Getter
+ @Column(name = RELATED_SERVICE_LAYER)
+ @BanyanDB.SeriesID(index = 3)
+ private Layer relatedServiceLayer = Layer.UNDEFINED;
+
+ @Override
+ protected StorageID id0() {
+ String id = IDManager.ServiceID.buildServiceHierarchyRelationId(
+ new IDManager.ServiceID.ServiceHierarchyRelationDefine(
+ serviceId, serviceLayer, relatedServiceId, relatedServiceLayer));
+
+ return new StorageID().appendMutant(new String[] {
+ SERVICE_ID,
+ SERVICE_LAYER,
+ RELATED_SERVICE_ID,
+ RELATED_SERVICE_LAYER
+ }, id);
+ }
+
+ @Override
+ public boolean combine(final Metrics metrics) {
+ return true;
+ }
+
+ @Override
+ public void calculate() {
+
+ }
+
+ @Override
+ public Metrics toHour() {
+ return null;
+ }
+
+ @Override
+ public Metrics toDay() {
+ return null;
+ }
+
+ @Override
+ public void deserialize(final RemoteData remoteData) {
+ setServiceId(remoteData.getDataStrings(0));
+ setServiceLayer(Layer.valueOf(remoteData.getDataIntegers(0)));
+ setRelatedServiceId(remoteData.getDataStrings(1));
+ setRelatedServiceLayer(Layer.valueOf(remoteData.getDataIntegers(1)));
+ setTimeBucket(remoteData.getDataLongs(0));
+ }
+
+ @Override
+ public RemoteData.Builder serialize() {
+ final RemoteData.Builder builder = RemoteData.newBuilder();
+ builder.addDataStrings(serviceId);
+ builder.addDataIntegers(serviceLayer.value());
+ builder.addDataStrings(relatedServiceId);
+ builder.addDataIntegers(relatedServiceLayer.value());
+ builder.addDataLongs(getTimeBucket());
+ return builder;
+ }
+
+ @Override
+ public int remoteHashCode() {
+ return this.hashCode();
+ }
+
+ public static class Builder implements StorageBuilder {
+ @Override
+ public ServiceHierarchyRelationTraffic storage2Entity(final Convert2Entity converter) {
+ ServiceHierarchyRelationTraffic traffic = new ServiceHierarchyRelationTraffic();
+ traffic.setServiceId((String) converter.get(SERVICE_ID));
+ traffic.setRelatedServiceId((String) converter.get(RELATED_SERVICE_ID));
+ if (converter.get(SERVICE_LAYER) != null) {
+ traffic.setServiceLayer(Layer.valueOf(((Number) converter.get(SERVICE_LAYER)).intValue()));
+ } else {
+ traffic.setServiceLayer(Layer.UNDEFINED);
+ }
+ if (converter.get(RELATED_SERVICE_LAYER) != null) {
+ traffic.setRelatedServiceLayer(
+ Layer.valueOf(((Number) converter.get(RELATED_SERVICE_LAYER)).intValue()));
+ } else {
+ traffic.setRelatedServiceLayer(Layer.UNDEFINED);
+ }
+ if (converter.get(TIME_BUCKET) != null) {
+ traffic.setTimeBucket(((Number) converter.get(TIME_BUCKET)).longValue());
+ }
+ return traffic;
+ }
+
+ @Override
+ public void entity2Storage(final ServiceHierarchyRelationTraffic storageData, final Convert2Storage converter) {
+ converter.accept(SERVICE_ID, storageData.getServiceId());
+ converter.accept(RELATED_SERVICE_ID, storageData.getRelatedServiceId());
+ converter.accept(SERVICE_LAYER, storageData.getServiceLayer().value());
+ converter.accept(RELATED_SERVICE_LAYER, storageData.getRelatedServiceLayer().value());
+ converter.accept(TIME_BUCKET, storageData.getTimeBucket());
+ }
+ }
+}
diff --git a/oap-server/server-core/src/main/java/org/apache/skywalking/oap/server/core/hierarchy/service/ServiceHierarchyTrafficRelationDispatcher.java b/oap-server/server-core/src/main/java/org/apache/skywalking/oap/server/core/hierarchy/service/ServiceHierarchyTrafficRelationDispatcher.java
new file mode 100644
index 0000000000..27723f5c39
--- /dev/null
+++ b/oap-server/server-core/src/main/java/org/apache/skywalking/oap/server/core/hierarchy/service/ServiceHierarchyTrafficRelationDispatcher.java
@@ -0,0 +1,35 @@
+/*
+ * 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.skywalking.oap.server.core.hierarchy.service;
+
+import org.apache.skywalking.oap.server.core.analysis.SourceDispatcher;
+import org.apache.skywalking.oap.server.core.analysis.worker.MetricsStreamProcessor;
+
+public class ServiceHierarchyTrafficRelationDispatcher implements SourceDispatcher {
+ @Override
+ public void dispatch(final ServiceHierarchyRelation source) {
+ ServiceHierarchyRelationTraffic traffic = new ServiceHierarchyRelationTraffic();
+ traffic.setServiceId(source.getServiceId());
+ traffic.setServiceLayer(source.getServiceLayer());
+ traffic.setRelatedServiceId(source.getRelatedServiceId());
+ traffic.setRelatedServiceLayer(source.getRelatedServiceLayer());
+ traffic.setTimeBucket(source.getTimeBucket());
+ MetricsStreamProcessor.getInstance().in(traffic);
+ }
+}
diff --git a/oap-server/server-core/src/main/java/org/apache/skywalking/oap/server/core/query/HierarchyQueryService.java b/oap-server/server-core/src/main/java/org/apache/skywalking/oap/server/core/query/HierarchyQueryService.java
new file mode 100644
index 0000000000..018953b041
--- /dev/null
+++ b/oap-server/server-core/src/main/java/org/apache/skywalking/oap/server/core/query/HierarchyQueryService.java
@@ -0,0 +1,189 @@
+/*
+ * 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.skywalking.oap.server.core.query;
+
+import com.google.common.cache.CacheBuilder;
+import com.google.common.cache.CacheLoader;
+import com.google.common.cache.LoadingCache;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.TimeUnit;
+import lombok.Data;
+import lombok.extern.slf4j.Slf4j;
+import org.apache.skywalking.oap.server.core.CoreModule;
+import org.apache.skywalking.oap.server.core.CoreModuleConfig;
+import org.apache.skywalking.oap.server.core.analysis.IDManager;
+import org.apache.skywalking.oap.server.core.config.HierarchyDefinitionService;
+import org.apache.skywalking.oap.server.core.hierarchy.instance.InstanceHierarchyRelationTraffic;
+import org.apache.skywalking.oap.server.core.hierarchy.service.ServiceHierarchyRelationTraffic;
+import org.apache.skywalking.oap.server.core.query.type.HierarchyInstanceRelation;
+import org.apache.skywalking.oap.server.core.query.type.HierarchyRelatedInstance;
+import org.apache.skywalking.oap.server.core.query.type.HierarchyRelatedService;
+import org.apache.skywalking.oap.server.core.query.type.HierarchyServiceRelation;
+import org.apache.skywalking.oap.server.core.query.type.InstanceHierarchy;
+import org.apache.skywalking.oap.server.core.query.type.ServiceHierarchy;
+import org.apache.skywalking.oap.server.core.storage.StorageModule;
+import org.apache.skywalking.oap.server.core.storage.query.IHierarchyQueryDAO;
+import org.apache.skywalking.oap.server.library.module.ModuleManager;
+import org.apache.skywalking.oap.server.library.module.Service;
+
+@Slf4j
+public class HierarchyQueryService implements Service {
+ private final ModuleManager moduleManager;
+ private final boolean isEnableHierarchy;
+ private IHierarchyQueryDAO hierarchyQueryDAO;
+ private Map> hierarchyDefinition;
+ private LoadingCache> serviceHierarchyCache;
+
+ public HierarchyQueryService(ModuleManager moduleManager, CoreModuleConfig moduleConfig) {
+ this.moduleManager = moduleManager;
+ this.isEnableHierarchy = moduleConfig.isEnableHierarchy();
+ if (moduleConfig.isEnableHierarchy()) {
+ this.serviceHierarchyCache =
+ CacheBuilder.newBuilder()
+ .maximumSize(1)
+ .refreshAfterWrite(moduleConfig.getServiceCacheRefreshInterval(), TimeUnit.SECONDS)
+ .build(
+ new CacheLoader<>() {
+ @Override
+ public Map load(final Boolean key) throws Exception {
+ return mapServiceHierarchy();
+ }
+ });
+ }
+ }
+
+ private IHierarchyQueryDAO getHierarchyQueryDAO() {
+ if (hierarchyQueryDAO == null) {
+ this.hierarchyQueryDAO = moduleManager.find(StorageModule.NAME)
+ .provider()
+ .getService(IHierarchyQueryDAO.class);
+ }
+ return hierarchyQueryDAO;
+ }
+
+ private Map> getHierarchyDefinition() {
+ if (hierarchyDefinition == null) {
+ hierarchyDefinition = moduleManager.find(CoreModule.NAME)
+ .provider()
+ .getService(HierarchyDefinitionService.class).getHierarchyDefinition();
+ }
+ return hierarchyDefinition;
+ }
+
+ private Map mapServiceHierarchy() throws Exception {
+ List traffics = getHierarchyQueryDAO().readAllServiceHierarchyRelations();
+ Map serviceRelationsMap = new HashMap<>();
+
+ for (ServiceHierarchyRelationTraffic traffic : traffics) {
+ HierarchyRelatedService service = new HierarchyRelatedService();
+ service.setId(traffic.getServiceId());
+ service.setName(IDManager.ServiceID.analysisId(traffic.getServiceId()).getName());
+ service.setLayer(traffic.getServiceLayer().name());
+ HierarchyRelatedService relatedService = new HierarchyRelatedService();
+ relatedService.setId(traffic.getRelatedServiceId());
+ relatedService.setName(IDManager.ServiceID.analysisId(traffic.getRelatedServiceId()).getName());
+ relatedService.setLayer(traffic.getRelatedServiceLayer().name());
+
+ ServiceRelations serviceRelations = serviceRelationsMap.computeIfAbsent(
+ service, k -> new ServiceRelations());
+ ServiceRelations relationServiceRelations = serviceRelationsMap.computeIfAbsent(
+ relatedService, k -> new ServiceRelations());
+ List lowerLayers = getHierarchyDefinition().getOrDefault(
+ traffic.getServiceLayer().name(), new ArrayList<>());
+ List relatedLowerLayers = getHierarchyDefinition().getOrDefault(
+ traffic.getRelatedServiceLayer().name(), new ArrayList<>());
+
+ // Build the relations in 2 directions
+ if (lowerLayers.contains(traffic.getRelatedServiceLayer().name())) {
+ serviceRelations.getLowerServices().add(relatedService);
+ relationServiceRelations.getUpperServices().add(service);
+ } else if (relatedLowerLayers.contains(traffic.getServiceLayer().name())) {
+ serviceRelations.getUpperServices().add(relatedService);
+ relationServiceRelations.getLowerServices().add(service);
+ }
+ }
+ return serviceRelationsMap;
+ }
+
+ public ServiceHierarchy getServiceHierarchy(String serviceId, String layer) throws Exception {
+ if (!this.isEnableHierarchy) {
+ log.warn("CoreModuleConfig config {enableHierarchy} is false, return empty ServiceHierarchy.");
+ return new ServiceHierarchy();
+ }
+ ServiceHierarchy hierarchy = new ServiceHierarchy();
+ HierarchyRelatedService self = new HierarchyRelatedService();
+ self.setId(serviceId);
+ self.setName(IDManager.ServiceID.analysisId(serviceId).getName());
+ self.setLayer(layer);
+ Map serviceRelationsMap = serviceHierarchyCache.get(true);
+ ServiceRelations serviceRelations = serviceRelationsMap.getOrDefault(self, new ServiceRelations());
+
+ serviceRelations.getLowerServices().forEach(relatedService -> {
+ hierarchy.getRelations().add(new HierarchyServiceRelation(self, relatedService));
+ });
+ serviceRelations.getUpperServices().forEach(relatedService -> {
+ hierarchy.getRelations().add(new HierarchyServiceRelation(relatedService, self));
+ });
+
+ return hierarchy;
+ }
+
+ public InstanceHierarchy getInstanceHierarchy(String instanceId, String layer) throws Exception {
+ if (!this.isEnableHierarchy) {
+ log.warn("CoreModuleConfig config {enableHierarchy} is false, return empty InstanceHierarchy.");
+ return new InstanceHierarchy();
+ }
+ InstanceHierarchy hierarchy = new InstanceHierarchy();
+ List traffics = getHierarchyQueryDAO().readInstanceHierarchyRelations(
+ instanceId, layer);
+
+ for (InstanceHierarchyRelationTraffic traffic : traffics) {
+ HierarchyRelatedInstance instance = new HierarchyRelatedInstance();
+ instance.setId(traffic.getInstanceId());
+ instance.setName(IDManager.ServiceInstanceID.analysisId(traffic.getInstanceId()).getName());
+ instance.setLayer(traffic.getServiceLayer().name());
+ HierarchyRelatedInstance relatedInstance = new HierarchyRelatedInstance();
+ relatedInstance.setId(traffic.getRelatedInstanceId());
+ relatedInstance.setName(IDManager.ServiceInstanceID.analysisId(traffic.getRelatedInstanceId()).getName());
+ relatedInstance.setLayer(traffic.getRelatedServiceLayer().name());
+ List lowerLayers = getHierarchyDefinition().getOrDefault(
+ traffic.getServiceLayer().name(), new ArrayList<>());
+ List relatedLowerLayers = getHierarchyDefinition().getOrDefault(
+ traffic.getRelatedServiceLayer().name(), new ArrayList<>());
+
+ //should build the relations in 2 direction
+ if (lowerLayers.contains(traffic.getRelatedServiceLayer().name())) {
+ hierarchy.getRelations().add(new HierarchyInstanceRelation(instance, relatedInstance));
+ } else if (relatedLowerLayers.contains(traffic.getServiceLayer().name())) {
+ hierarchy.getRelations().add(new HierarchyInstanceRelation(relatedInstance, instance));
+ }
+ }
+
+ return hierarchy;
+ }
+
+ @Data
+ static class ServiceRelations {
+ private List upperServices = new ArrayList<>();
+ private List lowerServices = new ArrayList<>();
+ }
+}
diff --git a/oap-server/server-core/src/main/java/org/apache/skywalking/oap/server/core/query/type/HierarchyInstanceRelation.java b/oap-server/server-core/src/main/java/org/apache/skywalking/oap/server/core/query/type/HierarchyInstanceRelation.java
new file mode 100644
index 0000000000..9165b4a38e
--- /dev/null
+++ b/oap-server/server-core/src/main/java/org/apache/skywalking/oap/server/core/query/type/HierarchyInstanceRelation.java
@@ -0,0 +1,31 @@
+/*
+ * 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.skywalking.oap.server.core.query.type;
+
+import lombok.AllArgsConstructor;
+import lombok.Getter;
+import lombok.Setter;
+
+@Getter
+@Setter
+@AllArgsConstructor
+public class HierarchyInstanceRelation {
+ private HierarchyRelatedInstance upperInstance;
+ private HierarchyRelatedInstance lowerInstance;
+}
diff --git a/oap-server/server-core/src/main/java/org/apache/skywalking/oap/server/core/query/type/HierarchyRelatedInstance.java b/oap-server/server-core/src/main/java/org/apache/skywalking/oap/server/core/query/type/HierarchyRelatedInstance.java
new file mode 100644
index 0000000000..62fae29f28
--- /dev/null
+++ b/oap-server/server-core/src/main/java/org/apache/skywalking/oap/server/core/query/type/HierarchyRelatedInstance.java
@@ -0,0 +1,32 @@
+/*
+ * 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.skywalking.oap.server.core.query.type;
+
+import lombok.EqualsAndHashCode;
+import lombok.Getter;
+import lombok.Setter;
+
+@Getter
+@Setter
+@EqualsAndHashCode
+public class HierarchyRelatedInstance {
+ private String id;
+ private String name;
+ private String layer;
+}
diff --git a/oap-server/server-core/src/main/java/org/apache/skywalking/oap/server/core/query/type/HierarchyRelatedService.java b/oap-server/server-core/src/main/java/org/apache/skywalking/oap/server/core/query/type/HierarchyRelatedService.java
new file mode 100644
index 0000000000..ab65a98dd6
--- /dev/null
+++ b/oap-server/server-core/src/main/java/org/apache/skywalking/oap/server/core/query/type/HierarchyRelatedService.java
@@ -0,0 +1,32 @@
+/*
+ * 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.skywalking.oap.server.core.query.type;
+
+import lombok.EqualsAndHashCode;
+import lombok.Getter;
+import lombok.Setter;
+
+@Getter
+@Setter
+@EqualsAndHashCode(of = {"id", "layer"})
+public class HierarchyRelatedService {
+ private String id;
+ private String name;
+ private String layer;
+}
diff --git a/oap-server/server-core/src/main/java/org/apache/skywalking/oap/server/core/query/type/HierarchyServiceRelation.java b/oap-server/server-core/src/main/java/org/apache/skywalking/oap/server/core/query/type/HierarchyServiceRelation.java
new file mode 100644
index 0000000000..b0ecc8127b
--- /dev/null
+++ b/oap-server/server-core/src/main/java/org/apache/skywalking/oap/server/core/query/type/HierarchyServiceRelation.java
@@ -0,0 +1,35 @@
+/*
+ * 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.skywalking.oap.server.core.query.type;
+
+import lombok.AllArgsConstructor;
+import lombok.EqualsAndHashCode;
+import lombok.Getter;
+import lombok.NoArgsConstructor;
+import lombok.Setter;
+
+@Getter
+@Setter
+@EqualsAndHashCode
+@AllArgsConstructor
+@NoArgsConstructor
+public class HierarchyServiceRelation {
+ private HierarchyRelatedService upperService;
+ private HierarchyRelatedService lowerService;
+}
diff --git a/oap-server/server-core/src/main/java/org/apache/skywalking/oap/server/core/query/type/InstanceHierarchy.java b/oap-server/server-core/src/main/java/org/apache/skywalking/oap/server/core/query/type/InstanceHierarchy.java
new file mode 100644
index 0000000000..67476d74e0
--- /dev/null
+++ b/oap-server/server-core/src/main/java/org/apache/skywalking/oap/server/core/query/type/InstanceHierarchy.java
@@ -0,0 +1,32 @@
+/*
+ * 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.skywalking.oap.server.core.query.type;
+
+import java.util.ArrayList;
+import java.util.List;
+import lombok.EqualsAndHashCode;
+import lombok.Getter;
+import lombok.Setter;
+
+@Getter
+@Setter
+@EqualsAndHashCode
+public class InstanceHierarchy {
+ private List relations = new ArrayList<>();
+}
diff --git a/oap-server/server-core/src/main/java/org/apache/skywalking/oap/server/core/query/type/ServiceHierarchy.java b/oap-server/server-core/src/main/java/org/apache/skywalking/oap/server/core/query/type/ServiceHierarchy.java
new file mode 100644
index 0000000000..4cd64da27e
--- /dev/null
+++ b/oap-server/server-core/src/main/java/org/apache/skywalking/oap/server/core/query/type/ServiceHierarchy.java
@@ -0,0 +1,32 @@
+/*
+ * 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.skywalking.oap.server.core.query.type;
+
+import java.util.ArrayList;
+import java.util.List;
+import lombok.EqualsAndHashCode;
+import lombok.Getter;
+import lombok.Setter;
+
+@Getter
+@Setter
+@EqualsAndHashCode
+public class ServiceHierarchy {
+ private List relations = new ArrayList<>();
+}
diff --git a/oap-server/server-core/src/main/java/org/apache/skywalking/oap/server/core/source/DefaultScopeDefine.java b/oap-server/server-core/src/main/java/org/apache/skywalking/oap/server/core/source/DefaultScopeDefine.java
index f6995e2eaa..5cf807eac2 100644
--- a/oap-server/server-core/src/main/java/org/apache/skywalking/oap/server/core/source/DefaultScopeDefine.java
+++ b/oap-server/server-core/src/main/java/org/apache/skywalking/oap/server/core/source/DefaultScopeDefine.java
@@ -127,6 +127,8 @@ public class DefaultScopeDefine {
public static final int UI_MENU = 69;
+ public static final int SERVICE_HIERARCHY_RELATION = 70;
+ public static final int INSTANCE_HIERARCHY_RELATION = 71;
/**
* Catalog of scope, the metrics processor could use this to group all generated metrics by oal rt.
*/
diff --git a/oap-server/server-core/src/main/java/org/apache/skywalking/oap/server/core/storage/StorageModule.java b/oap-server/server-core/src/main/java/org/apache/skywalking/oap/server/core/storage/StorageModule.java
index d599e74a88..7fc6af038c 100644
--- a/oap-server/server-core/src/main/java/org/apache/skywalking/oap/server/core/storage/StorageModule.java
+++ b/oap-server/server-core/src/main/java/org/apache/skywalking/oap/server/core/storage/StorageModule.java
@@ -33,6 +33,7 @@ import org.apache.skywalking.oap.server.core.storage.profiling.ebpf.IEBPFProfili
import org.apache.skywalking.oap.server.core.storage.profiling.ebpf.IEBPFProfilingScheduleDAO;
import org.apache.skywalking.oap.server.core.storage.profiling.ebpf.IEBPFProfilingTaskDAO;
import org.apache.skywalking.oap.server.core.storage.query.IEventQueryDAO;
+import org.apache.skywalking.oap.server.core.storage.query.IHierarchyQueryDAO;
import org.apache.skywalking.oap.server.core.storage.query.ILogQueryDAO;
import org.apache.skywalking.oap.server.core.storage.query.IMetadataQueryDAO;
import org.apache.skywalking.oap.server.core.storage.query.IMetricsQueryDAO;
@@ -86,7 +87,8 @@ public class StorageModule extends ModuleDefine {
IServiceLabelDAO.class,
ITagAutoCompleteQueryDAO.class,
IZipkinQueryDAO.class,
- ISpanAttachedEventQueryDAO.class
+ ISpanAttachedEventQueryDAO.class,
+ IHierarchyQueryDAO.class
};
}
}
diff --git a/oap-server/server-core/src/main/java/org/apache/skywalking/oap/server/core/storage/query/IHierarchyQueryDAO.java b/oap-server/server-core/src/main/java/org/apache/skywalking/oap/server/core/storage/query/IHierarchyQueryDAO.java
new file mode 100644
index 0000000000..4682394baa
--- /dev/null
+++ b/oap-server/server-core/src/main/java/org/apache/skywalking/oap/server/core/storage/query/IHierarchyQueryDAO.java
@@ -0,0 +1,33 @@
+/*
+ * 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.skywalking.oap.server.core.storage.query;
+
+import java.util.List;
+import org.apache.skywalking.oap.server.core.hierarchy.instance.InstanceHierarchyRelationTraffic;
+import org.apache.skywalking.oap.server.core.hierarchy.service.ServiceHierarchyRelationTraffic;
+import org.apache.skywalking.oap.server.core.storage.DAO;
+
+public interface IHierarchyQueryDAO extends DAO {
+ List readAllServiceHierarchyRelations() throws Exception;
+
+ /**
+ * Return the given instance's hierarchy.
+ */
+ List readInstanceHierarchyRelations(String instanceId, String layer) throws Exception;
+}
diff --git a/oap-server/server-core/src/test/java/org/apache/skywalking/oap/server/core/CoreModuleTest.java b/oap-server/server-core/src/test/java/org/apache/skywalking/oap/server/core/CoreModuleTest.java
index bbd6b37c3e..95e1063a02 100644
--- a/oap-server/server-core/src/test/java/org/apache/skywalking/oap/server/core/CoreModuleTest.java
+++ b/oap-server/server-core/src/test/java/org/apache/skywalking/oap/server/core/CoreModuleTest.java
@@ -26,6 +26,6 @@ public class CoreModuleTest {
public void testOpenServiceList() {
CoreModule coreModule = new CoreModule();
- Assertions.assertEquals(41, coreModule.services().length);
+ Assertions.assertEquals(44, coreModule.services().length);
}
}
diff --git a/oap-server/server-query-plugin/query-graphql-plugin/src/main/java/org/apache/skywalking/oap/query/graphql/GraphQLQueryProvider.java b/oap-server/server-query-plugin/query-graphql-plugin/src/main/java/org/apache/skywalking/oap/query/graphql/GraphQLQueryProvider.java
index e0c9ead3ab..eac7b99798 100644
--- a/oap-server/server-query-plugin/query-graphql-plugin/src/main/java/org/apache/skywalking/oap/query/graphql/GraphQLQueryProvider.java
+++ b/oap-server/server-query-plugin/query-graphql-plugin/src/main/java/org/apache/skywalking/oap/query/graphql/GraphQLQueryProvider.java
@@ -32,6 +32,7 @@ import org.apache.skywalking.oap.query.graphql.resolver.EBPFProcessProfilingMuta
import org.apache.skywalking.oap.query.graphql.resolver.EBPFProcessProfilingQuery;
import org.apache.skywalking.oap.query.graphql.resolver.EventQuery;
import org.apache.skywalking.oap.query.graphql.resolver.HealthQuery;
+import org.apache.skywalking.oap.query.graphql.resolver.HierarchyQuery;
import org.apache.skywalking.oap.query.graphql.resolver.LogQuery;
import org.apache.skywalking.oap.query.graphql.resolver.LogTestQuery;
import org.apache.skywalking.oap.query.graphql.resolver.MetadataQuery;
@@ -143,7 +144,8 @@ public class GraphQLQueryProvider extends ModuleProvider {
.file("query-protocol/continuous-profiling.graphqls")
.resolvers(new ContinuousProfilingQuery(getManager()), new ContinuousProfilingMutation(getManager()))
.file("query-protocol/record.graphqls")
- .resolvers(new RecordsQuery(getManager()));
+ .resolvers(new RecordsQuery(getManager()))
+ .file("query-protocol/hierarchy.graphqls").resolvers(new HierarchyQuery(getManager()));
if (config.isEnableOnDemandPodLog()) {
schemaBuilder
diff --git a/oap-server/server-query-plugin/query-graphql-plugin/src/main/java/org/apache/skywalking/oap/query/graphql/resolver/HierarchyQuery.java b/oap-server/server-query-plugin/query-graphql-plugin/src/main/java/org/apache/skywalking/oap/query/graphql/resolver/HierarchyQuery.java
new file mode 100644
index 0000000000..57241ee7d0
--- /dev/null
+++ b/oap-server/server-query-plugin/query-graphql-plugin/src/main/java/org/apache/skywalking/oap/query/graphql/resolver/HierarchyQuery.java
@@ -0,0 +1,52 @@
+/*
+ * 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.skywalking.oap.query.graphql.resolver;
+
+import graphql.kickstart.tools.GraphQLQueryResolver;
+import org.apache.skywalking.oap.server.core.CoreModule;
+import org.apache.skywalking.oap.server.core.query.HierarchyQueryService;
+import org.apache.skywalking.oap.server.core.query.type.InstanceHierarchy;
+import org.apache.skywalking.oap.server.core.query.type.ServiceHierarchy;
+import org.apache.skywalking.oap.server.library.module.ModuleManager;
+
+public class HierarchyQuery implements GraphQLQueryResolver {
+ private final ModuleManager moduleManager;
+ private HierarchyQueryService hierarchyQueryService;
+
+ public HierarchyQuery(ModuleManager moduleManager) {
+ this.moduleManager = moduleManager;
+ }
+
+ private HierarchyQueryService getHierarchyQueryService() {
+ if (hierarchyQueryService == null) {
+ this.hierarchyQueryService = moduleManager.find(CoreModule.NAME)
+ .provider()
+ .getService(HierarchyQueryService.class);
+ }
+ return hierarchyQueryService;
+ }
+
+ public ServiceHierarchy getServiceHierarchy(String serviceId, String layer) throws Exception {
+ return getHierarchyQueryService().getServiceHierarchy(serviceId, layer);
+ }
+
+ public InstanceHierarchy getInstanceHierarchy(String instanceId, String layer) throws Exception {
+ return getHierarchyQueryService().getInstanceHierarchy(instanceId, layer);
+ }
+}
diff --git a/oap-server/server-query-plugin/query-graphql-plugin/src/main/resources/query-protocol b/oap-server/server-query-plugin/query-graphql-plugin/src/main/resources/query-protocol
index e08b0a7825..b5fe539bce 160000
--- a/oap-server/server-query-plugin/query-graphql-plugin/src/main/resources/query-protocol
+++ b/oap-server/server-query-plugin/query-graphql-plugin/src/main/resources/query-protocol
@@ -1 +1 @@
-Subproject commit e08b0a78258a4c56e67076424add6bc2d0a1dd08
+Subproject commit b5fe539bce21d464e5dff360a691b79729bf065b
diff --git a/oap-server/server-starter/src/main/resources/hierarchy-definition.yml b/oap-server/server-starter/src/main/resources/hierarchy-definition.yml
new file mode 100644
index 0000000000..2345497155
--- /dev/null
+++ b/oap-server/server-starter/src/main/resources/hierarchy-definition.yml
@@ -0,0 +1,29 @@
+# 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.
+
+# Define the hierarchy of layers, the layers under the specific layer are lower of the layer.
+# All the layers are defined in the file `org.apache.skywalking.oap.server.core.analysis.Layers.java`.
+
+MESH:
+ - MESH_DP
+ - K8S_SERVICE
+ - K8S
+
+MESH_DP:
+ - K8S_SERVICE
+ - K8S
+
+K8S_SERVICE:
+ - K8S
diff --git a/oap-server/server-storage-plugin/storage-banyandb-plugin/src/main/java/org/apache/skywalking/oap/server/storage/plugin/banyandb/BanyanDBStorageProvider.java b/oap-server/server-storage-plugin/storage-banyandb-plugin/src/main/java/org/apache/skywalking/oap/server/storage/plugin/banyandb/BanyanDBStorageProvider.java
index eed76a31b4..aa3b9a88c6 100644
--- a/oap-server/server-storage-plugin/storage-banyandb-plugin/src/main/java/org/apache/skywalking/oap/server/storage/plugin/banyandb/BanyanDBStorageProvider.java
+++ b/oap-server/server-storage-plugin/storage-banyandb-plugin/src/main/java/org/apache/skywalking/oap/server/storage/plugin/banyandb/BanyanDBStorageProvider.java
@@ -43,6 +43,7 @@ import org.apache.skywalking.oap.server.core.storage.query.IAggregationQueryDAO;
import org.apache.skywalking.oap.server.core.storage.query.IAlarmQueryDAO;
import org.apache.skywalking.oap.server.core.storage.query.IBrowserLogQueryDAO;
import org.apache.skywalking.oap.server.core.storage.query.IEventQueryDAO;
+import org.apache.skywalking.oap.server.core.storage.query.IHierarchyQueryDAO;
import org.apache.skywalking.oap.server.core.storage.query.ILogQueryDAO;
import org.apache.skywalking.oap.server.core.storage.query.IMetadataQueryDAO;
import org.apache.skywalking.oap.server.core.storage.query.IMetricsQueryDAO;
@@ -58,6 +59,7 @@ import org.apache.skywalking.oap.server.library.module.ModuleStartException;
import org.apache.skywalking.oap.server.library.module.ServiceNotProvidedException;
import org.apache.skywalking.oap.server.storage.plugin.banyandb.measure.BanyanDBEBPFProfilingScheduleQueryDAO;
import org.apache.skywalking.oap.server.storage.plugin.banyandb.measure.BanyanDBEventQueryDAO;
+import org.apache.skywalking.oap.server.storage.plugin.banyandb.measure.BanyanDBHierarchyQueryDAO;
import org.apache.skywalking.oap.server.storage.plugin.banyandb.measure.BanyanDBMetadataQueryDAO;
import org.apache.skywalking.oap.server.storage.plugin.banyandb.measure.BanyanDBMetricsQueryDAO;
import org.apache.skywalking.oap.server.storage.plugin.banyandb.measure.BanyanDBNetworkAddressAliasDAO;
@@ -163,6 +165,7 @@ public class BanyanDBStorageProvider extends ModuleProvider {
this.registerServiceImplementation(IRecordsQueryDAO.class, new BanyanDBRecordsQueryDAO(client));
this.registerServiceImplementation(IZipkinQueryDAO.class, new BanyanDBZipkinQueryDAO(client));
this.registerServiceImplementation(ISpanAttachedEventQueryDAO.class, new BanyanDBSpanAttachedEventQueryDAO(client));
+ this.registerServiceImplementation(IHierarchyQueryDAO.class, new BanyanDBHierarchyQueryDAO(client));
}
@Override
diff --git a/oap-server/server-storage-plugin/storage-banyandb-plugin/src/main/java/org/apache/skywalking/oap/server/storage/plugin/banyandb/measure/BanyanDBHierarchyQueryDAO.java b/oap-server/server-storage-plugin/storage-banyandb-plugin/src/main/java/org/apache/skywalking/oap/server/storage/plugin/banyandb/measure/BanyanDBHierarchyQueryDAO.java
new file mode 100644
index 0000000000..c8fc1891ea
--- /dev/null
+++ b/oap-server/server-storage-plugin/storage-banyandb-plugin/src/main/java/org/apache/skywalking/oap/server/storage/plugin/banyandb/measure/BanyanDBHierarchyQueryDAO.java
@@ -0,0 +1,123 @@
+/*
+ * 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.skywalking.oap.server.storage.plugin.banyandb.measure;
+
+import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.Lists;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import java.util.Set;
+import org.apache.skywalking.banyandb.v1.client.AbstractCriteria;
+import org.apache.skywalking.banyandb.v1.client.DataPoint;
+import org.apache.skywalking.banyandb.v1.client.MeasureQuery;
+import org.apache.skywalking.banyandb.v1.client.MeasureQueryResponse;
+import org.apache.skywalking.oap.server.core.analysis.DownSampling;
+import org.apache.skywalking.oap.server.core.analysis.Layer;
+import org.apache.skywalking.oap.server.core.hierarchy.instance.InstanceHierarchyRelationTraffic;
+import org.apache.skywalking.oap.server.core.hierarchy.service.ServiceHierarchyRelationTraffic;
+import org.apache.skywalking.oap.server.core.storage.query.IHierarchyQueryDAO;
+import org.apache.skywalking.oap.server.storage.plugin.banyandb.BanyanDBConverter;
+import org.apache.skywalking.oap.server.storage.plugin.banyandb.BanyanDBStorageClient;
+import org.apache.skywalking.oap.server.storage.plugin.banyandb.MetadataRegistry;
+import org.apache.skywalking.oap.server.storage.plugin.banyandb.stream.AbstractBanyanDBDAO;
+
+public class BanyanDBHierarchyQueryDAO extends AbstractBanyanDBDAO implements IHierarchyQueryDAO {
+ private static final Set SERVICE_HIERARCHY_RELATION_TAGS = ImmutableSet.of(
+ ServiceHierarchyRelationTraffic.SERVICE_ID,
+ ServiceHierarchyRelationTraffic.SERVICE_LAYER,
+ ServiceHierarchyRelationTraffic.RELATED_SERVICE_ID,
+ ServiceHierarchyRelationTraffic.RELATED_SERVICE_LAYER
+ );
+
+ private static final Set INSTANCE_HIERARCHY_RELATION_TAGS = ImmutableSet.of(
+ InstanceHierarchyRelationTraffic.INSTANCE_ID,
+ InstanceHierarchyRelationTraffic.SERVICE_LAYER,
+ InstanceHierarchyRelationTraffic.RELATED_INSTANCE_ID,
+ InstanceHierarchyRelationTraffic.RELATED_SERVICE_LAYER
+ );
+
+ public BanyanDBHierarchyQueryDAO(final BanyanDBStorageClient client) {
+ super(client);
+ }
+
+ @Override
+ public List readAllServiceHierarchyRelations() throws Exception {
+ MeasureQueryResponse resp = query(ServiceHierarchyRelationTraffic.INDEX_NAME,
+ SERVICE_HIERARCHY_RELATION_TAGS,
+ Collections.emptySet(), new QueryBuilder<>() {
+ @Override
+ protected void apply(MeasureQuery query) {
+ }
+ }
+ );
+
+ final List relations = new ArrayList<>();
+ MetadataRegistry.Schema schema = MetadataRegistry.INSTANCE.findMetadata(
+ ServiceHierarchyRelationTraffic.INDEX_NAME, DownSampling.Minute);
+
+ for (final DataPoint dataPoint : resp.getDataPoints()) {
+ relations.add(new ServiceHierarchyRelationTraffic.Builder().storage2Entity(
+ new BanyanDBConverter.StorageToMeasure(schema, dataPoint)));
+ }
+
+ return relations;
+ }
+
+ @Override
+ public List readInstanceHierarchyRelations(final String instanceId,
+ final String layer) throws Exception {
+ MeasureQueryResponse resp = query(InstanceHierarchyRelationTraffic.INDEX_NAME,
+ INSTANCE_HIERARCHY_RELATION_TAGS,
+ Collections.emptySet(), buildInstanceRelationsQuery(instanceId, layer)
+ );
+
+ List relations = new ArrayList<>();
+ MetadataRegistry.Schema schema = MetadataRegistry.INSTANCE.findMetadata(
+ InstanceHierarchyRelationTraffic.INDEX_NAME, DownSampling.Minute);
+ for (final DataPoint dataPoint : resp.getDataPoints()) {
+ relations.add(new InstanceHierarchyRelationTraffic.Builder().storage2Entity(
+ new BanyanDBConverter.StorageToMeasure(schema, dataPoint)));
+ }
+ return relations;
+ }
+
+ private QueryBuilder buildInstanceRelationsQuery(String instanceId, String layer) {
+ int layerValue = Layer.valueOf(layer).value();
+ return new QueryBuilder<>() {
+ @Override
+ protected void apply(MeasureQuery query) {
+ List instanceRelationsQueryConditions = new ArrayList<>(2);
+
+ instanceRelationsQueryConditions.add(
+ and(Lists.newArrayList(
+ eq(InstanceHierarchyRelationTraffic.INSTANCE_ID, instanceId),
+ eq(InstanceHierarchyRelationTraffic.SERVICE_LAYER, layerValue))
+ ));
+ instanceRelationsQueryConditions.add(
+ and(Lists.newArrayList(
+ eq(InstanceHierarchyRelationTraffic.RELATED_INSTANCE_ID, instanceId),
+ eq(InstanceHierarchyRelationTraffic.RELATED_SERVICE_LAYER, layerValue)
+ ))
+ );
+ query.criteria(or(instanceRelationsQueryConditions));
+ }
+ };
+ }
+}
diff --git a/oap-server/server-storage-plugin/storage-elasticsearch-plugin/src/main/java/org/apache/skywalking/oap/server/storage/plugin/elasticsearch/StorageModuleElasticsearchProvider.java b/oap-server/server-storage-plugin/storage-elasticsearch-plugin/src/main/java/org/apache/skywalking/oap/server/storage/plugin/elasticsearch/StorageModuleElasticsearchProvider.java
index ba608fe065..a521928dad 100644
--- a/oap-server/server-storage-plugin/storage-elasticsearch-plugin/src/main/java/org/apache/skywalking/oap/server/storage/plugin/elasticsearch/StorageModuleElasticsearchProvider.java
+++ b/oap-server/server-storage-plugin/storage-elasticsearch-plugin/src/main/java/org/apache/skywalking/oap/server/storage/plugin/elasticsearch/StorageModuleElasticsearchProvider.java
@@ -48,6 +48,7 @@ import org.apache.skywalking.oap.server.core.storage.query.IAggregationQueryDAO;
import org.apache.skywalking.oap.server.core.storage.query.IAlarmQueryDAO;
import org.apache.skywalking.oap.server.core.storage.query.IBrowserLogQueryDAO;
import org.apache.skywalking.oap.server.core.storage.query.IEventQueryDAO;
+import org.apache.skywalking.oap.server.core.storage.query.IHierarchyQueryDAO;
import org.apache.skywalking.oap.server.core.storage.query.ILogQueryDAO;
import org.apache.skywalking.oap.server.core.storage.query.IMetadataQueryDAO;
import org.apache.skywalking.oap.server.core.storage.query.IMetricsQueryDAO;
@@ -79,6 +80,7 @@ import org.apache.skywalking.oap.server.storage.plugin.elasticsearch.query.EBPFP
import org.apache.skywalking.oap.server.storage.plugin.elasticsearch.query.EBPFProfilingScheduleEsDAO;
import org.apache.skywalking.oap.server.storage.plugin.elasticsearch.query.EBPFProfilingTaskEsDAO;
import org.apache.skywalking.oap.server.storage.plugin.elasticsearch.query.ESEventQueryDAO;
+import org.apache.skywalking.oap.server.storage.plugin.elasticsearch.query.HierarchyQueryEsDAO;
import org.apache.skywalking.oap.server.storage.plugin.elasticsearch.query.LogQueryEsDAO;
import org.apache.skywalking.oap.server.storage.plugin.elasticsearch.query.MetadataQueryEsDAO;
import org.apache.skywalking.oap.server.storage.plugin.elasticsearch.query.MetricsQueryEsDAO;
@@ -254,6 +256,7 @@ public class StorageModuleElasticsearchProvider extends ModuleProvider {
ISpanAttachedEventQueryDAO.class, new SpanAttachedEventEsDAO(elasticSearchClient, config));
IndexController.INSTANCE.setLogicSharding(config.isLogicSharding());
IndexController.INSTANCE.setEnableCustomRouting(config.isEnableCustomRouting());
+ this.registerServiceImplementation(IHierarchyQueryDAO.class, new HierarchyQueryEsDAO(elasticSearchClient, config));
}
@Override
diff --git a/oap-server/server-storage-plugin/storage-elasticsearch-plugin/src/main/java/org/apache/skywalking/oap/server/storage/plugin/elasticsearch/query/HierarchyQueryEsDAO.java b/oap-server/server-storage-plugin/storage-elasticsearch-plugin/src/main/java/org/apache/skywalking/oap/server/storage/plugin/elasticsearch/query/HierarchyQueryEsDAO.java
new file mode 100644
index 0000000000..1119a9379e
--- /dev/null
+++ b/oap-server/server-storage-plugin/storage-elasticsearch-plugin/src/main/java/org/apache/skywalking/oap/server/storage/plugin/elasticsearch/query/HierarchyQueryEsDAO.java
@@ -0,0 +1,133 @@
+/*
+ * 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.skywalking.oap.server.storage.plugin.elasticsearch.query;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.function.Function;
+import org.apache.skywalking.library.elasticsearch.requests.search.BoolQueryBuilder;
+import org.apache.skywalking.library.elasticsearch.requests.search.Query;
+import org.apache.skywalking.library.elasticsearch.requests.search.Search;
+import org.apache.skywalking.library.elasticsearch.requests.search.SearchBuilder;
+import org.apache.skywalking.library.elasticsearch.response.search.SearchHit;
+import org.apache.skywalking.library.elasticsearch.response.search.SearchResponse;
+import org.apache.skywalking.oap.server.core.analysis.Layer;
+import org.apache.skywalking.oap.server.core.hierarchy.instance.InstanceHierarchyRelationTraffic;
+import org.apache.skywalking.oap.server.core.hierarchy.service.ServiceHierarchyRelationTraffic;
+import org.apache.skywalking.oap.server.core.storage.query.IHierarchyQueryDAO;
+import org.apache.skywalking.oap.server.library.client.elasticsearch.ElasticSearchClient;
+import org.apache.skywalking.oap.server.library.client.elasticsearch.ElasticSearchScroller;
+import org.apache.skywalking.oap.server.storage.plugin.elasticsearch.StorageModuleElasticsearchConfig;
+import org.apache.skywalking.oap.server.storage.plugin.elasticsearch.base.ElasticSearchConverter;
+import org.apache.skywalking.oap.server.storage.plugin.elasticsearch.base.EsDAO;
+import org.apache.skywalking.oap.server.storage.plugin.elasticsearch.base.IndexController;
+
+public class HierarchyQueryEsDAO extends EsDAO implements IHierarchyQueryDAO {
+ private final int queryMaxSize;
+ private final int scrollingBatchSize;
+
+ protected final Function serviceRelationsFunction = hit -> {
+ final var sourceAsMap = hit.getSource();
+ final var builder = new ServiceHierarchyRelationTraffic.Builder();
+ return builder.storage2Entity(
+ new ElasticSearchConverter.ToEntity(ServiceHierarchyRelationTraffic.INDEX_NAME, sourceAsMap));
+ };
+
+ protected final Function instanceRelationsFunction = hit -> {
+ final var sourceAsMap = hit.getSource();
+ final var builder = new InstanceHierarchyRelationTraffic.Builder();
+ return builder.storage2Entity(
+ new ElasticSearchConverter.ToEntity(InstanceHierarchyRelationTraffic.INDEX_NAME, sourceAsMap));
+ };
+
+ public HierarchyQueryEsDAO(final ElasticSearchClient client,
+ final StorageModuleElasticsearchConfig config) {
+ super(client);
+ this.queryMaxSize = config.getMetadataQueryMaxSize();
+ this.scrollingBatchSize = config.getScrollingBatchSize();
+ }
+
+ @Override
+ public List readAllServiceHierarchyRelations() {
+ final String index =
+ IndexController.LogicIndicesRegister.getPhysicalTableName(ServiceHierarchyRelationTraffic.INDEX_NAME);
+
+ final int batchSize = Math.min(queryMaxSize, scrollingBatchSize);
+ final BoolQueryBuilder query = Query.bool();
+ final SearchBuilder search = Search.builder().query(query).size(batchSize);
+ if (IndexController.LogicIndicesRegister.isMergedTable(ServiceHierarchyRelationTraffic.INDEX_NAME)) {
+ query.must(Query.term(
+ IndexController.LogicIndicesRegister.METRIC_TABLE_NAME,
+ ServiceHierarchyRelationTraffic.INDEX_NAME
+ ));
+ }
+
+ final var scroller = ElasticSearchScroller
+ .builder()
+ .client(getClient())
+ .search(search.build())
+ .index(index)
+ .queryMaxSize(queryMaxSize)
+ .resultConverter(serviceRelationsFunction)
+ .build();
+ return scroller.scroll();
+ }
+
+ @Override
+ public List readInstanceHierarchyRelations(final String instanceId, final String layer) {
+ final String index =
+ IndexController.LogicIndicesRegister.getPhysicalTableName(InstanceHierarchyRelationTraffic.INDEX_NAME);
+ int layerValue = Layer.valueOf(layer).value();
+ final BoolQueryBuilder instanceSide =
+ Query.bool()
+ .must(Query.term(InstanceHierarchyRelationTraffic.INSTANCE_ID, instanceId))
+ .must(Query.term(InstanceHierarchyRelationTraffic.SERVICE_LAYER, layerValue));
+
+ final BoolQueryBuilder relatedInstanceSide =
+ Query.bool()
+ .must(Query.term(InstanceHierarchyRelationTraffic.RELATED_INSTANCE_ID, instanceId))
+ .must(Query.term(InstanceHierarchyRelationTraffic.RELATED_SERVICE_LAYER, layerValue));
+
+ final BoolQueryBuilder instanceQuery =
+ Query.bool()
+ .should(instanceSide)
+ .should(relatedInstanceSide);
+
+ final BoolQueryBuilder query =
+ Query.bool()
+ .must(instanceQuery);
+ if (IndexController.LogicIndicesRegister.isMergedTable(InstanceHierarchyRelationTraffic.INDEX_NAME)) {
+ query.must(Query.term(
+ IndexController.LogicIndicesRegister.METRIC_TABLE_NAME,
+ InstanceHierarchyRelationTraffic.INDEX_NAME
+ ));
+ }
+ final SearchBuilder search = Search.builder().query(query);
+ final SearchResponse response = getClient().search(index, search.build());
+ return buildInstanceHierarchyRelations(response);
+ }
+
+ private List buildInstanceHierarchyRelations(SearchResponse response) {
+ List relations = new ArrayList<>();
+ for (SearchHit searchHit : response.getHits()) {
+ relations.add(instanceRelationsFunction.apply(searchHit));
+ }
+ return relations;
+ }
+}
diff --git a/oap-server/server-storage-plugin/storage-jdbc-hikaricp-plugin/src/main/java/org/apache/skywalking/oap/server/storage/plugin/jdbc/common/JDBCStorageProvider.java b/oap-server/server-storage-plugin/storage-jdbc-hikaricp-plugin/src/main/java/org/apache/skywalking/oap/server/storage/plugin/jdbc/common/JDBCStorageProvider.java
index 99ce9f2fc6..cb72bc6cb3 100644
--- a/oap-server/server-storage-plugin/storage-jdbc-hikaricp-plugin/src/main/java/org/apache/skywalking/oap/server/storage/plugin/jdbc/common/JDBCStorageProvider.java
+++ b/oap-server/server-storage-plugin/storage-jdbc-hikaricp-plugin/src/main/java/org/apache/skywalking/oap/server/storage/plugin/jdbc/common/JDBCStorageProvider.java
@@ -42,6 +42,7 @@ import org.apache.skywalking.oap.server.core.storage.query.IAggregationQueryDAO;
import org.apache.skywalking.oap.server.core.storage.query.IAlarmQueryDAO;
import org.apache.skywalking.oap.server.core.storage.query.IBrowserLogQueryDAO;
import org.apache.skywalking.oap.server.core.storage.query.IEventQueryDAO;
+import org.apache.skywalking.oap.server.core.storage.query.IHierarchyQueryDAO;
import org.apache.skywalking.oap.server.core.storage.query.ILogQueryDAO;
import org.apache.skywalking.oap.server.core.storage.query.IMetadataQueryDAO;
import org.apache.skywalking.oap.server.core.storage.query.IMetricsQueryDAO;
@@ -65,6 +66,7 @@ import org.apache.skywalking.oap.server.storage.plugin.jdbc.common.dao.JDBCEBPFP
import org.apache.skywalking.oap.server.storage.plugin.jdbc.common.dao.JDBCEBPFProfilingScheduleDAO;
import org.apache.skywalking.oap.server.storage.plugin.jdbc.common.dao.JDBCEBPFProfilingTaskDAO;
import org.apache.skywalking.oap.server.storage.plugin.jdbc.common.dao.JDBCEventQueryDAO;
+import org.apache.skywalking.oap.server.storage.plugin.jdbc.common.dao.JDBCHierarchyQueryDAO;
import org.apache.skywalking.oap.server.storage.plugin.jdbc.common.dao.JDBCHistoryDeleteDAO;
import org.apache.skywalking.oap.server.storage.plugin.jdbc.common.dao.JDBCLogQueryDAO;
import org.apache.skywalking.oap.server.storage.plugin.jdbc.common.dao.JDBCMetadataQueryDAO;
@@ -221,6 +223,8 @@ public abstract class JDBCStorageProvider extends ModuleProvider {
this.registerServiceImplementation(
ISpanAttachedEventQueryDAO.class,
new JDBCSpanAttachedEventQueryDAO(jdbcClient, tableHelper));
+ this.registerServiceImplementation(IHierarchyQueryDAO.class,
+ new JDBCHierarchyQueryDAO(jdbcClient, config.getMetadataQueryMaxSize(), tableHelper));
}
@Override
diff --git a/oap-server/server-storage-plugin/storage-jdbc-hikaricp-plugin/src/main/java/org/apache/skywalking/oap/server/storage/plugin/jdbc/common/dao/JDBCHierarchyQueryDAO.java b/oap-server/server-storage-plugin/storage-jdbc-hikaricp-plugin/src/main/java/org/apache/skywalking/oap/server/storage/plugin/jdbc/common/dao/JDBCHierarchyQueryDAO.java
new file mode 100644
index 0000000000..2950fb76e8
--- /dev/null
+++ b/oap-server/server-storage-plugin/storage-jdbc-hikaricp-plugin/src/main/java/org/apache/skywalking/oap/server/storage/plugin/jdbc/common/dao/JDBCHierarchyQueryDAO.java
@@ -0,0 +1,142 @@
+/*
+ * 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.skywalking.oap.server.storage.plugin.jdbc.common.dao;
+
+import java.sql.ResultSet;
+import java.sql.SQLException;
+import java.util.ArrayList;
+import java.util.List;
+import org.apache.skywalking.oap.server.core.analysis.Layer;
+import org.apache.skywalking.oap.server.core.hierarchy.instance.InstanceHierarchyRelationTraffic;
+import org.apache.skywalking.oap.server.core.hierarchy.service.ServiceHierarchyRelationTraffic;
+import org.apache.skywalking.oap.server.core.query.type.Call;
+import org.apache.skywalking.oap.server.core.storage.query.IHierarchyQueryDAO;
+import org.apache.skywalking.oap.server.library.client.jdbc.hikaricp.JDBCClient;
+import org.apache.skywalking.oap.server.storage.plugin.jdbc.common.JDBCEntityConverters;
+import org.apache.skywalking.oap.server.storage.plugin.jdbc.common.JDBCTableInstaller;
+import org.apache.skywalking.oap.server.storage.plugin.jdbc.common.SQLAndParameters;
+import org.apache.skywalking.oap.server.storage.plugin.jdbc.common.TableHelper;
+
+import static java.util.stream.Collectors.toList;
+
+public class JDBCHierarchyQueryDAO implements IHierarchyQueryDAO {
+ private final JDBCClient jdbcClient;
+ private final int queryMaxSize;
+ private final TableHelper tableHelper;
+
+ public JDBCHierarchyQueryDAO(JDBCClient jdbcClient, int metadataQueryMaxSize, TableHelper tableHelper) {
+ this.jdbcClient = jdbcClient;
+ this.queryMaxSize = metadataQueryMaxSize * 2;
+ this.tableHelper = tableHelper;
+ }
+
+ @Override
+ public List readAllServiceHierarchyRelations() throws Exception {
+ final var results = new ArrayList();
+ final var tables = tableHelper.getTablesWithinTTL(ServiceHierarchyRelationTraffic.INDEX_NAME);
+
+ for (final var table : tables) {
+ final var sqlAndParameters = buildSQLForReadAllServiceRelations(table);
+ results.addAll(jdbcClient.executeQuery(
+ sqlAndParameters.sql(),
+ this::buildServiceRelations,
+ sqlAndParameters.parameters()
+ )
+ );
+ }
+ return results
+ .stream()
+ .limit(queryMaxSize)
+ .collect(toList());
+ }
+
+ @Override
+ public List readInstanceHierarchyRelations(final String instanceId,
+ final String layer) throws Exception {
+ final var results = new ArrayList();
+ final var tables = tableHelper.getTablesWithinTTL(InstanceHierarchyRelationTraffic.INDEX_NAME);
+ final var layerValue = Layer.valueOf(layer).value();
+ List calls = new ArrayList<>();
+
+ for (final var table : tables) {
+ final var sqlAndParameters = buildSQLForReadInstanceRelations(table, instanceId, layerValue);
+ results.addAll(jdbcClient.executeQuery(
+ sqlAndParameters.sql(),
+ this::buildInstanceRelations,
+ sqlAndParameters.parameters()
+ )
+ );
+ }
+ return results;
+ }
+
+ protected SQLAndParameters buildSQLForReadAllServiceRelations(String table) {
+ final var sql = new StringBuilder();
+ final var parameters = new ArrayList<>(5);
+ sql.append("select * from ").append(table)
+ .append(" where ").append(JDBCTableInstaller.TABLE_COLUMN).append(" = ? ");
+ parameters.add(ServiceHierarchyRelationTraffic.INDEX_NAME);
+ sql.append(" limit ").append(queryMaxSize);
+ return new SQLAndParameters(sql.toString(), parameters);
+ }
+
+ protected SQLAndParameters buildSQLForReadInstanceRelations(String table, String instanceId, int layerValue) {
+ final var sql = new StringBuilder();
+ final var parameters = new ArrayList<>(5) {
+ {
+ add(InstanceHierarchyRelationTraffic.INDEX_NAME);
+ add(instanceId);
+ add(layerValue);
+ add(instanceId);
+ add(layerValue);
+ }
+ };
+ sql.append("select * from ").append(table)
+ .append(" where ").append(JDBCTableInstaller.TABLE_COLUMN).append(" = ? ")
+ .append("and ((").append(InstanceHierarchyRelationTraffic.INSTANCE_ID)
+ .append("=? and ")
+ .append(InstanceHierarchyRelationTraffic.SERVICE_LAYER)
+ .append("=?")
+ .append(") or (")
+ .append(InstanceHierarchyRelationTraffic.RELATED_INSTANCE_ID)
+ .append("=? and ")
+ .append(InstanceHierarchyRelationTraffic.RELATED_SERVICE_LAYER)
+ .append("=?")
+ .append("))");
+ return new SQLAndParameters(sql.toString(), parameters);
+ }
+
+ private List buildServiceRelations(ResultSet resultSet) throws SQLException {
+ final var relations = new ArrayList();
+ while (resultSet.next()) {
+ relations.add(new ServiceHierarchyRelationTraffic.Builder().storage2Entity(
+ JDBCEntityConverters.toEntity(resultSet)));
+ }
+ return relations;
+ }
+
+ private List buildInstanceRelations(ResultSet resultSet) throws SQLException {
+ final var relations = new ArrayList();
+ while (resultSet.next()) {
+ relations.add(new InstanceHierarchyRelationTraffic.Builder().storage2Entity(
+ JDBCEntityConverters.toEntity(resultSet)));
+ }
+ return relations;
+ }
+}