-
Notifications
You must be signed in to change notification settings - Fork 10.4k
Use polling to watch certificate paths #50251
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 our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
76bd8bb
d458978
8856efe
3e93524
098c1b5
a0596c0
421f3a2
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -29,9 +29,17 @@ internal sealed partial class CertificatePathWatcher : IDisposable | |
|
||
public CertificatePathWatcher(IHostEnvironment hostEnvironment, ILogger<CertificatePathWatcher> logger) | ||
: this( | ||
hostEnvironment.ContentRootPath, | ||
logger, | ||
dir => Directory.Exists(dir) ? new PhysicalFileProvider(dir, ExclusionFilters.None) : null) | ||
hostEnvironment.ContentRootPath, | ||
logger, | ||
dir => Directory.Exists(dir) | ||
? new PhysicalFileProvider(dir, ExclusionFilters.None) | ||
{ | ||
// Force polling because it monitors both symlinks and their targets, | ||
// whereas the non-polling watcher only monitors the symlinks themselves | ||
UseActivePolling = true, | ||
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. Do we know the performance impact of always doing this? 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. How many certs do we expect an app to have? I was assuming < 10. We only poll every four seconds, so I was expecting the CPU usage to be negligible. If the certs aren't all in the same directory, then there will likely be some overhead from having multiple timer loops. I think a lot of apps would be fine with a longer polling period, but I don't actually see a way to configure it. If an app has 100 certs, each in a different directory and they find the overhead is too high, there's an appcontext switch to disable watching certificates. 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. Polling isn't on by default (for the IHostingEnvironment.ContentRootFileProvider), I'm not sure it's about the number of apps, it might be the number of files being polled in the common case. Is this just polling the path specified by the cert in the 90% scenario? 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. Sorry, I'm not sure I understand the question. This change should have no effect on whether or not You've previously pointed out that that's wrong, that certificates should be watched using the host env file provider. There are two reasons I didn't do that: first, the users requesting this functionality made clear that they wanted to be able to load and watch certificates that were not under their content root; second, I didn't want to enable polling for all files in the content root because I was worried about the perf (and respecting user settings). 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. My point is that this change always turns on file watching no matter what. That’s not something we’ve tested before. I just wanted to understand the scope and impact of the polling. 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. If ReloadOnChange is true, then we will poll files in your (non-code) configuration for the default certificate and for each endpoint's certificate and SNI certificates. Obviously, this only applies to file-based certs (vs from the store). If the same certificate file appears in more than one of those locations, it will only be polled once. (There's a corner case where two different symlinks point to the same file, in which case it would be polled twice.) |
||
UsePollingFileWatcher = true, | ||
} | ||
: null) | ||
{ | ||
} | ||
|
||
|
@@ -138,9 +146,6 @@ internal void AddWatchUnsynchronized(CertificateConfig certificateConfig) | |
_metadataForFile.Add(path, fileMetadata); | ||
dirMetadata.FileWatchCount++; | ||
|
||
// We actually don't care if the file doesn't exist - we'll watch in case it is created | ||
fileMetadata.LastModifiedTime = GetLastModifiedTimeOrMinimum(path, dirMetadata.FileProvider); | ||
|
||
_logger.CreatedFileWatcher(path); | ||
} | ||
|
||
|
@@ -156,20 +161,6 @@ internal void AddWatchUnsynchronized(CertificateConfig certificateConfig) | |
_logger.FileCount(dir, dirMetadata.FileWatchCount); | ||
} | ||
|
||
private DateTimeOffset GetLastModifiedTimeOrMinimum(string path, IFileProvider fileProvider) | ||
{ | ||
try | ||
{ | ||
return fileProvider.GetFileInfo(Path.GetFileName(path)).LastModified; | ||
} | ||
catch (Exception e) | ||
{ | ||
_logger.LastModifiedTimeError(path, e); | ||
} | ||
|
||
return DateTimeOffset.MinValue; | ||
} | ||
|
||
private void OnChange(string path) | ||
{ | ||
// Block until any in-progress updates are complete | ||
|
@@ -184,33 +175,17 @@ private void OnChange(string path) | |
// Existence implied by the fact that we're tracking the file | ||
var dirMetadata = _metadataForDirectory[Path.GetDirectoryName(path)!]; | ||
|
||
// We ignore file changes that don't advance the last modified time. | ||
// We ignore file changes that result in a file becoming unavailable. | ||
// For example, if we lose access to the network share the file is | ||
// stored on, we don't notify our listeners because no one wants | ||
// their endpoint/server to shutdown when that happens. | ||
// We also anticipate that a cert file might be renamed to cert.bak | ||
// before a new cert is introduced with the old name. | ||
// This also helps us in scenarios where the underlying file system | ||
// reports more than one change for a single logical operation. | ||
var lastModifiedTime = GetLastModifiedTimeOrMinimum(path, dirMetadata.FileProvider); | ||
if (lastModifiedTime > fileMetadata.LastModifiedTime) | ||
{ | ||
fileMetadata.LastModifiedTime = lastModifiedTime; | ||
} | ||
else | ||
|
||
var fileInfo = dirMetadata.FileProvider.GetFileInfo(Path.GetFileName(path)); | ||
if (!fileInfo.Exists) | ||
{ | ||
if (lastModifiedTime == DateTimeOffset.MinValue) | ||
{ | ||
_logger.EventWithoutLastModifiedTime(path); | ||
} | ||
else if (lastModifiedTime == fileMetadata.LastModifiedTime) | ||
{ | ||
_logger.RedundantEvent(path); | ||
} | ||
else | ||
{ | ||
_logger.OutOfOrderEvent(path); | ||
} | ||
_logger.EventWithoutFile(path); | ||
return; | ||
} | ||
|
||
|
@@ -219,6 +194,8 @@ private void OnChange(string path) | |
{ | ||
config.FileHasChanged = true; | ||
} | ||
|
||
_logger.FlaggedObservers(path, configs.Count); | ||
} | ||
|
||
// AddWatch and RemoveWatch don't affect the token, so this doesn't need to be under the semaphore. | ||
|
@@ -321,7 +298,6 @@ private sealed class FileWatchMetadata(IDisposable disposable) : IDisposable | |
{ | ||
public readonly IDisposable Disposable = disposable; | ||
public readonly HashSet<CertificateConfig> Configs = new(ReferenceEqualityComparer.Instance); | ||
public DateTimeOffset LastModifiedTime = DateTimeOffset.MinValue; | ||
|
||
public void Dispose() => Disposable.Dispose(); | ||
} | ||
|
Uh oh!
There was an error while loading. Please reload this page.