Skip to content
This repository was archived by the owner on Jul 5, 2020. It is now read-only.

Add monitor to keep track of current value of App Services env vars#870

Closed
d3r3kk wants to merge 4 commits intodevelopfrom
dekeeler/web-app-envvars
Closed

Add monitor to keep track of current value of App Services env vars#870
d3r3kk wants to merge 4 commits intodevelopfrom
dekeeler/web-app-envvars

Conversation

@d3r3kk
Copy link
Copy Markdown
Contributor

@d3r3kk d3r3kk commented Mar 30, 2018

Corrects aspects of issue #868, further to PR #869.

Use to keep the value of WEBSITE_SITE_NAME and other App Services values current during runtime, as these values can change when swapping App Service applications from one slot to the next.

  • I ran Unit Tests locally.
  • CHANGELOG.md updated with one line description of the fix, and a link to the original issue.

Initial PR to bring discussion on the fix, and get some feedback before pushing to Heartbeat update as well.

- Use to keep the value of WEBSITE_SITE_NAME and other App Services values current during runime
/// </summary>
internal static class AppServiceEnvVarMonitor
{
internal static TimeSpan CheckInterval = TimeSpan.FromSeconds(30);
Copy link
Copy Markdown
Member

@pharring pharring Mar 30, 2018

Choose a reason for hiding this comment

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

Nit: private static readonly (unless you need to change it in unit tests.)

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Done

/// </summary>
private static void CheckVariablesIntermittent()
{
DateTime checkTime = DateTime.Now;
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Always use DateTime.UtcNow to avoid an expensive local timezone lookup.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Done - good tip, thanks.

private static void CheckVariablesIntermittent()
{
DateTime checkTime = DateTime.Now;
if (checkTime - LastCheckTime > CheckInterval)
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

For bonus perf points, instead of LastCheckTime compute and store NextCheckTime (i.e. the time after which you should check again) and avoid the subtraction in the common case and make it an addition in the rare case.
i.e.

DateTime now = DateTime.UtcNow;
if (now > NextCheckTime)
{
    NextCheckTime = now + CheckInterval;
   // etc.
}

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Awesome. Done.

@SergeyKanzhelev
Copy link
Copy Markdown
Contributor

@d3r3kk can you please confirm you actually tried it and this code works before I'll be reviewing it? From the fast glance this is NOT the fix that suppose to be working.

internal static class AppServiceEnvVarMonitor
{
internal static TimeSpan CheckInterval = TimeSpan.FromSeconds(30);
internal static DateTime LastCheckTime = DateTime.MinValue;
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Nit: private (unless you need to change it in unit tests.)

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

CheckInterval can be private, NextCheckTime internal to allow tests to work - added a line comment to call this intention out.

/// <summary>
/// Environment variables tracked by this monitor.
/// </summary>
internal static Dictionary<string, string> CheckedValues = new Dictionary<string, string>()
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Nit: private static readonly (unless you need to change it in unit tests.)

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Agreed, done. I am always thrown by the readonly keyword with containers...

{
{ "WEBSITE_SITE_NAME", string.Empty },
{ "WEBSITE_HOME_STAMPNAME", string.Empty },
{ "WEBSITE_HOSTNAME", string.Empty }
Copy link
Copy Markdown
Member

@pharring pharring Mar 30, 2018

Choose a reason for hiding this comment

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

Nit: remove extra space (alignment)

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Done, this was breaking the build (stylecop).

@d3r3kk
Copy link
Copy Markdown
Contributor Author

d3r3kk commented Mar 30, 2018

Thanks for looking this over @pharring, exactly the feedback I was looking for (keeping perf in mind for this task is pretty important I think).

@SergeyKanzhelev I am looking for feedback if this is a decent idea to pursue or not. In principle it should work with the idea presented, but I understand if you need to keep your PR review time streamlined - apologies for the noise! I will ensure it works in a test now and ping you for feedback once I've proven it out further.

@SergeyKanzhelev
Copy link
Copy Markdown
Contributor

I gave a link to PR in Azure functions that fixing this issue. Problem there is that sandbox doesn't update every env variable after swap. So I'd like you to make sure it works

@d3r3kk
Copy link
Copy Markdown
Contributor Author

d3r3kk commented Mar 30, 2018

@SergeyKanzhelev Please have a look at this now that I've got it a bit more put together. I've added some test methods to ensure they do what we want, and that the change satisfies the same demands the Web Functions PR did.

internal static DateTime NextCheckTime = DateTime.MinValue;

// how often we allow the code to re-check the environment
private static readonly TimeSpan CheckInterval = TimeSpan.FromSeconds(30);
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Let user configure it?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Could do, not sure how much value would be there. I propose we raise an issue and chat about it for the next release.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Let's not overexpose API we are not certain in and may want to pull in the very next PR. We can make it configurable later if we stick with this solution, we cannot unmake public API once it's in stable release without the breaking change.

d3r3kk added a commit that referenced this pull request Apr 2, 2018
- Add heartbeat provider for Web Apps/App Services

- Add monitor for App Services environment variables (which can change during the runtime of an AppServices app)

- Ensure adequate testing is added, update current Env Var tests to be testable in a parallel environment
Copy link
Copy Markdown
Member

@Dmitry-Matveev Dmitry-Matveev left a comment

Choose a reason for hiding this comment

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

Asynchronous solution would seem better rather than modifying values from Telemetry Initializer. However, this solution will work as well. Discussed options offline with Derek, More comments below.

{
return Environment.GetEnvironmentVariable(WebAppHostNameEnvironmentVariable) ?? string.Empty;
string nodeName = string.Empty;
AppServiceEnvVarMonitor.GetUpdatedEnvironmentVariable(WebAppHostNameEnvironmentVariable, ref nodeName);
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

This is no longer lazy initialized and we've added ~3 extra stack frames to every request/dependency in the customer app. Variable will be read synchronously every 30 seconds (may be fine if we do not Expand..() it, though).
We may think of an asynchronous approach here. Discussed offline with Derek.

{
{ "WEBSITE_SITE_NAME", string.Empty },
{ "WEBSITE_HOME_STAMPNAME", string.Empty },
{ "WEBSITE_HOSTNAME", string.Empty }
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

In comments for environment variable extraction in this PR, it's noted that WEBSITE_HOSTNAME is not a reliable source:

//...refactored so that it did not use WEBSITE_HOSTNAME, which is determined to be unreliable for functions during slot swaps.

Do we want to rely on that field, or we may use some tricks, e.g. from the same PR to build something more reliable from what we have.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

I say we should resort to these tactics if we have an issue or have proof that App Services are affected by the same failing that Azure Functions are. I have put an issue together to verify that we can detect slot swap using only WEBSITE_HOSTNAME, and to update to using the WEBSITE_SITE_NAME + WEBSITE_SLOT_NAME if the need arises. Note that I've included the WEBSITE_SLOT_NAME in the list of environment variables that the monitor watches for now.

internal static DateTime NextCheckTime = DateTime.MinValue;

// how often we allow the code to re-check the environment
private static readonly TimeSpan CheckInterval = TimeSpan.FromSeconds(30);
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Let's not overexpose API we are not certain in and may want to pull in the very next PR. We can make it configurable later if we stick with this solution, we cannot unmake public API once it's in stable release without the breaking change.

d3r3kk added a commit that referenced this pull request Apr 3, 2018
- Use env var monitor to check for udpates to environment in controlled/minimal fashion

- default to 30 second interval for checking the changes to environment

- corrections for stylecop
@d3r3kk
Copy link
Copy Markdown
Contributor Author

d3r3kk commented Apr 3, 2018

I have merged this change into PR #869. Please move comments and issues to that PR.

@d3r3kk d3r3kk closed this Apr 3, 2018
d3r3kk added a commit that referenced this pull request Apr 5, 2018
Add heartbeat provider for Azure App Services (in particular, Web Apps) that will add some infrastructure-identifying properties.
- Solves issue #648, #868, and #872.
- Both `AzureWebAppRoleEnvironment` and `AppServiceHeartbeatTelemetryModule` need to update their telemetry cache whenever the environment chances (ie, slot swap). `AppServiceEnvironmentVariableMonitor` solves this.
- Keep the value of WEBSITE_SITE_NAME and other App Services values current during runtime
- Merge PR #870 and #869, regarding heartbeat providers for App Services
- Ensure adequate testing is added, update current Env Var tests to be testable in a parallel environment
- `EnvironmentVariableMonitor` only updates the environment variables at set intervals, its subscribers only update when they need to.
- Disable monitor if we don't detect that we are in Azure App Service environment, and on SecurityExceptions, disable the monitor altogether
@d3r3kk d3r3kk deleted the dekeeler/web-app-envvars branch April 5, 2018 04:21
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.

5 participants