Description
Network communication is inherently layered, whether with formal OSI layering or more ad-hoc composition.
For example, a typical client-server HTTP call probably will involve the following operations:
Client: GetUser
Client HTTP: GET /users/123
Client DNS: users.example.com
Client TCP: 10.0.5.1:15642 -> 189.10.14.10:80
Server TCP: 10.0.5.1:15642 -> 189.10.14.10:80
Server HTTP: GET /users/123
Server: GetUser
These aren't always fixed; the outer RPC layer could decide to retry. Or the HTTP layer could encounter redirects. Nor is are the layers used necessarily known to each other. For example, HTTP2/TCP could be replaced with HTTP3/QUIC.
The same is true of other architectures like message queues, e.g. SQS/HTTP/DNS+TCP. And some libraries/frameworks build even further on top of those.
If I am reading the SpanKind correctly, the semantics have only to do with the parent-child relationships.
How is this intended to work when communication is inherently layered?
Should the RPC instrumenter use SpanKind.CLIENT/SpanKind.SERVER, event though the lower layers may be instrumented?
Should instrumentations have options of what type of SpanKinds to use, and then the user decides which layers are instrumented?
Should RPC instrumentation also use CLIENT/SERVER, and the user just needs to avoid instrumenting lower layers when they are being used by RPC?
If we really want this sort feature, there should be optional span attribute msg.kind which is "sender" or "receiver" and msg.protocol which is "tcp" or "http" or "grpc" or "mongodb", and the backend matches them up.
Or, propagate the other span ids in the context inter-process so it can be linked.
Or propagate a flag in the context indicating the middle of a client/server span that prevents lower layers from adding spans.
(Though to be honest, I'm not seeing quite seeing the utility of a two-sided span. It seems tricky to work with and seems to heavily weight towards at-most-once communication paradigms.)