-
Notifications
You must be signed in to change notification settings - Fork 1k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
ObservationValidator introduced in TestObservationRegistry with version 1.14 sometimes throws InvalidObservationException #5802
Comments
Thank you for the issue!
In your case:
There is no
As far as I understand, it does not but it breaks your tests, right? (The test registry should not be used anywhere else other than tests.)
We considered doing these but decided not to, see part of the discussion here: #5300 |
@jonatan-ivanov Thanks a whole lot for the quick and exhaustive answer. I'm sorry for not having noticed that We are instrumenting our code using a little utility @Component
public class TraceHelper {
@Autowired
ObservationRegistry registry;
/**
* Adds a distributed tracing span using {@link Mono#tap(Function)}
*
* @param name will be converted to lower case to avoid ugly underscores being generated
* @param lowCardinalityKeyValues provide additional key/value pairs for the span; the values must be "low
* cardinality", see {@link Observation#lowCardinalityKeyValue(KeyValue)}
*/
public <T> SignalListenerFactory<T, ?> addTraceSpan(String name, KeyValue... lowCardinalityKeyValues) {
String lowerName = name.toLowerCase(Locale.ENGLISH);
return Micrometer.observation(registry, makeObservation(lowerName, lowCardinalityKeyValues));
}
private Function<ObservationRegistry, Observation> makeObservation(String name,
KeyValue... lowCardinalityKeyValues) {
return r -> {
Observation observation = Observation.createNotStarted(name, r).contextualName(name);
for (KeyValue kv : lowCardinalityKeyValues) {
observation.lowCardinalityKeyValue(kv);
}
return observation;
};
}
} which we use like Mono.tap(traceHelper.addTraceSpan("somename",
KeyValue.of("someKey", "someValue")))
Maybe my wording was a bit imprecise: the issue is not happening in the productive environment, but as soon as we use TestObservationRegistry as ObservationRegistry (by adding a @TestConfiguration), the issue appears and results in the observed Exceptions. Since the Not sure where to go from here. We could of course ask the Reactor colleagues to make their reactor-core-micrometer library smarter and issue only one stop(), but this may not exactly be trivial and pretty much impossible to test as long as the behavior of the validator differs from the behavior of |
I think @chemicL could you please help us to figure out what is happening above? It seems stop is called twice. |
This is a bit non-trivial... The reactive pipeline (Flux) is running for several "tenants". We skip some items depending on the outcome of the first request, (first request being an OIDC authorization exchange and second request being a backend call with the retrieved token). This is done by .flatMap(tenant -> serverAuthorization.backendAccessTokenFor(tenant)
.flatMap(token -> callService(backendRequest, token)), 1) // 1 is the concurrency of the outer flatMap
.skipWhile(serverAuthorization::backendRespondedForbidden).take(1, true).next() // |
Describe the bug
We are using micrometer in a spring boot application and have tests in place for our micrometer trace instrumentation. After (implicitly) updating micrometer from 1.13.8 to 1.14.2, a few of our tests fail due to InvalidObservationException being thrown. Tracing this down, the reason is the newly introduced ObservationValidator.
Note that the issue is in the micrometer test jar, not in the micrometer productive code.
The error message is "Invalid stop: Observation 'xxx' has already been stopped"
The underlying reason why "stop" is tried to be executed "twice" is probably that MicrometerObservationListener (which is not in the test jar) does an observation.stop in it's doOnCancel method (the same as in it's doOnComplete). It seems that the spring boot reactive framework does not guarantee that doOnStop and doOnCancel are always called mutually exclusive. Note that the situation only occurs in a few test cases where the request is terminated by an Exception or using skipWhile on a Flux which seems to indicate that the onCancel is issued by the spring framework when disposing the request.
Environment
Spring boot 3.4.1 with managed micrometer version 1.14.2
To Reproduce
How to reproduce the bug:
Hard to provide a test case short of our application.
Expected behavior
Adding micrometer test must not break existing productive code. A quick solution might be to allow to create a TestObservationRegistry that does not include the ObservationValidator by default. Alternatively, allowing to pass an alternative Consumer<ValidationResult> instead of throwInvalidObservationException might be an option so that validation results could be checked by the consuming tests.
Additional context
Add any other context about the problem here, e.g. related issues.
The text was updated successfully, but these errors were encountered: