mtth / tracing

Distributed tracing
https://hackage.haskell.org/package/tracing
BSD 3-Clause "New" or "Revised" License
24 stars 11 forks source link

Message Tracing span ID's #6

Closed alaendle closed 3 years ago

alaendle commented 3 years ago

To be honest I'm still a newbie to all the details of span propagation, but what caught my intention was the fact that in this framework PRODUCER/CONSUMER spans act the same way as CLIENT/SERVER spans.

But as far as I understood https://zipkin.io/pages/instrumenting.html ("Message Tracing is different than RPC tracing because the producer and consumer don’t share span IDs."). Unfortunately the specification how things should behave seems to be inexistent - at least I couldn't find a document that describes the correct behaviour.

Please let me know if this is really an issue - my guess is that someone who wrote such a library must be an expert to these topics 😉 Thanks in advance!

alaendle commented 3 years ago

To make things more clear - the following code (which I hope at least somehow makes sense):

{-# LANGUAGE OverloadedStrings #-}

module Main where

import Control.Monad.Trace (TraceT)
import qualified Control.Monad.Trace.Class as TC
import qualified Monitor.Tracing.Zipkin as ZPK
import Control.Monad.IO.Class (liftIO)

rootService :: TraceT IO ZPK.B3
rootService = TC.rootSpan TC.alwaysSampled "root" $ ZPK.producerSpanWith id "producer" (\(Just b3) -> pure b3)

consumerService :: ZPK.B3 -> TraceT IO ()
consumerService b3 = ZPK.consumerSpanWith id b3 $ liftIO $ print b3

main :: IO ()
main = do
    b3 <- ZPK.with ZPK.defaultSettings { ZPK.settingsEndpoint = Just "rootEndpoint" } $ ZPK.run rootService
    ZPK.with ZPK.defaultSettings { ZPK.settingsEndpoint = Just "consumerEndpoint" } $ ZPK.run $ consumerService b3

leads to three spans

[
    {
        "traceId": "38a798d0ca78faf3a027c3e78a2f16dd",
        "id": "bef37d00fcf35d42",
        "name": "root",
        "timestamp": 1616664131719454,
        "localEndpoint": {
            "serviceName": "rootendpoint"
        }
    },
    {
        "traceId": "38a798d0ca78faf3a027c3e78a2f16dd",
        "parentId": "bef37d00fcf35d42",
        "id": "131cb0bb5ad7e036",
        "kind": "PRODUCER",
        "name": "producer",
        "timestamp": 1616664131719454,
        "localEndpoint": {
            "serviceName": "rootendpoint"
        }
    },
    {
        "traceId": "38a798d0ca78faf3a027c3e78a2f16dd",
        "id": "131cb0bb5ad7e036",
        "kind": "CONSUMER",
        "timestamp": 1616664134461970,
        "duration": 977,
        "localEndpoint": {
            "serviceName": "consumerendpoint"
        }
    }
]

Please note that the last span, the CONSUMER span, has no parentId (I'm unsure if this is correct) and that the id is the same as the id of the producer (which is a violation of the specification, or isn't it?).

/cc @mtth

alaendle commented 3 years ago

Not sure if this is correct, but maybe line 306 in Zipkin.hs

https://github.com/mtth/tracing/blob/47a15f08c0d4b82cfb665cdbda4866e1341eea0f/src/Monitor/Tracing/Zipkin.hs#L298-L307

should just be changed to something like , builderReferences = Set.singleton (ChildOf $ b3SpanID b3)

mtth commented 3 years ago

Hi @alaendle. Thanks for the detailed report and investigation - I think you're right, consumerSpanWith should create a child span instead. If I read the spec correctly we'd also want to keep serverSpanWith as-is, so we need to do a bit of refactoring beyond importB3 (for example inlining/specializing it along with incomingSpan inside serverSpanWith and consumerSpanWith). Is this something you would be interested in contributing?