Spring Boot's Micrometer integration currently has a WebFlux WebFilter that calls MeterRegistry. The calls are made on the event loop thread and, as such, must be non-blocking. This isn't guaranteed by the current MeterRegistry contract and AbstractMeterRegistry synchronizes on meterMap which may block.
A couple of possible solutions (there may well be others):
MeterRegistry implementations to be non-blocking, including any types that are returned from its methodsMeterRegistry that can be used in a reactive applicationIn places where a blocking call is unavoidable, something like Reactor's scheduler could be used to dispatch the blocking work on a separate thread and get a Mono that is signalled when the work is completed.
I walked through this with @smaldini. By reducing the scope of the synchronized block to only the first time a meter with a unique name/tag combination is created, we will not cause any blocking concerns for reactive apps. What remains synchronized is a single map put, an in-memory operation without any possibility of user-defined code blocking for a substantial amount of time.
Previously, we were synchronizing on every retrieval of a metric from the map as well.
Thanks, @jkschneider. Do you think it would be worth making some updates to the javadoc of MeterRegistry and the types it returns (Meter, Counter, etc) to indicate that callers can expect them to be non-blocking and that implementers are required to ensure that this is the case?
@wilkinsona Can you point me to language like that in other APIs I can repurpose?
I don't think I can as I'm not sure we've got another example of an API that look unsuitable for use in a reactive context but actually is suitable. I'd say something like this:
MeterRegistry may be used in a reactive context. As such, implementations must guarantee not to block the calling thread.
Not sure if it helps, but i'd define "block": "MUST NOT negatively impact the calling thread, e.g. it should respond immediately by avoiding IO call, deep stack recursion or any coordination."
Most helpful comment
Not sure if it helps, but i'd define "block": "MUST NOT negatively impact the calling thread, e.g. it should respond immediately by avoiding IO call, deep stack recursion or any coordination."