A MongoDB driver can be used from various programming models (imperative where calls remain on the same thread, reactive where code is thread-agnostic). At the same time, the underlying application using the MongoDB driver can be interested in end-to-end tracing of outgoing remote calls. To properly construct traces, each participating component must be able to contribute their individual activity in the form of a span (one or more spans represent a full trace) and be able to attach its span to the overall trace.
In imperative programming arrangements, context propagation is typically implemented as out-of-band storage where the context is attached to the thread that is associated with the call. Context information is stored outside and integration components can associate context data and tracing identifiers through CommandListener.
Since CommandListener accept CommandEvent that are not associated with a context (or context provider), other types of programming models (asynchronous, event-loop, reactive) cannot leverage the ThreadLocal pattern.
The following proposal outlines a context capturing and propagation API to enable the driver to capture and propagate request contexts regardless of the underlying programming model. It consists of the following components:
- Generic context capturing interfaces to capture the current (inbound) context
- Context API
- Context consumption API
Calls to the driver are subject can happen within a potentially existing context. Depending on the called MongoDB API, context can be either stored ThreadLocal or can be passed as Reactor Context. Since the driver configuration infrastructure (MongoClientSettings) is API-agnostict, context providers should not depend on dependencies used in particular driver implementations. Instead, the driver internally can detect whether a context provider component was registered that can be leveraged to obtain the context:
The context API is a mutable, Map-like data structure that allows associating key-value tuples with the current request. In a tracing setup, beginning a request would create a new span, store it in the request context, and upon success, error, retry, the span would be retrieved from the context and completed with the command outcome.
The context would be primarily consumed through the command listener API CommandListener and ideally, RequestContext is provided through CommandEvent. An example implementation would be: