Skip to content
This repository was archived by the owner on Dec 18, 2018. It is now read-only.

[DESIGN] Damianedwards/date header perf #167

Closed
wants to merge 3 commits into from

Conversation

DamianEdwards
Copy link
Member

Design review please.

@halter73 @lodejard @davidfowl @Tratcher

@dnfclas
Copy link

dnfclas commented Aug 19, 2015

Hi @DamianEdwards, I'm your friendly neighborhood .NET Foundation Pull Request Bot (You can call me DNFBOT). Thanks for your contribution!
You've already signed the contribution license agreement. Thanks!

The agreement was validated by .NET Foundation and real humans are currently evaluating your PR.

TTYL, DNFBOT;

{
// Immediately assign the date value and start the timer again
_dateValue = DateTime.UtcNow.ToString("r");
_dateValueTimer = new Timer(UpdateDateValue, state: null, dueTime: _timerInterval, period: _timerInterval);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why not create the timer in the constructor and just do Change() here? Then, _dateValueTimer != null is an invariant and wouldn't need to be checked all the time.

Also: Why not use dueTime: TimeSpan.Zero to run the handler immediately, which sets the _dateValue immediately.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why not create the timer in the constructor and just do Change() here? Then, _dateValueTimer != null is an invariant and wouldn't need to be checked all the time.

_dateValueTimer == null is the thing determining whether it needs to be switched on again. Could use boolean with _dateValueTimer.Change() but that may make the logic more complicated?

Why not use dueTime: TimeSpan.Zero to run the handler immediately

That would queue the callback to run immediately; but doesn't call it as part of the Timer constructor so the initialisation of the value would be in a race with the function return; which it would probably always loose. Though that would be fixed by the null coalescing in GetDateHeaderValue as it would create its own value.

However, not sure switching it off during low request periods vs creating a string every second with a single timer has much benefit as the timer isn't necessarily a real "individual" timer? http://referencesource.microsoft.com/#mscorlib/system/threading/timer.cs,29

We use a single native timer, supplied by the VM, to schedule all managed timers in the AppDomain.

So in the spirit of

Why not create the timer in the constructor

Just switch the timer on in constructor and leave it ticking away until shut down? The side effect would be the GC has to clean up a string every second when the server isn't doing anything; but its happy doing that when the server is fully loaded; and the logic becomes much simpler in UpdateDateValue and you can drop PumpTimer and the locks altogether?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Here is a different thought for the entire implementation that completely removes any need for synchronization:

http://docs.libuv.org/en/latest/timer.html

There would be one timer per Kestrel thread, but on the other hand the DateTime string wouldn't be shared between processors and every update is already in the "correct" processor's cache.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The libuv timers are implemented in more or less the same way on linux and win as the .net timer; but you'd pay the added overhead of interop'ing the callback and the code for it would be less clear.

As Kestrel uses multiple (maybe?) libuv threads for socket IO then as you say, this would update in different cpu caches. However, as its a write once per second, read many times the processor cache would only have to refresh once per second (or once every 100k? executions) so it wouldn't be that significant; and it would put extra book keeping on the socket IO threads.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There was concern over having any code running when the process is idle as it may interfere with mechanisms that perform operations on or in relation to idle processes. This design allows us to run the timer only when there are requests.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Process suspension type things? If is a requirement LGTM

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yep, that was one of the scenarios.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That requirement makes this solution rather elegant 👍

@benaadams
Copy link
Contributor

Added extra bit to: #169

@halter73
Copy link
Member

I like the design 👍

@muratg
Copy link
Contributor

muratg commented Sep 9, 2015

@DamianEdwards Merge or close?

{
_Server = new[] { "Kestrel" };
_Date = new[] { DateTime.UtcNow.ToString("r") };
_Date = new[] { dateHeaderValue };
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This needs to be set at the end of the response rather than the beginning. Things like the error handling middleware will clear the response headers.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, we'll do that as a separate change.

@DamianEdwards
Copy link
Member Author

@muratg I'll close it and resubmit after rebasing and adding tests

@muratg
Copy link
Contributor

muratg commented Sep 9, 2015

👍

@muratg
Copy link
Contributor

muratg commented Sep 18, 2015

@halter73
Copy link
Member

#207 will conflict with this.

@lodejard
Copy link
Contributor

If they're set at the end the app can't see or change them... Should we change the middleware to preserve the server and re-apply the current time?

@Tratcher
Copy link
Member

Date is required and you can't trust misc middleware to handle it correctly. You can honor their value if they set it, but the server needs to set it at the end if it's missing.

Server is optional, we don't really care if it gets cleared.

- Doing it on each request is expensive
- The Timer is started when the first request comes in and fires every second
- Every request flips a bool so the Timer knows requests are coming in
- The Timer stops itself after a period of no requests coming in (10 ticks)
- #163
@DamianEdwards DamianEdwards force-pushed the damianedwards/date-header-perf branch from 098e04a to 4da048e Compare September 23, 2015 22:31
/// <summary>
/// Abstracts the system clock to facilitate testing.
/// </summary>
public interface ISystemClock
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

internal

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Closed this one, look at #220 instead

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

9 participants