Repository: jaegertracing/jaeger (Go)

Issue:
[Bug]: non-root spans with FollowsFrom reference incorrectly represent as trace in collector metrics
### What happened?

We noticed that non-root spans with **FollowsFrom** reference are showing as traces in collector metrics, e.g. `traces.received`, `traces.rejected` metrics.

The reason is that in collector metrics, `ParentSpanID()` is used to determine if a span is a root span, e.g. below
https://github.com/jaegertracing/jaeger/blob/3d67ac86f53866332568b0f542265b00d86407fc/cmd/collector/app/metrics.go#L233-L237

And the current implementation of `ParentSpanID()` only considers **ChildOf** relationship. So if a span has only **FollowsFrom** reference, the `ParentSpanID()` doesn't consider that and returns 0.
https://github.com/jaegertracing/jaeger/blob/506adaf2bf4f8364aa21d3f3001d1a6dcc6e2e97/model/span.go#L99-L109

Not sure any particular reason that **FollowsFrom**  is not considered in the current implementation of `ParentSpanID()`.


The proposal is to either 1) fix in collector metric to replace the `ParentSpanID()`, 2) fix in `ParentSpanID()`.
Prefer 2) because we use this method a lot to determine a span is a root span internally, but changing its behavior might break other places in jaeger OSS.

### Steps to reproduce

Create spans with **FollowsFrom** relationship, and check the collector trace metrics.

### Expected behavior

non-root spans with FollowsFrom reference should not show as trace in collector metrics.

### Relevant log output

_No response_

### Screenshot

_No response_

### Additional context

_No response_

### Jaeger backend version

_No response_

### SDK

_No response_

### Pipeline

_No response_

### Stogage backend

_No response_

### Operating system

_No response_

### Deployment model

_No response_

### Deployment configs

_No response_


Discussion:
Would chaning `ParentSpanID()` behaviour in 2) way also resolve this (rather old one) TODO?

https://github.com/jaegertracing/jaeger/blob/506adaf2bf4f8364aa21d3f3001d1a6dcc6e2e97/model/adjuster/clockskew.go#L117-L126
I think so.



Patch:
diff --git a/model/adjuster/clockskew.go b/model/adjuster/clockskew.go
index 167590d862d..dccc5e8d580 100644
--- a/model/adjuster/clockskew.go
+++ b/model/adjuster/clockskew.go
@@ -119,7 +119,6 @@ func (a *clockSkewAdjuster) buildNodesMap() {
 func (a *clockSkewAdjuster) buildSubGraphs() {
 	a.roots = make(map[model.SpanID]*node)
 	for _, n := range a.spans {
-		// TODO handle FOLLOWS_FROM references
 		if n.span.ParentSpanID() == 0 {
 			a.roots[n.span.SpanID] = n
 			continue
diff --git a/model/span.go b/model/span.go
index 5136e06b3ea..749f21dbf2a 100644
--- a/model/span.go
+++ b/model/span.go
@@ -97,13 +97,23 @@ func (s *Span) NormalizeTimestamps() {
 }
 
 // ParentSpanID returns ID of a parent span if it exists.
-// It searches for the first child-of reference pointing to the same trace ID.
+// It searches for the first child-of or follows-from reference pointing to the same trace ID.
 func (s *Span) ParentSpanID() SpanID {
+	var followsFromRef *SpanRef
 	for i := range s.References {
 		ref := &s.References[i]
-		if ref.TraceID == s.TraceID && ref.RefType == ChildOf {
+		if ref.TraceID != s.TraceID {
+			continue
+		}
+		if ref.RefType == ChildOf {
 			return ref.SpanID
 		}
+		if followsFromRef == nil && ref.RefType == FollowsFrom {
+			followsFromRef = ref
+		}
+	}
+	if followsFromRef != nil {
+		return followsFromRef.SpanID
 	}
 	return SpanID(0)
 }


Test patch:
diff --git a/model/span_test.go b/model/span_test.go
index 9aa50389365..efd9b5b9c08 100644
--- a/model/span_test.go
+++ b/model/span_test.go
@@ -277,6 +277,11 @@ func TestParentSpanID(t *testing.T) {
 	span := makeSpan(model.String("k", "v"))
 	assert.Equal(t, model.NewSpanID(123), span.ParentSpanID())
 
+	span.References = []model.SpanRef{
+		model.NewFollowsFromRef(span.TraceID, model.NewSpanID(777)),
+	}
+	assert.Equal(t, model.NewSpanID(777), span.ParentSpanID())
+
 	span.References = []model.SpanRef{
 		model.NewFollowsFromRef(span.TraceID, model.NewSpanID(777)),
 		model.NewChildOfRef(span.TraceID, model.NewSpanID(888)),