diff --git a/zipkin-server/http-query-plugin/pom.xml b/zipkin-server/http-query-plugin/pom.xml
index 2ddb0df1f6f..43db33e1803 100644
--- a/zipkin-server/http-query-plugin/pom.xml
+++ b/zipkin-server/http-query-plugin/pom.xml
@@ -23,6 +23,11 @@
zipkin-server-core
${project.version}
+
+ io.zipkin
+ zipkin-dependency
+ ${project.version}
+
\ No newline at end of file
diff --git a/zipkin-server/http-query-plugin/src/main/java/zipkin/server/query/http/HTTPQueryConfig.java b/zipkin-server/http-query-plugin/src/main/java/zipkin/server/query/http/HTTPQueryConfig.java
index 65984b58ec7..d55fac3cdad 100644
--- a/zipkin-server/http-query-plugin/src/main/java/zipkin/server/query/http/HTTPQueryConfig.java
+++ b/zipkin-server/http-query-plugin/src/main/java/zipkin/server/query/http/HTTPQueryConfig.java
@@ -42,6 +42,10 @@ public class HTTPQueryConfig extends ModuleConfig {
private boolean uiEnable = true;
private String uiBasePath = "/zipkin";
+ private boolean dependencyEnabled = true;
+ private double dependencyLowErrorRate = 0.5; // 50% of calls in error turns line yellow
+ private double dependencyHighErrorRate = 0.75;// 75% of calls in error turns line red
+
public ZipkinQueryConfig toSkyWalkingConfig() {
final ZipkinQueryConfig result = new ZipkinQueryConfig();
result.setLookback(lookback);
@@ -188,4 +192,28 @@ public String getAllowedOrigins() {
public void setAllowedOrigins(String allowedOrigins) {
this.allowedOrigins = allowedOrigins;
}
+
+ public boolean getDependencyEnabled() {
+ return dependencyEnabled;
+ }
+
+ public void setDependencyEnabled(boolean dependencyEnabled) {
+ this.dependencyEnabled = dependencyEnabled;
+ }
+
+ public double getDependencyLowErrorRate() {
+ return dependencyLowErrorRate;
+ }
+
+ public void setDependencyLowErrorRate(double dependencyLowErrorRate) {
+ this.dependencyLowErrorRate = dependencyLowErrorRate;
+ }
+
+ public double getDependencyHighErrorRate() {
+ return dependencyHighErrorRate;
+ }
+
+ public void setDependencyHighErrorRate(double dependencyHighErrorRate) {
+ this.dependencyHighErrorRate = dependencyHighErrorRate;
+ }
}
diff --git a/zipkin-server/http-query-plugin/src/main/java/zipkin/server/query/http/HTTPQueryHandler.java b/zipkin-server/http-query-plugin/src/main/java/zipkin/server/query/http/HTTPQueryHandler.java
index e3ae7ae5950..fab3dc224fc 100644
--- a/zipkin-server/http-query-plugin/src/main/java/zipkin/server/query/http/HTTPQueryHandler.java
+++ b/zipkin-server/http-query-plugin/src/main/java/zipkin/server/query/http/HTTPQueryHandler.java
@@ -14,27 +14,38 @@
package zipkin.server.query.http;
+import com.fasterxml.jackson.core.JsonFactory;
+import com.fasterxml.jackson.core.JsonGenerator;
import com.linecorp.armeria.common.AggregatedHttpResponse;
import com.linecorp.armeria.common.HttpData;
import com.linecorp.armeria.common.HttpStatus;
import com.linecorp.armeria.common.MediaType;
import com.linecorp.armeria.common.ResponseHeaders;
+import com.linecorp.armeria.server.annotation.Blocking;
+import com.linecorp.armeria.server.annotation.Get;
+import com.linecorp.armeria.server.annotation.Param;
import org.apache.skywalking.oap.query.zipkin.handler.ZipkinQueryHandler;
import org.apache.skywalking.oap.server.core.storage.StorageModule;
import org.apache.skywalking.oap.server.core.storage.query.IZipkinQueryDAO;
import org.apache.skywalking.oap.server.library.module.ModuleManager;
import org.apache.skywalking.oap.server.library.util.CollectionUtils;
import org.apache.skywalking.oap.server.library.util.StringUtil;
+import zipkin.server.dependency.IZipkinDependencyQueryDAO;
+import zipkin.server.dependency.ZipkinDependencyModule;
+import zipkin2.DependencyLink;
import zipkin2.Span;
+import zipkin2.codec.DependencyLinkBytesEncoder;
import zipkin2.codec.SpanBytesEncoder;
import java.io.IOException;
+import java.io.StringWriter;
import java.nio.ByteBuffer;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashSet;
import java.util.LinkedHashSet;
import java.util.List;
+import java.util.Optional;
import java.util.Set;
import java.util.stream.Collectors;
@@ -47,12 +58,41 @@ public class HTTPQueryHandler extends ZipkinQueryHandler {
private final ModuleManager moduleManager;
private IZipkinQueryDAO zipkinQueryDAO;
+ private IZipkinDependencyQueryDAO dependencyQueryDAO;
public HTTPQueryHandler(HTTPQueryConfig config, ModuleManager moduleManager) {
super(config.toSkyWalkingConfig(), moduleManager);
this.config = config;
this.moduleManager = moduleManager;
}
+ @Override
+ public AggregatedHttpResponse getUIConfig() throws IOException {
+ StringWriter writer = new StringWriter();
+ JsonGenerator generator = new JsonFactory().createGenerator(writer);
+ generator.writeStartObject();
+ generator.writeStringField("environment", config.getUiEnvironment());
+ generator.writeNumberField("queryLimit", config.getUiQueryLimit());
+ generator.writeNumberField("defaultLookback", config.getUiDefaultLookback());
+ generator.writeBooleanField("searchEnabled", config.getUiSearchEnabled());
+ generator.writeObjectFieldStart("dependency");
+ generator.writeBooleanField("enabled", config.getDependencyEnabled());
+ generator.writeNumberField("lowErrorRate", config.getDependencyLowErrorRate());
+ generator.writeNumberField("highErrorRate", config.getDependencyHighErrorRate());
+ generator.writeEndObject();
+ generator.writeEndObject();
+ generator.close();
+ return AggregatedHttpResponse.of(HttpStatus.OK, MediaType.JSON, HttpData.ofUtf8(writer.toString()));
+ }
+
+ @Get("/api/v2/dependencies")
+ @Blocking
+ public AggregatedHttpResponse getDependencies(
+ @Param("endTs") long endTs,
+ @Param("lookback") Optional lookback) throws IOException {
+ final List dependencies = getDependencyQueryDAO().getDependencies(endTs, lookback.orElse(config.getLookback()));
+ return response(DependencyLinkBytesEncoder.JSON_V1.encodeList(dependencies));
+ }
+
@Override
public AggregatedHttpResponse getTraceById(String traceId) throws IOException {
if (StringUtil.isEmpty(traceId)) {
@@ -129,4 +169,11 @@ private IZipkinQueryDAO getZipkinQueryDAO() {
}
return zipkinQueryDAO;
}
+
+ public IZipkinDependencyQueryDAO getDependencyQueryDAO() {
+ if (dependencyQueryDAO == null) {
+ dependencyQueryDAO = moduleManager.find(ZipkinDependencyModule.NAME).provider().getService(IZipkinDependencyQueryDAO.class);
+ }
+ return dependencyQueryDAO;
+ }
}
diff --git a/zipkin-server/pom.xml b/zipkin-server/pom.xml
index 640b8e7a4d1..8422d345faa 100644
--- a/zipkin-server/pom.xml
+++ b/zipkin-server/pom.xml
@@ -70,6 +70,8 @@
http-query-plugin
health-query-plugin
telemetry-zipkin
+ zipkin-dependency
+ zipkin-storage-ext
diff --git a/zipkin-server/server-starter/pom.xml b/zipkin-server/server-starter/pom.xml
index 9bfc243489e..b5471748397 100644
--- a/zipkin-server/server-starter/pom.xml
+++ b/zipkin-server/server-starter/pom.xml
@@ -56,6 +56,23 @@
${skywalking.version}
+
+
+ io.zipkin
+ zipkin-dependency-storage-jdbc
+ ${project.version}
+
+
+ io.zipkin
+ zipkin-dependency-storage-elasticsearch
+ ${project.version}
+
+
+ io.zipkin
+ zipkin-dependency-storage-banyandb
+ ${project.version}
+
+
io.zipkin
@@ -112,6 +129,13 @@
slf4j-api
${slf4j.version}
+
+
+
+ com.mysql
+ mysql-connector-j
+ 8.0.33
+
diff --git a/zipkin-server/server-starter/src/main/resources/application.yml b/zipkin-server/server-starter/src/main/resources/application.yml
index 8f280c886a6..10b6fc569ff 100644
--- a/zipkin-server/server-starter/src/main/resources/application.yml
+++ b/zipkin-server/server-starter/src/main/resources/application.yml
@@ -56,7 +56,7 @@ core:
restAcceptQueueSize: ${ZIPKIN_REST_QUEUE_SIZE:0}
restMaxRequestHeaderSize: ${ZIPKIN_REST_MAX_REQUEST_HEADER_SIZE:8192}
-storage:
+storage: &storage
selector: ${ZIPKIN_STORAGE:h2}
elasticsearch:
namespace: ${ZIPKIN_NAMESPACE:""}
@@ -147,6 +147,8 @@ storage:
superDatasetSegmentIntervalDays: ${ZIPKIN_STORAGE_BANYANDB_SUPER_DATASET_SEGMENT_INTERVAL_DAYS:1} # Unit is day
specificGroupSettings: ${ZIPKIN_STORAGE_BANYANDB_SPECIFIC_GROUP_SETTINGS:""} # For example, {"group1": {"blockIntervalHours": 4, "segmentIntervalDays": 1}}
+zipkin-dependency-storage-ext: *storage
+
receiver-zipkin-http:
selector: ${ZIPKIN_RECEIVER_ZIPKIN_HTTP:default}
default:
@@ -232,6 +234,9 @@ query-zipkin:
uiDefaultLookback: ${ZIPKIN_QUERY_UI_DEFAULT_LOOKBACK:900000}
uiEnable: ${ZIPKIN_QUERY_UI_ENABLE:true}
uiBasePath: ${ZIPKIN_QUERY_UI_BASE_PATH:/zipkin}
+ dependencyEnabled: ${ZIPKIN_QUERY_DEPENDENCY_ENABLED:true}
+ dependencyLowErrorRate: ${ZIPKIN_QUERY_DEPENDENCY_LOW_ERROR_RATE:0.5}
+ dependencyHighErrorRate: ${ZIPKIN_QUERY_DEPENDENCY_HIGH_ERROR_RATE:0.75}
query-health:
selector: ${ZIPKIN_QUERY_HEALTH:zipkin}
diff --git a/zipkin-server/zipkin-dependency/pom.xml b/zipkin-server/zipkin-dependency/pom.xml
new file mode 100644
index 00000000000..9bb2b1646a9
--- /dev/null
+++ b/zipkin-server/zipkin-dependency/pom.xml
@@ -0,0 +1,23 @@
+
+
+ 4.0.0
+
+ zipkin-parent
+ io.zipkin
+ 2.24.4-SNAPSHOT
+
+
+ zipkin-dependency
+ Zipkin Dependency
+
+
+
+ io.zipkin
+ zipkin-server-core
+ ${project.version}
+
+
+
+
\ No newline at end of file
diff --git a/zipkin-server/zipkin-dependency/src/main/java/org/apache/skywalking/zipkin/dependency/entity/ZipkinDependency.java b/zipkin-server/zipkin-dependency/src/main/java/org/apache/skywalking/zipkin/dependency/entity/ZipkinDependency.java
new file mode 100644
index 00000000000..af62d54dd4f
--- /dev/null
+++ b/zipkin-server/zipkin-dependency/src/main/java/org/apache/skywalking/zipkin/dependency/entity/ZipkinDependency.java
@@ -0,0 +1,176 @@
+/*
+ * Copyright 2015-2023 The OpenZipkin Authors
+ *
+ * Licensed 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.zipkin.dependency.entity;
+
+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.ScopeDeclaration;
+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;
+
+import java.util.Objects;
+
+@ScopeDeclaration(id = 10001, name = "ZipkinDependency")
+@Stream(name = ZipkinDependency.INDEX_NAME, scopeId = 10001, builder = ZipkinDependency.Builder.class, processor = MetricsStreamProcessor.class)
+@MetricsExtension(supportDownSampling = false, supportUpdate = false)
+@BanyanDB.TimestampColumn(ZipkinDependency.DAY)
+public class ZipkinDependency extends Metrics {
+ public static final String INDEX_NAME = "zipkin_dependency";
+ public static final String DAY = "analyze_day";
+ public static final String PARENT = "parent";
+ public static final String CHILD = "child";
+ public static final String CALL_COUNT = "call_count";
+ public static final String ERROR_COUNT = "error_count";
+
+ @Column(name = DAY)
+ @BanyanDB.SeriesID(index = 0)
+ private long day;
+ @Column(name = PARENT)
+ @BanyanDB.SeriesID(index = 1)
+ private String parent;
+ @Column(name = CHILD)
+ @BanyanDB.SeriesID(index = 2)
+ private String child;
+ @Column(name = CALL_COUNT)
+ @BanyanDB.MeasureField
+ private long callCount;
+ @Column(name = ERROR_COUNT)
+ @BanyanDB.MeasureField
+ private long errorCount;
+
+ @Override
+ public boolean combine(Metrics metrics) {
+ return true;
+ }
+
+ @Override
+ public void calculate() {
+ }
+
+ @Override
+ public Metrics toHour() {
+ return null;
+ }
+
+ @Override
+ public Metrics toDay() {
+ return null;
+ }
+
+ @Override
+ protected StorageID id0() {
+ return new StorageID().append(DAY, day)
+ .append(PARENT, parent).append(CHILD, child);
+ }
+
+ @Override
+ public void deserialize(RemoteData remoteData) {
+ }
+
+ @Override
+ public RemoteData.Builder serialize() {
+ // only query from the storage
+ return null;
+ }
+
+ @Override
+ public int remoteHashCode() {
+ return (int) this.day;
+ }
+
+ public static class Builder implements StorageBuilder {
+
+ @Override
+ public ZipkinDependency storage2Entity(Convert2Entity converter) {
+ final ZipkinDependency record = new ZipkinDependency();
+ record.setDay(((Number) converter.get(DAY)).longValue());
+ record.setParent((String) converter.get(PARENT));
+ record.setChild((String) converter.get(CHILD));
+ record.setCallCount(((Number) converter.get(CALL_COUNT)).longValue());
+ record.setErrorCount(((Number) converter.get(ERROR_COUNT)).longValue());
+ return record;
+ }
+
+ @Override
+ public void entity2Storage(ZipkinDependency entity, Convert2Storage converter) {
+ converter.accept(DAY, entity.getDay());
+ converter.accept(PARENT, entity.getParent());
+ converter.accept(CHILD, entity.getChild());
+ converter.accept(CALL_COUNT, entity.getCallCount());
+ converter.accept(ERROR_COUNT, entity.getErrorCount());
+ }
+ }
+
+ public Long getDay() {
+ return day;
+ }
+
+ public void setDay(Long day) {
+ this.day = day;
+ }
+
+ public String getParent() {
+ return parent;
+ }
+
+ public void setParent(String parent) {
+ this.parent = parent;
+ }
+
+ public String getChild() {
+ return child;
+ }
+
+ public void setChild(String child) {
+ this.child = child;
+ }
+
+ public Long getCallCount() {
+ return callCount;
+ }
+
+ public void setCallCount(Long callCount) {
+ this.callCount = callCount;
+ }
+
+ public Long getErrorCount() {
+ return errorCount;
+ }
+
+ public void setErrorCount(Long errorCount) {
+ this.errorCount = errorCount;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (!(o instanceof ZipkinDependency)) return false;
+ if (!super.equals(o)) return false;
+ ZipkinDependency that = (ZipkinDependency) o;
+ return getDay() == that.getDay() && Objects.equals(getParent(), that.getParent()) && Objects.equals(getChild(), that.getChild());
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(super.hashCode(), getDay(), getParent(), getChild());
+ }
+}
diff --git a/zipkin-server/zipkin-dependency/src/main/java/zipkin/server/dependency/IZipkinDependencyQueryDAO.java b/zipkin-server/zipkin-dependency/src/main/java/zipkin/server/dependency/IZipkinDependencyQueryDAO.java
new file mode 100644
index 00000000000..10586b2e31e
--- /dev/null
+++ b/zipkin-server/zipkin-dependency/src/main/java/zipkin/server/dependency/IZipkinDependencyQueryDAO.java
@@ -0,0 +1,26 @@
+/*
+ * Copyright 2015-2023 The OpenZipkin Authors
+ *
+ * Licensed 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 zipkin.server.dependency;
+
+import org.apache.skywalking.oap.server.library.module.Service;
+import zipkin2.DependencyLink;
+
+import java.io.IOException;
+import java.util.List;
+
+public interface IZipkinDependencyQueryDAO extends Service {
+
+ List getDependencies(long endTs, long lookback) throws IOException;
+}
diff --git a/zipkin-server/zipkin-dependency/src/main/java/zipkin/server/dependency/ZipkinDependencyModule.java b/zipkin-server/zipkin-dependency/src/main/java/zipkin/server/dependency/ZipkinDependencyModule.java
new file mode 100644
index 00000000000..1680003eb6a
--- /dev/null
+++ b/zipkin-server/zipkin-dependency/src/main/java/zipkin/server/dependency/ZipkinDependencyModule.java
@@ -0,0 +1,30 @@
+/*
+ * Copyright 2015-2023 The OpenZipkin Authors
+ *
+ * Licensed 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 zipkin.server.dependency;
+
+import org.apache.skywalking.oap.server.library.module.ModuleDefine;
+
+public class ZipkinDependencyModule extends ModuleDefine {
+ public static final String NAME = "zipkin-dependency-storage-ext";
+
+ public ZipkinDependencyModule() {
+ super(NAME);
+ }
+
+ @Override
+ public Class[] services() {
+ return new Class[] {IZipkinDependencyQueryDAO.class};
+ }
+}
diff --git a/zipkin-server/zipkin-dependency/src/main/resources/META-INF/services/org.apache.skywalking.oap.server.library.module.ModuleDefine b/zipkin-server/zipkin-dependency/src/main/resources/META-INF/services/org.apache.skywalking.oap.server.library.module.ModuleDefine
new file mode 100644
index 00000000000..578ab0f83a8
--- /dev/null
+++ b/zipkin-server/zipkin-dependency/src/main/resources/META-INF/services/org.apache.skywalking.oap.server.library.module.ModuleDefine
@@ -0,0 +1,19 @@
+#
+# 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.
+#
+#
+
+zipkin.server.dependency.ZipkinDependencyModule
\ No newline at end of file
diff --git a/zipkin-server/zipkin-storage-ext/pom.xml b/zipkin-server/zipkin-storage-ext/pom.xml
new file mode 100644
index 00000000000..66c0033b42c
--- /dev/null
+++ b/zipkin-server/zipkin-storage-ext/pom.xml
@@ -0,0 +1,30 @@
+
+
+ 4.0.0
+ pom
+
+ zipkin-server-parent
+ io.zipkin
+ 2.24.4-SNAPSHOT
+
+
+ zipkin-storage-ext
+ Zipkin Storage Extension
+
+
+ zipkin-dependency-storage-jdbc
+ zipkin-dependency-storage-elasticsearch
+ zipkin-dependency-storage-banyandb
+
+
+
+
+ io.zipkin
+ zipkin-dependency
+ ${project.version}
+
+
+
+
\ No newline at end of file
diff --git a/zipkin-server/zipkin-storage-ext/zipkin-dependency-storage-banyandb/pom.xml b/zipkin-server/zipkin-storage-ext/zipkin-dependency-storage-banyandb/pom.xml
new file mode 100644
index 00000000000..17f0c460bbc
--- /dev/null
+++ b/zipkin-server/zipkin-storage-ext/zipkin-dependency-storage-banyandb/pom.xml
@@ -0,0 +1,23 @@
+
+
+ 4.0.0
+
+ zipkin-storage-ext
+ io.zipkin
+ 2.24.4-SNAPSHOT
+
+
+ zipkin-dependency-storage-banyandb
+ Zipkin Dependency BanyanDB Extension
+
+
+
+ org.apache.skywalking
+ storage-banyandb-plugin
+ ${skywalking.version}
+
+
+
+
\ No newline at end of file
diff --git a/zipkin-server/zipkin-storage-ext/zipkin-dependency-storage-banyandb/src/main/java/zipkin/server/dependency/storage/banyandb/ZipkinDependencyBanyanDBQueryDAO.java b/zipkin-server/zipkin-storage-ext/zipkin-dependency-storage-banyandb/src/main/java/zipkin/server/dependency/storage/banyandb/ZipkinDependencyBanyanDBQueryDAO.java
new file mode 100644
index 00000000000..67eb7f897b2
--- /dev/null
+++ b/zipkin-server/zipkin-storage-ext/zipkin-dependency-storage-banyandb/src/main/java/zipkin/server/dependency/storage/banyandb/ZipkinDependencyBanyanDBQueryDAO.java
@@ -0,0 +1,60 @@
+/*
+ * Copyright 2015-2023 The OpenZipkin Authors
+ *
+ * Licensed 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 zipkin.server.dependency.storage.banyandb;
+
+import com.google.common.collect.ImmutableSet;
+import org.apache.skywalking.banyandb.v1.client.MeasureQuery;
+import org.apache.skywalking.banyandb.v1.client.MeasureQueryResponse;
+import org.apache.skywalking.banyandb.v1.client.TimestampRange;
+import org.apache.skywalking.oap.server.core.query.enumeration.Step;
+import org.apache.skywalking.oap.server.storage.plugin.banyandb.BanyanDBStorageClient;
+import org.apache.skywalking.oap.server.storage.plugin.banyandb.MetadataRegistry;
+import org.apache.skywalking.zipkin.dependency.entity.ZipkinDependency;
+import zipkin.server.dependency.IZipkinDependencyQueryDAO;
+import zipkin2.DependencyLink;
+import zipkin2.internal.DependencyLinker;
+
+import java.io.IOException;
+import java.util.List;
+import java.util.Set;
+import java.util.stream.Collectors;
+
+import static zipkin2.internal.DateUtil.epochDays;
+
+public class ZipkinDependencyBanyanDBQueryDAO implements IZipkinDependencyQueryDAO {
+ protected BanyanDBStorageClient client;
+ final Set tags = ImmutableSet.of(ZipkinDependency.DAY, ZipkinDependency.PARENT, ZipkinDependency.CHILD);
+ final Set fields = ImmutableSet.of(ZipkinDependency.CALL_COUNT, ZipkinDependency.ERROR_COUNT);
+
+ @Override
+ public List getDependencies(long endTs, long lookback) throws IOException {
+ final List days = epochDays(endTs, lookback);
+ final TimestampRange timeRange = new TimestampRange(days.get(0), days.get(days.size() - 1) + 1);
+ MetadataRegistry.Schema schema = MetadataRegistry.INSTANCE.findMetadata(ZipkinDependency.INDEX_NAME, Step.MINUTE);
+ final MeasureQuery query = new MeasureQuery(schema.getMetadata().getGroup(), schema.getMetadata().name(), timeRange, tags, fields);
+ final MeasureQueryResponse result = client.query(query);
+ return DependencyLinker.merge(result.getDataPoints().stream().map(s -> DependencyLink.newBuilder()
+ .parent(s.getTagValue(ZipkinDependency.PARENT))
+ .child(s.getTagValue(ZipkinDependency.CHILD))
+ .callCount(((Number)s.getFieldValue(ZipkinDependency.CALL_COUNT)).longValue())
+ .errorCount(s.getFieldValue(ZipkinDependency.ERROR_COUNT) != null ? ((Number)s.getFieldValue(ZipkinDependency.ERROR_COUNT)).longValue() : 0)
+ .build()).collect(Collectors.toList()));
+ }
+
+ public void setClient(BanyanDBStorageClient client) {
+ this.client = client;
+ }
+
+}
diff --git a/zipkin-server/zipkin-storage-ext/zipkin-dependency-storage-banyandb/src/main/java/zipkin/server/dependency/storage/banyandb/ZipkinDependencyBanyanDBStorageProvider.java b/zipkin-server/zipkin-storage-ext/zipkin-dependency-storage-banyandb/src/main/java/zipkin/server/dependency/storage/banyandb/ZipkinDependencyBanyanDBStorageProvider.java
new file mode 100644
index 00000000000..0f7571077e8
--- /dev/null
+++ b/zipkin-server/zipkin-storage-ext/zipkin-dependency-storage-banyandb/src/main/java/zipkin/server/dependency/storage/banyandb/ZipkinDependencyBanyanDBStorageProvider.java
@@ -0,0 +1,91 @@
+/*
+ * Copyright 2015-2023 The OpenZipkin Authors
+ *
+ * Licensed 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 zipkin.server.dependency.storage.banyandb;
+
+import org.apache.skywalking.oap.server.core.CoreModule;
+import org.apache.skywalking.oap.server.core.storage.StorageModule;
+import org.apache.skywalking.oap.server.library.module.ModuleConfig;
+import org.apache.skywalking.oap.server.library.module.ModuleDefine;
+import org.apache.skywalking.oap.server.library.module.ModuleProvider;
+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.BanyanDBStorageClient;
+import org.apache.skywalking.oap.server.storage.plugin.banyandb.BanyanDBStorageConfig;
+import org.apache.skywalking.oap.server.storage.plugin.banyandb.BanyanDBStorageProvider;
+import zipkin.server.dependency.IZipkinDependencyQueryDAO;
+import zipkin.server.dependency.ZipkinDependencyModule;
+
+import java.lang.reflect.Field;
+
+public class ZipkinDependencyBanyanDBStorageProvider extends ModuleProvider {
+ private BanyanDBStorageConfig config;
+ private ZipkinDependencyBanyanDBQueryDAO queryDAO;
+
+ @Override
+ public String name() {
+ return "banyandb";
+ }
+
+ @Override
+ public Class extends ModuleDefine> module() {
+ return ZipkinDependencyModule.class;
+ }
+
+ @Override
+ public ConfigCreator extends ModuleConfig> newConfigCreator() {
+ return new ConfigCreator() {
+
+ @Override
+ public Class type() {
+ return BanyanDBStorageConfig.class;
+ }
+
+ @Override
+ public void onInitialized(BanyanDBStorageConfig initialized) {
+ config = initialized;
+ }
+ };
+ }
+
+ @Override
+ public void prepare() throws ServiceNotProvidedException, ModuleStartException {
+ this.queryDAO = new ZipkinDependencyBanyanDBQueryDAO();
+ this.registerServiceImplementation(IZipkinDependencyQueryDAO.class, this.queryDAO);
+ }
+
+ @Override
+ public void start() throws ServiceNotProvidedException, ModuleStartException {
+
+ }
+
+ @Override
+ public void notifyAfterCompleted() throws ServiceNotProvidedException, ModuleStartException {
+ BanyanDBStorageProvider provider =
+ (BanyanDBStorageProvider) getManager().find(StorageModule.NAME).provider();
+ try {
+ Field field = BanyanDBStorageProvider.class.getDeclaredField("client");
+ field.setAccessible(true);
+ BanyanDBStorageClient client = (BanyanDBStorageClient) field.get(provider);
+ queryDAO.setClient(client);
+ } catch (NoSuchFieldException | IllegalAccessException e) {
+ throw new ModuleStartException("Failed to get BanyanDBStorageClient.", e);
+ }
+ }
+
+ @Override
+ public String[] requiredModules() {
+ return new String[] {CoreModule.NAME, StorageModule.NAME};
+ }
+}
diff --git a/zipkin-server/zipkin-storage-ext/zipkin-dependency-storage-banyandb/src/main/resources/META-INF/services/org.apache.skywalking.oap.server.library.module.ModuleProvider b/zipkin-server/zipkin-storage-ext/zipkin-dependency-storage-banyandb/src/main/resources/META-INF/services/org.apache.skywalking.oap.server.library.module.ModuleProvider
new file mode 100644
index 00000000000..dbee1d7b454
--- /dev/null
+++ b/zipkin-server/zipkin-storage-ext/zipkin-dependency-storage-banyandb/src/main/resources/META-INF/services/org.apache.skywalking.oap.server.library.module.ModuleProvider
@@ -0,0 +1,19 @@
+#
+# 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.
+#
+#
+
+zipkin.server.dependency.storage.banyandb.ZipkinDependencyBanyanDBStorageProvider
\ No newline at end of file
diff --git a/zipkin-server/zipkin-storage-ext/zipkin-dependency-storage-elasticsearch/pom.xml b/zipkin-server/zipkin-storage-ext/zipkin-dependency-storage-elasticsearch/pom.xml
new file mode 100644
index 00000000000..18589f758b8
--- /dev/null
+++ b/zipkin-server/zipkin-storage-ext/zipkin-dependency-storage-elasticsearch/pom.xml
@@ -0,0 +1,23 @@
+
+
+
+ zipkin-storage-ext
+ io.zipkin
+ 2.24.4-SNAPSHOT
+
+ 4.0.0
+
+ zipkin-dependency-storage-elasticsearch
+ Zipkin Dependency Elasticsearch Extension
+
+
+
+ org.apache.skywalking
+ storage-elasticsearch-plugin
+ ${skywalking.version}
+
+
+
+
\ No newline at end of file
diff --git a/zipkin-server/zipkin-storage-ext/zipkin-dependency-storage-elasticsearch/src/main/java/zipkin/server/dependency/storage/elasticsearch/ZipkinDependencyElasticsearchQueryDAO.java b/zipkin-server/zipkin-storage-ext/zipkin-dependency-storage-elasticsearch/src/main/java/zipkin/server/dependency/storage/elasticsearch/ZipkinDependencyElasticsearchQueryDAO.java
new file mode 100644
index 00000000000..9615d8ef842
--- /dev/null
+++ b/zipkin-server/zipkin-storage-ext/zipkin-dependency-storage-elasticsearch/src/main/java/zipkin/server/dependency/storage/elasticsearch/ZipkinDependencyElasticsearchQueryDAO.java
@@ -0,0 +1,63 @@
+/*
+ * Copyright 2015-2023 The OpenZipkin Authors
+ *
+ * Licensed 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 zipkin.server.dependency.storage.elasticsearch;
+
+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.SearchResponse;
+import org.apache.skywalking.oap.server.library.client.elasticsearch.ElasticSearchClient;
+import org.apache.skywalking.oap.server.storage.plugin.elasticsearch.base.IndexController;
+import org.apache.skywalking.zipkin.dependency.entity.ZipkinDependency;
+import zipkin.server.dependency.IZipkinDependencyQueryDAO;
+import zipkin2.DependencyLink;
+import zipkin2.internal.DependencyLinker;
+
+import java.io.IOException;
+import java.util.List;
+import java.util.stream.Collectors;
+
+import static zipkin2.internal.DateUtil.epochDays;
+
+public class ZipkinDependencyElasticsearchQueryDAO implements IZipkinDependencyQueryDAO {
+ private ElasticSearchClient client;
+ @Override
+ public List getDependencies(long endTs, long lookback) throws IOException {
+ final List days = epochDays(endTs, lookback);
+
+ final String index =
+ IndexController.LogicIndicesRegister.getPhysicalTableName(ZipkinDependency.INDEX_NAME);
+ final BoolQueryBuilder query = Query.bool();
+ if (IndexController.LogicIndicesRegister.isMergedTable(ZipkinDependency.INDEX_NAME)) {
+ query.must(Query.term(IndexController.LogicIndicesRegister.METRIC_TABLE_NAME, ZipkinDependency.INDEX_NAME));
+ }
+ query.must(Query.terms(ZipkinDependency.DAY, days));
+ final SearchBuilder search = Search.builder().query(query)
+ .size(days.size());
+
+ final SearchResponse response = client.search(index, search.build());
+ return DependencyLinker.merge(response.getHits().getHits().stream().map(h -> DependencyLink.newBuilder()
+ .parent((String) h.getSource().get(ZipkinDependency.PARENT))
+ .child((String) h.getSource().get(ZipkinDependency.CHILD))
+ .callCount(((Number) h.getSource().get(ZipkinDependency.CALL_COUNT)).longValue())
+ .errorCount(((Number) h.getSource().get(ZipkinDependency.ERROR_COUNT)).longValue())
+ .build()).collect(Collectors.toList()));
+ }
+
+ public void setClient(ElasticSearchClient client) {
+ this.client = client;
+ }
+}
diff --git a/zipkin-server/zipkin-storage-ext/zipkin-dependency-storage-elasticsearch/src/main/java/zipkin/server/dependency/storage/elasticsearch/ZipkinDependencyElasticsearchStorageProvider.java b/zipkin-server/zipkin-storage-ext/zipkin-dependency-storage-elasticsearch/src/main/java/zipkin/server/dependency/storage/elasticsearch/ZipkinDependencyElasticsearchStorageProvider.java
new file mode 100644
index 00000000000..39d318c73ab
--- /dev/null
+++ b/zipkin-server/zipkin-storage-ext/zipkin-dependency-storage-elasticsearch/src/main/java/zipkin/server/dependency/storage/elasticsearch/ZipkinDependencyElasticsearchStorageProvider.java
@@ -0,0 +1,92 @@
+/*
+ * Copyright 2015-2023 The OpenZipkin Authors
+ *
+ * Licensed 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 zipkin.server.dependency.storage.elasticsearch;
+
+import org.apache.skywalking.oap.server.core.CoreModule;
+import org.apache.skywalking.oap.server.core.storage.StorageModule;
+import org.apache.skywalking.oap.server.library.module.ModuleConfig;
+import org.apache.skywalking.oap.server.library.module.ModuleDefine;
+import org.apache.skywalking.oap.server.library.module.ModuleProvider;
+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.elasticsearch.StorageModuleElasticsearchConfig;
+import org.apache.skywalking.oap.server.storage.plugin.elasticsearch.StorageModuleElasticsearchProvider;
+import zipkin.server.dependency.IZipkinDependencyQueryDAO;
+import zipkin.server.dependency.ZipkinDependencyModule;
+
+import java.lang.reflect.Field;
+
+public class ZipkinDependencyElasticsearchStorageProvider extends ModuleProvider {
+ private StorageModuleElasticsearchConfig config;
+ private ZipkinDependencyElasticsearchQueryDAO queryDAO;
+
+ @Override
+ public String name() {
+ return "elasticsearch";
+ }
+
+ @Override
+ public Class extends ModuleDefine> module() {
+ return ZipkinDependencyModule.class;
+ }
+
+ @Override
+ public ConfigCreator extends ModuleConfig> newConfigCreator() {
+ return new ConfigCreator() {
+
+ @Override
+ public Class type() {
+ return StorageModuleElasticsearchConfig.class;
+ }
+
+ @Override
+ public void onInitialized(StorageModuleElasticsearchConfig initialized) {
+ config = initialized;
+ }
+ };
+ }
+
+ @Override
+ public void prepare() throws ServiceNotProvidedException, ModuleStartException {
+ this.queryDAO = new ZipkinDependencyElasticsearchQueryDAO();
+ this.registerServiceImplementation(IZipkinDependencyQueryDAO.class, this.queryDAO);
+ }
+
+ @Override
+ public void start() throws ServiceNotProvidedException, ModuleStartException {
+
+ }
+
+ @Override
+ public void notifyAfterCompleted() throws ServiceNotProvidedException, ModuleStartException {
+ StorageModuleElasticsearchProvider provider = (StorageModuleElasticsearchProvider) getManager().find(StorageModule.NAME)
+ .provider();
+ queryDAO.setClient(getFieldValue(provider, StorageModuleElasticsearchProvider.class, "elasticSearchClient"));
+ }
+
+ private T getFieldValue(Object from, Class fieldBelongClass, String fieldName) throws ModuleStartException {
+ try {
+ Field field = fieldBelongClass.getDeclaredField(fieldName);
+ field.setAccessible(true);
+ return (T) field.get(from);
+ } catch (NoSuchFieldException | IllegalAccessException e) {
+ throw new ModuleStartException("Failed to get " + fieldName, e);
+ }
+ }
+
+ @Override
+ public String[] requiredModules() {
+ return new String[] {CoreModule.NAME, StorageModule.NAME};
+ }
+}
diff --git a/zipkin-server/zipkin-storage-ext/zipkin-dependency-storage-elasticsearch/src/main/resources/META-INF/services/org.apache.skywalking.oap.server.library.module.ModuleProvider b/zipkin-server/zipkin-storage-ext/zipkin-dependency-storage-elasticsearch/src/main/resources/META-INF/services/org.apache.skywalking.oap.server.library.module.ModuleProvider
new file mode 100644
index 00000000000..49876f6e19c
--- /dev/null
+++ b/zipkin-server/zipkin-storage-ext/zipkin-dependency-storage-elasticsearch/src/main/resources/META-INF/services/org.apache.skywalking.oap.server.library.module.ModuleProvider
@@ -0,0 +1,19 @@
+#
+# 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.
+#
+#
+
+zipkin.server.dependency.storage.elasticsearch.ZipkinDependencyElasticsearchStorageProvider
\ No newline at end of file
diff --git a/zipkin-server/zipkin-storage-ext/zipkin-dependency-storage-jdbc/pom.xml b/zipkin-server/zipkin-storage-ext/zipkin-dependency-storage-jdbc/pom.xml
new file mode 100644
index 00000000000..5cc241a35a3
--- /dev/null
+++ b/zipkin-server/zipkin-storage-ext/zipkin-dependency-storage-jdbc/pom.xml
@@ -0,0 +1,23 @@
+
+
+
+ zipkin-storage-ext
+ io.zipkin
+ 2.24.4-SNAPSHOT
+
+ 4.0.0
+
+ zipkin-dependency-storage-jdbc
+ Zipkin Dependency JDBC Extension
+
+
+
+ org.apache.skywalking
+ storage-jdbc-hikaricp-plugin
+ ${skywalking.version}
+
+
+
+
\ No newline at end of file
diff --git a/zipkin-server/zipkin-storage-ext/zipkin-dependency-storage-jdbc/src/main/java/zipkin/server/dependency/storage/jdbc/common/ZipkinDependencyJDBCStorageProvider.java b/zipkin-server/zipkin-storage-ext/zipkin-dependency-storage-jdbc/src/main/java/zipkin/server/dependency/storage/jdbc/common/ZipkinDependencyJDBCStorageProvider.java
new file mode 100644
index 00000000000..b3eb49ad88b
--- /dev/null
+++ b/zipkin-server/zipkin-storage-ext/zipkin-dependency-storage-jdbc/src/main/java/zipkin/server/dependency/storage/jdbc/common/ZipkinDependencyJDBCStorageProvider.java
@@ -0,0 +1,89 @@
+/*
+ * Copyright 2015-2023 The OpenZipkin Authors
+ *
+ * Licensed 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 zipkin.server.dependency.storage.jdbc.common;
+
+import org.apache.skywalking.oap.server.core.CoreModule;
+import org.apache.skywalking.oap.server.core.storage.StorageModule;
+import org.apache.skywalking.oap.server.library.module.ModuleConfig;
+import org.apache.skywalking.oap.server.library.module.ModuleDefine;
+import org.apache.skywalking.oap.server.library.module.ModuleProvider;
+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.jdbc.common.JDBCStorageConfig;
+import org.apache.skywalking.oap.server.storage.plugin.jdbc.common.JDBCStorageProvider;
+import zipkin.server.dependency.IZipkinDependencyQueryDAO;
+import zipkin.server.dependency.ZipkinDependencyModule;
+import zipkin.server.dependency.storage.jdbc.common.dao.ZipkinDependencyJDBCQueryDAO;
+
+import java.lang.reflect.Field;
+
+public abstract class ZipkinDependencyJDBCStorageProvider extends ModuleProvider {
+ private JDBCStorageConfig config;
+ private ZipkinDependencyJDBCQueryDAO queryDAO;
+
+ @Override
+ public Class extends ModuleDefine> module() {
+ return ZipkinDependencyModule.class;
+ }
+
+ @Override
+ public ConfigCreator extends ModuleConfig> newConfigCreator() {
+ return new ConfigCreator() {
+ @Override
+ public Class type() {
+ return JDBCStorageConfig.class;
+ }
+
+ @Override
+ public void onInitialized(JDBCStorageConfig initialized) {
+ config = initialized;
+ }
+ };
+ }
+
+ @Override
+ public void prepare() throws ServiceNotProvidedException, ModuleStartException {
+ this.queryDAO = new ZipkinDependencyJDBCQueryDAO();
+ this.registerServiceImplementation(IZipkinDependencyQueryDAO.class, queryDAO);
+ }
+
+ @Override
+ public void start() throws ServiceNotProvidedException, ModuleStartException {
+
+ }
+
+ @Override
+ public void notifyAfterCompleted() throws ServiceNotProvidedException, ModuleStartException {
+ JDBCStorageProvider provider =
+ (JDBCStorageProvider) getManager().find(StorageModule.NAME).provider();
+ queryDAO.setClient(getFieldValue(provider, JDBCStorageProvider.class, "jdbcClient"));
+ queryDAO.setTableHelper(getFieldValue(provider, JDBCStorageProvider.class, "tableHelper"));
+ }
+
+ private T getFieldValue(Object from, Class fieldBelongClass, String fieldName) throws ModuleStartException {
+ try {
+ Field field = fieldBelongClass.getDeclaredField(fieldName);
+ field.setAccessible(true);
+ return (T) field.get(from);
+ } catch (NoSuchFieldException | IllegalAccessException e) {
+ throw new ModuleStartException("Failed to get " + fieldName, e);
+ }
+ }
+
+ @Override
+ public String[] requiredModules() {
+ return new String[] {CoreModule.NAME, StorageModule.NAME};
+ }
+}
diff --git a/zipkin-server/zipkin-storage-ext/zipkin-dependency-storage-jdbc/src/main/java/zipkin/server/dependency/storage/jdbc/common/dao/ZipkinDependencyJDBCQueryDAO.java b/zipkin-server/zipkin-storage-ext/zipkin-dependency-storage-jdbc/src/main/java/zipkin/server/dependency/storage/jdbc/common/dao/ZipkinDependencyJDBCQueryDAO.java
new file mode 100644
index 00000000000..17a8cee50b1
--- /dev/null
+++ b/zipkin-server/zipkin-storage-ext/zipkin-dependency-storage-jdbc/src/main/java/zipkin/server/dependency/storage/jdbc/common/dao/ZipkinDependencyJDBCQueryDAO.java
@@ -0,0 +1,75 @@
+/*
+ * Copyright 2015-2023 The OpenZipkin Authors
+ *
+ * Licensed 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 zipkin.server.dependency.storage.jdbc.common.dao;
+
+import org.apache.skywalking.oap.server.core.analysis.DownSampling;
+import org.apache.skywalking.oap.server.core.analysis.TimeBucket;
+import org.apache.skywalking.oap.server.library.client.jdbc.hikaricp.JDBCClient;
+import org.apache.skywalking.oap.server.storage.plugin.jdbc.common.TableHelper;
+import org.apache.skywalking.zipkin.dependency.entity.ZipkinDependency;
+import zipkin.server.dependency.IZipkinDependencyQueryDAO;
+import zipkin2.DependencyLink;
+import zipkin2.internal.DependencyLinker;
+
+import java.io.IOException;
+import java.sql.SQLException;
+import java.util.ArrayList;
+import java.util.List;
+
+import static java.util.stream.Collectors.joining;
+import static zipkin2.internal.DateUtil.epochDays;
+
+public class ZipkinDependencyJDBCQueryDAO implements IZipkinDependencyQueryDAO {
+ private JDBCClient client;
+ private TableHelper tableHelper;
+
+ @Override
+ public List getDependencies(long endTs, long lookback) throws IOException {
+ final List days = epochDays(endTs, lookback);
+
+ final long startBucket = TimeBucket.getTimeBucket(endTs - lookback, DownSampling.Day);
+ final long endBucket = TimeBucket.getTimeBucket(endTs, DownSampling.Day);
+ final List result = new ArrayList<>();
+
+ for (String table : tableHelper.getTablesForRead(ZipkinDependency.INDEX_NAME, startBucket, endBucket)) {
+ try {
+ client.executeQuery("select * from " + table + " where " + ZipkinDependency.DAY + " in "
+ + days.stream().map(it -> "?").collect(joining(",", "(", ")")),
+ resultSet -> {
+ while (resultSet.next()) {
+ result.add(DependencyLink.newBuilder()
+ .parent(resultSet.getString(ZipkinDependency.PARENT))
+ .child(resultSet.getString(ZipkinDependency.CHILD))
+ .callCount(resultSet.getLong(ZipkinDependency.CALL_COUNT))
+ .errorCount(resultSet.getLong(ZipkinDependency.ERROR_COUNT))
+ .build());
+ }
+ return null;
+ }, days.toArray());
+ } catch (SQLException e) {
+ throw new IOException(e);
+ }
+ }
+ return DependencyLinker.merge(result);
+ }
+
+ public void setClient(JDBCClient client) {
+ this.client = client;
+ }
+
+ public void setTableHelper(TableHelper tableHelper) {
+ this.tableHelper = tableHelper;
+ }
+}
diff --git a/zipkin-server/zipkin-storage-ext/zipkin-dependency-storage-jdbc/src/main/java/zipkin/server/dependency/storage/jdbc/h2/ZipkinDependencyH2StorageProvider.java b/zipkin-server/zipkin-storage-ext/zipkin-dependency-storage-jdbc/src/main/java/zipkin/server/dependency/storage/jdbc/h2/ZipkinDependencyH2StorageProvider.java
new file mode 100644
index 00000000000..3ab0f6beb14
--- /dev/null
+++ b/zipkin-server/zipkin-storage-ext/zipkin-dependency-storage-jdbc/src/main/java/zipkin/server/dependency/storage/jdbc/h2/ZipkinDependencyH2StorageProvider.java
@@ -0,0 +1,24 @@
+/*
+ * Copyright 2015-2023 The OpenZipkin Authors
+ *
+ * Licensed 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 zipkin.server.dependency.storage.jdbc.h2;
+
+import zipkin.server.dependency.storage.jdbc.common.ZipkinDependencyJDBCStorageProvider;
+
+public class ZipkinDependencyH2StorageProvider extends ZipkinDependencyJDBCStorageProvider {
+ @Override
+ public String name() {
+ return "h2";
+ }
+}
diff --git a/zipkin-server/zipkin-storage-ext/zipkin-dependency-storage-jdbc/src/main/java/zipkin/server/dependency/storage/jdbc/mysql/ZipkinDependencyMySQLStorageProvider.java b/zipkin-server/zipkin-storage-ext/zipkin-dependency-storage-jdbc/src/main/java/zipkin/server/dependency/storage/jdbc/mysql/ZipkinDependencyMySQLStorageProvider.java
new file mode 100644
index 00000000000..8adad7198d8
--- /dev/null
+++ b/zipkin-server/zipkin-storage-ext/zipkin-dependency-storage-jdbc/src/main/java/zipkin/server/dependency/storage/jdbc/mysql/ZipkinDependencyMySQLStorageProvider.java
@@ -0,0 +1,24 @@
+/*
+ * Copyright 2015-2023 The OpenZipkin Authors
+ *
+ * Licensed 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 zipkin.server.dependency.storage.jdbc.mysql;
+
+import zipkin.server.dependency.storage.jdbc.common.ZipkinDependencyJDBCStorageProvider;
+
+public class ZipkinDependencyMySQLStorageProvider extends ZipkinDependencyJDBCStorageProvider {
+ @Override
+ public String name() {
+ return "mysql";
+ }
+}
diff --git a/zipkin-server/zipkin-storage-ext/zipkin-dependency-storage-jdbc/src/main/java/zipkin/server/dependency/storage/jdbc/postgres/ZipkinDependencyPostgresStorageProvider.java b/zipkin-server/zipkin-storage-ext/zipkin-dependency-storage-jdbc/src/main/java/zipkin/server/dependency/storage/jdbc/postgres/ZipkinDependencyPostgresStorageProvider.java
new file mode 100644
index 00000000000..485a49e482d
--- /dev/null
+++ b/zipkin-server/zipkin-storage-ext/zipkin-dependency-storage-jdbc/src/main/java/zipkin/server/dependency/storage/jdbc/postgres/ZipkinDependencyPostgresStorageProvider.java
@@ -0,0 +1,24 @@
+/*
+ * Copyright 2015-2023 The OpenZipkin Authors
+ *
+ * Licensed 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 zipkin.server.dependency.storage.jdbc.postgres;
+
+import zipkin.server.dependency.storage.jdbc.common.ZipkinDependencyJDBCStorageProvider;
+
+public class ZipkinDependencyPostgresStorageProvider extends ZipkinDependencyJDBCStorageProvider {
+ @Override
+ public String name() {
+ return "postgresql";
+ }
+}
diff --git a/zipkin-server/zipkin-storage-ext/zipkin-dependency-storage-jdbc/src/main/resources/META-INF/services/org.apache.skywalking.oap.server.library.module.ModuleProvider b/zipkin-server/zipkin-storage-ext/zipkin-dependency-storage-jdbc/src/main/resources/META-INF/services/org.apache.skywalking.oap.server.library.module.ModuleProvider
new file mode 100644
index 00000000000..60a365293b3
--- /dev/null
+++ b/zipkin-server/zipkin-storage-ext/zipkin-dependency-storage-jdbc/src/main/resources/META-INF/services/org.apache.skywalking.oap.server.library.module.ModuleProvider
@@ -0,0 +1,21 @@
+#
+# 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.
+#
+#
+
+zipkin.server.dependency.storage.jdbc.h2.ZipkinDependencyH2StorageProvider
+zipkin.server.dependency.storage.jdbc.mysql.ZipkinDependencyMySQLStorageProvider
+zipkin.server.dependency.storage.jdbc.postgres.ZipkinDependencyPostgresStorageProvider
\ No newline at end of file