You are viewing a plain text version of this content. The canonical link for it is here.
Posted to notifications@skywalking.apache.org by wu...@apache.org on 2026/04/14 02:09:37 UTC

(skywalking) 01/01: Fix duplicate calls in EndpointTopologyBuilder and enforce no-duplicate in e2e

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

wusheng pushed a commit to branch fix/endpoint-topology-dedup-calls
in repository https://gitbox.apache.org/repos/asf/skywalking.git

commit d28e58c8f802b34bb9324b3f13a6a5b211444353
Author: Wu Sheng <wu...@foxmail.com>
AuthorDate: Tue Apr 14 10:09:22 2026 +0800

    Fix duplicate calls in EndpointTopologyBuilder and enforce no-duplicate in e2e
    
    EndpointTopologyBuilder.build() created a new Call for every CallDetail
    without deduplication, unlike ServiceTopologyBuilder which uses a HashMap.
    This caused duplicate call entries when storage returns multiple records
    for the same endpoint relation.
    
    Also switch all dependency topology e2e expected files from `contains`
    to `containsOnce` to enforce strict 1:1 matching and catch duplicates.
---
 docs/en/changes/changes.md                             |  2 ++
 .../oap/server/core/query/EndpointTopologyBuilder.java | 18 ++++++++++++------
 test/e2e-v2/cases/browser/expected/dependency.yml      |  4 ++--
 .../cases/gateway/expected/dependency-endpoint.yml     |  4 ++--
 .../cases/gateway/expected/dependency-instance.yml     |  4 ++--
 .../cases/go/expected/dependency-instance-go.yml       |  4 ++--
 .../ambient-als/expected/dependency-global-mesh.yml    |  4 ++--
 test/e2e-v2/cases/lua/expected/dependency-instance.yml |  4 ++--
 .../cases/nodejs/expected/dependency-instance.yml      |  4 ++--
 test/e2e-v2/cases/php/expected/dependency-instance.yml |  4 ++--
 .../ebpf/access_log/expected/dependency-instance.yml   |  4 ++--
 .../ebpf/network/expected/dependency-processs.yml      |  4 ++--
 .../expected/dependency-instance-consumer-py.yml       |  4 ++--
 .../expected/dependency-instance-provider-py-kafka.yml |  4 ++--
 .../expected/dependency-instance-provider-py.yml       |  4 ++--
 .../simple/expected/dependency-endpoint-consumer.yml   |  4 ++--
 .../simple/expected/dependency-endpoint-provider.yml   |  4 ++--
 .../cases/simple/expected/dependency-instance.yml      |  4 ++--
 .../storage/expected/cold/dependency-endpoint.yml      |  4 ++--
 .../storage/expected/cold/dependency-instance.yml      |  4 ++--
 .../storage/expected/dependency-endpoint-consumer.yml  |  4 ++--
 .../storage/expected/dependency-endpoint-provider.yml  |  4 ++--
 .../cases/storage/expected/dependency-instance.yml     |  4 ++--
 23 files changed, 56 insertions(+), 48 deletions(-)

diff --git a/docs/en/changes/changes.md b/docs/en/changes/changes.md
index c0bac9613b..0f3c0d7533 100644
--- a/docs/en/changes/changes.md
+++ b/docs/en/changes/changes.md
@@ -14,6 +14,8 @@
 * Support MCP (Model Context Protocol) observability for Envoy AI Gateway: MCP metrics (request CPM/latency, method breakdown, backend breakdown, initialization latency, capabilities), MCP access log sampling (errors only), `ai_route_type` searchable log tag, and MCP dashboard tabs.
 * Add weighted handler support to `BatchQueue` adaptive partitioning. MAL metrics use weight 0.05 at L1 (vs 1.0 for OAL), reducing partition count and memory overhead when many MAL metric types are registered.
 * Fix missing `taskId` filter in pprof task log query and its JDBC/BanyanDB/Elasticsearch implementations.
+* Fix duplicate calls in `EndpointTopologyBuilder` — calls were not deduplicated unlike `ServiceTopologyBuilder`, causing duplicate entries when storage returns multiple records for the same relation.
+* Use `containsOnce` for topology dependency e2e expected files to enforce no-duplicate verification.
 
 #### UI
 
