getsentry / getsentry/sentry-dotnet

Weird AsyncLocal behaviour in MAUI

Open
#4,198 0 comments 0 reactions 0 assignees View on GitHub
.NET Bug
Dominant language
C#
Stars
770
Forks
248
Avg merge
3d 4h
Merged PRs (30d)
49

Description

## Problem

The `MauiCommunityToolkitMvvmEventsBinder` has some [code that wraps `AsyncRelayCommand` execution in a transaction]()... however it does something unexpected.

The first time this event triggers, there's no transaction on the scope and so a transaction gets created here.

Later, when the relay command finishes, the associated transaction is finished, which should reset the transaction on the scope (setting this to null):
[https://github.com/getsentry/sentry-dotnet/blob/8abc247566132a4b284d7fe24c44a93d84d32076/src/Sentry/TransactionTracer.cs#L399]()

And immediately after `span.Finish()` is called, this is the case (`scope.Transaction` has been reset).

However when the code runs for subsequent AsyncRelayCommands, for some reason the (completed) transaction is back on the scope again!

We created a [workaround in that PR]() but we really should dig into this to understand what's going on.

## Initial analysis

\_Originally posted by @jamescrosswell in [https://github.com/getsentry/sentry-dotnet/pull/4125#discussion_r2088443402\_]()

I don't quite understand what's happening yet, but most stuff on the Scope is global in MAUI cause we always set `IsGlobalMode = true` for MAUI apps. `Scope.Transaction` is an exception:
[https://github.com/getsentry/sentry-dotnet/blob/8abc247566132a4b284d7fe24c44a93d84d32076/src/Sentry/Scope.cs#L203]()

`AsyncLocal` has a [constructor overload]() that lets you wire up a ValueChanged event handler, which I did for `Scope._transaction` and there is definitely something very odd going on.

Putting a breakpoint in there, the first time it triggers is when our `OnPropertyChanged` fires, assigning `Scope.Transaction` for the first time (with the new transaction):

![image](https://uploads.linear.app/3ed206af-f87a-40aa-82a0-150bf83e43e6/a69462b5-2908-4b17-b22f-79af3b326a15/d78682bc-f5b1-4eb2-bbe8-55eb96ae0827?signature=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJwYXRoIjoiLzNlZDIwNmFmLWY4N2EtNDBhYS04MmEwLTE1MGJmODNlNDNlNi9hNjk0NjJiNS0yOTA4LTRiMTctYjIyZi03OWFmM2IzMjZhMTUvZDc4NjgyYmMtZjViMS00ZWIyLWJiZTgtNTVlYjk2YWUwODI3IiwiaWF0IjoxNzg0MzA5ODUwLCJleHAiOjE4MTU4ODA0MTB9.BP0aJfKWjE9L8z7RxGmUyr5QOj2kDbhGc3O9diPtq2s)

The it fires twice more with `args.ThreadContextChanged == true`:

![image](https://uploads.linear.app/3ed206af-f87a-40aa-82a0-150bf83e43e6/04a0fb7e-9bc8-443b-97cf-7ea8bb9c9913/59849c37-35b5-4f31-b599-d0442721d209?signature=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJwYXRoIjoiLzNlZDIwNmFmLWY4N2EtNDBhYS04MmEwLTE1MGJmODNlNDNlNi8wNGEwZmI3ZS05YmM4LTQ0M2ItOTdjZi03ZWE4YmI5Yzk5MTMvNTk4NDljMzctMzViNS00ZjMxLWI1OTktZDA0NDI3MjFkMjA5IiwiaWF0IjoxNzg0MzA5ODUwLCJleHAiOjE4MTU4ODA0MTB9.ZwMIFYbgILaypWtyYEdYQVCuEoZuOCei8aQMiHdDjRo)

![image](https://uploads.linear.app/3ed206af-f87a-40aa-82a0-150bf83e43e6/b12df88b-d134-4e42-9582-b90fdc995c01/4c10b1de-62db-4201-b122-375d82ce1491?signature=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJwYXRoIjoiLzNlZDIwNmFmLWY4N2EtNDBhYS04MmEwLTE1MGJmODNlNDNlNi9iMTJkZjg4Yi1kMTM0LTRlNDItOTU4Mi1iOTBmZGM5OTVjMDEvNGMxMGIxZGUtNjJkYi00MjAxLWIxMjItMzc1ZDgyY2UxNDkxIiwiaWF0IjoxNzg0MzA5ODUwLCJleHAiOjE4MTU4ODA0MTB9.cvDj1_6lxTMb1fwbfg2H6QN0H9WdwVGpg6zfYlMnZfE)

Then it fires again from our `OnPropertyChanged` event handler when the command finishes running (this is finishing the transaction and resetting `Scope.Transaction = null`:

![image](https://uploads.linear.app/3ed206af-f87a-40aa-82a0-150bf83e43e6/991ca915-9bc3-4ed0-a4b5-47da85d561f9/3ec8931b-5fbf-4cac-9415-dab57fa07004?signature=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJwYXRoIjoiLzNlZDIwNmFmLWY4N2EtNDBhYS04MmEwLTE1MGJmODNlNDNlNi85OTFjYTkxNS05YmMzLTRlZDAtYTRiNS00N2RhODVkNTYxZjkvM2VjODkzMWItNWZiZi00Y2FjLTk0MTUtZGFiNTdmYTA3MDA0IiwiaWF0IjoxNzg0MzA5ODUwLCJleHAiOjE4MTU4ODA0MTB9.aM9BmOTnsSLWUTV4AQA1xZnUzqCMpJ3Bclw1nFHBdMs)

However immediately after that there's a thread context change again and the property gets restored to a non-null value!!! 🐛 🐞 😞

![image](https://uploads.linear.app/3ed206af-f87a-40aa-82a0-150bf83e43e6/a7544844-aa7b-4bca-93c0-067e9fe04d25/784516f0-94af-4edf-a89c-56d04e464ce2?signature=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJwYXRoIjoiLzNlZDIwNmFmLWY4N2EtNDBhYS04MmEwLTE1MGJmODNlNDNlNi9hNzU0NDg0NC1hYTdiLTRiY2EtOTNjMC0wNjdlOWZlMDRkMjUvNzg0NTE2ZjAtOTRhZi00ZWRmLWE4OWMtNTZkMDRlNDY0Y2UyIiwiaWF0IjoxNzg0MzA5ODUwLCJleHAiOjE4MTU4ODA0MTB9.fXrQkRFq_gmQdV79pPr4t3vlNjpEorjBkAcldtJfwEg)

Contributor guide

Open the contributing guide

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.