void index(Span span) {
      Object idKey, parentKey;
      if (Boolean.TRUE.equals(span.shared())) {
        // we need to classify a shared span by its endpoint in case multiple servers respond to the
        // same ID sent by the client.
        idKey = createKey(span.id(), true, span.localEndpoint());
        // the parent of a server span is a client, which is not ambiguous for a given span ID.
        parentKey = span.id();
      } else {
        idKey = span.id();
        parentKey = span.parentId();
      }
      spanToParent.put(idKey, parentKey);
    }
We index spans by (id, shared, localEndpoint) before processing them. This latter fields
(shared, endpoint) are important because in zipkin (specifically B3), a server can share
(re-use) the same ID as its client. This impacts processing quite a bit when multiple servers
share one span ID.

<p>In a Zipkin trace, a parent (client) and child (server) can share the same ID if in an
RPC. If two different servers respond to the same client, the only way for us to tell which
is which is by endpoint. Our goal is to retain full paths across multiple endpoints. Even
though instrumentation should be configured in such a way that a client never sends the same
span ID to multiple servers, it can happen. Accordingly, we index defensively including any
endpoint data that might be available.
zipkin/src/main/java/zipkin2/internal/SpanNode.java