Skip to content

Commit 4da3d62

Browse files
authored
Fix #2664 Backlog: move to full synchronous behavior since we're on a dedicated thread (#2667)
Resolving #2664. This went through a few iterations early on, but in the current form the issue is that we can await hop to a thread pool thread and have our wait blocking there - not awesome. We want to avoid that entirely and do a full sync path here.
1 parent baf2b1e commit 4da3d62

File tree

2 files changed

+11
-8
lines changed

2 files changed

+11
-8
lines changed

docs/ReleaseNotes.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,9 +8,10 @@ Current package versions:
88

99
## Unreleased
1010

11-
- Add new `LoggingTunnel` API; see https://stackexchange.github.io/StackExchange.Redis/Logging ([#2660 by mgravell](https://github.com/StackExchange/StackExchange.Redis/pull/2660))
1211
- **Potentiallty Breaking**: Fix `CheckTrustedIssuer` certificate validation for broken chain scenarios ([#2665 by NickCraver](https://github.com/StackExchange/StackExchange.Redis/pull/2665))
1312
- Users inadvertently trusting a remote cert with a broken chain could not be failing custom validation before this change. This is only in play if you are using `ConfigurationOptions.TrustIssuer` at all.
13+
- Add new `LoggingTunnel` API; see https://stackexchange.github.io/StackExchange.Redis/Logging ([#2660 by mgravell](https://github.com/StackExchange/StackExchange.Redis/pull/2660))
14+
- Fix [#2664](https://github.com/StackExchange/StackExchange.Redis/issues/2664): Move ProcessBacklog to fully sync to prevent thread pool hopping and blocking on awaits ([#2667 by NickCraver](https://github.com/StackExchange/StackExchange.Redis/pull/2667))
1415

1516
## 2.7.27
1617

src/StackExchange.Redis/PhysicalBridge.cs

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -904,7 +904,7 @@ private void StartBacklogProcessor()
904904
// to unblock the thread-pool when there could be sync-over-async callers. Note that in reality,
905905
// the initial "enough" of the back-log processor is typically sync, which means that the thread
906906
// we start is actually useful, despite thinking "but that will just go async and back to the pool"
907-
var thread = new Thread(s => ((PhysicalBridge)s!).ProcessBacklogAsync().RedisFireAndForget())
907+
var thread = new Thread(s => ((PhysicalBridge)s!).ProcessBacklog())
908908
{
909909
IsBackground = true, // don't keep process alive (also: act like the thread-pool used to)
910910
Name = "StackExchange.Redis Backlog", // help anyone looking at thread-dumps
@@ -985,7 +985,7 @@ internal enum BacklogStatus : byte
985985
/// Process the backlog(s) in play if any.
986986
/// This means flushing commands to an available/active connection (if any) or spinning until timeout if not.
987987
/// </summary>
988-
private async Task ProcessBacklogAsync()
988+
private void ProcessBacklog()
989989
{
990990
_backlogStatus = BacklogStatus.Starting;
991991
try
@@ -997,7 +997,7 @@ private async Task ProcessBacklogAsync()
997997
// TODO: vNext handoff this backlog to another primary ("can handle everything") connection
998998
// and remove any per-server commands. This means we need to track a bit of whether something
999999
// was server-endpoint-specific in PrepareToPushMessageToBridge (was the server ref null or not)
1000-
await ProcessBridgeBacklogAsync().ForAwait();
1000+
ProcessBridgeBacklog();
10011001
}
10021002

10031003
// The cost of starting a new thread is high, and we can bounce in and out of the backlog a lot.
@@ -1070,7 +1070,7 @@ private async Task ProcessBacklogAsync()
10701070
/// </summary>
10711071
private readonly AutoResetEvent _backlogAutoReset = new AutoResetEvent(false);
10721072

1073-
private async Task ProcessBridgeBacklogAsync()
1073+
private void ProcessBridgeBacklog()
10741074
{
10751075
// Importantly: don't assume we have a physical connection here
10761076
// We are very likely to hit a state where it's not re-established or even referenced here
@@ -1097,10 +1097,10 @@ private async Task ProcessBridgeBacklogAsync()
10971097

10981098
// try and get the lock; if unsuccessful, retry
10991099
#if NETCOREAPP
1100-
gotLock = await _singleWriterMutex.WaitAsync(TimeoutMilliseconds).ForAwait();
1100+
gotLock = _singleWriterMutex.Wait(TimeoutMilliseconds);
11011101
if (gotLock) break; // got the lock; now go do something with it
11021102
#else
1103-
token = await _singleWriterMutex.TryWaitAsync().ForAwait();
1103+
token = _singleWriterMutex.TryWait();
11041104
if (token.Success) break; // got the lock; now go do something with it
11051105
#endif
11061106
}
@@ -1132,7 +1132,9 @@ private async Task ProcessBridgeBacklogAsync()
11321132
if (result == WriteResult.Success)
11331133
{
11341134
_backlogStatus = BacklogStatus.Flushing;
1135-
result = await physical.FlushAsync(false).ForAwait();
1135+
#pragma warning disable CS0618 // Type or member is obsolete
1136+
result = physical.FlushSync(false, TimeoutMilliseconds);
1137+
#pragma warning restore CS0618 // Type or member is obsolete
11361138
}
11371139

11381140
_backlogStatus = BacklogStatus.MarkingInactive;

0 commit comments

Comments
 (0)