forked from dotnet/runtime
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
[wasm] spread WS based timers over next 6 minutes to prevent heavy th…
…rottling (dotnet#57745) spread WS based timers over next 6 minutes to prevent heavy throttling
- Loading branch information
1 parent
1fa304a
commit 5eaa911
Showing
8 changed files
with
334 additions
and
10 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
181 changes: 181 additions & 0 deletions
181
src/libraries/System.Net.WebSockets.Client/tests/wasm/BrowserTimerThrottlingTest.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,181 @@ | ||
// Licensed to the .NET Foundation under one or more agreements. | ||
// The .NET Foundation licenses this file to you under the MIT license. | ||
|
||
using System.Collections.Generic; | ||
using System.Linq; | ||
using System.Net.WebSockets.Client.Tests; | ||
using System.Runtime.InteropServices; | ||
using System.Text; | ||
using System.Threading; | ||
using System.Threading.Tasks; | ||
|
||
using Xunit; | ||
using Xunit.Abstractions; | ||
using Xunit.Sdk; | ||
|
||
namespace System.Net.WebSockets.Client.Wasm.Tests | ||
{ | ||
// https://developer.chrome.com/blog/timer-throttling-in-chrome-88/ | ||
// https://docs.google.com/document/d/11FhKHRcABGS4SWPFGwoL6g0ALMqrFKapCk5ZTKKupEk/view | ||
// requires chromium based browser | ||
// requires minimized browser or browser tab out of focus, browser can't be headless | ||
// requires --enable-features=IntensiveWakeUpThrottling:grace_period_seconds/1 chromeDriver flags | ||
// doesn't work with --disable-background-timer-throttling | ||
[TestCaseOrderer("System.Net.WebSockets.Client.Wasm.Tests.AlphabeticalOrderer", "System.Net.WebSockets.Client.Wasm.Tests")] | ||
public class BrowserTimerThrottlingTest : ClientWebSocketTestBase | ||
{ | ||
public static bool IsBrowser => RuntimeInformation.IsOSPlatform(OSPlatform.Create("BROWSER")); | ||
const double moreThanLightThrottlingThreshold = 1900; | ||
const double detectLightThrottlingThreshold = 900; | ||
const double webSocketMessageFrequency = 45000; | ||
const double fastTimeoutFrequency = 100; | ||
|
||
public BrowserTimerThrottlingTest(ITestOutputHelper output) : base(output) { } | ||
|
||
[ConditionalFact(nameof(PlatformDetection.IsBrowser))] | ||
[OuterLoop] // involves long delay | ||
// this test is influenced by usage of WS on the same browser tab in previous unit tests. we may need to wait long time for it to fizzle down | ||
public async Task DotnetTimersAreHeavilyThrottledWithoutWebSocket() | ||
{ | ||
double maxDelayMs = 0; | ||
double maxLightDelayMs = 0; | ||
DateTime start = DateTime.Now; | ||
CancellationTokenSource cts = new CancellationTokenSource(); | ||
|
||
using (var timer = new Timers.Timer(fastTimeoutFrequency)) | ||
{ | ||
DateTime last = DateTime.Now; | ||
timer.AutoReset = true; | ||
timer.Enabled = true; | ||
timer.Elapsed += (object? source, Timers.ElapsedEventArgs? e) => | ||
{ | ||
var ms = (e.SignalTime - last).TotalMilliseconds; | ||
if (maxDelayMs < ms) | ||
{ | ||
maxDelayMs = ms; | ||
} | ||
if (ms > moreThanLightThrottlingThreshold) | ||
{ | ||
#if DEBUG | ||
Console.WriteLine("Too slow tick " + ms); | ||
#endif | ||
// stop, we are throttled heavily, this is what we are looking for | ||
cts.Cancel(); | ||
} | ||
else if (ms > detectLightThrottlingThreshold) | ||
{ | ||
maxLightDelayMs = ms; | ||
// we are lightly throttled | ||
#if DEBUG | ||
Console.WriteLine("Slow tick NO-WS " + ms); | ||
#endif | ||
} | ||
last = e.SignalTime; | ||
}; | ||
|
||
// test it for 10 minutes | ||
try { await Task.Delay(10 * 60 * 1000, cts.Token); } catch (Exception) { } | ||
timer.Close(); | ||
} | ||
Assert.True(maxDelayMs > detectLightThrottlingThreshold, "Expect that it throttled lightly " + maxDelayMs); | ||
Assert.True(maxDelayMs > moreThanLightThrottlingThreshold, "Expect that it was heavily throttled " + maxDelayMs); | ||
} | ||
|
||
[ConditionalFact(nameof(WebSocketsSupported), nameof(PlatformDetection.IsBrowser))] | ||
[OuterLoop] // involves long delay | ||
public async Task WebSocketKeepsDotnetTimersOnlyLightlyThrottled() | ||
{ | ||
double maxDelayMs = 0; | ||
double maxLightDelayMs = 0; | ||
DateTime start = DateTime.Now; | ||
CancellationTokenSource cts = new CancellationTokenSource(); | ||
|
||
using (ClientWebSocket cws = await WebSocketHelper.GetConnectedWebSocket(Test.Common.Configuration.WebSockets.RemoteEchoServer, TimeOutMilliseconds, _output)) | ||
{ | ||
await SendAndReceive(cws, "test"); | ||
using (var timer = new Timers.Timer(fastTimeoutFrequency)) | ||
{ | ||
DateTime last = DateTime.Now; | ||
DateTime lastSent = DateTime.MinValue; | ||
timer.AutoReset = true; | ||
timer.Enabled = true; | ||
timer.Elapsed += async (object? source, Timers.ElapsedEventArgs? e) => | ||
{ | ||
var ms = (e.SignalTime - last).TotalMilliseconds; | ||
var msSent = (e.SignalTime - lastSent).TotalMilliseconds; | ||
if (maxDelayMs < ms) | ||
{ | ||
maxDelayMs = ms; | ||
} | ||
if (ms > moreThanLightThrottlingThreshold) | ||
{ | ||
// fail fast, we are throttled heavily | ||
#if DEBUG | ||
Console.WriteLine("Too slow tick " + ms); | ||
#endif | ||
cts.Cancel(); | ||
} | ||
else if (ms > detectLightThrottlingThreshold) | ||
{ | ||
maxLightDelayMs = ms; | ||
// we are lightly throttled | ||
#if DEBUG | ||
Console.WriteLine("Slow tick WS " + ms); | ||
#endif | ||
} | ||
if (msSent > webSocketMessageFrequency) | ||
{ | ||
await SendAndReceive(cws, "test"); | ||
lastSent = DateTime.Now; | ||
} | ||
last = e.SignalTime; | ||
}; | ||
|
||
// test it for 10 minutes | ||
try { await Task.Delay(10 * 60 * 1000, cts.Token); } catch (Exception) { } | ||
timer.Close(); | ||
} | ||
await cws.CloseAsync(WebSocketCloseStatus.NormalClosure, "WebSocketKeepsDotnetTimersOnlyLightlyThrottled", CancellationToken.None); | ||
} | ||
Assert.True(maxDelayMs > detectLightThrottlingThreshold, "Expect that it throttled lightly " + maxDelayMs); | ||
Assert.True(maxDelayMs < moreThanLightThrottlingThreshold, "Expect that it wasn't heavily throttled " + maxDelayMs); | ||
} | ||
|
||
private async static Task SendAndReceive(ClientWebSocket cws, string message) | ||
{ | ||
try | ||
{ | ||
byte[] buffer = Encoding.UTF8.GetBytes(message); | ||
await cws.SendAsync(new ArraySegment<byte>(buffer), WebSocketMessageType.Text, true, CancellationToken.None); | ||
|
||
var receiveBuffer = new byte[100]; | ||
var receiveSegment = new ArraySegment<byte>(receiveBuffer); | ||
WebSocketReceiveResult recvRet = await cws.ReceiveAsync(receiveSegment, CancellationToken.None); | ||
#if DEBUG | ||
Console.WriteLine("SendAndReceive"); | ||
#endif | ||
} | ||
catch (OperationCanceledException) | ||
{ | ||
} | ||
catch (Exception ex) | ||
{ | ||
#if DEBUG | ||
Console.WriteLine("SendAndReceive fail:" + ex); | ||
#endif | ||
} | ||
} | ||
} | ||
|
||
// this is just for convinience, as the second test has side-effect to running page, the first test would take longer if they are in another order | ||
public class AlphabeticalOrderer : ITestCaseOrderer | ||
{ | ||
public IEnumerable<TTestCase> OrderTestCases<TTestCase>(IEnumerable<TTestCase> testCases) | ||
where TTestCase : ITestCase | ||
{ | ||
List<TTestCase> result = testCases.ToList(); | ||
result.Sort((x, y) => StringComparer.Ordinal.Compare(x.TestMethod.Method.Name, y.TestMethod.Method.Name)); | ||
return result; | ||
} | ||
} | ||
} |
66 changes: 66 additions & 0 deletions
66
...es/System.Net.WebSockets.Client/tests/wasm/System.Net.WebSockets.Client.Wasm.Tests.csproj
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,66 @@ | ||
<Project Sdk="Microsoft.NET.Sdk"> | ||
<PropertyGroup> | ||
<StringResourcesPath>../../src/Resources/Strings.resx</StringResourcesPath> | ||
<TargetFrameworks>$(NetCoreAppCurrent)-Browser</TargetFrameworks> | ||
<DefineConstants>$(DefineConstants);NETSTANDARD</DefineConstants> | ||
</PropertyGroup> | ||
|
||
<PropertyGroup Condition="'$(TargetOS)' == 'Browser'"> | ||
<WasmXHarnessArgs>--background-throttling</WasmXHarnessArgs> | ||
<Scenario>WasmTestOnBrowser</Scenario> | ||
<TestArchiveTestsRoot>$(TestArchiveRoot)browseronly/</TestArchiveTestsRoot> | ||
<TestArchiveTestsDir>$(TestArchiveTestsRoot)$(OSPlatformConfig)/</TestArchiveTestsDir> | ||
<DefineConstants>$(DefineConstants);TARGET_BROWSER</DefineConstants> | ||
</PropertyGroup> | ||
|
||
<Import Condition="'$(TargetOS)' == 'Browser'" Project="$(CommonTestPath)System/Net/Prerequisites/LocalEchoServer.props" /> | ||
|
||
<!-- Browser specific files --> | ||
<ItemGroup Condition="'$(TargetOS)' == 'Browser'"> | ||
<ProjectReference Include="$(CommonTestPath)System/Net/Prerequisites/NetCoreServer/NetCoreServer.csproj" ReferenceOutputAssembly="false"/> | ||
<ProjectReference Include="$(CommonTestPath)System/Net/Prerequisites/RemoteLoopServer/RemoteLoopServer.csproj" ReferenceOutputAssembly="false"/> | ||
<Compile Include="$(CommonTestPath)System\Net\WebSockets\WebSocketStream.cs" | ||
Link="Common\System\Net\WebSockets\WebSocketStream.cs" /> | ||
</ItemGroup> | ||
|
||
<!-- Do not reference these assemblies from the TargetingPack since we are building part of the source code for tests. --> | ||
<ItemGroup> | ||
<DefaultReferenceExclusion Include="System.Configuration" /> | ||
</ItemGroup> | ||
<ItemGroup> | ||
<Compile Include="$(CommonTestPath)System\Net\Capability.Security.cs" | ||
Link="Common\System\Net\Capability.Security.cs" /> | ||
<Compile Include="$(CommonTestPath)System\Net\Configuration.cs" | ||
Link="Common\System\Net\Configuration.cs" /> | ||
<Compile Include="$(CommonTestPath)System\Net\Configuration.Certificates.cs" | ||
Link="Common\System\Net\Configuration.Certificates.cs" /> | ||
<Compile Include="$(CommonTestPath)System\Net\Configuration.Http.cs" | ||
Link="Common\System\Net\Configuration.Http.cs" /> | ||
<Compile Include="$(CommonTestPath)System\Net\Configuration.Security.cs" | ||
Link="Common\System\Net\Configuration.Security.cs" /> | ||
<Compile Include="$(CommonTestPath)System\Net\Configuration.WebSockets.cs" | ||
Link="Common\System\Net\Configuration.WebSockets.cs" /> | ||
<Compile Include="$(CommonTestPath)System\Net\EventSourceTestLogging.cs" | ||
Link="Common\System\Net\EventSourceTestLogging.cs" /> | ||
<Compile Include="$(CommonTestPath)System\Net\Http\LoopbackProxyServer.cs" | ||
Link="Common\System\Net\Http\LoopbackProxyServer.cs" /> | ||
<Compile Include="$(CommonTestPath)System\Net\Http\LoopbackServer.cs" | ||
Link="Common\System\Net\Http\LoopbackServer.cs" /> | ||
<Compile Include="$(CommonTestPath)System\Net\Http\GenericLoopbackServer.cs" | ||
Link="Common\System\Net\Http\GenericLoopbackServer.cs" /> | ||
<Compile Include="$(CommonTestPath)System\Security\Cryptography\PlatformSupport.cs" | ||
Link="CommonTest\System\Security\Cryptography\PlatformSupport.cs" /> | ||
<Compile Include="$(CommonTestPath)System\Threading\Tasks\TaskTimeoutExtensions.cs" | ||
Link="Common\System\Threading\Tasks\TaskTimeoutExtensions.cs" /> | ||
<Compile Include="..\ClientWebSocketTestBase.cs" /> | ||
<Compile Include="..\WebSocketHelper.cs" /> | ||
<Compile Include="..\LoopbackHelper.cs" /> | ||
<Compile Include="..\ResourceHelper.cs" /> | ||
<Compile Include="..\WebSocketData.cs" /> | ||
<Compile Include="BrowserTimerThrottlingTest.cs" /> | ||
</ItemGroup> | ||
<ItemGroup> | ||
<PackageReference Include="System.Net.TestData" Version="$(SystemNetTestDataVersion)" /> | ||
</ItemGroup> | ||
|
||
</Project> |
25 changes: 25 additions & 0 deletions
25
...aries/System.Net.WebSockets.Client/tests/wasm/System.Net.WebSockets.Client.Wasm.Tests.sln
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,25 @@ | ||
|
||
Microsoft Visual Studio Solution File, Format Version 12.00 | ||
# Visual Studio Version 17 | ||
VisualStudioVersion = 17.0.31521.260 | ||
MinimumVisualStudioVersion = 10.0.40219.1 | ||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "System.Net.WebSockets.Client.Wasm.Tests", "System.Net.WebSockets.Client.Wasm.Tests.csproj", "{DA6DF153-169E-485D-90A4-EDBDA54B32A6}" | ||
EndProject | ||
Global | ||
GlobalSection(SolutionConfigurationPlatforms) = preSolution | ||
Debug|Any CPU = Debug|Any CPU | ||
Release|Any CPU = Release|Any CPU | ||
EndGlobalSection | ||
GlobalSection(ProjectConfigurationPlatforms) = postSolution | ||
{DA6DF153-169E-485D-90A4-EDBDA54B32A6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU | ||
{DA6DF153-169E-485D-90A4-EDBDA54B32A6}.Debug|Any CPU.Build.0 = Debug|Any CPU | ||
{DA6DF153-169E-485D-90A4-EDBDA54B32A6}.Release|Any CPU.ActiveCfg = Release|Any CPU | ||
{DA6DF153-169E-485D-90A4-EDBDA54B32A6}.Release|Any CPU.Build.0 = Release|Any CPU | ||
EndGlobalSection | ||
GlobalSection(SolutionProperties) = preSolution | ||
HideSolutionNode = FALSE | ||
EndGlobalSection | ||
GlobalSection(ExtensibilityGlobals) = postSolution | ||
SolutionGuid = {627DB5DF-DE31-44B4-B5FD-E5666467145E} | ||
EndGlobalSection | ||
EndGlobal |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.