Repository navigation
Fix memory leak in Webflux related to an ever growing stack - #2580
Conversation
|
| SpringApplication.run(SentryDemoApplication.class, args); | ||
| } | ||
|
|
||
| @Bean |
There was a problem hiding this comment.
copied from other samples
| @@ -0,0 +1,25 @@ | |||
| package io.sentry.samples.spring.boot; | |||
|
|
|||
| public class Todo { | |||
There was a problem hiding this comment.
copied from other samples
| import org.springframework.web.reactive.function.client.WebClient; | ||
| import reactor.core.publisher.Mono; | ||
|
|
||
| @RestController |
There was a problem hiding this comment.
copied from other samples
| public Mono<Void> filter( | ||
| final @NotNull ServerWebExchange serverWebExchange, | ||
| final @NotNull WebFilterChain webFilterChain) { | ||
| @NotNull IHub requestHub = Sentry.cloneMainHub(); |
There was a problem hiding this comment.
We now clone the hub from the main hub instead of using whatever hub is present on the thread.
| __ -> { | ||
| hub.popScope(); | ||
| doFinally(requestHub); | ||
| Sentry.setCurrentHub(NoOpHub.getInstance()); |
There was a problem hiding this comment.
This only resets one of the ThreadLocals. In testing the thread were .doFirst is run still keeps the hub alive. Don't know of a way to clean the ThreadLocal (yet) but I have another idea i wanna explore so we only leak an id and reference the hub in a store instead. I wanna try that in a different PR.
| .doFirst( | ||
| () -> { | ||
| hub.pushScope(); | ||
| Sentry.setCurrentHub(requestHub); |
There was a problem hiding this comment.
Need to set it as current hub for the thread as SentryScheduleHook clones the current hub and without this it'd clone a random hub breaking scope.
Performance metrics 🚀
|
| Revision | Plain | With Sentry | Diff |
|---|---|---|---|
| db5bd4e | 352.90 ms | 359.52 ms | 6.62 ms |
| d691d8f | 352.56 ms | 389.59 ms | 37.03 ms |
| db5bd4e | 314.82 ms | 349.17 ms | 34.35 ms |
| db5bd4e | 347.00 ms | 366.74 ms | 19.74 ms |
| db5bd4e | 286.94 ms | 315.26 ms | 28.32 ms |
| e5bbb00 | 317.46 ms | 353.26 ms | 35.80 ms |
App size
| Revision | Plain | With Sentry | Diff |
|---|---|---|---|
| db5bd4e | 1.73 MiB | 2.34 MiB | 626.30 KiB |
| d691d8f | 1.73 MiB | 2.34 MiB | 626.23 KiB |
| db5bd4e | 1.73 MiB | 2.34 MiB | 626.30 KiB |
| db5bd4e | 1.73 MiB | 2.34 MiB | 626.30 KiB |
| db5bd4e | 1.73 MiB | 2.34 MiB | 626.30 KiB |
| e5bbb00 | 1.73 MiB | 2.34 MiB | 626.22 KiB |
Codecov ReportPatch coverage:
Additional details and impacted files@@ Coverage Diff @@
## main #2580 +/- ##
============================================
- Coverage 80.44% 80.42% -0.02%
- Complexity 4020 4024 +4
============================================
Files 332 333 +1
Lines 15160 15181 +21
Branches 1979 1979
============================================
+ Hits 12195 12210 +15
- Misses 2186 2192 +6
Partials 779 779
Help us with your feedback. Take ten seconds to tell us how you rate us. Have a feature suggestion? Share it here. ☔ View full report at Codecov. |
📜 Description
Use a new hub cloned from main hub for WebFlux requests instead of cloning the current one on the thread. This allows the hub, used for the request, to be garbage collected eventually. Previously we were using the current hub on the thread to call
.pushScope(), then cloned that hub including the stack and the call to.popScope()would go to the clone instead of the hub we called.pushScope()on. This means we were popping the copied stack of the cloned hub leaving the stack of the original hub untouched. This lead to an ever growing stack on the original hub, causing a memory leak and also scope bleed because the original hub survives across requests and is never cleaned up.💡 Motivation and Context
Improves #2557 but still holds on to one hub per thread + stack and scope etc.
I have some ideas for further improving this which I'll try later. I'd like to first ship this as it already makes the situation a lot better.
💚 How did you test it?
Manually running our sample and looking at
visualvmas well as debugging thread locals.wrk -t2 -c100 -d30s http://user:password@localhost:8080/person/91📝 Checklist
sendDefaultPIIis enabled.🔮 Next steps