diff --git a/oap-server/server-core/src/main/java/org/apache/skywalking/oap/server/core/query/EndpointTopologyBuilder.java b/oap-server/server-core/src/main/java/org/apache/skywalking/oap/server/core/query/EndpointTopologyBuilder.java
index dc391ca023..7b8e7977a3 100644
--- a/oap-server/server-core/src/main/java/org/apache/skywalking/oap/server/core/query/EndpointTopologyBuilder.java
+++ b/oap-server/server-core/src/main/java/org/apache/skywalking/oap/server/core/query/EndpointTopologyBuilder.java
@@ -18,8 +18,10 @@
 
 package org.apache.skywalking.oap.server.core.query;
 
+import java.util.HashMap;
 import java.util.HashSet;
 import java.util.List;
+import java.util.Map;
 import java.util.Set;
 import org.apache.skywalking.oap.server.core.analysis.IDManager;
 import org.apache.skywalking.oap.server.core.query.type.Call;
@@ -48,13 +50,17 @@ public class EndpointTopologyBuilder {
 
     EndpointTopology build(List<Call.CallDetail> serverSideCalls) {
         EndpointTopology topology = new EndpointTopology();
+        Map<String, Call> callMap = new HashMap<>();
         serverSideCalls.forEach(callDetail -> {
-            Call call = new Call();
-            call.setId(callDetail.getId());
-            call.setSource(callDetail.getSource());
-            call.setTarget(callDetail.getTarget());
-            call.addDetectPoint(DetectPoint.SERVER);
-            topology.getCalls().add(call);
+            if (!callMap.containsKey(callDetail.getId())) {
+                Call call = new Call();
+                call.setId(callDetail.getId());
+                call.setSource(callDetail.getSource());
+                call.setTarget(callDetail.getTarget());
+                call.addDetectPoint(DetectPoint.SERVER);
+                callMap.put(callDetail.getId(), call);
+                topology.getCalls().add(call);
+            }
         });
 
         Set<String> nodeIds = new HashSet<>();
diff --git a/test/e2e-v2/cases/browser/expected/dependency.yml b/test/e2e-v2/cases/browser/expected/dependency.yml
index 93ab0b700f..cc789f9dff 100644
--- a/test/e2e-v2/cases/browser/expected/dependency.yml
+++ b/test/e2e-v2/cases/browser/expected/dependency.yml
@@ -15,7 +15,7 @@
 
 debuggingtrace: null
 nodes:
-{{- contains .nodes }}
+{{- containsOnce .nodes }}
 - id: {{ b64enc "provider-py"}}.1
   name: provider-py
   type: Python
@@ -30,7 +30,7 @@ nodes:
     - BROWSER
 {{- end }}
 calls:
-{{- contains .calls }}
+{{- containsOnce .calls }}
 - source: {{ b64enc "test-ui"}}.1
   sourcecomponents:
     - ajax
diff --git a/test/e2e-v2/cases/gateway/expected/dependency-endpoint.yml b/test/e2e-v2/cases/gateway/expected/dependency-endpoint.yml
index 4454aaee77..69059fd5e0 100644
--- a/test/e2e-v2/cases/gateway/expected/dependency-endpoint.yml
+++ b/test/e2e-v2/cases/gateway/expected/dependency-endpoint.yml
@@ -15,7 +15,7 @@
 
 debuggingtrace: null
 nodes:
-{{- contains .nodes }}
+{{- containsOnce .nodes }}
 - id: {{ b64enc "e2e-service-consumer" }}.1_{{ b64enc "POST:/users" }}
   name: POST:/users
   serviceid: {{ b64enc "e2e-service-consumer" }}.1
@@ -30,7 +30,7 @@ nodes:
   isreal: true
 {{- end }}
 calls:
-{{- contains .calls }}
+{{- containsOnce .calls }}
 - source: {{ b64enc "e2e-service-consumer" }}.1_{{ b64enc "POST:/users" }}
   sourcecomponents: []
   target: {{ b64enc "e2e-service-provider" }}.1_{{ b64enc "POST:/users" }}
diff --git a/test/e2e-v2/cases/gateway/expected/dependency-instance.yml b/test/e2e-v2/cases/gateway/expected/dependency-instance.yml
index 8de73e882c..ec40ff74a3 100644
--- a/test/e2e-v2/cases/gateway/expected/dependency-instance.yml
+++ b/test/e2e-v2/cases/gateway/expected/dependency-instance.yml
@@ -15,7 +15,7 @@
 
 debuggingtrace: null
 nodes:
-{{- contains .nodes }}
+{{- containsOnce .nodes }}
 - id: {{ b64enc "gateway:80" }}.0_{{ b64enc "gateway:80" }}
   name: gateway:80
   serviceid: {{ b64enc "gateway:80" }}.0
@@ -36,7 +36,7 @@ nodes:
   isreal: true
 {{- end }}
 calls:
-{{- contains .calls }}
+{{- containsOnce .calls }}
 - source: {{ b64enc "gateway:80" }}.0_{{ b64enc "gateway:80" }}
   sourcecomponents: []
   target: {{ b64enc "e2e-service-provider" }}.1_{{ b64enc "provider1" }}
diff --git a/test/e2e-v2/cases/go/expected/dependency-instance-go.yml b/test/e2e-v2/cases/go/expected/dependency-instance-go.yml
index 43544bfcbd..75bd2696b2 100644
--- a/test/e2e-v2/cases/go/expected/dependency-instance-go.yml
+++ b/test/e2e-v2/cases/go/expected/dependency-instance-go.yml
@@ -15,7 +15,7 @@
 
 debuggingtrace: null
 nodes:
-{{- contains .nodes }}
+{{- containsOnce .nodes }}
 - id: {{ notEmpty .id }}
   name: {{ notEmpty .name }}
   serviceid: {{ b64enc "go-service" }}.1
@@ -30,7 +30,7 @@ nodes:
   isreal: true
 {{- end }}
 calls:
-{{- contains .calls }}
+{{- containsOnce .calls }}
 - source: {{ notEmpty .source }}
   sourcecomponents: []
   target: {{ b64enc "e2e-service-provider" }}.1_{{ b64enc "provider1" }}
diff --git a/test/e2e-v2/cases/istio/ambient-als/expected/dependency-global-mesh.yml b/test/e2e-v2/cases/istio/ambient-als/expected/dependency-global-mesh.yml
index 438172c4b3..b2bbc75fd5 100644
--- a/test/e2e-v2/cases/istio/ambient-als/expected/dependency-global-mesh.yml
+++ b/test/e2e-v2/cases/istio/ambient-als/expected/dependency-global-mesh.yml
@@ -15,7 +15,7 @@
 
 debuggingtrace: null
 nodes:
-{{- contains .nodes }}
+{{- containsOnce .nodes }}
 - id: {{ b64enc "reviews.default"}}.1
   name: reviews.default
   type: http
@@ -54,7 +54,7 @@ nodes:
   {{- end }}
 {{- end }}
 calls:
-{{- contains .calls }}
+{{- containsOnce .calls }}
 - source: {{ b64enc "productpage.default"}}.1
   sourcecomponents:
     {{- contains .sourcecomponents }}
diff --git a/test/e2e-v2/cases/lua/expected/dependency-instance.yml b/test/e2e-v2/cases/lua/expected/dependency-instance.yml
index 8c17de76f9..252f4987d8 100644
--- a/test/e2e-v2/cases/lua/expected/dependency-instance.yml
+++ b/test/e2e-v2/cases/lua/expected/dependency-instance.yml
@@ -15,7 +15,7 @@
 
 debuggingtrace: null
 nodes:
-{{- contains .nodes }}
+{{- containsOnce .nodes }}
 - id: {{ b64enc "User_Service_Name" }}.1_{{ b64enc "User_Service_Instance_Name" }}
   name: User_Service_Instance_Name
   serviceid: {{ b64enc "User_Service_Name" }}.1
@@ -30,7 +30,7 @@ nodes:
   isreal: true
 {{- end }}
 calls:
-{{- contains .calls }}
+{{- containsOnce .calls }}
 - source: {{ b64enc "e2e-service-entry-provider" }}.1_{{ b64enc "provider1" }}
   sourcecomponents: []
   target: {{ b64enc "User_Service_Name" }}.1_{{ b64enc "User_Service_Instance_Name" }}
diff --git a/test/e2e-v2/cases/nodejs/expected/dependency-instance.yml b/test/e2e-v2/cases/nodejs/expected/dependency-instance.yml
index 742d3b41d5..b5340b0d09 100644
--- a/test/e2e-v2/cases/nodejs/expected/dependency-instance.yml
+++ b/test/e2e-v2/cases/nodejs/expected/dependency-instance.yml
@@ -15,7 +15,7 @@
 
 debuggingtrace: null
 nodes:
-{{- contains .nodes }}
+{{- containsOnce .nodes }}
 - id: {{ b64enc "e2e-service-consumer" }}.1_{{ b64enc "consumer1" }}
   name: consumer1
   serviceid: {{ b64enc "e2e-service-consumer" }}.1
@@ -30,7 +30,7 @@ nodes:
   isreal: true
 {{- end }}
 calls:
-{{- contains .calls }}
+{{- containsOnce .calls }}
 - source: {{ b64enc "consumer" }}.1_{{ b64enc "consumer-instance" }}
   sourcecomponents: []
   target: {{ b64enc "e2e-service-consumer" }}.1_{{ b64enc "consumer1" }}
diff --git a/test/e2e-v2/cases/php/expected/dependency-instance.yml b/test/e2e-v2/cases/php/expected/dependency-instance.yml
index 27ffc6dff5..e079a20e9e 100644
--- a/test/e2e-v2/cases/php/expected/dependency-instance.yml
+++ b/test/e2e-v2/cases/php/expected/dependency-instance.yml
@@ -15,7 +15,7 @@
 
 debuggingtrace: null
 nodes:
-{{- contains .nodes }}
+{{- containsOnce .nodes }}
 - id: {{ notEmpty .id }}
   name: {{ notEmpty .name }}
   serviceid: {{ b64enc "php" }}.1
@@ -30,7 +30,7 @@ nodes:
   isreal: true
 {{- end }}
 calls:
-{{- contains .calls }}
+{{- containsOnce .calls }}
 - source: {{ notEmpty .source }}
   sourcecomponents: []
   target: {{ b64enc "e2e-service-provider" }}.1_{{ b64enc "provider1" }}
diff --git a/test/e2e-v2/cases/profiling/ebpf/access_log/expected/dependency-instance.yml b/test/e2e-v2/cases/profiling/ebpf/access_log/expected/dependency-instance.yml
index 3b0edea478..397922e124 100644
--- a/test/e2e-v2/cases/profiling/ebpf/access_log/expected/dependency-instance.yml
+++ b/test/e2e-v2/cases/profiling/ebpf/access_log/expected/dependency-instance.yml
@@ -15,7 +15,7 @@
 
 debuggingtrace: null
 nodes:
-{{- contains .nodes }}
+{{- containsOnce .nodes }}
 - id: {{ notEmpty .id }}
   name: {{ notEmpty .name }}
   serviceid: {{ b64enc "productpage.default" }}.1
@@ -30,7 +30,7 @@ nodes:
   isreal: true
 {{- end }}
 calls:
-{{- contains .calls }}
+{{- containsOnce .calls }}
 - source: {{ notEmpty .source }}
   sourcecomponents: []
   target: {{ notEmpty .target }}
diff --git a/test/e2e-v2/cases/profiling/ebpf/network/expected/dependency-processs.yml b/test/e2e-v2/cases/profiling/ebpf/network/expected/dependency-processs.yml
index 3c1eafdee9..b1e1b9cd87 100644
--- a/test/e2e-v2/cases/profiling/ebpf/network/expected/dependency-processs.yml
+++ b/test/e2e-v2/cases/profiling/ebpf/network/expected/dependency-processs.yml
@@ -25,7 +25,7 @@
 
 debuggingtrace: null
 nodes:
-{{- contains .nodes }}
+{{- containsOnce .nodes }}
 - id: {{template "genProcessId" "service"}}
   name: service
   serviceid: {{ b64enc "service" }}.1
@@ -42,7 +42,7 @@ nodes:
   isreal: false
 {{- end }}
 calls:
-{{- contains .calls }}
+{{- containsOnce .calls }}
 - source: {{template "genProcessId" "service"}}
   sourcecomponents:
   {{- contains .sourcecomponents }}
diff --git a/test/e2e-v2/cases/python/expected/dependency-instance-consumer-py.yml b/test/e2e-v2/cases/python/expected/dependency-instance-consumer-py.yml
index 0e01fded38..c948588de3 100644
--- a/test/e2e-v2/cases/python/expected/dependency-instance-consumer-py.yml
+++ b/test/e2e-v2/cases/python/expected/dependency-instance-consumer-py.yml
@@ -15,7 +15,7 @@
 
 debuggingtrace: null
 nodes:
-{{- contains .nodes }}
+{{- containsOnce .nodes }}
 - id: {{ b64enc "e2e-service-consumer" }}.1_{{ b64enc "consumer1" }}
   name: consumer1
   serviceid: {{ b64enc "e2e-service-consumer" }}.1
@@ -30,7 +30,7 @@ nodes:
   isreal: true
 {{- end }}
 calls:
-{{- contains .calls }}
+{{- containsOnce .calls }}
   - source: {{ b64enc "consumer-py" }}.1_{{ b64enc "consumer-py-instance" }}
     sourcecomponents: []
     target: {{ b64enc "e2e-service-consumer" }}.1_{{ b64enc "consumer1" }}
diff --git a/test/e2e-v2/cases/python/expected/dependency-instance-provider-py-kafka.yml b/test/e2e-v2/cases/python/expected/dependency-instance-provider-py-kafka.yml
index 77361a004a..5e2f4d3fee 100644
--- a/test/e2e-v2/cases/python/expected/dependency-instance-provider-py-kafka.yml
+++ b/test/e2e-v2/cases/python/expected/dependency-instance-provider-py-kafka.yml
@@ -15,7 +15,7 @@
 
 debuggingtrace: null
 nodes:
-{{- contains .nodes }}
+{{- containsOnce .nodes }}
 - id: {{ b64enc "provider-py-kafka" }}.1_{{ b64enc "provider-py-kafka-instance" }}
   name: provider-py-kafka-instance
   serviceid: {{ b64enc "provider-py-kafka" }}.1
@@ -30,7 +30,7 @@ nodes:
   isreal: true
 {{- end }}
 calls:
-{{- contains .calls }}
+{{- containsOnce .calls }}
   - source: {{ b64enc "consumer-py" }}.1_{{ b64enc "consumer-py-instance" }}
     sourcecomponents: []
     target: {{ b64enc "provider-py-kafka" }}.1_{{ b64enc "provider-py-kafka-instance" }}
diff --git a/test/e2e-v2/cases/python/expected/dependency-instance-provider-py.yml b/test/e2e-v2/cases/python/expected/dependency-instance-provider-py.yml
index 8fefbae5e9..db19bc127f 100644
--- a/test/e2e-v2/cases/python/expected/dependency-instance-provider-py.yml
+++ b/test/e2e-v2/cases/python/expected/dependency-instance-provider-py.yml
@@ -15,7 +15,7 @@
 
 debuggingtrace: null
 nodes:
-{{- contains .nodes }}
+{{- containsOnce .nodes }}
 - id: {{ b64enc "e2e-service-consumer" }}.1_{{ b64enc "consumer1" }}
   name: consumer1
   serviceid: {{ b64enc "e2e-service-consumer" }}.1
@@ -30,7 +30,7 @@ nodes:
   isreal: true
 {{- end }}
 calls:
-{{- contains .calls }}
+{{- containsOnce .calls }}
   - source: {{ b64enc "e2e-service-consumer" }}.1_{{ b64enc "consumer1" }}
     sourcecomponents: []
     target: {{ b64enc "provider-py" }}.1_{{ b64enc "provider-py-instance" }}
diff --git a/test/e2e-v2/cases/simple/expected/dependency-endpoint-consumer.yml b/test/e2e-v2/cases/simple/expected/dependency-endpoint-consumer.yml
index f9d09d4c7b..c6d497de38 100644
--- a/test/e2e-v2/cases/simple/expected/dependency-endpoint-consumer.yml
+++ b/test/e2e-v2/cases/simple/expected/dependency-endpoint-consumer.yml
@@ -15,7 +15,7 @@
 
 debuggingtrace: null
 nodes:
-{{- contains .nodes }}
+{{- containsOnce .nodes }}
 - id: {{ b64enc "User" }}.0_{{ b64enc "User" }}
   name: User
   serviceid: {{ b64enc "User" }}.0
@@ -36,7 +36,7 @@ nodes:
   isreal: true
 {{- end }}
 calls:
-{{- contains .calls }}
+{{- containsOnce .calls }}
 - source: {{ b64enc "User" }}.0_{{ b64enc "User" }}
   sourcecomponents: []
   target: {{ b64enc "e2e-service-consumer" }}.1_{{ b64enc "POST:/users" }}
diff --git a/test/e2e-v2/cases/simple/expected/dependency-endpoint-provider.yml b/test/e2e-v2/cases/simple/expected/dependency-endpoint-provider.yml
index 4454aaee77..69059fd5e0 100644
--- a/test/e2e-v2/cases/simple/expected/dependency-endpoint-provider.yml
+++ b/test/e2e-v2/cases/simple/expected/dependency-endpoint-provider.yml
@@ -15,7 +15,7 @@
 
 debuggingtrace: null
 nodes:
-{{- contains .nodes }}
+{{- containsOnce .nodes }}
 - id: {{ b64enc "e2e-service-consumer" }}.1_{{ b64enc "POST:/users" }}
   name: POST:/users
   serviceid: {{ b64enc "e2e-service-consumer" }}.1
@@ -30,7 +30,7 @@ nodes:
   isreal: true
 {{- end }}
 calls:
-{{- contains .calls }}
+{{- containsOnce .calls }}
 - source: {{ b64enc "e2e-service-consumer" }}.1_{{ b64enc "POST:/users" }}
   sourcecomponents: []
   target: {{ b64enc "e2e-service-provider" }}.1_{{ b64enc "POST:/users" }}
diff --git a/test/e2e-v2/cases/simple/expected/dependency-instance.yml b/test/e2e-v2/cases/simple/expected/dependency-instance.yml
index cc0dc034c1..e1c784da32 100644
--- a/test/e2e-v2/cases/simple/expected/dependency-instance.yml
+++ b/test/e2e-v2/cases/simple/expected/dependency-instance.yml
@@ -15,7 +15,7 @@
 
 debuggingtrace: null
 nodes:
-{{- contains .nodes }}
+{{- containsOnce .nodes }}
 - id: {{ b64enc "e2e-service-consumer" }}.1_{{ b64enc "consumer1" }}
   name: consumer1
   serviceid: {{ b64enc "e2e-service-consumer" }}.1
@@ -30,7 +30,7 @@ nodes:
   isreal: true
 {{- end }}
 calls:
-{{- contains .calls }}
+{{- containsOnce .calls }}
 - source: {{ b64enc "e2e-service-consumer" }}.1_{{ b64enc "consumer1" }}
   sourcecomponents: []
   target: {{ b64enc "e2e-service-provider" }}.1_{{ b64enc "provider1" }}
diff --git a/test/e2e-v2/cases/storage/expected/cold/dependency-endpoint.yml b/test/e2e-v2/cases/storage/expected/cold/dependency-endpoint.yml
index d381b07335..32c403ac7e 100644
--- a/test/e2e-v2/cases/storage/expected/cold/dependency-endpoint.yml
+++ b/test/e2e-v2/cases/storage/expected/cold/dependency-endpoint.yml
@@ -15,7 +15,7 @@
 
 debuggingtrace: null
 nodes:
-{{- contains .nodes }}
+{{- containsOnce .nodes }}
 - id: {{ b64enc "mock_a_service" }}.1_{{ b64enc "/dubbox-case/case/dubbox-rest/404-test" }}
   name: /dubbox-case/case/dubbox-rest/404-test
   serviceid: {{ b64enc "mock_a_service" }}.1
@@ -36,7 +36,7 @@ nodes:
   isreal: false
 {{- end }}
 calls:
-{{- contains .calls }}
+{{- containsOnce .calls }}
 - source: {{ b64enc "mock_a_service" }}.1_{{ b64enc "/dubbox-case/case/dubbox-rest/404-test" }}
   sourcecomponents: []
   target: {{ b64enc "mock_b_service" }}.1_{{ b64enc "org.skywaking.apm.testcase.dubbo.services.GreetServiceImpl.doBusiness()" }}
diff --git a/test/e2e-v2/cases/storage/expected/cold/dependency-instance.yml b/test/e2e-v2/cases/storage/expected/cold/dependency-instance.yml
index 7792498d7f..cdb9526e56 100644
--- a/test/e2e-v2/cases/storage/expected/cold/dependency-instance.yml
+++ b/test/e2e-v2/cases/storage/expected/cold/dependency-instance.yml
@@ -15,7 +15,7 @@
 
 debuggingtrace: null
 nodes:
-{{- contains .nodes }}
+{{- containsOnce .nodes }}
 - id: {{ b64enc "mock_a_service" }}.1_{{ b64enc "mock_a_service_instance" }}
   name: mock_a_service_instance
   serviceid: {{ b64enc "mock_a_service" }}.1
@@ -30,7 +30,7 @@ nodes:
   isreal: true
 {{- end }}
 calls:
-{{- contains .calls }}
+{{- containsOnce .calls }}
 - source: {{ b64enc "mock_a_service" }}.1_{{ b64enc "mock_a_service_instance" }}
   sourcecomponents: []
   target: {{ b64enc "mock_b_service" }}.1_{{ b64enc "mock_b_service_instance" }}
diff --git a/test/e2e-v2/cases/storage/expected/dependency-endpoint-consumer.yml b/test/e2e-v2/cases/storage/expected/dependency-endpoint-consumer.yml
index f9d09d4c7b..c6d497de38 100644
--- a/test/e2e-v2/cases/storage/expected/dependency-endpoint-consumer.yml
+++ b/test/e2e-v2/cases/storage/expected/dependency-endpoint-consumer.yml
@@ -15,7 +15,7 @@
 
 debuggingtrace: null
 nodes:
-{{- contains .nodes }}
+{{- containsOnce .nodes }}
 - id: {{ b64enc "User" }}.0_{{ b64enc "User" }}
   name: User
   serviceid: {{ b64enc "User" }}.0
@@ -36,7 +36,7 @@ nodes:
   isreal: true
 {{- end }}
 calls:
-{{- contains .calls }}
+{{- containsOnce .calls }}
 - source: {{ b64enc "User" }}.0_{{ b64enc "User" }}
   sourcecomponents: []
   target: {{ b64enc "e2e-service-consumer" }}.1_{{ b64enc "POST:/users" }}
diff --git a/test/e2e-v2/cases/storage/expected/dependency-endpoint-provider.yml b/test/e2e-v2/cases/storage/expected/dependency-endpoint-provider.yml
index 4454aaee77..69059fd5e0 100644
--- a/test/e2e-v2/cases/storage/expected/dependency-endpoint-provider.yml
+++ b/test/e2e-v2/cases/storage/expected/dependency-endpoint-provider.yml
@@ -15,7 +15,7 @@
 
 debuggingtrace: null
 nodes:
-{{- contains .nodes }}
+{{- containsOnce .nodes }}
 - id: {{ b64enc "e2e-service-consumer" }}.1_{{ b64enc "POST:/users" }}
   name: POST:/users
   serviceid: {{ b64enc "e2e-service-consumer" }}.1
@@ -30,7 +30,7 @@ nodes:
   isreal: true
 {{- end }}
 calls:
-{{- contains .calls }}
+{{- containsOnce .calls }}
 - source: {{ b64enc "e2e-service-consumer" }}.1_{{ b64enc "POST:/users" }}
   sourcecomponents: []
   target: {{ b64enc "e2e-service-provider" }}.1_{{ b64enc "POST:/users" }}
diff --git a/test/e2e-v2/cases/storage/expected/dependency-instance.yml b/test/e2e-v2/cases/storage/expected/dependency-instance.yml
index cc0dc034c1..e1c784da32 100644
--- a/test/e2e-v2/cases/storage/expected/dependency-instance.yml
+++ b/test/e2e-v2/cases/storage/expected/dependency-instance.yml
@@ -15,7 +15,7 @@
 
 debuggingtrace: null
 nodes:
-{{- contains .nodes }}
+{{- containsOnce .nodes }}
 - id: {{ b64enc "e2e-service-consumer" }}.1_{{ b64enc "consumer1" }}
   name: consumer1
   serviceid: {{ b64enc "e2e-service-consumer" }}.1
@@ -30,7 +30,7 @@ nodes:
   isreal: true
 {{- end }}
 calls:
-{{- contains .calls }}
+{{- containsOnce .calls }}
 - source: {{ b64enc "e2e-service-consumer" }}.1_{{ b64enc "consumer1" }}
   sourcecomponents: []
   target: {{ b64enc "e2e-service-provider" }}.1_{{ b64enc "provider1" }}