Running several SentrySdk.CaptureMessage in parallel freezes the MAUI Android app
Package
Sentry
.NET Flavor
.NET
.NET Version
9.0.203
OS
Android
OS Version
Android 15
Development Environment
Visual Studio v18.x
SDK Version
5.16.2
Self-Hosted Sentry Version
No response
Workload Versions
android 35.0.39/9.0.100 SDK 9.0.200, VS 17.13.35931.197 aspire 8.2.2/8.0.100 SDK 9.0.200, VS 17.13.35931.197 ios 18.2.9173/9.0.100 SDK 9.0.200, VS 17.13.35931.197 maccatalyst 18.2.9173/9.0.100 SDK 9.0.200, VS 17.13.35931.197 maui-windows 9.0.14/9.0.100 SDK 9.0.200, VS 17.13.35931.197
UseSentry or SentrySdk.Init call
.UseSentry("")
Steps to Reproduce
- Run SentrySdk.CaptureMessage("Hello") in parallel
- Use this somewhere in Main thread, like on a button press
Parallel.Invoke(
() => SentrySdk.CaptureMessage("Hello"),
() => SentrySdk.CaptureMessage("Hello"),
() => SentrySdk.CaptureMessage("Hello"),
() => SentrySdk.CaptureMessage("Hello"),
() => SentrySdk.CaptureMessage("Hello")
);
Expected Result
5 Messages will be reported to Sentry
Actual Result
App freezes and stops responding. This seems like a regression as at least the Sentry.Maui 5.4.0 version works fine with the same code. Please reach out if a sample is needed
@dalux-era I'm not able to reproduce this. I tried creating a brand new maui app with this:
private void OnCounterClicked(object? sender, EventArgs e)
{
count++;
if (count == 1)
CounterBtn.Text = $"Clicked {count} time";
else
CounterBtn.Text = $"Clicked {count} times";
Parallel.Invoke(
() => SentrySdk.CaptureMessage("Hello"),
() => SentrySdk.CaptureMessage("Hello"),
() => SentrySdk.CaptureMessage("Hello"),
() => SentrySdk.CaptureMessage("Hello"),
() => SentrySdk.CaptureMessage("Hello")
);
SemanticScreenReader.Announce(CounterBtn.Text);
}
The app runs without issue.
I tried using both dotnet SDK 9.0.203 and 9.0.308 (the most recent 9.x release).
Can you try running the same app in release configuration? It might be related to that. @jamescrosswell I tried running the same code you show in release and I could reproduce the bug
OK, in release mode I can reproduce this. Thanks @dalux-era - we'll look into it.
This one is a headscratcher... I added a bunch of logging to work out where it's coming from and I think it's coming from somewhere in this method: https://github.com/getsentry/sentry-dotnet/blob/876ec162482860676ff095e254187ebf7f6cd9bc/src/Sentry.Android.AssemblyReader/V2/AssemblyStoreReader.cs#L51-L76
That stacks up with the fact that only became an issue in the 5.5.0 release (which is when we introduced the v2 assembly readers).
However if I add further logging inside that method to try to determine more precisely which line is causing this, the problem goes away (even if I bump the number of parallel executions to 100 rather than 5). And sometimes when I add logging nowhere near that method, it goes away. Presumably the logging impacts the execution timings enough that whatever contention is causing the blocking is no longer an issue.
@dalux-era as a temporary workaround, I think you can set SentryOptions.AttachStacktrace = false when initialising Sentry... this seems to avoid the issue for me. If that setting is true, Sentry creates and attaches a stack trace even for events that don't have them by default (like CaptureMessage events). If it's false, you only get stack traces for things like exceptions.
It's worth pointing out that creating a stack trace is a really expensive operation. If you're planning on calling CreateMessage in a tight loop or in parallel like this, you probably want to disable this setting anyway, since it will really slow your app down (even once we resolve the issue with the hanging).
Ahhh, I think I might see an obvious issue actually. The store reader gets assigned to the options (a singleton) on startup... and this isn't really thread safe. If/when multiple threads are using this, they're all sharing the same StoreStream.
That's embarrassing, since I'm on the blame for that commit/code. I vendored it in from Microsoft... but clearly not carefully enough.
Thanks for looking into it @jamescrosswell ! Just an addition to the original ticket. The reproduction sample is just oversimplified version of the actual scenario. In our code, the app would freeze when 2 SentrySdk.CaptureException would be executed at the same time due to different event subscriptions. As you found out it seems like a thread so fingers crossed that this will also be fixed by you making operations threads-safe 💯