-
Notifications
You must be signed in to change notification settings - Fork 1.1k
Description
Describe the bug
Current ObservedAspect
implementation for async methods (with return type of CompletionStage
) leaks parent observation to the future completion thread.
It happens because Observation.Scope#close()
method is called on the thread that completes the future.
Environment
- Micrometer version: 1.12.2, 1.15.4
- Micrometer registry: -
- OS: macOS
- Java version: openjdk version "24.0.2" 2025-07-15
To Reproduce
Here is a code snippet:
class ObservedService {
@Observed(name = "test.child")
CompletableFuture<?> child(Supplier<String> supplier) {
return CompletableFuture.supplyAsync(supplier, executor); // executor is a thread pool
}
@Observed(name = "test.parent")
void parent(Supplier<String> supplier) {
child(supplier).join();
}
}
When ObservedService#parent
is called, I expect Micrometer to:
- open
test.parent
scope in threadmain
- open
test.child
scope in threadmain
- close
test.child
scope in threadmain
, scope is nowtest.parent
- close
test.parent
scope in threadmain
, scope is nownull
But the actual execution is different:
- open
test.parent
scope in threadmain
- open
test.child
scope in threadmain
- close
test.child
scope in threadmain
, scope is nowtest.parent
- close
test.child
scope in threadpool-1-thread-1
, scope is nowtest.parent
— this is wrong - close
test.parent
scope in threadmain
, scope is nownull
Further attempts to call ObservedService#parent
may trigger this warning — Micrometer detects an attempt to replace an incorrect observation with a new one (that is also incorrect).
Expected behavior
ObservedAspect
should not open any scopes on CompletionStage
completion thread.
Additional context
I will attach a PR with a reproducer and a probable fix.