- Notifications
You must be signed in to change notification settings - Fork5.2k
Implement dynamic HTTP/2 window scaling#54755
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 ourterms of service andprivacy statement. We’ll occasionally send you account related emails.
Already on GitHub?Sign in to your account
Uh oh!
There was an error while loading.Please reload this page.
Changes from1 commit
1d418534067443a3800d39f507643c7c4746eb66e94c7f3ac473139df2ff4ee2bf13e0526d7237362e874dfcfeb6e9142be30ab7520416ac9f53e0a13d2066e6cc7c3b89e4bc1df65a23e18b0bfb3b90ea54dfdcdd8d2cc4f302c7e4d8639c7761e2e23cac870a04c3ddd6ea17b006a5bc2b9b595f9e0daba17355cbf0e1cd94dcf6954f616dbfa473009773File filter
Filter by extension
Conversations
Uh oh!
There was an error while loading.Please reload this page.
Jump to
Uh oh!
There was an error while loading.Please reload this page.
Diff view
Diff view
- Loading branch information
Uh oh!
There was an error while loading.Please reload this page.
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -135,7 +135,7 @@ public Http2Connection(HttpConnectionPool pool, Stream stream) | ||
| _connectionWindow = new CreditManager(this, nameof(_connectionWindow), DefaultInitialWindowSize); | ||
| _concurrentStreams = new CreditManager(this, nameof(_concurrentStreams), InitialMaxConcurrentStreams); | ||
| _rttEstimator = RttEstimator.Create(); | ||
| _writeChannel = Channel.CreateUnbounded<WriteQueueEntry>(s_channelOptions); | ||
| @@ -454,7 +454,7 @@ private async ValueTask ProcessHeadersFrame(FrameHeader frameHeader) | ||
| if (http2Stream != null) | ||
| { | ||
| http2Stream.OnHeadersStart(); | ||
| _rttEstimator.OnDataOrHeadersReceived(this); | ||
| headersHandler = http2Stream; | ||
| } | ||
| else | ||
| @@ -590,7 +590,7 @@ private void ProcessDataFrame(FrameHeader frameHeader) | ||
| if (!endStream && frameData.Length > 0) | ||
| { | ||
| _rttEstimator.OnDataOrHeadersReceived(this); | ||
| } | ||
| } | ||
| @@ -626,7 +626,7 @@ private void ProcessSettingsFrame(FrameHeader frameHeader, bool initialFrame = f | ||
| // We only send SETTINGS once initially, so we don't need to do anything in response to the ACK. | ||
| // Just remember that we received one and we won't be expecting any more. | ||
| _expectingSettingsAck = false; | ||
| _rttEstimator.OnInitialSettingsAckReceived(this); | ||
| } | ||
| else | ||
| { | ||
| @@ -2005,7 +2005,7 @@ private void ProcessPingAck(long payload) | ||
| // _keepAlivePingPayload is always non-negative. | ||
| if (payload < 0) // RTT ping | ||
| { | ||
| _rttEstimator.OnPingAckReceived(payload, this); | ||
| return; | ||
| } | ||
| else // Keepalive ping | ||
antonfirsov marked this conversation as resolved. Show resolvedHide resolvedUh oh!There was an error while loading.Please reload this page. | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -17,35 +17,30 @@ private struct Http2StreamWindowManager | ||
| private static int MaxStreamWindowSize => GlobalHttpSettings.SocketsHttpHandler.MaxHttp2StreamWindowSize; | ||
| private static bool WindowScalingEnabled => !GlobalHttpSettings.SocketsHttpHandler.DisableDynamicHttp2WindowSizing; | ||
| private int _deliveredBytes; | ||
| private int _streamWindowSize; | ||
| private long _lastWindowUpdate; | ||
Member There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others.Learn more. You could potentially use ContributorAuthor There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others.Learn more. I stole the the unit conversion logic from Member There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others.Learn more. Well, the ContributorAuthor There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others.Learn more. A bit hesitant to change & rebenchmark at this point, may try on Monday. ContributorAuthor There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others.Learn more. What I mean is that: TimeSpandt=_stopwatch.GetElapsedTime();_stopwatch=ValueStopwatch.StartNew(); will call | ||
| public Http2StreamWindowManager(Http2Connection connection, Http2Stream stream) | ||
| { | ||
| HttpConnectionSettings settings = connection._pool.Settings; | ||
| _streamWindowSize = settings._initialHttp2StreamWindowSize; | ||
| _lastWindowUpdate = Stopwatch.GetTimestamp(); | ||
| ||
| _deliveredBytes = 0; | ||
| if (NetEventSource.Log.IsEnabled())stream.Trace($"[FlowControl] InitialClientStreamWindowSize: {StreamWindowSize}, StreamWindowThreshold: {StreamWindowThreshold}, WindowScaleThresholdMultiplier: {WindowScaleThresholdMultiplier}"); | ||
| } | ||
| internal int StreamWindowSize => _streamWindowSize; | ||
| internal int StreamWindowThreshold => _streamWindowSize / StreamWindowUpdateRatio; | ||
| public void AdjustWindow(int bytesConsumed, Http2Stream stream) | ||
| { | ||
| Debug.Assert(bytesConsumed > 0); | ||
| Debug.Assert(_deliveredBytes < StreamWindowThreshold); | ||
| if (!stream.ExpectResponseData) | ||
| { | ||
| // We are not expecting any more data (because we've either completed or aborted). | ||
| // So no need to send any more WINDOW_UPDATEs. | ||
| @@ -54,15 +49,15 @@ public void AdjustWindow(int bytesConsumed) | ||
| if (WindowScalingEnabled) | ||
| { | ||
| AdjustWindowDynamic(bytesConsumed, stream); | ||
| } | ||
| else | ||
| { | ||
| AjdustWindowStatic(bytesConsumed, stream); | ||
| } | ||
| } | ||
| private void AjdustWindowStatic(int bytesConsumed, Http2Stream stream) | ||
| { | ||
| _deliveredBytes += bytesConsumed; | ||
| if (_deliveredBytes < StreamWindowThreshold) | ||
| @@ -73,11 +68,12 @@ private void AjdustWindowStatic(int bytesConsumed) | ||
| int windowUpdateIncrement = _deliveredBytes; | ||
| _deliveredBytes = 0; | ||
| Http2Connection connection = stream.Connection; | ||
| Task sendWindowUpdateTask = connection.SendWindowUpdateAsync(stream.StreamId, windowUpdateIncrement); | ||
| connection.LogExceptions(sendWindowUpdateTask); | ||
| } | ||
| private void AdjustWindowDynamic(int bytesConsumed, Http2Stream stream) | ||
| { | ||
| _deliveredBytes += bytesConsumed; | ||
| @@ -88,10 +84,11 @@ private void AdjustWindowDynamic(int bytesConsumed) | ||
| int windowUpdateIncrement = _deliveredBytes; | ||
| long currentTime = Stopwatch.GetTimestamp(); | ||
| Http2Connection connection = stream.Connection; | ||
| if (connection._rttEstimator.MinRtt > TimeSpan.Zero) | ||
| { | ||
| TimeSpan rtt =connection._rttEstimator.MinRtt; | ||
| TimeSpan dt = StopwatchTicksToTimeSpan(currentTime - _lastWindowUpdate); | ||
| // We are detecting bursts in the amount of data consumed within a single 'dt' window update period. | ||
| @@ -109,20 +106,20 @@ private void AdjustWindowDynamic(int bytesConsumed) | ||
| windowUpdateIncrement += extendedWindowSize - _streamWindowSize; | ||
| _streamWindowSize = extendedWindowSize; | ||
| if (NetEventSource.Log.IsEnabled())stream.Trace($"[FlowControl] Updated Stream Window. StreamWindowSize: {StreamWindowSize}, StreamWindowThreshold: {StreamWindowThreshold}"); | ||
| Debug.Assert(_streamWindowSize <= MaxStreamWindowSize); | ||
| if (_streamWindowSize == MaxStreamWindowSize) | ||
| { | ||
| if (NetEventSource.Log.IsEnabled())stream.Trace($"[FlowControl] StreamWindowSize reached the configured maximum of {MaxStreamWindowSize}."); | ||
| } | ||
| } | ||
| } | ||
| _deliveredBytes = 0; | ||
| Task sendWindowUpdateTask =connection.SendWindowUpdateAsync(stream.StreamId, windowUpdateIncrement); | ||
| connection.LogExceptions(sendWindowUpdateTask); | ||
| _lastWindowUpdate = currentTime; | ||
| } | ||
| @@ -144,11 +141,10 @@ private enum State | ||
| PingSent | ||
| } | ||
| private const double PingIntervalInSeconds = 2; | ||
| private const int InitialBurstCount = 4; | ||
| private static readonly long PingIntervalInTicks =(long)(PingIntervalInSeconds * Stopwatch.Frequency); | ||
antonfirsov marked this conversation as resolved. OutdatedShow resolvedHide resolvedUh oh!There was an error while loading.Please reload this page. | ||
| private State _state; | ||
| private long _pingSentTimestamp; | ||
| private long _pingCounter; | ||
| @@ -157,14 +153,12 @@ private enum State | ||
| public TimeSpan MinRtt => new TimeSpan(_minRtt); | ||
| publicstaticRttEstimator Create() | ||
| { | ||
| RttEstimator e = default; | ||
| e._state = GlobalHttpSettings.SocketsHttpHandler.DisableDynamicHttp2WindowSizing ? State.Disabled : State.Init; | ||
| e._initialBurst = InitialBurstCount; | ||
| return e; | ||
| } | ||
| internal void OnInitialSettingsSent() | ||
| @@ -173,14 +167,14 @@ internal void OnInitialSettingsSent() | ||
| _pingSentTimestamp = Stopwatch.GetTimestamp(); | ||
| } | ||
| internal void OnInitialSettingsAckReceived(Http2Connection connection) | ||
| { | ||
| if (_state == State.Disabled) return; | ||
| RefreshRtt(connection); | ||
| _state = State.Waiting; | ||
| } | ||
| internal void OnDataOrHeadersReceived(Http2Connection connection) | ||
| { | ||
| if (_state != State.Waiting) return; | ||
| @@ -192,13 +186,13 @@ internal void OnDataOrHeadersReceived() | ||
| // Send a PING | ||
| _pingCounter--; | ||
| connection.LogExceptions(connection.SendPingAsync(_pingCounter, isAck: false)); | ||
| _pingSentTimestamp = now; | ||
| _state = State.PingSent; | ||
| } | ||
| } | ||
| internal void OnPingAckReceived(long payload, Http2Connection connection) | ||
| { | ||
| if (_state != State.PingSent) | ||
| { | ||
| @@ -211,7 +205,7 @@ internal void OnPingAckReceived(long payload) | ||
| if (_pingCounter != payload) | ||
| ThrowProtocolError(); | ||
| RefreshRtt(connection); | ||
| _state = State.Waiting; | ||
| } | ||
| @@ -220,7 +214,7 @@ internal void OnGoAwayReceived() | ||
| _state = State.Disabled; | ||
| } | ||
| private void RefreshRtt(Http2Connection connection) | ||
| { | ||
| long elapsedTicks = Stopwatch.GetTimestamp() - _pingSentTimestamp; | ||
antonfirsov marked this conversation as resolved. Show resolvedHide resolvedUh oh!There was an error while loading.Please reload this page. | ||
| long prevRtt = _minRtt == 0 ? long.MaxValue : _minRtt; | ||
| @@ -229,7 +223,7 @@ private void RefreshRtt() | ||
| Interlocked.Exchange(ref _minRtt, minRtt); // MinRtt is being queried from another thread | ||
| if (NetEventSource.Log.IsEnabled())connection.Trace($"[FlowControl] Updated MinRtt: {MinRtt.TotalMilliseconds} ms"); | ||
| } | ||
| } | ||
| } | ||