getsentry / sentry

Developer-first error tracking and performance monitoring
https://sentry.io
Other
37.54k stars 4.04k forks source link

Profiling with ts-node / express #73234

Open anuraagy opened 2 weeks ago

anuraagy commented 2 weeks ago

Problem Statement

We have an express/node/typescript backend and we set up Sentry profiler. However, most of the functions are showing up in the profiler with name "fulfilled" or "__awaiter". Any idea why that might be happening?

We use ts-node to run our application!

Solution Brainstorm

No response

Product Area

Profiling

getsantry[bot] commented 2 weeks ago

Assigning to @getsentry/support for routing ⏲️

getsantry[bot] commented 2 weeks ago

Routing to @getsentry/product-owners-profiling for triage ⏲️

JonasBa commented 2 weeks ago

@anuraagy that is the result of TS transpiling async/await expressions. I made a quick reproduction here where you can see the resulting expressions being transpiled from async/away

anuraagy commented 1 week ago

HI @JonasBa , thank you for the response. Is there any way around this? At this point the profiler isn't super useful because we don't have the function level insights anymore.

JonasBa commented 1 week ago

@anuraagy I need to take some time and see what might be some possible workarounds. Would you mind sharing how these frames look like in the profiling product?

anuraagy commented 1 week ago
Screen Shot 2024-06-25 at 1 53 45 PM Screen Shot 2024-06-25 at 1 56 27 PM

Everything is nested under fulfilled. Is this helpful?

JonasBa commented 1 week ago

@anuraagy Yes, thanks for sharing! This is sadly a limitation of async/await implementation inside v8. There is a good explanation here, but the tldr is that async await stack traces arent really a single stack trace and are actually spread out over time, hence why you seem them all being disconnected at the fulfilled frame (where async happens).

In such cases, we would suggest you to focus on the aggregate flamegraph and look for functions with high sample count. The main idea here is that by optimizing functions that are often occupying the call stack, you can still improve the runtime of the program without focusing on its chronological execution