-
Notifications
You must be signed in to change notification settings - Fork 523
Conversation
StopAsync(); | ||
} | ||
|
||
_secondsSinceLastConnectionEvent++; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This relies on Tick()
being called exactly once per second, which seems fragile. Have you compared the perf of something like System.Diagnostics.Stopwatch()
, which would basically track the precise time of the last connection event, rather than an integer number of seconds?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It hadn't occurred to me to use a Stopwatch
to track the time between ticks - I'll try that out. When I first wrote this I did try 3 different ways to track time more accurately but they all had significant perf costs.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looking at how Stopwatch
is implemented (https://github.com/dotnet/corefx/blob/master/src/System.Runtime.Extensions/src/System/Diagnostics/Stopwatch.cs), I think it'll have the same perf costs as the other stuff I did try before.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In this case I would make a few changes:
- Track the time between connection events as milliseconds rather than seconds.
- Make the millseconds between ticks public rather than private (
KestrelThread._heartbeatMilliseconds
).KestrelHttpServer/src/Microsoft.AspNetCore.Server.Kestrel/Internal/Infrastructure/KestrelThread.cs
Line 31 in f2085b1
private const int _heartbeatMilliseconds = 1000;
- Update the call to
_heartbeatTimer.Start()
to use the public constant.KestrelHttpServer/src/Microsoft.AspNetCore.Server.Kestrel/Internal/Infrastructure/KestrelThread.cs
Line 283 in f2085b1
_heartbeatTimer.Start(OnHeartbeat, timeout: 1000, repeat: 1000);
- In
Tick()
, increment the elapsed milliseconds by the public constant.
I must have gotten it right this time, because there's almost no perf impact with my time tracking changes (494ffde) 😁 Averages of five runs of dev vs this change, difference in numbers is due to noise across runs: dev: 1130819.484 RPS |
_frame.Tick(); | ||
if (_timeoutEnabled) | ||
{ | ||
if (_millisecondsSinceLastConnectionEvent > ServerOptions.Limits.ConnectionTimeout.TotalMilliseconds) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Store this so ms doesn't need to be recalculated. You could also add a second to the configuration since _millisecondsSinceLastConnectionEvent
could be up to a second too large.
/// </summary> | ||
/// <remarks> | ||
/// Defaults to 2 minutes. Timeout granularity is in seconds. Sub-second values will be rounded to the next second. | ||
/// </remarks> | ||
public TimeSpan KeepAliveTimeout | ||
public TimeSpan ConnectionTimeout |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I would rather keep this a timeout for only time spent between request. Having this timeout trigger because an app takes a long time doing business logic before writing a response or completing seems bad to me (even if it would never kill the connection mid request or response).
void IConnectionControl.SetTimeout(long milliseconds) | ||
{ | ||
// Add KestrelThread.HeartbeatMilliseconds extra milliseconds since this can be called right before the next heartbeat. | ||
Interlocked.Exchange(ref _timeoutTimestamp, _lastTimestamp + milliseconds + KestrelThread.HeartbeatMilliseconds); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can we have a Debug.Assert or something verifying that the _timeoutTimestamp was long.MaxValue previously?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done.
mockLibuv.ReadCallback(socket.InternalGetHandle(), 0, ref ignored); | ||
Assert.False(connection.SocketInput.CheckFinOrThrow()); | ||
Connection connection = null; | ||
await context.Thread.PostAsync(_ => |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why does this need to be changed now?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Because Connection
's ctor calls UvLoopHandle.Now()
, so it needs to run in the same thread the loop was created.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ahhh
I like this sooo much better. Nice 👍 |
- Track time more accurately - Control timeout in Connection instead of Frame
8cd46b3
to
1a273f5
Compare
Connection
instead ofFrame
@halter73 @mikeharder @davidfowl