From ce5d6a4ffb1b9631452f1fcd0eaa15450cd7302b Mon Sep 17 00:00:00 2001 From: Wan Kai Date: Wed, 27 Dec 2023 10:02:31 +0800 Subject: [PATCH] Support build Service/Instance Hierarchy and query. (#11698) --- apm-dist/src/main/assembly/binary.xml | 1 + docs/en/changes/changes.md | 1 + docs/en/swip/SWIP-1.md | 2 +- .../oap/server/core/CoreModule.java | 7 +- .../oap/server/core/CoreModuleConfig.java | 8 + .../oap/server/core/CoreModuleProvider.java | 6 + .../oap/server/core/analysis/IDManager.java | 38 ++++ .../config/HierarchyDefinitionService.java | 79 ++++++++ .../core/hierarchy/HierarchyService.java | 143 +++++++++++++ .../instance/InstanceHierarchyRelation.java | 75 +++++++ .../InstanceHierarchyRelationTraffic.java | 170 ++++++++++++++++ ...nceHierarchyTrafficRelationDispatcher.java | 35 ++++ .../service/ServiceHierarchyRelation.java | 63 ++++++ .../ServiceHierarchyRelationTraffic.java | 170 ++++++++++++++++ ...iceHierarchyTrafficRelationDispatcher.java | 35 ++++ .../core/query/HierarchyQueryService.java | 189 ++++++++++++++++++ .../query/type/HierarchyInstanceRelation.java | 31 +++ .../query/type/HierarchyRelatedInstance.java | 32 +++ .../query/type/HierarchyRelatedService.java | 32 +++ .../query/type/HierarchyServiceRelation.java | 35 ++++ .../core/query/type/InstanceHierarchy.java | 32 +++ .../core/query/type/ServiceHierarchy.java | 32 +++ .../core/source/DefaultScopeDefine.java | 2 + .../server/core/storage/StorageModule.java | 4 +- .../storage/query/IHierarchyQueryDAO.java | 33 +++ .../oap/server/core/CoreModuleTest.java | 2 +- .../query/graphql/GraphQLQueryProvider.java | 4 +- .../graphql/resolver/HierarchyQuery.java | 52 +++++ .../src/main/resources/query-protocol | 2 +- .../main/resources/hierarchy-definition.yml | 29 +++ .../banyandb/BanyanDBStorageProvider.java | 3 + .../measure/BanyanDBHierarchyQueryDAO.java | 123 ++++++++++++ .../StorageModuleElasticsearchProvider.java | 3 + .../query/HierarchyQueryEsDAO.java | 133 ++++++++++++ .../jdbc/common/JDBCStorageProvider.java | 4 + .../common/dao/JDBCHierarchyQueryDAO.java | 142 +++++++++++++ 36 files changed, 1746 insertions(+), 6 deletions(-) create mode 100644 oap-server/server-core/src/main/java/org/apache/skywalking/oap/server/core/config/HierarchyDefinitionService.java create mode 100644 oap-server/server-core/src/main/java/org/apache/skywalking/oap/server/core/hierarchy/HierarchyService.java create mode 100644 oap-server/server-core/src/main/java/org/apache/skywalking/oap/server/core/hierarchy/instance/InstanceHierarchyRelation.java create mode 100644 oap-server/server-core/src/main/java/org/apache/skywalking/oap/server/core/hierarchy/instance/InstanceHierarchyRelationTraffic.java create mode 100644 oap-server/server-core/src/main/java/org/apache/skywalking/oap/server/core/hierarchy/instance/InstanceHierarchyTrafficRelationDispatcher.java create mode 100644 oap-server/server-core/src/main/java/org/apache/skywalking/oap/server/core/hierarchy/service/ServiceHierarchyRelation.java create mode 100644 oap-server/server-core/src/main/java/org/apache/skywalking/oap/server/core/hierarchy/service/ServiceHierarchyRelationTraffic.java create mode 100644 oap-server/server-core/src/main/java/org/apache/skywalking/oap/server/core/hierarchy/service/ServiceHierarchyTrafficRelationDispatcher.java create mode 100644 oap-server/server-core/src/main/java/org/apache/skywalking/oap/server/core/query/HierarchyQueryService.java create mode 100644 oap-server/server-core/src/main/java/org/apache/skywalking/oap/server/core/query/type/HierarchyInstanceRelation.java create mode 100644 oap-server/server-core/src/main/java/org/apache/skywalking/oap/server/core/query/type/HierarchyRelatedInstance.java create mode 100644 oap-server/server-core/src/main/java/org/apache/skywalking/oap/server/core/query/type/HierarchyRelatedService.java create mode 100644 oap-server/server-core/src/main/java/org/apache/skywalking/oap/server/core/query/type/HierarchyServiceRelation.java create mode 100644 oap-server/server-core/src/main/java/org/apache/skywalking/oap/server/core/query/type/InstanceHierarchy.java create mode 100644 oap-server/server-core/src/main/java/org/apache/skywalking/oap/server/core/query/type/ServiceHierarchy.java create mode 100644 oap-server/server-core/src/main/java/org/apache/skywalking/oap/server/core/storage/query/IHierarchyQueryDAO.java create mode 100644 oap-server/server-query-plugin/query-graphql-plugin/src/main/java/org/apache/skywalking/oap/query/graphql/resolver/HierarchyQuery.java create mode 100644 oap-server/server-starter/src/main/resources/hierarchy-definition.yml create mode 100644 oap-server/server-storage-plugin/storage-banyandb-plugin/src/main/java/org/apache/skywalking/oap/server/storage/plugin/banyandb/measure/BanyanDBHierarchyQueryDAO.java create mode 100644 oap-server/server-storage-plugin/storage-elasticsearch-plugin/src/main/java/org/apache/skywalking/oap/server/storage/plugin/elasticsearch/query/HierarchyQueryEsDAO.java create mode 100644 oap-server/server-storage-plugin/storage-jdbc-hikaricp-plugin/src/main/java/org/apache/skywalking/oap/server/storage/plugin/jdbc/common/dao/JDBCHierarchyQueryDAO.java 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; + } +}