Repository: pinpoint-apm/pinpoint
Issue: Link data missing on emulated application nodes
Labels: bug

Call counts on edges pointing to emulated application nodes are calculated from callee statistics rather than caller statistics. This is done because the caller cannot differentiate which emulated application node it has called as the caller only knows the host name of it's target (and emulated application nodes share the same host name).

When such emulated application nodes sit on the edge of the callee search depth (for example, inbound depth=1, outbound depth=2 on a `USER -> SELECTED NODE -> EMULATED NODE -> some node`), the callee statistics of the `EMULATED_NODE` is not fetched as the inbound depth limit is 1.

In these situations, the server map incorrectly renders the above as `USER --N--> SELECTED NODE --0--> --N--> some node`.


Issue comments:

Pull request review comments:

Patch that resolved the issue:
diff --git a/web/src/main/java/com/navercorp/pinpoint/web/service/BFSLinkSelector.java b/web/src/main/java/com/navercorp/pinpoint/web/service/BFSLinkSelector.java
index 82dad1e34577..7eda23ea776d 100644
--- a/web/src/main/java/com/navercorp/pinpoint/web/service/BFSLinkSelector.java
+++ b/web/src/main/java/com/navercorp/pinpoint/web/service/BFSLinkSelector.java
@@ -196,7 +196,7 @@ private List<LinkData> checkRpcCallAccepted(LinkData linkData, Range range) {
     }
 
     private List<LinkData> createVirtualLinkData(LinkData linkData, Application toApplication, Set<AcceptApplication> acceptApplicationList) {
-        logger.warn("ono to N replaced. node:{}->host:{} accept:{}", linkData.getFromApplication(), toApplication.getName(), acceptApplicationList);
+        logger.warn("one to N replaced. node:{}->host:{} accept:{}", linkData.getFromApplication(), toApplication.getName(), acceptApplicationList);
 
         List<LinkData> emulationLink = new ArrayList<>();
         for (AcceptApplication acceptApplication : acceptApplicationList) {
@@ -234,7 +234,7 @@ private Set<AcceptApplication> findAcceptApplication(Application fromApplication
         return acceptApplication;
     }
 
-    private void fillEmulationLink(LinkDataDuplexMap linkDataDuplexMap) {
+    private void fillEmulationLink(LinkDataDuplexMap linkDataDuplexMap, Range range) {
         // TODO need to be reimplemented - virtual node creation logic needs an overhaul.
         // Currently, only the reversed relationship node is displayed. We need to create a virtual node and convert the rpc data appropriately.
         logger.debug("this.emulationLinkMarker:{}", this.emulationLinkMarker);
@@ -248,10 +248,18 @@ private void fillEmulationLink(LinkDataDuplexMap linkDataDuplexMap) {
             LinkKey findLinkKey = new LinkKey(emulationLinkData.getFromApplication(), emulationLinkData.getToApplication());
             LinkData targetLinkData = linkDataDuplexMap.getTargetLinkData(findLinkKey);
             if (targetLinkData == null) {
-                // There has been a case where targetLinkData was null, but exact event could not be captured for analysis.
-                // Logging the case for further analysis should it happen again in the future.
-                logger.error("targetLinkData not found findLinkKey:{}", findLinkKey);
-                continue;
+                // This is a case where the emulation target node has been only "partially" visited, (ie. does not have a target link data)
+                // Most likely due to the limit imposed by inbound search depth.
+                // Must go fetch the target link data here.
+                final Application targetApplication = emulationLinkData.getToApplication();
+                final LinkDataMap callee = mapStatisticsCalleeDao.selectCallee(targetApplication, range);
+                targetLinkData = callee.getLinkData(findLinkKey);
+                if (targetLinkData == null) {
+                    // There has been a case where targetLinkData was null, but exact event could not be captured for analysis.
+                    // Logging the case for further analysis should it happen again in the future.
+                    logger.error("targetLinkData not found findLinkKey:{}", findLinkKey);
+                    continue;
+                }
             }
 
             // create reversed link data - convert data accepted by the target to target's call data
@@ -332,7 +340,7 @@ public LinkDataDuplexMap select(Application sourceApplication, Range range, Sear
             logger.debug("Link emulation size:{}", emulationLinkMarker.size());
             // special case
             checkUnsearchEmulationCalleeNode(linkDataDuplexMap, range);
-            fillEmulationLink(linkDataDuplexMap);
+            fillEmulationLink(linkDataDuplexMap, range);
         }
 
         return linkDataDuplexMap;


Test patch: