-
Notifications
You must be signed in to change notification settings - Fork 1.5k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
sync-over-async in ConnectionMultiplexer #2223
Comments
I'm trying to understand what the ask is here - what's the issue? We offer a full async path in |
Yes, I was not clear at all, sorry. |
@gdoron are you seeing a lock around this? We can add a |
I did see a deadlock there. For now we got around this with simply not adding DataProtection with redis in UTs, since anyway it's a single device\pod. But Ideally, I would say it would be better if redis.StackExchange would keep |
We do this most places, but agree this is one of the few it isn't. For context: |
This scenario is hugely contextual. I absolutely agree that sync-over-async is a dangerous anti-pattern that should be aggressively avoided; that doesn't mean it is entirely without massively caveated usage, nor does it mean that it is inherently doomed to deadlock - by which I mean: in the evil nasty times you do use it, it is still marginally possible to avoid the worst outcomes, for example deadlocks. Now; the scenario you cite in the example here is So: the interesting question becomes: does the shared task-based code fail to guard against deadlocks? I'm very aware of the sync-context concerns here, so I've added some tests to investigate this; I have one unexpected failure surrounding To be explicit: the deadlock scenario you're referring to is presumably an async operation in the library calling back into the sync-context via |
^^^ tl;dr; I can't find a problem in the code you linked to, but I have found a tangential related problem, which I will investigate; this found problem is very specific to one API, so if you have more details on where you've observed a deadlock: please let me know so I can investigate that too |
this specifically is the failing test/scenario (the first method - [Fact]
public void SyncConfigure()
{
using var ctx = new MySyncContext();
using var conn = Create();
Assert.Equal(0, ctx.OpCount);
Assert.True(conn.Configure());
Assert.Equal(0, ctx.OpCount);
}
[Theory]
[InlineData(true)] // net472: pass; net6.0: fail (expected 0, actual 1)
[InlineData(false)] // net472\net6.0: fail (expected 0, actual 1)
public async Task AsyncConfigure(bool continueOnCapturedContext)
{
using var ctx = new MySyncContext();
using var conn = Create();
Assert.Equal(0, ctx.OpCount);
Assert.True(await conn.ConfigureAsync(Writer).ConfigureAwait(continueOnCapturedContext));
if (continueOnCapturedContext)
{
Assert.True(ctx.OpCount > 0, $"Opcount: {ctx.OpCount}");
}
else
{
Assert.Equal(0, ctx.OpCount);
}
} There seem to be two different potential failures here that I'm investigating:
|
Good news: the sync-context usage in I'm continuing to investigate the ran-to-completion issue, because that is even more of a head-scratcher (edit: this just turned out to be race conditions in the test, with local near-zero latency) |
@NickCraver ready for review ^^^ |
Sorry for ghosting, for some reason github did not send me notifications. I've put the sync code connecting redis inside a (disgusting) if block, to avoid this thing all together, so it does not reproduce any more (and even before like with all deadlocks, not easy to reproduce).
But I believe the dead lock was in this line: I hope it helps, and thanks for the fix! @mgravell |
Hi,
While I was searching for deadlocks in our UTs projects, I've found multiple 3rd libraries use sync-over-async (anti-)pattern.
One of which was redis.stackexchange
StackExchange.Redis/src/StackExchange.Redis/ConnectionMultiplexer.cs
Lines 697 to 699 in 7293213
It's even a bigger problem considering xUnit is using 2 SynchronizationContexts, even in .NET Core: xunit/xunit#2573
And that ASP.NET Core itself is using blocking apis: dotnet/aspnetcore#43353
Since adding
DataProtection
is being done on application startup:public void ConfigureServices(IServiceCollection services)
, we cannot use the async versions of redis.stackexchange.For reference, @mgravell's post from 8 years ago about the wonders of sync-over-async... 😉
https://blog.marcgravell.com/2014/03/beware-jamming-thread-pool.html
The text was updated successfully, but these errors were encountered: